Refactor audio backend and services

Use the new service naming convention.
Move both audout and audren into one directory.
Close the audio stream upon emulator exit.
This commit is contained in:
Billy Laws 2020-02-17 19:11:59 +00:00 committed by ◱ PixelyIon
parent 2e0ac9bdd5
commit 52d47120a8
21 changed files with 174 additions and 160 deletions

View File

@ -52,11 +52,12 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/serviceman.cpp ${source_DIR}/skyline/services/serviceman.cpp
${source_DIR}/skyline/services/sm/IUserInterface.cpp ${source_DIR}/skyline/services/sm/IUserInterface.cpp
${source_DIR}/skyline/services/fatalsrv/IService.cpp ${source_DIR}/skyline/services/fatalsrv/IService.cpp
${source_DIR}/skyline/services/audout/audout.cpp ${source_DIR}/skyline/services/audio/IAudioOutManager.cpp
${source_DIR}/skyline/services/audren/IAudioRendererManager.cpp ${source_DIR}/skyline/services/audio/IAudioOut.cpp
${source_DIR}/skyline/services/audren/IAudioRenderer.cpp ${source_DIR}/skyline/services/audio/IAudioRendererManager.cpp
${source_DIR}/skyline/services/audren/voice.cpp ${source_DIR}/skyline/services/audio/IAudioRenderer/IAudioRenderer.cpp
${source_DIR}/skyline/services/audren/memoryPool.cpp ${source_DIR}/skyline/services/audio/IAudioRenderer/voice.cpp
${source_DIR}/skyline/services/audio/IAudioRenderer/memoryPool.cpp
${source_DIR}/skyline/services/settings/ISystemSettingsServer.cpp ${source_DIR}/skyline/services/settings/ISystemSettingsServer.cpp
${source_DIR}/skyline/services/apm/apm.cpp ${source_DIR}/skyline/services/apm/apm.cpp
${source_DIR}/skyline/services/am/applet.cpp ${source_DIR}/skyline/services/am/applet.cpp

View File

@ -13,7 +13,11 @@ namespace skyline::audio {
outputStream->requestStart(); outputStream->requestStart();
} }
std::shared_ptr<AudioTrack> Audio::OpenTrack(int channelCount, int sampleRate, const std::function<void()> &releaseCallback) { Audio::~Audio() {
outputStream->close();
}
std::shared_ptr<AudioTrack> Audio::OpenTrack(const int channelCount, const int sampleRate, const std::function<void()> &releaseCallback) {
std::shared_ptr<AudioTrack> track = std::make_shared<AudioTrack>(channelCount, sampleRate, releaseCallback); std::shared_ptr<AudioTrack> track = std::make_shared<AudioTrack>(channelCount, sampleRate, releaseCallback);
audioTracks.push_back(track); audioTracks.push_back(track);
@ -27,7 +31,7 @@ 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) {
i16 *destBuffer = static_cast<i16 *>(audioData); i16 *destBuffer = static_cast<i16 *>(audioData);
int setIndex = 0; uint setIndex = 0;
size_t sampleI16Size = static_cast<size_t>(numFrames) * audioStream->getChannelCount(); size_t sampleI16Size = static_cast<size_t>(numFrames) * audioStream->getChannelCount();
for (auto &track : audioTracks) { for (auto &track : audioTracks) {

View File

@ -20,6 +20,11 @@ namespace skyline::audio {
public: public:
Audio(const DeviceState &state); Audio(const DeviceState &state);
/**
* @brief The destructor for the audio class
*/
~Audio();
/** /**
* @brief Opens a new track that can be used to play sound * @brief Opens a new track that can be used to play sound
* @param channelCount The amount channels that are present in the track * @param channelCount The amount channels that are present in the track
@ -27,7 +32,7 @@ namespace skyline::audio {
* @param releaseCallback The callback to call when a buffer has been released * @param releaseCallback The callback to call when a buffer has been released
* @return A shared pointer to a new AudioTrack object * @return A shared pointer to a new AudioTrack object
*/ */
std::shared_ptr<AudioTrack> OpenTrack(int channelCount, int sampleRate, const std::function<void()> &releaseCallback); std::shared_ptr<AudioTrack> OpenTrack(const int channelCount, const int sampleRate, const std::function<void()> &releaseCallback);
/** /**
* @brief Closes a track and frees its data * @brief Closes a track and frees its data

View File

@ -1,7 +1,7 @@
#include "track.h" #include "track.h"
namespace skyline::audio { namespace skyline::audio {
AudioTrack::AudioTrack(int channelCount, int sampleRate, const std::function<void()> &releaseCallback) : channelCount(channelCount), sampleRate(sampleRate), releaseCallback(releaseCallback) { AudioTrack::AudioTrack(const int channelCount, const int sampleRate, const std::function<void()> &releaseCallback) : channelCount(channelCount), sampleRate(sampleRate), releaseCallback(releaseCallback) {
if (sampleRate != constant::SampleRate) if (sampleRate != constant::SampleRate)
throw exception("Unsupported audio sample rate: {}", sampleRate); throw exception("Unsupported audio sample rate: {}", sampleRate);

View File

@ -15,8 +15,8 @@ namespace skyline::audio {
const std::function<void()> releaseCallback; //!< Callback called when a buffer has been played const std::function<void()> releaseCallback; //!< Callback called when a buffer has been played
std::deque<BufferIdentifier> identifierQueue; //!< Queue of all appended buffer identifiers std::deque<BufferIdentifier> identifierQueue; //!< Queue of all appended buffer identifiers
int channelCount; //!< The amount channels present in the track const int channelCount; //!< The amount channels present in the track
int sampleRate; //!< The sample rate of the track const int sampleRate; //!< The sample rate of the track
public: public:
std::queue<i16> sampleQueue; //!< Queue of all appended buffer data std::queue<i16> sampleQueue; //!< Queue of all appended buffer data
@ -30,7 +30,7 @@ namespace skyline::audio {
* @param sampleRate The sample rate to use for the track * @param sampleRate The sample rate to use for the track
* @param releaseCallback A callback to call when a buffer has been played * @param releaseCallback A callback to call when a buffer has been played
*/ */
AudioTrack(int channelCount, int sampleRate, const std::function<void()> &releaseCallback); AudioTrack(const int channelCount, const int sampleRate, const std::function<void()> &releaseCallback);
/** /**
* @brief Starts audio playback using data from appended buffers. * @brief Starts audio playback using data from appended buffers.

View File

@ -1,36 +1,8 @@
#include "audout.h"
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include "IAudioOut.h"
namespace skyline::service::audout { namespace skyline::service::audio {
audoutU::audoutU(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::audout_u, "audout:u", { IAudioOut::IAudioOut(const DeviceState &state, ServiceManager &manager, const int channelCount, const int sampleRate) : sampleRate(sampleRate), channelCount(channelCount), releaseEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager, Service::audio_IAudioOut, "audio:IAudioOut", {
{0x0, SFUNC(audoutU::ListAudioOuts)},
{0x1, SFUNC(audoutU::OpenAudioOut)}
}) {}
void audoutU::ListAudioOuts(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
state.process->WriteMemory(reinterpret_cast<void *>(const_cast<char *>(constant::DefaultAudioOutName.data())),
request.outputBuf.at(0).address, constant::DefaultAudioOutName.size());
}
void audoutU::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>();
state.logger->Debug("audoutU: Opening an IAudioOut with sample rate: {}, channel count: {}", sampleRate, channelCount);
sampleRate = sampleRate ? sampleRate : audio::constant::SampleRate;
channelCount = channelCount ? channelCount : static_cast<u16>(audio::constant::ChannelCount);
manager.RegisterService(std::make_shared<IAudioOut>(state, manager, channelCount, sampleRate), session, response);
response.Push<u32>(sampleRate);
response.Push<u16>(channelCount);
response.Push<u16>(0);
response.Push(static_cast<u32>(audio::PcmFormat::Int16));
response.Push(static_cast<u32>(audio::AudioOutState::Stopped));
}
IAudioOut::IAudioOut(const DeviceState &state, ServiceManager &manager, int channelCount, int sampleRate) : sampleRate(sampleRate), channelCount(channelCount), releaseEvent(std::make_shared<type::KEvent>(state)), BaseService(state, manager, Service::audout_IAudioOut, "audout:IAudioOut", {
{0x0, SFUNC(IAudioOut::GetAudioOutState)}, {0x0, SFUNC(IAudioOut::GetAudioOutState)},
{0x1, SFUNC(IAudioOut::StartAudioOut)}, {0x1, SFUNC(IAudioOut::StartAudioOut)},
{0x2, SFUNC(IAudioOut::StopAudioOut)}, {0x2, SFUNC(IAudioOut::StopAudioOut)},
@ -39,7 +11,7 @@ namespace skyline::service::audout {
{0x5, SFUNC(IAudioOut::GetReleasedAudioOutBuffer)}, {0x5, SFUNC(IAudioOut::GetReleasedAudioOutBuffer)},
{0x6, SFUNC(IAudioOut::ContainsAudioOutBuffer)} {0x6, SFUNC(IAudioOut::ContainsAudioOutBuffer)}
}) { }) {
track = state.audio->OpenTrack(channelCount, audio::constant::SampleRate, [this]() { this->releaseEvent->Signal(); }); track = state.audio->OpenTrack(channelCount, skyline::audio::constant::SampleRate, [this]() { this->releaseEvent->Signal(); });
} }
IAudioOut::~IAudioOut() { IAudioOut::~IAudioOut() {
@ -74,7 +46,7 @@ namespace skyline::service::audout {
tmpSampleBuffer.resize(data.sampleSize / sizeof(i16)); tmpSampleBuffer.resize(data.sampleSize / sizeof(i16));
state.process->ReadMemory(tmpSampleBuffer.data(), data.sampleBufferPtr, data.sampleSize); state.process->ReadMemory(tmpSampleBuffer.data(), data.sampleBufferPtr, data.sampleSize);
resampler.ResampleBuffer(tmpSampleBuffer, static_cast<double>(sampleRate) / audio::constant::SampleRate, channelCount); resampler.ResampleBuffer(tmpSampleBuffer, static_cast<double>(sampleRate) / skyline::audio::constant::SampleRate, channelCount);
track->AppendBuffer(tmpSampleBuffer, tag); track->AppendBuffer(tmpSampleBuffer, tag);
} }

View File

@ -1,53 +1,31 @@
#pragma once #pragma once
#include <audio/resampler.h> #include <kernel/types/KEvent.h>
#include <audio.h>
#include <services/base_service.h> #include <services/base_service.h>
#include <services/serviceman.h> #include <services/serviceman.h>
#include <kernel/types/KEvent.h> #include <audio/resampler.h>
#include <audio.h>
namespace skyline::service::audout {
namespace constant {
constexpr std::string_view DefaultAudioOutName = "DeviceOut"; //!< The default audio output device name
};
namespace skyline::service::audio {
/** /**
* @brief audout:u or IAudioOutManager is used to manage audio outputs (https://switchbrew.org/wiki/Audio_services#audout:u) * @brief IAudioOut is a service opened when OpenAudioOut is called by IAudioOutManager (https://switchbrew.org/wiki/Audio_services#IAudioOut)
*/
class audoutU : public BaseService {
public:
audoutU(const DeviceState &state, ServiceManager &manager);
/**
* @brief Returns a list of all available audio outputs (https://switchbrew.org/wiki/Audio_services#ListAudioOuts)
*/
void ListAudioOuts(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Creates a new audoutU::IAudioOut object and returns a handle to it (https://switchbrew.org/wiki/Audio_services#OpenAudioOut)
*/
void OpenAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
};
/**
* @brief IAudioOut is a service opened when OpenAudioOut is called by audout (https://switchbrew.org/wiki/Audio_services#IAudioOut)
*/ */
class IAudioOut : public BaseService { class IAudioOut : public BaseService {
private: private:
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<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 std::vector<i16> tmpSampleBuffer; //!< A temporary buffer used to store sample data in AppendAudioOutBuffer
int sampleRate; //!< The sample rate of the audio out const int sampleRate; //!< The sample rate of the audio out
int channelCount; //!< The amount of channels in the data sent to the audio out const int channelCount; //!< The amount of channels in the data sent to the audio out
public: public:
/** /**
* @param channelCount The channel count of the audio data the audio out will be fed * @param channelCount The channel count of the audio data the audio out will be fed
* @param sampleRate The sample rate of the audio data the audio out will be fed * @param sampleRate The sample rate of the audio data the audio out will be fed
*/ */
IAudioOut(const DeviceState &state, ServiceManager &manager, int channelCount, int sampleRate); IAudioOut(const DeviceState &state, ServiceManager &manager, const int channelCount, const int sampleRate);
/** /**
* @brief Closes the audio track * @brief Closes the audio track

View File

@ -0,0 +1,33 @@
#include <kernel/types/KProcess.h>
#include "IAudioOutManager.h"
#include "IAudioOut.h"
namespace skyline::service::audio {
IAudioOutManager::IAudioOutManager(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::audio_IAudioOutManager, "audio:IAudioOutManager", {
{0x0, SFUNC(IAudioOutManager::ListAudioOuts)},
{0x1, SFUNC(IAudioOutManager::OpenAudioOut)}
}) {}
void IAudioOutManager::ListAudioOuts(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
state.process->WriteMemory(reinterpret_cast<void *>(const_cast<char *>(constant::DefaultAudioOutName.data())),
request.outputBuf.at(0).address, constant::DefaultAudioOutName.size());
}
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>();
state.logger->Debug("audoutU: Opening an IAudioOut with sample rate: {}, channel count: {}", sampleRate, channelCount);
sampleRate = sampleRate ? sampleRate : skyline::audio::constant::SampleRate;
channelCount = channelCount ? channelCount : static_cast<u16>(skyline::audio::constant::ChannelCount);
manager.RegisterService(std::make_shared<IAudioOut>(state, manager, channelCount, sampleRate), session, response);
response.Push<u32>(sampleRate);
response.Push<u16>(channelCount);
response.Push<u16>(0);
response.Push(static_cast<u32>(skyline::audio::PcmFormat::Int16));
response.Push(static_cast<u32>(skyline::audio::AudioOutState::Stopped));
}
}

View File

@ -0,0 +1,29 @@
#pragma once
#include <kernel/types/KEvent.h>
#include <services/base_service.h>
#include <services/serviceman.h>
namespace skyline::service::audio {
namespace constant {
constexpr std::string_view DefaultAudioOutName = "DeviceOut"; //!< The default audio output device name
};
/**
* @brief IAudioOutManager or audout:u is used to manage audio outputs (https://switchbrew.org/wiki/Audio_services#audout:u)
*/
class IAudioOutManager : public BaseService {
public:
IAudioOutManager(const DeviceState &state, ServiceManager &manager);
/**
* @brief Returns a list of all available audio outputs (https://switchbrew.org/wiki/Audio_services#ListAudioOuts)
*/
void ListAudioOuts(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Creates a new audoutU::IAudioOut object and returns a handle to it (https://switchbrew.org/wiki/Audio_services#OpenAudioOut)
*/
void OpenAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
};
}

View File

@ -1,8 +1,9 @@
#include "IAudioRenderer.h"
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include "IAudioRenderer.h"
namespace skyline::service::audren { namespace skyline::service::audio::IAudioRenderer {
IAudioRenderer::IAudioRenderer(const DeviceState &state, ServiceManager &manager, AudioRendererParams &params) : releaseEvent(std::make_shared<type::KEvent>(state)), rendererParams(params), BaseService(state, manager, Service::IAudioRenderer, "IAudioRenderer", { IAudioRenderer::IAudioRenderer(const DeviceState &state, ServiceManager &manager, AudioRendererParams &params)
: releaseEvent(std::make_shared<type::KEvent>(state)), rendererParams(params), memoryPoolCount(params.effectCount + params.voiceCount * 4), samplesPerBuffer(state.settings->GetInt("audren_buffer_size")), BaseService(state, manager, Service::audio_IAudioRenderer, "audio:IAudioRenderer", {
{0x0, SFUNC(IAudioRenderer::GetSampleRate)}, {0x0, SFUNC(IAudioRenderer::GetSampleRate)},
{0x1, SFUNC(IAudioRenderer::GetSampleCount)}, {0x1, SFUNC(IAudioRenderer::GetSampleCount)},
{0x2, SFUNC(IAudioRenderer::GetMixBufferCount)}, {0x2, SFUNC(IAudioRenderer::GetMixBufferCount)},
@ -12,11 +13,9 @@ namespace skyline::service::audren {
{0x6, SFUNC(IAudioRenderer::Stop)}, {0x6, SFUNC(IAudioRenderer::Stop)},
{0x7, SFUNC(IAudioRenderer::QuerySystemEvent)}, {0x7, SFUNC(IAudioRenderer::QuerySystemEvent)},
}) { }) {
track = state.audio->OpenTrack(audio::constant::ChannelCount, params.sampleRate, [this]() { this->releaseEvent->Signal(); }); track = state.audio->OpenTrack(skyline::audio::constant::ChannelCount, params.sampleRate, [this]() { this->releaseEvent->Signal(); });
track->Start(); track->Start();
samplesPerBuffer = state.settings->GetInt("audren_buffer_size");
memoryPoolCount = rendererParams.effectCount + rendererParams.voiceCount * 4;
memoryPools.resize(memoryPoolCount); memoryPools.resize(memoryPoolCount);
effects.resize(rendererParams.effectCount); effects.resize(rendererParams.effectCount);
voices.resize(rendererParams.voiceCount, Voice(state)); voices.resize(rendererParams.voiceCount, Voice(state));
@ -78,7 +77,7 @@ namespace skyline::service::audren {
UpdateAudio(); UpdateAudio();
UpdateDataHeader outputHeader { UpdateDataHeader outputHeader{
.revision = constant::RevMagic, .revision = constant::RevMagic,
.behaviorSize = 0xb0, .behaviorSize = 0xb0,
.memoryPoolSize = (rendererParams.effectCount + rendererParams.voiceCount * 4) * static_cast<u32>(sizeof(MemoryPoolOut)), .memoryPoolSize = (rendererParams.effectCount + rendererParams.voiceCount * 4) * static_cast<u32>(sizeof(MemoryPoolOut)),
@ -133,7 +132,7 @@ namespace skyline::service::audren {
void IAudioRenderer::MixFinalBuffer() { void IAudioRenderer::MixFinalBuffer() {
int setIndex = 0; int setIndex = 0;
sampleBuffer.resize(samplesPerBuffer * audio::constant::ChannelCount); sampleBuffer.resize(samplesPerBuffer * skyline::audio::constant::ChannelCount);
for (auto &voice : voices) { for (auto &voice : voices) {
if (!voice.Playable()) if (!voice.Playable())
@ -150,7 +149,7 @@ namespace skyline::service::audren {
if (voiceBufferSize == 0) if (voiceBufferSize == 0)
break; break;
pendingSamples -= voiceBufferSize / audio::constant::ChannelCount; pendingSamples -= voiceBufferSize / skyline::audio::constant::ChannelCount;
for (int i = voiceBufferOffset; i < voiceBufferOffset + voiceBufferSize; i++) { for (int i = voiceBufferOffset; i < voiceBufferOffset + voiceBufferSize; i++) {
if (setIndex == bufferOffset) { if (setIndex == bufferOffset) {
@ -160,8 +159,8 @@ namespace skyline::service::audren {
setIndex++; setIndex++;
} else { } else {
sampleBuffer[bufferOffset] += static_cast<i16>(std::clamp(static_cast<int>(sampleBuffer[voiceSamples[i]]) + sampleBuffer[bufferOffset] += static_cast<i16>(std::clamp(static_cast<int>(sampleBuffer[voiceSamples[i]]) +
static_cast<int>(static_cast<float>(voiceSamples[i]) * voice.volume), static_cast<int>(static_cast<float>(voiceSamples[i]) * voice.volume),
static_cast<int>(std::numeric_limits<i16>::min()), static_cast<int>(std::numeric_limits<i16>::max()))); static_cast<int>(std::numeric_limits<i16>::min()), static_cast<int>(std::numeric_limits<i16>::max())));
} }
bufferOffset++; bufferOffset++;
@ -171,11 +170,11 @@ namespace skyline::service::audren {
} }
void IAudioRenderer::Start(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IAudioRenderer::Start(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
playbackState = audio::AudioOutState::Started; playbackState = skyline::audio::AudioOutState::Started;
} }
void IAudioRenderer::Stop(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IAudioRenderer::Stop(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
playbackState = audio::AudioOutState::Stopped; playbackState = skyline::audio::AudioOutState::Stopped;
} }
void IAudioRenderer::QuerySystemEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IAudioRenderer::QuerySystemEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {

View File

@ -1,15 +1,15 @@
#pragma once #pragma once
#include <audio.h> #include <kernel/types/KEvent.h>
#include <services/base_service.h> #include <services/base_service.h>
#include <services/serviceman.h> #include <services/serviceman.h>
#include <kernel/types/KEvent.h> #include <audio.h>
#include "memoryPool.h" #include "memoryPool.h"
#include "effect.h" #include "effect.h"
#include "voice.h" #include "voice.h"
#include "revisionInfo.h" #include "revisionInfo.h"
namespace skyline::service::audren { namespace skyline::service::audio::IAudioRenderer {
namespace constant { namespace constant {
constexpr int BufferAlignment = 0x40; //!< The alignment for all audren buffers constexpr int BufferAlignment = 0x40; //!< The alignment for all audren buffers
} }
@ -58,18 +58,18 @@ namespace skyline::service::audren {
*/ */
class IAudioRenderer : public BaseService { class IAudioRenderer : public BaseService {
private: private:
std::shared_ptr<audio::AudioTrack> track; //!< The audio track associated with the audio renderer
AudioRendererParams rendererParams; //!< The parameters to use for the renderer AudioRendererParams rendererParams; //!< The parameters to use for the renderer
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> 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<MemoryPool> memoryPools; //!< An vector of all memory pools that the guest may need 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<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<Voice> voices; //!< An vector of all voices that the guest may need
std::vector<i16> sampleBuffer; //!< The final output data that is appended to the stream std::vector<i16> sampleBuffer; //!< The final output data that is appended to the stream
RevisionInfo revisionInfo{}; //!< Stores info about supported features for the audren revision used
audio::AudioOutState playbackState{audio::AudioOutState::Stopped}; //!< The current state of playback skyline::audio::AudioOutState playbackState{skyline::audio::AudioOutState::Stopped}; //!< The current state of playback
size_t memoryPoolCount{}; //!< The amount of memory pools the guest may need const size_t memoryPoolCount; //!< The amount of memory pools the guest may need
int samplesPerBuffer{}; //!< The amount of samples each appended buffer should contain const int samplesPerBuffer; //!< The amount of samples each appended buffer should contain
/** /**
* @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

@ -2,7 +2,7 @@
#include <common.h> #include <common.h>
namespace skyline::service::audren { namespace skyline::service::audio::IAudioRenderer {
/** /**
* @brief This enumerates various states an effect can be in * @brief This enumerates various states an effect can be in
*/ */

View File

@ -1,7 +1,6 @@
#include <common.h>
#include "memoryPool.h" #include "memoryPool.h"
namespace skyline::service::audren { namespace skyline::service::audio::IAudioRenderer {
void MemoryPool::ProcessInput(const MemoryPoolIn &input) { void MemoryPool::ProcessInput(const MemoryPoolIn &input) {
if (input.state == MemoryPoolState::RequestAttach) if (input.state == MemoryPoolState::RequestAttach)
output.state = MemoryPoolState::Attached; output.state = MemoryPoolState::Attached;

View File

@ -2,7 +2,7 @@
#include <common.h> #include <common.h>
namespace skyline::service::audren { 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
*/ */

View File

@ -2,7 +2,7 @@
#include <common.h> #include <common.h>
namespace skyline::service::audren { namespace skyline::service::audio::IAudioRenderer {
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 = ('R' << 0) | ('E' << 8) | ('V' << 16) | ('0' << 24); //!< The HOS 1.0 revision magic constexpr u32 Rev0Magic = ('R' << 0) | ('E' << 8) | ('V' << 16) | ('0' << 24); //!< The HOS 1.0 revision magic

View File

@ -1,8 +1,7 @@
#include <common.h>
#include "voice.h"
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include "voice.h"
namespace skyline::service::audren { namespace skyline::service::audio::IAudioRenderer {
Voice::Voice(const DeviceState &state) : state(state) {} Voice::Voice(const DeviceState &state) : state(state) {}
void Voice::ProcessInput(const VoiceIn &input) { void Voice::ProcessInput(const VoiceIn &input) {
@ -23,7 +22,7 @@ namespace skyline::service::audren {
return; return;
if (input.firstUpdate) { if (input.firstUpdate) {
if (input.pcmFormat != audio::PcmFormat::Int16) if (input.pcmFormat != skyline::audio::PcmFormat::Int16)
throw exception("Unsupported voice PCM format: {}", input.pcmFormat); throw exception("Unsupported voice PCM format: {}", input.pcmFormat);
pcmFormat = input.pcmFormat; pcmFormat = input.pcmFormat;
@ -48,7 +47,7 @@ namespace skyline::service::audren {
return; return;
switch (pcmFormat) { switch (pcmFormat) {
case audio::PcmFormat::Int16: case skyline::audio::PcmFormat::Int16:
sampleBuffer.resize(currentBuffer.size / sizeof(i16)); sampleBuffer.resize(currentBuffer.size / sizeof(i16));
state.process->ReadMemory(sampleBuffer.data(), currentBuffer.position, currentBuffer.size); state.process->ReadMemory(sampleBuffer.data(), currentBuffer.position, currentBuffer.size);
break; break;
@ -56,15 +55,15 @@ namespace skyline::service::audren {
throw exception("Unsupported voice PCM format: {}", pcmFormat); throw exception("Unsupported voice PCM format: {}", pcmFormat);
} }
if (sampleRate != audio::constant::SampleRate) if (sampleRate != skyline::audio::constant::SampleRate)
sampleBuffer = resampler.ResampleBuffer(sampleBuffer, static_cast<double>(sampleRate) / audio::constant::SampleRate, channelCount); sampleBuffer = resampler.ResampleBuffer(sampleBuffer, static_cast<double>(sampleRate) / skyline::audio::constant::SampleRate, channelCount);
if (channelCount == 1 && audio::constant::ChannelCount != channelCount) { if (channelCount == 1 && skyline::audio::constant::ChannelCount != channelCount) {
size_t originalSize = sampleBuffer.size(); size_t originalSize = sampleBuffer.size();
sampleBuffer.resize((originalSize / channelCount) * audio::constant::ChannelCount); sampleBuffer.resize((originalSize / channelCount) * skyline::audio::constant::ChannelCount);
for (size_t monoIndex = originalSize - 1, targetIndex = sampleBuffer.size(); monoIndex > 0; monoIndex--) for (size_t monoIndex = originalSize - 1, targetIndex = sampleBuffer.size(); monoIndex > 0; monoIndex--)
for (uint i = 0; i < audio::constant::ChannelCount; i++) for (uint i = 0; i < skyline::audio::constant::ChannelCount; i++)
sampleBuffer[--targetIndex] = sampleBuffer[monoIndex]; sampleBuffer[--targetIndex] = sampleBuffer[monoIndex];
} }
} }
@ -72,7 +71,7 @@ namespace skyline::service::audren {
std::vector<i16> &Voice::GetBufferData(int maxSamples, int &outOffset, int &outSize) { std::vector<i16> &Voice::GetBufferData(int maxSamples, int &outOffset, int &outSize) {
WaveBuffer &currentBuffer = waveBuffers.at(bufferIndex); WaveBuffer &currentBuffer = waveBuffers.at(bufferIndex);
if (!acquired || playbackState != audio::AudioOutState::Started) { if (!acquired || playbackState != skyline::audio::AudioOutState::Started) {
outSize = 0; outSize = 0;
return sampleBuffer; return sampleBuffer;
} }
@ -83,16 +82,16 @@ namespace skyline::service::audren {
} }
outOffset = sampleOffset; outOffset = sampleOffset;
outSize = std::min(maxSamples * audio::constant::ChannelCount, static_cast<int>(sampleBuffer.size() - sampleOffset)); outSize = std::min(maxSamples * skyline::audio::constant::ChannelCount, static_cast<int>(sampleBuffer.size() - sampleOffset));
output.playedSamplesCount += outSize / audio::constant::ChannelCount; output.playedSamplesCount += outSize / skyline::audio::constant::ChannelCount;
sampleOffset += outSize; sampleOffset += outSize;
if (sampleOffset == sampleBuffer.size()) { if (sampleOffset == sampleBuffer.size()) {
sampleOffset = 0; sampleOffset = 0;
if (currentBuffer.lastBuffer) if (currentBuffer.lastBuffer)
playbackState = audio::AudioOutState::Paused; playbackState = skyline::audio::AudioOutState::Paused;
if (!currentBuffer.looping) if (!currentBuffer.looping)
SetWaveBufferIndex(bufferIndex + 1); SetWaveBufferIndex(bufferIndex + 1);

View File

@ -1,11 +1,11 @@
#pragma once #pragma once
#include <common.h>
#include <array> #include <array>
#include <audio.h>
#include <audio/resampler.h> #include <audio/resampler.h>
#include <audio.h>
#include <common.h>
namespace skyline::service::audren { namespace skyline::service::audio::IAudioRenderer {
/** /**
* @brief This stores data for configuring a biquadratic filter * @brief This stores data for configuring a biquadratic filter
*/ */
@ -46,8 +46,8 @@ namespace skyline::service::audren {
u32 nodeId; //!< The node ID of the voice u32 nodeId; //!< The node ID of the voice
u8 firstUpdate; //!< Whether this voice is new u8 firstUpdate; //!< Whether this voice is new
u8 acquired; //!< Whether the sample is in use u8 acquired; //!< Whether the sample is in use
audio::AudioOutState playbackState; //!< The playback state skyline::audio::AudioOutState playbackState; //!< The playback state
audio::PcmFormat pcmFormat; //!< The sample format skyline::audio::PcmFormat pcmFormat; //!< The sample format
u32 sampleRate; //!< The sample rate u32 sampleRate; //!< The sample rate
u32 priority; //!< The priority for this voice u32 priority; //!< The priority for this voice
u32 _unk0_; u32 _unk0_;
@ -87,7 +87,7 @@ namespace skyline::service::audren {
const DeviceState &state; //!< The emulator state object const DeviceState &state; //!< The emulator state object
std::array<WaveBuffer, 4> waveBuffers; //!< An array containing the state of all four wave buffers std::array<WaveBuffer, 4> waveBuffers; //!< An array containing the state of all four wave buffers
std::vector<i16> sampleBuffer; //!< A buffer containing processed sample data std::vector<i16> sampleBuffer; //!< A buffer containing processed sample data
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 stream
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}; //!< If the buffer needs to be updated
@ -95,8 +95,8 @@ namespace skyline::service::audren {
int sampleOffset{}; //!< The offset in the sample data of the current wave buffer int sampleOffset{}; //!< The offset in the sample data of the current wave buffer
int sampleRate{}; //!< The sample rate of the sample data int sampleRate{}; //!< The sample rate of the sample data
int channelCount{}; //!< The amount of channels in the sample data int channelCount{}; //!< The amount of channels in the sample data
audio::AudioOutState playbackState{audio::AudioOutState::Stopped}; //!< The playback state of the voice skyline::audio::AudioOutState playbackState{skyline::audio::AudioOutState::Stopped}; //!< The playback state of the voice
audio::PcmFormat pcmFormat{audio::PcmFormat::Invalid}; //!< The PCM format used for guest audio data skyline::audio::PcmFormat pcmFormat{skyline::audio::PcmFormat::Invalid}; //!< The PCM format used for guest audio data
/** /**
* @brief Updates the sample buffer with data from the current wave buffer and processes it * @brief Updates the sample buffer with data from the current wave buffer and processes it
@ -132,7 +132,7 @@ namespace skyline::service::audren {
* @return Whether the voice is currently playable * @return Whether the voice is currently playable
*/ */
inline bool Playable() { inline bool Playable() {
return acquired && playbackState == audio::AudioOutState::Started && waveBuffers[bufferIndex].size != 0; return acquired && playbackState == skyline::audio::AudioOutState::Started && waveBuffers[bufferIndex].size != 0;
} }
}; };
} }

View File

@ -1,52 +1,49 @@
#include "IAudioRenderer.h"
#include "IAudioRendererManager.h"
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include "IAudioRenderer/IAudioRenderer.h"
#include "IAudioRendererManager.h"
namespace skyline::service::audren { namespace skyline::service::audio {
IAudioRendererManager::IAudioRendererManager(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::IAudioRendererManager, "IAudioRendererManager", { IAudioRendererManager::IAudioRendererManager(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::audio_IAudioRendererManager, "audio:IAudioRendererManager", {
{0x0, SFUNC(IAudioRendererManager::OpenAudioRenderer)}, {0x0, SFUNC(IAudioRendererManager::OpenAudioRenderer)},
{0x1, SFUNC(IAudioRendererManager::GetAudioRendererWorkBufferSize)} {0x1, SFUNC(IAudioRendererManager::GetAudioRendererWorkBufferSize)}
}) {} }) {}
void IAudioRendererManager::OpenAudioRenderer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IAudioRendererManager::OpenAudioRenderer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
AudioRendererParams params = request.Pop<AudioRendererParams>(); IAudioRenderer::AudioRendererParams params = request.Pop<IAudioRenderer::AudioRendererParams>();
state.logger->Debug("IAudioRendererManager: Opening a rev {} IAudioRenderer with sample rate: {}, voice count: {}, effect count: {}", state.logger->Debug("IAudioRendererManager: Opening a rev {} IAudioRenderer with sample rate: {}, voice count: {}, effect count: {}",
ExtractVersionFromRevision(params.revision), params.sampleRate, params.voiceCount, params.effectCount); IAudioRenderer::ExtractVersionFromRevision(params.revision), params.sampleRate, params.voiceCount, params.effectCount);
manager.RegisterService(std::make_shared<IAudioRenderer>(state, manager, params), session, response);
manager.RegisterService(std::make_shared<IAudioRenderer::IAudioRenderer>(state, manager, params), session, response);
} }
void IAudioRendererManager::GetAudioRendererWorkBufferSize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IAudioRendererManager::GetAudioRendererWorkBufferSize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
AudioRendererParams params = request.Pop<AudioRendererParams>(); IAudioRenderer::AudioRendererParams params = request.Pop<IAudioRenderer::AudioRendererParams>();
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 = utils::AlignUp(params.mixBufferCount * 4, constant::BufferAlignment) + i64 size = utils::AlignUp(params.mixBufferCount * 4, IAudioRenderer::constant::BufferAlignment) +
params.subMixCount * 0x400 + params.subMixCount * 0x400 +
totalMixCount * 0x940 + totalMixCount * 0x940 +
params.voiceCount * 0x3F0 + params.voiceCount * 0x3F0 +
utils::AlignUp(totalMixCount * 8, 16) + utils::AlignUp(totalMixCount * 8, 16) +
utils::AlignUp(params.voiceCount * 8, 16) + utils::AlignUp(params.voiceCount * 8, 16) +
utils::AlignUp(((params.sinkCount + params.subMixCount) * 0x3C0 + params.sampleCount * 4) * (params.mixBufferCount + 6), constant::BufferAlignment) + utils::AlignUp(((params.sinkCount + params.subMixCount) * 0x3C0 + params.sampleCount * 4) * (params.mixBufferCount + 6), IAudioRenderer::constant::BufferAlignment) +
(params.sinkCount + params.subMixCount) * 0x2C0 + (params.sinkCount + params.subMixCount) * 0x2C0 +
(params.effectCount + params.voiceCount * 4) * 0x30 + (params.effectCount + params.voiceCount * 4) * 0x30 +
0x50; 0x50;
if (revisionInfo.SplitterSupported()) { if (revisionInfo.SplitterSupported()) {
i32 nodeStateWorkSize = utils::AlignUp(totalMixCount, constant::BufferAlignment); i32 nodeStateWorkSize = utils::AlignUp(totalMixCount, IAudioRenderer::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 = utils::AlignUp(totalMixCount * totalMixCount, constant::BufferAlignment); i32 edgeMatrixWorkSize = utils::AlignUp(totalMixCount * totalMixCount, IAudioRenderer::constant::BufferAlignment);
if (edgeMatrixWorkSize < 0) if (edgeMatrixWorkSize < 0)
edgeMatrixWorkSize |= 7; edgeMatrixWorkSize |= 7;

View File

@ -1,25 +1,23 @@
#pragma once #pragma once
#include <audio.h>
#include <services/base_service.h> #include <services/base_service.h>
#include <services/serviceman.h> #include <services/serviceman.h>
#include <kernel/types/KEvent.h>
namespace skyline::service::audren { namespace skyline::service::audio {
/** /**
* @brief audren:u or IAudioRendererManager is used to manage audio renderer outputs (https://switchbrew.org/wiki/Audio_services#audren:u) * @brief IAudioRendererManager or audren:u is used to manage audio renderer outputs (https://switchbrew.org/wiki/Audio_services#audren:u)
*/ */
class IAudioRendererManager : public BaseService { class IAudioRendererManager : public BaseService {
public: public:
IAudioRendererManager(const DeviceState &state, ServiceManager &manager); IAudioRendererManager(const DeviceState &state, ServiceManager &manager);
/** /**
* @brief Creates a new audrenU::IAudioRenderer object and returns a handle to it * @brief Creates a new IAudioRenderer object and returns a handle to it
*/ */
void OpenAudioRenderer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); void OpenAudioRenderer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/** /**
* @brief Calculates the size of the buffer the guest needs to allocate for audren * @brief Calculates the size of the buffer the guest needs to allocate for IAudioRendererManager
*/ */
void GetAudioRendererWorkBufferSize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); void GetAudioRendererWorkBufferSize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
}; };

View File

@ -38,10 +38,10 @@ namespace skyline::service {
am_ILibraryAppletCreator, am_ILibraryAppletCreator,
am_IDebugFunctions, am_IDebugFunctions,
am_IAppletCommonFunctions, am_IAppletCommonFunctions,
audout_u, audio_IAudioOutManager,
audout_IAudioOut, audio_IAudioOut,
IAudioRendererManager, audio_IAudioRendererManager,
IAudioRenderer, audio_IAudioRenderer,
hid_IHidServer, hid_IHidServer,
hid_IAppletResource, hid_IAppletResource,
timesrv_IStaticService, timesrv_IStaticService,
@ -66,8 +66,8 @@ namespace skyline::service {
{"apm", Service::apm}, {"apm", Service::apm},
{"appletOE", Service::am_appletOE}, {"appletOE", Service::am_appletOE},
{"appletAE", Service::am_appletAE}, {"appletAE", Service::am_appletAE},
{"audout:u", Service::audout_u}, {"audout:u", Service::audio_IAudioOutManager},
{"audren:u", Service::IAudioRendererManager}, {"audren:u", Service::audio_IAudioRendererManager},
{"hid", Service::hid_IHidServer}, {"hid", Service::hid_IHidServer},
{"time:s", Service::timesrv_IStaticService}, {"time:s", Service::timesrv_IStaticService},
{"time:a", Service::timesrv_IStaticService}, {"time:a", Service::timesrv_IStaticService},

View File

@ -1,11 +1,11 @@
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <services/audren/IAudioRendererManager.h>
#include "sm/IUserInterface.h" #include "sm/IUserInterface.h"
#include "settings/ISystemSettingsServer.h" #include "settings/ISystemSettingsServer.h"
#include "apm/apm.h" #include "apm/apm.h"
#include "am/applet.h" #include "am/applet.h"
#include "am/appletController.h" #include "am/appletController.h"
#include "audout/audout.h" #include "audio/IAudioOutManager.h"
#include "audio/IAudioRendererManager.h"
#include "fatalsrv/IService.h" #include "fatalsrv/IService.h"
#include "hid/IHidServer.h" #include "hid/IHidServer.h"
#include "timesrv/IStaticService.h" #include "timesrv/IStaticService.h"
@ -41,11 +41,11 @@ namespace skyline::service {
case Service::am_appletAE: case Service::am_appletAE:
serviceObj = std::make_shared<am::appletAE>(state, *this); serviceObj = std::make_shared<am::appletAE>(state, *this);
break; break;
case Service::audout_u: case Service::audio_IAudioOutManager:
serviceObj = std::make_shared<audout::audoutU>(state, *this); serviceObj = std::make_shared<audio::IAudioOutManager>(state, *this);
break; break;
case Service::IAudioRendererManager: case Service::audio_IAudioRendererManager:
serviceObj = std::make_shared<audren::IAudioRendererManager>(state, *this); serviceObj = std::make_shared<audio::IAudioRendererManager>(state, *this);
break; break;
case Service::hid_IHidServer: case Service::hid_IHidServer:
serviceObj = std::make_shared<hid::IHidServer>(state, *this); serviceObj = std::make_shared<hid::IHidServer>(state, *this);