new audio api changes

This commit is contained in:
octocar 2025-10-01 21:04:31 +02:00 committed by crueter
parent e41b50ada5
commit 26ccc14d5a

View file

@ -4,6 +4,7 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <atomic>
#include <span> #include <span>
#include <vector> #include <vector>
@ -13,7 +14,6 @@
#include "audio_core/sink/sdl2_sink.h" #include "audio_core/sink/sdl2_sink.h"
#include "audio_core/sink/sink_stream.h" #include "audio_core/sink/sink_stream.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/core.h" #include "core/core.h"
namespace AudioCore::Sink { namespace AudioCore::Sink {
@ -39,37 +39,53 @@ public:
system_channels = system_channels_; system_channels = system_channels_;
device_channels = device_channels_; device_channels = device_channels_;
SDL_AudioSpec spec; SDL_AudioSpec spec{};
spec.freq = TargetSampleRate; spec.freq = TargetSampleRate;
spec.channels = static_cast<u8>(device_channels); spec.channels = static_cast<int>(device_channels);
spec.format = SDL_AUDIO_S16; spec.format = SDL_AUDIO_S16;
spec.samples = TargetSampleCount * 2;
spec.callback = &SDLSinkStream::DataCallback;
spec.userdata = this;
SDL_AudioDeviceID device_id = 0;
std::string device_name{output_device}; std::string device_name{output_device};
bool capture{false}; bool is_capture{false};
if (type == StreamType::In) { if (type == StreamType::In) {
device_name = input_device; device_name = input_device;
capture = true; is_capture = true;
} }
SDL_AudioSpec obtained; if (!device_name.empty()) {
if (device_name.empty()) { int count = 0;
device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false); SDL_AudioDeviceID* devices = is_capture ? SDL_GetAudioRecordingDevices(&count)
} else { : SDL_GetAudioPlaybackDevices(&count);
device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
if (devices) {
for (int i = 0; i < count; ++i) {
const char* name = SDL_GetAudioDeviceName(devices[i]);
if (name && device_name == name) {
device_id = devices[i];
break;
}
}
SDL_free(devices);
}
} }
if (device == 0) { if (device_id == 0) {
LOG_CRITICAL(Audio_Sink, "Error opening SDL audio device: {}", SDL_GetError()); device_id =
is_capture ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
}
stream = SDL_OpenAudioDeviceStream(
device_id, &spec,
is_capture ? &SDLSinkStream::CaptureCallback : &SDLSinkStream::PlaybackCallback, this);
if (!stream) {
LOG_CRITICAL(Audio_Sink, "Error opening SDL audio stream: {}", SDL_GetError());
return; return;
} }
LOG_INFO(Service_Audio, LOG_INFO(Service_Audio, "Opening SDL stream with: rate {} channels {} (system channels {})",
"Opening SDL stream {} with: rate {} channels {} (system channels {}) " spec.freq, spec.channels, system_channels);
" samples {}",
device, obtained.freq, obtained.channels, system_channels, obtained.samples);
} }
/** /**
@ -84,13 +100,14 @@ public:
* Finalize the sink stream. * Finalize the sink stream.
*/ */
void Finalize() override { void Finalize() override {
if (device == 0) { if (!stream) {
return; return;
} }
Stop(); Stop();
SDL_ClearQueuedAudio(device); SDL_ClearAudioStream(stream);
SDL_CloseAudioDevice(device); SDL_DestroyAudioStream(stream);
stream = nullptr;
} }
/** /**
@ -100,62 +117,80 @@ public:
* Default false. * Default false.
*/ */
void Start(bool resume = false) override { void Start(bool resume = false) override {
if (device == 0 || !paused) { if (!stream || !paused) {
return; return;
} }
paused = false; paused = false;
SDL_PauseAudioDevice(device, 0); SDL_ResumeAudioStreamDevice(stream);
} }
/** /**
* Stop the sink stream. * Stop the sink stream.
*/ */
void Stop() override { void Stop() override {
if (device == 0 || paused) { if (!stream || paused) {
return; return;
} }
SignalPause(); SignalPause();
SDL_PauseAudioDevice(device, 1); SDL_PauseAudioStreamDevice(stream);
paused = true;
} }
private: private:
/** static void PlaybackCallback(void* userdata, SDL_AudioStream* stream, int additional_amount,
* Main callback from SDL. Either expects samples from us (audio render/audio out), or will int total_amount) {
* provide samples to be copied (audio in).
*
* @param userdata - Custom data pointer passed along, points to a SDLSinkStream.
* @param stream - Buffer of samples to be filled or read.
* @param len - Length of the stream in bytes.
*/
static void DataCallback(void* userdata, Uint8* stream, int len) {
auto* impl = static_cast<SDLSinkStream*>(userdata); auto* impl = static_cast<SDLSinkStream*>(userdata);
if (!impl) { if (!impl) {
return; return;
} }
const std::size_t num_channels = impl->GetDeviceChannels(); const std::size_t num_channels = impl->GetDeviceChannels();
const std::size_t frame_size = num_channels; const std::size_t frame_size = num_channels;
const std::size_t num_frames{len / num_channels / sizeof(s16)}; const std::size_t num_frames = additional_amount / (sizeof(s16) * frame_size);
if (impl->type == StreamType::In) { if (num_frames == 0) {
std::span<const s16> input_buffer{reinterpret_cast<const s16*>(stream), return;
num_frames * frame_size}; }
impl->ProcessAudioIn(input_buffer, num_frames);
} else { std::vector<s16> buffer(num_frames * frame_size);
std::span<s16> output_buffer{reinterpret_cast<s16*>(stream), num_frames * frame_size}; impl->ProcessAudioOutAndRender(buffer, num_frames);
impl->ProcessAudioOutAndRender(output_buffer, num_frames); SDL_PutAudioStreamData(stream, buffer.data(),
static_cast<int>(buffer.size() * sizeof(s16)));
}
static void CaptureCallback(void* userdata, SDL_AudioStream* stream, int additional_amount,
int total_amount) {
auto* impl = static_cast<SDLSinkStream*>(userdata);
if (!impl) {
return;
}
const std::size_t num_channels = impl->GetDeviceChannels();
const std::size_t frame_size = num_channels;
const std::size_t bytes_available = SDL_GetAudioStreamAvailable(stream);
if (bytes_available == 0) {
return;
}
const std::size_t num_frames = bytes_available / (sizeof(s16) * frame_size);
std::vector<s16> buffer(num_frames * frame_size);
int bytes_read =
SDL_GetAudioStreamData(stream, buffer.data(), static_cast<int>(bytes_available));
if (bytes_read > 0) {
const std::size_t frames_read = bytes_read / (sizeof(s16) * frame_size);
impl->ProcessAudioIn(buffer, frames_read);
} }
} }
/// SDL device id of the opened input/output device SDL_AudioStream* stream{nullptr};
SDL_AudioDeviceID device{};
}; };
SDLSink::SDLSink(std::string_view target_device_name) { SDLSink::SDLSink(std::string_view target_device_name) {
if (!SDL_WasInit(SDL_INIT_AUDIO)) { if (!SDL_WasInit(SDL_INIT_AUDIO)) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError()); LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
return; return;
} }
@ -218,66 +253,31 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
std::vector<std::string> device_list; std::vector<std::string> device_list;
if (!SDL_WasInit(SDL_INIT_AUDIO)) { if (!SDL_WasInit(SDL_INIT_AUDIO)) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError()); LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
return {}; return {};
} }
} }
const int device_count = SDL_GetNumAudioDevices(capture); int count = 0;
for (int i = 0; i < device_count; ++i) { SDL_AudioDeviceID* devices =
if (const char* name = SDL_GetAudioDeviceName(i, capture)) { capture ? SDL_GetAudioRecordingDevices(&count) : SDL_GetAudioPlaybackDevices(&count);
if (devices) {
for (int i = 0; i < count; ++i) {
const char* name = SDL_GetAudioDeviceName(devices[i]);
if (name) {
device_list.emplace_back(name); device_list.emplace_back(name);
} }
} }
SDL_free(devices);
}
return device_list; return device_list;
} }
/* REVERSION to 3833 - function GetSDLLatency() REINTRODUCED FROM 3833 - DIABLO 3 FIX */
u32 GetSDLLatency() { u32 GetSDLLatency() {
return TargetSampleCount * 2; return TargetSampleCount * 2;
} }
// REVERTED back to 3833 - Below function IsSDLSuitable() removed, reverting to GetSDLLatency() above. - DIABLO 3 FIX
/*
bool IsSDLSuitable() {
#if !defined(HAVE_SDL2)
return false;
#else
// Check SDL can init
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
LOG_ERROR(Audio_Sink, "SDL failed to init, it is not suitable. Error: {}",
SDL_GetError());
return false;
}
}
// We can set any latency frequency we want with SDL, so no need to check that.
// Check we can open a device with standard parameters
SDL_AudioSpec spec;
spec.freq = TargetSampleRate;
spec.channels = 2u;
spec.format = SDL_AUDIO_S16;
spec.samples = TargetSampleCount * 2;
spec.callback = nullptr;
spec.userdata = nullptr;
SDL_AudioSpec obtained;
auto device = SDL_OpenAudioDevice(nullptr, false, &spec, &obtained, false);
if (device == 0) {
LOG_ERROR(Audio_Sink, "SDL failed to open a device, it is not suitable. Error: {}",
SDL_GetError());
return false;
}
SDL_CloseAudioDevice(device);
return true;
#endif
}
*/
} // namespace AudioCore::Sink } // namespace AudioCore::Sink