// SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include "audio.h" namespace skyline::audio { Audio::Audio(const DeviceState &state) : state(state), oboe::AudioStreamCallback() { builder.setChannelCount(constant::ChannelCount); builder.setSampleRate(constant::SampleRate); builder.setFormat(constant::PcmFormat); builder.setFramesPerCallback(constant::MixBufferSize); builder.setUsage(oboe::Usage::Game); builder.setCallback(this); builder.openManagedStream(outputStream); outputStream->requestStart(); } std::shared_ptr Audio::OpenTrack(const int channelCount, const int sampleRate, const std::function &releaseCallback) { std::lock_guard trackGuard(trackLock); auto track = std::make_shared(channelCount, sampleRate, releaseCallback); audioTracks.push_back(track); return track; } void Audio::CloseTrack(std::shared_ptr &track) { std::lock_guard trackGuard(trackLock); audioTracks.erase(std::remove(audioTracks.begin(), audioTracks.end(), track), audioTracks.end()); track.reset(); } oboe::DataCallbackResult Audio::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) { i16 *destBuffer = static_cast(audioData); size_t streamSamples = static_cast(numFrames) * audioStream->getChannelCount(); size_t writtenSamples = 0; std::unique_lock trackGuard(trackLock); for (auto &track : audioTracks) { if (track->playbackState == AudioOutState::Stopped) continue; std::lock_guard bufferGuard(track->bufferLock); auto trackSamples = track->samples.Read(destBuffer, streamSamples, [](i16 *source, i16 *destination) { *destination = Saturate(static_cast(*destination) + static_cast(*source)); }, writtenSamples); writtenSamples = std::max(trackSamples, writtenSamples); track->sampleCounter += trackSamples; track->CheckReleasedBuffers(); } trackGuard.unlock(); if (streamSamples > writtenSamples) memset(destBuffer + writtenSamples, 0, (streamSamples - writtenSamples) * sizeof(i16)); return oboe::DataCallbackResult::Continue; } void Audio::onErrorAfterClose(oboe::AudioStream *audioStream, oboe::Result error) { if (error == oboe::Result::ErrorDisconnected) { builder.openManagedStream(outputStream); outputStream->requestStart(); } } }