Re: 音声波形表示について ( No.1 ) |
- 名前:管理人 日時:2015/11/02 02:38
> こちらの動画のType3にあるような、周波数毎の音量レベルを表示するような音声波形を表示するプログラムを作成したいのですが、DxLibに入っている機能で実現可能でしょうか。
サウンドの波形を1サンプル毎で参照する機能はあるので、自力でフーリエ変換を行えば可能ですが、
関数ひとつ呼び出せば周波数毎の音量レベルを取得できるといったものはありません
なので、追加しました、よろしければその関数を追加したこちらのバージョンをダウンロードしてください m(_ _)m
https://dxlib.xsrv.jp/temp/DxLibVCTest.exe // VisualC++ 用
https://dxlib.xsrv.jp/temp/DxLibBCCTest.exe // BorlandC++ 用
https://dxlib.xsrv.jp/temp/DxLibGCC_DevCppTest.exe // Dev-C++ 用
https://dxlib.xsrv.jp/temp/DxLibGCC_MinGWTest.exe // MinGW 用
https://dxlib.xsrv.jp/temp/DxLibDotNet.zip // .NET用
https://dxlib.xsrv.jp/temp/DxLibMakeTest.exe // ソース
(中身を既存のライブラリのファイルに上書きして、BCCをお使いの
場合は『再構築』を、VCをお使いの場合は『リビルド』を、
Dev-C++をお使いの方は「Rebuild All(Ctrl+F11)」をして下さい)
以下の関数を追加しました
// ソフトウエアで扱う波形データハンドルの指定の範囲を高速フーリエ変換を行い、各周波数域の振幅を取得する
// SoftSoundHandle : ソフトサウンドハンドル
// Channel : フーリエ変換を行うチャンネル( -1 を指定するとすべてのチャンネルを合成した結果になります )
// SamplePosition : フーリエ変換で使用するサンプルの範囲の開始サンプル位置
// SampleNum : フーリエ変換で使用するサンプルの数( 256, 512, 1024, 2048, 4096, 8192, 16384 の何れかである必要があります )
// Buffer : フーリエ変換を行った結果を代入する float 型配列の先頭アドレス
// BufferLength : Buffer の配列の長さ、こちらは長さに制限はありません
int GetFFTVibrationSoftSound( int SoftSoundHandle, int Channel, int SamplePosition, int SampleNum, float *Buffer, int BufferLength ) ;
指定のサンプル位置から指定のサンプル数を使って、周波数毎の音量レベルを取得( Buffer配列に代入 )します
SampleNum は値が大きいほど多くのサンプルを使って( 多くの再生時間分を使って )フーリエ変換をするので、
音量表示の反応を考えると 4096 くらいが丁度良いです
GetFFTVibrationSoftSound を使って画面に周波数毎の音量レベルを描画するサンプルを作成してみましたので、
よろしければご覧ください m(_ _)m ( プログラム中の TestBGM.wav をお手持ちのサウンドファイルに変更してください )
#include "DxLib.h"
#include <math.h>
#define BUFFERLENGTH 800
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int SoundHandle ;
int SoftSoundHandle ;
float ParamList[ BUFFERLENGTH ] ;
int SamplePos ;
int i ;
// ウインドウモードで起動
ChangeWindowMode( TRUE ) ;
// 画面解像度をセット
SetGraphMode( 800, 600, 32 ) ;
// DXライブラリの初期化
if( DxLib_Init() < 0 ) return -1 ;
// サウンドファイルをソフトサウンドハンドルとして読み込み
SoftSoundHandle = LoadSoftSound( "TestBGM.wav" ) ;
// ソフトサウンドハンドルからサウンドハンドルを作成( もう一度ファイルから読み込んでも同じです )
SoundHandle = LoadSoundMemFromSoftSound( SoftSoundHandle ) ;
// サウンドの再生開始
PlaySoundMem( SoundHandle, DX_PLAYTYPE_LOOP ) ;
// 描画先を裏画面に変更
SetDrawScreen( DX_SCREEN_BACK ) ;
// メインループ
while( ProcessMessage() == 0 )
{
// 画面を初期化
ClearDrawScreen() ;
// 現在の再生位置を取得
SamplePos = GetCurrentPositionSoundMem( SoundHandle ) ;
// 現在の再生位置から 4096 サンプルを使用して周波数分布を得る
GetFFTVibrationSoftSound( SoftSoundHandle, -1, SamplePos, 4096, ParamList, BUFFERLENGTH ) ;
// 周波数分布を画面に描画する
for( i = 0 ; i < BUFFERLENGTH ; i ++ )
{
float Param ;
// 関数から取得できる値を描画に適した値に調整
Param = pow( ParamList[ i ], 0.5f ) * 4.0f ;
// 縦線を描画
DrawBox( i, 600 - ( int )( Param * 600 ), i + 1, 600, GetColor( 255,255,0 ), TRUE ) ;
}
// 裏画面の内容を表画面に反映
ScreenFlip() ;
}
// DXライブラリの後始末
DxLib_End() ;
// ソフトの終了
return 0 ;
}
 |
Re: 音声波形表示について ( No.2 ) |
- 名前:はーむ(解決) 日時:2015/11/02 20:28
自力で解決する方法もあったということなのに、機能の実装までしていただいてくださり、ありがとうございます。全く頭が上がらない思いです…
こちらの方でも動作の確認できました。
本当にありがとうございました。ぜひとも活用させていただきます。
|
Re: 音声波形表示について ( No.3 ) |
- 名前:はーむ 日時:2015/11/03 01:08
解決にしてからで大変申し訳ありません…
横軸(周波数分布)で対数をとってイコライザーのように音域をバランスよく表示しようとしたのですが、一番多いサンプル数をとっても最初の方のサンプルが飛びぬけて音量が振れてしまいます。
どうにかlogでサンプルをとれるようにはできないでしょうか。
|
Re: 音声波形表示について ( No.4 ) |
- 名前:はーむ(解決) 日時:2015/11/03 22:05
BufferLengthの方をサンプル数に合わせたら可能でした。
お騒がせしてすみません…
|
Re: 音声波形表示について ( No.5 ) |
- 名前:管理人(解決) 日時:2015/11/04 03:57
No.3 のお書き込みを拝見してから私も一般的なイコライザーやアナライザーの表示について調べてみたのですが、
既に解決されたということで何よりです
ところで私が少し調べた限りでは特に BufferLength をサンプル数に合わせる必要は無さそうに感じたのですが、
はーむさんがされた方法では BufferLength をサンプル数に合わせる必要があったということで、
よろしければどのように解決されたのか教えていただけないでしょうか? m(_ _)m
因みに振幅については No.1 のプログラムでは pow を使って調整していましたが、一般的には log を使うようです
あと GetFFTVibrationSoftSound では 44100Hz のサンプリング周波数のサウンドデータの場合は
取得できる周波数分布は半分の 0Hz 〜 22050Hz になるのですが、一般的なアナライザーでは 16KHz までしか
表示していなかったり、サウンド関係には疎いので色々勉強になります・・・
|
Re: 音声波形表示について ( No.6 ) |
- 名前:はーむ(解決) 日時:2015/11/04 18:46
>ところで私が少し調べた限りでは特に BufferLength をサンプル数に合わせる必要は無さそうに感じたのですが、
>はーむさんがされた方法では BufferLength をサンプル数に合わせる必要があったということで、
>よろしければどのように解決されたのか教えていただけないでしょうか? m(_ _)m
関数がBufferLengthの大きさに合わせてサンプルを振り分けてBufferに代入しているのかと予測していましたので、
BufferLengthが大きいほど細かいサンプルがとれるのかと思ってしまったので…
ほとんどご提示いただいたサンプルプログラムを少しいじっただけですが、コードはこんな感じです。
#include "DxLib.h"
#include <math.h>
#define BUFFERLENGTH 16384
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
int SoundHandle;
int SoftSoundHandle;
float ParamList[BUFFERLENGTH];
int SamplePos;
int i;
// ウインドウモードで起動
ChangeWindowMode(TRUE);
// 画面解像度をセット
SetGraphMode(800, 600, 32);
// DXライブラリの初期化
if (DxLib_Init() < 0) return -1;
// サウンドファイルをソフトサウンドハンドルとして読み込み
SoftSoundHandle = LoadSoftSound("TestBGM.wav");
// ソフトサウンドハンドルからサウンドハンドルを作成( もう一度ファイルから読み込んでも同じです )
SoundHandle = LoadSoundMemFromSoftSound(SoftSoundHandle);
// サウンドの再生開始
PlaySoundMem(SoundHandle, DX_PLAYTYPE_LOOP);
// 描画先を裏画面に変更
SetDrawScreen(DX_SCREEN_BACK);
// メインループ
while (ProcessMessage() == 0)
{
// 画面を初期化
ClearDrawScreen();
// 現在の再生位置を取得
SamplePos = GetCurrentPositionSoundMem(SoundHandle);
// 現在の再生位置から 4096 サンプルを使用して周波数分布を得る
GetFFTVibrationSoftSound(SoftSoundHandle, -1, SamplePos, 16384, ParamList, BUFFERLENGTH);
int x = -1,j = 0;
// 周波数分布を画面に描画する
for (i = 0; i < BUFFERLENGTH; i++)
{
if ((int)(log10((double)i) * 10) != x) {
j++;
x = (int)(log10((double)i) * 10);
float Param;
// 関数から取得できる値を描画に適した値に調整
Param = pow(ParamList[i], 0.5f) * 4.0f;
// 縦線を描画
DrawBox(j*20, 600 - (int)(Param * 600), j*20 + 4, 600, GetColor(255, 255, 0), TRUE);
}
}
// 裏画面の内容を表画面に反映
ScreenFlip();
}
// DXライブラリの後始末
DxLib_End();
// ソフトの終了
return 0;
}
かなりあやふやになってしまいましたが、自分が見てきたオーディオスペクトラムの波形に似てきたので解決としてしまいました。
詳しいことは私も調べている段階です…
私は音楽方面でも活動していたのですが、論理的なことはまだまだ分からないことがたくさんあって、
振幅にもlogを使うことも初めてお伺いしました。
 |
Re: 音声波形表示について ( No.7 ) |
- 名前:管理人 日時:2015/11/08 03:51
ご返信遅くなり申し訳ありません
プログラムを載せていただきありがとうございます、勉強になります
> 関数がBufferLengthの大きさに合わせてサンプルを振り分けてBufferに代入しているのかと予測していましたので、
> BufferLengthが大きいほど細かいサンプルがとれるのかと思ってしまったので…
仰る通りです、すみません自分でも試してみて周波数分布を対数表示する場合に4096サンプル等では
低周波数部の分解能が足りないことに気付きました
スペクトル表示の反応が良く且つ周波数分布を対数表示にしているスペクトル表示はどうやって
実装しているんでしょうね・・・
> 振幅にもlogを使うことも初めてお伺いしました。
私が試した log を使う方法ではこちらの部分が
// 関数から取得できる値を描画に適した値に調整
Param = pow(ParamList[i], 0.5f) * 4.0f;
// 縦線を描画
DrawBox(j*20, 600 - (int)(Param * 600), j*20 + 4, 600, GetColor(255, 255, 0), TRUE);
こうなります
// 関数から取得できる値を描画に適した値に調整
if( ParamList[ i ] > 0.000000000001f )
{
Param = log( ParamList[ i ] ) * 10.0f ;
}
else
{
Param = -120.0f ;
}
// 縦線を描画
DrawBox( j*20, 600 - ( int )( ( 120.0f + Param ) / 120.0f * 600 ), j*20 + 4, 600, GetColor( 255,255,0 ), TRUE ) ;
10 * log( 振幅の強さ ) とすることでデシベル値に変換でき、それが大体 0db 〜 -120db になるので、
符号を反転して 0 〜 120 の値にした後、それを 0 〜 600 の値に変換して描画、という感じです
因みに見た目は pow を使う方式のほうが良いです(・・;
|
Re: 音声波形表示について ( No.8 ) |
- 名前:はーむ(解決) 日時:2015/11/08 11:35
ご返信ありがとうございます。
>10 * log( 振幅の強さ ) とすることでデシベル値に変換でき、それが大体 0db 〜 -120db になるので、
>符号を反転して 0 〜 120 の値にした後、それを 0 〜 600 の値に変換して描画、という感じです
なるほど…とても参考になります。
こちらでも試してみましたが、私もpowの方が好きです。
今は演出程度に使わせてもらわせてますが、いつか本格的に調査する機会が出てきたら
私なりに調べたことをまた書いてみようかと思います。
|
|