2.Windowsのサウンド出力のレベルをリアルタイム表示するプログラムを作る
まずはフォームのデザインから示します。珍しくFMXベースです。
縦長の棒二本は、TPaintBoxです。他にTTimerが一つ、TCheckBoxとTButtonもそれぞれ一個づつ置きます。プログラム全体の制御はTimer1なるインスタンスが統括します。その割り込み周期は、25msで、秒40回サウンド出力をサンプリングして逐次左右のチャンネル別にPaintBox1とPaintBox2に表示するというだけのことです。サウンド出力を取ってくるのは、前回の関数の親戚の
float apeak[2];
hr = pMeterInfo->GetChannelsPeakValues(chcount,apeak);
を使います。画面表示と並行してWS2812Bタイプの円環状のLED(24素子)に出力レベルを”ピークレベルを適宜残しながら適当に”表示します。USB-RS232CのI/Fがつながれていなくてもおけです。エラーにはなりません。さて、全体のソースは、
//---------------------------------------------------------------------------
#include <fmx.h>
#pragma hdrstop
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include "Unit1.h"
#include "ws2812b.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
#define TIMER_PERIOD 25
// "安全な"COMポインタの解放処理マクロ
#define SAFE_RELEASE(p) if(p){ p->Release(); p=NULL; }
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioMeterInformation *pMeterInfo = NULL;
float apeak[6] = {0,0,0,0,0,0};
float last1=0;
float last2=0;
DWORD now,lasttime,lasttime2;
UINT chcount;
ws2812b* aled;//= new ws2812b(NUMLEDS, leds);
#define NUMLEDS 24
float lastl=0;
float lasth=0;
CCRGB leds[NUMLEDS];
CCRGB tables[NUMLEDS];
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// set size ?
Form1->Width = 73;
Form1->Height = 461;
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;
}
aled = new ws2812b(NUMLEDS, leds);
aled->clear();
int steps = 12;
int i;
float n;
unsigned char r,g,b;
char obuf[80];
for( i = 0 ; i < steps ; i++ ){
n = (float)i/(float)(steps-1);
r = (float)0*(1.0f-n) + (float)(0x20)*n;
g = (float)(0x20)*(1.0f-n) + (float)(0x20)*n;
b = (float)0*(1.0f-n)+ (float)0*n;
tables[i].setRGB(r,g,b);
tables[23-i].setRGB(r,g,b);
}
Timer1->Interval = TIMER_PERIOD;
Timer1->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
SAFE_RELEASE(pEnumerator);
SAFE_RELEASE(pDevice);
SAFE_RELEASE(pMeterInfo);
CoUninitialize();
}
//---------------------------------------------------------------------------
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;
}
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);
/* 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;
}
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();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::PaintBox1Paint(TObject *Sender, TCanvas *Canvas)
{
TAlphaColor t;
TRectF var;
float start;
if( Dot->IsChecked )
start = 360*(apeak[0]-0.01);
else
start = 0;
for( float i = start ; i <360*apeak[0] ; i += 1.0 ){
t = HSLtoRGB(i/360,0.75,0.5);
PaintBox1->Canvas->Fill->Color = t;
var = TRectF(0,360-i,this->Width,360-i-1);
PaintBox1->Canvas->FillRect(var,0,0,AllCorners,1.0);
}
if( last1 > apeak[0] ) {
t = HSLtoRGB(0,0.75,0.5);
PaintBox1->Canvas->Fill->Color = t;
var = TRectF(0,360-last1*360-2,this->Width,360-last1*360+2);
PaintBox1->Canvas->FillRect(var,0,0,AllCorners,1.0);
last1 -= 0.01;
}
else
last1 = apeak[0];
//last1 = apeak[0];
}
//---------------------------------------------------------------------------
void __fastcall TForm1::PaintBox2Paint(TObject *Sender, TCanvas *Canvas)
{
// static float lastvalue = 0.0;
TAlphaColor t;
TRectF var;
float start;
if( Dot->IsChecked )
start = 360*(apeak[1]-0.01);
else
start = 0;
for( float i = start ; i <360*apeak[1] ; i += 1.0 ){
t = HSLtoRGB(i/360,0.75,0.5);
PaintBox2->Canvas->Fill->Color = t;
//var = TRectF(i,0,i+1,this->Height);
var = TRectF(0,360-i,this->Width,360-i-1);
PaintBox2->Canvas->FillRect(var,0,0,AllCorners,1.0);
}
//plot previous peak if any
if( last2 > apeak[1] ) {
t = HSLtoRGB(0,0.75,0.5);
PaintBox2->Canvas->Fill->Color = t;
//var = TRectF(last2*360-2,0,last2*360+2,this->Height);
var = TRectF(0,360-last2*360-2,this->Width,360-last2*360+2);
PaintBox2->Canvas->FillRect(var,0,0,AllCorners,1.0);
last2 -= 0.01;
}
else
last2 = apeak[1];
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
aled->clear();
Application->Terminate();
}
//---------------------------------------------------------------------------
これに加えて、WS2812Bの回で取り上げたSerialPort.cpp,SerialPort.h,ws2812b.cpp,ws2812b.hをプロジェクトに加えてからBuildします。起動してみた時の動画を以下に示します。
音源は、BGMerさんの作品で、
これを使わせていただいています。ダウンロードリンクは、https://bgmer.net/wp-content/uploads/2022/05/294_BPM88.mp3です。さて動画の話に戻ると、サウンド出力レベルの記録動画と別テイクのLED発光の動画を合成しています。なるべく頭を合わせたつもりですが、少しだけズレているかもしれません。また前回消費電流が0mAとなっていましたが、今回のピーク時の電流値は60~70mAなので、それなりに消費してますが、まだ余裕はあるかもしれません。5V電源はI/Fからのを使っています。電圧は安定して5V程度ですね。
コメント