2

Win32 マルチメディア関数PlaySoundを使用して、アプリケーションからサウンドを再生しています。

システムの音量レベルを変更せずに、再生中のサウンドの音量を動的に調整できるようにしたいと考えています。

PlaySoundを介して再生されるサウンドの音量を操作するために見つけた唯一の提案は、waveOutSetVolumeを使用することでしたが、その関数はシステム全体の音量レベルを設定します (私が望むものではありません)。

4

2 に答える 2

11

考えられる解決策は次の 2 つです。

まず、Vista 以降をターゲットにしている場合は、新しい Windows オーディオ API を使用して、アプリケーションごとの音量を調整できます。ISimpleAudioVolume、IAudioEndpointVolume など...

それが適切でない場合は、WAV ファイルをメモリに直接ロードして、サンプルをその場で変更できます。これを試して:

WAV ファイルをディスクからメモリ バッファーに読み取り、サンプルを元に戻します。問題の WAV ファイルは、圧縮されていない (PCM) サンプルを含む 16 ビット ステレオであると仮定します。ステレオまたはモノ。そうでない場合、これの多くは窓の外に出ます。

WAV ファイル バイトのメモリへの読み取りは、読者の演習として残しておきます。しかし、次のコードから始めましょう。ここで、"ReadWavFileIntoMemory" は独自の関数です。

DWORD dwFileSize;
BYTE* pFileBytes;
ReadWavFileIntoMemory(szFilename, &pFileBytes, &dwFileSize);

この時点で、pFileBytes の検査は次のようになります。

RIFF....WAVEfmt ............data....

これは WAV ファイルのヘッダーです。「データ」は、オーディオ サンプル チャンクの開始です。

「data」部分にシークし、「data」に続く 4 バイトを DWORD に読み込みます。これは、オーディオ サンプルを含む「データ」チャンクのサイズです。サンプルの数 (PCM 16 ビットがこの数を 2 で割ったものであると仮定)。

// FindDataChunk is your function that parses the WAV file and returns the pointer to the "data" chunk.
BYTE* pDataOffset = FindDataChunk(pBuffer);
DWORD dwNumSampleBytes = *(DWORD*)(pDataOffset + 4);
DWORD dwNumSamples = dwNumSamplesBytes / 2;

次に、メモリ バッファー内の最初の実際のサンプルを指すサンプル ポインターを作成します。

SHORT* pSample = (SHORT*)(pDataOffset + 8);

pSample は、WAV ファイルの最初の 16 ビット サンプルを指します。これで、オーディオ サンプルを適切な音量レベルにスケーリングする準備が整いました。ボリューム範囲が 0.0 から 1.0 の間であると仮定しましょう。0.0 は完全な無音です。1.0 は通常のフル ボリュームです。次に、各サンプルにターゲット ボリュームを掛けるだけです。

float fVolume = 0.5; // half-volume
for (DWORD dwIndex = 0; dwIndex < dwNumSamples; dwIndex++)
{
    SHORT shSample = *pSample;
    shSample = (SHORT)(shSample * fVolume);
    *pSample = shSample;
    pSample++;


    if (((BYTE*)pSample) >= (pFileBytes + dwFileSize - 1))
       break;
}

この時点で、メモリ内の WAV ファイルを PlaySound で再生する準備が整いました。

PlaySound((LPCSTR)pFileBytes, NULL, SND_MEMORY);

そして、それはそれを行う必要があります。SND_ASYNC フラグを使用して上記の呼び出しをノンブロッキングにする場合は、再生が終了するまでメモリ バッファを解放できません。ので注意してください。

WAVファイルのヘッダーの解析について。私は、「FindDataChunk」と呼ばれる架空の関数を宣言することで、この問題を回避しました。ヘッダーで最初に「データ」に遭遇する場所を探すのではなく、おそらく適切な WAV ファイル ヘッダー パーサーを作成することに投資する必要があります。簡潔にするために、通常のエラー チェックは省略しました。そのため、上記のコードでは、特にメモリ バッファのトラバースと書き込みに関連するセキュリティ上の問題に対処する必要があります。

于 2010-02-20T20:31:07.857 に答える
0

@selbie クリーン ソリューションの実装への回答として追加します。

#include <Windows.h>
#include <string>
#include <iostream>
#include <fstream>
#include <conio.h>

using namespace std;

void ReadWavFileIntoMemory(string fname, BYTE** pb, DWORD *fsize){
    ifstream f(fname, ios::binary);

    f.seekg(0, ios::end);
    int lim = f.tellg();
    *fsize = lim;

    *pb = new BYTE[lim];
    f.seekg(0, ios::beg);

    f.read((char *)*pb, lim);

    f.close();
}

int main(){
    DWORD dwFileSize;
    BYTE* pFileBytes;
    ReadWavFileIntoMemory("D:\\OpenAL 1.1 SDK\\samples\\media\\fiveptone.wav", &pFileBytes, &dwFileSize);

    BYTE* pDataOffset = (pFileBytes + 40);

    cout << "Length: " << dwFileSize << endl;

    float fVolume = 0.02;

    __int16 * p = (__int16 *)(pDataOffset + 8);
    cout << sizeof(*p) << endl;
    for (int i = 80 / sizeof(*p); i < dwFileSize / sizeof(*p); i++){
        p[i] = (float)p[i] * fVolume;
    }

    cout << "PlaySound" << endl;
    PlaySound((LPCSTR)pFileBytes, NULL, SND_MEMORY);

    return 0;
}

wavファイルでの操作に適切なサイズのデータ​​型(ここでは__int16を使用)で直面した問題。

使用した wav ファイルには、OpenAL メディア ファイル「fiveptone.wav」が付属しています。

オフセットはwavファイルのドキュメントから探して試行錯誤したので自由に修正してください。

于 2017-10-29T14:35:54.710 に答える