mirror of https://github.com/AxioDL/boo.git
Sync macOS with API changes
This commit is contained in:
parent
261c06d746
commit
8ee04c8f1a
|
@ -123,8 +123,8 @@ elseif(APPLE)
|
||||||
PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||||
|
|
||||||
list(APPEND PLAT_HDRS
|
list(APPEND PLAT_HDRS
|
||||||
|
lib/CFPointer.hpp
|
||||||
include/boo/graphicsdev/Metal.hpp
|
include/boo/graphicsdev/Metal.hpp
|
||||||
lib/inputdev/CFPointer.hpp
|
|
||||||
lib/inputdev/IOKitPointer.hpp)
|
lib/inputdev/IOKitPointer.hpp)
|
||||||
|
|
||||||
find_library(APPKIT_LIBRARY AppKit)
|
find_library(APPKIT_LIBRARY AppKit)
|
||||||
|
|
|
@ -296,7 +296,7 @@ public:
|
||||||
virtual bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz)=0;
|
virtual bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz)=0;
|
||||||
virtual std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz)=0;
|
virtual std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz)=0;
|
||||||
|
|
||||||
virtual void waitForRetrace(IAudioVoiceEngine* voxEngine=nullptr)=0;
|
virtual void waitForRetrace()=0;
|
||||||
|
|
||||||
virtual uintptr_t getPlatformHandle() const=0;
|
virtual uintptr_t getPlatformHandle() const=0;
|
||||||
virtual bool _incomingEvent(void* event) {(void)event; return false;}
|
virtual bool _incomingEvent(void* event) {(void)event; return false;}
|
||||||
|
|
|
@ -102,12 +102,6 @@ struct IAudioVoiceEngine
|
||||||
|
|
||||||
/** Get canonical count of frames for each 5ms output block */
|
/** Get canonical count of frames for each 5ms output block */
|
||||||
virtual size_t get5MsFrames() const=0;
|
virtual size_t get5MsFrames() const=0;
|
||||||
|
|
||||||
/** IWindow::waitForRetrace() enter - for platforms that spend v-sync waits synchronously pumping audio */
|
|
||||||
virtual void _pumpAndMixVoicesRetrace() { pumpAndMixVoices(); }
|
|
||||||
|
|
||||||
/** IWindow::waitForRetrace() break - signal retrace event to break out of pumping cycles */
|
|
||||||
virtual void _retraceBreak() {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Construct host platform's voice engine */
|
/** Construct host platform's voice engine */
|
||||||
|
|
|
@ -44,12 +44,12 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
CFTypeRef* operator&()
|
T* operator&()
|
||||||
{
|
{
|
||||||
if (CFTypeRef pointer = storage) {
|
if (CFTypeRef pointer = storage) {
|
||||||
CFRelease(pointer);
|
CFRelease(pointer);
|
||||||
}
|
}
|
||||||
return &storage;
|
return (T*)&storage;
|
||||||
}
|
}
|
||||||
operator bool() const { return storage != nullptr; }
|
operator bool() const { return storage != nullptr; }
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "AudioVoiceEngine.hpp"
|
#include "AudioVoiceEngine.hpp"
|
||||||
#include "logvisor/logvisor.hpp"
|
#include "logvisor/logvisor.hpp"
|
||||||
|
#include "boo/IApplication.hpp"
|
||||||
|
#include "../CFPointer.hpp"
|
||||||
|
|
||||||
#include <AudioToolbox/AudioToolbox.h>
|
#include <AudioToolbox/AudioToolbox.h>
|
||||||
#include <CoreMIDI/CoreMIDI.h>
|
#include <CoreMIDI/CoreMIDI.h>
|
||||||
|
@ -12,6 +14,8 @@ namespace boo
|
||||||
{
|
{
|
||||||
static logvisor::Module Log("boo::AQS");
|
static logvisor::Module Log("boo::AQS");
|
||||||
|
|
||||||
|
#define AQS_NUM_BUFFERS 24
|
||||||
|
|
||||||
static AudioChannel AQSChannelToBooChannel(AudioChannelLabel ch)
|
static AudioChannel AQSChannelToBooChannel(AudioChannelLabel ch)
|
||||||
{
|
{
|
||||||
switch (ch)
|
switch (ch)
|
||||||
|
@ -38,105 +42,181 @@ static AudioChannel AQSChannelToBooChannel(AudioChannelLabel ch)
|
||||||
|
|
||||||
struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
{
|
{
|
||||||
|
CFPointer<CFStringRef> m_runLoopMode;
|
||||||
|
CFPointer<CFStringRef> m_devName;
|
||||||
|
|
||||||
AudioQueueRef m_queue = nullptr;
|
AudioQueueRef m_queue = nullptr;
|
||||||
AudioQueueBufferRef m_buffers[3];
|
AudioQueueBufferRef m_buffers[AQS_NUM_BUFFERS];
|
||||||
size_t m_frameBytes;
|
size_t m_frameBytes;
|
||||||
|
|
||||||
MIDIClientRef m_midiClient = 0;
|
MIDIClientRef m_midiClient = 0;
|
||||||
|
|
||||||
std::mutex m_engineMutex;
|
|
||||||
std::condition_variable m_engineEnterCv;
|
|
||||||
std::condition_variable m_engineLeaveCv;
|
|
||||||
bool m_inRetrace = false;
|
|
||||||
bool m_inCb = false;
|
|
||||||
bool m_cbRunning = true;
|
bool m_cbRunning = true;
|
||||||
|
bool m_needsRebuild = false;
|
||||||
|
|
||||||
static void Callback(AQSAudioVoiceEngine* engine, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
|
static void Callback(AQSAudioVoiceEngine* engine, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
|
||||||
{
|
{
|
||||||
if (!engine->m_cbRunning)
|
if (!engine->m_cbRunning)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lk(engine->m_engineMutex);
|
|
||||||
engine->m_inCb = true;
|
|
||||||
if (!engine->m_inRetrace)
|
|
||||||
{
|
|
||||||
if (engine->m_engineEnterCv.wait_for(lk,
|
|
||||||
std::chrono::nanoseconds(engine->m_mixInfo.m_periodFrames * 1000000000 /
|
|
||||||
size_t(engine->m_mixInfo.m_sampleRate))) == std::cv_status::timeout ||
|
|
||||||
!engine->m_inRetrace)
|
|
||||||
{
|
|
||||||
inBuffer->mAudioDataByteSize = engine->m_frameBytes;
|
|
||||||
memset(inBuffer->mAudioData, 0, engine->m_frameBytes);
|
|
||||||
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nullptr);
|
|
||||||
engine->m_engineLeaveCv.notify_one();
|
|
||||||
engine->m_inCb = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
engine->_pumpAndMixVoices(engine->m_mixInfo.m_periodFrames,
|
engine->_pumpAndMixVoices(engine->m_mixInfo.m_periodFrames,
|
||||||
reinterpret_cast<float*>(inBuffer->mAudioData));
|
reinterpret_cast<float*>(inBuffer->mAudioData));
|
||||||
inBuffer->mAudioDataByteSize = engine->m_frameBytes;
|
inBuffer->mAudioDataByteSize = engine->m_frameBytes;
|
||||||
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nullptr);
|
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nullptr);
|
||||||
|
|
||||||
engine->m_engineLeaveCv.notify_one();
|
|
||||||
engine->m_inCb = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DummyCallback(AQSAudioVoiceEngine* engine, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {}
|
static void DummyCallback(AQSAudioVoiceEngine* engine, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {}
|
||||||
|
|
||||||
AudioChannelSet _getAvailableSet()
|
std::pair<AudioChannelSet, Float64> _getAvailableSetAndRate()
|
||||||
{
|
{
|
||||||
const unsigned chCount = 8;
|
AudioObjectPropertyAddress propertyAddress;
|
||||||
AudioStreamBasicDescription desc = {};
|
UInt32 argSize;
|
||||||
desc.mSampleRate = 96000;
|
int numStreams;
|
||||||
desc.mFormatID = kAudioFormatLinearPCM;
|
std::vector<AudioStreamID> streamIDs;
|
||||||
desc.mFormatFlags = kLinearPCMFormatFlagIsFloat;
|
|
||||||
desc.mBytesPerPacket = chCount * 4;
|
|
||||||
desc.mFramesPerPacket = 1;
|
|
||||||
desc.mBytesPerFrame = chCount * 4;
|
|
||||||
desc.mChannelsPerFrame = chCount;
|
|
||||||
desc.mBitsPerChannel = 32;
|
|
||||||
|
|
||||||
AudioQueueRef queue;
|
CFStringRef devName = m_devName.get();
|
||||||
if (AudioQueueNewOutput(&desc, AudioQueueOutputCallback(DummyCallback),
|
AudioObjectID devId;
|
||||||
this, nullptr, nullptr, 0, &queue))
|
propertyAddress.mSelector = kAudioHardwarePropertyTranslateUIDToDevice;
|
||||||
{
|
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
Log.report(logvisor::Error, "unable to create output audio queue");
|
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
return AudioChannelSet::Unknown;
|
argSize = sizeof(devId);
|
||||||
|
if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, sizeof(devName), &devName, &argSize, &devId) != noErr) {
|
||||||
|
Log.report(logvisor::Error, "unable to resolve audio device UID %s, using default",
|
||||||
|
CFStringGetCStringPtr(devName, kCFStringEncodingUTF8));
|
||||||
|
argSize = sizeof(devId);
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||||
|
if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &argSize, &devId) == noErr) {
|
||||||
|
argSize = sizeof(CFStringRef);
|
||||||
|
AudioObjectPropertyAddress deviceAddress;
|
||||||
|
deviceAddress.mSelector = kAudioDevicePropertyDeviceUID;
|
||||||
|
AudioObjectGetPropertyData(devId, &deviceAddress, 0, NULL, &argSize, &m_devName);
|
||||||
|
} else {
|
||||||
|
Log.report(logvisor::Fatal, "unable determine default audio device");
|
||||||
|
return {AudioChannelSet::Unknown, 48000.0};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt32 hwChannels;
|
propertyAddress.mSelector = kAudioDevicePropertyStreams;
|
||||||
UInt32 channelsSz = sizeof(UInt32);
|
if (AudioObjectGetPropertyDataSize(devId, &propertyAddress, 0, NULL, &argSize) == noErr) {
|
||||||
if (AudioQueueGetProperty(queue, kAudioQueueDeviceProperty_NumberChannels, &hwChannels, &channelsSz))
|
numStreams = argSize / sizeof(AudioStreamID);
|
||||||
{
|
streamIDs.resize(numStreams);
|
||||||
Log.report(logvisor::Error, "unable to get channel count from audio queue");
|
|
||||||
AudioQueueDispose(queue, true);
|
if (AudioObjectGetPropertyData(devId, &propertyAddress, 0, NULL, &argSize, &streamIDs[0]) == noErr) {
|
||||||
return AudioChannelSet::Unknown;
|
propertyAddress.mSelector = kAudioStreamPropertyDirection;
|
||||||
|
for (int stm = 0; stm < numStreams; stm++) {
|
||||||
|
UInt32 streamDir;
|
||||||
|
argSize = sizeof(streamDir);
|
||||||
|
if (AudioObjectGetPropertyData(streamIDs[stm], &propertyAddress, 0, NULL, &argSize, &streamDir) == noErr) {
|
||||||
|
if (streamDir == 0) {
|
||||||
|
propertyAddress.mSelector = kAudioStreamPropertyPhysicalFormat;
|
||||||
|
AudioStreamBasicDescription asbd;
|
||||||
|
argSize = sizeof(asbd);
|
||||||
|
if (AudioObjectGetPropertyData(streamIDs[stm], &propertyAddress, 0, NULL, &argSize, &asbd) == noErr) {
|
||||||
|
switch (asbd.mChannelsPerFrame)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
return {AudioChannelSet::Stereo, asbd.mSampleRate};
|
||||||
|
case 4:
|
||||||
|
return {AudioChannelSet::Quad, asbd.mSampleRate};
|
||||||
|
case 6:
|
||||||
|
return {AudioChannelSet::Surround51, asbd.mSampleRate};
|
||||||
|
case 8:
|
||||||
|
return {AudioChannelSet::Surround71, asbd.mSampleRate};
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
if (asbd.mChannelsPerFrame > 8)
|
||||||
|
return {AudioChannelSet::Surround71, asbd.mSampleRate};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioQueueDispose(queue, true);
|
return {AudioChannelSet::Unknown, 48000.0};
|
||||||
|
|
||||||
switch (hwChannels)
|
|
||||||
{
|
|
||||||
case 2:
|
|
||||||
return AudioChannelSet::Stereo;
|
|
||||||
case 4:
|
|
||||||
return AudioChannelSet::Quad;
|
|
||||||
case 6:
|
|
||||||
return AudioChannelSet::Surround51;
|
|
||||||
case 8:
|
|
||||||
return AudioChannelSet::Surround71;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwChannels > 8)
|
|
||||||
return AudioChannelSet::Surround71;
|
|
||||||
|
|
||||||
return AudioChannelSet::Unknown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() const
|
std::string getCurrentAudioOutput() const
|
||||||
|
{
|
||||||
|
return CFStringGetCStringPtr(m_devName.get(), kCFStringEncodingUTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setCurrentAudioOutput(const char* name)
|
||||||
|
{
|
||||||
|
m_devName = CFPointer<CFStringRef>::adopt(CFStringCreateWithCString(nullptr, name, kCFStringEncodingUTF8));
|
||||||
|
_rebuildAudioQueue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* https://stackoverflow.com/questions/1983984/how-to-get-audio-device-uid-to-pass-into-nssounds-setplaybackdeviceidentifier
|
||||||
|
*/
|
||||||
|
std::vector<std::pair<std::string, std::string>> enumerateAudioOutputs() const
|
||||||
|
{
|
||||||
|
std::vector<std::pair<std::string, std::string>> ret;
|
||||||
|
|
||||||
|
AudioObjectPropertyAddress propertyAddress;
|
||||||
|
std::vector<AudioObjectID> deviceIDs;
|
||||||
|
UInt32 propertySize;
|
||||||
|
int numDevices;
|
||||||
|
|
||||||
|
std::vector<AudioStreamID> streamIDs;
|
||||||
|
int numStreams;
|
||||||
|
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDevices;
|
||||||
|
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize) == noErr) {
|
||||||
|
numDevices = propertySize / sizeof(AudioDeviceID);
|
||||||
|
ret.reserve(numDevices);
|
||||||
|
deviceIDs.resize(numDevices);
|
||||||
|
|
||||||
|
if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &deviceIDs[0]) == noErr) {
|
||||||
|
char deviceName[64];
|
||||||
|
|
||||||
|
for (int idx = 0; idx < numDevices; idx++) {
|
||||||
|
propertyAddress.mSelector = kAudioDevicePropertyStreams;
|
||||||
|
if (AudioObjectGetPropertyDataSize(deviceIDs[idx], &propertyAddress, 0, NULL, &propertySize) == noErr) {
|
||||||
|
numStreams = propertySize / sizeof(AudioStreamID);
|
||||||
|
streamIDs.resize(numStreams);
|
||||||
|
|
||||||
|
if (AudioObjectGetPropertyData(deviceIDs[idx], &propertyAddress, 0, NULL, &propertySize, &streamIDs[0]) == noErr) {
|
||||||
|
propertyAddress.mSelector = kAudioStreamPropertyDirection;
|
||||||
|
bool foundOutput = false;
|
||||||
|
for (int stm = 0; stm < numStreams; stm++) {
|
||||||
|
UInt32 streamDir;
|
||||||
|
propertySize = sizeof(streamDir);
|
||||||
|
if (AudioObjectGetPropertyData(streamIDs[stm], &propertyAddress, 0, NULL, &propertySize, &streamDir) == noErr) {
|
||||||
|
if (streamDir == 0) {
|
||||||
|
foundOutput = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundOutput)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
propertySize = sizeof(deviceName);
|
||||||
|
propertyAddress.mSelector = kAudioDevicePropertyDeviceName;
|
||||||
|
if (AudioObjectGetPropertyData(deviceIDs[idx], &propertyAddress, 0, NULL, &propertySize, deviceName) == noErr) {
|
||||||
|
CFPointer<CFStringRef> uidString;
|
||||||
|
|
||||||
|
propertySize = sizeof(CFStringRef);
|
||||||
|
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
|
||||||
|
if (AudioObjectGetPropertyData(deviceIDs[idx], &propertyAddress, 0, NULL, &propertySize, &uidString) == noErr) {
|
||||||
|
ret.emplace_back(CFStringGetCStringPtr(uidString.get(), kCFStringEncodingUTF8), deviceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, std::string>> enumerateMIDIInputs() const
|
||||||
{
|
{
|
||||||
if (!m_midiClient)
|
if (!m_midiClient)
|
||||||
return {};
|
return {};
|
||||||
|
@ -151,32 +231,50 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
if (!dev)
|
if (!dev)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
bool isInput = false;
|
||||||
|
ItemCount numEnt = MIDIDeviceGetNumberOfEntities(dev);
|
||||||
|
for (ItemCount j=0 ; j<numEnt ; ++j)
|
||||||
|
{
|
||||||
|
MIDIEntityRef ent = MIDIDeviceGetEntity(dev, j);
|
||||||
|
if (ent)
|
||||||
|
{
|
||||||
|
ItemCount numSrc = MIDIEntityGetNumberOfSources(ent);
|
||||||
|
if (numSrc)
|
||||||
|
{
|
||||||
|
isInput = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isInput)
|
||||||
|
continue;
|
||||||
|
|
||||||
SInt32 idNum;
|
SInt32 idNum;
|
||||||
if (MIDIObjectGetIntegerProperty(dev, kMIDIPropertyUniqueID, &idNum))
|
if (MIDIObjectGetIntegerProperty(dev, kMIDIPropertyUniqueID, &idNum))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CFStringRef namestr;
|
CFPointer<CFStringRef> namestr;
|
||||||
const char* nameCstr;
|
const char* nameCstr;
|
||||||
if (MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &namestr))
|
if (MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &namestr))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!(nameCstr = CFStringGetCStringPtr(namestr, kCFStringEncodingUTF8)))
|
if (!(nameCstr = CFStringGetCStringPtr(namestr.get(), kCFStringEncodingUTF8)))
|
||||||
{
|
|
||||||
CFRelease(namestr);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
char idStr[9];
|
char idStr[9];
|
||||||
snprintf(idStr, 9, "%08X\n", idNum);
|
snprintf(idStr, 9, "%08X\n", idNum);
|
||||||
ret.push_back(std::make_pair(std::string(idStr),
|
ret.push_back(std::make_pair(std::string(idStr),
|
||||||
std::string(nameCstr)));
|
std::string(nameCstr)));
|
||||||
|
|
||||||
CFRelease(namestr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool supportsVirtualMIDIIn() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static MIDIDeviceRef LookupMIDIDevice(const char* name)
|
static MIDIDeviceRef LookupMIDIDevice(const char* name)
|
||||||
{
|
{
|
||||||
ItemCount numDevices = MIDIGetNumberOfDevices();
|
ItemCount numDevices = MIDIGetNumberOfDevices();
|
||||||
|
@ -269,8 +367,8 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
MIDIEndpointRef m_midi = 0;
|
MIDIEndpointRef m_midi = 0;
|
||||||
MIDIPortRef m_midiPort = 0;
|
MIDIPortRef m_midiPort = 0;
|
||||||
|
|
||||||
MIDIIn(bool virt, ReceiveFunctor&& receiver)
|
MIDIIn(AQSAudioVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver)
|
||||||
: IMIDIIn(virt, std::move(receiver)) {}
|
: IMIDIIn(parent, virt, std::move(receiver)) {}
|
||||||
|
|
||||||
~MIDIIn()
|
~MIDIIn()
|
||||||
{
|
{
|
||||||
|
@ -282,18 +380,14 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
|
|
||||||
std::string description() const
|
std::string description() const
|
||||||
{
|
{
|
||||||
CFStringRef namestr;
|
CFPointer<CFStringRef> namestr;
|
||||||
const char* nameCstr;
|
const char* nameCstr;
|
||||||
if (MIDIObjectGetStringProperty(m_midi, kMIDIPropertyName, &namestr))
|
if (MIDIObjectGetStringProperty(m_midi, kMIDIPropertyName, &namestr))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (!(nameCstr = CFStringGetCStringPtr(namestr, kCFStringEncodingUTF8)))
|
if (!(nameCstr = CFStringGetCStringPtr(namestr.get(), kCFStringEncodingUTF8)))
|
||||||
{
|
|
||||||
CFRelease(namestr);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
CFRelease(namestr);
|
|
||||||
return nameCstr;
|
return nameCstr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -303,8 +397,8 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
MIDIEndpointRef m_midi = 0;
|
MIDIEndpointRef m_midi = 0;
|
||||||
MIDIPortRef m_midiPort = 0;
|
MIDIPortRef m_midiPort = 0;
|
||||||
|
|
||||||
MIDIOut(bool virt)
|
MIDIOut(AQSAudioVoiceEngine* parent, bool virt)
|
||||||
: IMIDIOut(virt) {}
|
: IMIDIOut(parent, virt) {}
|
||||||
|
|
||||||
~MIDIOut()
|
~MIDIOut()
|
||||||
{
|
{
|
||||||
|
@ -316,18 +410,14 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
|
|
||||||
std::string description() const
|
std::string description() const
|
||||||
{
|
{
|
||||||
CFStringRef namestr;
|
CFPointer<CFStringRef> namestr;
|
||||||
const char* nameCstr;
|
const char* nameCstr;
|
||||||
if (MIDIObjectGetStringProperty(m_midi, kMIDIPropertyName, &namestr))
|
if (MIDIObjectGetStringProperty(m_midi, kMIDIPropertyName, &namestr))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (!(nameCstr = CFStringGetCStringPtr(namestr, kCFStringEncodingUTF8)))
|
if (!(nameCstr = CFStringGetCStringPtr(namestr.get(), kCFStringEncodingUTF8)))
|
||||||
{
|
|
||||||
CFRelease(namestr);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
CFRelease(namestr);
|
|
||||||
return nameCstr;
|
return nameCstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,8 +449,8 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
MIDIEndpointRef m_midiOut = 0;
|
MIDIEndpointRef m_midiOut = 0;
|
||||||
MIDIPortRef m_midiPortOut = 0;
|
MIDIPortRef m_midiPortOut = 0;
|
||||||
|
|
||||||
MIDIInOut(bool virt, ReceiveFunctor&& receiver)
|
MIDIInOut(AQSAudioVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver)
|
||||||
: IMIDIInOut(virt, std::move(receiver)) {}
|
: IMIDIInOut(parent, virt, std::move(receiver)) {}
|
||||||
|
|
||||||
~MIDIInOut()
|
~MIDIInOut()
|
||||||
{
|
{
|
||||||
|
@ -376,18 +466,14 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
|
|
||||||
std::string description() const
|
std::string description() const
|
||||||
{
|
{
|
||||||
CFStringRef namestr;
|
CFPointer<CFStringRef> namestr;
|
||||||
const char* nameCstr;
|
const char* nameCstr;
|
||||||
if (MIDIObjectGetStringProperty(m_midiIn, kMIDIPropertyName, &namestr))
|
if (MIDIObjectGetStringProperty(m_midiIn, kMIDIPropertyName, &namestr))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (!(nameCstr = CFStringGetCStringPtr(namestr, kCFStringEncodingUTF8)))
|
if (!(nameCstr = CFStringGetCStringPtr(namestr.get(), kCFStringEncodingUTF8)))
|
||||||
{
|
|
||||||
CFRelease(namestr);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
CFRelease(namestr);
|
|
||||||
return nameCstr;
|
return nameCstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,19 +506,24 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
if (!m_midiClient)
|
if (!m_midiClient)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::unique_ptr<IMIDIIn> ret = std::make_unique<MIDIIn>(true, std::move(receiver));
|
std::unique_ptr<IMIDIIn> ret = std::make_unique<MIDIIn>(this, true, std::move(receiver));
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
char name[256];
|
char name[256];
|
||||||
snprintf(name, 256, "Boo MIDI Virtual In %u", m_midiInCounter++);
|
auto appName = APP->getFriendlyName();
|
||||||
CFStringRef midiName = CFStringCreateWithCStringNoCopy(nullptr, name, kCFStringEncodingUTF8, kCFAllocatorNull);
|
if (!m_midiInCounter)
|
||||||
|
snprintf(name, 256, "%s MIDI-In", appName.data());
|
||||||
|
else
|
||||||
|
snprintf(name, 256, "%s MIDI-In %u", appName.data(), m_midiInCounter);
|
||||||
|
m_midiInCounter++;
|
||||||
|
CFPointer<CFStringRef> midiName = CFPointer<CFStringRef>::adopt(
|
||||||
|
CFStringCreateWithCStringNoCopy(nullptr, name, kCFStringEncodingUTF8, kCFAllocatorNull));
|
||||||
OSStatus stat;
|
OSStatus stat;
|
||||||
if ((stat = MIDIDestinationCreate(m_midiClient, midiName, MIDIReadProc(MIDIReceiveProc),
|
if ((stat = MIDIDestinationCreate(m_midiClient, midiName.get(), MIDIReadProc(MIDIReceiveProc),
|
||||||
static_cast<IMIDIReceiver*>(ret.get()),
|
static_cast<IMIDIReceiver*>(ret.get()),
|
||||||
&static_cast<MIDIIn&>(*ret).m_midi)))
|
&static_cast<MIDIIn&>(*ret).m_midi)))
|
||||||
ret.reset();
|
ret.reset();
|
||||||
CFRelease(midiName);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -442,16 +533,21 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
if (!m_midiClient)
|
if (!m_midiClient)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::unique_ptr<IMIDIOut> ret = std::make_unique<MIDIOut>(true);
|
std::unique_ptr<IMIDIOut> ret = std::make_unique<MIDIOut>(this, true);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
char name[256];
|
char name[256];
|
||||||
snprintf(name, 256, "Boo MIDI Virtual Out %u", m_midiOutCounter++);
|
auto appName = APP->getFriendlyName();
|
||||||
CFStringRef midiName = CFStringCreateWithCStringNoCopy(nullptr, name, kCFStringEncodingUTF8, kCFAllocatorNull);
|
if (!m_midiOutCounter)
|
||||||
if (MIDISourceCreate(m_midiClient, midiName, &static_cast<MIDIOut&>(*ret).m_midi))
|
snprintf(name, 256, "%s MIDI-Out", appName.data());
|
||||||
|
else
|
||||||
|
snprintf(name, 256, "%s MIDI-Out %u", appName.data(), m_midiOutCounter);
|
||||||
|
m_midiOutCounter++;
|
||||||
|
CFPointer<CFStringRef> midiName = CFPointer<CFStringRef>::adopt(
|
||||||
|
CFStringCreateWithCStringNoCopy(nullptr, name, kCFStringEncodingUTF8, kCFAllocatorNull));
|
||||||
|
if (MIDISourceCreate(m_midiClient, midiName.get(), &static_cast<MIDIOut&>(*ret).m_midi))
|
||||||
ret.reset();
|
ret.reset();
|
||||||
CFRelease(midiName);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -461,27 +557,36 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
if (!m_midiClient)
|
if (!m_midiClient)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::unique_ptr<IMIDIInOut> ret = std::make_unique<MIDIInOut>(true, std::move(receiver));
|
std::unique_ptr<IMIDIInOut> ret = std::make_unique<MIDIInOut>(this, true, std::move(receiver));
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
char name[256];
|
char name[256];
|
||||||
snprintf(name, 256, "Boo MIDI Virtual In %u", m_midiInCounter++);
|
auto appName = APP->getFriendlyName();
|
||||||
CFStringRef midiName = CFStringCreateWithCStringNoCopy(nullptr, name, kCFStringEncodingUTF8, kCFAllocatorNull);
|
if (!m_midiInCounter)
|
||||||
if (MIDIDestinationCreate(m_midiClient, midiName, MIDIReadProc(MIDIReceiveProc),
|
snprintf(name, 256, "%s MIDI-In", appName.data());
|
||||||
|
else
|
||||||
|
snprintf(name, 256, "%s MIDI-In %u", appName.data(), m_midiInCounter);
|
||||||
|
m_midiInCounter++;
|
||||||
|
CFPointer<CFStringRef> midiName = CFPointer<CFStringRef>::adopt(
|
||||||
|
CFStringCreateWithCStringNoCopy(nullptr, name, kCFStringEncodingUTF8, kCFAllocatorNull));
|
||||||
|
if (MIDIDestinationCreate(m_midiClient, midiName.get(), MIDIReadProc(MIDIReceiveProc),
|
||||||
static_cast<IMIDIReceiver*>(ret.get()),
|
static_cast<IMIDIReceiver*>(ret.get()),
|
||||||
&static_cast<MIDIInOut&>(*ret).m_midiIn))
|
&static_cast<MIDIInOut&>(*ret).m_midiIn))
|
||||||
ret.reset();
|
ret.reset();
|
||||||
CFRelease(midiName);
|
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
snprintf(name, 256, "Boo MIDI Virtual Out %u", m_midiOutCounter++);
|
if (!m_midiOutCounter)
|
||||||
midiName = CFStringCreateWithCStringNoCopy(nullptr, name, kCFStringEncodingUTF8, kCFAllocatorNull);
|
snprintf(name, 256, "%s MIDI-Out", appName.data());
|
||||||
if (MIDISourceCreate(m_midiClient, midiName, &static_cast<MIDIInOut&>(*ret).m_midiOut))
|
else
|
||||||
|
snprintf(name, 256, "%s MIDI-Out %u", appName.data(), m_midiOutCounter);
|
||||||
|
m_midiOutCounter++;
|
||||||
|
midiName = CFPointer<CFStringRef>::adopt(
|
||||||
|
CFStringCreateWithCStringNoCopy(nullptr, name, kCFStringEncodingUTF8, kCFAllocatorNull));
|
||||||
|
if (MIDISourceCreate(m_midiClient, midiName.get(), &static_cast<MIDIInOut&>(*ret).m_midiOut))
|
||||||
ret.reset();
|
ret.reset();
|
||||||
CFRelease(midiName);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -495,20 +600,20 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
if (!src)
|
if (!src)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::unique_ptr<IMIDIIn> ret = std::make_unique<MIDIIn>(false, std::move(receiver));
|
std::unique_ptr<IMIDIIn> ret = std::make_unique<MIDIIn>(this, false, std::move(receiver));
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
char mname[256];
|
char mname[256];
|
||||||
snprintf(mname, 256, "Boo MIDI Real In %u", m_midiInCounter++);
|
snprintf(mname, 256, "Boo MIDI Real In %u", m_midiInCounter++);
|
||||||
CFStringRef midiName = CFStringCreateWithCStringNoCopy(nullptr, mname, kCFStringEncodingUTF8, kCFAllocatorNull);
|
CFPointer<CFStringRef> midiName = CFPointer<CFStringRef>::adopt(
|
||||||
if (MIDIInputPortCreate(m_midiClient, midiName, MIDIReadProc(MIDIReceiveProc),
|
CFStringCreateWithCStringNoCopy(nullptr, mname, kCFStringEncodingUTF8, kCFAllocatorNull));
|
||||||
|
if (MIDIInputPortCreate(m_midiClient, midiName.get(), MIDIReadProc(MIDIReceiveProc),
|
||||||
static_cast<IMIDIReceiver*>(ret.get()),
|
static_cast<IMIDIReceiver*>(ret.get()),
|
||||||
&static_cast<MIDIIn&>(*ret).m_midiPort))
|
&static_cast<MIDIIn&>(*ret).m_midiPort))
|
||||||
ret.reset();
|
ret.reset();
|
||||||
else
|
else
|
||||||
MIDIPortConnectSource(static_cast<MIDIIn&>(*ret).m_midiPort, src, nullptr);
|
MIDIPortConnectSource(static_cast<MIDIIn&>(*ret).m_midiPort, src, nullptr);
|
||||||
CFRelease(midiName);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -522,18 +627,18 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
if (!dst)
|
if (!dst)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::unique_ptr<IMIDIOut> ret = std::make_unique<MIDIOut>(false);
|
std::unique_ptr<IMIDIOut> ret = std::make_unique<MIDIOut>(this, false);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
char mname[256];
|
char mname[256];
|
||||||
snprintf(mname, 256, "Boo MIDI Real Out %u", m_midiOutCounter++);
|
snprintf(mname, 256, "Boo MIDI Real Out %u", m_midiOutCounter++);
|
||||||
CFStringRef midiName = CFStringCreateWithCStringNoCopy(nullptr, mname, kCFStringEncodingUTF8, kCFAllocatorNull);
|
CFPointer<CFStringRef> midiName = CFPointer<CFStringRef>::adopt(
|
||||||
if (MIDIOutputPortCreate(m_midiClient, midiName, &static_cast<MIDIOut&>(*ret).m_midiPort))
|
CFStringCreateWithCStringNoCopy(nullptr, mname, kCFStringEncodingUTF8, kCFAllocatorNull));
|
||||||
|
if (MIDIOutputPortCreate(m_midiClient, midiName.get(), &static_cast<MIDIOut&>(*ret).m_midiPort))
|
||||||
ret.reset();
|
ret.reset();
|
||||||
else
|
else
|
||||||
static_cast<MIDIOut&>(*ret).m_midi = dst;
|
static_cast<MIDIOut&>(*ret).m_midi = dst;
|
||||||
CFRelease(midiName);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -551,44 +656,60 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
if (!dst)
|
if (!dst)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::unique_ptr<IMIDIInOut> ret = std::make_unique<MIDIInOut>(false, std::move(receiver));
|
std::unique_ptr<IMIDIInOut> ret = std::make_unique<MIDIInOut>(this, false, std::move(receiver));
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
char mname[256];
|
char mname[256];
|
||||||
snprintf(mname, 256, "Boo MIDI Real In %u", m_midiInCounter++);
|
snprintf(mname, 256, "Boo MIDI Real In %u", m_midiInCounter++);
|
||||||
CFStringRef midiName = CFStringCreateWithCStringNoCopy(nullptr, mname, kCFStringEncodingUTF8, kCFAllocatorNull);
|
CFPointer<CFStringRef> midiName = CFPointer<CFStringRef>::adopt(
|
||||||
if (MIDIInputPortCreate(m_midiClient, midiName, MIDIReadProc(MIDIReceiveProc),
|
CFStringCreateWithCStringNoCopy(nullptr, mname, kCFStringEncodingUTF8, kCFAllocatorNull));
|
||||||
|
if (MIDIInputPortCreate(m_midiClient, midiName.get(), MIDIReadProc(MIDIReceiveProc),
|
||||||
static_cast<IMIDIReceiver*>(ret.get()),
|
static_cast<IMIDIReceiver*>(ret.get()),
|
||||||
&static_cast<MIDIInOut&>(*ret).m_midiPortIn))
|
&static_cast<MIDIInOut&>(*ret).m_midiPortIn))
|
||||||
ret.reset();
|
ret.reset();
|
||||||
else
|
else
|
||||||
MIDIPortConnectSource(static_cast<MIDIInOut&>(*ret).m_midiPortIn, src, nullptr);
|
MIDIPortConnectSource(static_cast<MIDIInOut&>(*ret).m_midiPortIn, src, nullptr);
|
||||||
CFRelease(midiName);
|
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
snprintf(mname, 256, "Boo MIDI Real Out %u", m_midiOutCounter++);
|
snprintf(mname, 256, "Boo MIDI Real Out %u", m_midiOutCounter++);
|
||||||
midiName = CFStringCreateWithCStringNoCopy(nullptr, mname, kCFStringEncodingUTF8, kCFAllocatorNull);
|
midiName = CFPointer<CFStringRef>::adopt(
|
||||||
if (MIDIOutputPortCreate(m_midiClient, midiName, &static_cast<MIDIInOut&>(*ret).m_midiPortOut))
|
CFStringCreateWithCStringNoCopy(nullptr, mname, kCFStringEncodingUTF8, kCFAllocatorNull));
|
||||||
|
if (MIDIOutputPortCreate(m_midiClient, midiName.get(), &static_cast<MIDIInOut&>(*ret).m_midiPortOut))
|
||||||
ret.reset();
|
ret.reset();
|
||||||
else
|
else
|
||||||
static_cast<MIDIInOut&>(*ret).m_midiOut = dst;
|
static_cast<MIDIInOut&>(*ret).m_midiOut = dst;
|
||||||
CFRelease(midiName);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool useMIDILock() const {return true;}
|
bool useMIDILock() const {return true;}
|
||||||
|
|
||||||
AQSAudioVoiceEngine()
|
static void SampleRateChanged(AQSAudioVoiceEngine* engine,
|
||||||
|
AudioQueueRef inAQ,
|
||||||
|
AudioQueuePropertyID inID)
|
||||||
{
|
{
|
||||||
m_mixInfo.m_channels = _getAvailableSet();
|
engine->m_needsRebuild = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _rebuildAudioQueue()
|
||||||
|
{
|
||||||
|
if (m_queue)
|
||||||
|
{
|
||||||
|
m_cbRunning = false;
|
||||||
|
AudioQueueDispose(m_queue, true);
|
||||||
|
m_cbRunning = true;
|
||||||
|
m_queue = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto setAndRate = _getAvailableSetAndRate();
|
||||||
|
m_mixInfo.m_channels = setAndRate.first;
|
||||||
unsigned chCount = ChannelCount(m_mixInfo.m_channels);
|
unsigned chCount = ChannelCount(m_mixInfo.m_channels);
|
||||||
|
|
||||||
AudioStreamBasicDescription desc = {};
|
AudioStreamBasicDescription desc = {};
|
||||||
desc.mSampleRate = 96000;
|
desc.mSampleRate = setAndRate.second;
|
||||||
desc.mFormatID = kAudioFormatLinearPCM;
|
desc.mFormatID = kAudioFormatLinearPCM;
|
||||||
desc.mFormatFlags = kLinearPCMFormatFlagIsFloat;
|
desc.mFormatFlags = kLinearPCMFormatFlagIsFloat;
|
||||||
desc.mBytesPerPacket = chCount * 4;
|
desc.mBytesPerPacket = chCount * 4;
|
||||||
|
@ -599,34 +720,26 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
|
|
||||||
OSStatus err;
|
OSStatus err;
|
||||||
if ((err = AudioQueueNewOutput(&desc, AudioQueueOutputCallback(Callback),
|
if ((err = AudioQueueNewOutput(&desc, AudioQueueOutputCallback(Callback),
|
||||||
this, nullptr, nullptr, 0, &m_queue)))
|
this, CFRunLoopGetCurrent(), m_runLoopMode.get(), 0, &m_queue)))
|
||||||
{
|
{
|
||||||
Log.report(logvisor::Fatal, "unable to create output audio queue");
|
Log.report(logvisor::Fatal, "unable to create output audio queue");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Float64 actualSampleRate;
|
CFStringRef devName = m_devName.get();
|
||||||
UInt32 argSize = 8;
|
if ((err = AudioQueueSetProperty(m_queue, kAudioQueueProperty_CurrentDevice, &devName, sizeof(devName))))
|
||||||
err = AudioQueueGetProperty(m_queue, kAudioQueueDeviceProperty_SampleRate, &actualSampleRate, &argSize);
|
|
||||||
AudioQueueDispose(m_queue, true);
|
|
||||||
if (err)
|
|
||||||
{
|
{
|
||||||
Log.report(logvisor::Fatal, "unable to get native sample rate from audio queue");
|
Log.report(logvisor::Fatal, "unable to set current device into audio queue");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
desc.mSampleRate = actualSampleRate;
|
AudioQueueAddPropertyListener(m_queue, kAudioQueueDeviceProperty_SampleRate,
|
||||||
if ((err = AudioQueueNewOutput(&desc, AudioQueueOutputCallback(Callback),
|
AudioQueuePropertyListenerProc(SampleRateChanged), this);
|
||||||
this, nullptr, nullptr, 0, &m_queue)))
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Fatal, "unable to create output audio queue");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_mixInfo.m_sampleRate = actualSampleRate;
|
m_mixInfo.m_sampleRate = desc.mSampleRate;
|
||||||
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
|
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
|
||||||
m_mixInfo.m_bitsPerSample = 32;
|
m_mixInfo.m_bitsPerSample = 32;
|
||||||
m_5msFrames = actualSampleRate * 5 / 1000;
|
m_5msFrames = desc.mSampleRate * 5 / 1000;
|
||||||
|
|
||||||
ChannelMap& chMapOut = m_mixInfo.m_channelMap;
|
ChannelMap& chMapOut = m_mixInfo.m_channelMap;
|
||||||
chMapOut.m_channelCount = 0;
|
chMapOut.m_channelCount = 0;
|
||||||
|
@ -741,8 +854,8 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
while (chMapOut.m_channelCount < chCount)
|
while (chMapOut.m_channelCount < chCount)
|
||||||
chMapOut.m_channels[chMapOut.m_channelCount++] = AudioChannel::Unknown;
|
chMapOut.m_channels[chMapOut.m_channelCount++] = AudioChannel::Unknown;
|
||||||
|
|
||||||
m_mixInfo.m_periodFrames = m_5msFrames * 3;
|
m_mixInfo.m_periodFrames = m_5msFrames;
|
||||||
for (int i=0 ; i<3 ; ++i)
|
for (int i=0 ; i<AQS_NUM_BUFFERS ; ++i)
|
||||||
if (AudioQueueAllocateBuffer(m_queue, m_mixInfo.m_periodFrames * chCount * 4, &m_buffers[i]))
|
if (AudioQueueAllocateBuffer(m_queue, m_mixInfo.m_periodFrames * chCount * 4, &m_buffers[i]))
|
||||||
{
|
{
|
||||||
Log.report(logvisor::Fatal, "unable to create audio queue buffer");
|
Log.report(logvisor::Fatal, "unable to create audio queue buffer");
|
||||||
|
@ -753,7 +866,9 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
|
|
||||||
m_frameBytes = m_mixInfo.m_periodFrames * m_mixInfo.m_channelMap.m_channelCount * 4;
|
m_frameBytes = m_mixInfo.m_periodFrames * m_mixInfo.m_channelMap.m_channelCount * 4;
|
||||||
|
|
||||||
for (unsigned i=0 ; i<3 ; ++i)
|
_resetSampleRate();
|
||||||
|
|
||||||
|
for (unsigned i=0 ; i<AQS_NUM_BUFFERS ; ++i)
|
||||||
{
|
{
|
||||||
memset(m_buffers[i]->mAudioData, 0, m_frameBytes);
|
memset(m_buffers[i]->mAudioData, 0, m_frameBytes);
|
||||||
m_buffers[i]->mAudioDataByteSize = m_frameBytes;
|
m_buffers[i]->mAudioDataByteSize = m_frameBytes;
|
||||||
|
@ -761,6 +876,51 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
}
|
}
|
||||||
AudioQueuePrime(m_queue, 0, nullptr);
|
AudioQueuePrime(m_queue, 0, nullptr);
|
||||||
AudioQueueStart(m_queue, nullptr);
|
AudioQueueStart(m_queue, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static OSStatus AudioDeviceChanged(AudioObjectID inObjectID,
|
||||||
|
UInt32 inNumberAddresses,
|
||||||
|
const AudioObjectPropertyAddress* inAddresses,
|
||||||
|
AQSAudioVoiceEngine* engine)
|
||||||
|
{
|
||||||
|
AudioObjectID defaultDeviceId;
|
||||||
|
UInt32 argSize = sizeof(defaultDeviceId);
|
||||||
|
if (AudioObjectGetPropertyData(inObjectID, inAddresses, 0, NULL, &argSize, &defaultDeviceId) == noErr) {
|
||||||
|
argSize = sizeof(CFStringRef);
|
||||||
|
AudioObjectPropertyAddress deviceAddress;
|
||||||
|
deviceAddress.mSelector = kAudioDevicePropertyDeviceUID;
|
||||||
|
AudioObjectGetPropertyData(defaultDeviceId, &deviceAddress, 0, NULL, &argSize, &engine->m_devName);
|
||||||
|
}
|
||||||
|
engine->m_needsRebuild = true;
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
AQSAudioVoiceEngine()
|
||||||
|
: m_runLoopMode(CFPointer<CFStringRef>::adopt(
|
||||||
|
CFStringCreateWithCStringNoCopy(nullptr, "BooAQSMode", kCFStringEncodingUTF8, kCFAllocatorNull)))
|
||||||
|
{
|
||||||
|
AudioObjectPropertyAddress propertyAddress;
|
||||||
|
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
|
||||||
|
AudioObjectID defaultDeviceId;
|
||||||
|
UInt32 argSize = sizeof(defaultDeviceId);
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||||
|
if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &argSize, &defaultDeviceId) == noErr) {
|
||||||
|
argSize = sizeof(CFStringRef);
|
||||||
|
AudioObjectPropertyAddress deviceAddress;
|
||||||
|
deviceAddress.mSelector = kAudioDevicePropertyDeviceUID;
|
||||||
|
AudioObjectGetPropertyData(defaultDeviceId, &deviceAddress, 0, NULL, &argSize, &m_devName);
|
||||||
|
} else {
|
||||||
|
Log.report(logvisor::Fatal, "unable determine default audio device");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||||
|
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress,
|
||||||
|
AudioObjectPropertyListenerProc(AudioDeviceChanged), this);
|
||||||
|
|
||||||
|
_rebuildAudioQueue();
|
||||||
|
|
||||||
/* Also create shared MIDI client */
|
/* Also create shared MIDI client */
|
||||||
MIDIClientCreate(CFSTR("Boo MIDI"), nullptr, nullptr, &m_midiClient);
|
MIDIClientCreate(CFSTR("Boo MIDI"), nullptr, nullptr, &m_midiClient);
|
||||||
|
@ -769,50 +929,20 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
~AQSAudioVoiceEngine()
|
~AQSAudioVoiceEngine()
|
||||||
{
|
{
|
||||||
m_cbRunning = false;
|
m_cbRunning = false;
|
||||||
if (m_inCb)
|
|
||||||
m_engineEnterCv.notify_one();
|
|
||||||
AudioQueueDispose(m_queue, true);
|
AudioQueueDispose(m_queue, true);
|
||||||
if (m_midiClient)
|
if (m_midiClient)
|
||||||
MIDIClientDispose(m_midiClient);
|
MIDIClientDispose(m_midiClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is temperamental for AudioQueueServices
|
|
||||||
* (which has unpredictable buffering windows).
|
|
||||||
* _pumpAndMixVoicesRetrace() is highly recommended. */
|
|
||||||
void pumpAndMixVoices()
|
void pumpAndMixVoices()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(m_engineMutex);
|
while (CFRunLoopRunInMode(m_runLoopMode.get(), 0, true) == kCFRunLoopRunHandledSource) {}
|
||||||
if (m_inCb)
|
if (m_needsRebuild)
|
||||||
{
|
{
|
||||||
/* Wake up callback */
|
_rebuildAudioQueue();
|
||||||
m_engineEnterCv.notify_one();
|
m_needsRebuild = false;
|
||||||
/* Wait for callback completion */
|
|
||||||
m_engineLeaveCv.wait(lk);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _pumpAndMixVoicesRetrace()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lk(m_engineMutex);
|
|
||||||
m_inRetrace = true;
|
|
||||||
while (m_inRetrace)
|
|
||||||
{
|
|
||||||
if (m_inCb) /* Wake up callback */
|
|
||||||
m_engineEnterCv.notify_one();
|
|
||||||
/* Wait for callback completion */
|
|
||||||
m_engineLeaveCv.wait(lk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _retraceBreak()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lk(m_engineMutex);
|
|
||||||
m_inRetrace = false;
|
|
||||||
if (m_inCb) /* Break out of callback */
|
|
||||||
m_engineEnterCv.notify_one();
|
|
||||||
else /* Break out of client */
|
|
||||||
m_engineLeaveCv.notify_one();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<IAudioVoiceEngine> NewAudioVoiceEngine()
|
std::unique_ptr<IAudioVoiceEngine> NewAudioVoiceEngine()
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <IOKit/IOCFPlugIn.h>
|
#include <IOKit/IOCFPlugIn.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
#include "IOKitPointer.hpp"
|
#include "IOKitPointer.hpp"
|
||||||
#include "CFPointer.hpp"
|
#include "../CFPointer.hpp"
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
|
@ -182,9 +182,9 @@ class HIDListenerIOKit : public IHIDListener
|
||||||
|
|
||||||
/* Game controllers only */
|
/* Game controllers only */
|
||||||
CFPointer<CFNumberRef> usagePage;
|
CFPointer<CFNumberRef> usagePage;
|
||||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsagePageKey), &usagePage);
|
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsagePageKey), (CFTypeRef*)&usagePage);
|
||||||
CFPointer<CFNumberRef> usage;
|
CFPointer<CFNumberRef> usage;
|
||||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsageKey), &usage);
|
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsageKey), (CFTypeRef*)&usage);
|
||||||
int usagePageV, usageV;
|
int usagePageV, usageV;
|
||||||
CFNumberGetValue(usagePage.get(), kCFNumberIntType, &usagePageV);
|
CFNumberGetValue(usagePage.get(), kCFNumberIntType, &usagePageV);
|
||||||
CFNumberGetValue(usage.get(), kCFNumberIntType, &usageV);
|
CFNumberGetValue(usage.get(), kCFNumberIntType, &usageV);
|
||||||
|
@ -199,14 +199,14 @@ class HIDListenerIOKit : public IHIDListener
|
||||||
}
|
}
|
||||||
|
|
||||||
CFPointer<CFNumberRef> vid, pid;
|
CFPointer<CFNumberRef> vid, pid;
|
||||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDVendorIDKey), &vid);
|
dev->getProperty(dev.storage(), CFSTR(kIOHIDVendorIDKey), (CFTypeRef*)&vid);
|
||||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductIDKey), &pid);
|
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductIDKey), (CFTypeRef*)&pid);
|
||||||
CFNumberGetValue(vid.get(), kCFNumberIntType, &vidv);
|
CFNumberGetValue(vid.get(), kCFNumberIntType, &vidv);
|
||||||
CFNumberGetValue(pid.get(), kCFNumberIntType, &pidv);
|
CFNumberGetValue(pid.get(), kCFNumberIntType, &pidv);
|
||||||
|
|
||||||
CFPointer<CFStringRef> vstridx, pstridx;
|
CFPointer<CFStringRef> vstridx, pstridx;
|
||||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDManufacturerKey), &vstridx);
|
dev->getProperty(dev.storage(), CFSTR(kIOHIDManufacturerKey), (CFTypeRef*)&vstridx);
|
||||||
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductKey), &pstridx);
|
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductKey), (CFTypeRef*)&pstridx);
|
||||||
|
|
||||||
if (vstridx)
|
if (vstridx)
|
||||||
CFStringGetCString(vstridx.get(), vstr, 128, kCFStringEncodingUTF8);
|
CFStringGetCString(vstridx.get(), vstr, 128, kCFStringEncodingUTF8);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef __IOKITPOINTER_HPP__
|
#ifndef __IOKITPOINTER_HPP__
|
||||||
#define __IOKITPOINTER_HPP__
|
#define __IOKITPOINTER_HPP__
|
||||||
|
|
||||||
#include "CFPointer.hpp"
|
#include "../CFPointer.hpp"
|
||||||
#include <IOKit/IOTypes.h>
|
#include <IOKit/IOTypes.h>
|
||||||
#include <IOKit/IOKitLib.h>
|
#include <IOKit/IOKitLib.h>
|
||||||
#include <IOKit/IOCFPlugIn.h>
|
#include <IOKit/IOCFPlugIn.h>
|
||||||
|
|
|
@ -123,7 +123,6 @@ protected:
|
||||||
|
|
||||||
std::mutex m_dlmt;
|
std::mutex m_dlmt;
|
||||||
std::condition_variable m_dlcv;
|
std::condition_variable m_dlcv;
|
||||||
IAudioVoiceEngine* m_voxEngine = nullptr;
|
|
||||||
|
|
||||||
static CVReturn DLCallback(CVDisplayLinkRef displayLink,
|
static CVReturn DLCallback(CVDisplayLinkRef displayLink,
|
||||||
const CVTimeStamp * inNow,
|
const CVTimeStamp * inNow,
|
||||||
|
@ -133,10 +132,7 @@ protected:
|
||||||
GraphicsContextCocoa* ctx)
|
GraphicsContextCocoa* ctx)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(ctx->m_dlmt);
|
std::unique_lock<std::mutex> lk(ctx->m_dlmt);
|
||||||
if (ctx->m_voxEngine)
|
ctx->m_dlcv.notify_one();
|
||||||
ctx->m_voxEngine->_retraceBreak();
|
|
||||||
else
|
|
||||||
ctx->m_dlcv.notify_one();
|
|
||||||
return kCVReturnSuccess;
|
return kCVReturnSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,21 +148,10 @@ public:
|
||||||
|
|
||||||
std::recursive_mutex m_callbackMutex;
|
std::recursive_mutex m_callbackMutex;
|
||||||
IWindowCallback* m_callback = nullptr;
|
IWindowCallback* m_callback = nullptr;
|
||||||
void waitForRetrace(IAudioVoiceEngine* voxEngine)
|
void waitForRetrace()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(m_dlmt);
|
std::unique_lock<std::mutex> lk(m_dlmt);
|
||||||
if (voxEngine)
|
m_dlcv.wait(lk);
|
||||||
{
|
|
||||||
m_voxEngine = voxEngine;
|
|
||||||
lk.unlock();
|
|
||||||
voxEngine->_pumpAndMixVoicesRetrace();
|
|
||||||
lk.lock();
|
|
||||||
m_voxEngine = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_dlcv.wait(lk);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
virtual BooCocoaResponder* responder() const=0;
|
virtual BooCocoaResponder* responder() const=0;
|
||||||
};
|
};
|
||||||
|
@ -1570,9 +1555,9 @@ public:
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void waitForRetrace(IAudioVoiceEngine* voxEngine)
|
void waitForRetrace()
|
||||||
{
|
{
|
||||||
static_cast<GraphicsContextCocoa*>(m_gfxCtx)->waitForRetrace(voxEngine);
|
static_cast<GraphicsContextCocoa*>(m_gfxCtx)->waitForRetrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t getPlatformHandle() const
|
uintptr_t getPlatformHandle() const
|
||||||
|
|
Loading…
Reference in New Issue