MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
CoreAudioBackend.cpp
Go to the documentation of this file.
2
4
5#ifdef COREAUDIO_BACKEND
6
7namespace {
8
11
12bool property_exists(
13 AudioObjectID object,
14 const AudioObjectPropertyAddress& address)
15{
16 return AudioObjectHasProperty(object, &address);
17}
18
19} // namespace
20
21namespace MayaFlux::Core {
22
23// ============================================================================
24// CoreAudioBackend
25// ============================================================================
26
27CoreAudioBackend::CoreAudioBackend()
28{
29 MF_INFO(C, X, "CoreAudio backend initialised");
30}
31
32CoreAudioBackend::~CoreAudioBackend()
33{
34 cleanup();
35}
36
37std::unique_ptr<AudioDevice> CoreAudioBackend::create_device_manager()
38{
39 return std::make_unique<CoreAudioDevice>();
40}
41
42std::unique_ptr<AudioStream> CoreAudioBackend::create_stream(
43 unsigned int output_device_id,
44 unsigned int input_device_id,
45 GlobalStreamInfo& stream_info,
46 void* user_data)
47{
48 CoreAudioDevice devices;
49
50 return std::make_unique<CoreAudioStream>(
51 devices.resolve_output_device(output_device_id),
52 devices.resolve_input_device(input_device_id),
53 stream_info,
54 user_data);
55}
56
57std::string CoreAudioBackend::get_version_string() const
58{
59 return "CoreAudio";
60}
61
62void CoreAudioBackend::cleanup()
63{
64}
65
66// ============================================================================
67// CoreAudioDevice
68// ============================================================================
69
70CoreAudioDevice::CoreAudioDevice()
71{
72 enumerate_devices();
73}
74
75std::vector<DeviceInfo> CoreAudioDevice::get_output_devices() const
76{
77 std::vector<DeviceInfo> result;
78 result.reserve(m_outputs.size());
79
80 for (const auto& device : m_outputs)
81 result.push_back(device.info);
82
83 return result;
84}
85
86std::vector<DeviceInfo> CoreAudioDevice::get_input_devices() const
87{
88 std::vector<DeviceInfo> result;
89 result.reserve(m_inputs.size());
90
91 for (const auto& device : m_inputs)
92 result.push_back(device.info);
93
94 return result;
95}
96
97unsigned int CoreAudioDevice::get_default_output_device() const
98{
99 return m_default_output;
100}
101
102unsigned int CoreAudioDevice::get_default_input_device() const
103{
104 return m_default_input;
105}
106
107AudioDeviceID CoreAudioDevice::resolve_output_device(
108 unsigned int index) const
109{
110 if (index >= m_outputs.size())
111 return m_default_output_id;
112
113 return m_outputs[index].id;
114}
115
116AudioDeviceID CoreAudioDevice::resolve_input_device(
117 unsigned int index) const
118{
119 if (index >= m_inputs.size())
120 return m_default_input_id;
121
122 return m_inputs[index].id;
123}
124
125std::string CoreAudioDevice::get_device_name(AudioDeviceID device_id)
126{
127 AudioObjectPropertyAddress address {
128 .mSelector = kAudioObjectPropertyName,
129 .mScope = kAudioObjectPropertyScopeGlobal,
130 .mElement = kAudioObjectPropertyElementMain
131 };
132
133 CFStringRef name_ref = nullptr;
134 UInt32 size = sizeof(CFStringRef);
135
136 auto status = AudioObjectGetPropertyData(
137 device_id,
138 &address,
139 0,
140 nullptr,
141 &size,
142 static_cast<void*>(&name_ref));
143
144 if (status != noErr || !name_ref)
145 return "Unknown Device";
146
147 char buffer[512] {};
148
149 CFStringGetCString(
150 name_ref,
151 buffer,
152 sizeof(buffer),
153 kCFStringEncodingUTF8);
154
155 CFRelease(name_ref);
156
157 return buffer;
158}
159
160uint32_t CoreAudioDevice::get_channel_count(
161 AudioDeviceID device_id,
162 AudioObjectPropertyScope scope)
163{
164 AudioObjectPropertyAddress address {
165 .mSelector = kAudioDevicePropertyStreamConfiguration,
166 .mScope = scope,
167 .mElement = kAudioObjectPropertyElementMain
168 };
169
170 UInt32 size = 0;
171
172 auto status = AudioObjectGetPropertyDataSize(
173 device_id,
174 &address,
175 0,
176 nullptr,
177 &size);
178
179 if (status != noErr || size == 0)
180 return 0;
181
182 auto buffer = std::make_unique<uint8_t[]>(size);
183
184 auto* config = reinterpret_cast<AudioBufferList*>(buffer.get());
185
186 status = AudioObjectGetPropertyData(
187 device_id,
188 &address,
189 0,
190 nullptr,
191 &size,
192 config);
193
194 if (status != noErr)
195 return 0;
196
197 uint32_t channels = 0;
198
199 for (UInt32 i = 0; i < config->mNumberBuffers; ++i)
200 channels += config->mBuffers[i].mNumberChannels;
201
202 return channels;
203}
204
205double CoreAudioDevice::get_nominal_sample_rate(
206 AudioDeviceID device_id)
207{
208 AudioObjectPropertyAddress address {
209 .mSelector = kAudioDevicePropertyNominalSampleRate,
210 .mScope = kAudioObjectPropertyScopeGlobal,
211 .mElement = kAudioObjectPropertyElementMain
212 };
213
214 Float64 rate = 48000.0;
215 UInt32 size = sizeof(rate);
216
217 auto status = AudioObjectGetPropertyData(
218 device_id,
219 &address,
220 0,
221 nullptr,
222 &size,
223 &rate);
224
225 if (status != noErr)
226 return 48000.0;
227
228 return static_cast<double>(rate);
229}
230
231void CoreAudioDevice::enumerate_devices()
232{
233 AudioObjectPropertyAddress devices_address {
234 .mSelector = kAudioHardwarePropertyDevices,
235 .mScope = kAudioObjectPropertyScopeGlobal,
236 .mElement = kAudioObjectPropertyElementMain
237 };
238
239 UInt32 size = 0;
240
241 auto status = AudioObjectGetPropertyDataSize(
242 kAudioObjectSystemObject,
243 &devices_address,
244 0,
245 nullptr,
246 &size);
247
248 if (status != noErr) {
249 error<std::runtime_error>(
250 C,
251 X,
252 std::source_location::current(),
253 "Failed to query CoreAudio device list");
254 }
255
256 std::vector<AudioDeviceID> device_ids(
257 size / sizeof(AudioDeviceID));
258
259 status = AudioObjectGetPropertyData(
260 kAudioObjectSystemObject,
261 &devices_address,
262 0,
263 nullptr,
264 &size,
265 device_ids.data());
266
267 if (status != noErr) {
268 error<std::runtime_error>(
269 C,
270 X,
271 std::source_location::current(),
272 "Failed to retrieve CoreAudio device list");
273 }
274
275 {
276 AudioObjectPropertyAddress address {
277 .mSelector = kAudioHardwarePropertyDefaultOutputDevice,
278 .mScope = kAudioObjectPropertyScopeGlobal,
279 .mElement = kAudioObjectPropertyElementMain
280 };
281
282 UInt32 property_size = sizeof(AudioDeviceID);
283
284 AudioObjectGetPropertyData(
285 kAudioObjectSystemObject,
286 &address,
287 0,
288 nullptr,
289 &property_size,
290 &m_default_output_id);
291 }
292
293 {
294 AudioObjectPropertyAddress address {
295 .mSelector = kAudioHardwarePropertyDefaultInputDevice,
296 .mScope = kAudioObjectPropertyScopeGlobal,
297 .mElement = kAudioObjectPropertyElementMain
298 };
299
300 UInt32 property_size = sizeof(AudioDeviceID);
301
302 AudioObjectGetPropertyData(
303 kAudioObjectSystemObject,
304 &address,
305 0,
306 nullptr,
307 &property_size,
308 &m_default_input_id);
309 }
310
311 for (auto device_id : device_ids) {
312
313 const auto name = get_device_name(device_id);
314
315 const auto output_channels = get_channel_count(
316 device_id,
317 kAudioDevicePropertyScopeOutput);
318
319 const auto input_channels = get_channel_count(
320 device_id,
321 kAudioDevicePropertyScopeInput);
322
323 const auto sample_rate = get_nominal_sample_rate(device_id);
324
325 if (output_channels > 0) {
326
327 DeviceInfo info;
328 info.name = name;
329 info.output_channels = output_channels;
330 info.input_channels = 0;
331 info.preferred_sample_rate = static_cast<uint32_t>(sample_rate);
332
333 info.is_default_output = (device_id == m_default_output_id);
334
335 if (info.is_default_output)
336 m_default_output = static_cast<unsigned int>(m_outputs.size());
337
338 m_outputs.push_back({ device_id,
339 std::move(info) });
340 }
341
342 if (input_channels > 0) {
343
344 DeviceInfo info;
345 info.name = name;
346 info.output_channels = 0;
347 info.input_channels = input_channels;
348 info.preferred_sample_rate = static_cast<uint32_t>(sample_rate);
349
350 info.is_default_input = (device_id == m_default_input_id);
351
352 if (info.is_default_input)
353 m_default_input = static_cast<unsigned int>(m_inputs.size());
354
355 m_inputs.push_back({ device_id,
356 std::move(info) });
357 }
358 }
359
360 MF_INFO(
361 C,
362 X,
363 "CoreAudio enumeration: {} output device(s), {} input device(s)",
364 m_outputs.size(),
365 m_inputs.size());
366}
367
368// ============================================================================
369// CoreAudioStream
370// ============================================================================
371
372CoreAudioStream::CoreAudioStream(
373 AudioDeviceID output_device_id,
374 AudioDeviceID input_device_id,
375 GlobalStreamInfo& stream_info,
376 void* user_data)
377 : m_output_device_id(output_device_id)
378 , m_input_device_id(input_device_id)
379 , m_stream_info(stream_info)
380 , m_user_data(user_data)
381{
382}
383
384CoreAudioStream::~CoreAudioStream()
385{
386 if (is_running())
387 stop();
388
389 if (is_open())
390 close();
391}
392
393void CoreAudioStream::open()
394{
395 if (m_is_open.load())
396 return;
397
398 if (!configure_output_unit()) {
399 error<std::runtime_error>(C, X, std::source_location::current(),
400 "Failed to configure CoreAudio output unit");
401 }
402
403 if (!configure_output_device()) {
404 error<std::runtime_error>(C, X, std::source_location::current(),
405 "Failed to configure CoreAudio output device");
406 }
407
408 if (!configure_output_format()) {
409 error<std::runtime_error>(C, X, std::source_location::current(),
410 "Failed to configure CoreAudio output format");
411 }
412
413 auto max_frames = static_cast<UInt32>(m_stream_info.buffer_size);
414
415 AudioUnitSetProperty(
416 m_output_unit,
417 kAudioUnitProperty_MaximumFramesPerSlice,
418 kAudioUnitScope_Global,
419 0,
420 &max_frames,
421 sizeof(max_frames));
422
423 auto status = AudioUnitInitialize(m_output_unit);
424
425 if (status != noErr) {
426 error<std::runtime_error>(C, X, std::source_location::current(),
427 "AudioUnitInitialize failed for output ({})", static_cast<int>(status));
428 }
429
430 m_input_enabled.store(
431 m_stream_info.input.enabled && m_stream_info.input.channels > 0,
432 std::memory_order_release);
433
434 if (m_input_enabled.load(std::memory_order_acquire)) {
435
436 if (!configure_input_unit()) {
437 error<std::runtime_error>(C, X, std::source_location::current(),
438 "Failed to configure CoreAudio input unit");
439 }
440
441 if (!configure_input_device()) {
442 error<std::runtime_error>(C, X, std::source_location::current(),
443 "Failed to configure CoreAudio input device");
444 }
445
446 if (!configure_input_format()) {
447 error<std::runtime_error>(C, X, std::source_location::current(),
448 "Failed to configure CoreAudio input format");
449 }
450
451 AudioUnitSetProperty(
452 m_input_unit,
453 kAudioUnitProperty_MaximumFramesPerSlice,
454 kAudioUnitScope_Global,
455 0,
456 &max_frames,
457 sizeof(max_frames));
458
459 status = AudioUnitInitialize(m_input_unit);
460
461 if (status != noErr) {
462 error<std::runtime_error>(C, X, std::source_location::current(),
463 "AudioUnitInitialize failed for input ({})", static_cast<int>(status));
464 }
465 }
466
467 m_is_open.store(true);
468}
469
470void CoreAudioStream::start()
471{
472 if (!m_is_open.load()) {
473 error<std::runtime_error>(C, X, std::source_location::current(),
474 "Cannot start stream before open()");
475 }
476
477 if (m_is_running.load())
478 return;
479
480 if (m_input_enabled.load(std::memory_order_acquire)) {
481 auto status = AudioOutputUnitStart(m_input_unit);
482 if (status != noErr) {
483 error<std::runtime_error>(C, X, std::source_location::current(),
484 "AudioOutputUnitStart failed for input ({})", static_cast<int>(status));
485 }
486 }
487
488 auto status = AudioOutputUnitStart(m_output_unit);
489
490 if (status != noErr) {
491 error<std::runtime_error>(C, X, std::source_location::current(),
492 "AudioOutputUnitStart failed for output ({})", static_cast<int>(status));
493 }
494
495 m_is_running.store(true);
496}
497
498void CoreAudioStream::stop()
499{
500 if (!m_is_running.load())
501 return;
502
503 AudioOutputUnitStop(m_output_unit);
504
505 if (m_input_enabled.load(std::memory_order_acquire))
506 AudioOutputUnitStop(m_input_unit);
507
508 m_is_running.store(false);
509}
510
511void CoreAudioStream::pause()
512{
513 stop();
514 m_is_paused.store(true, std::memory_order_release);
515}
516
517void CoreAudioStream::resume()
518{
519 if (!m_is_paused.load(std::memory_order_acquire))
520 return;
521
522 start();
523 m_is_paused.store(false, std::memory_order_release);
524}
525
526void CoreAudioStream::close()
527{
528 if (!m_is_open.load())
529 return;
530
531 if (m_is_running.load())
532 stop();
533
534 if (m_input_buffer_list) {
535 std::free(m_input_buffer_list->mBuffers[0].mData);
536 std::free(m_input_buffer_list);
537 m_input_buffer_list = nullptr;
538 }
539
540 if (m_input_unit) {
541 AudioUnitUninitialize(m_input_unit);
542 AudioComponentInstanceDispose(m_input_unit);
543 m_input_unit = nullptr;
544 }
545
546 AudioUnitUninitialize(m_output_unit);
547 AudioComponentInstanceDispose(m_output_unit);
548 m_output_unit = nullptr;
549
550 m_is_open.store(false);
551}
552
553bool CoreAudioStream::is_running() const
554{
555 return m_is_running.load(std::memory_order_acquire);
556}
557
558bool CoreAudioStream::is_open() const
559{
560 return m_is_open.load(std::memory_order_acquire);
561}
562
563void CoreAudioStream::set_process_callback(
564 std::function<int(void*, void*, unsigned int)> callback)
565{
566 m_process_callback = std::move(callback);
567}
568
569bool CoreAudioStream::configure_output_unit()
570{
571 AudioComponentDescription desc {};
572 desc.componentType = kAudioUnitType_Output;
573 desc.componentSubType = kAudioUnitSubType_HALOutput;
574 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
575
576 AudioComponent component = AudioComponentFindNext(nullptr, &desc);
577
578 if (!component) {
579 MF_ERROR(C, X, "Failed to find HALOutput AudioUnit");
580 return false;
581 }
582
583 auto status = AudioComponentInstanceNew(component, &m_output_unit);
584
585 if (status != noErr || !m_output_unit) {
586 MF_ERROR(C, X, "AudioComponentInstanceNew failed for output ({})",
587 static_cast<int>(status));
588 return false;
589 }
590
591 UInt32 enable = 1;
592
593 status = AudioUnitSetProperty(
594 m_output_unit,
595 kAudioOutputUnitProperty_EnableIO,
596 kAudioUnitScope_Output,
597 0,
598 &enable,
599 sizeof(enable));
600
601 if (status != noErr) {
602 MF_ERROR(C, X, "Failed to enable output bus ({})", static_cast<int>(status));
603 return false;
604 }
605
606 UInt32 disable = 0;
607
608 status = AudioUnitSetProperty(
609 m_output_unit,
610 kAudioOutputUnitProperty_EnableIO,
611 kAudioUnitScope_Input,
612 1,
613 &disable,
614 sizeof(disable));
615
616 if (status != noErr) {
617 MF_ERROR(C, X, "Failed to disable input bus on output unit ({})",
618 static_cast<int>(status));
619 return false;
620 }
621
622 AURenderCallbackStruct callback {};
623 callback.inputProc = &CoreAudioStream::output_callback;
624 callback.inputProcRefCon = this;
625
626 status = AudioUnitSetProperty(
627 m_output_unit,
628 kAudioUnitProperty_SetRenderCallback,
629 kAudioUnitScope_Input,
630 0,
631 &callback,
632 sizeof(callback));
633
634 if (status != noErr) {
635 MF_ERROR(C, X, "Failed to register render callback ({})", static_cast<int>(status));
636 return false;
637 }
638
639 return true;
640}
641
642bool CoreAudioStream::configure_input_unit()
643{
644 AudioComponentDescription desc {};
645 desc.componentType = kAudioUnitType_Output;
646 desc.componentSubType = kAudioUnitSubType_HALOutput;
647 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
648
649 AudioComponent component = AudioComponentFindNext(nullptr, &desc);
650
651 if (!component) {
652 MF_ERROR(C, X, "Failed to find HALOutput AudioUnit for input");
653 return false;
654 }
655
656 auto status = AudioComponentInstanceNew(component, &m_input_unit);
657
658 if (status != noErr || !m_input_unit) {
659 MF_ERROR(C, X, "AudioComponentInstanceNew failed for input ({})",
660 static_cast<int>(status));
661 return false;
662 }
663
664 UInt32 disable = 0;
665
666 status = AudioUnitSetProperty(
667 m_input_unit,
668 kAudioOutputUnitProperty_EnableIO,
669 kAudioUnitScope_Output,
670 0,
671 &disable,
672 sizeof(disable));
673
674 if (status != noErr) {
675 MF_ERROR(C, X, "Failed to disable output bus on input unit ({})",
676 static_cast<int>(status));
677 return false;
678 }
679
680 UInt32 enable = 1;
681
682 status = AudioUnitSetProperty(
683 m_input_unit,
684 kAudioOutputUnitProperty_EnableIO,
685 kAudioUnitScope_Input,
686 1,
687 &enable,
688 sizeof(enable));
689
690 if (status != noErr) {
691 MF_ERROR(C, X, "Failed to enable input bus ({})", static_cast<int>(status));
692 return false;
693 }
694
695 AURenderCallbackStruct callback {};
696 callback.inputProc = &CoreAudioStream::input_callback;
697 callback.inputProcRefCon = this;
698
699 status = AudioUnitSetProperty(
700 m_input_unit,
701 kAudioOutputUnitProperty_SetInputCallback,
702 kAudioUnitScope_Global,
703 0,
704 &callback,
705 sizeof(callback));
706
707 if (status != noErr) {
708 MF_ERROR(C, X, "Failed to register input callback ({})", static_cast<int>(status));
709 return false;
710 }
711
712 return true;
713}
714
715bool CoreAudioStream::configure_output_device()
716{
717 auto status = AudioUnitSetProperty(
718 m_output_unit,
719 kAudioOutputUnitProperty_CurrentDevice,
720 kAudioUnitScope_Global,
721 0,
722 &m_output_device_id,
723 sizeof(m_output_device_id));
724
725 if (status != noErr) {
726 MF_ERROR(C, X, "Failed to set output device {} ({})",
727 static_cast<unsigned>(m_output_device_id), static_cast<int>(status));
728 return false;
729 }
730
731 AudioObjectPropertyAddress buffer_addr {
732 .mSelector = kAudioDevicePropertyBufferFrameSize,
733 .mScope = kAudioObjectPropertyScopeGlobal,
734 .mElement = kAudioObjectPropertyElementMain
735 };
736
737 auto requested = static_cast<UInt32>(m_stream_info.buffer_size);
738
739 AudioObjectSetPropertyData(
740 m_output_device_id,
741 &buffer_addr,
742 0,
743 nullptr,
744 sizeof(requested),
745 &requested);
746
747 UInt32 actual = 0;
748 UInt32 actual_size = sizeof(actual);
749
750 if (AudioObjectGetPropertyData(m_output_device_id, &buffer_addr,
751 0, nullptr, &actual_size, &actual)
752 == noErr) {
753 MF_INFO(C, X, "Output buffer size: requested={} actual={}", requested, actual);
754 }
755
756 return true;
757}
758
759bool CoreAudioStream::configure_input_device()
760{
761 auto status = AudioUnitSetProperty(
762 m_input_unit,
763 kAudioOutputUnitProperty_CurrentDevice,
764 kAudioUnitScope_Global,
765 0,
766 &m_input_device_id,
767 sizeof(m_input_device_id));
768
769 if (status != noErr) {
770 MF_ERROR(C, X, "Failed to set input device {} ({})",
771 static_cast<unsigned>(m_input_device_id), static_cast<int>(status));
772 return false;
773 }
774
775 AudioObjectPropertyAddress buffer_addr {
776 .mSelector = kAudioDevicePropertyBufferFrameSize,
777 .mScope = kAudioObjectPropertyScopeGlobal,
778 .mElement = kAudioObjectPropertyElementMain
779 };
780
781 auto requested = static_cast<UInt32>(m_stream_info.buffer_size);
782
783 AudioObjectSetPropertyData(
784 m_input_device_id,
785 &buffer_addr,
786 0,
787 nullptr,
788 sizeof(requested),
789 &requested);
790
791 UInt32 actual = 0;
792 UInt32 actual_size = sizeof(actual);
793
794 if (AudioObjectGetPropertyData(m_input_device_id, &buffer_addr,
795 0, nullptr, &actual_size, &actual)
796 == noErr) {
797 MF_INFO(C, X, "Input buffer size: requested={} actual={}", requested, actual);
798 }
799
800 return true;
801}
802
803bool CoreAudioStream::configure_output_format()
804{
805 AudioStreamBasicDescription format {};
806 format.mSampleRate = m_stream_info.sample_rate;
807 format.mFormatID = kAudioFormatLinearPCM;
808 format.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
809 format.mBitsPerChannel = 64;
810 format.mChannelsPerFrame = m_stream_info.output.channels;
811 format.mFramesPerPacket = 1;
812 format.mBytesPerFrame = sizeof(double) * format.mChannelsPerFrame;
813 format.mBytesPerPacket = format.mBytesPerFrame;
814
815 auto status = AudioUnitSetProperty(
816 m_output_unit,
817 kAudioUnitProperty_StreamFormat,
818 kAudioUnitScope_Input,
819 0,
820 &format,
821 sizeof(format));
822
823 if (status != noErr) {
824 MF_ERROR(C, X, "Failed to set output stream format ({})", static_cast<int>(status));
825 return false;
826 }
827
828 MF_LOG(C, X, "Output format: {} channels {} Hz {} bits",
829 format.mChannelsPerFrame, format.mSampleRate, format.mBitsPerChannel);
830
831 return true;
832}
833
834bool CoreAudioStream::configure_input_format()
835{
836 AudioStreamBasicDescription format {};
837 format.mSampleRate = m_stream_info.sample_rate;
838 format.mFormatID = kAudioFormatLinearPCM;
839 format.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
840 format.mBitsPerChannel = 64;
841 format.mChannelsPerFrame = m_stream_info.input.channels;
842 format.mFramesPerPacket = 1;
843 format.mBytesPerFrame = sizeof(double) * format.mChannelsPerFrame;
844 format.mBytesPerPacket = format.mBytesPerFrame;
845
846 auto status = AudioUnitSetProperty(
847 m_input_unit,
848 kAudioUnitProperty_StreamFormat,
849 kAudioUnitScope_Output,
850 1,
851 &format,
852 sizeof(format));
853
854 if (status != noErr) {
855 MF_ERROR(C, X, "Failed to set input stream format ({})", static_cast<int>(status));
856 return false;
857 }
858
859 const auto bytes = static_cast<UInt32>(
860 sizeof(double) * m_stream_info.input.channels * m_stream_info.buffer_size);
861
862 m_input_buffer_list = static_cast<AudioBufferList*>(
863 std::calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer)));
864
865 m_input_buffer_list->mNumberBuffers = 1;
866 m_input_buffer_list->mBuffers[0].mNumberChannels = m_stream_info.input.channels;
867 m_input_buffer_list->mBuffers[0].mDataByteSize = bytes;
868 m_input_buffer_list->mBuffers[0].mData = std::calloc(1, bytes);
869
870 MF_LOG(C, X, "Input format: {} channels {} Hz {} bits",
871 format.mChannelsPerFrame, format.mSampleRate, format.mBitsPerChannel);
872
873 return true;
874}
875
876OSStatus CoreAudioStream::output_callback(
877 void* ref_con,
878 AudioUnitRenderActionFlags*,
879 const AudioTimeStamp*,
880 UInt32,
881 UInt32 num_frames,
882 AudioBufferList* io_data)
883{
884 auto* stream = static_cast<CoreAudioStream*>(ref_con);
885
886 if (!stream)
887 return noErr;
888
889 if (!stream->m_process_callback) {
890 for (UInt32 b = 0; b < io_data->mNumberBuffers; ++b)
891 std::memset(io_data->mBuffers[b].mData, 0, io_data->mBuffers[b].mDataByteSize);
892 return noErr;
893 }
894
895 auto* output = static_cast<double*>(io_data->mBuffers[0].mData);
896
897 void* input = stream->m_input_enabled.load(std::memory_order_acquire)
898 ? stream->m_input_buffer_list->mBuffers[0].mData
899 : nullptr;
900
901 stream->m_process_callback(output, input, num_frames);
902
903 return noErr;
904}
905
906OSStatus CoreAudioStream::input_callback(
907 void* ref_con,
908 AudioUnitRenderActionFlags* action_flags,
909 const AudioTimeStamp* time_stamp,
910 UInt32 bus_number,
911 UInt32 num_frames,
912 AudioBufferList*)
913{
914 auto* stream = static_cast<CoreAudioStream*>(ref_con);
915
916 if (!stream || !stream->m_input_buffer_list)
917 return noErr;
918
919 stream->m_input_buffer_list->mBuffers[0].mDataByteSize = static_cast<UInt32>(
920 sizeof(double) * stream->m_stream_info.input.channels * num_frames);
921
922 AudioUnitRender(
923 stream->m_input_unit,
924 action_flags,
925 time_stamp,
926 bus_number,
927 num_frames,
928 stream->m_input_buffer_list);
929
930 return noErr;
931}
932
933} // namespace MayaFlux::Core
934
935#endif // COREAUDIO_BACKEND
#define MF_INFO(comp, ctx,...)
#define MF_LOG(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
Core::GlobalStreamInfo stream
Definition Config.cpp:34
Core::GlobalInputConfig input
Definition Config.cpp:36
size_t b
@ AudioBackend
Audio processing backend (Pipewire, wasapi, coreaudio)
std::string format(format_string< std::remove_cvref_t< Args >... > fmt_str, Args &&... args)
Definition Format.hpp:30
@ Core
Core engine, backend, subsystems.
void stop()
Stop all Portal::Graphics operations.
Definition Graphics.cpp:69