mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-10 19:05:08 +01:00
Add 6 channel downmixing support for Audout
The specific attenuations used for each channel are taken from Ryujinx.
This commit is contained in:
parent
2e1a1a965d
commit
78238d550a
@ -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);
|
||||
|
@ -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 {
|
||||
|
57
app/src/main/cpp/skyline/audio/downmixer.h
Normal file
57
app/src/main/cpp/skyline/audio/downmixer.h
Normal file
@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
#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<StereoSample> DownMix(span<Surround51Sample> 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<i16>(front +
|
||||
(centre * Attenuation3Db +
|
||||
lowFrequency * Attenuation12Db +
|
||||
back * Attenuation6Db) / FixedPointMultiplier);
|
||||
}};
|
||||
|
||||
std::vector<StereoSample> 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;
|
||||
}
|
||||
}
|
@ -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<Surround51Sample>())};
|
||||
samples.Append(span(stereoBuffer).cast<i16>());
|
||||
} else {
|
||||
samples.Append(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioTrack::CheckReleasedBuffers() {
|
||||
|
@ -20,7 +20,7 @@ namespace skyline::audio {
|
||||
u32 sampleRate;
|
||||
|
||||
public:
|
||||
CircularBuffer<i16, constant::SampleRate * constant::ChannelCount * 10> samples; //!< A circular buffer with all appended audio samples
|
||||
CircularBuffer<i16, constant::SampleRate * constant::StereoChannelCount * 10> 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
|
||||
|
@ -41,7 +41,7 @@ namespace skyline::service::audio {
|
||||
}
|
||||
|
||||
Result IAudioDevice::GetActiveChannelCount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push<u32>(constant::ChannelCount);
|
||||
response.Push<u32>(constant::StereoChannelCount);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
@ -14,21 +14,27 @@ namespace skyline::service::audio {
|
||||
}
|
||||
|
||||
Result IAudioOutManager::OpenAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto sampleRate{request.Pop<u32>()};
|
||||
auto channelCount{static_cast<u16>(request.Pop<u32>())};
|
||||
struct AudioInputParams {
|
||||
u32 sampleRate;
|
||||
u16 channelCount;
|
||||
u16 _pad_;
|
||||
};
|
||||
auto &inputParams{request.Pop<AudioInputParams>()};
|
||||
|
||||
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<IAudioOut>(state, manager, channelCount, sampleRate), session, response);
|
||||
|
||||
response.Push<u32>(sampleRate);
|
||||
response.Push<u16>(channelCount);
|
||||
response.Push<u16>(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<IAudioOut>(state, manager, inputParams.channelCount, inputParams.sampleRate), session, response);
|
||||
response.Push<AudioInputParams>(inputParams);
|
||||
response.Push(static_cast<u32>(skyline::audio::AudioFormat::Int16));
|
||||
response.Push(static_cast<u32>(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 {};
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
namespace skyline::service::audio::IAudioRenderer {
|
||||
IAudioRenderer::IAudioRenderer(const DeviceState &state, ServiceManager &manager, AudioRendererParameters ¶meters)
|
||||
: systemEvent(std::make_shared<type::KEvent>(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) {
|
||||
|
@ -69,7 +69,7 @@ namespace skyline {
|
||||
std::vector<MemoryPool> memoryPools;
|
||||
std::vector<Effect> effects;
|
||||
std::vector<Voice> voices;
|
||||
std::array<i16, constant::MixBufferSize * constant::ChannelCount> sampleBuffer{}; //!< The final output data that is appended to the stream
|
||||
std::array<i16, constant::MixBufferSize * constant::StereoChannelCount> sampleBuffer{}; //!< The final output data that is appended to the stream
|
||||
skyline::audio::AudioOutState playbackState{skyline::audio::AudioOutState::Stopped};
|
||||
|
||||
/**
|
||||
|
@ -79,13 +79,13 @@ namespace skyline::service::audio::IAudioRenderer {
|
||||
if (sampleRate != constant::SampleRate)
|
||||
samples = resampler.ResampleBuffer(samples, static_cast<double>(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<u32>(samples.size() - sampleOffset));
|
||||
outSize = std::min(maxSamples * constant::StereoChannelCount, static_cast<u32>(samples.size() - sampleOffset));
|
||||
|
||||
output.playedSamplesCount += outSize / constant::ChannelCount;
|
||||
output.playedSamplesCount += outSize / constant::StereoChannelCount;
|
||||
sampleOffset += outSize;
|
||||
|
||||
if (sampleOffset == samples.size()) {
|
||||
|
Loading…
Reference in New Issue
Block a user