2011年02月11日

モノラル音源の擬似ステレオ再生



モノラル音源を一定時間遅延させ、元の音源と合わせて再生する擬似ステレオ再生について解説します。手順は以下の通りです。


  1. 音源データWAVEファイルから読み込みます。
  2. 前回の残りの音源データがあれば、遅延バッファの先頭に遅延時間分書き込みます。
  3. 元の音源データを一定時間分ずらしてバッファに書き込みます。
  4. ずらして書いたために書ききれずに残った音源データを、次回分として保存します。
  5. 元の音源データ右チャンネル遅延バッファの音源データ左チャンネルに書き込んで再生します。




CWaveFile::StreamIn関数において、同名のオーバーロード関数のCWaveFile::StreamIn音源データを取得した後、CWaveOut::DoStereo関数を呼び出して、音源データ擬似ステレオ効果を施します。
(音源データのモノラル→擬似ステレオ、ステレオ→モノラル変換、8ビット→16ビット、16ビット→8ビット変換を一手に実現する関数です。)

//メンバー変数のm_byteInBuf(CByteArrayクラス)に格納された音源データを、
//モノラル→擬似ステレオ、ステレオ→モノラル変換して、引数のバッファに格納します。
// pBuffer:加工後の音源データを格納するバッファ
// nBufSize:バッファのバイト数
// indexSample:現在の演奏位置(サンプル数)
// validBytes:音源データの有効バイト数
// pwfxFile:音源データの演奏形式を格納したWAVEFORMATEX構造体

UINT CWaveOut::DoStereo(LPVOID pBuffer,UINT nBufSize,UINT indexSample,UINT validBytes,WAVEFORMATEX* pwfxFile)
{
ASSERT(::AfxIsValidAddress(pBuffer,nBufSize));
ASSERT(!::IsBadReadPtr(pwfxFile,sizeof(WAVEFORMATEX)));

//加工後の音源データを格納するバッファをゼロクリアします。
::ZeroMemory(pBuffer,nBufSize);

//擬似ステレオに変更:1、モノラルに変更:-1、変更せず:0
int iDeltaChannel=m_wfx.nChannels-pwfxFile->nChannels;
//遅延時間内に相当するサンプル数
UINT delaySample=pwfxFile->nSamplesPerSec*m_nDelayStereo/1000;
//遅延時間に相当するサンプルバイト数
UINT delayBytes=delaySample*pwfxFile->nBlockAlign;
//1サンプル当りビット数の差を取る。16ビットにする:8、8ビットにする:-8、変更せず:0
int iDeltaBits=m_wfx.wBitsPerSample-pwfxFile->wBitsPerSample;

//音源データのサンプル数を算出します。
UINT nSamples=validBytes/pwfxFile->nBlockAlign;

//音源データへのポインタを取得します。
PBYTE pInBuf=m_byteInBuf.GetData();
PBYTE pDelay=0;

//チャンネル変更なしで、1サンプルあたりのビット数変更なしの場合、
//音源データをそのまま入力バッファにコピーし、有効バイト数を返して終了します。

if ((iDeltaChannel==0)&&(iDeltaBits==0)){
::CopyMemory(pBuffer,pInBuf,validBytes);
return validBytes;
}
//擬似ステレオに変換する場合。
if (iDeltaChannel>0){
//遅延バッファが音源データの有効バイト数より小さい場合、
//遅延バッファのサイズを音源データの有効バイト数とします。

if ((UINT)m_byteDelay.GetSize()<validBytes)
m_byteDelay.SetSize(validBytes);
//遅延時間分の残りバッファが遅延バイト数より小さい場合、
//遅延時間分の残りバッファのサイズを遅延バイト数とします。

if ((UINT)m_byteDelta.GetSize()<delayBytes)
m_byteDelta.SetSize(delayBytes);
//遅延バッファをゼロクリアします。
pDelay=m_byteDelay.GetData();
::ZeroMemory(pDelay,validBytes);
//遅延時間分の残りバッファへのポインタを取得します。
PBYTE pDelta=m_byteDelta.GetData();

//音源データの有効バイト数が遅延バイト数より大きい場合。
if (validBytes>delayBytes){
//演奏位置が遅延時間を超えている場合は、前回の遅延時間分の
//残りバッファの内容を遅延バッファの先頭にコピーします。

if (indexSample>delaySample) ::CopyMemory(pDelay,pDelta,delayBytes);
//音源データを遅延時間分ずらして遅延バッファにコピーします。
::CopyMemory(pDelay+delayBytes,pInBuf,validBytes-delayBytes);
//音源データの遅延時間分の残りを遅延時間分の残りバッファにコピーして、
//次回分に残しておきます。

::CopyMemory(pDelta,pInBuf+validBytes-delayBytes,delayBytes);
}
//音源データの有効バイト数が遅延バイト数と等しいか、小さい場合。
else{
//演奏位置が遅延時間を超えている場合は、前回の遅延時間分の
//残りバッファの内容を遅延バッファの先頭にコピーします。

if (indexSample>delaySample) ::CopyMemory(pDelay,pDelta,validBytes);
//音源データを遅延時間分ずらして遅延バッファにコピーします。
::CopyMemory(pDelta,pDelta+validBytes,delayBytes-validBytes);
//音源データの遅延時間分の残りを遅延時間分の残りバッファにコピーして、
//次回分に残しておきます。

::CopyMemory(pDelta+delayBytes-validBytes,pInBuf,validBytes);
}
}

//サンプル数を取得します。
UINT validSamples=validBytes/pwfxFile->nBlockAlign;

BYTE array[4]; //1サンプルあたりの入力用バッファです。
BYTE delay[4]; //1サンプルあたりの遅延用バッファです。
//加工後の音源データを格納するバッファへのポインタを取得します。

PBYTE dst=(PBYTE)pBuffer;
//音源データバッファへのポインタを取得します。
PBYTE src=pInBuf;
//遅延バッファへのポインタを取得します。
PBYTE dly=pDelay;
for(UINT i=0;i<validSamples;i++){
//1サンプルあたりの音源データを取得します。
array[0]=array[1]=array[2]=array[3]=0;
::CopyMemory(&array,src,pwfxFile->nBlockAlign);
src+=pwfxFile->nBlockAlign;
//擬似ステレオに変更する場合は、
//擬似ステレオ化のための時間遅延データを作成します。

if (iDeltaChannel>0){
delay[0]=delay[1]=delay[2]=delay[3]=0;
if (dly){
//1サンプルあたりの遅延データを取得します。
::CopyMemory(&delay,dly,pwfxFile->nBlockAlign);
dly+=pwfxFile->nBlockAlign;
//8ビットモノラルの場合。
if (pwfxFile->wBitsPerSample<=8)
//右チャンネルに遅延データを格納します。
array[1]=delay[0];
//16ビットモノラルの場合。
else{
//右チャンネルに遅延データを格納します。
array[3]=delay[1];
array[2]=delay[0];
}
}
}
//ステレオからモノラルに変更する場合は、
//モノラル化のため左右の音の平均を求めます。

else if (iDeltaChannel<0){
DWORD dwL=(DWORD)array[0]+((DWORD)array[1]<<8);
if (dwL&0x8000) dwL|=0xFFFF0000;
DWORD dwR=(DWORD)array[2]+((DWORD)array[3]<<8);
if (dwR&0x8000) dwR|=0xFFFF0000;
DWORD dw=(dwL+dwR)/2;
array[0]=(BYTE)dw;
array[1]=(BYTE)(dw>>8);
}
//8ビット→16ビットに変更する場合。
if (iDeltaBits>0){
array[3]=0x80-array[1];
array[1]=0x80-array[0];
array[0]=array[2]=0;
}
//16ビット→8ビットに変更する場合。
else if (iDeltaBits<0){
array[0]=0x80-array[1];
array[1]=0x80-array[3];
array[2]=array[3]=0;
}
//作成した音源データをバッファに格納します。
switch(m_wfx.nBlockAlign){
case 1:
*dst++=array[0];
break;
case 2:
*dst++=array[0];
*dst++=array[1];
break;
case 4:
*dst++=array[0];
*dst++=array[1];
*dst++=array[2];
*dst++=array[3];
break;
}
}
return (UINT)(dst-(PBYTE)pBuffer);
}





posted by ひろし at 14:11| Comment(0) | WAVEファイルフォーマット | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。