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

View File

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

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include <span>
#include <common.h> #include <common.h>
namespace skyline::audio { namespace skyline::audio {
@ -15,14 +16,13 @@ namespace skyline::audio {
* @brief This struct holds a single ADPCM frame header * @brief This struct holds a single ADPCM frame header
*/ */
union FrameHeader { union FrameHeader {
u8 raw;
struct { struct {
u8 scale : 4; //!< The scale factor for this frame 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 _pad_ :1;
}; };
u8 raw; //!< The raw value
FrameHeader(u8 raw) : raw(raw) {}
}; };
static_assert(sizeof(FrameHeader) == 0x1); static_assert(sizeof(FrameHeader) == 0x1);
@ -37,6 +37,6 @@ namespace skyline::audio {
* @param adpcmData A buffer containing the raw ADPCM data * @param adpcmData A buffer containing the raw ADPCM data
* @return A buffer containing decoded single channel I16 PCM 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 #pragma once
#include <common.h>
#include <array> #include <array>
#include <span>
#include <common.h>
namespace skyline::audio { namespace skyline::audio {
/** /**
@ -142,11 +143,11 @@ namespace skyline::audio {
} }
/** /**
* @brief This appends data from a vector to the buffer * @brief This appends data from a span to the buffer
* @param sampleData A reference to a vector containing the data to be appended * @param data A span containing the data to be appended
*/ */
inline void Append(const std::vector<Type> &data) { inline void Append(std::span<Type> data) {
Append(const_cast<Type *>(data.data()), data.size()); Append(data.data(), data.size());
} }
}; };
} }

View File

@ -8,10 +8,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 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 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 auto PcmFormat{oboe::AudioFormat::I16}; //!< The common PCM data format to use for audio output
}; };
namespace audio { namespace audio {
@ -41,9 +41,9 @@ namespace skyline {
* @brief This stores information about pushed buffers * @brief This stores information about pushed buffers
*/ */
struct BufferIdentifier { struct BufferIdentifier {
u64 tag; //!< The tag of the buffer u64 tag;
u64 finalSample; //!< The final sample this buffer will be played in 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 bool released; //!< If the buffer has been released (fully played back)
}; };
/** /**

View File

@ -3,17 +3,18 @@
#include <array> #include <array>
#include <common.h> #include <common.h>
#include "common.h"
#include "resampler.h" #include "resampler.h"
namespace skyline::audio { 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 { struct LutEntry {
i32 a; //!< The coefficient for the first index i32 a;
i32 b; //!< The coefficient for the second index i32 b;
i32 c; //!< The coefficient for the third index i32 c;
i32 d; //!< The coefficient for the fourth index i32 d;
}; };
// @fmt:off // @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}, {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}}}; {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}, {-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}, {-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}, {-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}, {-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}}}; {-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}, {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}, {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}, {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}}}; {-42, 3751, 26253, 2811}, {-38, 3608, 26270, 2936}, {-34, 3467, 26281, 3064}, {-32, 3329, 26287, 3195}}};
// @fmt:on // @fmt:on
std::vector<i16> Resampler::ResampleBuffer(const std::vector<i16> &inputBuffer, double ratio, u8 channelCount) { std::vector<i16> Resampler::ResampleBuffer(std::span<i16> inputBuffer, double ratio, u8 channelCount) {
auto step = static_cast<u32>(ratio * 0x8000); auto step{static_cast<u32>(ratio * 0x8000)};
auto outputSize = static_cast<size_t>(inputBuffer.size() / ratio); auto outputSize{static_cast<size_t>(inputBuffer.size() / ratio)};
std::vector<i16> outputBuffer(outputSize); std::vector<i16> outputBuffer(outputSize);
const std::array<skyline::audio::LutEntry, 128> &lut = [step] { const std::array<LutEntry, 128> &lut = [step] {
if (step > 0xAAAA) if (step > 0xAAAA)
return CurveLut0; return CurveLut0;
else if (step <= 0x8000) else if (step <= 0x8000)
@ -134,19 +135,19 @@ namespace skyline::audio {
return CurveLut2; return CurveLut2;
}(); }();
for (size_t outIndex = 0, inIndex = 0; outIndex < outputSize; outIndex += channelCount) { for (size_t outIndex{}, inIndex{}; outIndex < outputSize; outIndex += channelCount) {
auto lutIndex = fraction >> 8; 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 + i32 data = inputBuffer[(inIndex + 0) * channelCount + channel] * lut[lutIndex].a +
inputBuffer[(inIndex + 1) * channelCount + channel] * lut[lutIndex].b + inputBuffer[(inIndex + 1) * channelCount + channel] * lut[lutIndex].b +
inputBuffer[(inIndex + 2) * channelCount + channel] * lut[lutIndex].c + inputBuffer[(inIndex + 2) * channelCount + channel] * lut[lutIndex].c +
inputBuffer[(inIndex + 3) * channelCount + channel] * lut[lutIndex].d; 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; inIndex += newOffset >> 15;
fraction = newOffset & 0x7FFF; fraction = newOffset & 0x7FFF;
} }

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include <span>
#include <common.h> #include <common.h>
namespace skyline::audio { namespace skyline::audio {
@ -20,6 +21,6 @@ namespace skyline::audio {
* @param ratio The conversion ratio needed * @param ratio The conversion ratio needed
* @param channelCount The amount of channels the buffer contains * @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> AudioTrack::GetReleasedBuffers(u32 max) {
std::vector<u64> bufferIds; std::vector<u64> bufferIds;
std::unique_lock trackGuard(bufferLock); std::lock_guard trackGuard(bufferLock);
for (u32 index = 0; index < max; index++) { for (u32 index{}; index < max; index++) {
if (!identifiers.back().released) if (identifiers.empty() || !identifiers.back().released)
break; break;
bufferIds.push_back(identifiers.back().tag); bufferIds.push_back(identifiers.back().tag);
identifiers.pop_back(); identifiers.pop_back();
@ -44,29 +44,30 @@ namespace skyline::audio {
return bufferIds; return bufferIds;
} }
void AudioTrack::AppendBuffer(u64 tag, const i16 *address, u64 size) { void AudioTrack::AppendBuffer(u64 tag, std::span<i16> buffer) {
BufferIdentifier identifier; BufferIdentifier identifier{
.released = false,
identifier.released = false; .tag = tag,
identifier.tag = tag; .finalSample = identifiers.empty() ? (buffer.size()) : (buffer.size() + identifiers.front().finalSample)
};
if (identifiers.empty())
identifier.finalSample = size;
else
identifier.finalSample = size + identifiers.front().finalSample;
std::lock_guard guard(bufferLock); std::lock_guard guard(bufferLock);
identifiers.push_front(identifier); identifiers.push_front(identifier);
samples.Append(const_cast<i16 *>(address), size); samples.Append(buffer);
} }
void AudioTrack::CheckReleasedBuffers() { void AudioTrack::CheckReleasedBuffers() {
bool anyReleased{};
for (auto &identifier : identifiers) { for (auto &identifier : identifiers) {
if (identifier.finalSample <= sampleCounter && !identifier.released) { if (identifier.finalSample <= sampleCounter && !identifier.released) {
releaseCallback(); anyReleased = true;
identifier.released = true; identifier.released = true;
} }
} }
if (anyReleased)
releaseCallback();
} }
} }

View File

@ -3,8 +3,8 @@
#pragma once #pragma once
#include <span>
#include <queue> #include <queue>
#include <kernel/types/KEvent.h> #include <kernel/types/KEvent.h>
#include <common.h> #include <common.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::function<void()> releaseCallback; //!< Callback called when a buffer has been played
std::deque<BufferIdentifier> identifiers; //!< Queue of all appended buffer identifiers std::deque<BufferIdentifier> identifiers; //!< Queue of all appended buffer identifiers
u8 channelCount; //!< The amount channels present in the track u8 channelCount;
u32 sampleRate; //!< The sample rate of the track u32 sampleRate;
public: 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 Mutex bufferLock; //!< This mutex ensures that appending to buffers doesn't overlap
AudioOutState playbackState{AudioOutState::Stopped}; //!< The current state of playback 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 * @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 * @brief Appends audio samples to the output buffer
* @param tag The tag of the buffer * @param tag The tag of the buffer
* @param address The address of the audio buffer * @param buffer A span containing the source sample buffer
* @param size The size of the audio buffer in i16 units
*/ */
void AppendBuffer(u64 tag, const i16 *address, u64 size); void AppendBuffer(u64 tag, std::span<i16> buffer = {});
/**
* @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());
}
/** /**
* @brief Checks if any buffers have been released and calls the appropriate callback for them * @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(); void CheckReleasedBuffers();
}; };

View File

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

View File

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

View File

@ -18,14 +18,13 @@ namespace skyline::service::audio {
} }
void IAudioOutManager::OpenAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IAudioOutManager::OpenAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
u32 sampleRate = request.Pop<u32>(); auto sampleRate{request.Pop<u32>()};
request.Pop<u16>(); // Channel count is stored in the upper half of a u32 auto channelCount{static_cast<u16>(request.Pop<u32>())};
u16 channelCount = request.Pop<u16>();
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; 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); manager.RegisterService(std::make_shared<IAudioOut>(state, manager, channelCount, sampleRate), session, response);
response.Push<u32>(sampleRate); 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) { 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); revisionInfo.SetUserRevision(inputHeader.revision);
inputAddress += sizeof(UpdateDataHeader); inputAddress += sizeof(UpdateDataHeader);
inputAddress += inputHeader.behaviorSize; // Unused inputAddress += inputHeader.behaviorSize; // Unused
auto memoryPoolCount = memoryPools.size(); auto memoryPoolCount{memoryPools.size()};
std::vector<MemoryPoolIn> memoryPoolsIn(memoryPoolCount); std::vector<MemoryPoolIn> memoryPoolsIn(memoryPoolCount);
state.process->ReadMemory(memoryPoolsIn.data(), inputAddress, memoryPoolCount * sizeof(MemoryPoolIn)); state.process->ReadMemory(memoryPoolsIn.data(), inputAddress, memoryPoolCount * sizeof(MemoryPoolIn));
inputAddress += inputHeader.memoryPoolSize; inputAddress += inputHeader.memoryPoolSize;
@ -128,11 +128,11 @@ namespace skyline::service::audio::IAudioRenderer {
} }
void IAudioRenderer::UpdateAudio() { void IAudioRenderer::UpdateAudio() {
auto released = track->GetReleasedBuffers(2); auto released{track->GetReleasedBuffers(2)};
for (auto &tag : released) { for (auto &tag : released) {
MixFinalBuffer(); MixFinalBuffer();
track->AppendBuffer(tag, sampleBuffer.data(), sampleBuffer.size()); track->AppendBuffer(tag, sampleBuffer);
} }
} }
@ -149,14 +149,14 @@ namespace skyline::service::audio::IAudioRenderer {
while (pendingSamples > 0) { while (pendingSamples > 0) {
u32 voiceBufferOffset{}; u32 voiceBufferOffset{};
u32 voiceBufferSize{}; u32 voiceBufferSize{};
auto &voiceSamples = voice.GetBufferData(pendingSamples, voiceBufferOffset, voiceBufferSize); auto &voiceSamples{voice.GetBufferData(pendingSamples, voiceBufferOffset, voiceBufferSize)};
if (voiceBufferSize == 0) if (voiceBufferSize == 0)
break; break;
pendingSamples -= voiceBufferSize / constant::ChannelCount; pendingSamples -= voiceBufferSize / constant::ChannelCount;
for (auto index = voiceBufferOffset; index < voiceBufferOffset + voiceBufferSize; index++) { for (auto index{voiceBufferOffset}; index < voiceBufferOffset + voiceBufferSize; index++) {
if (writtenSamples == bufferOffset) { if (writtenSamples == bufferOffset) {
sampleBuffer[bufferOffset] = skyline::audio::Saturate<i16, i32>(voiceSamples[index] * voice.volume); 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) { 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); state.logger->Debug("Audren System Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);
} }

View File

@ -22,39 +22,39 @@ namespace skyline {
* @brief The parameters used to configure an IAudioRenderer * @brief The parameters used to configure an IAudioRenderer
*/ */
struct AudioRendererParameters { struct AudioRendererParameters {
u32 sampleRate; //!< The sample rate to use for the renderer u32 sampleRate;
u32 sampleCount; //!< The buffer sample count u32 sampleCount;
u32 mixBufferCount; //!< The amount of mix buffers to use u32 mixBufferCount;
u32 subMixCount; //!< The amount of sub mixes to use u32 subMixCount;
u32 voiceCount; //!< The amount of voices to use u32 voiceCount;
u32 sinkCount; //!< The amount of sinks to use u32 sinkCount;
u32 effectCount; //!< The amount of effects to use u32 effectCount;
u32 performanceManagerCount; //!< The amount of performance managers to use u32 performanceManagerCount;
u32 voiceDropEnable; //!< Whether to enable voice drop u32 voiceDropEnable;
u32 splitterCount; //!< The amount of splitters to use u32 splitterCount;
u32 splitterDestinationDataCount; //!< The amount of splitter destination outputs to use u32 splitterDestinationDataCount;
u32 _unk0_; u32 _unk0_;
u32 revision; //!< The revision of audren to use u32 revision;
}; };
static_assert(sizeof(AudioRendererParameters) == 0x34); 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 { struct UpdateDataHeader {
u32 revision; //!< Revision of the software implementation u32 revision;
u32 behaviorSize; //!< The total size of the behaviour info u32 behaviorSize;
u32 memoryPoolSize; //!< The total size of all MemoryPoolIn structs u32 memoryPoolSize;
u32 voiceSize; //!< The total size of all VoiceIn structs u32 voiceSize;
u32 voiceResourceSize; //!< The total size of the voice resources u32 voiceResourceSize;
u32 effectSize; //!< The total size of all EffectIn structs u32 effectSize;
u32 mixSize; //!< The total size of all mixer descriptors in the input u32 mixSize;
u32 sinkSize; //!< The total size of all sink descriptors in the input u32 sinkSize;
u32 performanceManagerSize; //!< The total size of all performance manager descriptors in the input u32 performanceManagerSize;
u32 _unk0_; u32 _unk0_;
u32 elapsedFrameCountInfoSize; //!< The total size of all the elapsed frame info u32 elapsedFrameCountInfoSize;
u32 _unk1_[4]; u32 _unk1_[4];
u32 totalSize; //!< The total size of the whole input u32 totalSize;
}; };
static_assert(sizeof(UpdateDataHeader) == 0x40); static_assert(sizeof(UpdateDataHeader) == 0x40);
@ -63,15 +63,15 @@ namespace skyline {
*/ */
class IAudioRenderer : public BaseService { class IAudioRenderer : public BaseService {
private: private:
AudioRendererParameters parameters; //!< The parameters to use for the renderer AudioRendererParameters parameters;
RevisionInfo revisionInfo{}; //!< Stores info about supported features for the audren revision used 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<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::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<MemoryPool> memoryPools;
std::vector<Effect> effects; //!< An vector of all effects that the guest may need std::vector<Effect> effects;
std::vector<Voice> voices; //!< An vector of all voices that the guest may need 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::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 * @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 { enum class EffectState : u8 {
None = 0, //!< The effect isn't being used 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 * @brief This is returned to inform the guest of the state of an effect
*/ */
struct EffectOut { struct EffectOut {
EffectState state; //!< The state of the effect EffectState state;
u8 _pad0_[15]; u8 _pad0_[15];
}; };
static_assert(sizeof(EffectOut) == 0x10); static_assert(sizeof(EffectOut) == 0x10);
@ -38,7 +38,7 @@ namespace skyline::service::audio::IAudioRenderer {
*/ */
class Effect { class Effect {
public: public:
EffectOut output{}; //!< The current output state EffectOut output{};
inline void ProcessInput(const EffectIn &input) { inline void ProcessInput(const EffectIn &input) {
if (input.isNew) 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 * @brief This enumerates various states a memory pool can be in
*/ */
enum class MemoryPoolState : u32 { enum class MemoryPoolState : u32 {
Invalid = 0, //!< The memory pool is invalid Invalid = 0,
Unknown = 1, //!< The memory pool is in an unknown state Unknown = 1,
RequestDetach = 2, //!< The memory pool should be detached from RequestDetach = 2,
Detached = 3, //!< The memory pool has been detached from Detached = 3,
RequestAttach = 4, //!< The memory pool should be attached to RequestAttach = 4,
Attached = 5, //!< The memory pool has been attached to Attached = 5,
Released = 6 //!< The memory pool has been released Released = 6,
}; };
/** /**
* @brief This is in input containing information about a memory pool for use by the dsp * @brief This is in input containing information about a memory pool for use by the dsp
*/ */
struct MemoryPoolIn { struct MemoryPoolIn {
u64 address; //!< The address of the memory pool u64 address;
u64 size; //!< The size of the memory pool u64 size;
MemoryPoolState state; //!< The state that is requested for the memory pool MemoryPoolState state;
u32 _unk0_; u32 _unk0_;
u64 _unk1_; 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 * @brief This is returned to inform the guest of the state of a memory pool
*/ */
struct MemoryPoolOut { struct MemoryPoolOut {
MemoryPoolState state = MemoryPoolState::Detached; //!< The state of the memory pool MemoryPoolState state = MemoryPoolState::Detached;
u32 _unk0_; u32 _unk0_;
u64 _unk1_; u64 _unk1_;
}; };
@ -46,7 +46,7 @@ namespace skyline::service::audio::IAudioRenderer {
*/ */
class MemoryPool { class MemoryPool {
public: 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 * @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 skyline {
namespace constant { namespace constant {
constexpr u32 SupportedRevision = 7; //!< The audren revision our implementation supports 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 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 RevMagic{Rev0Magic + (SupportedRevision << 24)}; //!< The revision magic for our supported revision
namespace supportTags { namespace supportTags {
constexpr u32 Splitter = 2; //!< The revision splitter support 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 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 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 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 ElapsedFrameCount{5}; //!< The revision support for counting elapsed frames was added
}; };
} }

View File

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

View File

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

View File

@ -28,24 +28,24 @@ namespace skyline::service::audio {
IAudioRenderer::RevisionInfo revisionInfo{}; IAudioRenderer::RevisionInfo revisionInfo{};
revisionInfo.SetUserRevision(params.revision); 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 + params.subMixCount * 0x400 +
totalMixCount * 0x940 + totalMixCount * 0x940 +
params.voiceCount * 0x3F0 + params.voiceCount * 0x3F0 +
util::AlignUp(totalMixCount * 8, 16) + util::AlignUp(totalMixCount * 8, 16) +
util::AlignUp(params.voiceCount * 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()) { if (revisionInfo.SplitterSupported()) {
i32 nodeStateWorkSize = util::AlignUp(totalMixCount, constant::BufferAlignment); i32 nodeStateWorkSize{util::AlignUp<i32>(totalMixCount, constant::BufferAlignment)};
if (nodeStateWorkSize < 0) if (nodeStateWorkSize < 0)
nodeStateWorkSize |= 7; nodeStateWorkSize |= 7;
nodeStateWorkSize = 4 * (totalMixCount * totalMixCount) + 12 * totalMixCount + 2 * (nodeStateWorkSize / 8); 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) if (edgeMatrixWorkSize < 0)
edgeMatrixWorkSize |= 7; edgeMatrixWorkSize |= 7;
@ -53,7 +53,7 @@ namespace skyline::service::audio {
size += util::AlignUp(edgeMatrixWorkSize + nodeStateWorkSize, 16); size += util::AlignUp(edgeMatrixWorkSize + nodeStateWorkSize, 16);
} }
i64 splitterWorkSize = 0; i64 splitterWorkSize{};
if (revisionInfo.SplitterSupported()) { if (revisionInfo.SplitterSupported()) {
splitterWorkSize += params.splitterDestinationDataCount * 0xE0 + params.splitterCount * 0x20; 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); 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) { if (params.performanceManagerCount > 0) {
i64 performanceMetricsBufferSize; i64 performanceMetricsBufferSize{};
if (revisionInfo.UsesPerformanceMetricDataFormatV2()) { if (revisionInfo.UsesPerformanceMetricDataFormatV2()) {
performanceMetricsBufferSize = (params.voiceCount + params.effectCount + totalMixCount + params.sinkCount) + 0x990; performanceMetricsBufferSize = (params.voiceCount + params.effectCount + totalMixCount + params.sinkCount) + 0x990;

View File

@ -43,7 +43,7 @@ namespace skyline::service::lm {
LogLevel level; LogLevel level;
u8 verbosity; u8 verbosity;
u32 payloadLength; 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; std::ostringstream logMessage;
logMessage << "Guest log:"; logMessage << "Guest log:";

View File

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

View File

@ -47,7 +47,7 @@ namespace skyline::service::nvdrv::device {
u32 raw; u32 raw;
} flags; } flags;
Fence fence; 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; auto &hostSyncpoint = state.os->serviceManager.GetService<nvdrv::INvDrvServices>(Service::nvdrv_INvDrvServices)->hostSyncpoint;
@ -103,7 +103,7 @@ namespace skyline::service::nvdrv::device {
u32 flags; u32 flags;
Fence fence; Fence fence;
u32 reserved[3]; 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); channelFence.UpdateValue(state.os->serviceManager.GetService<nvdrv::INvDrvServices>(Service::nvdrv_INvDrvServices)->hostSyncpoint);
args.fence = channelFence; args.fence = channelFence;

View File

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