VS.net2005 VC++です。
今作成しているプログラムでPCMのデータを再生する機能を実装しようとしています。
データは固定長の連続したデータですがどのようにしたら音になるのかがさっぱり分か
りません。。
一般的な方法などとりかかりの情報をご存知でしたら教えていただけますでしょうか?
データを渡すだけで良いAPIがあれば一番良いのですが。。
WAVファイルを再生すると言うならわかるんですが、
PCMデータってフォーマットどうなってましたっけ?
Windowsのマルチメディア系のAPIにPCMを直接扱える関数があったかどうかは
知らないですね。ただ、私が知らないだけと言う可能性はあると思います。
マルチメディア系は殆ど使わないので詳しく有りませんし。
DirectSound絡みで同様の関数が無ければ、自分でフォーマット解析をして
再生可能なデータに変換して再生するしかないかもしれませんね。
音声処理とかに詳しい方がいましたらアドバイスしてあげてください。
PCM には詳しくないけど次のリンクはどうでしょうか?
http://wisdom.sakura.ne.jp/system/winapi/media/mm5.html
http://wisdom.sakura.ne.jp/system/winapi/media/index.html
上のリンクは参考になりませんかね。
#define Sample 44100 //サンプリング
#define LENG 44100 *10 //作成するデータ容量
typedef struct
{
char chRiff[4] ;
DWORD dwRiffSize ;
char chWave[4] ;
char chFmt [4] ;
DWORD dwFmtSize ;
PCMWAVEFORMAT pwf ;
char chData[4] ;
DWORD dwDataSize ;
unsigned char byData[0];
}
WAVEFORM ;
WAVEFORMATEX waveformat ;
WAVEFORM waveform = { 'R','I','F','F', Sample +
0x24, 'W','A','V','E','f','m','t',' ',
sizeof (PCMWAVEFORMAT), 1, 1, Sample,
Sample, 1, 8, 'd','a','t','a', Sample } ;
WAVEFORM *pwaveform ;
unsigned char * pBuffer; //BYTE
pBuffer=(unsigned char *)malloc(LENG+100);
pwaveform = (WAVEFORM *)malloc (sizeof (WAVEFORM) + LENG) ;
*pwaveform = waveform ;
pwaveform->dwRiffSize=LENG+ sizeof(WAVEFORM);
pwaveform->dwDataSize=LENG;
//音を作る
//ここでpBuffer[]に音声データを代入
DPI=2*3.14159/Sample;
for(i=0;i<LENG;i++){
pBuffer[i]=(BYTE)(128+127*sin(DPI*freq*i)); //音声データ 8bit
pwaveform->byData[i] = (BYTE)pBuffer[i]; //ここではサイン波
}
//発音
PlaySound ((PTSTR) pwaveform, NULL, SND_MEMORY | SND_LOOP | SND_ASYNC);
//発音停止
PlaySound(NULL,NULL,0);
回答とソースありがとうございます。
szgy_physさんのソースをもとに以下の様に作りました。
正直内容を理解しきれていないのですが、結果ブツブツ言う音がスピーカーから鳴るだ
けで音声は流れませんでした。
データがおかしいのかレートが合っていないのか試行錯誤中です。
とりあえずご報告まで。
下記のソースで何か問題があればご教授下さい。
※オーディオデータ1データ80バイトを連続して再生しています。
void PlayAudio()
{
WAVEFORMATEX waveformat ;
WAVEFORM waveform = { 'R','I','F','F', Sample +
0x24, 'W','A','V','E','f','m','t',' ',
sizeof (PCMWAVEFORMAT), 1, 1, Sample,
Sample, 1, 8, 'd','a','t','a', Sample } ;
WAVEFORM *pwaveform ;
unsigned char * pBuffer; //BYTE
double DPI;
int i;
ULONGLONG paraOffset;
char pcImagePoint[READ_BUFF];
BOOL bRet;
DWORD paraStartAddr = 0;
unsigned char cAudioData[80];
pBuffer=(unsigned char *)malloc(LENG+100);
pwaveform = (WAVEFORM *)malloc (sizeof (WAVEFORM) + LENG) ;
*pwaveform = waveform ;
pwaveform->dwRiffSize=LENG+ sizeof(WAVEFORM);
pwaveform->dwDataSize=LENG;
//データの取得
//読み出し位置のセット
paraStartAddr = gsDatInfo.startCluster;
for (i = 0; i<100; i++){
paraOffset = gpsAList[i].Offset;
//データ読み出し
bRet = ReadPhysicsData(paraStartAddr,paraOffset,pcImagePoint,VTL_JPEG);
memcpy(&cAudioData[0], &pcImagePoint[24], 80);
//データのセット
memcpy(&pwaveform->byData[0], &cAudioData[0], sizeof(cAudioData));
//発音
PlaySound ((PTSTR) pwaveform, NULL, SND_MEMORY | SND_LOOP | SND_ASYNC);
}
//発音停止
PlaySound(NULL,NULL,0);
}
>paraStartAddr = gsDatInfo.startCluster;
>for (i = 0; i<100; i++){
> ..................
>PlaySound ((PTSTR) pwaveform, NULL, SND_MEMORY | SND_LOOP | SND_ASYNC);
>}
の部分の意味が分かりません(自分が VC6を使用しているため?)
流れとして、次の1. 2. 3. は分ける
1.音声データを作り終える
80バイトのデータの終わりと、始めが連続していますか?
連続していないと切れ目でブツブツ言います
void Sakusei(){
for(i=0;i<80;i++){
pBuffer[i]=(BYTE)(....); //データは0と255の間
pwaveform->byData[i] = (BYTE)pBuffer[i];
}
}
2.作り終えてから
void Play(){
PlaySound ((PTSTR) pwaveform, NULL, SND_MEMORY | SND_LOOP |
SND_ASYNC);}
で繰り返し(SND_LOOP)発音
3.発音後 停止
void Stop(){
PlaySound(NULL,NULL,0);}
分らないなりに、気がついたこと
① }とPlaySound() の位置
for (i = 0; i<100; i++){
........
}
の所でデータを全て作り終えてから
PlaySound ((PTSTR) pwaveform, NULL, SND_MEMORY | SND_LOOP | SND_ASYNC);
で発音
②PlaySound(NULL,NULL,0); がこの位置では発音と同時に停止してしまう
回答ありがとうございます。
>paraStartAddr = gsDatInfo.startCluster;
>for (i = 0; i<100; i++){
> ..................
>PlaySound ((PTSTR) pwaveform, NULL, SND_MEMORY | SND_LOOP | SND_ASYNC);
>}
の部分は、
80バイト単位のデータを取得してそれを100データ分流そうとしています。
ですので
1.1データ(80バイト)取得
2.再生
3.次のデータ(80バイト)取得
4.再生
・・・
5.再生停止
という感じのソースにしています。
//発音
PlaySound ((PTSTR) pwaveform, NULL, SND_MEMORY | SND_LOOP | SND_ASYNC);
は100回コール
//発音停止
PlaySound(NULL,NULL,0);
は最後に1回コールしているだけです。
タブスペースがない関係で分かりにくくなっていますが・・・
waveformに設定するサンプリング周波数が合っていないのかなと思っていますが、
このデータの仕様を確認中です。
ロジック的には上記方法で問題ないでしょうか?
また、疑問に思っているのがPlaySoundで連続して複数回コールして音を出した場合、
1つ目のデータが流れ終わってから2つ目のデータが流れるようになるのでしょうか?
1つ目のデータが流れ終わるまで次のデータのPlaySoundをしないようなウェイト処理が
必要でしょうか?
1.説明が不十分で申し訳ありません
PlaySound では、メモリーに作成したファイル(SND_MEMORY) を
PlaySound(NULL,NULL,0)で停止させられるまで繰り返し (SND_LOOP )読み込んで
発音しています
従って、PlaySoundを一端実行したら、同じ音が出続けます
PlaySoundを続けて実行した時どうなるかは分かりません
2.1データが80バイトでは、発音時間が1/500秒以下です
この短い時間にSND_LOOP が反応できるのか疑問を感じます
3.解決方法としては、100データ分の連続データを作成し、
一回だけPlaySound()を実行する
停止したいタイミングでPlaySound(NULL,NULL,0)を実行する
ただし、80バイトのデータの終わりと始めが連続していなければなりません
データに飛びがあると、ブツブツ言います
4.サンプリング周波数は、44010 22050 11025 のどれでも支障はありませんが
音の振動数が2倍 4倍と変わります
追加します
1.PlaySound ((PTSTR) pwaveform, NULL, SND_MEMORY | SND_LOOP | SND_ASYNC);
は同じ発音を繰り返す
2.PlaySound ((PTSTR) pwaveform, NULL, SND_MEMORY | SND_ASYNC);
は一回だけ発音
ただし、80×100 バイトのとき
サンプリング周波数 発音継続時間 振動数
44100 8000/44100 ≒ 0.2 秒 4 :
22050 8000/22050 ≒ 0.4 秒 2 :
11025 8000/11025 ≒ 0.7 秒 1 :
回答ありがとうございます。
1.データを前よりも多く80000バイト分作成
2.1回の再生データ作成後に一度だけPlaySound ((PTSTR) pwaveform, NULL,
SND_MEMORY | SND_ASYNC)を実行
の方法にした所、おっしゃるように数秒音が流れます。
ですが音がサンプリング周波数44100ですとキーキー鳴る機械音で、
低くしていくと2500ぐらいでぼわーっとした音になります。
ですが、通常の音声としては聞こえません。
今このデータのサンプリング周波数が分からずお試しで再生しているのですが。。
下にソースを載せます。
再生の直後に停止を書いてありますが、とりあえず今はステップ実行で確認しています
ので問題ないです。
但し、一度この関数を抜けて2度目再度同じ関数を呼んでも再生の部分で音が流れませ
ん。
プログラムを実行しなおすと流れます。
停止以外に初期化など必要なのでしょうか?
※※※※※ヘッダファイル
#define Sample 2600 //サンプリング
#define AUDIO_DATA_SIZE 80//1オーディオデータサイズ
#define PLAY_AUDIO_DATA_SIZE 80000 //1回に再生するオーディオデータサイズ
typedef struct{
char chRiff[4] ;
DWORD dwRiffSize ;
char chWave[4] ;
char chFmt [4] ;
DWORD dwFmtSize ;
PCMWAVEFORMAT pwf ;
char chData[4] ;
DWORD dwDataSize ;
unsigned char byData[PLAY_AUDIO_DATA_SIZE];
}WAVEFORM;
※※※※※ソースファイル
WAVEFORMATEX waveformat ;
WAVEFORM waveform = { 'R','I','F','F', Sample +
0x24, 'W','A','V','E','f','m','t',' ',
sizeof (PCMWAVEFORMAT), 1, 1, Sample,
Sample, 1, 8, 'd','a','t','a', Sample } ;
WAVEFORM *pwaveform ;
unsigned char * pBuffer; //BYTE
int i;
ULONGLONG paraOffset;
char pcImagePoint[READ_BUFF];
BOOL bRet;
DWORD paraStartAddr = 0;
unsigned char cAudioData[AUDIO_DATA_SIZE];
pBuffer=(unsigned char *)malloc(PLAY_AUDIO_DATA_SIZE+100);
pwaveform = (WAVEFORM *)malloc (sizeof (WAVEFORM) + PLAY_AUDIO_DATA_SIZE) ;
*pwaveform = waveform ;
pwaveform->dwRiffSize=PLAY_AUDIO_DATA_SIZE+ sizeof(WAVEFORM);
pwaveform->dwDataSize=PLAY_AUDIO_DATA_SIZE;
//データの取得
//オフセットのセット
paraStartAddr = gsDatInfo.startCluster;
for (i = 0; i<PLAY_AUDIO_DATA_SIZE / AUDIO_DATA_SIZE; i++){
paraOffset = gpsAList[i].Offset;
//データ読み出し
bRet = ReadPhysicsData(paraStartAddr,paraOffset,pcImagePoint,VTL_JPEG);
memcpy(&cAudioData[0], &pcImagePoint[24], AUDIO_DATA_SIZE);
//データのセット
memcpy(&pwaveform->byData[i*AUDIO_DATA_SIZE], &cAudioData[0],
AUDIO_DATA_SIZE);
}
//発音
PlaySound ((PTSTR) pwaveform, NULL, SND_MEMORY | SND_ASYNC);
//発音停止
PlaySound(NULL,NULL,0);
>PlaySound ((PTSTR) pwaveform, NULL, SND_MEMORY | SND_ASYNC);
pwaveformのデータを「*.wav」等のファイルに変換することは可能ですか?
可能なら作成して、メディアプレイア等で聞いてみるのもいいと思います。
1.サンプリング周波数は前記の3通りしかサポートしていない筈です
つまり、サンプリング周波数の変更は自由にできない
2.目的の振動数の音を作るには、データを圧縮したり伸長したりしなければ
なりません
具体的には、データと目的の振動数が分からないとなんとも言えません
キーキーいうのは、データを正直に発音しているのではないでしょうか
(自分のhomepage '画像から作成' 'ドップラー効果' '音作成1'で同じようなこと
をしています)
>停止以外に初期化など必要なのでしょうか?
3.初期化は一回で十分です
ただ添付された形では、waveform などの変数がglobalでないため、関数を抜けると
消えてしまい、発音毎に初期化が必要になります
>但し、一度この関数を抜けて2度目再度同じ関数を呼んでも再生の部分で音が流れませ
ん。
>プログラムを実行しなおすと流れます。
4。発音停止が 発音の直後にあるのが何らかの影響を及ぼしているのではないでしょう
か
ソースのなかのREAD_BUFFは80ですね?
データ読み出しの部分は、VC6 には無いコードのため理解できません
net C++を勉強しなければなりませんね
>ITOさん
WAVEファイル作成に必要なデータ部分の情報が不足している為ファイルを作成出来ない
状況です。
必要な情報が判明したら試してみます。
>szgy_physさん
1.サンプリング周波数は前記の3通りしかサポートしていない筈です
つまり、サンプリング周波数の変更は自由にできない
→サンプリング周波数は8000との事でした。
そこで再生したいデータのサンプリング周波数を8000にしたところ音楽っぽいのが流ま
した!
但し前のピーピー言う機械音ではなくなったのですがまだ少し違う感じです。
表現が難しいですが、、
他のパラメータの転送率、1サンプルのデータサイズ等を疑っていますが
あとはこのパラメータが判明すれば音が流れそうな感じになってきました!
ただ、「サンプリング周波数は3通りしかサポートしていない」というのが気になってい
ます。
試しに上記プログラムで普通にウィンドウズの録音ツールで作成したWAVファイル(サン
プリング周波数8000、モノラル)を取り込み再生した所問題なく再生出来ました。
問題ありでしょうか?
2.目的の振動数の音を作るには、データを圧縮したり伸長したりしなければ
なりません
具体的には、データと目的の振動数が分からないとなんとも言えません
キーキーいうのは、データを正直に発音しているのではないでしょうか
(自分のhomepage '画像から作成' 'ドップラー効果' '音作成1'で同じようなこと
をしています)
→キーキー言うのはサンプリング周波数があっていなかったせいでした。
いろいろ教えてくださりありがとうございます!
>停止以外に初期化など必要なのでしょうか?
3.初期化は一回で十分です
ただ添付された形では、waveform などの変数がglobalでないため、関数を抜けると
消えてしまい、発音毎に初期化が必要になります
>但し、一度この関数を抜けて2度目再度同じ関数を呼んでも再生の部分で音が流れませ
ん。
>プログラムを実行しなおすと流れます。
4。発音停止が 発音の直後にあるのが何らかの影響を及ぼしているのではないでしょう
か
→ちょっとここはまだ原因が分かっていないのですが助言を元に見直してみます。
ソースのなかのREAD_BUFFは80ですね?
データ読み出しの部分は、VC6 には無いコードのため理解できません
net C++を勉強しなければなりませんね
→READ_BUFFは読み出したいデータの1音声データあたりのバイト数です。
データ読み出し部分は自作です。紛らわしくてすいません。この部分は問題ないと思い
ます。
今やろうとしているのは、あるデータの中に80バイト単位で記録されている音声データ
を取り出して再生しようとしています。
最初は1データずつ再生するつもりでしたが80バイトでは再生するには短すぎるとの事
で、
1000データ分80000バイトの音声データを作成して、それを流すようにしました。
今のところあとはヘッダ情報の調整だけでいけそうな気がしています。
本当に助かりました。
まだ正確な音は出ていませんが、いろいろ試して進展がありましたらまた報告させてい
ただきます。
ちなみにヘッダに設定する転送率(bps)が間違っていた場合、再生される音にどう影響が
あるのでしょうか?
適当に変えてみても再生される音は変わらない気がするのですが・・・
>ただ、「サンプリング周波数は3通りしかサポートしていない」というのが気になっ
ています。
MSDNのWAVEFORMATEXについての部分
If wFormatTag is WAVE_FORMAT_PCM, then common values for
nSamplesPerSec are 8.0 kHz, 11.025 kHz, 22.05 kHz, and 44.1 kHz.
8000kHzも有効です。
common valuesって事なので、他でも成功する可能性は十分にあるかと思いま
す。
>但し前のピーピー言う機械音ではなくなったのですがまだ少し違う感じです。
元のデータがどう生成されたかってのが影響しそうな気がします。
音楽っぽく聞こえたとの事なので、圧縮という事は無いかと思います。
幾つか思いつくものを書いてみます。
・実は16bitのサウンドだった。
>部分的に聞き取れる音が出る可能性がある。
・実は、符号付と符号無しの差だった。
>この場合マトモに音に聞こえるかどうか怪しいです。
>このヘッダ項目が無いので自前で変換する必要がある可能性があります。
・実は、4bitのサウンドだった。
>2倍速再生且つノイズが載ったサウンドが聞こえる筈。
・実は、ステレオのサウンドだった。
>0.5倍速再生且つノイズの載ったサウンドが聞こえる筈。
>左右の差が大きい部分は聞けた物ではなくなる。
このあたりがあり得そうな原因ではないかと思います。
WAVEファイルを取り込んだ場合は正常との事なので、恐らく元データと整合性
の無い設定をしているのだと思われます。
元ファイルのフォーマットを正確に調べるのが良いかと思います。
皆様回答とアドバイスありがとうございました。
結局記録されている音声データの仕様を確認し、
4000Mhzで音声が流れました。
いろいろ音声ファイルの仕様も勉強になりとても助かりました。
ありがとうございました。