Refactor Audio + Fix NV Bugs (#92)

* Fix NvHostCtrl:EventSignal event ID parsing
* Divide the audout buffer length by the sample size
* Correct audout channel quantity handling
* A few bugfixes for audio tracks
* * Correctly lock in CheckReleasedBuffers and only call the callback once
* * Check if the identifier queue is empty before accessing it's iterator
* Refactor audio to better fit the codestyle
* Explictly specify reference when using GetReference
* Fix CheckReleasedBuffers
This commit is contained in:
Billy Laws 2020-08-21 14:28:47 +01:00 committed by GitHub
parent 7290a80c3e
commit d8ccdd723e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 225 additions and 241 deletions

View File

@ -19,7 +19,7 @@ namespace skyline::audio {
std::shared_ptr<AudioTrack> Audio::OpenTrack(u8 channelCount, u32 sampleRate, const std::function<void()> &releaseCallback) {
std::lock_guard trackGuard(trackLock);
auto track = std::make_shared<AudioTrack>(channelCount, sampleRate, releaseCallback);
auto track{std::make_shared<AudioTrack>(channelCount, sampleRate, releaseCallback)};
audioTracks.push_back(track);
return track;
@ -33,30 +33,30 @@ namespace skyline::audio {
}
oboe::DataCallbackResult Audio::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) {
auto destBuffer = static_cast<i16 *>(audioData);
auto streamSamples = static_cast<size_t>(numFrames) * audioStream->getChannelCount();
size_t writtenSamples = 0;
auto destBuffer{static_cast<i16 *>(audioData)};
auto streamSamples{static_cast<size_t>(numFrames) * audioStream->getChannelCount()};
size_t writtenSamples{};
std::unique_lock trackGuard(trackLock);
{
std::lock_guard trackGuard(trackLock);
for (auto &track : audioTracks) {
if (track->playbackState == AudioOutState::Stopped)
continue;
for (auto &track : audioTracks) {
if (track->playbackState == AudioOutState::Stopped)
continue;
std::lock_guard bufferGuard(track->bufferLock);
std::lock_guard bufferGuard(track->bufferLock);
auto trackSamples = track->samples.Read(destBuffer, streamSamples, [](i16 *source, i16 *destination) {
*destination = Saturate<i16, i32>(static_cast<u32>(*destination) + static_cast<u32>(*source));
}, writtenSamples);
auto trackSamples = track->samples.Read(destBuffer, streamSamples, [](i16 *source, i16 *destination) {
*destination = Saturate<i16, i32>(static_cast<u32>(*destination) + static_cast<u32>(*source));
}, writtenSamples);
writtenSamples = std::max(trackSamples, writtenSamples);
writtenSamples = std::max(trackSamples, writtenSamples);
track->sampleCounter += trackSamples;
track->CheckReleasedBuffers();
track->sampleCounter += trackSamples;
track->CheckReleasedBuffers();
}
}
trackGuard.unlock();
if (streamSamples > writtenSamples)
memset(destBuffer + writtenSamples, 0, (streamSamples - writtenSamples) * sizeof(i16));

View File

@ -9,11 +9,11 @@
namespace skyline::audio {
AdpcmDecoder::AdpcmDecoder(const std::vector<std::array<i16, 2>> &coefficients) : coefficients(coefficients) {}
std::vector<i16> AdpcmDecoder::Decode(const std::vector<u8> &adpcmData) {
constexpr size_t BytesPerFrame = 0x8; //!< The number of bytes in a single frame
constexpr size_t SamplesPerFrame = 0xE; //!< The number of samples in a single frame
std::vector<i16> AdpcmDecoder::Decode(std::span<u8> adpcmData) {
constexpr size_t BytesPerFrame{0x8};
constexpr size_t SamplesPerFrame{0xE};
size_t remainingSamples = (adpcmData.size() / BytesPerFrame) * SamplesPerFrame;
size_t remainingSamples{(adpcmData.size() / BytesPerFrame) * SamplesPerFrame};
std::vector<i16> output;
output.reserve(remainingSamples);
@ -21,9 +21,9 @@ namespace skyline::audio {
size_t inputOffset{};
while (inputOffset < adpcmData.size()) {
FrameHeader header(adpcmData[inputOffset++]);
FrameHeader header{adpcmData[inputOffset++]};
size_t frameSamples = std::min(SamplesPerFrame, remainingSamples);
size_t frameSamples{std::min(SamplesPerFrame, remainingSamples)};
i32 ctx{};

View File

@ -3,6 +3,7 @@
#pragma once
#include <span>
#include <common.h>
namespace skyline::audio {
@ -15,14 +16,13 @@ namespace skyline::audio {
* @brief This struct holds a single ADPCM frame header
*/
union FrameHeader {
u8 raw;
struct {
u8 scale : 4; //!< The scale factor for this frame
u8 coefficientIndex : 3; //!< The index of the coeffcients to use for this frame
u8 coefficientIndex : 3;
u8 _pad_ :1;
};
u8 raw; //!< The raw value
FrameHeader(u8 raw) : raw(raw) {}
};
static_assert(sizeof(FrameHeader) == 0x1);
@ -37,6 +37,6 @@ namespace skyline::audio {
* @param adpcmData A buffer containing the raw ADPCM data
* @return A buffer containing decoded single channel I16 PCM data
*/
std::vector<i16> Decode(const std::vector<u8> &adpcmData);
std::vector<i16> Decode(std::span<u8> adpcmData);
};
}

View File

@ -3,8 +3,9 @@
#pragma once
#include <common.h>
#include <array>
#include <span>
#include <common.h>
namespace skyline::audio {
/**
@ -142,11 +143,11 @@ namespace skyline::audio {
}
/**
* @brief This appends data from a vector to the buffer
* @param sampleData A reference to a vector containing the data to be appended
* @brief This appends data from a span to the buffer
* @param data A span containing the data to be appended
*/
inline void Append(const std::vector<Type> &data) {
Append(const_cast<Type *>(data.data()), data.size());
inline void Append(std::span<Type> data) {
Append(data.data(), data.size());
}
};
}

View File

@ -8,10 +8,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 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
};
namespace audio {
@ -41,9 +41,9 @@ namespace skyline {
* @brief This stores information about pushed buffers
*/
struct BufferIdentifier {
u64 tag; //!< The tag of the buffer
u64 finalSample; //!< The final sample this buffer will be played in
bool released; //!< If the buffer has been released
u64 tag;
u64 finalSample; //!< The final sample this buffer will be played in, after that the buffer can be safely released
bool released; //!< If the buffer has been released (fully played back)
};
/**

View File

@ -3,17 +3,18 @@
#include <array>
#include <common.h>
#include "common.h"
#include "resampler.h"
namespace skyline::audio {
/**
* @brief This holds the coefficients of a single output frame
* @brief This holds the coefficients for each index of a single output frame
*/
struct LutEntry {
i32 a; //!< The coefficient for the first index
i32 b; //!< The coefficient for the second index
i32 c; //!< The coefficient for the third index
i32 d; //!< The coefficient for the fourth index
i32 a;
i32 b;
i32 c;
i32 d;
};
// @fmt:off
@ -51,7 +52,7 @@ namespace skyline::audio {
{48, 7600, 19361, 5773}, {41, 7472, 19377, 5888}, {34, 7345, 19391, 6004}, {28, 7219, 19403, 6121},
{22, 7093, 19412, 6239}, {15, 6968, 19419, 6359}, {9, 6845, 19424, 6479}, {3, 6722, 19426, 6600}}};
constexpr std::array<LutEntry, 128> CurveLut1 = {{
constexpr std::array<LutEntry, 128> CurveLut1{{
{-68, 32639, 69, -5}, {-200, 32630, 212, -15}, {-328, 32613, 359, -26}, {-450, 32586, 512, -36},
{-568, 32551, 669, -47}, {-680, 32507, 832, -58}, {-788, 32454, 1000, -69}, {-891, 32393, 1174, -80},
{-990, 32323, 1352, -92}, {-1084, 32244, 1536, -103}, {-1173, 32157, 1724, -115}, {-1258, 32061, 1919, -128},
@ -85,7 +86,7 @@ namespace skyline::audio {
{-80, 1174, 32393, -891}, {-69, 1000, 32454, -788}, {-58, 832, 32507, -680}, {-47, 669, 32551, -568},
{-36, 512, 32586, -450}, {-26, 359, 32613, -328}, {-15, 212, 32630, -200}, {-5, 69, 32639, -68}}};
constexpr std::array<LutEntry, 128> CurveLut2 = {{
constexpr std::array<LutEntry, 128> CurveLut2{{
{3195, 26287, 3329, -32}, {3064, 26281, 3467, -34}, {2936, 26270, 3608, -38}, {2811, 26253, 3751, -42},
{2688, 26230, 3897, -46}, {2568, 26202, 4046, -50}, {2451, 26169, 4199, -54}, {2338, 26130, 4354, -58},
{2227, 26085, 4512, -63}, {2120, 26035, 4673, -67}, {2015, 25980, 4837, -72}, {1912, 25919, 5004, -76},
@ -120,12 +121,12 @@ namespace skyline::audio {
{-42, 3751, 26253, 2811}, {-38, 3608, 26270, 2936}, {-34, 3467, 26281, 3064}, {-32, 3329, 26287, 3195}}};
// @fmt:on
std::vector<i16> Resampler::ResampleBuffer(const std::vector<i16> &inputBuffer, double ratio, u8 channelCount) {
auto step = static_cast<u32>(ratio * 0x8000);
auto outputSize = static_cast<size_t>(inputBuffer.size() / ratio);
std::vector<i16> Resampler::ResampleBuffer(std::span<i16> inputBuffer, double ratio, u8 channelCount) {
auto step{static_cast<u32>(ratio * 0x8000)};
auto outputSize{static_cast<size_t>(inputBuffer.size() / ratio)};
std::vector<i16> outputBuffer(outputSize);
const std::array<skyline::audio::LutEntry, 128> &lut = [step] {
const std::array<LutEntry, 128> &lut = [step] {
if (step > 0xAAAA)
return CurveLut0;
else if (step <= 0x8000)
@ -134,19 +135,19 @@ namespace skyline::audio {
return CurveLut2;
}();
for (size_t outIndex = 0, inIndex = 0; outIndex < outputSize; outIndex += channelCount) {
auto lutIndex = fraction >> 8;
for (size_t outIndex{}, inIndex{}; outIndex < outputSize; outIndex += channelCount) {
u32 lutIndex{fraction >> 8};
for (u8 channel = 0; channel < channelCount; channel++) {
for (u8 channel{}; channel < channelCount; channel++) {
i32 data = inputBuffer[(inIndex + 0) * channelCount + channel] * lut[lutIndex].a +
inputBuffer[(inIndex + 1) * channelCount + channel] * lut[lutIndex].b +
inputBuffer[(inIndex + 2) * channelCount + channel] * lut[lutIndex].c +
inputBuffer[(inIndex + 3) * channelCount + channel] * lut[lutIndex].d;
outputBuffer[outIndex + channel] = static_cast<i16>(std::clamp(data >> 15, static_cast<i32>(std::numeric_limits<i16>::min()), static_cast<i32>(std::numeric_limits<i16>::max())));
outputBuffer[outIndex + channel] = Saturate<i16, i32>(data >> 15);
}
auto newOffset = fraction + step;
u32 newOffset{fraction + step};
inIndex += newOffset >> 15;
fraction = newOffset & 0x7FFF;
}

View File

@ -3,6 +3,7 @@
#pragma once
#include <span>
#include <common.h>
namespace skyline::audio {
@ -20,6 +21,6 @@ namespace skyline::audio {
* @param ratio The conversion ratio needed
* @param channelCount The amount of channels the buffer contains
*/
std::vector<i16> ResampleBuffer(const std::vector<i16> &inputBuffer, double ratio, u8 channelCount);
std::vector<i16> ResampleBuffer(std::span<i16> inputBuffer, double ratio, u8 channelCount);
};
}

View File

@ -32,10 +32,10 @@ namespace skyline::audio {
std::vector<u64> AudioTrack::GetReleasedBuffers(u32 max) {
std::vector<u64> bufferIds;
std::unique_lock trackGuard(bufferLock);
std::lock_guard trackGuard(bufferLock);
for (u32 index = 0; index < max; index++) {
if (!identifiers.back().released)
for (u32 index{}; index < max; index++) {
if (identifiers.empty() || !identifiers.back().released)
break;
bufferIds.push_back(identifiers.back().tag);
identifiers.pop_back();
@ -44,29 +44,30 @@ namespace skyline::audio {
return bufferIds;
}
void AudioTrack::AppendBuffer(u64 tag, const i16 *address, u64 size) {
BufferIdentifier identifier;
identifier.released = false;
identifier.tag = tag;
if (identifiers.empty())
identifier.finalSample = size;
else
identifier.finalSample = size + identifiers.front().finalSample;
void AudioTrack::AppendBuffer(u64 tag, std::span<i16> buffer) {
BufferIdentifier identifier{
.released = false,
.tag = tag,
.finalSample = identifiers.empty() ? (buffer.size()) : (buffer.size() + identifiers.front().finalSample)
};
std::lock_guard guard(bufferLock);
identifiers.push_front(identifier);
samples.Append(const_cast<i16 *>(address), size);
samples.Append(buffer);
}
void AudioTrack::CheckReleasedBuffers() {
bool anyReleased{};
for (auto &identifier : identifiers) {
if (identifier.finalSample <= sampleCounter && !identifier.released) {
releaseCallback();
anyReleased = true;
identifier.released = true;
}
}
if (anyReleased)
releaseCallback();
}
}

View File

@ -3,8 +3,8 @@
#pragma once
#include <span>
#include <queue>
#include <kernel/types/KEvent.h>
#include <common.h>
#include "common.h"
@ -18,15 +18,15 @@ namespace skyline::audio {
std::function<void()> releaseCallback; //!< Callback called when a buffer has been played
std::deque<BufferIdentifier> identifiers; //!< Queue of all appended buffer identifiers
u8 channelCount; //!< The amount channels present in the track
u32 sampleRate; //!< The sample rate of the track
u8 channelCount;
u32 sampleRate;
public:
CircularBuffer<i16, constant::SampleRate * constant::ChannelCount * 10> samples; //!< A vector of all appended audio samples
CircularBuffer<i16, constant::SampleRate * constant::ChannelCount * 10> samples; //!< A circular buffer with all appended audio samples
Mutex bufferLock; //!< This mutex ensures that appending to buffers doesn't overlap
AudioOutState playbackState{AudioOutState::Stopped}; //!< The current state of playback
u64 sampleCounter{}; //!< A counter used for tracking buffer status
u64 sampleCounter{}; //!< A counter used for tracking when buffers have been played and can be released
/**
* @param channelCount The amount channels that will be present in the track
@ -64,22 +64,13 @@ namespace skyline::audio {
/**
* @brief Appends audio samples to the output buffer
* @param tag The tag of the buffer
* @param address The address of the audio buffer
* @param size The size of the audio buffer in i16 units
* @param buffer A span containing the source sample buffer
*/
void AppendBuffer(u64 tag, const i16 *address, u64 size);
/**
* @brief Appends audio samples to the output buffer
* @param tag The tag of the buffer
* @param sampleData A reference to a vector containing I16 format PCM data
*/
void AppendBuffer(u64 tag, const std::vector<i16> &sampleData = {}) {
AppendBuffer(tag, sampleData.data(), sampleData.size());
}
void AppendBuffer(u64 tag, std::span<i16> buffer = {});
/**
* @brief Checks if any buffers have been released and calls the appropriate callback for them
* @note bufferLock MUST be locked when calling this
*/
void CheckReleasedBuffers();
};

View File

@ -40,7 +40,7 @@ namespace skyline::service::audio {
}
void IAudioDevice::QueryAudioDeviceSystemEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto handle = state.process->InsertItem(systemEvent);
auto handle{state.process->InsertItem(systemEvent)};
state.logger->Debug("Audio Device System Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle);
}

View File

@ -44,32 +44,29 @@ namespace skyline::service::audio {
u64 sampleCapacity;
u64 sampleSize;
u64 sampleOffset;
} data = state.process->GetObject<Data>(request.inputBuf.at(0).address);
} &data{state.process->GetReference<Data>(request.inputBuf.at(0).address)};
auto tag = request.Pop<u64>();
state.logger->Debug("IAudioOut: Appending buffer with address: 0x{:X}, size: 0x{:X}", data.sampleBufferPtr, data.sampleSize);
if (sampleRate != constant::SampleRate) {
tmpSampleBuffer.resize(data.sampleSize / sizeof(i16));
state.process->ReadMemory(tmpSampleBuffer.data(), data.sampleBufferPtr, data.sampleSize);
resampler.ResampleBuffer(tmpSampleBuffer, static_cast<double>(sampleRate) / constant::SampleRate, channelCount);
track->AppendBuffer(tag, tmpSampleBuffer);
auto resampledBuffer = resampler.ResampleBuffer(std::span(state.process->GetPointer<i16>(data.sampleBufferPtr), data.sampleSize / sizeof(i16)), static_cast<double>(sampleRate) / constant::SampleRate, channelCount);
track->AppendBuffer(tag, resampledBuffer);
} else {
track->AppendBuffer(tag, state.process->GetPointer<i16>(data.sampleBufferPtr), data.sampleSize);
track->AppendBuffer(tag, std::span(state.process->GetPointer<i16>(data.sampleBufferPtr), data.sampleSize / sizeof(i16)));
}
}
void IAudioOut::RegisterBufferEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto handle = state.process->InsertItem(releaseEvent);
auto handle{state.process->InsertItem(releaseEvent)};
state.logger->Debug("IAudioOut: Buffer Release Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle);
}
void IAudioOut::GetReleasedAudioOutBuffer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto maxCount = static_cast<u32>(request.outputBuf.at(0).size >> 3);
std::vector<u64> releasedBuffers = track->GetReleasedBuffers(maxCount);
auto count = static_cast<u32>(releasedBuffers.size());
auto maxCount{static_cast<u32>(request.outputBuf.at(0).size >> 3)};
std::vector<u64> releasedBuffers{track->GetReleasedBuffers(maxCount)};
auto count{static_cast<u32>(releasedBuffers.size())};
// Fill rest of output buffer with zeros
releasedBuffers.resize(maxCount, 0);
@ -79,7 +76,7 @@ namespace skyline::service::audio {
}
void IAudioOut::ContainsAudioOutBuffer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto tag = request.Pop<u64>();
auto tag{request.Pop<u64>()};
response.Push(static_cast<u32>(track->ContainsBuffer(tag)));
}

View File

@ -18,10 +18,9 @@ namespace skyline::service::audio {
skyline::audio::Resampler resampler; //!< The audio resampler object used to resample audio
std::shared_ptr<skyline::audio::AudioTrack> track; //!< The audio track associated with the audio out
std::shared_ptr<type::KEvent> releaseEvent; //!< The KEvent that is signalled when a buffer has been released
std::vector<i16> tmpSampleBuffer; //!< A temporary buffer used to store sample data in AppendAudioOutBuffer
const u32 sampleRate; //!< The sample rate of the audio out
const u8 channelCount; //!< The amount of channels in the data sent to the audio out
u32 sampleRate;
u8 channelCount;
public:
/**

View File

@ -18,14 +18,13 @@ namespace skyline::service::audio {
}
void IAudioOutManager::OpenAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
u32 sampleRate = request.Pop<u32>();
request.Pop<u16>(); // Channel count is stored in the upper half of a u32
u16 channelCount = request.Pop<u16>();
auto sampleRate{request.Pop<u32>()};
auto channelCount{static_cast<u16>(request.Pop<u32>())};
state.logger->Debug("audoutU: Opening an IAudioOut with sample rate: {}, channel count: {}", sampleRate, channelCount);
state.logger->Debug("Opening an IAudioOut with sample rate: {}, channel count: {}", sampleRate, channelCount);
sampleRate = sampleRate ? sampleRate : constant::SampleRate;
channelCount = channelCount ? channelCount : static_cast<u16>(constant::ChannelCount);
channelCount = channelCount ? channelCount : constant::ChannelCount;
manager.RegisterService(std::make_shared<IAudioOut>(state, manager, channelCount, sampleRate), session, response);
response.Push<u32>(sampleRate);

View File

@ -51,14 +51,14 @@ namespace skyline::service::audio::IAudioRenderer {
}
void IAudioRenderer::RequestUpdate(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto inputAddress = request.inputBuf.at(0).address;
auto inputAddress{request.inputBuf.at(0).address};
auto inputHeader = state.process->GetObject<UpdateDataHeader>(inputAddress);
auto inputHeader{state.process->GetObject<UpdateDataHeader>(inputAddress)};
revisionInfo.SetUserRevision(inputHeader.revision);
inputAddress += sizeof(UpdateDataHeader);
inputAddress += inputHeader.behaviorSize; // Unused
auto memoryPoolCount = memoryPools.size();
auto memoryPoolCount{memoryPools.size()};
std::vector<MemoryPoolIn> memoryPoolsIn(memoryPoolCount);
state.process->ReadMemory(memoryPoolsIn.data(), inputAddress, memoryPoolCount * sizeof(MemoryPoolIn));
inputAddress += inputHeader.memoryPoolSize;
@ -128,11 +128,11 @@ namespace skyline::service::audio::IAudioRenderer {
}
void IAudioRenderer::UpdateAudio() {
auto released = track->GetReleasedBuffers(2);
auto released{track->GetReleasedBuffers(2)};
for (auto &tag : released) {
MixFinalBuffer();
track->AppendBuffer(tag, sampleBuffer.data(), sampleBuffer.size());
track->AppendBuffer(tag, sampleBuffer);
}
}
@ -149,14 +149,14 @@ namespace skyline::service::audio::IAudioRenderer {
while (pendingSamples > 0) {
u32 voiceBufferOffset{};
u32 voiceBufferSize{};
auto &voiceSamples = voice.GetBufferData(pendingSamples, voiceBufferOffset, voiceBufferSize);
auto &voiceSamples{voice.GetBufferData(pendingSamples, voiceBufferOffset, voiceBufferSize)};
if (voiceBufferSize == 0)
break;
pendingSamples -= voiceBufferSize / constant::ChannelCount;
for (auto index = voiceBufferOffset; index < voiceBufferOffset + voiceBufferSize; index++) {
for (auto index{voiceBufferOffset}; index < voiceBufferOffset + voiceBufferSize; index++) {
if (writtenSamples == bufferOffset) {
sampleBuffer[bufferOffset] = skyline::audio::Saturate<i16, i32>(voiceSamples[index] * voice.volume);
@ -180,7 +180,7 @@ namespace skyline::service::audio::IAudioRenderer {
}
void IAudioRenderer::QuerySystemEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto handle = state.process->InsertItem(systemEvent);
auto handle{state.process->InsertItem(systemEvent)};
state.logger->Debug("Audren System Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle);
}

View File

@ -22,39 +22,39 @@ namespace skyline {
* @brief The parameters used to configure an IAudioRenderer
*/
struct AudioRendererParameters {
u32 sampleRate; //!< The sample rate to use for the renderer
u32 sampleCount; //!< The buffer sample count
u32 mixBufferCount; //!< The amount of mix buffers to use
u32 subMixCount; //!< The amount of sub mixes to use
u32 voiceCount; //!< The amount of voices to use
u32 sinkCount; //!< The amount of sinks to use
u32 effectCount; //!< The amount of effects to use
u32 performanceManagerCount; //!< The amount of performance managers to use
u32 voiceDropEnable; //!< Whether to enable voice drop
u32 splitterCount; //!< The amount of splitters to use
u32 splitterDestinationDataCount; //!< The amount of splitter destination outputs to use
u32 sampleRate;
u32 sampleCount;
u32 mixBufferCount;
u32 subMixCount;
u32 voiceCount;
u32 sinkCount;
u32 effectCount;
u32 performanceManagerCount;
u32 voiceDropEnable;
u32 splitterCount;
u32 splitterDestinationDataCount;
u32 _unk0_;
u32 revision; //!< The revision of audren to use
u32 revision;
};
static_assert(sizeof(AudioRendererParameters) == 0x34);
/**
* @brief Header containing information about the software side audren implementation
* @brief Header containing information about the software side audren implementation and the sizes of all input data
*/
struct UpdateDataHeader {
u32 revision; //!< Revision of the software implementation
u32 behaviorSize; //!< The total size of the behaviour info
u32 memoryPoolSize; //!< The total size of all MemoryPoolIn structs
u32 voiceSize; //!< The total size of all VoiceIn structs
u32 voiceResourceSize; //!< The total size of the voice resources
u32 effectSize; //!< The total size of all EffectIn structs
u32 mixSize; //!< The total size of all mixer descriptors in the input
u32 sinkSize; //!< The total size of all sink descriptors in the input
u32 performanceManagerSize; //!< The total size of all performance manager descriptors in the input
u32 revision;
u32 behaviorSize;
u32 memoryPoolSize;
u32 voiceSize;
u32 voiceResourceSize;
u32 effectSize;
u32 mixSize;
u32 sinkSize;
u32 performanceManagerSize;
u32 _unk0_;
u32 elapsedFrameCountInfoSize; //!< The total size of all the elapsed frame info
u32 elapsedFrameCountInfoSize;
u32 _unk1_[4];
u32 totalSize; //!< The total size of the whole input
u32 totalSize;
};
static_assert(sizeof(UpdateDataHeader) == 0x40);
@ -63,15 +63,15 @@ namespace skyline {
*/
class IAudioRenderer : public BaseService {
private:
AudioRendererParameters parameters; //!< The parameters to use for the renderer
AudioRendererParameters parameters;
RevisionInfo revisionInfo{}; //!< Stores info about supported features for the audren revision used
std::shared_ptr<skyline::audio::AudioTrack> track; //!< The audio track associated with the audio renderer
std::shared_ptr<type::KEvent> systemEvent; //!< The KEvent that is signalled when the DSP has processed all the commands
std::vector<MemoryPool> memoryPools; //!< An vector of all memory pools that the guest may need
std::vector<Effect> effects; //!< An vector of all effects that the guest may need
std::vector<Voice> voices; //!< An vector of all voices that the guest may need
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
skyline::audio::AudioOutState playbackState{skyline::audio::AudioOutState::Stopped}; //!< The current state of playback
skyline::audio::AudioOutState playbackState{skyline::audio::AudioOutState::Stopped};
/**
* @brief Obtains new sample data from voices and mixes it together into the sample buffer

View File

@ -11,7 +11,7 @@ namespace skyline::service::audio::IAudioRenderer {
*/
enum class EffectState : u8 {
None = 0, //!< The effect isn't being used
New = 1 //!< The effect is new
New = 1,
};
/**
@ -28,7 +28,7 @@ namespace skyline::service::audio::IAudioRenderer {
* @brief This is returned to inform the guest of the state of an effect
*/
struct EffectOut {
EffectState state; //!< The state of the effect
EffectState state;
u8 _pad0_[15];
};
static_assert(sizeof(EffectOut) == 0x10);
@ -38,7 +38,7 @@ namespace skyline::service::audio::IAudioRenderer {
*/
class Effect {
public:
EffectOut output{}; //!< The current output state
EffectOut output{};
inline void ProcessInput(const EffectIn &input) {
if (input.isNew)

View File

@ -10,22 +10,22 @@ namespace skyline::service::audio::IAudioRenderer {
* @brief This enumerates various states a memory pool can be in
*/
enum class MemoryPoolState : u32 {
Invalid = 0, //!< The memory pool is invalid
Unknown = 1, //!< The memory pool is in an unknown state
RequestDetach = 2, //!< The memory pool should be detached from
Detached = 3, //!< The memory pool has been detached from
RequestAttach = 4, //!< The memory pool should be attached to
Attached = 5, //!< The memory pool has been attached to
Released = 6 //!< The memory pool has been released
Invalid = 0,
Unknown = 1,
RequestDetach = 2,
Detached = 3,
RequestAttach = 4,
Attached = 5,
Released = 6,
};
/**
* @brief This is in input containing information about a memory pool for use by the dsp
*/
struct MemoryPoolIn {
u64 address; //!< The address of the memory pool
u64 size; //!< The size of the memory pool
MemoryPoolState state; //!< The state that is requested for the memory pool
u64 address;
u64 size;
MemoryPoolState state;
u32 _unk0_;
u64 _unk1_;
};
@ -35,7 +35,7 @@ namespace skyline::service::audio::IAudioRenderer {
* @brief This is returned to inform the guest of the state of a memory pool
*/
struct MemoryPoolOut {
MemoryPoolState state = MemoryPoolState::Detached; //!< The state of the memory pool
MemoryPoolState state = MemoryPoolState::Detached;
u32 _unk0_;
u64 _unk1_;
};
@ -46,7 +46,7 @@ namespace skyline::service::audio::IAudioRenderer {
*/
class MemoryPool {
public:
MemoryPoolOut output{}; //!< The current output state
MemoryPoolOut output{};
/**
* @brief Processes the input memory pool data from the guest and sets internal data based off it

View File

@ -7,16 +7,16 @@
namespace skyline {
namespace constant {
constexpr u32 SupportedRevision = 7; //!< The audren revision our implementation supports
constexpr u32 Rev0Magic = util::MakeMagic<u32>("REV0"); //!< The HOS 1.0 revision magic
constexpr u32 RevMagic = Rev0Magic + (SupportedRevision << 24); //!< The revision magic for our supported revision
constexpr u32 SupportedRevision{7}; //!< The audren revision our implementation supports
constexpr u32 Rev0Magic{util::MakeMagic<u32>("REV0")}; //!< The HOS 1.0 revision magic
constexpr u32 RevMagic{Rev0Magic + (SupportedRevision << 24)}; //!< The revision magic for our supported revision
namespace supportTags {
constexpr u32 Splitter = 2; //!< The revision splitter support was added
constexpr u32 SplitterBugFix = 5; //!< The revision the splitter buffer was made aligned
constexpr u32 PerformanceMetricsDataFormatV2 = 5; //!< The revision a new performance metrics format is used
constexpr u32 VaradicCommandBufferSize = 5; //!< The revision support for varying command buffer sizes was added
constexpr u32 ElapsedFrameCount = 5; //!< The revision support for counting elapsed frames was added
constexpr u32 Splitter{2}; //!< The revision splitter support was added
constexpr u32 SplitterBugFix{5}; //!< The revision the splitter buffer was made aligned
constexpr u32 PerformanceMetricsDataFormatV2{5}; //!< The revision a new performance metrics format is used
constexpr u32 VaradicCommandBufferSize{5}; //!< The revision support for varying command buffer sizes was added
constexpr u32 ElapsedFrameCount{5}; //!< The revision support for counting elapsed frames was added
};
}

View File

@ -6,7 +6,7 @@
namespace skyline::service::audio::IAudioRenderer {
void Voice::SetWaveBufferIndex(u8 index) {
bufferIndex = static_cast<u8>(index & 3);
bufferIndex = index & 3;
bufferReload = true;
}
@ -57,7 +57,7 @@ namespace skyline::service::audio::IAudioRenderer {
}
void Voice::UpdateBuffers() {
const auto &currentBuffer = waveBuffers.at(bufferIndex);
const auto &currentBuffer{waveBuffers.at(bufferIndex)};
if (currentBuffer.size == 0)
return;
@ -68,9 +68,7 @@ namespace skyline::service::audio::IAudioRenderer {
state.process->ReadMemory(samples.data(), currentBuffer.address, currentBuffer.size);
break;
case skyline::audio::AudioFormat::ADPCM: {
std::vector<u8> adpcmData(currentBuffer.size);
state.process->ReadMemory(adpcmData.data(), currentBuffer.address, currentBuffer.size);
samples = adpcmDecoder->Decode(adpcmData);
samples = adpcmDecoder->Decode(std::span(state.process->GetPointer<u8>(currentBuffer.address), currentBuffer.size));
break;
}
default:
@ -81,19 +79,19 @@ namespace skyline::service::audio::IAudioRenderer {
samples = resampler.ResampleBuffer(samples, static_cast<double>(sampleRate) / constant::SampleRate, channelCount);
if (channelCount == 1 && constant::ChannelCount != channelCount) {
auto originalSize = samples.size();
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++)
for (auto monoIndex{originalSize - 1}, targetIndex{samples.size()}; monoIndex > 0; monoIndex--) {
auto sample{samples[monoIndex]};
for (u8 i{}; i < constant::ChannelCount; i++)
samples[--targetIndex] = sample;
}
}
}
std::vector<i16> &Voice::GetBufferData(u32 maxSamples, u32 &outOffset, u32 &outSize) {
WaveBuffer &currentBuffer = waveBuffers.at(bufferIndex);
auto &currentBuffer{waveBuffers.at(bufferIndex)};
if (!acquired || playbackState != skyline::audio::AudioOutState::Started) {
outSize = 0;

View File

@ -14,13 +14,13 @@ namespace skyline::service::audio::IAudioRenderer {
* @brief This stores data for configuring a biquadratic filter
*/
struct BiquadFilter {
u8 enable; //!< Whether to enable the filter
u8 enable;
u8 _pad0_;
u16 b0; //!< The b0 constant
u16 b1; //!< The b1 constant
u16 b2; //!< The b2 constant
u16 a1; //!< The a1 constant
u16 a2; //!< The a2 constant
u16 b0;
u16 b1;
u16 b2;
u16 a1;
u16 a2;
};
static_assert(sizeof(BiquadFilter) == 0xC);
@ -28,16 +28,16 @@ namespace skyline::service::audio::IAudioRenderer {
* @brief This stores information of a wave buffer of samples
*/
struct WaveBuffer {
u64 address; //!< The address of the wave buffer in guest memory
u64 size; //!< The size of the wave buffer
u32 firstSampleOffset; //!< The offset of the first sample in the buffer
u32 lastSampleOffset; //!< The offset of the last sample in the buffer
u64 address;
u64 size;
u32 firstSampleOffset;
u32 lastSampleOffset;
u8 looping; //!< Whether to loop the buffer
u8 lastBuffer; //!< Whether this is the last populated buffer
u16 _unk0_;
u32 _unk1_;
u64 adpcmLoopContextPosition; //!< The position of the ADPCM loop context data
u64 adpcmLoopContextSize; //!< The size of the ADPCM loop context data
u64 adpcmLoopContextPosition;
u64 adpcmLoopContextSize;
u64 _unk2_;
};
static_assert(sizeof(WaveBuffer) == 0x38);
@ -46,28 +46,28 @@ namespace skyline::service::audio::IAudioRenderer {
* @brief This is in input containing the configuration of a voice
*/
struct VoiceIn {
u32 slot; //!< The slot of the voice
u32 nodeId; //!< The node ID of the voice
u8 firstUpdate; //!< Whether this voice is new
u32 slot;
u32 nodeId;
u8 firstUpdate; //!< Whether this voice is newly added
u8 acquired; //!< Whether the sample is in use
skyline::audio::AudioOutState playbackState; //!< The playback state
skyline::audio::AudioFormat format; //!< The sample format
u32 sampleRate; //!< The sample rate
u32 priority; //!< The priority for this voice
skyline::audio::AudioOutState playbackState;
skyline::audio::AudioFormat format;
u32 sampleRate;
u32 priority;
u32 _unk0_;
u32 channelCount; //!< The amount of channels the wave buffers contain
float pitch; //!< The pitch to play wave data at
float volume; //!< The volume to play wave data at
std::array<BiquadFilter, 2> biquadFilters; //!< Biquadratic filter configurations
u32 appendedWaveBuffersCount; //!< The amount of buffers appended
u32 baseWaveBufferIndex; //!< The start index of the buffer to use
u32 channelCount;
float pitch;
float volume;
std::array<BiquadFilter, 2> biquadFilters;
u32 appendedWaveBuffersCount;
u32 baseWaveBufferIndex;
u32 _unk1_;
u64 adpcmCoeffsPosition; //!< The ADPCM coefficient position in wave data
u64 adpcmCoeffsSize; //!< The size of the ADPCM coefficient configuration data
u32 destination; //!< The voice destination address
u64 adpcmCoeffsPosition;
u64 adpcmCoeffsSize;
u32 destination;
u32 _pad0_;
std::array<WaveBuffer, 4> waveBuffers; //!< The wave buffers for the voice
std::array<u32, 6> voiceChannelResourceIds; //!< A list of IDs corresponding to each channel
std::array<WaveBuffer, 4> waveBuffers;
std::array<u32, 6> voiceChannelResourceIds;
u32 _pad1_[6];
};
static_assert(sizeof(VoiceIn) == 0x170);
@ -77,8 +77,8 @@ namespace skyline::service::audio::IAudioRenderer {
* @brief This is returned to inform the guest of the state of a voice
*/
struct VoiceOut {
u64 playedSamplesCount; //!< The amount of samples played
u32 playedWaveBuffersCount; //!< The amount of wave buffers fully played
u64 playedSamplesCount;
u32 playedWaveBuffersCount;
u32 voiceDropsCount; //!< The amount of time audio frames have been dropped due to the rendering time limit
};
static_assert(sizeof(VoiceOut) == 0x10);
@ -88,20 +88,20 @@ namespace skyline::service::audio::IAudioRenderer {
*/
class Voice {
private:
const DeviceState &state; //!< The emulator state object
std::array<WaveBuffer, 4> waveBuffers; //!< An array containing the state of all four wave buffers
const DeviceState &state;
std::array<WaveBuffer, 4> waveBuffers;
std::vector<i16> samples; //!< A vector containing processed sample data
skyline::audio::Resampler resampler; //!< The resampler object used for changing the sample rate of a stream
std::optional<skyline::audio::AdpcmDecoder> adpcmDecoder; //!< The decoder object used for decoding ADPCM encoded samples
skyline::audio::Resampler resampler; //!< The resampler object used for changing the sample rate of a wave buffer's stream
std::optional<skyline::audio::AdpcmDecoder> adpcmDecoder;
bool acquired{false}; //!< If the voice is in use
bool bufferReload{true}; //!< If the buffer needs to be updated
bool bufferReload{true};
u8 bufferIndex{}; //!< The index of the wave buffer currently in use
u32 sampleOffset{}; //!< The offset in the sample data of the current wave buffer
u32 sampleRate{}; //!< The sample rate of the sample data
u8 channelCount{}; //!< The amount of channels in the sample data
skyline::audio::AudioOutState playbackState{skyline::audio::AudioOutState::Stopped}; //!< The playback state of the voice
skyline::audio::AudioFormat format{skyline::audio::AudioFormat::Invalid}; //!< The format used for guest audio data
u32 sampleRate{};
u8 channelCount{};
skyline::audio::AudioOutState playbackState{skyline::audio::AudioOutState::Stopped};
skyline::audio::AudioFormat format{skyline::audio::AudioFormat::Invalid};
/**
* @brief This updates the sample buffer with data from the current wave buffer and processes it
@ -115,8 +115,8 @@ namespace skyline::service::audio::IAudioRenderer {
void SetWaveBufferIndex(u8 index);
public:
VoiceOut output{}; //!< The current output state
float volume{}; //!< The volume of the voice
VoiceOut output{};
float volume{};
Voice(const DeviceState &state);

View File

@ -28,24 +28,24 @@ namespace skyline::service::audio {
IAudioRenderer::RevisionInfo revisionInfo{};
revisionInfo.SetUserRevision(params.revision);
u32 totalMixCount = params.subMixCount + 1;
u32 totalMixCount{params.subMixCount + 1};
i64 size = util::AlignUp(params.mixBufferCount * 4, constant::BufferAlignment) +
i64 size{util::AlignUp(params.mixBufferCount * 4, constant::BufferAlignment) +
params.subMixCount * 0x400 +
totalMixCount * 0x940 +
params.voiceCount * 0x3F0 +
util::AlignUp(totalMixCount * 8, 16) +
util::AlignUp(params.voiceCount * 8, 16) +
util::AlignUp(((params.sinkCount + params.subMixCount) * 0x3C0 + params.sampleCount * 4) * (params.mixBufferCount + 6), constant::BufferAlignment) + (params.sinkCount + params.subMixCount) * 0x2C0 + (params.effectCount + params.voiceCount * 4) * 0x30 + 0x50;
util::AlignUp(((params.sinkCount + params.subMixCount) * 0x3C0 + params.sampleCount * 4) * (params.mixBufferCount + 6), constant::BufferAlignment) + (params.sinkCount + params.subMixCount) * 0x2C0 + (params.effectCount + params.voiceCount * 4) * 0x30 + 0x50};
if (revisionInfo.SplitterSupported()) {
i32 nodeStateWorkSize = util::AlignUp(totalMixCount, constant::BufferAlignment);
i32 nodeStateWorkSize{util::AlignUp<i32>(totalMixCount, constant::BufferAlignment)};
if (nodeStateWorkSize < 0)
nodeStateWorkSize |= 7;
nodeStateWorkSize = 4 * (totalMixCount * totalMixCount) + 12 * totalMixCount + 2 * (nodeStateWorkSize / 8);
i32 edgeMatrixWorkSize = util::AlignUp(totalMixCount * totalMixCount, constant::BufferAlignment);
i32 edgeMatrixWorkSize{util::AlignUp<i32>(totalMixCount * totalMixCount, constant::BufferAlignment)};
if (edgeMatrixWorkSize < 0)
edgeMatrixWorkSize |= 7;
@ -53,7 +53,7 @@ namespace skyline::service::audio {
size += util::AlignUp(edgeMatrixWorkSize + nodeStateWorkSize, 16);
}
i64 splitterWorkSize = 0;
i64 splitterWorkSize{};
if (revisionInfo.SplitterSupported()) {
splitterWorkSize += params.splitterDestinationDataCount * 0xE0 + params.splitterCount * 0x20;
@ -65,7 +65,7 @@ namespace skyline::service::audio {
size = params.sinkCount * 0x170 + (params.sinkCount + params.subMixCount) * 0x280 + params.effectCount * 0x4C0 + ((size + splitterWorkSize + 0x30 * params.effectCount + (4 * params.voiceCount) + 0x8F) & ~0x3FL) + ((params.voiceCount << 8) | 0x40);
if (params.performanceManagerCount > 0) {
i64 performanceMetricsBufferSize;
i64 performanceMetricsBufferSize{};
if (revisionInfo.UsesPerformanceMetricDataFormatV2()) {
performanceMetricsBufferSize = (params.voiceCount + params.effectCount + totalMixCount + params.sinkCount) + 0x990;

View File

@ -43,7 +43,7 @@ namespace skyline::service::lm {
LogLevel level;
u8 verbosity;
u32 payloadLength;
} data = state.process->GetReference<Data>(request.inputBuf.at(0).address);
} &data = state.process->GetReference<Data>(request.inputBuf.at(0).address);
std::ostringstream logMessage;
logMessage << "Guest log:";

View File

@ -22,7 +22,7 @@ namespace skyline::service::nvdrv::device {
void NvHostAsGpu::BindChannel(IoctlData &buffer) {
struct Data {
u32 fd;
} channelInfo = state.process->GetReference<Data>(buffer.input.at(0).address);
} &channelInfo = state.process->GetReference<Data>(buffer.input.at(0).address);
}
void NvHostAsGpu::AllocSpace(IoctlData &buffer) {
@ -100,7 +100,7 @@ namespace skyline::service::nvdrv::device {
u32 pad;
u64 pages;
} regions[2];
} regionInfo = state.process->GetReference<Data>(buffer.input.at(0).address);
} &regionInfo = state.process->GetReference<Data>(buffer.input.at(0).address);
state.process->WriteMemory(regionInfo, buffer.output.at(0).address);
}

View File

@ -47,7 +47,7 @@ namespace skyline::service::nvdrv::device {
u32 raw;
} flags;
Fence fence;
} args = state.process->GetReference<Data>(buffer.output.at(0).address);
} &args = state.process->GetReference<Data>(buffer.output.at(0).address);
auto &hostSyncpoint = state.os->serviceManager.GetService<nvdrv::INvDrvServices>(Service::nvdrv_INvDrvServices)->hostSyncpoint;
@ -103,7 +103,7 @@ namespace skyline::service::nvdrv::device {
u32 flags;
Fence fence;
u32 reserved[3];
} args = state.process->GetReference<Data>(buffer.input.at(0).address);
} &args = state.process->GetReference<Data>(buffer.input.at(0).address);
channelFence.UpdateValue(state.os->serviceManager.GetService<nvdrv::INvDrvServices>(Service::nvdrv_INvDrvServices)->hostSyncpoint);
args.fence = channelFence;

View File

@ -83,7 +83,7 @@ namespace skyline::service::nvdrv::device {
Fence fence;
u32 timeout;
EventValue value;
} args = state.process->GetReference<Data>(buffer.output.at(0).address);
} &args = state.process->GetReference<Data>(buffer.output.at(0).address);
if (args.fence.id >= constant::MaxHwSyncpointCount) {
buffer.status = NvStatus::BadValue;
@ -155,11 +155,7 @@ namespace skyline::service::nvdrv::device {
}
void NvHostCtrl::EventSignal(IoctlData &buffer) {
struct Data {
u16 _pad_;
u16 userEventId;
};
auto userEventId = state.process->GetObject<Data>(buffer.input.at(0).address).userEventId;
auto userEventId = static_cast<u16>(state.process->GetObject<u32>(buffer.input.at(0).address));
state.logger->Debug("Signalling nvhost event: {}", userEventId);
if (userEventId >= constant::NvHostEventCount || !events.at(userEventId)) {