#include "sdl.h" #include #include SdlAudioRenderer::SdlAudioRenderer() : m_AudioDevice(0), m_AudioBuffer(nullptr) { SDL_assert(!SDL_WasInit(SDL_INIT_AUDIO)); if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_InitSubSystem(SDL_INIT_AUDIO) failed: %s", SDL_GetError()); SDL_assert(SDL_WasInit(SDL_INIT_AUDIO)); } } bool SdlAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig) { SDL_AudioSpec want, have; SDL_zero(want); want.freq = opusConfig->sampleRate; want.format = AUDIO_S16; want.channels = opusConfig->channelCount; // This is supposed to be a power of 2, but our // frames contain a non-power of 2 number of samples, // so the slop would require buffering another full frame. // Specifying non-Po2 seems to work for our supported platforms. want.samples = opusConfig->samplesPerFrame; m_FrameSize = opusConfig->samplesPerFrame * sizeof(short) * opusConfig->channelCount; m_AudioDevice = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0); if (m_AudioDevice == 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open audio device: %s", SDL_GetError()); return false; } m_AudioBuffer = SDL_malloc(m_FrameSize); if (m_AudioBuffer == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to allocate audio buffer"); return false; } SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Desired audio buffer: %u samples (%u bytes)", want.samples, want.samples * (Uint32)sizeof(short) * want.channels); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Obtained audio buffer: %u samples (%u bytes)", have.samples, have.size); // Start playback SDL_PauseAudioDevice(m_AudioDevice, 0); return true; } SdlAudioRenderer::~SdlAudioRenderer() { if (m_AudioDevice != 0) { // Stop playback SDL_PauseAudioDevice(m_AudioDevice, 1); SDL_CloseAudioDevice(m_AudioDevice); } if (m_AudioBuffer != nullptr) { SDL_free(m_AudioBuffer); } SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_assert(!SDL_WasInit(SDL_INIT_AUDIO)); } void* SdlAudioRenderer::getAudioBuffer(int*) { return m_AudioBuffer; } bool SdlAudioRenderer::submitAudio(int bytesWritten) { if (bytesWritten == 0) { // Nothing to do return true; } // Don't queue if there's already more than 30 ms of audio data waiting // in Moonlight's audio queue. if (LiGetPendingAudioDuration() > 30) { return true; } // Provide backpressure on the queue to ensure too many frames don't build up // in SDL's audio queue. while (SDL_GetQueuedAudioSize(m_AudioDevice) / m_FrameSize > 10) { SDL_Delay(1); } if (SDL_QueueAudio(m_AudioDevice, m_AudioBuffer, bytesWritten) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to queue audio sample: %s", SDL_GetError()); } return true; } int SdlAudioRenderer::getCapabilities() { // Direct submit can't be used because we use LiGetPendingAudioDuration() return CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION; }