OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
asio_protocol.hpp
1#pragma once
2#include <ossia/detail/config.hpp>
3
4#if defined(OSSIA_ENABLE_ASIO)
5#include <ossia/audio/audio_engine.hpp>
6#include <ossia/detail/thread.hpp>
7
8#if !defined(WIN32_LEAN_AND_MEAN)
9#define WIN32_LEAN_AND_MEAN
10#endif
11#if !defined(NOMINMAX)
12#define NOMINMAX
13#endif
14
15
16#include <asiodrivers.h>
17#include <asio.h>
18#include <iasiodrv.h>
19
20bool loadAsioDriver(char *name);
21extern AsioDrivers* asioDrivers;
22
23#include <algorithm>
24#include <cmath>
25#include <cstring>
26#include <iostream>
27#include <string>
28#include <vector>
29
30#define OSSIA_AUDIO_ASIO 1
31
32namespace ossia
33{
34struct asio_card
35{
36 std::string name;
37 int driver_index{-1};
38};
39
40// Must be a singleton — ASIO SDK only supports one loaded driver at a time.
41// We store the active engine pointer in a global so the static ASIO callbacks
42// can reach it.
43class asio_engine;
44namespace detail
45{
46inline asio_engine* current_asio_engine = nullptr;
47}
48
49class asio_engine final : public audio_engine
50{
51public:
52 asio_engine(
53 std::string driver_name, int inputs, int outputs, int rate, int bs)
54 {
55 if(detail::current_asio_engine)
56 throw std::runtime_error("ASIO error: only one ASIO engine can be active at a time");
57
58 detail::current_asio_engine = this;
59
60 // Load the ASIO driver
61 if(!loadAsioDriver(const_cast<char*>(driver_name.c_str())))
62 {
63 detail::current_asio_engine = nullptr;
64 throw std::runtime_error("ASIO error: could not load driver '" + driver_name + "'");
65 }
66
67 // Initialize the driver
68 ASIODriverInfo driverInfo{};
69 driverInfo.asioVersion = 2;
70 driverInfo.sysRef = nullptr; // no window handle needed for headless operation
71
72 if(ASIOInit(&driverInfo) != ASE_OK)
73 {
74 unload_driver();
75 throw std::runtime_error(
76 std::string("ASIO error: ASIOInit failed: ") + driverInfo.errorMessage);
77 }
78 m_initialized = true;
79
80 // Query driver capabilities
81 long maxInputChannels = 0, maxOutputChannels = 0;
82 if(ASIOGetChannels(&maxInputChannels, &maxOutputChannels) != ASE_OK)
83 {
84 cleanup();
85 throw std::runtime_error("ASIO error: ASIOGetChannels failed");
86 }
87
88 inputs = std::min((long)inputs, maxInputChannels);
89 outputs = std::min((long)outputs, maxOutputChannels);
90
91 if(inputs == 0 && outputs == 0)
92 {
93 cleanup();
94 throw std::runtime_error("ASIO error: no channels available");
95 }
96
97 // Set sample rate
98 if(ASIOCanSampleRate((ASIOSampleRate)rate) != ASE_OK)
99 {
100 // Try to use the driver's current rate
101 ASIOSampleRate currentRate = 0;
102 if(ASIOGetSampleRate(&currentRate) == ASE_OK && currentRate > 0)
103 {
104 rate = (int)currentRate;
105 }
106 else
107 {
108 cleanup();
109 throw std::runtime_error("ASIO error: sample rate not supported");
110 }
111 }
112 else
113 {
114 if(ASIOSetSampleRate((ASIOSampleRate)rate) != ASE_OK)
115 {
116 cleanup();
117 throw std::runtime_error("ASIO error: could not set sample rate");
118 }
119 }
120
121 // Query buffer size
122 long minSize = 0, maxSize = 0, preferredSize = 0, granularity = 0;
123 if(ASIOGetBufferSize(&minSize, &maxSize, &preferredSize, &granularity) != ASE_OK)
124 {
125 cleanup();
126 throw std::runtime_error("ASIO error: ASIOGetBufferSize failed");
127 }
128
129 // Clamp requested buffer size to driver constraints
130 if(bs <= 0)
131 bs = preferredSize;
132 bs = std::max(bs, (int)minSize);
133 bs = std::min(bs, (int)maxSize);
134
135 // For power-of-2 granularity, snap to nearest valid power of 2
136 if(granularity == -1)
137 {
138 int pot = minSize;
139 while(pot < bs && pot < maxSize)
140 pot *= 2;
141 bs = pot;
142 }
143 else if(granularity > 0)
144 {
145 bs = ((bs + granularity - 1) / granularity) * granularity;
146 bs = std::min(bs, (int)maxSize);
147 }
148
149 m_bufferSize = bs;
150 m_inputCount = inputs;
151 m_outputCount = outputs;
152
153 // Allocate buffer info structures
154 int totalChannels = inputs + outputs;
155 m_bufferInfos.resize(totalChannels);
156 m_channelInfos.resize(totalChannels);
157
158 int idx = 0;
159 for(int i = 0; i < inputs; i++, idx++)
160 {
161 m_bufferInfos[idx].isInput = ASIOTrue;
162 m_bufferInfos[idx].channelNum = i;
163 m_bufferInfos[idx].buffers[0] = m_bufferInfos[idx].buffers[1] = nullptr;
164 }
165 for(int i = 0; i < outputs; i++, idx++)
166 {
167 m_bufferInfos[idx].isInput = ASIOFalse;
168 m_bufferInfos[idx].channelNum = i;
169 m_bufferInfos[idx].buffers[0] = m_bufferInfos[idx].buffers[1] = nullptr;
170 }
171
172 // Set up callbacks
173 m_asioCallbacks.bufferSwitch = &asio_bufferSwitch;
174 m_asioCallbacks.sampleRateDidChange = &asio_sampleRateDidChange;
175 m_asioCallbacks.asioMessage = &asio_message;
176 m_asioCallbacks.bufferSwitchTimeInfo = &asio_bufferSwitchTimeInfo;
177
178 // Create buffers
179 if(ASIOCreateBuffers(
180 m_bufferInfos.data(), totalChannels, m_bufferSize, &m_asioCallbacks)
181 != ASE_OK)
182 {
183 cleanup();
184 throw std::runtime_error("ASIO error: ASIOCreateBuffers failed");
185 }
186 m_buffersCreated = true;
187
188 // Get channel info (for sample format)
189 for(int i = 0; i < totalChannels; i++)
190 {
191 m_channelInfos[i].channel = m_bufferInfos[i].channelNum;
192 m_channelInfos[i].isInput = m_bufferInfos[i].isInput;
193 if(ASIOGetChannelInfo(&m_channelInfos[i]) != ASE_OK)
194 {
195 cleanup();
196 throw std::runtime_error("ASIO error: ASIOGetChannelInfo failed");
197 }
198 }
199
200 // Check if ASIOOutputReady is supported
201 m_postOutput = (ASIOOutputReady() == ASE_OK);
202
203 // Allocate float conversion buffers (non-interleaved, one per channel)
204 m_floatInputs.resize(inputs);
205 m_floatOutputs.resize(outputs);
206 m_inputPtrs.resize(inputs);
207 m_outputPtrs.resize(outputs);
208 for(int i = 0; i < inputs; i++)
209 {
210 m_floatInputs[i].resize(m_bufferSize);
211 m_inputPtrs[i] = m_floatInputs[i].data();
212 }
213 for(int i = 0; i < outputs; i++)
214 {
215 m_floatOutputs[i].resize(m_bufferSize);
216 m_outputPtrs[i] = m_floatOutputs[i].data();
217 }
218
219 // Set effective parameters
220 this->effective_sample_rate = rate;
221 this->effective_buffer_size = m_bufferSize;
222 this->effective_inputs = inputs;
223 this->effective_outputs = outputs;
224
225 // Start processing
226 if(ASIOStart() != ASE_OK)
227 {
228 cleanup();
229 throw std::runtime_error("ASIO error: ASIOStart failed");
230 }
231 m_started = true;
232 }
233
234 bool running() const override { return m_started && !stop_processing; }
235
236 void stop() override
237 {
238 audio_engine::stop();
239
240 if(m_started)
241 {
242 ASIOStop();
243 m_started = false;
244 }
245 }
246
247 ~asio_engine() override
248 {
249 stop();
250 cleanup();
251 }
252
253 static std::vector<asio_card> enumerate_drivers()
254 {
255 std::vector<asio_card> cards;
256
257 AsioDrivers drivers;
258 long numDrivers = drivers.asioGetNumDev();
259 for(long i = 0; i < numDrivers; i++)
260 {
261 char name[128]{};
262 if(drivers.asioGetDriverName(i, name, sizeof(name)) == 0)
263 {
264 cards.push_back({name, (int)i});
265 }
266 }
267 return cards;
268 }
269
270 // Open the ASIO control panel for a given driver.
271 // If the engine is currently running with that driver, calls ASIOControlPanel() directly.
272 // Otherwise, temporarily loads the driver to show the panel.
273 static void open_control_panel(const std::string& driver_name)
274 {
275 // If the engine is active and running this driver, just call the API directly
276 if(detail::current_asio_engine)
277 {
278 ASIOControlPanel();
279 return;
280 }
281
282 // No engine running — load driver temporarily
283 if(!loadAsioDriver(const_cast<char*>(driver_name.c_str())))
284 return;
285
286 ASIODriverInfo info{};
287 info.asioVersion = 2;
288 if(ASIOInit(&info) == ASE_OK)
289 {
290 ASIOControlPanel();
291 // Note: we do NOT call ASIOExit() here. Most ASIO control panels are
292 // modal dialogs that block until dismissed, but some are not. Either way
293 // the driver stays loaded while the panel is up. The next rescan or
294 // engine creation will removeCurrentDriver() and reload as needed.
295 }
296 }
297
298private:
299 void cleanup()
300 {
301 if(m_started)
302 {
303 ASIOStop();
304 m_started = false;
305 }
306
307 if(m_buffersCreated)
308 {
309 ASIODisposeBuffers();
310 m_buffersCreated = false;
311 }
312
313 if(m_initialized)
314 {
315 ASIOExit();
316 m_initialized = false;
317 }
318
319 unload_driver();
320 }
321
322 void unload_driver()
323 {
324 if(asioDrivers)
325 {
326 asioDrivers->removeCurrentDriver();
327 }
328 detail::current_asio_engine = nullptr;
329 }
330
331 // Convert ASIO native buffer to float for one channel
332 static void convertToFloat(
333 void* src, float* dst, long frames, ASIOSampleType type)
334 {
335 switch(type)
336 {
337 case ASIOSTFloat32LSB:
338 {
339 std::memcpy(dst, src, frames * sizeof(float));
340 break;
341 }
342 case ASIOSTFloat64LSB:
343 {
344 auto* s = static_cast<double*>(src);
345 for(long i = 0; i < frames; i++)
346 dst[i] = (float)s[i];
347 break;
348 }
349 case ASIOSTInt32LSB:
350 {
351 auto* s = static_cast<int32_t*>(src);
352 constexpr float scale = 1.0f / 2147483648.0f;
353 for(long i = 0; i < frames; i++)
354 dst[i] = s[i] * scale;
355 break;
356 }
357 case ASIOSTInt24LSB:
358 {
359 auto* s = static_cast<uint8_t*>(src);
360 constexpr float scale = 1.0f / 8388608.0f;
361 for(long i = 0; i < frames; i++)
362 {
363 int32_t val = (int32_t(s[i * 3 + 2]) << 24) | (int32_t(s[i * 3 + 1]) << 16)
364 | (int32_t(s[i * 3]) << 8);
365 dst[i] = (val >> 8) * scale;
366 }
367 break;
368 }
369 case ASIOSTInt16LSB:
370 {
371 auto* s = static_cast<int16_t*>(src);
372 constexpr float scale = 1.0f / 32768.0f;
373 for(long i = 0; i < frames; i++)
374 dst[i] = s[i] * scale;
375 break;
376 }
377 case ASIOSTInt32LSB16:
378 {
379 auto* s = static_cast<int32_t*>(src);
380 constexpr float scale = 1.0f / 32768.0f;
381 for(long i = 0; i < frames; i++)
382 dst[i] = (s[i] & 0xFFFF) * scale;
383 break;
384 }
385 case ASIOSTInt32LSB18:
386 {
387 auto* s = static_cast<int32_t*>(src);
388 constexpr float scale = 1.0f / 131072.0f;
389 for(long i = 0; i < frames; i++)
390 dst[i] = (s[i] & 0x3FFFF) * scale;
391 break;
392 }
393 case ASIOSTInt32LSB20:
394 {
395 auto* s = static_cast<int32_t*>(src);
396 constexpr float scale = 1.0f / 524288.0f;
397 for(long i = 0; i < frames; i++)
398 dst[i] = (s[i] & 0xFFFFF) * scale;
399 break;
400 }
401 case ASIOSTInt32LSB24:
402 {
403 auto* s = static_cast<int32_t*>(src);
404 constexpr float scale = 1.0f / 8388608.0f;
405 for(long i = 0; i < frames; i++)
406 dst[i] = (s[i] & 0xFFFFFF) * scale;
407 break;
408 }
409 // MSB formats (big-endian) — rare on x86 but must be handled
410 case ASIOSTFloat32MSB:
411 {
412 auto* s = static_cast<uint8_t*>(src);
413 for(long i = 0; i < frames; i++)
414 {
415 uint32_t val = (uint32_t(s[i * 4]) << 24) | (uint32_t(s[i * 4 + 1]) << 16)
416 | (uint32_t(s[i * 4 + 2]) << 8) | uint32_t(s[i * 4 + 3]);
417 float f;
418 std::memcpy(&f, &val, 4);
419 dst[i] = f;
420 }
421 break;
422 }
423 case ASIOSTInt32MSB:
424 {
425 auto* s = static_cast<uint8_t*>(src);
426 constexpr float scale = 1.0f / 2147483648.0f;
427 for(long i = 0; i < frames; i++)
428 {
429 int32_t val = (int32_t(s[i * 4]) << 24) | (int32_t(s[i * 4 + 1]) << 16)
430 | (int32_t(s[i * 4 + 2]) << 8) | int32_t(s[i * 4 + 3]);
431 dst[i] = val * scale;
432 }
433 break;
434 }
435 case ASIOSTInt16MSB:
436 {
437 auto* s = static_cast<uint8_t*>(src);
438 constexpr float scale = 1.0f / 32768.0f;
439 for(long i = 0; i < frames; i++)
440 {
441 int16_t val = (int16_t(s[i * 2]) << 8) | int16_t(s[i * 2 + 1]);
442 dst[i] = val * scale;
443 }
444 break;
445 }
446 default:
447 std::memset(dst, 0, frames * sizeof(float));
448 break;
449 }
450 }
451
452 // Convert float to ASIO native buffer for one channel
453 static void convertFromFloat(
454 const float* src, void* dst, long frames, ASIOSampleType type)
455 {
456 switch(type)
457 {
458 case ASIOSTFloat32LSB:
459 {
460 std::memcpy(dst, src, frames * sizeof(float));
461 break;
462 }
463 case ASIOSTFloat64LSB:
464 {
465 auto* d = static_cast<double*>(dst);
466 for(long i = 0; i < frames; i++)
467 d[i] = (double)src[i];
468 break;
469 }
470 case ASIOSTInt32LSB:
471 {
472 auto* d = static_cast<int32_t*>(dst);
473 constexpr double scale = 2147483647.0;
474 for(long i = 0; i < frames; i++)
475 {
476 double val = std::max(-1.0, std::min(1.0, (double)src[i]));
477 d[i] = (int32_t)(val * scale);
478 }
479 break;
480 }
481 case ASIOSTInt24LSB:
482 {
483 auto* d = static_cast<uint8_t*>(dst);
484 constexpr double scale = 8388607.0;
485 for(long i = 0; i < frames; i++)
486 {
487 double val = std::max(-1.0, std::min(1.0, (double)src[i]));
488 int32_t s = (int32_t)(val * scale);
489 d[i * 3] = (uint8_t)(s & 0xFF);
490 d[i * 3 + 1] = (uint8_t)((s >> 8) & 0xFF);
491 d[i * 3 + 2] = (uint8_t)((s >> 16) & 0xFF);
492 }
493 break;
494 }
495 case ASIOSTInt16LSB:
496 {
497 auto* d = static_cast<int16_t*>(dst);
498 constexpr double scale = 32767.0;
499 for(long i = 0; i < frames; i++)
500 {
501 double val = std::max(-1.0, std::min(1.0, (double)src[i]));
502 d[i] = (int16_t)(val * scale);
503 }
504 break;
505 }
506 case ASIOSTInt32LSB16:
507 {
508 auto* d = static_cast<int32_t*>(dst);
509 constexpr double scale = 32767.0;
510 for(long i = 0; i < frames; i++)
511 {
512 double val = std::max(-1.0, std::min(1.0, (double)src[i]));
513 d[i] = (int32_t)(val * scale);
514 }
515 break;
516 }
517 case ASIOSTInt32LSB18:
518 {
519 auto* d = static_cast<int32_t*>(dst);
520 constexpr double scale = 131071.0;
521 for(long i = 0; i < frames; i++)
522 {
523 double val = std::max(-1.0, std::min(1.0, (double)src[i]));
524 d[i] = (int32_t)(val * scale);
525 }
526 break;
527 }
528 case ASIOSTInt32LSB20:
529 {
530 auto* d = static_cast<int32_t*>(dst);
531 constexpr double scale = 524287.0;
532 for(long i = 0; i < frames; i++)
533 {
534 double val = std::max(-1.0, std::min(1.0, (double)src[i]));
535 d[i] = (int32_t)(val * scale);
536 }
537 break;
538 }
539 case ASIOSTInt32LSB24:
540 {
541 auto* d = static_cast<int32_t*>(dst);
542 constexpr double scale = 8388607.0;
543 for(long i = 0; i < frames; i++)
544 {
545 double val = std::max(-1.0, std::min(1.0, (double)src[i]));
546 d[i] = (int32_t)(val * scale);
547 }
548 break;
549 }
550 case ASIOSTFloat32MSB:
551 {
552 auto* d = static_cast<uint8_t*>(dst);
553 for(long i = 0; i < frames; i++)
554 {
555 uint32_t val;
556 std::memcpy(&val, &src[i], 4);
557 d[i * 4] = (uint8_t)(val >> 24);
558 d[i * 4 + 1] = (uint8_t)(val >> 16);
559 d[i * 4 + 2] = (uint8_t)(val >> 8);
560 d[i * 4 + 3] = (uint8_t)(val);
561 }
562 break;
563 }
564 case ASIOSTInt32MSB:
565 {
566 auto* d = static_cast<uint8_t*>(dst);
567 constexpr double scale = 2147483647.0;
568 for(long i = 0; i < frames; i++)
569 {
570 double val = std::max(-1.0, std::min(1.0, (double)src[i]));
571 int32_t s = (int32_t)(val * scale);
572 d[i * 4] = (uint8_t)(s >> 24);
573 d[i * 4 + 1] = (uint8_t)(s >> 16);
574 d[i * 4 + 2] = (uint8_t)(s >> 8);
575 d[i * 4 + 3] = (uint8_t)(s);
576 }
577 break;
578 }
579 case ASIOSTInt16MSB:
580 {
581 auto* d = static_cast<uint8_t*>(dst);
582 constexpr double scale = 32767.0;
583 for(long i = 0; i < frames; i++)
584 {
585 double val = std::max(-1.0, std::min(1.0, (double)src[i]));
586 int16_t s = (int16_t)(val * scale);
587 d[i * 2] = (uint8_t)(s >> 8);
588 d[i * 2 + 1] = (uint8_t)(s);
589 }
590 break;
591 }
592 default:
593 break;
594 }
595 }
596
597 // === ASIO Callbacks (static, use global engine pointer) ===
598
599 static ASIOTime* asio_bufferSwitchTimeInfo(
600 ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess)
601 {
602 auto* self = detail::current_asio_engine;
603 if(!self)
604 return nullptr;
605
606 [[maybe_unused]] static const thread_local auto _ = [] {
607 ossia::set_thread_name("ossia audio 0");
608 ossia::set_thread_pinned(thread_type::Audio, 0);
609 return 0;
610 }();
611
612 self->tick_start();
613
614 if(self->stop_processing)
615 {
616 self->tick_clear();
617 // Clear output buffers
618 for(int i = 0; i < self->m_outputCount; i++)
619 {
620 auto& info = self->m_bufferInfos[self->m_inputCount + i];
621 auto& chanInfo = self->m_channelInfos[self->m_inputCount + i];
622 void* buf = info.buffers[doubleBufferIndex];
623 if(buf)
624 {
625 std::memset(buf, 0, self->sampleSize(chanInfo.type) * self->m_bufferSize);
626 }
627 }
628 if(self->m_postOutput)
629 ASIOOutputReady();
630 return nullptr;
631 }
632
633 const long frames = self->m_bufferSize;
634
635 // Convert ASIO input buffers -> float
636 for(int i = 0; i < self->m_inputCount; i++)
637 {
638 void* src = self->m_bufferInfos[i].buffers[doubleBufferIndex];
639 convertToFloat(src, self->m_inputPtrs[i], frames, self->m_channelInfos[i].type);
640 }
641
642 // Clear float output buffers
643 for(int i = 0; i < self->m_outputCount; i++)
644 {
645 std::memset(self->m_outputPtrs[i], 0, frames * sizeof(float));
646 }
647
648 // Compute time — ASIO has no transport, so we only provide wall-clock
649 // seconds (like PortAudio). samplePosition is a monotonic counter since
650 // ASIOStart() and must NOT be used as position_in_frames (which is a
651 // transport position that resets on stop/play).
652 double seconds = 0.0;
653 if(params && (params->timeInfo.flags & kSystemTimeValid))
654 {
655 seconds = asioSamplesToDouble(params->timeInfo.systemTime) * 1e-9;
656 }
657
658 // Call audio tick
659 ossia::audio_tick_state ts{
660 const_cast<float* const*>(self->m_inputPtrs.data()),
661 self->m_outputPtrs.data(),
662 self->m_inputCount,
663 self->m_outputCount,
664 (uint64_t)frames,
665 seconds};
666
667 self->audio_tick(ts);
668 self->tick_end();
669
670 // Convert float output buffers -> ASIO native format
671 for(int i = 0; i < self->m_outputCount; i++)
672 {
673 int chIdx = self->m_inputCount + i;
674 void* dst = self->m_bufferInfos[chIdx].buffers[doubleBufferIndex];
675 convertFromFloat(
676 self->m_outputPtrs[i], dst, frames, self->m_channelInfos[chIdx].type);
677 }
678
679 if(self->m_postOutput)
680 ASIOOutputReady();
681
682 return nullptr;
683 }
684
685 static void asio_bufferSwitch(long doubleBufferIndex, ASIOBool directProcess)
686 {
687 // Construct time info and delegate to the time-info callback
688 ASIOTime timeInfo{};
689 std::memset(&timeInfo, 0, sizeof(ASIOTime));
690
691 ASIOSamples sPos;
692 ASIOTimeStamp tStamp;
693 if(ASIOGetSamplePosition(&sPos, &tStamp) == ASE_OK)
694 {
695 timeInfo.timeInfo.samplePosition = sPos;
696 timeInfo.timeInfo.systemTime = tStamp;
697 timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
698 }
699
700 ASIOSampleRate sRate = 0;
701 if(ASIOGetSampleRate(&sRate) == ASE_OK)
702 {
703 timeInfo.timeInfo.sampleRate = sRate;
704 timeInfo.timeInfo.flags |= kSampleRateValid;
705 }
706
707 asio_bufferSwitchTimeInfo(&timeInfo, doubleBufferIndex, directProcess);
708 }
709
710 static void asio_sampleRateDidChange(ASIOSampleRate sRate)
711 {
712 auto* self = detail::current_asio_engine;
713 if(self && sRate > 0)
714 {
715 self->effective_sample_rate = (int)sRate;
716 }
717 }
718
719 static long asio_message(long selector, long value, void* message, double* opt)
720 {
721 switch(selector)
722 {
723 case kAsioSelectorSupported:
724 switch(value)
725 {
726 case kAsioEngineVersion:
727 case kAsioSupportsTimeInfo:
728 case kAsioResetRequest:
729 case kAsioResyncRequest:
730 case kAsioLatenciesChanged:
731 case kAsioOverload:
732 return 1;
733 default:
734 return 0;
735 }
736
737 case kAsioEngineVersion:
738 return 2;
739
740 case kAsioSupportsTimeInfo:
741 return 1;
742
743 case kAsioResetRequest:
744 // Driver requests reset — for now, just acknowledge
745 return 1;
746
747 case kAsioResyncRequest:
748 return 1;
749
750 case kAsioLatenciesChanged:
751 return 1;
752
753 case kAsioOverload:
754 return 1;
755
756 default:
757 return 0;
758 }
759 }
760
761 static double asioSamplesToDouble(const ASIOSamples& s)
762 {
763#if NATIVE_INT64
764 return (double)s;
765#else
766 return s.hi * 4294967296.0 + s.lo;
767#endif
768 }
769
770 static double asioSamplesToDouble(const ASIOTimeStamp& s)
771 {
772#if NATIVE_INT64
773 return (double)s;
774#else
775 return s.hi * 4294967296.0 + s.lo;
776#endif
777 }
778
779 static long sampleSize(ASIOSampleType type)
780 {
781 switch(type)
782 {
783 case ASIOSTInt16MSB:
784 case ASIOSTInt16LSB:
785 return 2;
786 case ASIOSTInt24MSB:
787 case ASIOSTInt24LSB:
788 return 3;
789 case ASIOSTInt32MSB:
790 case ASIOSTInt32LSB:
791 case ASIOSTFloat32MSB:
792 case ASIOSTFloat32LSB:
793 case ASIOSTInt32MSB16:
794 case ASIOSTInt32MSB18:
795 case ASIOSTInt32MSB20:
796 case ASIOSTInt32MSB24:
797 case ASIOSTInt32LSB16:
798 case ASIOSTInt32LSB18:
799 case ASIOSTInt32LSB20:
800 case ASIOSTInt32LSB24:
801 return 4;
802 case ASIOSTFloat64MSB:
803 case ASIOSTFloat64LSB:
804 return 8;
805 default:
806 return 4;
807 }
808 }
809
810 std::vector<ASIOBufferInfo> m_bufferInfos;
811 std::vector<ASIOChannelInfo> m_channelInfos;
812 ASIOCallbacks m_asioCallbacks{};
813
814 // Float conversion buffers
815 std::vector<std::vector<float>> m_floatInputs;
816 std::vector<std::vector<float>> m_floatOutputs;
817 std::vector<float*> m_inputPtrs;
818 std::vector<float*> m_outputPtrs;
819
820 int m_bufferSize{};
821 int m_inputCount{};
822 int m_outputCount{};
823 bool m_postOutput{};
824 bool m_initialized{};
825 bool m_buffersCreated{};
826 bool m_started{};
827};
828}
829
830#endif
Definition git_info.h:7