mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-23 01:39:19 +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 {
|
namespace skyline::audio {
|
||||||
Audio::Audio(const DeviceState &state) : oboe::AudioStreamCallback() {
|
Audio::Audio(const DeviceState &state) : oboe::AudioStreamCallback() {
|
||||||
builder.setChannelCount(constant::ChannelCount);
|
builder.setChannelCount(constant::StereoChannelCount);
|
||||||
builder.setSampleRate(constant::SampleRate);
|
builder.setSampleRate(constant::SampleRate);
|
||||||
builder.setFormat(constant::PcmFormat);
|
builder.setFormat(constant::PcmFormat);
|
||||||
builder.setFramesPerCallback(constant::MixBufferSize);
|
builder.setFramesPerCallback(constant::MixBufferSize);
|
||||||
|
@ -9,9 +9,10 @@
|
|||||||
namespace skyline {
|
namespace skyline {
|
||||||
namespace constant {
|
namespace constant {
|
||||||
constexpr u16 SampleRate{48000}; //!< The common sampling rate to use for audio output
|
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 u8 StereoChannelCount{2}; //!< Channels to use for stereo audio output
|
||||||
constexpr u16 MixBufferSize{960}; //!< The size of the mix buffer by default
|
constexpr u8 SurroundChannelCount{6}; //!< Channels to use for surround audio output (downsampled by backend)
|
||||||
constexpr auto PcmFormat{oboe::AudioFormat::I16}; //!< The common PCM data format to use for audio output
|
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 {
|
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
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include "downmixer.h"
|
||||||
#include "track.h"
|
#include "track.h"
|
||||||
|
|
||||||
namespace skyline::audio {
|
namespace skyline::audio {
|
||||||
@ -11,7 +12,7 @@ namespace skyline::audio {
|
|||||||
if (sampleRate != constant::SampleRate)
|
if (sampleRate != constant::SampleRate)
|
||||||
throw exception("Unsupported audio sample rate: {}", 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);
|
throw exception("Unsupported quantity of audio channels: {}", channelCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,8 +65,14 @@ namespace skyline::audio {
|
|||||||
};
|
};
|
||||||
|
|
||||||
identifiers.push_front(identifier);
|
identifiers.push_front(identifier);
|
||||||
|
|
||||||
|
if (channelCount == constant::SurroundChannelCount) {
|
||||||
|
auto stereoBuffer{DownMix(buffer.cast<Surround51Sample>())};
|
||||||
|
samples.Append(span(stereoBuffer).cast<i16>());
|
||||||
|
} else {
|
||||||
samples.Append(buffer);
|
samples.Append(buffer);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AudioTrack::CheckReleasedBuffers() {
|
void AudioTrack::CheckReleasedBuffers() {
|
||||||
bool anyReleased{};
|
bool anyReleased{};
|
||||||
|
@ -20,7 +20,7 @@ namespace skyline::audio {
|
|||||||
u32 sampleRate;
|
u32 sampleRate;
|
||||||
|
|
||||||
public:
|
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
|
std::mutex bufferLock; //!< Synchronizes appending to audio buffers
|
||||||
|
|
||||||
AudioOutState playbackState{AudioOutState::Stopped}; //!< The current state of playback
|
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) {
|
Result IAudioDevice::GetActiveChannelCount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
response.Push<u32>(constant::ChannelCount);
|
response.Push<u32>(constant::StereoChannelCount);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,21 +14,27 @@ namespace skyline::service::audio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result IAudioOutManager::OpenAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
Result IAudioOutManager::OpenAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
auto sampleRate{request.Pop<u32>()};
|
struct AudioInputParams {
|
||||||
auto channelCount{static_cast<u16>(request.Pop<u32>())};
|
u32 sampleRate;
|
||||||
|
u16 channelCount;
|
||||||
|
u16 _pad_;
|
||||||
|
};
|
||||||
|
auto &inputParams{request.Pop<AudioInputParams>()};
|
||||||
|
|
||||||
Logger::Debug("Opening an IAudioOut with sample rate: {}, channel count: {}", sampleRate, channelCount);
|
inputParams.sampleRate = inputParams.sampleRate ? inputParams.sampleRate : constant::SampleRate;
|
||||||
|
inputParams.channelCount = inputParams.channelCount <= constant::StereoChannelCount ? constant::StereoChannelCount : constant::SurroundChannelCount;
|
||||||
sampleRate = sampleRate ? sampleRate : constant::SampleRate;
|
inputParams._pad_ = 0;
|
||||||
channelCount = channelCount ? channelCount : constant::ChannelCount;
|
Logger::Debug("Opening an IAudioOut with sample rate: {}, channel count: {}", inputParams.sampleRate, inputParams.channelCount);
|
||||||
manager.RegisterService(std::make_shared<IAudioOut>(state, manager, channelCount, sampleRate), session, response);
|
manager.RegisterService(std::make_shared<IAudioOut>(state, manager, inputParams.channelCount, inputParams.sampleRate), session, response);
|
||||||
|
response.Push<AudioInputParams>(inputParams);
|
||||||
response.Push<u32>(sampleRate);
|
|
||||||
response.Push<u16>(channelCount);
|
|
||||||
response.Push<u16>(0);
|
|
||||||
response.Push(static_cast<u32>(skyline::audio::AudioFormat::Int16));
|
response.Push(static_cast<u32>(skyline::audio::AudioFormat::Int16));
|
||||||
response.Push(static_cast<u32>(skyline::audio::AudioOutState::Stopped));
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
namespace skyline::service::audio::IAudioRenderer {
|
namespace skyline::service::audio::IAudioRenderer {
|
||||||
IAudioRenderer::IAudioRenderer(const DeviceState &state, ServiceManager &manager, AudioRendererParameters ¶meters)
|
IAudioRenderer::IAudioRenderer(const DeviceState &state, ServiceManager &manager, AudioRendererParameters ¶meters)
|
||||||
: systemEvent(std::make_shared<type::KEvent>(state, true)), parameters(parameters), BaseService(state, manager) {
|
: 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();
|
track->Start();
|
||||||
|
|
||||||
memoryPools.resize(parameters.effectCount + parameters.voiceCount * 4);
|
memoryPools.resize(parameters.effectCount + parameters.voiceCount * 4);
|
||||||
@ -144,7 +144,7 @@ namespace skyline::service::audio::IAudioRenderer {
|
|||||||
if (voiceBufferSize == 0)
|
if (voiceBufferSize == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
pendingSamples -= voiceBufferSize / constant::ChannelCount;
|
pendingSamples -= voiceBufferSize / constant::StereoChannelCount;
|
||||||
|
|
||||||
for (auto index{voiceBufferOffset}; index < voiceBufferOffset + voiceBufferSize; index++) {
|
for (auto index{voiceBufferOffset}; index < voiceBufferOffset + voiceBufferSize; index++) {
|
||||||
if (writtenSamples == bufferOffset) {
|
if (writtenSamples == bufferOffset) {
|
||||||
|
@ -69,7 +69,7 @@ namespace skyline {
|
|||||||
std::vector<MemoryPool> memoryPools;
|
std::vector<MemoryPool> memoryPools;
|
||||||
std::vector<Effect> effects;
|
std::vector<Effect> effects;
|
||||||
std::vector<Voice> voices;
|
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};
|
skyline::audio::AudioOutState playbackState{skyline::audio::AudioOutState::Stopped};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,13 +79,13 @@ namespace skyline::service::audio::IAudioRenderer {
|
|||||||
if (sampleRate != constant::SampleRate)
|
if (sampleRate != constant::SampleRate)
|
||||||
samples = resampler.ResampleBuffer(samples, static_cast<double>(sampleRate) / constant::SampleRate, channelCount);
|
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()};
|
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--) {
|
for (auto monoIndex{originalSize - 1}, targetIndex{samples.size()}; monoIndex > 0; monoIndex--) {
|
||||||
auto sample{samples[monoIndex]};
|
auto sample{samples[monoIndex]};
|
||||||
for (u8 i{}; i < constant::ChannelCount; i++)
|
for (u8 i{}; i < constant::StereoChannelCount; i++)
|
||||||
samples[--targetIndex] = sample;
|
samples[--targetIndex] = sample;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,9 +105,9 @@ namespace skyline::service::audio::IAudioRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
outOffset = sampleOffset;
|
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;
|
sampleOffset += outSize;
|
||||||
|
|
||||||
if (sampleOffset == samples.size()) {
|
if (sampleOffset == samples.size()) {
|
||||||
|
Loading…
Reference in New Issue
Block a user