2016-06-10 110 views
1

我試圖在獨佔模式下使用Windows音頻會話API(WASAPI)播放簡單的sineous波形,但遇到聲音故障我做的事。我一直在使用MSDN Exclusive-Mode Streams example作爲參考點,以下是稍微改編的代碼目前的樣子。WASAPI:以最小等待時間播放正弦波聲音,無毛刺(獨佔事件驅動模式)

設置代碼:

-- <variable declarations, incl. "HRESULT hr; BYTE *pData;" > -- 

// also, hr is checked for errors every step of the way 

hr = CoCreateInstance(
    CLSID_MMDeviceEnumerator, NULL, 
    CLSCTX_ALL, IID_IMMDeviceEnumerator, 
    (void**)&pEnumerator); 

hr = pEnumerator->GetDefaultAudioEndpoint(
    eRender, eConsole, &pDevice); 

hr = pDevice->Activate(
    IID_IAudioClient, CLSCTX_ALL, 
    NULL, (void**)&pAudioClient); 


REFERENCE_TIME DefaultDevicePeriod = 0, MinimumDevicePeriod = 0; 
hr = pAudioClient->GetDevicePeriod(&DefaultDevicePeriod, &MinimumDevicePeriod); 

WAVEFORMATEX wave_format = {}; 
wave_format.wFormatTag = WAVE_FORMAT_PCM; 
wave_format.nChannels = 2; 
wave_format.nSamplesPerSec = 44100; 
wave_format.nAvgBytesPerSec = 44100 * 2 * 16/8; 
wave_format.nBlockAlign = 2 * 16/8; 
wave_format.wBitsPerSample = 16; 

hr = pAudioClient->IsFormatSupported(
    AUDCLNT_SHAREMODE_EXCLUSIVE, 
    &wave_format, 
    NULL // can't suggest a "closest match" in exclusive mode 
    ); 

hr = pAudioClient->Initialize(
    AUDCLNT_SHAREMODE_EXCLUSIVE, 
    AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 
    MinimumDevicePeriod, 
    MinimumDevicePeriod, 
    &wave_format, 
    NULL); 


// Get the actual size of the allocated buffer. 
hr = pAudioClient->GetBufferSize(&bufferFrameCount); 

INT32 FrameSize_bytes = bufferFrameCount * wave_format.nChannels * wave_format.wBitsPerSample/8; 

hr = pAudioClient->GetService(
    IID_IAudioRenderClient, 
    (void**)&pRenderClient); 

hEvent = CreateEvent(nullptr, false, false, nullptr); 
if (hEvent == INVALID_HANDLE_VALUE) { printf("CreateEvent failed\n"); return -1; } 

hr = pAudioClient->SetEventHandle(hEvent); 

緩衝設置:

const size_t num_samples = FrameSize_bytes/sizeof(unsigned short); 

unsigned short *samples = new unsigned short[num_samples]; 

float min = (float)(std::numeric_limits<unsigned short>::min)(); 
float max = (float)(std::numeric_limits<unsigned short>::max)(); 
float halfmax = max/2.0; 
float dt = 1.0/(float)wave_format.nSamplesPerSec; 

float freq = (float)wave_format.nSamplesPerSec/(float)bufferFrameCount; 

for (int i = 0; i < num_samples/2; ++i) { 
    float t = (float)i * dt; 
    samples[2*i] = sin_minmax_Hz(min, max, freq, t); 
    samples[2*i + 1] = sin_minmax_Hz(min, max, freq, t); 
} 

hr = pRenderClient->GetBuffer(bufferFrameCount, &pData); 

memcpy(pData, samples, FrameSize_bytes); 

hr = pRenderClient->ReleaseBuffer(bufferFrameCount, flags); 

DWORD taskIndex = 0; 
hTask = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskIndex); 

if (hTask == NULL) { 
    hr = E_FAIL; 
    IF_ERROR_EXIT(hr); 
} 

功能sin_minmax_Hz定義如下:

#define TWO_PI (3.14159265359*2) 

static inline float sin01(float alpha) { 
    return 0.5*sin(alpha) + 0.5; 
} 

static inline float sin_minmax_Hz(float min, float max, float freq_Hz, float t) { 
    return (max - min)/2.0 * sin01(t * freq_Hz * TWO_PI); 
} 

回放:

hr = pAudioClient->Start(); // Start playing. 
IF_ERROR_EXIT(hr); 

// just play indefinitely 

while (true) { 
    WaitForSingleObject(hEvent, INFINITE); 

    hr = pRenderClient->GetBuffer(bufferFrameCount, &pData); 
    memcpy(pData, samples, FrameSize_bytes); 
    hr = pRenderClient->ReleaseBuffer(bufferFrameCount, 0); 
} 

問題是,在最小延遲時間內,正弦波通常平滑地播放約2秒鐘,然後開始大量鋸齒動作,聽起來像鋸齒波。我在這裏錯過了什麼嗎?

(整個工作的例子可以發現here

回答

1

只是想你全樣本,IAudioClient ::初始化失敗,AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED。這個錯誤是在Windows 7中引入的,你在運行Vista嗎?在修正對齊的週期後,我得到一個完美的正弦波。如果你的系統沒有產生這個錯誤,請嘗試手動對齊128字節(不是位)邊界上的緩衝區。否則,這裏的對齊代碼:

hr = pAudioClient->Initialize(
     AUDCLNT_SHAREMODE_EXCLUSIVE, 
     AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 
     MinimumDevicePeriod, 
     MinimumDevicePeriod, 
     &wave_format, 
     NULL); 

    if(hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { 
    UINT32 nFramesInBuffer; 
    REFERENCE_TIME hnsPeriod; 
    hr = pAudioClient->GetBufferSize(&nFramesInBuffer); 
    IF_ERROR_EXIT(hr); 
    hnsPeriod = (REFERENCE_TIME)(REFTIMES_PER_SEC * nFramesInBuffer/wave_format.nSamplesPerSec + 0.5); 
    hr = pAudioClient->Release(); 
    IF_ERROR_EXIT(hr); 
    hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient); 
    IF_ERROR_EXIT(hr); 
    hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, hnsPeriod, hnsPeriod, &wave_format, NULL); 
    IF_ERROR_EXIT(hr); 
    } 
+0

我實際上在Windows 10上,但我的系統似乎沒有給出對齊錯誤。它可能是駕駛員特定的事情嗎?無論如何,將緩衝區手動對齊爲128字節(整數)的倍數似乎可行,至少對於44100和48000 Hz的採樣率。在96KHz時,同樣的毛刺又開始發生。 – ehoopz

+0

此外,在我的系統上,由您發佈的代碼計算的'hnsPeriod'在我的機器上給出的時間段小於'MinimumDevicePeriod'(幀大小爲128 @ 44,1 KHz,對應的週期爲29024,最小值爲132 - > 〜30000)。因此,第一個工作週期值@ 44,1 KHz似乎是58049,這使得幀大小爲256. – ehoopz

+0

似乎這個系統不能真正處理'<〜5.5 ms'的週期,我會嘗試在另一個系統上。 – ehoopz