EC_VOID AudioRender::DispatchMsg(EC_VOID* pMsg) { if (pMsg) HandleControlMsg(pMsg); else RenderAudio(); }
/** * Entry point for the thread. */ int AudioOutputDeviceAlsa::Main() { while (true) { // let all connected engines render 'FragmentSize' sample points RenderAudio(FragmentSize); // convert from DSP value range (-1.0..+1.0) to 16 bit integer value // range (-32768..+32767), check clipping and copy to Alsa output buffer // (note: we use interleaved output method to Alsa) for (int c = 0; c < uiAlsaChannels; c++) { float* in = Channels[c]->Buffer(); for (int i = 0, o = c; i < FragmentSize; i++ , o += uiAlsaChannels) { float sample_point = in[i] * 32768.0f; if (sample_point < -32768.0) sample_point = -32768.0; if (sample_point > 32767.0) sample_point = 32767.0; pAlsaOutputBuffer[o] = (int16_t) sample_point; } } // output sound int res = Output(); if (res < 0) { fprintf(stderr, "Alsa: Audio output error, exiting.\n"); exit(EXIT_FAILURE); } } // just to suppress compiler warning return EXIT_FAILURE; }
/** * This method should not be called directly! It will be called by * libjack to demand transmission of further sample points. */ int AudioOutputDeviceJack::Process(uint Samples) { if (csIsPlaying.Pop()) { // let all connected engines render 'Samples' sample points return RenderAudio(Samples); } else { // playback stop by zeroing output buffer(s) and not calling connected sampler engines to render audio return RenderSilence(Samples); } }
DWORD CWASAPIRenderFilter::ThreadProc() { Log("CWASAPIRenderFilter::Render thread - starting up - thread ID: %d", m_ThreadId); SetThreadName(0, "WASAPI-renderer"); // Polling delay LARGE_INTEGER liDueTime; liDueTime.QuadPart = -1LL; AudioSinkCommand command; LONGLONG writeSilence = 0; BYTE* sampleData = NULL; bool flush = false; bool sampleProcessed = false; REFERENCE_TIME dueTime = 0; REFERENCE_TIME maxSampleWaitTime = Latency() / 20000; HRESULT hr = S_FALSE; m_csResources.Lock(); if (m_pSettings->m_bReleaseDeviceOnStop && !m_pAudioClient && m_pInputFormat) { hr = CreateAudioClient(true); if (FAILED(hr)) { Log("CWASAPIRenderFilter::Render thread Error, audio client not available: (0x%08x)", hr); StopRenderThread(); m_csResources.Unlock(); return 0; } } if (m_pAudioClient) { hr = StartAudioClient(); if (FAILED(hr)) { Log("CWASAPIRenderFilter::Render thread Error, starting audio client failed: (0x%08x)", hr); StopRenderThread(); m_csResources.Unlock(); return 0; } } if (!m_bDeviceInitialized) { Log("CWASAPIRenderFilter::Render thread Error, device not initialized"); StopRenderThread(); m_csResources.Unlock(); return 0; } EnableMMCSS(); m_state = StateRunning; while (true) { if (flush) { Log("CWASAPIRenderFilter::Render thread flushing buffers"); HandleFlush(); flush = false; } m_csResources.Unlock(); hr = WaitForEvents(INFINITE, &m_hDataEvents, &m_dwDataWaitObjects); m_csResources.Lock(); if (hr == MPAR_S_THREAD_STOPPING || !m_pAudioClient) { StopRenderThread(); return 0; } else if (hr == MPAR_S_NEED_DATA) { UpdateAudioClock(); UINT32 bytesFilled = 0; UINT32 bufferSize = 0; UINT32 currentPadding = 0; UINT32 bufferSizeInBytes = 0; BYTE* data = NULL; DWORD flags = 0; static BYTE* prevData = NULL; hr = GetWASAPIBuffer(bufferSize, currentPadding, bufferSizeInBytes, &data); if (SUCCEEDED(hr)) { do { fetchSample: bool OOBCommandOnly = m_nDataLeftInSample > 0; if (m_nDataLeftInSample == 0 || OOBCommandOnly) { m_csResources.Unlock(); HRESULT result = GetNextSampleOrCommand(&command, &m_pCurrentSample.p, maxSampleWaitTime, &m_hSampleEvents, &m_dwSampleWaitObjects, OOBCommandOnly); m_csResources.Lock(); if (result == MPAR_S_THREAD_STOPPING || !m_pAudioClient) { if (m_pAudioClient) { hr = m_pRenderClient->ReleaseBuffer(bufferSize - currentPadding, flags); if (FAILED(hr) && hr != AUDCLNT_E_OUT_OF_ORDER) Log("CWASAPIRenderFilter::Render thread: ReleaseBuffer failed (0x%08x)", hr); } StopRenderThread(); return 0; } if (!m_pCurrentSample) m_nDataLeftInSample = 0; if (command == ASC_PutSample && m_pCurrentSample) { sampleProcessed = false; m_nSampleOffset = 0; m_nDataLeftInSample = m_pCurrentSample->GetActualDataLength(); } else if (command == ASC_Flush) { m_pCurrentSample.Release(); flush = true; sampleData = NULL; m_nSampleOffset = 0; m_nDataLeftInSample = 0; break; } else if (command == ASC_Pause) { m_pCurrentSample.Release(); m_state = StatePaused; } else if (command == ASC_Resume) { sampleProcessed = false; writeSilence = 0; m_state = StateRunning; if (!m_pCurrentSample) { m_nDataLeftInSample = 0; goto fetchSample; } } } if (m_state != StateRunning) writeSilence = bufferSizeInBytes - bytesFilled; else if (m_nSampleOffset == 0 && !OOBCommandOnly) { // TODO error checking if (CheckSample(m_pCurrentSample, bufferSize - currentPadding) == S_FALSE) { GetWASAPIBuffer(bufferSize, currentPadding, bufferSizeInBytes, &data); bytesFilled = 0; } } if (writeSilence == 0 && (m_nSampleOffset == 0 || m_nSampleNum == 0) && !sampleProcessed) { HRESULT schedulingHR = CheckStreamTimeline(m_pCurrentSample, &dueTime, m_nSampleOffset); sampleProcessed = true; // m_pCurrentSample must exist if CheckStreamTimeline returns either of these if (schedulingHR == MPAR_S_DROP_SAMPLE) { m_pCurrentSample.Release(); m_nDataLeftInSample = 0; goto fetchSample; } else if (schedulingHR == MPAR_S_WAIT_RENDER_TIME) CalculateSilence(&dueTime, &writeSilence); } if (writeSilence == 0 && m_pCurrentSample) RenderAudio(data, bufferSizeInBytes, m_nDataLeftInSample, m_nSampleOffset, m_pCurrentSample, bytesFilled); else { if (bufferSizeInBytes == writeSilence) flags = AUDCLNT_BUFFERFLAGS_SILENT; if (!m_pCurrentSample) writeSilence = bufferSizeInBytes; RenderSilence(data, bufferSizeInBytes, writeSilence, bytesFilled); } } while (bytesFilled < bufferSizeInBytes); hr = m_pRenderClient->ReleaseBuffer(bufferSize - currentPadding, flags); if (FAILED(hr) && hr != AUDCLNT_E_OUT_OF_ORDER) Log("CWASAPIRenderFilter::Render thread: ReleaseBuffer failed (0x%08x)", hr); } if (!m_pSettings->m_bWASAPIUseEventMode) { if (m_pAudioClient) hr = m_pAudioClient->GetCurrentPadding(¤tPadding); else hr = S_FALSE; if (SUCCEEDED(hr) && bufferSize > 0) { liDueTime.QuadPart = (double)currentPadding / (double)bufferSize * (double)m_pSettings->m_hnsPeriod * -0.9; // Log(" currentPadding: %d QuadPart: %lld", currentPadding, liDueTime.QuadPart); } else { liDueTime.QuadPart = (double)m_pSettings->m_hnsPeriod * -0.9; if (hr != AUDCLNT_E_NOT_INITIALIZED) Log("CWASAPIRenderFilter::Render thread: GetCurrentPadding failed (0x%08x)", hr); } SetWaitableTimer(m_hDataEvent, &liDueTime, 0, NULL, NULL, 0); } } } m_csResources.Unlock(); return 0; }