2010-02-20 2 views
2

나는 내 응용 프로그램에서 사운드를 재생하기는 Win32 멀티미디어 기능 PlaySound를 사용하고 있습니다.Win32 PlaySound : 볼륨을 제어하는 ​​방법?

시스템 볼륨 레벨을 수정하지 않고 재생되는 사운드의 볼륨을 동적으로 조정하고 싶습니다.

PlaySound을 통해 재생되는 사운드의 볼륨을 조정할 때 내가 발견 할 수있는 유일한 제안은 waveOutSetVolume을 사용하는 것이지만이 기능은 시스템 전체의 볼륨 레벨을 설정합니다.

답변

11

두 가지 가능한 솔루션 :

먼저 비스타를 대상으로 최대 경우, 당신은 응용 프로그램 별 볼륨을 조절하는 새로운 Windows 오디오 API를 사용할 수 있습니다. 그 적합하지 있다면 ISimpleAudioVolume, IAudioEndpointVolume, 등 ...

직접 메모리에 WAV 파일을로드하고 장소에 샘플을 수정할 수 있습니다. 이 시도 :

는 디스크와 메모리 버퍼에 WAV 파일을 읽고 다시 샘플을 확장 할 수 있습니다. 문제의 WAV 파일은 압축되지 않은 (PCM) 샘플이있는 16 비트 스테레오라고 가정합니다. 스테레오 또는 모노. 그렇지 않은 경우이 중 많은 부분이 창 밖으로 나옵니다.

독자는 연습을 위해 WAV 파일 바이트를 메모리에 남겨 둘 것입니다. 그러나 다음 코드로 시작해 봅시다. "ReadWavFileIntoMemory"는 자신의 함수입니다.

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

이는 WAV 파일 헤더입니다 :이 시점에서

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

는 pFileBytes의 검사는 다음과 같을 것입니다. "데이터"는 오디오 샘플 청크의 시작 부분입니다.

는 "데이터"부분을 탐색하고, DWORD으로 "데이터"를 다음의 4 바이트를 판독. 이것은 오디오 샘플이 들어있는 "데이터"청크의 크기입니다. 샘플 수 (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은 정상적인 전체 볼륨입니다. 이제 우리는 단지 대상 볼륨에 의해 각각의 샘플을 곱

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

을 그리고 그것을 수행해야합니다

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 파일을 재생할 준비가 된 것입니다. 당신은 위의 호출 비 차단을 만들기 위해 SND_ASYNC 플래그를 사용하려는 경우, 당신은 재생이 완료 될 때까지 메모리 버퍼를 확보 할 수 없습니다. 그러니 조심해. WAV 파일 헤더의 해석에 관해서는

. 필자는 "FindDataChunk"라는 가설적인 함수를 선언함으로써이 문제를 해결했습니다.헤더에서 "데이터"가 처음 만나는 곳을 찾는 것보다 적절한 WAV 파일 헤더 파서를 작성하는 데 투자해야합니다. 간결함을 위해, 나는 보통 오류 검사를 생략했다. 이와 같이, 위의 코드로 해결할 보안 문제가있을 수 있습니다 - 특히 메모리 버퍼를 가로 지르고 쓰는 것과 관련되어 있습니다.

+0

이제 막 돌아 왔습니다. 요즘 Core Audio를 가정하고 그 일을 끝내면 행복 하겠지만, 그럼에도 불구하고 당신의 대답은 훌륭합니다! –

0

@selbie clean solution 구현에 대한 답변으로 추가.

#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 파일에는 "5ptone.wav"라는 OpenAL 미디어 파일이 포함되어 있습니다.

wav 파일의 문서 및 시행 착오에서 검색 한 오프셋 때문에 언제든지 수정할 수 있습니다.

관련 문제