134 lines
4.7 KiB
C++
134 lines
4.7 KiB
C++
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
|
|
|
#include <kernel/types/KProcess.h>
|
|
#include <audio/downmixer.h>
|
|
#include "voice.h"
|
|
|
|
namespace skyline::service::audio::IAudioRenderer {
|
|
void Voice::SetWaveBufferIndex(u8 index) {
|
|
bufferIndex = 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 && input.format != skyline::audio::AudioFormat::ADPCM)
|
|
throw exception("Unsupported voice PCM format: {}", input.format);
|
|
|
|
format = input.format;
|
|
sampleRate = input.sampleRate;
|
|
|
|
if (input.channelCount > (input.format == skyline::audio::AudioFormat::ADPCM ? 1 : 6))
|
|
throw exception("Unsupported voice channel count: {}", input.channelCount);
|
|
|
|
channelCount = static_cast<u8>(input.channelCount);
|
|
|
|
if (input.format == skyline::audio::AudioFormat::ADPCM) {
|
|
std::vector<std::array<i16, 2>> adpcmCoefficients(input.adpcmCoeffsSize / (sizeof(u16) * 2));
|
|
span(adpcmCoefficients).copy_from(span(input.adpcmCoeffs, input.adpcmCoeffsSize / sizeof(u32)));
|
|
|
|
adpcmDecoder = skyline::audio::AdpcmDecoder(std::move(adpcmCoefficients));
|
|
}
|
|
|
|
SetWaveBufferIndex(static_cast<u8>(input.baseWaveBufferIndex));
|
|
}
|
|
|
|
waveBuffers = input.waveBuffers;
|
|
volume = input.volume;
|
|
playbackState = input.playbackState;
|
|
}
|
|
|
|
void Voice::UpdateBuffers() {
|
|
const auto ¤tBuffer{waveBuffers.at(bufferIndex)};
|
|
span buffer(currentBuffer.pointer, currentBuffer.size);
|
|
|
|
if (currentBuffer.size == 0)
|
|
return;
|
|
|
|
switch (format) {
|
|
case skyline::audio::AudioFormat::Int16:
|
|
samples.resize(currentBuffer.size / sizeof(i16));
|
|
span(samples).copy_from(buffer);
|
|
break;
|
|
case skyline::audio::AudioFormat::ADPCM: {
|
|
samples = adpcmDecoder->Decode(buffer);
|
|
break;
|
|
}
|
|
default:
|
|
throw exception("Unsupported PCM format used by Voice: {}", format);
|
|
}
|
|
|
|
if (sampleRate != constant::SampleRate)
|
|
samples = resampler.ResampleBuffer(samples, static_cast<double>(sampleRate) / constant::SampleRate, channelCount);
|
|
|
|
if (channelCount == 1 && constant::StereoChannelCount != channelCount) {
|
|
auto originalSize{samples.size()};
|
|
samples.resize((originalSize / channelCount) * constant::StereoChannelCount);
|
|
|
|
for (auto monoIndex{originalSize - 1}, targetIndex{samples.size()}; monoIndex > 0; monoIndex--) {
|
|
auto sample{samples[monoIndex]};
|
|
for (u8 i{}; i < constant::StereoChannelCount; i++)
|
|
samples[--targetIndex] = sample;
|
|
}
|
|
}
|
|
|
|
if (channelCount == constant::SurroundChannelCount) {
|
|
span(samples).copy_from(span(skyline::audio::DownMix(span(samples).cast<skyline::audio::Surround51Sample>())));
|
|
samples.resize((samples.size() / constant::SurroundChannelCount) * constant::StereoChannelCount);
|
|
}
|
|
}
|
|
|
|
std::vector<i16> &Voice::GetBufferData(u32 maxSamples, u32 &outOffset, u32 &outSize) {
|
|
auto ¤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::StereoChannelCount, static_cast<u32>(samples.size() - sampleOffset));
|
|
|
|
output.playedSamplesCount += outSize / constant::StereoChannelCount;
|
|
sampleOffset += outSize;
|
|
|
|
if (sampleOffset == samples.size()) {
|
|
sampleOffset = 0;
|
|
|
|
if (currentBuffer.lastBuffer)
|
|
playbackState = skyline::audio::AudioOutState::Paused;
|
|
|
|
if (!currentBuffer.looping)
|
|
SetWaveBufferIndex(static_cast<u8>(bufferIndex + 1));
|
|
|
|
output.playedWaveBuffersCount++;
|
|
}
|
|
|
|
return samples;
|
|
}
|
|
}
|