OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
sdl_protocol.hpp
1#pragma once
2#include <ossia/detail/config.hpp>
3
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/thread.hpp>
10
11#include <SDL2/SDL.h>
12#include <SDL2/SDL_audio.h>
13
14#define OSSIA_AUDIO_SDL 1
15
16namespace ossia
17{
18class sdl_protocol final : public audio_engine
19{
20 static constexpr int inputs = 0;
21 static constexpr int outputs = 2;
22
23public:
24 sdl_protocol(int rate, int bs)
25 {
26 SDL_Init(SDL_INIT_AUDIO);
27 m_desired.freq = rate;
28 m_desired.format = AUDIO_F32SYS;
29 m_desired.channels = outputs;
30 m_desired.samples = bs;
31 m_desired.callback = SDLCallback;
32 m_desired.userdata = this;
33
34 m_deviceId = SDL_OpenAudioDevice(nullptr, 0, &m_desired, &m_obtained, 0);
35
36 if(m_deviceId < 2)
37 {
38 using namespace std::literals;
39 throw std::runtime_error("SDL: Couldn't open audio: "s + SDL_GetError());
40 }
41
42 this->effective_sample_rate = m_obtained.freq;
43 this->effective_buffer_size = m_obtained.samples;
44 this->effective_inputs = 0;
45 this->effective_outputs = m_obtained.channels;
46
47 SDL_PauseAudioDevice(m_deviceId, 0);
48 m_activated = true;
49 }
50
51 ~sdl_protocol() override { stop(); }
52
53 bool running() const override
54 {
55 return m_activated && SDL_GetAudioDeviceStatus(m_deviceId) == SDL_AUDIO_PLAYING;
56 }
57
58 void stop() override
59 {
60 audio_engine::stop();
61 if(m_activated)
62 {
63 SDL_CloseAudioDevice(m_deviceId);
64 m_activated = false;
65 }
66 SDL_Quit();
67 }
68
69private:
70 static void SDLCallback(void* userData, Uint8* data, int bytes)
71 {
72 [[maybe_unused]]
73 static const thread_local auto _
74 = [] {
75 ossia::set_thread_name("ossia audio 0");
76 ossia::set_thread_pinned(thread_type::Audio, 0);
77 return 0;
78 }();
79
80 auto& self = *static_cast<sdl_protocol*>(userData);
81 self.tick_start();
82 if(!self.m_start)
83 self.m_start = std::chrono::steady_clock::now();
84
85 auto audio_out = reinterpret_cast<float*>(data);
86 const int out_chan = self.m_obtained.channels;
87 const int frames = self.m_obtained.samples;
88 assert(out_chan > 0);
89 assert(frames > 0);
90 assert(frames * out_chan * sizeof(float) == bytes);
91
92 if(self.stop_processing)
93 {
94 self.tick_clear();
95 memset(data, 0, bytes);
96 return;
97 }
98
99 {
100 auto float_data = (float*)alloca(sizeof(float) * frames * out_chan);
101 memset(float_data, 0, sizeof(sizeof(float) * frames * out_chan));
102
103 auto float_output = (float**)alloca(sizeof(float*) * out_chan);
104
105 for(int c = 0; c < out_chan; c++)
106 {
107 float_output[c] = float_data + c * frames;
108 }
109
110 // if one day there's input... samples[j++] / 32768.;
111
112 auto now = std::chrono::steady_clock::now();
113 auto nsecs
114 = std::chrono::duration_cast<std::chrono::nanoseconds>(now - *self.m_start)
115 .count()
116 / 1e9;
117
118 ossia::audio_tick_state ts{nullptr, float_output, 0,
119 out_chan, (uint64_t)frames, nsecs};
120 self.audio_tick(ts);
121
122 for(int j = 0; j < frames; j++)
123 for(int c = 0; c < out_chan; c++)
124 *audio_out++ = float_output[c][j];
125
126 self.tick_end();
127 self.m_total_frames += frames;
128 }
129 }
130
131 SDL_AudioDeviceID m_deviceId{};
132 SDL_AudioSpec m_desired, m_obtained;
133 uint64_t m_total_frames{};
134 std::optional<std::chrono::steady_clock::time_point> m_start;
135 bool m_activated{};
136};
137}
138
139#endif
140#endif
141#endif
Definition git_info.h:7