// Submits any buffers that have currently been queued, // assuming they are needed based on current queue depth void sound_xaudio2::submit_needed() { XAUDIO2_VOICE_STATE state; m_sourceVoice->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED); std::lock_guard<std::mutex> lock(m_buffer_lock); // If we have buffers queued into XAudio and our current in-memory buffer // isn't yet full, there's no need to submit it if (state.BuffersQueued >= 1 && m_queue.empty()) return; // We do however want to achieve some kind of minimal latency, so if the queued buffers // are greater than 2, flush them to re-sync the audio if (state.BuffersQueued > 2) { m_sourceVoice->FlushSourceBuffers(); m_overflows++; } // Roll the buffer roll_buffer(); // Submit the next buffer submit_next_queued(); }
int sound_xaudio2::init(osd_options const &options) { HRESULT result; WAVEFORMATEX format = {0}; auto init_start = std::chrono::system_clock::now(); std::chrono::milliseconds init_time; CoInitializeEx(nullptr, COINIT_MULTITHREADED); // Make sure our XAudio2Create entrypoint is bound if (!XAudio2Create) { osd_printf_error("Could not find XAudio2. Please try to reinstall DirectX runtime package.\n"); return 1; } // Create the IXAudio2 object HR_GOERR(this->XAudio2Create(m_xAudio2.GetAddressOf(), 0, XAUDIO2_DEFAULT_PROCESSOR)); // make a format description for what we want format.wBitsPerSample = 16; format.wFormatTag = WAVE_FORMAT_PCM; format.nChannels = 2; format.nSamplesPerSec = sample_rate(); format.nBlockAlign = format.wBitsPerSample * format.nChannels / 8; format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; m_sample_bytes = format.nBlockAlign; // Create the buffers create_buffers(format); // Initialize our events m_hEventBufferCompleted = CreateEvent(nullptr, FALSE, FALSE, nullptr); m_hEventDataAvailable = CreateEvent(nullptr, FALSE, FALSE, nullptr); m_hEventExiting = CreateEvent(nullptr, FALSE, FALSE, nullptr); // create the voices and start them HR_GOERR(create_voices(format)); HR_GOERR(m_sourceVoice->Start()); // Start the thread listening m_audioThread = std::thread([](sound_xaudio2* self) { self->process_audio(); }, this); init_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - init_start); osd_printf_verbose("Sound: XAudio2 initialized. %d ms.\n", static_cast<int>(init_time.count())); m_initialized = TRUE; return 0; Error: this->exit(); return 1; }
int sound_xaudio2::init(osd_options const &options) { HRESULT result; // Make sure our XAudio2Create entrypoint is bound int status = XAudio2Create.initialize(); if (status != 0) { osd_printf_error("Could not find XAudio2 library\n"); return 1; } // Create the IXAudio2 object IXAudio2 *temp_xaudio2 = nullptr; HR_RET1(this->XAudio2Create(&temp_xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR)); m_xAudio2 = xaudio2_ptr(temp_xaudio2); // make a format description for what we want WAVEFORMATEX format = { 0 }; format.wBitsPerSample = 16; format.wFormatTag = WAVE_FORMAT_PCM; format.nChannels = 2; format.nSamplesPerSec = sample_rate(); format.nBlockAlign = format.wBitsPerSample * format.nChannels / 8; format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; m_sample_bytes = format.nBlockAlign; // Create the buffers create_buffers(format); // Initialize our events m_hEventBufferCompleted = CreateEvent(nullptr, FALSE, FALSE, nullptr); m_hEventDataAvailable = CreateEvent(nullptr, FALSE, FALSE, nullptr); m_hEventExiting = CreateEvent(nullptr, FALSE, FALSE, nullptr); // create the voices and start them HR_RET1(create_voices(format)); HR_RET1(m_sourceVoice->Start()); // Start the thread listening m_audioThread = std::thread([](sound_xaudio2* self) { self->process_audio(); }, this); osd_printf_verbose("Sound: XAudio2 initialized\n"); return 0; }
// Submits any buffers that have currently been queued, // assuming they are needed based on current queue depth void sound_xaudio2::submit_needed() { XAUDIO2_VOICE_STATE state; m_sourceVoice->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED); // If we have a buffer on the queue, no reason to submit if (state.BuffersQueued >= 1) return; std::lock_guard<std::mutex> lock(m_buffer_lock); // Roll the buffer roll_buffer(); // Submit the next buffer submit_next_queued(); }
void sound_xaudio2::set_mastervolume(int attenuation) { assert(m_sourceVoice); HRESULT result; // clamp the attenuation to 0-32 range attenuation = MAX(MIN(attenuation, 0), -32); // Ranges from 1.0 to XAUDIO2_MAX_VOLUME_LEVEL indicate additional gain // Ranges from 0 to 1.0 indicate a reduced volume level // 0 indicates silence // We only support a reduction from 1.0, so we generate values in the range 0.0 to 1.0 float scaledVolume = (32.0f + attenuation) / 32.0f; // set the master volume HR_RETV(m_sourceVoice->SetVolume(scaledVolume)); }
void sound_xaudio2::exit() { // Wait on processing thread to end SetEvent(m_hEventExiting); m_audioThread.join(); CloseHandle(m_hEventBufferCompleted); CloseHandle(m_hEventDataAvailable); CloseHandle(m_hEventExiting); m_sourceVoice.reset(); m_masterVoice.reset(); m_xAudio2.reset(); m_buffer.reset(); m_buffer_pool.reset(); if (m_overflows != 0 || m_underflows != 0) osd_printf_verbose("Sound: overflows=%u, underflows=%u\n", m_overflows, m_underflows); osd_printf_verbose("Sound: XAudio2 deinitialized\n"); }
void sound_xaudio2::submit_buffer(std::unique_ptr<BYTE[]> audioData, DWORD audioLength) const { assert(audioLength != 0); XAUDIO2_BUFFER buf = { 0 }; buf.AudioBytes = audioLength; buf.pAudioData = audioData.get(); buf.PlayBegin = 0; buf.PlayLength = audioLength / m_sample_bytes; buf.Flags = XAUDIO2_END_OF_STREAM; buf.pContext = audioData.get(); HRESULT result; if (FAILED(result = m_sourceVoice->SubmitSourceBuffer(&buf))) { osd_printf_verbose("Sound: XAudio2 failed to submit source buffer (non-fatal). Error: 0x%X\n", static_cast<unsigned int>(result)); m_buffer_pool->return_to_pool(audioData.release()); return; } // If we succeeded, relinquish the buffer allocation to the XAudio2 runtime // The buffer will be freed on the OnBufferCompleted callback audioData.release(); }