2#include <ossia/detail/config.hpp>
4#if defined(OSSIA_ENABLE_ASIO)
5#include <ossia/audio/audio_engine.hpp>
6#include <ossia/detail/fmt.hpp>
8#include <ossia/detail/pod_vector.hpp>
9#include <ossia/detail/thread.hpp>
11#if !defined(WIN32_LEAN_AND_MEAN)
12#define WIN32_LEAN_AND_MEAN
23#include <asiodrivers.h>
27bool loadAsioDriver(
char *name);
28extern AsioDrivers* asioDrivers;
36#define OSSIA_AUDIO_ASIO 1
52inline asio_engine* current_asio_engine =
nullptr;
55class asio_engine final :
public audio_engine
59 std::string driver_name,
int inputs,
int outputs,
int rate,
int bs)
61 if(detail::current_asio_engine)
63 throw std::runtime_error(
64 "ASIO error: driver '" + detail::current_asio_engine->m_driver_name
65 +
"' is still active, cannot open '" + driver_name +
"'");
68 detail::current_asio_engine =
this;
69 m_driver_name = driver_name;
72 if(!loadAsioDriver(
const_cast<char*
>(driver_name.c_str())))
75 throw std::runtime_error(
"ASIO error: could not load driver '" + driver_name +
"'");
79 ASIODriverInfo driverInfo{};
80 driverInfo.asioVersion = 2;
81 driverInfo.sysRef =
nullptr;
83 if(ASIOInit(&driverInfo) != ASE_OK)
86 throw std::runtime_error(
87 std::string(
"ASIO error: ASIOInit failed: ") + driverInfo.errorMessage);
92 long maxInputChannels = 0, maxOutputChannels = 0;
93 if(ASIOGetChannels(&maxInputChannels, &maxOutputChannels) != ASE_OK)
96 throw std::runtime_error(
"ASIO error: ASIOGetChannels failed");
99 inputs = std::min((
long)inputs, maxInputChannels);
100 outputs = std::min((
long)outputs, maxOutputChannels);
102 if(inputs == 0 && outputs == 0)
105 throw std::runtime_error(
"ASIO error: no channels available");
109 if(ASIOCanSampleRate((ASIOSampleRate)rate) != ASE_OK)
112 ASIOSampleRate currentRate = 0;
113 if(ASIOGetSampleRate(¤tRate) == ASE_OK && currentRate > 0)
115 rate = (int)currentRate;
120 throw std::runtime_error(
"ASIO error: sample rate not supported");
125 if(ASIOSetSampleRate((ASIOSampleRate)rate) != ASE_OK)
128 throw std::runtime_error(
"ASIO error: could not set sample rate");
133 long minSize = 0, maxSize = 0, preferredSize = 0, granularity = 0;
134 if(ASIOGetBufferSize(&minSize, &maxSize, &preferredSize, &granularity) != ASE_OK)
137 throw std::runtime_error(
"ASIO error: ASIOGetBufferSize failed");
143 bs = std::max(bs, (
int)minSize);
144 bs = std::min(bs, (
int)maxSize);
147 if(granularity == -1)
150 while(pot < bs && pot < maxSize)
154 else if(granularity > 0)
156 bs = ((bs + granularity - 1) / granularity) * granularity;
157 bs = std::min(bs, (
int)maxSize);
161 m_inputCount = inputs;
162 m_outputCount = outputs;
165 int totalChannels = inputs + outputs;
166 m_bufferInfos.resize(totalChannels);
167 m_channelInfos.resize(totalChannels);
170 for(
int i = 0; i < inputs; i++, idx++)
172 m_bufferInfos[idx].isInput = ASIOTrue;
173 m_bufferInfos[idx].channelNum = i;
174 m_bufferInfos[idx].buffers[0] = m_bufferInfos[idx].buffers[1] =
nullptr;
176 for(
int i = 0; i < outputs; i++, idx++)
178 m_bufferInfos[idx].isInput = ASIOFalse;
179 m_bufferInfos[idx].channelNum = i;
180 m_bufferInfos[idx].buffers[0] = m_bufferInfos[idx].buffers[1] =
nullptr;
184 m_asioCallbacks.bufferSwitch = &asio_bufferSwitch;
185 m_asioCallbacks.sampleRateDidChange = &asio_sampleRateDidChange;
186 m_asioCallbacks.asioMessage = &asio_message;
187 m_asioCallbacks.bufferSwitchTimeInfo = &asio_bufferSwitchTimeInfo;
190 if(ASIOCreateBuffers(
191 m_bufferInfos.data(), totalChannels, m_bufferSize, &m_asioCallbacks)
195 throw std::runtime_error(
"ASIO error: ASIOCreateBuffers failed");
197 m_buffersCreated =
true;
200 for(
int i = 0; i < totalChannels; i++)
202 m_channelInfos[i].channel = m_bufferInfos[i].channelNum;
203 m_channelInfos[i].isInput = m_bufferInfos[i].isInput;
204 if(ASIOGetChannelInfo(&m_channelInfos[i]) != ASE_OK)
207 throw std::runtime_error(
"ASIO error: ASIOGetChannelInfo failed");
212 m_postOutput = (ASIOOutputReady() == ASE_OK);
215 m_floatInputs.resize(inputs);
216 m_floatOutputs.resize(outputs);
217 m_inputPtrs.resize(inputs);
218 m_outputPtrs.resize(outputs);
219 for(
int i = 0; i < inputs; i++)
221 m_floatInputs[i].resize(m_bufferSize);
222 m_inputPtrs[i] = m_floatInputs[i].data();
224 for(
int i = 0; i < outputs; i++)
226 m_floatOutputs[i].resize(m_bufferSize);
227 m_outputPtrs[i] = m_floatOutputs[i].data();
231 this->effective_sample_rate = rate;
232 this->effective_buffer_size = m_bufferSize;
233 this->effective_inputs = inputs;
234 this->effective_outputs = outputs;
237 if(ASIOStart() != ASE_OK)
240 throw std::runtime_error(
"ASIO error: ASIOStart failed");
245 bool running()
const override {
return m_started && !stop_processing; }
249 audio_engine::stop();
256 stop_received =
true;
259 ~asio_engine()
override { stop(); }
261 static std::vector<asio_card> enumerate_drivers()
263 std::vector<asio_card> cards;
266 const long numDrivers = drivers.asioGetNumDev();
267 ossia::logger().info(fmt::format(
"ASIO: {} driver(s) installed", numDrivers));
269 for(
long i = 0; i < numDrivers; i++)
271 char name[MAXDRVNAMELEN]{};
272 const long rc = drivers.asioGetDriverName(i, name,
sizeof(name));
275 ossia::logger().info(fmt::format(
"ASIO: [{}] '{}'", i, name));
276 cards.push_back({name, (int)i});
282 fmt::format(
"ASIO: asioGetDriverName({}) failed, rc={}, driver skipped", i, rc));
290 static std::string active_driver()
292 if(
auto* e = detail::current_asio_engine)
293 return e->m_driver_name;
297 enum class control_panel_result
308 static control_panel_result open_control_panel(
const std::string& driver_name)
310 if(
auto* engine = detail::current_asio_engine)
312 if(engine->m_driver_name != driver_name)
315 "ASIO: cannot open the control panel of '{}': '{}' is currently streaming",
316 driver_name, engine->m_driver_name));
317 return control_panel_result::other_driver_active;
321 return control_panel_result::ok;
325 if(!loadAsioDriver(
const_cast<char*
>(driver_name.c_str())))
328 fmt::format(
"ASIO: could not load '{}' to show its control panel", driver_name));
329 return control_panel_result::load_failed;
332 ASIODriverInfo info{};
333 info.asioVersion = 2;
334 if(ASIOInit(&info) != ASE_OK)
337 asioDrivers->removeCurrentDriver();
339 "ASIO: ASIOInit failed for '{}', cannot show its control panel ({})",
340 driver_name, info.errorMessage[0] ? info.errorMessage :
"no message"));
341 return control_panel_result::init_failed;
349 asioDrivers->removeCurrentDriver();
351 return control_panel_result::ok;
358 if(detail::current_asio_engine !=
this)
369 ASIODisposeBuffers();
370 m_buffersCreated =
false;
376 m_initialized =
false;
380 asioDrivers->removeCurrentDriver();
383 detail::current_asio_engine =
nullptr;
387 static void convertToFloat(
388 void* src,
float* dst,
long frames, ASIOSampleType type)
392 case ASIOSTFloat32LSB:
394 std::memcpy(dst, src, frames *
sizeof(
float));
397 case ASIOSTFloat64LSB:
399 auto* s =
static_cast<double*
>(src);
400 for(
long i = 0; i < frames; i++)
401 dst[i] = (
float)s[i];
406 auto* s =
static_cast<int32_t*
>(src);
407 constexpr float scale = 1.0f / 2147483648.0f;
408 for(
long i = 0; i < frames; i++)
409 dst[i] = s[i] * scale;
414 auto* s =
static_cast<uint8_t*
>(src);
415 constexpr float scale = 1.0f / 8388608.0f;
416 for(
long i = 0; i < frames; i++)
418 int32_t val = (int32_t(s[i * 3 + 2]) << 24) | (int32_t(s[i * 3 + 1]) << 16)
419 | (int32_t(s[i * 3]) << 8);
420 dst[i] = (val >> 8) * scale;
426 auto* s =
static_cast<int16_t*
>(src);
427 constexpr float scale = 1.0f / 32768.0f;
428 for(
long i = 0; i < frames; i++)
429 dst[i] = s[i] * scale;
432 case ASIOSTInt32LSB16:
434 auto* s =
static_cast<int32_t*
>(src);
435 constexpr float scale = 1.0f / 32768.0f;
436 for(
long i = 0; i < frames; i++)
437 dst[i] = (s[i] & 0xFFFF) * scale;
440 case ASIOSTInt32LSB18:
442 auto* s =
static_cast<int32_t*
>(src);
443 constexpr float scale = 1.0f / 131072.0f;
444 for(
long i = 0; i < frames; i++)
445 dst[i] = (s[i] & 0x3FFFF) * scale;
448 case ASIOSTInt32LSB20:
450 auto* s =
static_cast<int32_t*
>(src);
451 constexpr float scale = 1.0f / 524288.0f;
452 for(
long i = 0; i < frames; i++)
453 dst[i] = (s[i] & 0xFFFFF) * scale;
456 case ASIOSTInt32LSB24:
458 auto* s =
static_cast<int32_t*
>(src);
459 constexpr float scale = 1.0f / 8388608.0f;
460 for(
long i = 0; i < frames; i++)
461 dst[i] = (s[i] & 0xFFFFFF) * scale;
465 case ASIOSTFloat32MSB:
467 auto* s =
static_cast<uint8_t*
>(src);
468 for(
long i = 0; i < frames; i++)
470 uint32_t val = (uint32_t(s[i * 4]) << 24) | (uint32_t(s[i * 4 + 1]) << 16)
471 | (uint32_t(s[i * 4 + 2]) << 8) | uint32_t(s[i * 4 + 3]);
473 std::memcpy(&f, &val, 4);
480 auto* s =
static_cast<uint8_t*
>(src);
481 constexpr float scale = 1.0f / 2147483648.0f;
482 for(
long i = 0; i < frames; i++)
484 int32_t val = (int32_t(s[i * 4]) << 24) | (int32_t(s[i * 4 + 1]) << 16)
485 | (int32_t(s[i * 4 + 2]) << 8) | int32_t(s[i * 4 + 3]);
486 dst[i] = val * scale;
492 auto* s =
static_cast<uint8_t*
>(src);
493 constexpr float scale = 1.0f / 32768.0f;
494 for(
long i = 0; i < frames; i++)
496 int16_t val = (int16_t(s[i * 2]) << 8) | int16_t(s[i * 2 + 1]);
497 dst[i] = val * scale;
502 std::memset(dst, 0, frames *
sizeof(
float));
508 static void convertFromFloat(
509 const float* src,
void* dst,
long frames, ASIOSampleType type)
513 case ASIOSTFloat32LSB:
515 std::memcpy(dst, src, frames *
sizeof(
float));
518 case ASIOSTFloat64LSB:
520 auto* d =
static_cast<double*
>(dst);
521 for(
long i = 0; i < frames; i++)
522 d[i] = (
double)src[i];
527 auto* d =
static_cast<int32_t*
>(dst);
528 constexpr double scale = 2147483647.0;
529 for(
long i = 0; i < frames; i++)
531 double val = std::max(-1.0, std::min(1.0, (
double)src[i]));
532 d[i] = (int32_t)(val * scale);
538 auto* d =
static_cast<uint8_t*
>(dst);
539 constexpr double scale = 8388607.0;
540 for(
long i = 0; i < frames; i++)
542 double val = std::max(-1.0, std::min(1.0, (
double)src[i]));
543 int32_t s = (int32_t)(val * scale);
544 d[i * 3] = (uint8_t)(s & 0xFF);
545 d[i * 3 + 1] = (uint8_t)((s >> 8) & 0xFF);
546 d[i * 3 + 2] = (uint8_t)((s >> 16) & 0xFF);
552 auto* d =
static_cast<int16_t*
>(dst);
553 constexpr double scale = 32767.0;
554 for(
long i = 0; i < frames; i++)
556 double val = std::max(-1.0, std::min(1.0, (
double)src[i]));
557 d[i] = (int16_t)(val * scale);
561 case ASIOSTInt32LSB16:
563 auto* d =
static_cast<int32_t*
>(dst);
564 constexpr double scale = 32767.0;
565 for(
long i = 0; i < frames; i++)
567 double val = std::max(-1.0, std::min(1.0, (
double)src[i]));
568 d[i] = (int32_t)(val * scale);
572 case ASIOSTInt32LSB18:
574 auto* d =
static_cast<int32_t*
>(dst);
575 constexpr double scale = 131071.0;
576 for(
long i = 0; i < frames; i++)
578 double val = std::max(-1.0, std::min(1.0, (
double)src[i]));
579 d[i] = (int32_t)(val * scale);
583 case ASIOSTInt32LSB20:
585 auto* d =
static_cast<int32_t*
>(dst);
586 constexpr double scale = 524287.0;
587 for(
long i = 0; i < frames; i++)
589 double val = std::max(-1.0, std::min(1.0, (
double)src[i]));
590 d[i] = (int32_t)(val * scale);
594 case ASIOSTInt32LSB24:
596 auto* d =
static_cast<int32_t*
>(dst);
597 constexpr double scale = 8388607.0;
598 for(
long i = 0; i < frames; i++)
600 double val = std::max(-1.0, std::min(1.0, (
double)src[i]));
601 d[i] = (int32_t)(val * scale);
605 case ASIOSTFloat32MSB:
607 auto* d =
static_cast<uint8_t*
>(dst);
608 for(
long i = 0; i < frames; i++)
611 std::memcpy(&val, &src[i], 4);
612 d[i * 4] = (uint8_t)(val >> 24);
613 d[i * 4 + 1] = (uint8_t)(val >> 16);
614 d[i * 4 + 2] = (uint8_t)(val >> 8);
615 d[i * 4 + 3] = (uint8_t)(val);
621 auto* d =
static_cast<uint8_t*
>(dst);
622 constexpr double scale = 2147483647.0;
623 for(
long i = 0; i < frames; i++)
625 double val = std::max(-1.0, std::min(1.0, (
double)src[i]));
626 int32_t s = (int32_t)(val * scale);
627 d[i * 4] = (uint8_t)(s >> 24);
628 d[i * 4 + 1] = (uint8_t)(s >> 16);
629 d[i * 4 + 2] = (uint8_t)(s >> 8);
630 d[i * 4 + 3] = (uint8_t)(s);
636 auto* d =
static_cast<uint8_t*
>(dst);
637 constexpr double scale = 32767.0;
638 for(
long i = 0; i < frames; i++)
640 double val = std::max(-1.0, std::min(1.0, (
double)src[i]));
641 int16_t s = (int16_t)(val * scale);
642 d[i * 2] = (uint8_t)(s >> 8);
643 d[i * 2 + 1] = (uint8_t)(s);
654 static ASIOTime* asio_bufferSwitchTimeInfo(
655 ASIOTime* params,
long doubleBufferIndex, ASIOBool directProcess)
657 auto* self = detail::current_asio_engine;
661 [[maybe_unused]]
static const thread_local auto _ = [] {
662 ossia::set_thread_name(
"ossia audio 0");
663 ossia::set_thread_pinned(thread_type::Audio, 0);
669 if(self->stop_processing)
673 for(
int i = 0; i < self->m_outputCount; i++)
675 auto& info = self->m_bufferInfos[self->m_inputCount + i];
676 auto& chanInfo = self->m_channelInfos[self->m_inputCount + i];
677 void* buf = info.buffers[doubleBufferIndex];
680 std::memset(buf, 0, self->sampleSize(chanInfo.type) * self->m_bufferSize);
683 if(self->m_postOutput)
688 const long frames = self->m_bufferSize;
691 for(
int i = 0; i < self->m_inputCount; i++)
693 void* src = self->m_bufferInfos[i].buffers[doubleBufferIndex];
694 convertToFloat(src, self->m_inputPtrs[i], frames, self->m_channelInfos[i].type);
698 for(
int i = 0; i < self->m_outputCount; i++)
700 std::memset(self->m_outputPtrs[i], 0, frames *
sizeof(
float));
707 double seconds = 0.0;
708 if(params && (params->timeInfo.flags & kSystemTimeValid))
710 seconds = asioSamplesToDouble(params->timeInfo.systemTime) * 1e-9;
714 ossia::audio_tick_state ts{
715 const_cast<float* const*
>(self->m_inputPtrs.data()),
716 self->m_outputPtrs.data(),
722 self->audio_tick(ts);
726 for(
int i = 0; i < self->m_outputCount; i++)
728 int chIdx = self->m_inputCount + i;
729 void* dst = self->m_bufferInfos[chIdx].buffers[doubleBufferIndex];
731 self->m_outputPtrs[i], dst, frames, self->m_channelInfos[chIdx].type);
734 if(self->m_postOutput)
740 static void asio_bufferSwitch(
long doubleBufferIndex, ASIOBool directProcess)
744 std::memset(&timeInfo, 0,
sizeof(ASIOTime));
747 ASIOTimeStamp tStamp;
748 if(ASIOGetSamplePosition(&sPos, &tStamp) == ASE_OK)
750 timeInfo.timeInfo.samplePosition = sPos;
751 timeInfo.timeInfo.systemTime = tStamp;
752 timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
755 ASIOSampleRate sRate = 0;
756 if(ASIOGetSampleRate(&sRate) == ASE_OK)
758 timeInfo.timeInfo.sampleRate = sRate;
759 timeInfo.timeInfo.flags |= kSampleRateValid;
762 asio_bufferSwitchTimeInfo(&timeInfo, doubleBufferIndex, directProcess);
765 static void asio_sampleRateDidChange(ASIOSampleRate sRate)
767 auto* self = detail::current_asio_engine;
768 if(self && sRate > 0)
770 self->effective_sample_rate = (int)sRate;
774 static long asio_message(
long selector,
long value,
void* message,
double* opt)
778 case kAsioSelectorSupported:
781 case kAsioEngineVersion:
782 case kAsioSupportsTimeInfo:
783 case kAsioResetRequest:
784 case kAsioResyncRequest:
785 case kAsioLatenciesChanged:
792 case kAsioEngineVersion:
795 case kAsioSupportsTimeInfo:
798 case kAsioResetRequest:
802 case kAsioResyncRequest:
805 case kAsioLatenciesChanged:
816 static double asioSamplesToDouble(
const ASIOSamples& s)
821 return s.hi * 4294967296.0 + s.lo;
825 static double asioSamplesToDouble(
const ASIOTimeStamp& s)
830 return s.hi * 4294967296.0 + s.lo;
834 static long sampleSize(ASIOSampleType type)
846 case ASIOSTFloat32MSB:
847 case ASIOSTFloat32LSB:
848 case ASIOSTInt32MSB16:
849 case ASIOSTInt32MSB18:
850 case ASIOSTInt32MSB20:
851 case ASIOSTInt32MSB24:
852 case ASIOSTInt32LSB16:
853 case ASIOSTInt32LSB18:
854 case ASIOSTInt32LSB20:
855 case ASIOSTInt32LSB24:
857 case ASIOSTFloat64MSB:
858 case ASIOSTFloat64LSB:
865 std::vector<ASIOBufferInfo> m_bufferInfos;
866 std::vector<ASIOChannelInfo> m_channelInfos;
867 ASIOCallbacks m_asioCallbacks{};
870 std::vector<ossia::float_vector> m_floatInputs;
871 std::vector<ossia::float_vector> m_floatOutputs;
872 ossia::pod_vector<float*> m_inputPtrs;
873 ossia::pod_vector<float*> m_outputPtrs;
875 std::string m_driver_name;
881 bool m_initialized{};
882 bool m_buffersCreated{};
spdlog::logger & logger() noexcept
Where the errors will be logged. Default is stderr.
Definition context.cpp:120