【问题标题】:Win32 PlaySound: How to control the volume?Win32 PlaySound:如何控制音量?
【发布时间】:2010-02-20 16:33:36
【问题描述】:

我正在使用 Win32 MultiMedia 函数 PlaySound 从我的应用程序中播放声音。

我希望能够动态调整正在播放的声音的音量而无需修改系统音量。

我能找到的关于通过PlaySound 操纵声音音量的唯​​一建议是使用waveOutSetVolume,但是该函数设置系统范围的音量级别(不是我想要的)。

【问题讨论】:

    标签: c++ winapi audio multimedia


    【解决方案1】:

    两种可能的解决方案:

    首先,如果您的目标是 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”是音频样本块的开始。

    寻找“数据”部分,并将“数据”后面的 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;
    }
    

    此时,您已准备好使用 PlaySound 播放内存中的 WAV 文件:

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

    应该这样做。如果您打算使用 SND_ASYNC 标志使上述调用成为非阻塞的,那么您将无法释放内存缓冲区,直到它完成播放。所以要小心。

    关于WAV文件头的解析。我通过声明一个名为“FindDataChunk”的假设函数来摆脱这种情况。您可能应该投资于编写适当的 WAV 文件头解析器,而不是仅仅寻找您第一次在头中遇到“数据”的位置。为了简洁起见,我省略了通常的错误检查。因此,上述代码可能需要解决一些安全问题——尤其是涉及到遍历内存缓冲区并写入其中时。

    【讨论】:

    • 刚回到这个。这些天来,我很高兴使用 Core Audio 并完成了它,但是您的回答仍然很棒!
    【解决方案2】:

    添加作为@selbie clean 解决方案实施的答案。

    #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;
    }
    

    我在使用适当大小的数据类型(这里我使用 __int16)来处理 wav 文件时遇到的问题。

    我使用的 wav 文件带有 OpenAL 媒体文件“fiveptone.wav”。

    请随意修改,因为我从 wav 文件的文档中搜索的偏移量和一些试验和错误。

    【讨论】:

      猜你喜欢
      • 2016-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多