// SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include #include "voice.h" namespace skyline::service::audio::IAudioRenderer { void Voice::SetWaveBufferIndex(u8 index) { bufferIndex = static_cast(index & 3); bufferReload = true; } Voice::Voice(const DeviceState &state) : state(state) {} void Voice::ProcessInput(const VoiceIn &input) { // Voice no longer in use, reset it if (acquired && !input.acquired) { bufferReload = true; bufferIndex = 0; sampleOffset = 0; output.playedSamplesCount = 0; output.playedWaveBuffersCount = 0; output.voiceDropsCount = 0; } acquired = input.acquired; if (!acquired) return; if (input.firstUpdate) { if (input.format != skyline::audio::AudioFormat::Int16) throw exception("Unsupported voice PCM format: {}", input.format); format = input.format; sampleRate = input.sampleRate; if (input.channelCount > 2) throw exception("Unsupported voice channel count: {}", input.channelCount); channelCount = static_cast(input.channelCount); SetWaveBufferIndex(static_cast(input.baseWaveBufferIndex)); } waveBuffers = input.waveBuffers; volume = input.volume; playbackState = input.playbackState; } void Voice::UpdateBuffers() { const auto ¤tBuffer = waveBuffers.at(bufferIndex); if (currentBuffer.size == 0) return; switch (format) { case skyline::audio::AudioFormat::Int16: samples.resize(currentBuffer.size / sizeof(i16)); state.process->ReadMemory(samples.data(), currentBuffer.address, currentBuffer.size); break; default: throw exception("Unsupported PCM format used by Voice: {}", format); } if (sampleRate != constant::SampleRate) samples = resampler.ResampleBuffer(samples, static_cast(sampleRate) / constant::SampleRate, channelCount); if (channelCount == 1 && constant::ChannelCount != channelCount) { auto originalSize = samples.size(); samples.resize((originalSize / channelCount) * constant::ChannelCount); for (auto monoIndex = originalSize - 1, targetIndex = samples.size(); monoIndex > 0; monoIndex--) { auto sample = samples[monoIndex]; for (auto i = 0; i < constant::ChannelCount; i++) samples[--targetIndex] = sample; } } } std::vector &Voice::GetBufferData(u32 maxSamples, u32 &outOffset, u32 &outSize) { WaveBuffer ¤tBuffer = waveBuffers.at(bufferIndex); if (!acquired || playbackState != skyline::audio::AudioOutState::Started) { outSize = 0; return samples; } if (bufferReload) { bufferReload = false; UpdateBuffers(); } outOffset = sampleOffset; outSize = std::min(maxSamples * constant::ChannelCount, static_cast(samples.size() - sampleOffset)); output.playedSamplesCount += outSize / constant::ChannelCount; sampleOffset += outSize; if (sampleOffset == samples.size()) { sampleOffset = 0; if (currentBuffer.lastBuffer) playbackState = skyline::audio::AudioOutState::Paused; if (!currentBuffer.looping) SetWaveBufferIndex(static_cast(bufferIndex + 1)); output.playedWaveBuffersCount++; } return samples; } }