OSSIA
Open Scenario System for Interactive Application
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 
16 namespace ossia
17 {
18 class sdl_protocol final : public audio_engine
19 {
20  static constexpr int inputs = 0;
21  static constexpr int outputs = 2;
22 
23 public:
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  }
49 
50  ~sdl_protocol() override
51  {
52  stop();
53  SDL_CloseAudioDevice(m_deviceId);
54  SDL_Quit();
55  }
56 
57  bool running() const override
58  {
59  return SDL_GetAudioDeviceStatus(m_deviceId) == SDL_AUDIO_PLAYING;
60  }
61 
62 private:
63  static void SDLCallback(void* userData, Uint8* data, int bytes)
64  {
65  static const thread_local auto _ = [] {
66  ossia::set_thread_name("ossia audio 0");
67  ossia::set_thread_pinned(thread_type::Audio, 0);
68  return 0;
69  }();
70 
71  auto& self = *static_cast<sdl_protocol*>(userData);
72  self.tick_start();
73 
74  auto audio_out = reinterpret_cast<float*>(data);
75  const int out_chan = self.m_obtained.channels;
76  const int frames = self.m_obtained.samples;
77  assert(out_chan > 0);
78  assert(frames > 0);
79  assert(frames * out_chan * sizeof(float) == bytes);
80 
81  if(self.stop_processing)
82  {
83  self.tick_clear();
84  memset(data, 0, bytes);
85  return;
86  }
87 
88  {
89  auto float_data = (float*)alloca(sizeof(float) * frames * out_chan);
90  memset(float_data, 0, sizeof(sizeof(float) * frames * out_chan));
91 
92  auto float_output = (float**)alloca(sizeof(float*) * out_chan);
93 
94  for(int c = 0; c < out_chan; c++)
95  {
96  float_output[c] = float_data + c * frames;
97  }
98 
99  // if one day there's input... samples[j++] / 32768.;
100 
101  // TODO time in seconds !
102  ossia::audio_tick_state ts{nullptr, float_output, 0,
103  out_chan, (uint64_t)frames, 0};
104  self.audio_tick(ts);
105 
106  for(int j = 0; j < frames; j++)
107  for(int c = 0; c < out_chan; c++)
108  *audio_out++ = float_output[c][j];
109 
110  self.tick_end();
111  }
112  }
113 
114  SDL_AudioDeviceID m_deviceId{};
115  SDL_AudioSpec m_desired, m_obtained;
116 };
117 }
118 
119 #endif
120 #endif
121 #endif
Definition: git_info.h:7