2#include <ossia/detail/config.hpp>
4#if defined(OSSIA_ENABLE_SDL)
5#if __has_include(<SDL2/SDL_audio.h>)
6#include <SDL2/SDL_config.h>
7#if !defined(SDL_AUDIO_DISABLED)
8#include <ossia/audio/audio_engine.hpp>
9#include <ossia/detail/pod_vector.hpp>
10#include <ossia/detail/thread.hpp>
13#include <SDL2/SDL_audio.h>
15#define OSSIA_AUDIO_SDL 1
19class sdl_protocol final :
public audio_engine
21 static constexpr int inputs = 0;
22 static constexpr int outputs = 2;
25 sdl_protocol(
int rate,
int bs)
27 SDL_Init(SDL_INIT_AUDIO);
28 m_desired.freq = rate;
29 m_desired.format = AUDIO_F32SYS;
30 m_desired.channels = outputs;
31 m_desired.samples = bs;
32 m_desired.callback = SDLCallback;
33 m_desired.userdata =
this;
35 m_deviceId = SDL_OpenAudioDevice(
nullptr, 0, &m_desired, &m_obtained, 0);
39 using namespace std::literals;
40 throw std::runtime_error(
"SDL: Couldn't open audio: "s + SDL_GetError());
43 this->effective_sample_rate = m_obtained.freq;
44 this->effective_buffer_size = m_obtained.samples;
45 this->effective_inputs = 0;
46 this->effective_outputs = m_obtained.channels;
50 m_channels.resize(m_obtained.channels);
51 m_scratch.resize(std::size_t(m_obtained.samples) * m_obtained.channels);
53 SDL_PauseAudioDevice(m_deviceId, 0);
57 ~sdl_protocol()
override { stop(); }
59 bool running()
const override
61 return m_activated && SDL_GetAudioDeviceStatus(m_deviceId) == SDL_AUDIO_PLAYING;
69 SDL_CloseAudioDevice(m_deviceId);
74 SDL_QuitSubSystem(SDL_INIT_AUDIO);
78 static void SDLCallback(
void* userData, Uint8* data,
int bytes)
81 static const thread_local auto _
83 ossia::set_thread_name(
"ossia audio 0");
84 ossia::set_thread_pinned(thread_type::Audio, 0);
88 auto& self = *
static_cast<sdl_protocol*
>(userData);
91 self.m_start = std::chrono::steady_clock::now();
93 auto audio_out =
reinterpret_cast<float*
>(data);
94 const int out_chan = self.m_obtained.channels;
105 const int frames = bytes / int(
sizeof(
float) * out_chan);
107 if(self.stop_processing || frames <= 0)
110 memset(data, 0, bytes);
115 const std::size_t samples = std::size_t(frames) * out_chan;
116 if(self.m_scratch.size() < samples)
117 self.m_scratch.resize(samples);
119 float*
const float_data = self.m_scratch.data();
120 memset(float_data, 0,
sizeof(
float) * samples);
122 float**
const float_output = self.m_channels.data();
123 for(
int c = 0; c < out_chan; c++)
125 float_output[c] = float_data + c * frames;
130 auto now = std::chrono::steady_clock::now();
132 = std::chrono::duration_cast<std::chrono::nanoseconds>(now - *self.m_start)
136 ossia::audio_tick_state ts{
nullptr, float_output, 0,
137 out_chan, (uint64_t)frames, nsecs};
140 for(
int j = 0; j < frames; j++)
141 for(
int c = 0; c < out_chan; c++)
142 *audio_out++ = float_output[c][j];
146 const int written = int(
sizeof(
float) * samples);
148 memset(data + written, 0, bytes - written);
151 self.m_total_frames += frames;
155 SDL_AudioDeviceID m_deviceId{};
156 SDL_AudioSpec m_desired, m_obtained;
157 ossia::float_vector m_scratch;
158 ossia::pod_vector<float*> m_channels;
159 uint64_t m_total_frames{};
160 std::optional<std::chrono::steady_clock::time_point> m_start;