2 #if __has_include(<alsa/asoundlib.h>) && !defined(__EMSCRIPTEN__)
4 #include <ossia/audio/audio_engine.hpp>
5 #include <ossia/dataflow/float_to_sample.hpp>
6 #include <ossia/detail/dylib_loader.hpp>
8 #include <ossia/detail/pod_vector.hpp>
9 #include <ossia/detail/thread.hpp>
11 #include <alsa/asoundlib.h>
14 #include <string_view>
18 #define OSSIA_AUDIO_ALSA 1
22 #define snd_alloca(ptr, lib, type) \
25 *ptr = (snd_##type##_t*)alloca(lib.type##_sizeof()); \
26 memset(*ptr, 0, lib.type##_sizeof()); \
31 decltype(&::snd_pcm_open) pcm_open{};
32 decltype(&::snd_pcm_hw_params_any) pcm_hw_params_any{};
33 decltype(&::snd_pcm_hw_params_sizeof) pcm_hw_params_sizeof{};
34 decltype(&::snd_pcm_hw_params_set_access) pcm_hw_params_set_access{};
35 decltype(&::snd_pcm_hw_params_get_format) pcm_hw_params_get_format{};
36 decltype(&::snd_pcm_hw_params_set_format) pcm_hw_params_set_format{};
37 decltype(&::snd_pcm_hw_params_set_channels) pcm_hw_params_set_channels{};
38 decltype(&::snd_pcm_hw_params_set_period_size) pcm_hw_params_set_period_size{};
39 decltype(&::snd_pcm_hw_params_set_buffer_size) pcm_hw_params_set_buffer_size{};
40 decltype(&::snd_pcm_hw_params_set_periods) pcm_hw_params_set_periods{};
41 decltype(&::snd_pcm_hw_params_set_periods_near) pcm_hw_params_set_periods_near{};
42 decltype(&::snd_pcm_hw_params_set_rate_near) pcm_hw_params_set_rate_near{};
43 decltype(&::snd_pcm_hw_params_get_channels) pcm_hw_params_get_channels{};
44 decltype(&::snd_pcm_hw_params_get_channels_min) pcm_hw_params_get_channels_min{};
45 decltype(&::snd_pcm_hw_params_get_channels_max) pcm_hw_params_get_channels_max{};
46 decltype(&::snd_pcm_hw_params_get_rate) pcm_hw_params_get_rate{};
47 decltype(&::snd_pcm_hw_params_get_rate_min) pcm_hw_params_get_rate_min{};
48 decltype(&::snd_pcm_hw_params_get_rate_max) pcm_hw_params_get_rate_max{};
49 decltype(&::snd_pcm_hw_params_get_buffer_size) pcm_hw_params_get_buffer_size{};
50 decltype(&::snd_pcm_hw_params_get_buffer_size_min) pcm_hw_params_get_buffer_size_min{};
51 decltype(&::snd_pcm_hw_params_get_buffer_size_max) pcm_hw_params_get_buffer_size_max{};
52 decltype(&::snd_pcm_hw_params_get_period_size) pcm_hw_params_get_period_size{};
53 decltype(&::snd_pcm_hw_params_get_period_size_min) pcm_hw_params_get_period_size_min{};
54 decltype(&::snd_pcm_hw_params_get_period_size_max) pcm_hw_params_get_period_size_max{};
55 decltype(&::snd_pcm_hw_params_get_periods) pcm_hw_params_get_periods{};
56 decltype(&::snd_pcm_hw_params) pcm_hw_params{};
57 decltype(&::snd_pcm_sw_params_current) pcm_sw_params_current{};
58 decltype(&::snd_pcm_sw_params_sizeof) pcm_sw_params_sizeof{};
59 decltype(&::snd_pcm_sw_params_set_start_threshold) pcm_sw_params_set_start_threshold{};
60 decltype(&::snd_pcm_sw_params_set_avail_min) pcm_sw_params_set_avail_min{};
61 decltype(&::snd_pcm_sw_params) pcm_sw_params{};
62 decltype(&::snd_pcm_name) pcm_name{};
63 decltype(&::snd_pcm_state) pcm_state{};
64 decltype(&::snd_pcm_state_name) pcm_state_name{};
65 decltype(&::snd_pcm_writei) pcm_writei{};
66 decltype(&::snd_pcm_writen) pcm_writen{};
67 decltype(&::snd_pcm_prepare) pcm_prepare{};
68 decltype(&::snd_pcm_start) pcm_start{};
69 decltype(&::snd_pcm_recover) pcm_recover{};
70 decltype(&::snd_pcm_drain) pcm_drain{};
71 decltype(&::snd_pcm_close) pcm_close{};
72 decltype(&::snd_strerror) strerror{};
73 decltype(&::snd_device_name_hint) device_name_hint{};
74 decltype(&::snd_device_name_get_hint) device_name_get_hint{};
75 decltype(&::snd_device_name_free_hint) device_name_free_hint{};
77 static const libasound& instance()
79 static const libasound
self;
87 : library(
"libasound.so.2")
93 pcm_open = library.symbol<decltype(&::snd_pcm_open)>(
"snd_pcm_open");
95 = library.symbol<decltype(&::snd_pcm_hw_params_any)>(
"snd_pcm_hw_params_any");
96 pcm_hw_params_sizeof = library.symbol<decltype(&::snd_pcm_hw_params_sizeof)>(
97 "snd_pcm_hw_params_sizeof");
98 pcm_hw_params_set_access = library.symbol<decltype(&::snd_pcm_hw_params_set_access)>(
99 "snd_pcm_hw_params_set_access");
100 pcm_hw_params_get_format = library.symbol<decltype(&::snd_pcm_hw_params_get_format)>(
101 "snd_pcm_hw_params_get_format");
102 pcm_hw_params_set_format = library.symbol<decltype(&::snd_pcm_hw_params_set_format)>(
103 "snd_pcm_hw_params_set_format");
104 pcm_hw_params_set_channels
105 = library.symbol<decltype(&::snd_pcm_hw_params_set_channels)>(
106 "snd_pcm_hw_params_set_channels");
107 pcm_hw_params_set_period_size
108 = library.symbol<decltype(&::snd_pcm_hw_params_set_period_size)>(
109 "snd_pcm_hw_params_set_period_size");
110 pcm_hw_params_set_buffer_size
111 = library.symbol<decltype(&::snd_pcm_hw_params_set_buffer_size)>(
112 "snd_pcm_hw_params_set_buffer_size");
113 pcm_hw_params_set_periods
114 = library.symbol<decltype(&::snd_pcm_hw_params_set_periods)>(
115 "snd_pcm_hw_params_set_periods");
116 pcm_hw_params_set_periods_near
117 = library.symbol<decltype(&::snd_pcm_hw_params_set_periods_near)>(
118 "snd_pcm_hw_params_set_periods_near");
119 pcm_hw_params_set_rate_near
120 = library.symbol<decltype(&::snd_pcm_hw_params_set_rate_near)>(
121 "snd_pcm_hw_params_set_rate_near");
122 pcm_hw_params_get_channels
123 = library.symbol<decltype(&::snd_pcm_hw_params_get_channels)>(
124 "snd_pcm_hw_params_get_channels");
125 pcm_hw_params_get_channels_min
126 = library.symbol<decltype(&::snd_pcm_hw_params_get_channels_min)>(
127 "snd_pcm_hw_params_get_channels_min");
128 pcm_hw_params_get_channels_max
129 = library.symbol<decltype(&::snd_pcm_hw_params_get_channels_max)>(
130 "snd_pcm_hw_params_get_channels_max");
131 pcm_hw_params_get_rate = library.symbol<decltype(&::snd_pcm_hw_params_get_rate)>(
132 "snd_pcm_hw_params_get_rate");
133 pcm_hw_params_get_rate_min
134 = library.symbol<decltype(&::snd_pcm_hw_params_get_rate_min)>(
135 "snd_pcm_hw_params_get_rate_min");
136 pcm_hw_params_get_rate_max
137 = library.symbol<decltype(&::snd_pcm_hw_params_get_rate_max)>(
138 "snd_pcm_hw_params_get_rate_max");
139 pcm_hw_params_get_buffer_size
140 = library.symbol<decltype(&::snd_pcm_hw_params_get_buffer_size)>(
141 "snd_pcm_hw_params_get_buffer_size");
142 pcm_hw_params_get_buffer_size_min
143 = library.symbol<decltype(&::snd_pcm_hw_params_get_buffer_size_min)>(
144 "snd_pcm_hw_params_get_buffer_size_min");
145 pcm_hw_params_get_buffer_size_max
146 = library.symbol<decltype(&::snd_pcm_hw_params_get_buffer_size_max)>(
147 "snd_pcm_hw_params_get_buffer_size_max");
148 pcm_hw_params_get_period_size
149 = library.symbol<decltype(&::snd_pcm_hw_params_get_period_size)>(
150 "snd_pcm_hw_params_get_period_size");
151 pcm_hw_params_get_period_size_min
152 = library.symbol<decltype(&::snd_pcm_hw_params_get_period_size_min)>(
153 "snd_pcm_hw_params_get_period_size_min");
154 pcm_hw_params_get_period_size_max
155 = library.symbol<decltype(&::snd_pcm_hw_params_get_period_size_max)>(
156 "snd_pcm_hw_params_get_period_size_max");
157 pcm_hw_params_get_periods
158 = library.symbol<decltype(&::snd_pcm_hw_params_get_periods)>(
159 "snd_pcm_hw_params_get_periods");
160 pcm_hw_params = library.symbol<decltype(&::snd_pcm_hw_params)>(
"snd_pcm_hw_params");
161 pcm_sw_params_current = library.symbol<decltype(&::snd_pcm_sw_params_current)>(
162 "snd_pcm_sw_params_current");
163 pcm_sw_params_sizeof = library.symbol<decltype(&::snd_pcm_sw_params_sizeof)>(
164 "snd_pcm_sw_params_sizeof");
165 pcm_sw_params_set_start_threshold
166 = library.symbol<decltype(&::snd_pcm_sw_params_set_start_threshold)>(
167 "snd_pcm_sw_params_set_start_threshold");
168 pcm_sw_params_set_avail_min
169 = library.symbol<decltype(&::snd_pcm_sw_params_set_avail_min)>(
170 "snd_pcm_sw_params_set_avail_min");
171 pcm_sw_params = library.symbol<decltype(&::snd_pcm_sw_params)>(
"snd_pcm_sw_params");
172 pcm_name = library.symbol<decltype(&::snd_pcm_name)>(
"snd_pcm_name");
173 pcm_state = library.symbol<decltype(&::snd_pcm_state)>(
"snd_pcm_state");
175 = library.symbol<decltype(&::snd_pcm_state_name)>(
"snd_pcm_state_name");
176 pcm_writei = library.symbol<decltype(&::snd_pcm_writei)>(
"snd_pcm_writei");
177 pcm_writen = library.symbol<decltype(&::snd_pcm_writen)>(
"snd_pcm_writen");
178 pcm_prepare = library.symbol<decltype(&::snd_pcm_prepare)>(
"snd_pcm_prepare");
179 pcm_start = library.symbol<decltype(&::snd_pcm_start)>(
"snd_pcm_start");
180 pcm_recover = library.symbol<decltype(&::snd_pcm_recover)>(
"snd_pcm_recover");
181 pcm_drain = library.symbol<decltype(&::snd_pcm_drain)>(
"snd_pcm_drain");
182 pcm_close = library.symbol<decltype(&::snd_pcm_close)>(
"snd_pcm_close");
183 strerror = library.symbol<decltype(&::snd_strerror)>(
"snd_strerror");
185 = library.symbol<decltype(&::snd_device_name_hint)>(
"snd_device_name_hint");
186 device_name_get_hint = library.symbol<decltype(&::snd_device_name_get_hint)>(
187 "snd_device_name_get_hint");
188 device_name_free_hint = library.symbol<decltype(&::snd_device_name_free_hint)>(
189 "snd_device_name_free_hint");
195 assert(pcm_hw_params_any);
196 assert(pcm_hw_params_sizeof);
197 assert(pcm_hw_params_set_access);
198 assert(pcm_hw_params_get_format);
199 assert(pcm_hw_params_set_format);
200 assert(pcm_hw_params_set_channels);
201 assert(pcm_hw_params_set_period_size);
202 assert(pcm_hw_params_set_buffer_size);
203 assert(pcm_hw_params_set_periods);
204 assert(pcm_hw_params_set_periods_near);
205 assert(pcm_hw_params_set_rate_near);
206 assert(pcm_hw_params_get_channels);
207 assert(pcm_hw_params_get_channels_min);
208 assert(pcm_hw_params_get_channels_max);
209 assert(pcm_hw_params_get_rate);
210 assert(pcm_hw_params_get_rate_min);
211 assert(pcm_hw_params_get_rate_max);
212 assert(pcm_hw_params_get_buffer_size);
213 assert(pcm_hw_params_get_buffer_size_min);
214 assert(pcm_hw_params_get_buffer_size_max);
215 assert(pcm_hw_params_get_period_size);
216 assert(pcm_hw_params_get_period_size_min);
217 assert(pcm_hw_params_get_period_size_max);
218 assert(pcm_hw_params_get_periods);
219 assert(pcm_hw_params);
220 assert(pcm_sw_params_current);
221 assert(pcm_sw_params_sizeof);
222 assert(pcm_sw_params_set_start_threshold);
223 assert(pcm_sw_params_set_avail_min);
224 assert(pcm_sw_params);
227 assert(pcm_state_name);
236 assert(device_name_hint);
237 assert(device_name_get_hint);
238 assert(device_name_free_hint);
242 struct exception : std::runtime_error
244 template <
typename... Args>
245 exception(fmt::format_string<Args...> format, Args&&... args)
246 : std::runtime_error{fmt::format(format, std::forward<Args>(args)...)}
251 template <auto Format>
252 static constexpr
void
253 snd_interleave(
const float*
const* in,
char* out,
int channels,
int bs)
257 case SND_PCM_FORMAT_S16_LE:
258 return ossia::interleave<int16_t, 16, 2>(
259 in,
reinterpret_cast<int16_t*
>(out), channels, bs);
260 case SND_PCM_FORMAT_S24_LE:
261 return ossia::interleave<int32_t, 24, 4>(
262 in,
reinterpret_cast<int32_t*
>(out), channels, bs);
263 case SND_PCM_FORMAT_S24_3LE:
264 return ossia::interleave<int32_t, 24, 3>(
265 in,
reinterpret_cast<int32_t*
>(out), channels, bs);
266 case SND_PCM_FORMAT_S32_LE:
267 return ossia::interleave<int32_t, 32, 4>(
268 in,
reinterpret_cast<int32_t*
>(out), channels, bs);
269 case SND_PCM_FORMAT_FLOAT_LE:
270 return ossia::interleave<float, 32, 4>(
271 in,
reinterpret_cast<float*
>(out), channels, bs);
277 template <auto Format>
278 static constexpr
void
279 snd_convert(
const float*
const* in,
char* out,
int channels,
int bs)
283 case SND_PCM_FORMAT_S16_LE:
284 return ossia::convert<int16_t, 16>(
285 in,
reinterpret_cast<int16_t*
>(out), channels, bs);
286 case SND_PCM_FORMAT_S24_LE:
287 return ossia::convert<int32_t, 24>(
288 in,
reinterpret_cast<int32_t*
>(out), channels, bs);
289 case SND_PCM_FORMAT_S24_3LE:
290 return ossia::convert<int32_t, 24>(
291 in,
reinterpret_cast<int32_t*
>(out), channels, bs);
292 case SND_PCM_FORMAT_S32_LE:
293 return ossia::convert<int32_t, 32>(
294 in,
reinterpret_cast<int32_t*
>(out), channels, bs);
295 case SND_PCM_FORMAT_FLOAT_LE:
296 return ossia::convert<float, 32>(in,
reinterpret_cast<float*
>(out), channels, bs);
302 template <auto Format>
303 static constexpr
int snd_bytes_per_sample()
307 case SND_PCM_FORMAT_S16_LE:
309 case SND_PCM_FORMAT_S24_3LE:
311 case SND_PCM_FORMAT_S24_LE:
313 case SND_PCM_FORMAT_S32_LE:
315 case SND_PCM_FORMAT_FLOAT_LE:
322 class alsa_engine final :
public audio_engine
326 std::string , std::string card_out,
int inputs,
int outputs,
int rate,
329 const auto& snd = libasound::instance();
332 if(
int ret = snd.pcm_open(&m_client, card_out.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
334 throw ossia::exception(
335 "alsa_engine: error when opening device '{}': {}", card_out,
338 snd_pcm_hw_params_t* hwparams;
339 snd_alloca(&hwparams, snd, pcm_hw_params);
340 snd.pcm_hw_params_any(m_client, hwparams);
345 auto access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
346 if(
int ret = snd.pcm_hw_params_set_access(m_client, hwparams, access); ret < 0)
349 "alsa_engine: can't set noninterleaved mode: {}", snd.strerror(ret));
351 access = SND_PCM_ACCESS_RW_INTERLEAVED;
354 auto format = SND_PCM_FORMAT_FLOAT_LE;
355 if(
int ret = snd.pcm_hw_params_set_format(m_client, hwparams, format); ret < 0)
357 format = SND_PCM_FORMAT_S32_LE;
358 if(
int ret = snd.pcm_hw_params_set_format(m_client, hwparams, format); ret < 0)
360 format = SND_PCM_FORMAT_S24_LE;
361 if(
int ret = snd.pcm_hw_params_set_format(m_client, hwparams, format); ret < 0)
363 format = SND_PCM_FORMAT_S16_LE;
364 if(
int ret = snd.pcm_hw_params_set_format(m_client, hwparams, format);
367 snd.pcm_hw_params_get_format(hwparams, &format);
370 "alsa_engine: can't set format: {} => got {}", snd.strerror(ret),
377 if(
int ret = snd.pcm_hw_params_set_channels(m_client, hwparams, outputs); ret < 0)
379 "alsa_engine: can't set channels number. {}", snd.strerror(ret));
381 if(
int ret = snd.pcm_hw_params_set_period_size(m_client, hwparams, bs, 0); ret < 0)
383 "alsa_engine: can't set period size. {}", snd.strerror(ret));
385 unsigned int pnear = 2;
386 if(
int ret = snd.pcm_hw_params_set_periods_near(m_client, hwparams, &pnear, 0);
388 ossia::logger().error(
"alsa_engine: can't set periods. {}", snd.strerror(ret));
396 unsigned int urate = rate;
397 if(
int ret = snd.pcm_hw_params_set_rate_near(m_client, hwparams, &urate, 0);
399 ossia::logger().error(
"alsa_engine: can't set rate. {}", snd.strerror(ret));
401 if(
int ret = snd.pcm_hw_params(m_client, hwparams); ret < 0)
402 ossia::logger().error(
"alsa_engine: snd_pcm_hw_params: {}", snd.strerror(ret));
404 this->effective_inputs = 0;
407 snd.pcm_hw_params_get_channels(hwparams, &tmp);
408 this->effective_outputs = tmp;
413 snd.pcm_hw_params_get_rate(hwparams, &tmp, 0);
414 this->effective_sample_rate = tmp;
418 snd_pcm_uframes_t tmp{};
419 snd.pcm_hw_params_get_period_size(hwparams, &tmp, 0);
420 this->effective_buffer_size = tmp;
423 snd_pcm_uframes_t tmp_bufsize{};
424 snd.pcm_hw_params_get_buffer_size(hwparams, &tmp_bufsize);
425 unsigned int tmp_periods{};
426 snd.pcm_hw_params_get_periods(hwparams, &tmp_periods, 0);
429 ossia::logger().error(
"Expected: {} : {} : {}\n", outputs, rate, bs);
431 "Got: {} : {} : {} => {} ; {}\n", effective_outputs, effective_sample_rate,
432 effective_buffer_size, tmp_bufsize, tmp_periods);
435 if(access == SND_PCM_ACCESS_RW_NONINTERLEAVED)
439 case SND_PCM_FORMAT_S16_LE:
440 m_thread = std::thread([
this] {
442 run_thread_deinterleaved<SND_PCM_FORMAT_S16_LE>();
446 case SND_PCM_FORMAT_S24_LE:
447 m_thread = std::thread([
this] {
449 run_thread_deinterleaved<SND_PCM_FORMAT_S24_LE>();
453 case SND_PCM_FORMAT_S24_3LE:
454 m_thread = std::thread([
this] {
456 run_thread_deinterleaved<SND_PCM_FORMAT_S24_LE>();
460 case SND_PCM_FORMAT_S32_LE:
461 m_thread = std::thread([
this] {
463 run_thread_deinterleaved<SND_PCM_FORMAT_S32_LE>();
467 case SND_PCM_FORMAT_FLOAT_LE:
468 m_thread = std::thread([
this] {
470 run_thread_deinterleaved<SND_PCM_FORMAT_FLOAT_LE>();
483 case SND_PCM_FORMAT_S16_LE:
484 m_thread = std::thread([
this] {
486 run_thread_interleaved<SND_PCM_FORMAT_S16_LE>();
490 case SND_PCM_FORMAT_S24_LE:
491 m_thread = std::thread([
this] {
493 run_thread_interleaved<SND_PCM_FORMAT_S24_LE>();
497 case SND_PCM_FORMAT_S24_3LE:
498 m_thread = std::thread([
this] {
500 run_thread_interleaved<SND_PCM_FORMAT_S24_LE>();
504 case SND_PCM_FORMAT_S32_LE:
505 m_thread = std::thread([
this] {
507 run_thread_interleaved<SND_PCM_FORMAT_S32_LE>();
511 case SND_PCM_FORMAT_FLOAT_LE:
512 m_thread = std::thread([
this] {
514 run_thread_interleaved<SND_PCM_FORMAT_FLOAT_LE>();
526 ~alsa_engine()
override
533 assert(m_thread.joinable());
536 snd.pcm_drain(m_client);
537 snd.pcm_close(m_client);
541 bool running()
const override
551 ossia::set_thread_name(
"ossia audio 0");
552 ossia::set_thread_pinned(thread_type::Audio, 0);
557 ossia::fill(this->m_temp_buffer, 0.f);
558 ossia::fill(this->m_deinterleaved_buffer, 0.f);
563 ossia::logger().error(
"alsa_engine::submit: {}", snd.strerror(ret));
566 this->stop_processing =
true;
569 bool submit_interleaved(
char* data,
int sample_size_in_bytes)
571 int samples = this->effective_buffer_size;
572 const int channels = this->effective_outputs;
575 int ret = snd.pcm_writei(m_client, data, samples);
577 if(ret == -EPIPE || ret == -ESTRPIPE)
579 ossia::logger().error(
"alsa_engine: snd_pcm_writei: buffer underrun.");
580 if(
int ret = snd.pcm_prepare(m_client); ret < 0)
586 else if(ret == -EAGAIN)
600 data += ret * channels * sample_size_in_bytes;
602 ret = snd.pcm_start(m_client);
605 ossia::logger().error(
"alsa_engine: snd_pcm_start: {}", snd.strerror(ret));
611 bool submit_deinterleaved(
void** data,
int sample_size_in_bytes)
613 int samples = this->effective_buffer_size;
614 const int channels = this->effective_outputs;
617 int ret = snd.pcm_writen(m_client, data, samples);
619 if(ret == -EPIPE || ret == -ESTRPIPE)
621 ossia::logger().error(
"alsa_engine: snd_pcm_writei: buffer underrun.");
622 if(
int ret = snd.pcm_prepare(m_client); ret < 0)
628 else if(ret == -EAGAIN)
642 auto data_c =
reinterpret_cast<char**
>(data);
643 for(
int c = 0; c < channels; ++c)
645 data_c[c] += ret * sample_size_in_bytes;
649 snd.pcm_start(m_client);
655 template <auto Format>
656 void run_thread_interleaved()
658 constexpr
int bytes_per_sample = snd_bytes_per_sample<Format>();
659 m_temp_buffer.resize(
660 this->effective_outputs * this->effective_buffer_size * bytes_per_sample);
661 m_deinterleaved_buffer.resize(this->effective_outputs * this->effective_buffer_size);
663 float** score_outs = (
float**)alloca(
sizeof(
float*) * this->effective_outputs);
664 for(
int c = 0; c < this->effective_outputs; c++)
665 score_outs[c] = m_deinterleaved_buffer.data() + c * this->effective_buffer_size;
667 m_start_time = clk::now();
668 m_last_time = m_start_time;
669 while(!this->m_stop_token)
671 process(
nullptr, score_outs, this->effective_buffer_size);
673 snd_interleave<Format>(
674 score_outs, m_temp_buffer.data(), this->effective_outputs,
675 this->effective_buffer_size);
677 if(!submit_interleaved(m_temp_buffer.data(), bytes_per_sample))
682 template <auto Format>
683 void run_thread_deinterleaved()
685 constexpr
int bytes_per_sample = snd_bytes_per_sample<Format>();
687 if constexpr(Format != SND_PCM_FORMAT_FLOAT_LE)
689 m_temp_buffer.resize(
690 this->effective_outputs * this->effective_buffer_size * bytes_per_sample);
692 m_deinterleaved_buffer.resize(this->effective_outputs * this->effective_buffer_size);
694 float** score_outs = (
float**)alloca(
sizeof(
float*) * this->effective_outputs);
695 void** deinterleaved_outs = (
void**)alloca(
sizeof(
void*) * this->effective_outputs);
697 for(
int c = 0; c < this->effective_outputs; c++)
699 score_outs[c] = m_deinterleaved_buffer.data() + c * this->effective_buffer_size;
702 m_start_time = clk::now();
703 m_last_time = m_start_time;
704 while(!this->m_stop_token)
706 process(
nullptr, score_outs, this->effective_buffer_size);
708 if constexpr(Format != SND_PCM_FORMAT_FLOAT_LE)
712 score_outs, m_temp_buffer.data(), this->effective_outputs,
713 this->effective_buffer_size);
716 for(
int c = 0; c < this->effective_outputs; c++)
718 if constexpr(Format != SND_PCM_FORMAT_FLOAT_LE)
719 deinterleaved_outs[c]
720 = m_deinterleaved_buffer.data() + c * this->effective_buffer_size;
722 deinterleaved_outs[c] = score_outs[c];
726 if(!submit_deinterleaved(deinterleaved_outs, bytes_per_sample))
731 void process(
float**,
float** float_output, uint64_t nframes)
733 auto&
self = *
static_cast<alsa_engine*
>(
this);
736 const auto inputs = this->effective_inputs;
737 const auto outputs = this->effective_outputs;
738 if(
self.stop_processing)
745 using namespace std::chrono;
746 ossia::audio_tick_state ts{
752 duration_cast<nanoseconds>(m_last_time - m_start_time).count() / 1e9};
756 m_last_time = clk::now();
759 using clk = std::chrono::steady_clock;
761 const libasound& snd{libasound::instance()};
763 snd_pcm_t* m_client{};
764 std::thread m_thread;
765 ossia::pod_vector<char> m_temp_buffer;
766 ossia::float_vector m_deinterleaved_buffer;
768 clk::time_point m_start_time{};
769 clk::time_point m_last_time{};
771 std::atomic_bool m_stop_token{};
772 bool m_activated =
false;
spdlog::logger & logger() noexcept
Where the errors will be logged. Default is stderr.
Definition: context.cpp:104