LCOV - code coverage report
Current view: top level - src/audio/backend - sdl_backend.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 0.0 % 126 0
Test Date: 2026-06-07 15:51:50 Functions: 0.0 % 17 0

            Line data    Source code
       1              : #include "audio/backend/sdl_backend.h"
       2              : 
       3              : #include <algorithm>
       4              : #include <cassert>
       5              : #include <cctype>
       6              : #include <cstring>
       7              : #include <iostream>
       8              : #include <vector>
       9              : 
      10              : #include "audio/engine/i_audio_engine.h"
      11              : 
      12              : namespace Amplitron {
      13              : 
      14            0 : static void sdl_audio_callback(void* userdata, Uint8* stream, int len) {
      15            0 :     auto* be = static_cast<SdlBackend*>(userdata);
      16            0 :     auto* engine = be->get_engine();
      17            0 :     if (!engine) return;
      18              : 
      19            0 :     auto* out = reinterpret_cast<float*>(stream);
      20            0 :     int frame_count = len / static_cast<int>(2 * sizeof(float));
      21              : 
      22            0 :     auto& cap = be->get_capture_buffer();
      23            0 :     if (frame_count > static_cast<int>(cap.size())) {
      24            0 :         cap.resize(frame_count * 2, 0.0f);
      25            0 :     }
      26              : 
      27            0 :     SDL_AudioDeviceID cap_dev = be->get_capture_device();
      28            0 :     if (cap_dev != 0) {
      29            0 :         Uint32 queued = SDL_GetQueuedAudioSize(cap_dev);
      30            0 :         Uint32 need = static_cast<Uint32>(frame_count * sizeof(float));
      31              : 
      32            0 :         Uint32 max_queued = need * 2;
      33            0 :         while (queued > max_queued) {
      34            0 :             Uint8 junk[4096];
      35            0 :             Uint32 chunk = (queued - need) > 4096 ? 4096 : (queued - need);
      36            0 :             SDL_DequeueAudio(cap_dev, junk, chunk);
      37            0 :             queued -= chunk;
      38              :         }
      39              : 
      40            0 :         Uint32 got = SDL_DequeueAudio(cap_dev, cap.data(), need);
      41            0 :         int captured = static_cast<int>(got / sizeof(float));
      42            0 :         if (captured < frame_count)
      43            0 :             std::memset(cap.data() + captured, 0,
      44            0 :                         static_cast<size_t>(frame_count - captured) * sizeof(float));
      45            0 :     } else {
      46            0 :         std::memset(cap.data(), 0, static_cast<size_t>(frame_count) * sizeof(float));
      47              :     }
      48              : 
      49            0 :     engine->process_audio(cap.data(), out, frame_count);
      50            0 : }
      51              : 
      52            0 : SdlBackend::SdlBackend() = default;
      53            0 : SdlBackend::~SdlBackend() { shutdown(); }
      54              : 
      55            0 : bool SdlBackend::initialize(IAudioEngine* engine) {
      56            0 :     if (initialized_) return true;
      57            0 :     engine_ = engine;
      58              : 
      59            0 :     if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
      60            0 :         std::cerr << "SDL audio init failed: " << SDL_GetError() << std::endl;
      61            0 :         return false;
      62              :     }
      63            0 :     initialized_ = true;
      64            0 :     return true;
      65            0 : }
      66              : 
      67            0 : void SdlBackend::shutdown() {
      68            0 :     stop();
      69            0 :     if (initialized_) {
      70            0 :         SDL_QuitSubSystem(SDL_INIT_AUDIO);
      71            0 :         initialized_ = false;
      72            0 :     }
      73            0 : }
      74              : 
      75            0 : bool SdlBackend::start() {
      76            0 :     if (!initialized_ || running_) return false;
      77              : 
      78            0 :     int target_buffer = engine_->get_buffer_size();
      79              : #ifdef __EMSCRIPTEN__
      80              :     if (target_buffer < 256) {
      81              :         target_buffer = 256;
      82              :     } else {
      83              :         int p = 256;
      84              :         while (p < target_buffer && p < 16384) {
      85              :             p *= 2;
      86              :         }
      87              :         target_buffer = p;
      88              :     }
      89              : #endif
      90            0 :     int target_rate = engine_->get_sample_rate();
      91              : 
      92            0 :     SDL_AudioSpec want_out, have_out;
      93            0 :     SDL_memset(&want_out, 0, sizeof(want_out));
      94            0 :     want_out.freq = target_rate;
      95            0 :     want_out.format = AUDIO_F32;
      96            0 :     want_out.channels = 2;
      97            0 :     want_out.samples = static_cast<Uint16>(target_buffer);
      98            0 :     want_out.callback = sdl_audio_callback;
      99            0 :     want_out.userdata = this;
     100              : 
     101            0 :     audio_device_ =
     102            0 :         SDL_OpenAudioDevice(nullptr, 0, &want_out, &have_out, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
     103            0 :     if (audio_device_ == 0) {
     104            0 :         std::cerr << "[SDL] Failed to open output audio: " << SDL_GetError() << std::endl;
     105            0 :         return false;
     106              :     }
     107              : 
     108            0 :     sample_rate_ = have_out.freq;
     109            0 :     buffer_size_ = have_out.samples;
     110              : 
     111            0 :     SDL_AudioSpec want_cap, have_cap;
     112            0 :     SDL_memset(&want_cap, 0, sizeof(want_cap));
     113            0 :     want_cap.freq = sample_rate_;
     114            0 :     want_cap.format = AUDIO_F32;
     115            0 :     want_cap.channels = 1;
     116            0 :     want_cap.samples = static_cast<Uint16>(buffer_size_);
     117            0 :     want_cap.callback = nullptr;
     118              : 
     119            0 :     static const char* usb_keywords[] = {
     120              :         "usb",       "guitar",   "irig",      "scarlett",        "behringer",
     121              :         "focusrite", "presonus", "steinberg", "audio interface", "line 6",
     122              :         "rocksmith", "umc",      "um2",       "uphoria",         "podcast",
     123              :         "xenyx",     "external"};
     124              : 
     125            0 :     const char* preferred_device = nullptr;
     126            0 :     int num_capture = SDL_GetNumAudioDevices(1);
     127            0 :     for (int i = 0; i < num_capture; ++i) {
     128            0 :         const char* name = SDL_GetAudioDeviceName(i, 1);
     129            0 :         if (!name) continue;
     130            0 :         std::string lower = name;
     131            0 :         std::transform(lower.begin(), lower.end(), lower.begin(),
     132            0 :                        [](unsigned char c) { return std::tolower(c); });
     133            0 :         for (const auto* kw : usb_keywords) {
     134            0 :             if (lower.find(kw) != std::string::npos) {
     135            0 :                 preferred_device = name;
     136            0 :                 break;
     137              :             }
     138              :         }
     139            0 :         if (preferred_device) break;
     140            0 :     }
     141              : 
     142            0 :     capture_device_ = SDL_OpenAudioDevice(preferred_device, 1, &want_cap, &have_cap,
     143              :                                           SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
     144              : 
     145            0 :     capture_buffer_.resize(static_cast<size_t>(buffer_size_ * 2), 0.0f);
     146              : 
     147            0 :     SDL_PauseAudioDevice(audio_device_, 0);
     148            0 :     if (capture_device_ != 0) SDL_PauseAudioDevice(capture_device_, 0);
     149              : 
     150            0 :     running_ = true;
     151            0 :     return true;
     152            0 : }
     153              : 
     154            0 : void SdlBackend::stop() {
     155            0 :     if (running_) {
     156            0 :         if (audio_device_ != 0) SDL_PauseAudioDevice(audio_device_, 1);
     157            0 :         if (capture_device_ != 0) SDL_PauseAudioDevice(capture_device_, 1);
     158            0 :         running_ = false;
     159            0 :     }
     160            0 :     if (capture_device_ != 0) {
     161            0 :         SDL_CloseAudioDevice(capture_device_);
     162            0 :         capture_device_ = 0;
     163            0 :     }
     164            0 :     if (audio_device_ != 0) {
     165            0 :         SDL_CloseAudioDevice(audio_device_);
     166            0 :         audio_device_ = 0;
     167            0 :     }
     168            0 : }
     169              : 
     170            0 : std::vector<AudioDeviceInfo> SdlBackend::get_input_devices() const {
     171            0 :     return {{0, "Browser Microphone", 1, 0, 48000.0, false}};
     172            0 : }
     173              : 
     174            0 : std::vector<AudioDeviceInfo> SdlBackend::get_output_devices() const {
     175            0 :     return {{0, "Browser Audio Output", 0, 2, 48000.0, false}};
     176            0 : }
     177              : 
     178            0 : bool SdlBackend::set_input_device(int device_index) {
     179            0 :     selected_input_device_ = device_index;
     180            0 :     return true;
     181              : }
     182              : 
     183            0 : bool SdlBackend::set_output_device(int device_index) {
     184            0 :     selected_output_device_ = device_index;
     185            0 :     return true;
     186              : }
     187              : 
     188            0 : std::string SdlBackend::get_input_device_name() const { return "Browser Microphone"; }
     189              : 
     190            0 : std::string SdlBackend::get_output_device_name() const { return "Browser Audio Output"; }
     191              : 
     192            0 : int SdlBackend::get_sample_rate() const { return sample_rate_; }
     193              : 
     194            0 : int SdlBackend::get_buffer_size() const { return buffer_size_; }
     195              : 
     196              : }  // namespace Amplitron
        

Generated by: LCOV version 2.0-1