diff --git a/app/src/main/cpp/skyline/audio.cpp b/app/src/main/cpp/skyline/audio.cpp index 073bc098..c559f421 100644 --- a/app/src/main/cpp/skyline/audio.cpp +++ b/app/src/main/cpp/skyline/audio.cpp @@ -5,7 +5,7 @@ namespace skyline::audio { Audio::Audio(const DeviceState &state) : oboe::AudioStreamCallback() { - builder.setChannelCount(constant::ChannelCount); + builder.setChannelCount(constant::StereoChannelCount); builder.setSampleRate(constant::SampleRate); builder.setFormat(constant::PcmFormat); builder.setFramesPerCallback(constant::MixBufferSize); diff --git a/app/src/main/cpp/skyline/audio/common.h b/app/src/main/cpp/skyline/audio/common.h index 1fbd487d..82266f51 100644 --- a/app/src/main/cpp/skyline/audio/common.h +++ b/app/src/main/cpp/skyline/audio/common.h @@ -9,9 +9,10 @@ namespace skyline { namespace constant { constexpr u16 SampleRate{48000}; //!< The common sampling rate to use for audio output - constexpr u8 ChannelCount{2}; //!< The common amount of channels to use for audio output - constexpr u16 MixBufferSize{960}; //!< The size of the mix buffer by default - constexpr auto PcmFormat{oboe::AudioFormat::I16}; //!< The common PCM data format to use for audio output + constexpr u8 StereoChannelCount{2}; //!< Channels to use for stereo audio output + constexpr u8 SurroundChannelCount{6}; //!< Channels to use for surround audio output (downsampled by backend) + constexpr u16 MixBufferSize{960}; //!< Default size of the audren mix buffer + constexpr auto PcmFormat{oboe::AudioFormat::I16}; //!< PCM data format to use for audio output } namespace audio { diff --git a/app/src/main/cpp/skyline/audio/downmixer.h b/app/src/main/cpp/skyline/audio/downmixer.h new file mode 100644 index 00000000..ab09ffd3 --- /dev/null +++ b/app/src/main/cpp/skyline/audio/downmixer.h @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include "common.h" + +namespace skyline::audio { + /** + * @brief Format of a 5.1 channel audio sample + */ + struct Surround51Sample { + i16 frontLeft; + i16 frontRight; + i16 centre; + i16 lowFrequency; + i16 backLeft; + i16 backRight; + }; + + /** + * @brief Format of a stereo audio sample + */ + struct StereoSample { + i16 left; + i16 right; + }; + + /** + * @brief Downmixes a buffer of 5.1 surround audio to stereo + */ + std::vector DownMix(span surroundSamples) { + constexpr i16 FixedPointMultiplier{1000}; //!< Avoids using floating point maths + constexpr i16 Attenuation3Db{707}; //! 10^(-3/20) + constexpr i16 Attenuation6Db{501}; //! 10^(-6/20) + constexpr i16 Attenuation12Db{251}; //! 10^(-6/20) + + auto downmixChannel{[](i32 front, i32 centre, i32 lowFrequency, i32 back) { + return static_cast(front + + (centre * Attenuation3Db + + lowFrequency * Attenuation12Db + + back * Attenuation6Db) / FixedPointMultiplier); + }}; + + std::vector stereoSamples(surroundSamples.size()); + for (size_t i{}; i < surroundSamples.size(); i++) { + auto &surroundSample = surroundSamples[i]; + auto &stereoSample = stereoSamples[i]; + + stereoSample.left = downmixChannel(surroundSample.frontLeft, surroundSample.centre, surroundSample.lowFrequency, surroundSample.backLeft); + stereoSample.right = downmixChannel(surroundSample.frontRight, surroundSample.centre, surroundSample.lowFrequency, surroundSample.backRight); + } + + return stereoSamples; + } +} diff --git a/app/src/main/cpp/skyline/audio/track.cpp b/app/src/main/cpp/skyline/audio/track.cpp index 8f694cda..54959df8 100644 --- a/app/src/main/cpp/skyline/audio/track.cpp +++ b/app/src/main/cpp/skyline/audio/track.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +#include "downmixer.h" #include "track.h" namespace skyline::audio { @@ -11,7 +12,7 @@ namespace skyline::audio { if (sampleRate != constant::SampleRate) throw exception("Unsupported audio sample rate: {}", sampleRate); - if (channelCount != constant::ChannelCount) + if (channelCount != constant::StereoChannelCount && channelCount != constant::SurroundChannelCount) throw exception("Unsupported quantity of audio channels: {}", channelCount); } @@ -64,7 +65,13 @@ namespace skyline::audio { }; identifiers.push_front(identifier); - samples.Append(buffer); + + if (channelCount == constant::SurroundChannelCount) { + auto stereoBuffer{DownMix(buffer.cast())}; + samples.Append(span(stereoBuffer).cast()); + } else { + samples.Append(buffer); + } } void AudioTrack::CheckReleasedBuffers() { diff --git a/app/src/main/cpp/skyline/audio/track.h b/app/src/main/cpp/skyline/audio/track.h index 9bdabb19..791a3c84 100644 --- a/app/src/main/cpp/skyline/audio/track.h +++ b/app/src/main/cpp/skyline/audio/track.h @@ -20,7 +20,7 @@ namespace skyline::audio { u32 sampleRate; public: - CircularBuffer samples; //!< A circular buffer with all appended audio samples + CircularBuffer samples; //!< A circular buffer with all appended audio samples std::mutex bufferLock; //!< Synchronizes appending to audio buffers AudioOutState playbackState{AudioOutState::Stopped}; //!< The current state of playback diff --git a/app/src/main/cpp/skyline/services/audio/IAudioDevice.cpp b/app/src/main/cpp/skyline/services/audio/IAudioDevice.cpp index be3f842f..ff44f845 100644 --- a/app/src/main/cpp/skyline/services/audio/IAudioDevice.cpp +++ b/app/src/main/cpp/skyline/services/audio/IAudioDevice.cpp @@ -41,7 +41,7 @@ namespace skyline::service::audio { } Result IAudioDevice::GetActiveChannelCount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - response.Push(constant::ChannelCount); + response.Push(constant::StereoChannelCount); return {}; } } diff --git a/app/src/main/cpp/skyline/services/audio/IAudioOutManager.cpp b/app/src/main/cpp/skyline/services/audio/IAudioOutManager.cpp index b6acd711..f5bb064c 100644 --- a/app/src/main/cpp/skyline/services/audio/IAudioOutManager.cpp +++ b/app/src/main/cpp/skyline/services/audio/IAudioOutManager.cpp @@ -14,21 +14,27 @@ namespace skyline::service::audio { } Result IAudioOutManager::OpenAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto sampleRate{request.Pop()}; - auto channelCount{static_cast(request.Pop())}; + struct AudioInputParams { + u32 sampleRate; + u16 channelCount; + u16 _pad_; + }; + auto &inputParams{request.Pop()}; - Logger::Debug("Opening an IAudioOut with sample rate: {}, channel count: {}", sampleRate, channelCount); - - sampleRate = sampleRate ? sampleRate : constant::SampleRate; - channelCount = channelCount ? channelCount : constant::ChannelCount; - manager.RegisterService(std::make_shared(state, manager, channelCount, sampleRate), session, response); - - response.Push(sampleRate); - response.Push(channelCount); - response.Push(0); + inputParams.sampleRate = inputParams.sampleRate ? inputParams.sampleRate : constant::SampleRate; + inputParams.channelCount = inputParams.channelCount <= constant::StereoChannelCount ? constant::StereoChannelCount : constant::SurroundChannelCount; + inputParams._pad_ = 0; + Logger::Debug("Opening an IAudioOut with sample rate: {}, channel count: {}", inputParams.sampleRate, inputParams.channelCount); + manager.RegisterService(std::make_shared(state, manager, inputParams.channelCount, inputParams.sampleRate), session, response); + response.Push(inputParams); response.Push(static_cast(skyline::audio::AudioFormat::Int16)); response.Push(static_cast(skyline::audio::AudioOutState::Stopped)); + if (request.inputBuf.at(0).empty() || !request.inputBuf.at(0)[0]) + request.outputBuf.at(0).copy_from(constant::DefaultAudioOutName); + else + request.outputBuf.at(0).copy_from(request.inputBuf.at(0)); + return {}; } } diff --git a/app/src/main/cpp/skyline/services/audio/IAudioRenderer/IAudioRenderer.cpp b/app/src/main/cpp/skyline/services/audio/IAudioRenderer/IAudioRenderer.cpp index 0e01566d..d446f4f2 100644 --- a/app/src/main/cpp/skyline/services/audio/IAudioRenderer/IAudioRenderer.cpp +++ b/app/src/main/cpp/skyline/services/audio/IAudioRenderer/IAudioRenderer.cpp @@ -7,7 +7,7 @@ namespace skyline::service::audio::IAudioRenderer { IAudioRenderer::IAudioRenderer(const DeviceState &state, ServiceManager &manager, AudioRendererParameters ¶meters) : systemEvent(std::make_shared(state, true)), parameters(parameters), BaseService(state, manager) { - track = state.audio->OpenTrack(constant::ChannelCount, constant::SampleRate, [&]() { systemEvent->Signal(); }); + track = state.audio->OpenTrack(constant::StereoChannelCount, constant::SampleRate, [&]() { systemEvent->Signal(); }); track->Start(); memoryPools.resize(parameters.effectCount + parameters.voiceCount * 4); @@ -144,7 +144,7 @@ namespace skyline::service::audio::IAudioRenderer { if (voiceBufferSize == 0) break; - pendingSamples -= voiceBufferSize / constant::ChannelCount; + pendingSamples -= voiceBufferSize / constant::StereoChannelCount; for (auto index{voiceBufferOffset}; index < voiceBufferOffset + voiceBufferSize; index++) { if (writtenSamples == bufferOffset) { diff --git a/app/src/main/cpp/skyline/services/audio/IAudioRenderer/IAudioRenderer.h b/app/src/main/cpp/skyline/services/audio/IAudioRenderer/IAudioRenderer.h index f9f2d539..ba5a2905 100644 --- a/app/src/main/cpp/skyline/services/audio/IAudioRenderer/IAudioRenderer.h +++ b/app/src/main/cpp/skyline/services/audio/IAudioRenderer/IAudioRenderer.h @@ -69,7 +69,7 @@ namespace skyline { std::vector memoryPools; std::vector effects; std::vector voices; - std::array sampleBuffer{}; //!< The final output data that is appended to the stream + std::array sampleBuffer{}; //!< The final output data that is appended to the stream skyline::audio::AudioOutState playbackState{skyline::audio::AudioOutState::Stopped}; /** diff --git a/app/src/main/cpp/skyline/services/audio/IAudioRenderer/voice.cpp b/app/src/main/cpp/skyline/services/audio/IAudioRenderer/voice.cpp index 52db99d7..a4431067 100644 --- a/app/src/main/cpp/skyline/services/audio/IAudioRenderer/voice.cpp +++ b/app/src/main/cpp/skyline/services/audio/IAudioRenderer/voice.cpp @@ -79,13 +79,13 @@ namespace skyline::service::audio::IAudioRenderer { if (sampleRate != constant::SampleRate) samples = resampler.ResampleBuffer(samples, static_cast(sampleRate) / constant::SampleRate, channelCount); - if (channelCount == 1 && constant::ChannelCount != channelCount) { + if (channelCount == 1 && constant::StereoChannelCount != channelCount) { auto originalSize{samples.size()}; - samples.resize((originalSize / channelCount) * constant::ChannelCount); + 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::ChannelCount; i++) + for (u8 i{}; i < constant::StereoChannelCount; i++) samples[--targetIndex] = sample; } } @@ -105,9 +105,9 @@ namespace skyline::service::audio::IAudioRenderer { } outOffset = sampleOffset; - outSize = std::min(maxSamples * constant::ChannelCount, static_cast(samples.size() - sampleOffset)); + outSize = std::min(maxSamples * constant::StereoChannelCount, static_cast(samples.size() - sampleOffset)); - output.playedSamplesCount += outSize / constant::ChannelCount; + output.playedSamplesCount += outSize / constant::StereoChannelCount; sampleOffset += outSize; if (sampleOffset == samples.size()) {