// SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include #include "IAudioOut.h" namespace skyline::service::audio { IAudioOut::IAudioOut(const DeviceState &state, ServiceManager &manager, const u8 channelCount, const u32 sampleRate) : sampleRate(sampleRate), channelCount(channelCount), releaseEvent(std::make_shared(state)), BaseService(state, manager, Service::audio_IAudioOut, "audio:IAudioOut", { {0x0, SFUNC(IAudioOut::GetAudioOutState)}, {0x1, SFUNC(IAudioOut::StartAudioOut)}, {0x2, SFUNC(IAudioOut::StopAudioOut)}, {0x3, SFUNC(IAudioOut::AppendAudioOutBuffer)}, {0x4, SFUNC(IAudioOut::RegisterBufferEvent)}, {0x5, SFUNC(IAudioOut::GetReleasedAudioOutBuffer)}, {0x6, SFUNC(IAudioOut::ContainsAudioOutBuffer)} }) { track = state.audio->OpenTrack(channelCount, constant::SampleRate, [this]() { this->releaseEvent->Signal(); }); } IAudioOut::~IAudioOut() { state.audio->CloseTrack(track); } void IAudioOut::GetAudioOutState(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { response.Push(static_cast(track->playbackState)); } void IAudioOut::StartAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { state.logger->Debug("IAudioOut: Start playback"); track->Start(); } void IAudioOut::StopAudioOut(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { state.logger->Debug("IAudioOut: Stop playback"); track->Stop(); } void IAudioOut::AppendAudioOutBuffer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { struct Data { u64 nextBufferPtr; u64 sampleBufferPtr; u64 sampleCapacity; u64 sampleSize; u64 sampleOffset; } data = state.process->GetObject(request.inputBuf.at(0).address); auto tag = request.Pop(); state.logger->Debug("IAudioOut: Appending buffer with address: 0x{:X}, size: 0x{:X}", data.sampleBufferPtr, data.sampleSize); if (sampleRate != constant::SampleRate) { tmpSampleBuffer.resize(data.sampleSize / sizeof(i16)); state.process->ReadMemory(tmpSampleBuffer.data(), data.sampleBufferPtr, data.sampleSize); resampler.ResampleBuffer(tmpSampleBuffer, static_cast(sampleRate) / constant::SampleRate, channelCount); track->AppendBuffer(tag, tmpSampleBuffer); } else { track->AppendBuffer(tag, state.process->GetPointer(data.sampleBufferPtr), data.sampleSize); } } void IAudioOut::RegisterBufferEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { auto handle = state.process->InsertItem(releaseEvent); state.logger->Debug("IAudioOut: Buffer Release Event Handle: 0x{:X}", handle); response.copyHandles.push_back(handle); } void IAudioOut::GetReleasedAudioOutBuffer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { auto maxCount = static_cast(request.outputBuf.at(0).size >> 3); std::vector releasedBuffers = track->GetReleasedBuffers(maxCount); auto count = static_cast(releasedBuffers.size()); // Fill rest of output buffer with zeros releasedBuffers.resize(maxCount, 0); state.process->WriteMemory(releasedBuffers.data(), request.outputBuf.at(0).address, request.outputBuf.at(0).size); response.Push(count); } void IAudioOut::ContainsAudioOutBuffer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { auto tag = request.Pop(); response.Push(static_cast(track->ContainsBuffer(tag))); } }