Win32 PlaySound: Как управлять громкостью? - PullRequest
2 голосов
/ 20 февраля 2010

Я использую функцию Win32 MultiMedia PlaySound для воспроизведения звука из моего приложения.

Я бы хотел иметь возможность динамически регулировать громкость воспроизводимого звука без изменения уровня громкости системы.

Единственные предложения, которые я мог найти для управления громкостью звука, воспроизводимого с помощью PlaySound , - это использование waveOutSetVolume , однако эта функция устанавливает уровень громкости для всей системы (не то, что мне нужно ).

Ответы [ 2 ]

11 голосов
/ 20 февраля 2010

Два возможных решения:

Во-первых, если вы ориентируетесь на Vista и выше, вы можете использовать новые API-интерфейсы Windows Audio для настройки уровня громкости для каждого приложения. ISimpleAudioVolume, IAudioEndpointVolume и т. Д. ...

Если это не подходит, можете загрузить файл WAV непосредственно в память и изменить образцы на месте. Попробуйте это:

Считайте файл WAV с диска в буфер памяти и уменьшите образцы. Я собираюсь предположить, что рассматриваемый файл WAV является 16-битным стерео с несжатыми (PCM) семплами. Стерео или моно. Если это не так, большая часть этого уходит в окно.

Я оставлю чтение байтов WAV-файла в памяти в качестве упражнения для читателя: Но давайте начнем со следующего кода, где «ReadWavFileIntoMemory» - ваша собственная функция.

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

На этом этапе проверка pFileBytes будет выглядеть примерно так:

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

Это заголовок файла WAV. «data» - начало фрагмента аудиосэмпла.

Найдите часть «data» и прочитайте 4 байта после «data» в DWORD. Это размер блока «данных», который содержит аудиосэмплы. Количество выборок (при условии, что 16-разрядное значение PCM равно этому числу, деленному на 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 указывает на первую 16-битную выборку в файле WAV. Таким образом, мы готовы масштабировать аудиосэмплы до соответствующего уровня громкости. Давайте предположим, что наш диапазон объема составляет от 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, а не просто искать, где вы впервые столкнулись с «данными» в заголовке. Ради краткости я упустил обычную проверку ошибок. В связи с этим могут возникнуть некоторые проблемы безопасности, связанные с приведенным выше кодом, особенно в связи с обходом буфера памяти и записью в него.

0 голосов
/ 29 октября 2017

Добавление в качестве ответа на реализацию чистого решения @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;
}

Проблема, с которой я столкнулся с типом данных надлежащего размера (здесь я использовал __int16) для манипуляций в файле wav.

Файл wav, который я использовал, поставляется с медиа-файлами OpenAL "fiveptone.wav".

Пожалуйста, не стесняйтесь изменять, потому что смещение, которое я искал из документации wav-файлов и некоторых проб и ошибок.

...