7 changed files with 298 additions and 4 deletions
-
3CMakeLists.txt
-
11src/audio_core/CMakeLists.txt
-
184src/audio_core/sink/oboe_sink.cpp
-
75src/audio_core/sink/oboe_sink.h
-
13src/audio_core/sink/sink_details.cpp
-
7src/common/settings_enums.h
-
9vcpkg.json
@ -0,0 +1,184 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
|
||||
|
#include <span>
|
||||
|
#include <vector>
|
||||
|
|
||||
|
#include <oboe/Oboe.h>
|
||||
|
|
||||
|
#include "audio_core/common/common.h"
|
||||
|
#include "audio_core/sink/oboe_sink.h"
|
||||
|
#include "audio_core/sink/sink_stream.h"
|
||||
|
#include "common/logging/log.h"
|
||||
|
#include "common/scope_exit.h"
|
||||
|
#include "core/core.h"
|
||||
|
|
||||
|
namespace AudioCore::Sink { |
||||
|
|
||||
|
class OboeSinkStream final : public SinkStream, |
||||
|
public oboe::AudioStreamDataCallback, |
||||
|
public oboe::AudioStreamErrorCallback { |
||||
|
public: |
||||
|
explicit OboeSinkStream(Core::System& system_, StreamType type_, const std::string& name_, |
||||
|
u32 device_channels_, u32 system_channels_) |
||||
|
: SinkStream(system_, type_) { |
||||
|
name = name_; |
||||
|
system_channels = system_channels_; |
||||
|
device_channels = device_channels_; |
||||
|
|
||||
|
this->OpenStream(); |
||||
|
} |
||||
|
|
||||
|
~OboeSinkStream() override { |
||||
|
LOG_DEBUG(Audio_Sink, "Destructing Oboe stream {}", name); |
||||
|
} |
||||
|
|
||||
|
void Finalize() override { |
||||
|
this->Stop(); |
||||
|
m_stream.reset(); |
||||
|
} |
||||
|
|
||||
|
void Start(bool resume = false) override { |
||||
|
if (!m_stream || !paused) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
paused = false; |
||||
|
|
||||
|
if (m_stream->start() != oboe::Result::OK) { |
||||
|
LOG_CRITICAL(Audio_Sink, "Error starting Oboe stream"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void Stop() override { |
||||
|
if (!m_stream || paused) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this->SignalPause(); |
||||
|
|
||||
|
if (m_stream->stop() != oboe::Result::OK) { |
||||
|
LOG_CRITICAL(Audio_Sink, "Error stopping Oboe stream"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected: |
||||
|
oboe::DataCallbackResult onAudioReady(oboe::AudioStream*, void* audio_data, |
||||
|
s32 num_buffer_frames) override { |
||||
|
const size_t num_channels = this->GetDeviceChannels(); |
||||
|
const size_t frame_size = num_channels; |
||||
|
const size_t num_frames = static_cast<size_t>(num_buffer_frames); |
||||
|
|
||||
|
if (type == StreamType::In) { |
||||
|
std::span<const s16> input_buffer{reinterpret_cast<const s16*>(audio_data), |
||||
|
num_frames * frame_size}; |
||||
|
this->ProcessAudioIn(input_buffer, num_frames); |
||||
|
} else { |
||||
|
std::span<s16> output_buffer{reinterpret_cast<s16*>(audio_data), |
||||
|
num_frames * frame_size}; |
||||
|
this->ProcessAudioOutAndRender(output_buffer, num_frames); |
||||
|
} |
||||
|
|
||||
|
return oboe::DataCallbackResult::Continue; |
||||
|
} |
||||
|
|
||||
|
void onErrorAfterClose(oboe::AudioStream*, oboe::Result) override { |
||||
|
LOG_INFO(Audio_Sink, "Audio stream closed, reinitializing"); |
||||
|
|
||||
|
if (this->OpenStream()) { |
||||
|
m_stream->start(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
bool OpenStream() { |
||||
|
const auto direction = [&]() { |
||||
|
switch (type) { |
||||
|
case StreamType::In: |
||||
|
return oboe::Direction::Input; |
||||
|
case StreamType::Out: |
||||
|
case StreamType::Render: |
||||
|
return oboe::Direction::Output; |
||||
|
default: |
||||
|
ASSERT(false); |
||||
|
return oboe::Direction::Output; |
||||
|
} |
||||
|
}(); |
||||
|
|
||||
|
const auto channel_mask = [&]() { |
||||
|
switch (device_channels) { |
||||
|
case 1: |
||||
|
return oboe::ChannelMask::Mono; |
||||
|
case 2: |
||||
|
return oboe::ChannelMask::Stereo; |
||||
|
case 6: |
||||
|
return oboe::ChannelMask::CM5Point1; |
||||
|
default: |
||||
|
ASSERT(false); |
||||
|
return oboe::ChannelMask::Unspecified; |
||||
|
} |
||||
|
}(); |
||||
|
|
||||
|
oboe::AudioStreamBuilder builder; |
||||
|
const auto result = builder.setDirection(direction) |
||||
|
->setSampleRate(TargetSampleRate) |
||||
|
->setChannelCount(device_channels) |
||||
|
->setChannelMask(channel_mask) |
||||
|
->setFormat(oboe::AudioFormat::I16) |
||||
|
->setFormatConversionAllowed(true) |
||||
|
->setDataCallback(this) |
||||
|
->setErrorCallback(this) |
||||
|
->openStream(m_stream); |
||||
|
|
||||
|
ASSERT(result == oboe::Result::OK); |
||||
|
return result == oboe::Result::OK; |
||||
|
} |
||||
|
|
||||
|
std::shared_ptr<oboe::AudioStream> m_stream{}; |
||||
|
}; |
||||
|
|
||||
|
OboeSink::OboeSink() { |
||||
|
// TODO: how do we get the number of channels, or device list?
|
||||
|
// This seems to be missing from NDK.
|
||||
|
device_channels = 2; |
||||
|
} |
||||
|
|
||||
|
OboeSink::~OboeSink() = default; |
||||
|
|
||||
|
SinkStream* OboeSink::AcquireSinkStream(Core::System& system, u32 system_channels, |
||||
|
const std::string& name, StreamType type) { |
||||
|
SinkStreamPtr& stream = sink_streams.emplace_back( |
||||
|
std::make_unique<OboeSinkStream>(system, type, name, device_channels, system_channels)); |
||||
|
|
||||
|
return stream.get(); |
||||
|
} |
||||
|
|
||||
|
void OboeSink::CloseStream(SinkStream* to_remove) { |
||||
|
sink_streams.remove_if([&](auto& stream) { return stream.get() == to_remove; }); |
||||
|
} |
||||
|
|
||||
|
void OboeSink::CloseStreams() { |
||||
|
sink_streams.clear(); |
||||
|
} |
||||
|
|
||||
|
f32 OboeSink::GetDeviceVolume() const { |
||||
|
if (sink_streams.empty()) { |
||||
|
return 1.0f; |
||||
|
} |
||||
|
|
||||
|
return sink_streams.front()->GetDeviceVolume(); |
||||
|
} |
||||
|
|
||||
|
void OboeSink::SetDeviceVolume(f32 volume) { |
||||
|
for (auto& stream : sink_streams) { |
||||
|
stream->SetDeviceVolume(volume); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void OboeSink::SetSystemVolume(f32 volume) { |
||||
|
for (auto& stream : sink_streams) { |
||||
|
stream->SetSystemVolume(volume); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} // namespace AudioCore::Sink
|
||||
@ -0,0 +1,75 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <list> |
||||
|
#include <string> |
||||
|
|
||||
|
#include "audio_core/sink/sink.h" |
||||
|
|
||||
|
namespace Core { |
||||
|
class System; |
||||
|
} |
||||
|
|
||||
|
namespace AudioCore::Sink { |
||||
|
class SinkStream; |
||||
|
|
||||
|
class OboeSink final : public Sink { |
||||
|
public: |
||||
|
explicit OboeSink(); |
||||
|
~OboeSink() override; |
||||
|
|
||||
|
/** |
||||
|
* Create a new sink stream. |
||||
|
* |
||||
|
* @param system - Core system. |
||||
|
* @param system_channels - Number of channels the audio system expects. |
||||
|
* May differ from the device's channel count. |
||||
|
* @param name - Name of this stream. |
||||
|
* @param type - Type of this stream, render/in/out. |
||||
|
* |
||||
|
* @return A pointer to the created SinkStream |
||||
|
*/ |
||||
|
SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels, |
||||
|
const std::string& name, StreamType type) override; |
||||
|
|
||||
|
/** |
||||
|
* Close a given stream. |
||||
|
* |
||||
|
* @param stream - The stream to close. |
||||
|
*/ |
||||
|
void CloseStream(SinkStream* stream) override; |
||||
|
|
||||
|
/** |
||||
|
* Close all streams. |
||||
|
*/ |
||||
|
void CloseStreams() override; |
||||
|
|
||||
|
/** |
||||
|
* Get the device volume. Set from calls to the IAudioDevice service. |
||||
|
* |
||||
|
* @return Volume of the device. |
||||
|
*/ |
||||
|
f32 GetDeviceVolume() const override; |
||||
|
|
||||
|
/** |
||||
|
* Set the device volume. Set from calls to the IAudioDevice service. |
||||
|
* |
||||
|
* @param volume - New volume of the device. |
||||
|
*/ |
||||
|
void SetDeviceVolume(f32 volume) override; |
||||
|
|
||||
|
/** |
||||
|
* Set the system volume. Comes from the audio system using this stream. |
||||
|
* |
||||
|
* @param volume - New volume of the system. |
||||
|
*/ |
||||
|
void SetSystemVolume(f32 volume) override; |
||||
|
|
||||
|
private: |
||||
|
/// List of streams managed by this sink |
||||
|
std::list<SinkStreamPtr> sink_streams{}; |
||||
|
}; |
||||
|
|
||||
|
} // namespace AudioCore::Sink |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue