FC2ブログ

ホーム > WAVEファイルフォーマット > モノラル音源の擬似ステレオ再生

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



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


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


「WAVEファイルの再生」で解説したCWaveOutクラスに、下記のメンバ関数メンバ変数を新たに追加します。

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

UINT DoStereo(LPVOID pBuffer,UINT nBufSize,UINT indexSample,UINT validBytes,WAVEFORMATEX* pwfxFile);
//擬似ステレオ遅延時間の設定
void SetDelay(int delay){m_nDelayStereo=delay;};
//擬似ステレオ遅延時間の取得
int GetDelay(){return m_nDelayStereo;};
protected:
CByteArray m_byteInBuf; //音源データ取り込み用バッファ
CByteArray m_byteDelay; //遅延バッファ
CByteArray m_byteDelta; //遅延バッファに書ききれなかった音源データを、次回分に保存しておくバッファ
int m_nDelayStereo; //ステレオ変換時の遅延時間(㍉秒)


「WAVEファイルの再生」で解説したCWaveFileクラスStreamIn関数を下記のとおり変更します。

//Waveストリームの読み込み
UINT CWaveFile::StreamIn(LPVOID pBuffer,UINT nBufSize,UINT* pIndexSample)
{
ASSERT(AfxIsValidAddress(pBuffer,nBufSize));
ASSERT(m_hmmioIn);
ASSERT(m_posStart);
ASSERT(AfxIsValidAddress(pIndexSample,sizeof(UINT)));

//指定の演奏位置からファイル読み込み位置を算出して、
//その位置にシークします。

LONG offset=m_posStart+*pIndexSample*m_wfx.nBlockAlign;
LONG pos=::mmioSeek(m_hmmioIn,offset,SEEK_SET);
//シークに失敗したら、FALSEを返して終了します。
if (pos!=offset) return FALSE;

//dataチャンクの音源データを読み込みます。
LONG readBytes=::mmioRead(m_hmmioIn,(HPSTR)pBuffer,nBufSize);
//読み込みに失敗したら、FALSEを返して終了します。
if (readBytes==-1) return FALSE;
//指定の演奏位置を読み込んだ分だけ進めます。
*pIndexSample+=readBytes/m_wfx.nBlockAlign;
//読み込んだバイト数を返して終了します。
return readBytes;
}

//音源ストリームの入力(CWaveOutクラスのStreamIn関数をオーバーライドします。)
UINT CWaveFile::StreamIn(LPVOID pBuffer,UINT nBufSize)
{
ASSERT(AfxIsValidAddress(pBuffer,nBufSize));

UINT readBytes=0;
//ファイルとWAVE出力デバイスのWAVEFORMATEX構造体要素のうち、
//サンプル当りのバイト数、1秒あたりのサンプル数、サンプル当りのビット数が
//いずれも等しければ、読み込んだ音源をそのまま返して関数を終了します。

if ((CWaveOut::m_wfx.nBlockAlign==m_wfx.nBlockAlign)&&
(CWaveOut::m_wfx.nSamplesPerSec==m_wfx.nSamplesPerSec)&&
(CWaveOut::m_wfx.wBitsPerSample==m_wfx.wBitsPerSample))
{
readBytes=StreamIn(pBuffer,nBufSize,&m_indexSample);
}
else
{
//入力バッファのサイズから、加工前の入力バイト数を算出します。
UINT nSamples=nBufSize/CWaveOut::m_wfx.nBlockAlign;
UINT inputBytes=nSamples*m_wfx.nBlockAlign;

//入力用バッファ(CByteArrayクラス)のサイズが入力バイト数より小さい場合、
//入力用バッファのサイズを入力バイト数に合わせます。

if ((UINT)m_byteInBuf.GetSize()<inputBytes)
m_byteInBuf.SetSize(inputBytes);
//入力用バッファへのポインタを取得します。
PBYTE pInBuf=m_byteInBuf.GetData();
//入力用バッファをゼロクリアします。
::ZeroMemory(pInBuf,inputBytes);
UINT indexSafe=m_indexSample;
UINT sizeBytes=StreamIn(pInBuf,inputBytes,&m_indexSample);
//音源データに擬似ステレオ加工を施します。
readBytes=DoStereo(pBuffer,nBufSize,indexSafe,sizeBytes,&m_wfx);
}
return readBytes;
}


音源データのモノラル→擬似ステレオ、ステレオ→モノラル変換、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)
{
if (!AfxIsValidAddress(pBuffer,nBufSize)) return FALSE;
if (!AfxIsValidAddress(pwfxFile,sizeof(WAVEFORMATEX),0)) return FALSE;

//加工後の音源データを格納するバッファをゼロクリアします。
::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);
}


サンプルプログラム(VisualC++net2003ソリューション)WaveFileTest02.zip

上記のzipファイルをダウンロードしてからWinRAR等で解凍し、WaveFileTest02フォルダー内にある「WaveFileTest.sln」を開きます。
「F5」キーを押すと、ビルド確認のダイヤログが表示されるので「Yes」を選択してソリューションをビルドします。



ビルドが終わると直ちにプログラムが自動起動して下の図にあるダイヤログが表示されます。



ダイヤログにある「ファイルを開く」ボタンを押すと、「ファイルを開く」ダイヤログが表示されます。



resフォルダー内の「Sample2.wav」を選択し「開く」ボタンを押して、演奏が始まるのを確認してからダイヤログにある「ステレオ」ボタンを押すと、音声がモノラルからステレオに変わるか確認します。



「PAUSE」ボタンを押して演奏を一時停止させ、メニューから「ツール」/「WAVEフォーマットの変更」を選択し、「WAVEフォーマットの変更」ダイヤログで「サンプルビット数」を8ビットに変更してから「適用する」ボタンを押し、音がスムーズに出るか確認して下さい。



スライドバーを演奏開始位置に戻してから、メニューで「ツール」/「擬似ステレオ遅延時間の変更」を選択し、「擬似ステレオ効果」ダイヤログのスライドバーを操作して、お好みの遅延時間を探してみて下さい。




<<WAVE再生時の音量調節ページの先頭WAVEファイルの書き込み>>


スポンサーサイト



コメント: 0

この記事へのコメント
ブログ著者にのみ知らせます。

Trackback+Pingback: 0

TrackBack URL for this entry
http://hiroshi0945.blog75.fc2.com/tb.php/47-2e5002d1
Listed below are links to weblogs that reference
モノラル音源の擬似ステレオ再生 from マルチメディアファイルフォーマット

Home > WAVEファイルフォーマット > モノラル音源の擬似ステレオ再生

タグクラウド
ブロとも申請フォーム

この人とブロともになる

ブロとも一覧

このページの先頭へ戻る