Два возможных решения:
Во-первых, если вы ориентируетесь на 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, а не просто искать, где вы впервые столкнулись с «данными» в заголовке. Ради краткости я упустил обычную проверку ошибок. В связи с этим могут возникнуть некоторые проблемы безопасности, связанные с приведенным выше кодом, особенно в связи с обходом буфера памяти и записью в него.