Peak-meterの作成

C++ Builder CE

C++ Builder CEサウンドプログラミング

システム音を拾って、ピークメーターに似た表示を行うプログラムを作る。できあがりの感じの動画は、

使っている楽曲は、BGMerさん提供の”遠くの景色”です。メディアプレイヤーで再生している音声レベルに応じてバーを表示します。ま、これを作るには、色々なアプローチがあるとは思いますが、システムで再生されているレベルを捉えることがまず必要です。それは、

#include <mmdeviceapi.h>
#include <endpointvolume.h>

IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioMeterInformation *pMeterInfo = NULL;


........

	HRESULT hr;
	CoInitialize(NULL);
	hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
						NULL, CLSCTX_INPROC_SERVER,
						__uuidof(IMMDeviceEnumerator),
						(void**)&pEnumerator);
	if ( FAILED(hr) ) {
		ShowMessage("CoCreateInstance failed");
		return;
	}
	hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
	if ( FAILED(hr) ) {
		ShowMessage("GetDefaultAudioEndpoint  failed");
		return;
	}

	hr = pDevice->Activate(__uuidof(IAudioMeterInformation),
						CLSCTX_ALL,NULL,(void**)&pMeterInfo);
	if ( FAILED(hr) ) {
		ShowMessage("device activate failed");
		return;
	}
		hr = pMeterInfo->GetMeteringChannelCount(&chcount);
	if( FAILED(hr)){
		ShowMessage("get ch count failed");
		return;

	}


	Timer1->Interval = TIMER_PERIOD;
	Timer1->Enabled = true;

等として、準備しておいて、実際の音の大きさを拾うのは、タイマードリブンなルーチンで、

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
	HRESULT hr;
	int numl,numh;
	char    obuf[80];

	hr = pMeterInfo->GetChannelsPeakValues(chcount,apeak);
	if( FAILED(hr)){
		ShowMessage("get ch values failed");
		Timer1->Enabled = false;
	}
	//ProgressBar1->Value = apeak[0]*100;
	//ProgressBar2->Value = apeak[1]*100;
	//now = GetTickCount();
	//updatebar1all(apeak[0]);
	//updatebar2all(apeak[1]);
	//aled->clear();
	numl = apeak[0]*NUMLEDS/2;
	numh = NUMLEDS-1-apeak[1]*NUMLEDS/2;
	for( int k = 0 ; k < NUMLEDS ; k++ ){
		if( k <= numl || k >= numh){
			if( k <= numl )
				leds[k] = tables[k];
			else
				leds[k] = tables[29-k];
		}
		else
			leds[k].setRGB(0,0,0);
	}
	if( numl == 0 )
		leds[0].setRGB(0,0,0);
	if( numh == NUMLEDS-1 )
		leds[NUMLEDS-1].setRGB(0,0,0);
	//	sprintf(obuf,"peak detected now:%g old:%g\n",apeak[0],lastl);
	//	OutputDebugString((LPCWSTR)obuf);
	/* peak and hold */
	if( apeak[0] < lastl ){
		//leds[(int)(lastl*NUMLEDS/2)].setRGB(30,0,0);
		unsigned char r,g,b;
		r = tables[(int)(lastl*NUMLEDS/2)].r;
		g = tables[(int)(lastl*NUMLEDS/2)].g;
		b = tables[(int)(lastl*NUMLEDS/2)].b;
		leds[(int)(lastl*NUMLEDS/2)].setRGB(3*r,0,0);
		lastl = apeak[0];
		lastl -= 0.12;
		//sprintf(obuf,"peak detected %g %g\n",apeak[0],lastl);
		//OutputDebugString((LPCWSTR)obuf);
	}
	else
		lastl = apeak[0];
	if( apeak[1] < lasth ){
		unsigned char r,g,b;
		r = tables[NUMLEDS-(int)(lasth*NUMLEDS/2)].r;
		g = tables[NUMLEDS-(int)(lasth*NUMLEDS/2)].g;
		b = tables[NUMLEDS-(int)(lasth*NUMLEDS/2)].b;
		leds[NUMLEDS-(int)(lasth*NUMLEDS/2)].setRGB(3*r,0,0);
		lasth = apeak[1];
		lasth -= 0.12;
	}
	else
		lasth = apeak[1];
	/* */
	aled->showleds();
	PaintBox1->Repaint();
	PaintBox2->Repaint();
}
//---------------------------------------------------------------------------

ここではTIMER_PERIOD=25で、25msということなので毎秒40サンプルという前提ですね。上のタイマールーチンでバレているように、前記事のws2812bなLEDに画面と同様なディスプレイを行います。LEDが使えれば。同期再生している状況の動画は、

右側のフレームの上の方に映り込んでいるのは、USB経由での消費電流と出力電圧を計測し表示している装置で、

同様なものはまだ秋月電子から購入できるようです。上の動画では、最大の負荷電流は50ないし60mA程度ですからUSB2.0の定格内に収まっています。WS2812BなLEDは今回は円形に並べた24素子を使いました。従ってNUMLEDSは24に定義しています。cpu-zによると、

M/BはASUSのROG MAXIMUS X FORMULA Rev 1.xxなので、こちらのM/B側ではなくUSB2.0の許容電流の最大値は500mAまでと考えられます。まだ余裕がありますかね。

コメント