手拍子をカウントする

Siv3D Advent Calendar 2013, 4 日目の記事です。
今日はマイク入力を使って手拍子を認識するソフトを作ってみましょう。

まずは Play Siv3D! のチュートリアルからマイク入力のサンプルを持ってきます。
チュートリアル | マイク入力 | 録音した波形の解析 | 振幅

# include <Siv3D.hpp>

void Main()
{
	const Font font(30);

	// 10 秒間, サンプリングレート 44100Hz で録音を開始する
	if (!Recorder::Start())
	{
		return;
	}

	while (System::Update())
	{
		const double max = Recorder::GetMaxAmplitude();
		const double ave = Recorder::GetAverageAmplitude();

		font.draw(Format(L"最大振幅: ", max));

		RectF(0, 50, max*Window::Width(), 30).draw();

		font.draw(Format(L"平均振幅: ", ave), 0, 100);

		RectF(0, 150, ave*Window::Width(), 30).draw();

		if (Recorder::IsEnd()) // 録音が終了
		{
			// 最初の位置から録音し直し
			Recorder::Restart();
		}
	}
}

録音中は
Recorder::GetMaxAmplitude(sec) で最新の sec 秒間の最大の振幅 [0.0, 1.0] を、
Recorder::GetAverageAmplitude(sec) で最新の sec 秒間の平均の振幅を取得できます。
sec はデフォルトでは 0.02 です。

さて、手拍子の波形は、ごく一瞬だけ大きな振動が現れるのが特徴です。

この特徴を利用して、毎フレーム最大の振幅を調べ、閾値を超えた瞬間を調べれば、それなりの精度で手拍子を認識できます。

実際に作ってみましょう。

# include <Siv3D.hpp>

void Main()
{
	const double threshold = 0.2;

	const Font font(30);

	const Point center = Window::Size() / 2;

	if (!Recorder::Start())
	{
		return;
	}

	bool clapping = false;

	int count = 0;

	int impact = 0;

	while (System::Update())
	{
		const double max = Recorder::GetMaxAmplitude();

		if (!clapping && max >= threshold)
		{
			++count;

			clapping = true;

			impact = 40;
		}

		if (max < threshold)
		{
			clapping = false;
		}

		font.draw(Format(L"最大振幅: ", max));

		RectF(0, 50, max*Window::Width(), 30).draw();

		impact = Max(impact - 4, 0);

		Circle(center, 80 - impact).draw(Color(Palette::Orange, 120 + impact*3));

		font.drawCenter(Format(count), center - Vec2(0, impact*0.3));

		if (Recorder::IsEnd())
		{
			Recorder::Restart();
		}
	}
}

ポケットをたたく(手拍子をする)たびにクッキーが増えていくゲームなんてどうでしょうか。