diff --git a/src/audio_core/sink/openal_sink.cpp b/src/audio_core/sink/openal_sink.cpp deleted file mode 100644 index 47ae13a333..0000000000 --- a/src/audio_core/sink/openal_sink.cpp +++ /dev/null @@ -1,911 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include - -#include "audio_core/common/common.h" -#include "audio_core/sink/openal_sink.h" -#include "audio_core/sink/sink.h" -#include "audio_core/sink/sink_stream.h" -#include "common/logging.h" -#include "common/scope_exit.h" -#include "core/core.h" - -// Define missing ALC constants for device enumeration -#ifndef ALC_ALL_DEVICES_SPECIFIER -#define ALC_ALL_DEVICES_SPECIFIER 0x1013 -#endif - -#ifndef ALC_ENUMERATE_ALL_EXT -#define ALC_ENUMERATE_ALL_EXT 1 -#endif - -#ifndef ALC_DEFAULT_ALL_DEVICES_SPECIFIER -#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012 -#endif - -#ifndef ALC_DEFAULT_DEVICE_SPECIFIER -#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 -#endif - -#ifndef ALC_DEVICE_SPECIFIER -#define ALC_DEVICE_SPECIFIER 0x1005 -#endif - -#ifndef ALC_CAPTURE_DEVICE_SPECIFIER -#define ALC_CAPTURE_DEVICE_SPECIFIER 0x310 -#endif - -#ifndef ALC_CAPTURE_SAMPLES -#define ALC_CAPTURE_SAMPLES 0x312 -#endif - -namespace { -const char* alGetErrorString(ALCenum error) { - switch (error) { -#define X(N) case N: return #N; - X(AL_INVALID_VALUE) - X(AL_INVALID_OPERATION) - X(AL_OUT_OF_MEMORY) -#undef X - default: return "Unknown error"; - } -} -const char* alcGetErrorString(ALCenum error) { - switch (error) { -#define X(N) case N: return #N; - X(ALC_INVALID_DEVICE) - X(ALC_INVALID_CONTEXT) - X(ALC_INVALID_VALUE) - X(ALC_OUT_OF_MEMORY) -#undef X - default: return "Unknown error"; - } -} -} // Anonymous namespace - - -namespace AudioCore::Sink { -/** - * OpenAL sink stream, responsible for sinking samples to hardware. - */ -class OpenALSinkStream final : public SinkStream { -public: - /** - * Create a new sink stream. - * - * @param device_channels_ - Number of channels supported by the hardware. - * @param system_channels_ - Number of channels the audio systems expect. - * @param output_device - Name of the output device to use for this stream. - * @param input_device - Name of the input device to use for this stream. - * @param type_ - Type of this stream. - * @param system_ - Core system. - * @param al_device - OpenAL device. - * @param al_context - OpenAL context. - */ - OpenALSinkStream(u32 device_channels_, u32 system_channels_, const std::string& output_device, - const std::string& input_device, StreamType type_, Core::System& system_, - ALCdevice* al_device, ALCcontext* al_context) - : SinkStream{system_, type_}, device{al_device}, context{al_context} { - system_channels = system_channels_; - device_channels = device_channels_; - - LOG_DEBUG(Audio_Sink, "Creating OpenAL stream: type={}, device_channels={}, system_channels={}", - static_cast(type_), device_channels_, system_channels_); - - if (type == StreamType::In) { - // For input streams, we need to create a capture device - const char* device_name = input_device.empty() ? nullptr : input_device.c_str(); - capture_device = alcCaptureOpenDevice(device_name, TargetSampleRate, AL_FORMAT_STEREO16, - TargetSampleCount * 4); - if (!capture_device) { - LOG_CRITICAL(Audio_Sink, "Error opening OpenAL capture device: {}", - alcGetString(nullptr, alcGetError(nullptr))); - return; - } - } else { - // Ensure the context is current before creating OpenAL objects - if (!alcMakeContextCurrent(context)) { - LOG_CRITICAL(Audio_Sink, "Failed to make OpenAL context current for stream creation"); - // Create a dummy stream that does nothing but allows the system to continue - is_dummy_stream = true; - LOG_WARNING(Audio_Sink, "Creating dummy audio stream to allow system to continue"); - return; - } - - // Clear any previous errors - alGetError(); - - // Verify the context is current and valid - ALCcontext* current_context = alcGetCurrentContext(); - if (current_context != context) { - LOG_CRITICAL(Audio_Sink, "OpenAL context mismatch: expected {:p}, got {:p}", - static_cast(context), static_cast(current_context)); - is_dummy_stream = true; - LOG_WARNING(Audio_Sink, "Creating dummy audio stream due to context mismatch"); - return; - } - - // Log diagnostic information - const char* renderer = reinterpret_cast(alGetString(AL_RENDERER)); - const char* vendor = reinterpret_cast(alGetString(AL_VENDOR)); - if (renderer && vendor) { - LOG_DEBUG(Audio_Sink, "OpenAL renderer: {}, vendor: {}", renderer, vendor); - } - - // Attempt to create source with multiple retries and better error handling - bool source_created = false; - for (int attempt = 0; attempt < 3 && !source_created; ++attempt) { - if (attempt > 0) { - LOG_WARNING(Audio_Sink, "OpenAL source creation attempt {} of 3", attempt + 1); - // Wait longer between retries - std::this_thread::sleep_for(std::chrono::milliseconds(50 * attempt)); - - // Try to clear and reset the context - alGetError(); // Clear any existing errors - alcMakeContextCurrent(nullptr); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - if (!alcMakeContextCurrent(context)) { - LOG_ERROR(Audio_Sink, "Failed to restore OpenAL context on attempt {}", attempt + 1); - continue; - } - } - - alGenSources(1, &source); - ALenum error = alGetError(); - - if (error == AL_NO_ERROR) { - source_created = true; - if (attempt > 0) { - LOG_INFO(Audio_Sink, "OpenAL source creation succeeded on attempt {}", attempt + 1); - } - } else { - if (attempt == 2) { - LOG_CRITICAL(Audio_Sink, "Final attempt failed - Error creating OpenAL source: {} ({})", alGetErrorString(error), error); - LOG_CRITICAL(Audio_Sink, "This may indicate OpenAL driver issues or resource exhaustion"); - LOG_WARNING(Audio_Sink, "Creating dummy audio stream to allow system to continue"); - is_dummy_stream = true; - return; - } else { - LOG_WARNING(Audio_Sink, "Attempt {} failed - Error creating OpenAL source: {} ({})", attempt + 1, alGetErrorString(error), error); - } - } - } - - if (!source_created) { - LOG_CRITICAL(Audio_Sink, "Failed to create OpenAL source after all attempts"); - LOG_WARNING(Audio_Sink, "Creating dummy audio stream to allow system to continue"); - is_dummy_stream = true; - return; - } - - alGenBuffers(num_buffers, buffers.data()); - ALenum error = alGetError(); - if (error != AL_NO_ERROR) { - LOG_CRITICAL(Audio_Sink, "Error creating OpenAL buffers: {} ({})", alGetErrorString(error), error); - // Clean up the source we created - alDeleteSources(1, &source); - source = 0; - return; - } - - // Set source properties - alSourcef(source, AL_PITCH, 1.0f); - alSourcef(source, AL_GAIN, 1.0f); - alSource3f(source, AL_POSITION, 0.0f, 0.0f, 0.0f); - alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - alSourcei(source, AL_LOOPING, AL_FALSE); - - // Initialize buffers with silence - std::vector silence(TargetSampleCount * device_channels, 0); - for (auto& buffer : buffers) { - alBufferData(buffer, device_channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, silence.data(), static_cast(silence.size() * sizeof(s16)), TargetSampleRate); - } - - // Queue all buffers - alSourceQueueBuffers(source, num_buffers, buffers.data()); - } - - LOG_INFO(Service_Audio, - "Opening OpenAL stream with: rate {} channels {} (system channels {})", - TargetSampleRate, device_channels, system_channels); - } - - /** - * Destroy the sink stream. - */ - ~OpenALSinkStream() override { - LOG_DEBUG(Service_Audio, "Destructing OpenAL stream"); - Finalize(); - } - - /** - * Finalize the sink stream. - */ - void Finalize() override { - if (is_dummy_stream) { - LOG_DEBUG(Audio_Sink, "Finalize called on dummy stream - ignoring"); - return; - } - - StopAudioThread(); - - if (type == StreamType::In) { - if (capture_device) { - if (is_playing) { - alcCaptureStop(capture_device); - } - alcCaptureCloseDevice(capture_device); - capture_device = nullptr; - } - } else { - if (source != 0) { - Stop(); - alDeleteSources(1, &source); - source = 0; - } - if (buffers[0] != 0) { - alDeleteBuffers(num_buffers, buffers.data()); - buffers.fill(0); - } - } - } - - /** - * Start the sink stream. - * - * @param resume - Set to true if this is resuming the stream a previously-active stream. - * Default false. - */ - void Start(bool resume = false) override { - if (is_dummy_stream) { - LOG_DEBUG(Audio_Sink, "Start called on dummy stream - ignoring"); - return; - } - - if (paused) { - paused = false; - if (type == StreamType::In) { - if (capture_device) { - alcCaptureStart(capture_device); - is_playing = true; - } - } else { - if (source != 0) { - alSourcePlay(source); - is_playing = true; - } - } - StartAudioThread(); - } - } - - /** - * Stop the sink stream. - */ - void Stop() override { - if (is_dummy_stream) { - LOG_DEBUG(Audio_Sink, "Stop called on dummy stream - ignoring"); - return; - } - - if (!paused) { - SignalPause(); - StopAudioThread(); - if (type == StreamType::In) { - if (capture_device && is_playing) { - alcCaptureStop(capture_device); - is_playing = false; - } - } else { - if (source != 0 && is_playing) { - alSourceStop(source); - is_playing = false; - } - } - } - } - -private: - /** - * Start the audio processing thread. - */ - void StartAudioThread() { - if (!audio_thread.joinable()) { - audio_thread = std::thread(&OpenALSinkStream::AudioThreadFunc, this); - } - } - - /** - * Stop the audio processing thread. - */ - void StopAudioThread() { - if (audio_thread.joinable()) { - audio_thread.join(); - } - } - - /** - * Audio processing thread function. - */ - void AudioThreadFunc() { - if (is_dummy_stream) { - return; // No-op for dummy streams - } - - while (is_playing && !paused) { - if (type == StreamType::In) { - ProcessInputAudio(); - } else { - ProcessOutputAudio(); - } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - } - - /** - * Process audio data for output streams. - */ - void ProcessOutputAudio() { - if (is_dummy_stream || (type != StreamType::Out && type != StreamType::Render)) { - return; - } - - // Check if any buffers have finished playing - ALint processed = 0; - alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed); - - while (processed > 0) { - ALuint buffer; - alSourceUnqueueBuffers(source, 1, &buffer); - - // Prepare output buffer - const std::size_t num_frames = TargetSampleCount; - std::vector output_buffer(num_frames * device_channels); - - // Get audio data from the system - std::span output_span{output_buffer.data(), output_buffer.size()}; - ProcessAudioOutAndRender(output_span, num_frames); - - // Fill the buffer with new data - alBufferData(buffer, device_channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, - output_buffer.data(), static_cast(output_buffer.size() * sizeof(s16)), TargetSampleRate); - - // Queue the buffer back - alSourceQueueBuffers(source, 1, &buffer); - processed--; - } - - // Make sure the source is playing - ALint state; - alGetSourcei(source, AL_SOURCE_STATE, &state); - if (state != AL_PLAYING && is_playing) { - alSourcePlay(source); - } - } - - /** - * Process audio data for input streams. - */ - void ProcessInputAudio() { - if (is_dummy_stream || type != StreamType::In || !capture_device) { - return; - } - - // Check how many samples are available - ALint samples_available = 0; - alcGetIntegerv(capture_device, ALC_CAPTURE_SAMPLES, 1, &samples_available); - - const std::size_t num_frames = TargetSampleCount; - if (samples_available >= static_cast(num_frames)) { - // Capture the audio data - std::vector capture_buffer(num_frames * device_channels); - alcCaptureSamples(capture_device, capture_buffer.data(), static_cast(num_frames)); - - // Process the captured data - std::span captured_span{capture_buffer.data(), capture_buffer.size()}; - ProcessAudioIn(captured_span, num_frames); - } - } - - /// OpenAL device - ALCdevice* device{}; - /// OpenAL context - ALCcontext* context{}; - /// OpenAL capture device (for input streams) - ALCdevice* capture_device{}; - /// OpenAL source - ALuint source{0}; - /// OpenAL buffers - static constexpr size_t num_buffers = 4; - std::array buffers{}; - /// Whether the stream is currently playing - bool is_playing{false}; - /// Audio processing thread - std::thread audio_thread; - /// Whether this is a dummy stream - bool is_dummy_stream{false}; -}; - -OpenALSink::OpenALSink(std::string_view target_device_name) { - // Log OpenAL version and available extensions - LOG_INFO(Audio_Sink, "Initializing OpenAL sink..."); - - // Check for device enumeration extensions - if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT")) { - LOG_INFO(Audio_Sink, "OpenAL ALC_ENUMERATE_ALL_EXT extension available"); - } else if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT")) { - LOG_INFO(Audio_Sink, "OpenAL ALC_ENUMERATION_EXT extension available"); - } else { - LOG_WARNING(Audio_Sink, "OpenAL device enumeration extensions not available"); - } - - // Initialize OpenAL with better device selection logic - const char* device_name = nullptr; - - if (!target_device_name.empty() && target_device_name != auto_device_name) { - // Use the specified device name - device_name = target_device_name.data(); - LOG_INFO(Audio_Sink, "Using specified device: {}", target_device_name); - } else { - // Auto selection - try multiple strategies to find a working device - LOG_INFO(Audio_Sink, "Auto device selected, attempting auto-selection..."); - - // Strategy 1: Try to get the default device using newer extensions - if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT")) { - device_name = alcGetString(nullptr, ALC_DEFAULT_ALL_DEVICES_SPECIFIER); - if (device_name) { - LOG_INFO(Audio_Sink, "Using default device (ALC_ENUMERATE_ALL_EXT): {}", device_name); - } - } - - // Strategy 2: If no default device found, try the basic default device - if (!device_name) { - device_name = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER); - if (device_name) { - LOG_INFO(Audio_Sink, "Using default device (ALC_DEFAULT_DEVICE_SPECIFIER): {}", device_name); - } - } - - // Strategy 3: If still no device, try opening with nullptr (should use system default) - if (!device_name) { - LOG_INFO(Audio_Sink, "No default device found, trying nullptr (system default)"); - } - } - - // Try to open the device with multiple fallback strategies - device = alcOpenDevice(device_name); - if (!device) { - ALenum error = alcGetError(nullptr); - LOG_WARNING(Audio_Sink, "Failed to open OpenAL device '{}': {} ({}), trying fallback strategies", device_name ? device_name : "nullptr", alcGetErrorString(error), error); - // Fallback 1: Try with nullptr (system default) - if (device_name) { - LOG_INFO(Audio_Sink, "Trying fallback 1: nullptr (system default)"); - device = alcOpenDevice(nullptr); - if (device) { - LOG_INFO(Audio_Sink, "Successfully opened OpenAL device with nullptr fallback"); - } else { - error = alcGetError(nullptr); - LOG_WARNING(Audio_Sink, "Fallback 1 failed: {} ({})", alcGetErrorString(error), error); - } - } - - // Fallback 2: Try with empty string - if (!device) { - LOG_INFO(Audio_Sink, "Trying fallback 2: empty string"); - device = alcOpenDevice(""); - if (device) { - LOG_INFO(Audio_Sink, "Successfully opened OpenAL device with empty string fallback"); - } else { - error = alcGetError(nullptr); - LOG_WARNING(Audio_Sink, "Fallback 2 failed: {} ({})", alcGetErrorString(error), error); - } - } - - // If all fallbacks failed, log the final error - if (!device) { - LOG_CRITICAL(Audio_Sink, "All OpenAL device opening strategies failed - audio will be disabled"); - } - } - - // Only try to create context if device was successfully opened - if (device) { - // Create context with attributes for better compatibility - ALCint context_attributes[] = { - ALC_FREQUENCY, TargetSampleRate, - ALC_REFRESH, 50, // 50Hz refresh rate - ALC_SYNC, ALC_FALSE, - 0 // Null terminator - }; - - context = alcCreateContext(static_cast(device), context_attributes); - if (!context) { - // Try with minimal attributes if the first attempt failed - LOG_WARNING(Audio_Sink, "Failed to create OpenAL context with full attributes, trying minimal attributes"); - context = alcCreateContext(static_cast(device), nullptr); - } - - if (!context) { - ALenum error = alcGetError(static_cast(device)); - LOG_CRITICAL(Audio_Sink, "Failed to create OpenAL context: {} ({})", alcGetErrorString(error), error); - alcCloseDevice(static_cast(device)); - device = nullptr; - } else { - if (!alcMakeContextCurrent(static_cast(context))) { - ALenum error = alcGetError(static_cast(device)); - LOG_CRITICAL(Audio_Sink, "Failed to make OpenAL context current: {} ({})", alcGetErrorString(error), error); - alcDestroyContext(static_cast(context)); - alcCloseDevice(static_cast(device)); - context = nullptr; - device = nullptr; - } - } - } - - // Set device name - if (!target_device_name.empty() && target_device_name != auto_device_name) { - output_device = target_device_name; - } else { - if (device) { - const char* default_device = alcGetString(static_cast(device), ALC_DEVICE_SPECIFIER); - if (default_device) { - output_device = default_device; - } else { - output_device = "Default"; - } - } else { - output_device = "Default"; - } - } - - // Get device capabilities - device_channels = 2; // OpenAL typically supports stereo output - - // Log OpenAL implementation details - if (device && context) { - const char* al_version = reinterpret_cast(alGetString(AL_VERSION)); - const char* al_renderer = reinterpret_cast(alGetString(AL_RENDERER)); - const char* al_vendor = reinterpret_cast(alGetString(AL_VENDOR)); - const char* al_extensions = reinterpret_cast(alGetString(AL_EXTENSIONS)); - - LOG_INFO(Audio_Sink, "OpenAL implementation details:"); - LOG_INFO(Audio_Sink, " Version: {}", al_version ? al_version : "Unknown"); - LOG_INFO(Audio_Sink, " Renderer: {}", al_renderer ? al_renderer : "Unknown"); - LOG_INFO(Audio_Sink, " Vendor: {}", al_vendor ? al_vendor : "Unknown"); - LOG_INFO(Audio_Sink, " Device: {}", output_device); - - // Check for important extensions - if (al_extensions) { - std::string extensions_str(al_extensions); - LOG_DEBUG(Audio_Sink, " Extensions: {}", extensions_str); - - if (extensions_str.find("AL_SOFT_direct_channels") != std::string::npos) { - LOG_INFO(Audio_Sink, " AL_SOFT_direct_channels extension available"); - } - if (extensions_str.find("AL_SOFT_source_latency") != std::string::npos) { - LOG_INFO(Audio_Sink, " AL_SOFT_source_latency extension available"); - } - } - - LOG_INFO(Audio_Sink, "OpenAL sink initialized successfully with device: {}", output_device); - } else { - LOG_WARNING(Audio_Sink, "OpenAL sink initialized with null device/context - audio will be disabled"); - } -} - -OpenALSink::~OpenALSink() { - CloseStreams(); - - if (context) { - alcMakeContextCurrent(nullptr); - alcDestroyContext(static_cast(context)); - } - if (device) { - alcCloseDevice(static_cast(device)); - } -} - -SinkStream* OpenALSink::AcquireSinkStream(Core::System& system, u32 system_channels_, - const std::string&, StreamType type) { - if (!device || !context) { - LOG_ERROR(Audio_Sink, "Cannot create sink stream - OpenAL device or context is null (device: {}, context: {})", - device ? "valid" : "null", context ? "valid" : "null"); - // Return nullptr to indicate failure - the audio system should handle this gracefully - return nullptr; - } - - // Limit the number of concurrent streams to avoid resource exhaustion - constexpr size_t max_streams = 8; - if (sink_streams.size() >= max_streams) { - LOG_WARNING(Audio_Sink, "Maximum number of OpenAL streams ({}) reached, cannot create more", max_streams); - return nullptr; - } - - // Ensure context is current before creating streams - if (!alcMakeContextCurrent(static_cast(context))) { - LOG_ERROR(Audio_Sink, "Failed to make OpenAL context current before creating stream"); - return nullptr; - } - - system_channels = system_channels_; - - // Add some delay between stream creations to avoid resource conflicts - if (!sink_streams.empty()) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - - SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique( - device_channels, system_channels, output_device, input_device, type, system, - static_cast(device), static_cast(context))); - return stream.get(); -} - -void OpenALSink::CloseStream(SinkStream* stream) { - for (size_t i = 0; i < sink_streams.size(); i++) { - if (sink_streams[i].get() == stream) { - sink_streams[i].reset(); - sink_streams.erase(sink_streams.begin() + i); - break; - } - } -} - -void OpenALSink::CloseStreams() { - sink_streams.clear(); -} - -f32 OpenALSink::GetDeviceVolume() const { - if (sink_streams.empty() || !sink_streams[0]) { - return 1.0f; - } - return sink_streams[0]->GetDeviceVolume(); -} - -void OpenALSink::SetDeviceVolume(f32 volume) { - for (auto& stream : sink_streams) { - stream->SetDeviceVolume(volume); - } -} - -void OpenALSink::SetSystemVolume(f32 volume) { - for (auto& stream : sink_streams) { - stream->SetSystemVolume(volume); - } -} - -std::vector ListOpenALSinkDevices(bool capture) { - std::vector device_list; - - LOG_INFO(Audio_Sink, "Enumerating OpenAL {} devices...", capture ? "capture" : "playback"); - - if (capture) { - // List capture devices - if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT")) { - // Use the newer extension for better device names - const char* devices = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); - if (devices) { - LOG_INFO(Audio_Sink, "Using ALC_ENUMERATE_ALL_EXT for capture device enumeration"); - while (*devices) { - device_list.emplace_back(devices); - devices += strlen(devices) + 1; - } - } else { - LOG_WARNING(Audio_Sink, "ALC_ENUMERATE_ALL_EXT returned null device list"); - } - } - - // Fallback to older enumeration - if (device_list.empty()) { - const char* devices = alcGetString(nullptr, ALC_CAPTURE_DEVICE_SPECIFIER); - if (devices) { - LOG_INFO(Audio_Sink, "Using ALC_CAPTURE_DEVICE_SPECIFIER for capture device enumeration"); - while (*devices) { - device_list.emplace_back(devices); - devices += strlen(devices) + 1; - } - } else { - LOG_WARNING(Audio_Sink, "ALC_CAPTURE_DEVICE_SPECIFIER returned null device list"); - } - } - } else { - // List playback devices - if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT")) { - // Use the newer extension for better device names - const char* devices = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); - if (devices) { - LOG_INFO(Audio_Sink, "Using ALC_ENUMERATE_ALL_EXT for playback device enumeration"); - while (*devices) { - device_list.emplace_back(devices); - devices += strlen(devices) + 1; - } - } else { - LOG_WARNING(Audio_Sink, "ALC_ENUMERATE_ALL_EXT returned null device list"); - } - } - - // Fallback to older enumeration if the extension isn't available - if (device_list.empty() && alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT")) { - const char* devices = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); - if (devices) { - LOG_INFO(Audio_Sink, "Using ALC_ENUMERATION_EXT for playback device enumeration"); - while (*devices) { - device_list.emplace_back(devices); - devices += strlen(devices) + 1; - } - } else { - LOG_WARNING(Audio_Sink, "ALC_ENUMERATION_EXT returned null device list"); - } - } - - // Last resort fallback - if (device_list.empty()) { - const char* devices = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); - if (devices) { - LOG_INFO(Audio_Sink, "Using ALC_DEVICE_SPECIFIER for playback device enumeration"); - while (*devices) { - device_list.emplace_back(devices); - devices += strlen(devices) + 1; - } - } else { - LOG_WARNING(Audio_Sink, "ALC_DEVICE_SPECIFIER returned null device list"); - } - } - } - - // Log the devices we found for debugging - LOG_INFO(Audio_Sink, "OpenAL {} devices found: {}", capture ? "capture" : "playback", device_list.size()); - for (size_t i = 0; i < device_list.size(); ++i) { - LOG_INFO(Audio_Sink, " {}: {}", i, device_list[i]); - } - - if (device_list.empty()) { - LOG_WARNING(Audio_Sink, "No OpenAL {} devices found, using default", capture ? "capture" : "playback"); - device_list.emplace_back("Default"); - } - - return device_list; -} - -bool IsOpenALSuitable() { - LOG_INFO(Audio_Sink, "Checking OpenAL suitability..."); - - // Simple test: try to open a device with the most basic approach - LOG_INFO(Audio_Sink, "Performing basic OpenAL functionality test..."); - ALCdevice* basic_device = alcOpenDevice(nullptr); - if (!basic_device) { - LOG_ERROR(Audio_Sink, "Basic OpenAL test failed - cannot open device with nullptr"); - return false; - } - - ALCcontext* basic_context = alcCreateContext(basic_device, nullptr); - if (!basic_context) { - LOG_ERROR(Audio_Sink, "Basic OpenAL test failed - cannot create context"); - alcCloseDevice(basic_device); - return false; - } - - if (!alcMakeContextCurrent(basic_context)) { - LOG_ERROR(Audio_Sink, "Basic OpenAL test failed - cannot make context current"); - alcDestroyContext(basic_context); - alcCloseDevice(basic_device); - return false; - } - - // Test basic AL functions - ALuint test_source = 0; - alGenSources(1, &test_source); - ALenum basic_error = alGetError(); - - if (basic_error != AL_NO_ERROR || test_source == 0) { - LOG_ERROR(Audio_Sink, "Basic OpenAL test failed - cannot create source (error: {})", basic_error); - alcMakeContextCurrent(nullptr); - alcDestroyContext(basic_context); - alcCloseDevice(basic_device); - return false; - } - - // Clean up basic test - alDeleteSources(1, &test_source); - alcMakeContextCurrent(nullptr); - alcDestroyContext(basic_context); - alcCloseDevice(basic_device); - - LOG_INFO(Audio_Sink, "Basic OpenAL functionality test passed"); - - // Check OpenAL version first - const char* al_version = reinterpret_cast(alGetString(AL_VERSION)); - const char* al_vendor = reinterpret_cast(alGetString(AL_VENDOR)); - const char* al_renderer = reinterpret_cast(alGetString(AL_RENDERER)); - - LOG_INFO(Audio_Sink, "OpenAL version: {}", al_version ? al_version : "Unknown"); - LOG_INFO(Audio_Sink, "OpenAL vendor: {}", al_vendor ? al_vendor : "Unknown"); - LOG_INFO(Audio_Sink, "OpenAL renderer: {}", al_renderer ? al_renderer : "Unknown"); - - // Test device enumeration - LOG_INFO(Audio_Sink, "Testing OpenAL device enumeration..."); - const char* default_device = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER); - if (default_device) { - LOG_INFO(Audio_Sink, "Default device: {}", default_device); - } else { - LOG_WARNING(Audio_Sink, "No default device found"); - } - - // Test all device enumeration methods - if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT")) { - const char* all_devices = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); - if (all_devices) { - LOG_INFO(Audio_Sink, "ALC_ALL_DEVICES_SPECIFIER available"); - } else { - LOG_WARNING(Audio_Sink, "ALC_ALL_DEVICES_SPECIFIER returned null"); - } - } - - if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT")) { - const char* devices = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); - if (devices) { - LOG_INFO(Audio_Sink, "ALC_DEVICE_SPECIFIER available"); - } else { - LOG_WARNING(Audio_Sink, "ALC_DEVICE_SPECIFIER returned null"); - } - } - - // Try to initialize OpenAL to check if it's available - LOG_INFO(Audio_Sink, "Attempting to open OpenAL device..."); - ALCdevice* test_device = alcOpenDevice(nullptr); - if (!test_device) { - ALenum device_error = alcGetError(nullptr); - LOG_ERROR(Audio_Sink, "OpenAL not suitable - failed to open default device (error: {})", device_error); - return false; - } - - LOG_INFO(Audio_Sink, "Successfully opened OpenAL device, attempting to create context..."); - - ALCcontext* test_context = alcCreateContext(test_device, nullptr); - if (!test_context) { - ALenum context_error = alcGetError(test_device); - LOG_ERROR(Audio_Sink, "OpenAL not suitable - failed to create context (error: {})", context_error); - alcCloseDevice(test_device); - return false; - } - - LOG_INFO(Audio_Sink, "Successfully created OpenAL context, attempting to make it current..."); - - // Try to make the context current - if (!alcMakeContextCurrent(test_context)) { - ALenum current_error = alcGetError(test_device); - LOG_ERROR(Audio_Sink, "OpenAL not suitable - failed to make context current (error: {})", current_error); - alcDestroyContext(test_context); - alcCloseDevice(test_device); - return false; - } - - LOG_INFO(Audio_Sink, "Successfully made OpenAL context current, testing source creation..."); - - // Try to create a test source to verify functionality - ALuint test_source2 = 0; - alGenSources(1, &test_source2); - ALenum source_error = alGetError(); - - bool suitable = (source_error == AL_NO_ERROR && test_source2 != 0); - - if (suitable) { - alDeleteSources(1, &test_source2); - LOG_INFO(Audio_Sink, "OpenAL is suitable for use"); - } else { - LOG_ERROR(Audio_Sink, "OpenAL not suitable - failed to create test source (error: {})", source_error); - } - - // Clean up - alcMakeContextCurrent(nullptr); - alcDestroyContext(test_context); - alcCloseDevice(test_device); - - return suitable; -} - -} // namespace AudioCore::Sink \ No newline at end of file diff --git a/src/audio_core/sink/openal_sink.h b/src/audio_core/sink/openal_sink.h deleted file mode 100644 index 3e479434d3..0000000000 --- a/src/audio_core/sink/openal_sink.h +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#include "audio_core/sink/sink.h" - -namespace Core { -class System; -} - -namespace AudioCore::Sink { -class SinkStream; - -/** - * OpenAL backend sink, holds multiple output streams and is responsible for sinking samples to - * hardware. Used by Audio Render, Audio In and Audio Out. - */ -class OpenALSink final : public Sink { -public: - explicit OpenALSink(std::string_view device_id); - ~OpenALSink() 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: - /// OpenAL device - void* device{}; - /// OpenAL context - void* context{}; - /// Device channels - u32 device_channels{2}; - /// System channels - u32 system_channels{2}; - /// Output device name - std::string output_device{}; - /// Input device name - std::string input_device{}; - /// Vector of streams managed by this sink - std::vector sink_streams{}; -}; - -/** - * Get a list of connected devices from OpenAL. - * - * @param capture - Return input (capture) devices if true, otherwise output devices. - */ -std::vector ListOpenALSinkDevices(bool capture); - -/** - * Check if this backend is suitable for use. - * Checks if enabled, its latency, whether it opens successfully, etc. - * - * @return True is this backend is suitable, false otherwise. - */ -bool IsOpenALSuitable(); - -} // namespace AudioCore::Sink \ No newline at end of file