17 #pragma clang diagnostic push
18 #pragma clang diagnostic ignored "-Wdocumentation"
19 #include <RtAudio/RtAudio.h>
20 #pragma clang diagnostic pop
23 #include <CoreAudio/CoreAudio.h>
24 #include <objc/objc-runtime.h>
35 "VuoAudioInputDevice",
36 "VuoAudioOutputDevice",
37 "VuoList_VuoAudioSamples",
38 "VuoList_VuoAudioInputDevice",
39 "VuoList_VuoAudioOutputDevice",
64 dispatch_async(dispatch_get_main_queue(), ^{
65 CFRunLoopRef theRunLoop = NULL;
66 AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
67 AudioObjectSetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL,
sizeof(CFRunLoopRef), &theRunLoop);
77 unsigned int deviceCount = rta.getDeviceCount();
79 for (
unsigned int i = 0; i < deviceCount; ++i)
81 RtAudio::DeviceInfo info = rta.getDeviceInfo(i);
82 if (info.inputChannels)
94 unsigned int deviceCount = rta.getDeviceCount();
96 for (
unsigned int i = 0; i < deviceCount; ++i)
98 RtAudio::DeviceInfo info = rta.getDeviceInfo(i);
99 if (info.outputChannels)
102 return outputDevices;
125 AudioObjectPropertyAddress address;
126 address.mSelector = kAudioHardwarePropertyDevices;
127 address.mScope = kAudioObjectPropertyScopeGlobal;
128 address.mElement = kAudioObjectPropertyElementMaster;
133 VUserLog(
"Error: Couldn't register device change listener: %s", description);
149 VUserLog(
"Error: Unbalanced VuoAudio_use() / _disuse() calls.");
155 AudioObjectPropertyAddress address;
156 address.mSelector = kAudioHardwarePropertyDevices;
157 address.mScope = kAudioObjectPropertyScopeGlobal;
158 address.mElement = kAudioObjectPropertyElementMaster;
163 VUserLog(
"Error: Couldn't unregister device change listener: %s", description);
230 int VuoAudio_receivedEvent(
void *outputBuffer,
void *inputBuffer,
unsigned int nBufferFrames,
double streamTime, RtAudioStreamStatus status,
void *userData)
240 status == RTAUDIO_INPUT_OVERFLOW ?
"overflow"
241 : (status == RTAUDIO_OUTPUT_UNDERFLOW ?
"underflow" :
"error"),
254 unsigned int samplesPerSecond = ai->
rta->getStreamSampleRate();
270 double *outputBufferDouble = (
double *)outputBuffer;
273 memset(outputBufferDouble, 0, nBufferFrames*
sizeof(
VuoReal)*outputChannelCount);
280 bool pendingOutput = false;
281 for (pendingOutputType::iterator it = ai->pendingOutput.begin(); it != ai->pendingOutput.end(); ++it)
282 while (!it->second.empty())
284 VuoRelease(it->second.front());
286 pendingOutput = true;
290 VUserLog(
"This audio device (%s) doesn't support output.", ai->outputDevice.name);
298 void *id = it->first;
299 if (it->second.empty())
301 if (ai->lastOutputSample.find(id) == ai->lastOutputSample.end())
304 for (unsigned int channel = 0; channel < outputChannelCount; ++channel)
306 if (ai->lastOutputSample[id].find(channel) == ai->lastOutputSample[id].end())
310 VuoReal lastOutputSample = ai->lastOutputSample[id][channel];
311 for (VuoInteger i=0; i<nBufferFrames; ++i)
312 outputBufferDouble[nBufferFrames*(channel) + i] = VuoReal_lerp(lastOutputSample, 0, (float)i/nBufferFrames);
315 ai->lastOutputSample[id].erase(ai->lastOutputSample[id].find(channel));
320 VuoList_VuoAudioSamples channels = it->second.front();
322 for (unsigned int channel = 0; channel < outputChannelCount; ++channel)
324 VuoAudioSamples as = VuoListGetValue_VuoAudioSamples(channels, channel+1);
325 if (!as.samples || as.sampleCount == 0)
328 if (ai->lastOutputSample[id].find(channel) == ai->lastOutputSample[id].end())
332 if (it->second.size() < VuoAudio_queueSize)
336 for (VuoInteger i=0; i<nBufferFrames; ++i)
337 outputBufferDouble[nBufferFrames*channel + i] = (float)i/nBufferFrames * as.samples[i];
342 for (VuoInteger i=0; i<nBufferFrames; ++i)
344 outputBufferDouble[nBufferFrames*(channel) + i] += as.samples[i];
347 ai->lastOutputSample[id][channel] = as.samples[as.sampleCount-1];
368 Class avCaptureDeviceClass = objc_getClass(
"AVCaptureDevice");
369 if (class_getClassMethod(avCaptureDeviceClass, sel_getUid(
"authorizationStatusForMediaType:")))
371 CFStringRef mediaType = CFStringCreateWithCString(NULL,
"soun", kCFStringEncodingUTF8);
372 long status = (long)objc_msgSend((
id)avCaptureDeviceClass, sel_getUid(
"authorizationStatusForMediaType:"), mediaType);
373 CFRelease(mediaType);
376 VUserLog(
"Warning: Audio input may be unavailable due to system restrictions. Check System Preferences > Security & Privacy > Privacy > Microphone.");
379 VUserLog(
"Error: Audio input is unavailable due to system restrictions. Check System Preferences > Security & Privacy > Privacy > Microphone.");
393 ai->
rta =
new RtAudio();
396 RtAudio::StreamParameters inputParameters;
397 inputParameters.deviceId = deviceId;
398 inputParameters.nChannels = ai->
rta->getDeviceInfo(deviceId).inputChannels;
403 RtAudio::StreamParameters outputParameters;
404 outputParameters.deviceId = deviceId;
405 outputParameters.nChannels = ai->
rta->getDeviceInfo(deviceId).outputChannels;
410 RtAudio::StreamOptions options;
411 options.flags = RTAUDIO_NONINTERLEAVED;
416 outputParameters.nChannels ? &outputParameters : NULL,
417 inputParameters.nChannels ? &inputParameters : NULL,
425 ai->
rta->startStream();
427 catch (RtAudioError &error)
430 VUserLog(
"Failed to open the audio device (%d): %s", deviceId, error.what());
447 VuoAudio_internalPool->removeSharedInstance(ai->
inputDevice.
id);
451 if (ai->
rta->isStreamOpen())
453 ai->
rta->stopStream();
454 ai->
rta->closeStream();
457 catch (RtAudioError &error)
467 while (!it->second.empty())
603 aii->pendingOutput[id].push(channels);
608 #define setRealizedDevice(newDevice) \
609 realizedDevice->id = newDevice.id; \
610 realizedDevice->modelUid = VuoText_make(newDevice.modelUid); \
611 realizedDevice->name = VuoText_make(newDevice.name); \
612 realizedDevice->channelCount = newDevice.channelCount;
640 __block
bool found =
false;
644 if (device.
id != -1 && device.
id == item.
id)
646 VDebugLog(
"Matched by ID: %s",json_object_to_json_string(VuoAudioInputDevice_getJson(item)));
647 setRealizedDevice(item);
662 VDebugLog(
"Matched by model and name: %s",json_object_to_json_string(VuoAudioInputDevice_getJson(item)));
663 setRealizedDevice(item);
676 VDebugLog(
"Matched by model or name: %s",json_object_to_json_string(VuoAudioInputDevice_getJson(item)));
677 setRealizedDevice(item);
687 __block RtAudio *temporaryRTA;
693 temporaryRTA =
new RtAudio();
696 int defaultID = temporaryRTA->getDefaultInputDevice();
698 if (item.
id == defaultID)
700 VDebugLog(
"Using default device: %s",json_object_to_json_string(VuoAudioInputDevice_getJson(item)));
701 setRealizedDevice(item);
710 catch (RtAudioError &error)
712 VUserLog(
"Error: Couldn't enumerate audio devices: %s", error.what());
749 __block
bool found =
false;
753 if (device.
id != -1 && device.
id == item.
id)
755 VDebugLog(
"Matched by ID: %s",json_object_to_json_string(VuoAudioOutputDevice_getJson(item)));
756 setRealizedDevice(item);
771 VDebugLog(
"Matched by model and name: %s",json_object_to_json_string(VuoAudioOutputDevice_getJson(item)));
772 setRealizedDevice(item);
785 VDebugLog(
"Matched by model or name: %s",json_object_to_json_string(VuoAudioOutputDevice_getJson(item)));
786 setRealizedDevice(item);
796 __block RtAudio *temporaryRTA;
802 temporaryRTA =
new RtAudio();
805 int defaultID = temporaryRTA->getDefaultOutputDevice();
807 if (item.
id == defaultID)
809 VDebugLog(
"Using default device: %s",json_object_to_json_string(VuoAudioOutputDevice_getJson(item)));
810 setRealizedDevice(item);
819 catch (RtAudioError &error)
821 VUserLog(
"Error: Couldn't enumerate audio devices: %s", error.what());