Add iOS/tvOS support to audiodev

This commit is contained in:
Luke Street 2022-05-27 13:32:20 -04:00
parent 33cfcd8b63
commit 49364a152f
6 changed files with 634 additions and 39 deletions

View File

@ -184,6 +184,8 @@ elseif(WIN32)
elseif(APPLE) elseif(APPLE)
target_sources(boo PRIVATE target_sources(boo PRIVATE
lib/audiodev/AQS.cpp lib/audiodev/AQS.cpp
lib/audiodev/CADebugPrintf.cpp
lib/audiodev/CAHostTimeBase.cpp
${AudioMatrix_SRC} ${AudioMatrix_SRC}
lib/CFPointer.hpp lib/CFPointer.hpp
) )
@ -192,7 +194,11 @@ elseif(APPLE)
PROPERTIES COMPILE_FLAGS -fobjc-arc PROPERTIES COMPILE_FLAGS -fobjc-arc
) )
find_library(APPKIT_LIBRARY AppKit) if (IOS OR TVOS)
set(APPKIT_LIBRARY "")
else()
find_library(APPKIT_LIBRARY AppKit)
endif()
unset(BOO_HAS_METAL CACHE) unset(BOO_HAS_METAL CACHE)
if (NOT CMAKE_OSX_DEPLOYMENT_TARGET OR CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER 10.11) if (NOT CMAKE_OSX_DEPLOYMENT_TARGET OR CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER 10.11)
set(BOO_HAS_METAL ON CACHE BOOL "Metal is available in this OS X version" FORCE) set(BOO_HAS_METAL ON CACHE BOOL "Metal is available in this OS X version" FORCE)

View File

@ -1,10 +1,10 @@
#include "lib/audiodev/AudioVoiceEngine.hpp" #include "lib/audiodev/AudioVoiceEngine.hpp"
#include "lib/CFPointer.hpp" #include "lib/CFPointer.hpp"
#include "CAHostTimeBase.h"
#include <AudioToolbox/AudioToolbox.h> #include <AudioToolbox/AudioToolbox.h>
#include <CoreMIDI/CoreMIDI.h> #include <CoreMIDI/CoreMIDI.h>
#include <CoreAudio/HostTime.h>
#include <logvisor/logvisor.hpp> #include <logvisor/logvisor.hpp>
@ -61,6 +61,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
static void DummyCallback(AQSAudioVoiceEngine* engine, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {} static void DummyCallback(AQSAudioVoiceEngine* engine, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {}
std::pair<AudioChannelSet, Float64> _getAvailableSetAndRate() { std::pair<AudioChannelSet, Float64> _getAvailableSetAndRate() {
#if !TARGET_OS_IOS && !TARGET_OS_TV
AudioObjectPropertyAddress propertyAddress; AudioObjectPropertyAddress propertyAddress;
UInt32 argSize; UInt32 argSize;
int numStreams; int numStreams;
@ -78,7 +79,8 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
CFStringGetCStringPtr(devName, kCFStringEncodingUTF8)); CFStringGetCStringPtr(devName, kCFStringEncodingUTF8));
argSize = sizeof(devId); argSize = sizeof(devId);
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, nullptr, &argSize, &devId) == noErr) { if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, nullptr, &argSize, &devId) ==
noErr) {
argSize = sizeof(CFStringRef); argSize = sizeof(CFStringRef);
AudioObjectPropertyAddress deviceAddress; AudioObjectPropertyAddress deviceAddress;
deviceAddress.mSelector = kAudioDevicePropertyDeviceUID; deviceAddress.mSelector = kAudioDevicePropertyDeviceUID;
@ -126,11 +128,13 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
} }
} }
} }
#endif
return {AudioChannelSet::Unknown, 48000.0}; return {AudioChannelSet::Stereo, 48000.0};
} }
std::string getCurrentAudioOutput() const override { return CFStringGetCStringPtr(m_devName.get(), kCFStringEncodingUTF8); } std::string getCurrentAudioOutput() const override {
return CFStringGetCStringPtr(m_devName.get(), kCFStringEncodingUTF8);
}
bool setCurrentAudioOutput(const char* name) override { bool setCurrentAudioOutput(const char* name) override {
m_devName = CFPointer<CFStringRef>::adopt(CFStringCreateWithCString(nullptr, name, kCFStringEncodingUTF8)); m_devName = CFPointer<CFStringRef>::adopt(CFStringCreateWithCString(nullptr, name, kCFStringEncodingUTF8));
@ -143,7 +147,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
*/ */
std::vector<std::pair<std::string, std::string>> enumerateAudioOutputs() const override { std::vector<std::pair<std::string, std::string>> enumerateAudioOutputs() const override {
std::vector<std::pair<std::string, std::string>> ret; std::vector<std::pair<std::string, std::string>> ret;
#if !TARGET_OS_IOS && !TARGET_OS_TV
AudioObjectPropertyAddress propertyAddress; AudioObjectPropertyAddress propertyAddress;
std::vector<AudioObjectID> deviceIDs; std::vector<AudioObjectID> deviceIDs;
UInt32 propertySize; UInt32 propertySize;
@ -155,7 +159,8 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
propertyAddress.mSelector = kAudioHardwarePropertyDevices; propertyAddress.mSelector = kAudioHardwarePropertyDevices;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal; propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster; propertyAddress.mElement = kAudioObjectPropertyElementMaster;
if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, nullptr, &propertySize) == noErr) { if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, nullptr, &propertySize) ==
noErr) {
numDevices = propertySize / sizeof(AudioDeviceID); numDevices = propertySize / sizeof(AudioDeviceID);
ret.reserve(numDevices); ret.reserve(numDevices);
deviceIDs.resize(numDevices); deviceIDs.resize(numDevices);
@ -170,15 +175,15 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
numStreams = propertySize / sizeof(AudioStreamID); numStreams = propertySize / sizeof(AudioStreamID);
streamIDs.resize(numStreams); streamIDs.resize(numStreams);
if (AudioObjectGetPropertyData(deviceIDs[idx], &propertyAddress, 0, nullptr, &propertySize, &streamIDs[0]) == if (AudioObjectGetPropertyData(deviceIDs[idx], &propertyAddress, 0, nullptr, &propertySize,
noErr) { &streamIDs[0]) == noErr) {
propertyAddress.mSelector = kAudioStreamPropertyDirection; propertyAddress.mSelector = kAudioStreamPropertyDirection;
bool foundOutput = false; bool foundOutput = false;
for (int stm = 0; stm < numStreams; stm++) { for (int stm = 0; stm < numStreams; stm++) {
UInt32 streamDir; UInt32 streamDir;
propertySize = sizeof(streamDir); propertySize = sizeof(streamDir);
if (AudioObjectGetPropertyData(streamIDs[stm], &propertyAddress, 0, nullptr, &propertySize, &streamDir) == if (AudioObjectGetPropertyData(streamIDs[stm], &propertyAddress, 0, nullptr, &propertySize,
noErr) { &streamDir) == noErr) {
if (streamDir == 0) { if (streamDir == 0) {
foundOutput = true; foundOutput = true;
break; break;
@ -206,7 +211,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
} }
} }
} }
#endif
return ret; return ret;
} }
@ -215,7 +220,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
return {}; return {};
std::vector<std::pair<std::string, std::string>> ret; std::vector<std::pair<std::string, std::string>> ret;
#if !TARGET_OS_TV
ItemCount numDevices = MIDIGetNumberOfDevices(); ItemCount numDevices = MIDIGetNumberOfDevices();
ret.reserve(numDevices); ret.reserve(numDevices);
for (int i = int(numDevices) - 1; i >= 0; --i) { for (int i = int(numDevices) - 1; i >= 0; --i) {
@ -252,13 +257,14 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
ret.push_back(std::make_pair(fmt::format(FMT_STRING("{:08X}"), idNum), std::string(nameCstr))); ret.push_back(std::make_pair(fmt::format(FMT_STRING("{:08X}"), idNum), std::string(nameCstr)));
} }
#endif
return ret; return ret;
} }
bool supportsVirtualMIDIIn() const override { return true; } bool supportsVirtualMIDIIn() const override { return true; }
static MIDIDeviceRef LookupMIDIDevice(const char* name) { static MIDIDeviceRef LookupMIDIDevice(const char* name) {
#if !TARGET_OS_TV
ItemCount numDevices = MIDIGetNumberOfDevices(); ItemCount numDevices = MIDIGetNumberOfDevices();
for (ItemCount i = 0; i < numDevices; ++i) { for (ItemCount i = 0; i < numDevices; ++i) {
MIDIDeviceRef dev = MIDIGetDevice(i); MIDIDeviceRef dev = MIDIGetDevice(i);
@ -274,7 +280,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
return dev; return dev;
} }
#endif
return {}; return {};
} }
@ -282,7 +288,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
MIDIDeviceRef dev = LookupMIDIDevice(name); MIDIDeviceRef dev = LookupMIDIDevice(name);
if (!dev) if (!dev)
return {}; return {};
#if !TARGET_OS_TV
ItemCount numEnt = MIDIDeviceGetNumberOfEntities(dev); ItemCount numEnt = MIDIDeviceGetNumberOfEntities(dev);
for (ItemCount i = 0; i < numEnt; ++i) { for (ItemCount i = 0; i < numEnt; ++i) {
MIDIEntityRef ent = MIDIDeviceGetEntity(dev, i); MIDIEntityRef ent = MIDIDeviceGetEntity(dev, i);
@ -295,7 +301,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
} }
} }
} }
#endif
return {}; return {};
} }
@ -303,7 +309,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
MIDIDeviceRef dev = LookupMIDIDevice(name); MIDIDeviceRef dev = LookupMIDIDevice(name);
if (!dev) if (!dev)
return {}; return {};
#if !TARGET_OS_TV
ItemCount numEnt = MIDIDeviceGetNumberOfEntities(dev); ItemCount numEnt = MIDIDeviceGetNumberOfEntities(dev);
for (ItemCount i = 0; i < numEnt; ++i) { for (ItemCount i = 0; i < numEnt; ++i) {
MIDIEntityRef ent = MIDIDeviceGetEntity(dev, i); MIDIEntityRef ent = MIDIDeviceGetEntity(dev, i);
@ -316,7 +322,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
} }
} }
} }
#endif
return {}; return {};
} }
@ -324,7 +330,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
const MIDIPacket* packet = &pktlist->packet[0]; const MIDIPacket* packet = &pktlist->packet[0];
for (int i = 0; i < pktlist->numPackets; ++i) { for (int i = 0; i < pktlist->numPackets; ++i) {
std::vector<uint8_t> bytes(std::cbegin(packet->data), std::cbegin(packet->data) + packet->length); std::vector<uint8_t> bytes(std::cbegin(packet->data), std::cbegin(packet->data) + packet->length);
readProcRefCon->m_receiver(std::move(bytes), AudioConvertHostTimeToNanos(packet->timeStamp) / 1.0e9); readProcRefCon->m_receiver(std::move(bytes), CAHostTimeBase::ConvertToNanos(packet->timeStamp) / 1.0e9);
packet = MIDIPacketNext(packet); packet = MIDIPacketNext(packet);
} }
} }
@ -337,22 +343,27 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
: IMIDIIn(parent, virt, std::move(receiver)) {} : IMIDIIn(parent, virt, std::move(receiver)) {}
~MIDIIn() override { ~MIDIIn() override {
#if !TARGET_OS_TV
if (m_midi) if (m_midi)
MIDIEndpointDispose(m_midi); MIDIEndpointDispose(m_midi);
if (m_midiPort) if (m_midiPort)
MIDIPortDispose(m_midiPort); MIDIPortDispose(m_midiPort);
#endif
} }
std::string description() const override { std::string description() const override {
CFPointer<CFStringRef> namestr; CFPointer<CFStringRef> namestr;
#if !TARGET_OS_TV
const char* nameCstr; const char* nameCstr;
if (MIDIObjectGetStringProperty(m_midi, kMIDIPropertyName, &namestr)) if (MIDIObjectGetStringProperty(m_midi, kMIDIPropertyName, &namestr))
return {}; return {};
if (!(nameCstr = CFStringGetCStringPtr(namestr.get(), kCFStringEncodingUTF8))) if (!(nameCstr = CFStringGetCStringPtr(namestr.get(), kCFStringEncodingUTF8)))
return {}; return {};
return nameCstr; return nameCstr;
#else
return {};
#endif
} }
}; };
@ -363,31 +374,37 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
MIDIOut(AQSAudioVoiceEngine* parent, bool virt) : IMIDIOut(parent, virt) {} MIDIOut(AQSAudioVoiceEngine* parent, bool virt) : IMIDIOut(parent, virt) {}
~MIDIOut() override { ~MIDIOut() override {
#if !TARGET_OS_TV
if (m_midi) if (m_midi)
MIDIEndpointDispose(m_midi); MIDIEndpointDispose(m_midi);
if (m_midiPort) if (m_midiPort)
MIDIPortDispose(m_midiPort); MIDIPortDispose(m_midiPort);
#endif
} }
std::string description() const override { std::string description() const override {
CFPointer<CFStringRef> namestr; CFPointer<CFStringRef> namestr;
#if !TARGET_OS_TV
const char* nameCstr; const char* nameCstr;
if (MIDIObjectGetStringProperty(m_midi, kMIDIPropertyName, &namestr)) if (MIDIObjectGetStringProperty(m_midi, kMIDIPropertyName, &namestr))
return {}; return {};
if (!(nameCstr = CFStringGetCStringPtr(namestr.get(), kCFStringEncodingUTF8))) if (!(nameCstr = CFStringGetCStringPtr(namestr.get(), kCFStringEncodingUTF8)))
return {}; return {};
return nameCstr; return nameCstr;
#else
return {};
#endif
} }
size_t send(const void* buf, size_t len) const override { size_t send(const void* buf, size_t len) const override {
#if !TARGET_OS_TV
union { union {
MIDIPacketList head; MIDIPacketList head;
Byte storage[512]; Byte storage[512];
} list; } list;
MIDIPacket* curPacket = MIDIPacketListInit(&list.head); MIDIPacket* curPacket = MIDIPacketListInit(&list.head);
if (MIDIPacketListAdd(&list.head, sizeof(list), curPacket, AudioGetCurrentHostTime(), len, if (MIDIPacketListAdd(&list.head, sizeof(list), curPacket, CAHostTimeBase::GetTheCurrentTime(), len,
reinterpret_cast<const Byte*>(buf))) { reinterpret_cast<const Byte*>(buf))) {
if (m_midiPort) if (m_midiPort)
MIDISend(m_midiPort, m_midi, &list.head); MIDISend(m_midiPort, m_midi, &list.head);
@ -395,6 +412,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
MIDIReceived(m_midi, &list.head); MIDIReceived(m_midi, &list.head);
return len; return len;
} }
#endif
return 0; return 0;
} }
}; };
@ -409,6 +427,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
: IMIDIInOut(parent, virt, std::move(receiver)) {} : IMIDIInOut(parent, virt, std::move(receiver)) {}
~MIDIInOut() override { ~MIDIInOut() override {
#if !TARGET_OS_TV
if (m_midiIn) if (m_midiIn)
MIDIEndpointDispose(m_midiIn); MIDIEndpointDispose(m_midiIn);
if (m_midiPortIn) if (m_midiPortIn)
@ -417,27 +436,32 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
MIDIEndpointDispose(m_midiOut); MIDIEndpointDispose(m_midiOut);
if (m_midiPortOut) if (m_midiPortOut)
MIDIPortDispose(m_midiPortOut); MIDIPortDispose(m_midiPortOut);
#endif
} }
std::string description() const override { std::string description() const override {
CFPointer<CFStringRef> namestr; CFPointer<CFStringRef> namestr;
#if !TARGET_OS_TV
const char* nameCstr; const char* nameCstr;
if (MIDIObjectGetStringProperty(m_midiIn, kMIDIPropertyName, &namestr)) if (MIDIObjectGetStringProperty(m_midiIn, kMIDIPropertyName, &namestr))
return {}; return {};
if (!(nameCstr = CFStringGetCStringPtr(namestr.get(), kCFStringEncodingUTF8))) if (!(nameCstr = CFStringGetCStringPtr(namestr.get(), kCFStringEncodingUTF8)))
return {}; return {};
return nameCstr; return nameCstr;
#else
return {};
#endif
} }
size_t send(const void* buf, size_t len) const override { size_t send(const void* buf, size_t len) const override {
#if !TARGET_OS_TV
union { union {
MIDIPacketList head; MIDIPacketList head;
Byte storage[512]; Byte storage[512];
} list; } list;
MIDIPacket* curPacket = MIDIPacketListInit(&list.head); MIDIPacket* curPacket = MIDIPacketListInit(&list.head);
if (MIDIPacketListAdd(&list.head, sizeof(list), curPacket, AudioGetCurrentHostTime(), len, if (MIDIPacketListAdd(&list.head, sizeof(list), curPacket, CAHostTimeBase::GetTheCurrentTime(), len,
reinterpret_cast<const Byte*>(buf))) { reinterpret_cast<const Byte*>(buf))) {
if (m_midiPortOut) if (m_midiPortOut)
MIDISend(m_midiPortOut, m_midiOut, &list.head); MIDISend(m_midiPortOut, m_midiOut, &list.head);
@ -445,6 +469,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
MIDIReceived(m_midiOut, &list.head); MIDIReceived(m_midiOut, &list.head);
return len; return len;
} }
#endif
return 0; return 0;
} }
}; };
@ -468,11 +493,12 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
m_midiInCounter++; m_midiInCounter++;
CFPointer<CFStringRef> midiName = CFPointer<CFStringRef>::adopt( CFPointer<CFStringRef> midiName = CFPointer<CFStringRef>::adopt(
CFStringCreateWithCStringNoCopy(nullptr, name.c_str(), kCFStringEncodingUTF8, kCFAllocatorNull)); CFStringCreateWithCStringNoCopy(nullptr, name.c_str(), kCFStringEncodingUTF8, kCFAllocatorNull));
#if !TARGET_OS_TV
OSStatus stat; OSStatus stat;
if ((stat = MIDIDestinationCreate(m_midiClient, midiName.get(), MIDIReadProc(MIDIReceiveProc), if ((stat = MIDIDestinationCreate(m_midiClient, midiName.get(), MIDIReadProc(MIDIReceiveProc),
static_cast<IMIDIReceiver*>(ret.get()), &static_cast<MIDIIn&>(*ret).m_midi))) static_cast<IMIDIReceiver*>(ret.get()), &static_cast<MIDIIn&>(*ret).m_midi)))
ret.reset(); ret.reset();
#endif
return ret; return ret;
} }
@ -492,9 +518,10 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
m_midiOutCounter++; m_midiOutCounter++;
CFPointer<CFStringRef> midiName = CFPointer<CFStringRef>::adopt( CFPointer<CFStringRef> midiName = CFPointer<CFStringRef>::adopt(
CFStringCreateWithCStringNoCopy(nullptr, name.c_str(), kCFStringEncodingUTF8, kCFAllocatorNull)); CFStringCreateWithCStringNoCopy(nullptr, name.c_str(), kCFStringEncodingUTF8, kCFAllocatorNull));
#if !TARGET_OS_TV
if (MIDISourceCreate(m_midiClient, midiName.get(), &static_cast<MIDIOut&>(*ret).m_midi)) if (MIDISourceCreate(m_midiClient, midiName.get(), &static_cast<MIDIOut&>(*ret).m_midi))
ret.reset(); ret.reset();
#endif
return ret; return ret;
} }
@ -514,10 +541,11 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
m_midiInCounter++; m_midiInCounter++;
CFPointer<CFStringRef> midiName = CFPointer<CFStringRef>::adopt( CFPointer<CFStringRef> midiName = CFPointer<CFStringRef>::adopt(
CFStringCreateWithCStringNoCopy(nullptr, name.c_str(), kCFStringEncodingUTF8, kCFAllocatorNull)); CFStringCreateWithCStringNoCopy(nullptr, name.c_str(), kCFStringEncodingUTF8, kCFAllocatorNull));
#if !TARGET_OS_TV
if (MIDIDestinationCreate(m_midiClient, midiName.get(), MIDIReadProc(MIDIReceiveProc), if (MIDIDestinationCreate(m_midiClient, midiName.get(), MIDIReadProc(MIDIReceiveProc),
static_cast<IMIDIReceiver*>(ret.get()), &static_cast<MIDIInOut&>(*ret).m_midiIn)) static_cast<IMIDIReceiver*>(ret.get()), &static_cast<MIDIInOut&>(*ret).m_midiIn))
ret.reset(); ret.reset();
#endif
if (!ret) if (!ret)
return {}; return {};
@ -528,9 +556,10 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
m_midiOutCounter++; m_midiOutCounter++;
midiName = CFPointer<CFStringRef>::adopt( midiName = CFPointer<CFStringRef>::adopt(
CFStringCreateWithCStringNoCopy(nullptr, name.c_str(), kCFStringEncodingUTF8, kCFAllocatorNull)); CFStringCreateWithCStringNoCopy(nullptr, name.c_str(), kCFStringEncodingUTF8, kCFAllocatorNull));
#if !TARGET_OS_TV
if (MIDISourceCreate(m_midiClient, midiName.get(), &static_cast<MIDIInOut&>(*ret).m_midiOut)) if (MIDISourceCreate(m_midiClient, midiName.get(), &static_cast<MIDIInOut&>(*ret).m_midiOut))
ret.reset(); ret.reset();
#endif
return ret; return ret;
} }
@ -538,6 +567,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
if (!m_midiClient) if (!m_midiClient)
return {}; return {};
#if !TARGET_OS_TV
MIDIEndpointRef src = LookupMIDISource(name); MIDIEndpointRef src = LookupMIDISource(name);
if (!src) if (!src)
return {}; return {};
@ -556,12 +586,16 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
MIDIPortConnectSource(static_cast<MIDIIn&>(*ret).m_midiPort, src, nullptr); MIDIPortConnectSource(static_cast<MIDIIn&>(*ret).m_midiPort, src, nullptr);
return ret; return ret;
} #else
return {};
#endif
};
std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name) override { std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name) override {
if (!m_midiClient) if (!m_midiClient)
return {}; return {};
#if !TARGET_OS_TV
MIDIEndpointRef dst = LookupMIDIDest(name); MIDIEndpointRef dst = LookupMIDIDest(name);
if (!dst) if (!dst)
return {}; return {};
@ -579,12 +613,16 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
static_cast<MIDIOut&>(*ret).m_midi = dst; static_cast<MIDIOut&>(*ret).m_midi = dst;
return ret; return ret;
} #else
return {};
#endif
};
std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver) override { std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver) override {
if (!m_midiClient) if (!m_midiClient)
return {}; return {};
#if !TARGET_OS_TV
MIDIEndpointRef src = LookupMIDISource(name); MIDIEndpointRef src = LookupMIDISource(name);
if (!src) if (!src)
return {}; return {};
@ -618,7 +656,10 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
static_cast<MIDIInOut&>(*ret).m_midiOut = dst; static_cast<MIDIInOut&>(*ret).m_midiOut = dst;
return ret; return ret;
} #else
return {};
#endif
};
bool useMIDILock() const override { return true; } bool useMIDILock() const override { return true; }
@ -651,15 +692,17 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
OSStatus err; OSStatus err;
if ((err = AudioQueueNewOutput(&desc, AudioQueueOutputCallback(Callback), this, CFRunLoopGetCurrent(), if ((err = AudioQueueNewOutput(&desc, AudioQueueOutputCallback(Callback), this, CFRunLoopGetCurrent(),
m_runLoopMode.get(), 0, &m_queue))) { m_runLoopMode.get(), 0, &m_queue))) {
Log.report(logvisor::Fatal, FMT_STRING("unable to create output audio queue")); Log.report(logvisor::Fatal, FMT_STRING("unable to create output audio queue: {}"), err);
return; return;
} }
#if !TARGET_OS_IOS && !TARGET_OS_TV
CFStringRef devName = m_devName.get(); CFStringRef devName = m_devName.get();
if ((err = AudioQueueSetProperty(m_queue, kAudioQueueProperty_CurrentDevice, &devName, sizeof(devName)))) { if ((err = AudioQueueSetProperty(m_queue, kAudioQueueProperty_CurrentDevice, &devName, sizeof(devName)))) {
Log.report(logvisor::Fatal, FMT_STRING("unable to set current device into audio queue")); Log.report(logvisor::Fatal, FMT_STRING("unable to set current device into audio queue"));
return; return;
} }
#endif
AudioQueueAddPropertyListener(m_queue, kAudioQueueDeviceProperty_SampleRate, AudioQueueAddPropertyListener(m_queue, kAudioQueueDeviceProperty_SampleRate,
AudioQueuePropertyListenerProc(SampleRateChanged), this); AudioQueuePropertyListenerProc(SampleRateChanged), this);
@ -675,7 +718,8 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
AudioChannelLayout layout; AudioChannelLayout layout;
UInt32 layoutSz = sizeof(layout); UInt32 layoutSz = sizeof(layout);
if (AudioQueueGetProperty(m_queue, kAudioQueueProperty_ChannelLayout, &layout, &layoutSz)) { if (AudioQueueGetProperty(m_queue, kAudioQueueProperty_ChannelLayout, &layout, &layoutSz)) {
Log.report(logvisor::Warning, FMT_STRING("unable to get channel layout from audio queue; using count's default")); Log.report(logvisor::Warning,
FMT_STRING("unable to get channel layout from audio queue; using count's default"));
switch (m_mixInfo.m_channels) { switch (m_mixInfo.m_channels) {
case AudioChannelSet::Stereo: case AudioChannelSet::Stereo:
default: default:
@ -756,7 +800,8 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
chMapOut.m_channels[4] = AudioChannel::FrontCenter; chMapOut.m_channels[4] = AudioChannel::FrontCenter;
break; break;
default: default:
Log.report(logvisor::Warning, FMT_STRING("unknown channel layout {}; using stereo"), layout.mChannelLayoutTag); Log.report(logvisor::Warning, FMT_STRING("unknown channel layout {}; using stereo"),
layout.mChannelLayoutTag);
chMapOut.m_channelCount = 2; chMapOut.m_channelCount = 2;
chMapOut.m_channels[0] = AudioChannel::FrontLeft; chMapOut.m_channels[0] = AudioChannel::FrontLeft;
chMapOut.m_channels[1] = AudioChannel::FrontRight; chMapOut.m_channels[1] = AudioChannel::FrontRight;
@ -793,6 +838,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
AudioQueueStart(m_queue, nullptr); AudioQueueStart(m_queue, nullptr);
} }
#if !TARGET_OS_IOS && !TARGET_OS_TV
static OSStatus AudioDeviceChanged(AudioObjectID inObjectID, UInt32 inNumberAddresses, static OSStatus AudioDeviceChanged(AudioObjectID inObjectID, UInt32 inNumberAddresses,
const AudioObjectPropertyAddress* inAddresses, AQSAudioVoiceEngine* engine) { const AudioObjectPropertyAddress* inAddresses, AQSAudioVoiceEngine* engine) {
AudioObjectID defaultDeviceId; AudioObjectID defaultDeviceId;
@ -806,11 +852,13 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
engine->m_needsRebuild = true; engine->m_needsRebuild = true;
return noErr; return noErr;
} }
#endif
AQSAudioVoiceEngine(const char* friendlyName) AQSAudioVoiceEngine(const char* friendlyName)
: m_runLoopMode(CFPointer<CFStringRef>::adopt( : m_runLoopMode(CFPointer<CFStringRef>::adopt(
CFStringCreateWithCStringNoCopy(nullptr, "BooAQSMode", kCFStringEncodingUTF8, kCFAllocatorNull))) CFStringCreateWithCStringNoCopy(nullptr, "BooAQSMode", kCFStringEncodingUTF8, kCFAllocatorNull)))
, m_friendlyName(friendlyName) { , m_friendlyName(friendlyName) {
#if !TARGET_OS_IOS && !TARGET_OS_TV && false
AudioObjectPropertyAddress propertyAddress; AudioObjectPropertyAddress propertyAddress;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal; propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster; propertyAddress.mElement = kAudioObjectPropertyElementMaster;
@ -818,8 +866,8 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
AudioObjectID defaultDeviceId; AudioObjectID defaultDeviceId;
UInt32 argSize = sizeof(defaultDeviceId); UInt32 argSize = sizeof(defaultDeviceId);
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, nullptr, &argSize, &defaultDeviceId) == if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, nullptr, &argSize,
noErr) { &defaultDeviceId) == noErr) {
argSize = sizeof(CFStringRef); argSize = sizeof(CFStringRef);
AudioObjectPropertyAddress deviceAddress; AudioObjectPropertyAddress deviceAddress;
deviceAddress.mSelector = kAudioDevicePropertyDeviceUID; deviceAddress.mSelector = kAudioDevicePropertyDeviceUID;
@ -832,18 +880,22 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine {
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress,
AudioObjectPropertyListenerProc(AudioDeviceChanged), this); AudioObjectPropertyListenerProc(AudioDeviceChanged), this);
#endif
_rebuildAudioQueue(); _rebuildAudioQueue();
#if !TARGET_OS_TV
/* 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);
#endif
} }
~AQSAudioVoiceEngine() override { ~AQSAudioVoiceEngine() override {
m_cbRunning = false; m_cbRunning = false;
AudioQueueDispose(m_queue, true); AudioQueueDispose(m_queue, true);
#if !TARGET_OS_TV
if (m_midiClient) if (m_midiClient)
MIDIClientDispose(m_midiClient); MIDIClientDispose(m_midiClient);
#endif
} }
void pumpAndMixVoices() override { void pumpAndMixVoices() override {

View File

@ -0,0 +1,89 @@
/*
File: CADebugPrintf.cpp
Abstract: CADebugPrintf.h
Version: 1.1
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
//==================================================================================================
// Includes
//==================================================================================================
// Self Include
#include "CADebugPrintf.h"
#if DEBUG || CoreAudio_Debug
#if TARGET_OS_WIN32
#include <stdarg.h>
#include <stdio.h>
#include <Windows.h>
extern "C"
int CAWin32DebugPrintf(char* inFormat, ...)
{
char theMessage[1024];
va_list theArguments;
va_start(theArguments, inFormat);
_vsnprintf(theMessage, 1024, inFormat, theArguments);
va_end(theArguments);
OutputDebugString(theMessage);
return 0;
}
#endif
#if defined(CoreAudio_UseSideFile)
#include <unistd.h>
FILE* sDebugPrintfSideFile = NULL;
extern "C"
void OpenDebugPrintfSideFile()
{
if(sDebugPrintfSideFile == NULL)
{
char theFileName[1024];
snprintf(theFileName, sizeof(theFileName), CoreAudio_UseSideFile, getpid());
sDebugPrintfSideFile = fopen(theFileName, "a+");
DebugPrintfRtn(DebugPrintfFileComma "\n------------------------------\n");
}
}
#endif
#endif

View File

@ -0,0 +1,115 @@
/*
File: CADebugPrintf.h
Abstract: Part of CoreAudio Utility Classes
Version: 1.1
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
#if !defined(__CADebugPrintf_h__)
#define __CADebugPrintf_h__
//=============================================================================
// Includes
//=============================================================================
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
#include <CoreAudio/CoreAudioTypes.h>
#else
#include "CoreAudioTypes.h"
#endif
//=============================================================================
// Macros to redirect debugging output to various logging services
//=============================================================================
//#define CoreAudio_UseSysLog 1
//#define CoreAudio_UseSideFile "/CoreAudio-%d.txt"
#if DEBUG || CoreAudio_Debug
#if TARGET_OS_WIN32
#if defined(__cplusplus)
extern "C"
#endif
extern int CAWin32DebugPrintf(char* inFormat, ...);
#define DebugPrintfRtn CAWin32DebugPrintf
#define DebugPrintfFile
#define DebugPrintfLineEnding "\n"
#define DebugPrintfFileComma
#else
#if CoreAudio_UseSysLog
#include <sys/syslog.h>
#define DebugPrintfRtn syslog
#define DebugPrintfFile LOG_NOTICE
#define DebugPrintfLineEnding ""
#define DebugPrintfFileComma DebugPrintfFile,
#elif defined(CoreAudio_UseSideFile)
#include <stdio.h>
#if defined(__cplusplus)
extern "C"
#endif
void OpenDebugPrintfSideFile();
extern FILE* sDebugPrintfSideFile;
#define DebugPrintfRtn fprintf
#define DebugPrintfFile ((sDebugPrintfSideFile != NULL) ? sDebugPrintfSideFile : stderr)
#define DebugPrintfLineEnding "\n"
#define DebugPrintfFileComma DebugPrintfFile,
#else
#include <stdio.h>
#define DebugPrintfRtn fprintf
#define DebugPrintfFile stderr
#define DebugPrintfLineEnding "\n"
#define DebugPrintfFileComma DebugPrintfFile,
#endif
#endif
#define DebugPrintf(inFormat, ...) DebugPrintfRtn(DebugPrintfFileComma inFormat DebugPrintfLineEnding, ## __VA_ARGS__)
#else
#define DebugPrintfRtn
#define DebugPrintfFile
#define DebugPrintfLineEnding
#define DebugPrintfFileComma
#define DebugPrintf(inFormat, ...)
#endif
#endif

View File

@ -0,0 +1,99 @@
/*
File: CAHostTimeBase.cpp
Abstract: CAHostTimeBase.h
Version: 1.1
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
//=============================================================================
// Includes
//=============================================================================
#include "CAHostTimeBase.h"
Float64 CAHostTimeBase::sFrequency = 0;
Float64 CAHostTimeBase::sInverseFrequency = 0;
UInt32 CAHostTimeBase::sMinDelta = 0;
UInt32 CAHostTimeBase::sToNanosNumerator = 0;
UInt32 CAHostTimeBase::sToNanosDenominator = 0;
pthread_once_t CAHostTimeBase::sIsInited = PTHREAD_ONCE_INIT;
#if Track_Host_TimeBase
UInt64 CAHostTimeBase::sLastTime = 0;
#endif
//=============================================================================
// CAHostTimeBase
//
// This class provides platform independent access to the host's time base.
//=============================================================================
void CAHostTimeBase::Initialize()
{
// get the info about Absolute time
#if TARGET_OS_MAC
struct mach_timebase_info theTimeBaseInfo;
mach_timebase_info(&theTimeBaseInfo);
sMinDelta = 1;
sToNanosNumerator = theTimeBaseInfo.numer;
sToNanosDenominator = theTimeBaseInfo.denom;
// the frequency of that clock is: (sToNanosDenominator / sToNanosNumerator) * 10^9
sFrequency = static_cast<Float64>(sToNanosDenominator) / static_cast<Float64>(sToNanosNumerator);
sFrequency *= 1000000000.0;
#elif TARGET_OS_WIN32
LARGE_INTEGER theFrequency;
QueryPerformanceFrequency(&theFrequency);
sMinDelta = 1;
sToNanosNumerator = 1000000000ULL;
sToNanosDenominator = *((UInt64*)&theFrequency);
sFrequency = static_cast<Float64>(*((UInt64*)&theFrequency));
#endif
sInverseFrequency = 1.0 / sFrequency;
#if Log_Host_Time_Base_Parameters
DebugPrintf("Host Time Base Parameters");
DebugPrintf(" Minimum Delta: %lu", (unsigned long)sMinDelta);
DebugPrintf(" Frequency: %f", sFrequency);
DebugPrintf(" To Nanos Numerator: %lu", (unsigned long)sToNanosNumerator);
DebugPrintf(" To Nanos Denominator: %lu", (unsigned long)sToNanosDenominator);
#endif
}

View File

@ -0,0 +1,234 @@
/*
File: CAHostTimeBase.h
Abstract: Part of CoreAudio Utility Classes
Version: 1.1
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
#if !defined(__CAHostTimeBase_h__)
#define __CAHostTimeBase_h__
//=============================================================================
// Includes
//=============================================================================
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
#include <CoreAudio/CoreAudioTypes.h>
#else
#include <CoreAudioTypes.h>
#endif
#if TARGET_OS_MAC
#include <mach/mach_time.h>
#include <pthread.h>
#elif TARGET_OS_WIN32
#include <windows.h>
#include "WinPThreadDefs.h"
#else
#error Unsupported operating system
#endif
#include "CADebugPrintf.h"
//=============================================================================
// CAHostTimeBase
//
// This class provides platform independent access to the host's time base.
//=============================================================================
#if CoreAudio_Debug
// #define Log_Host_Time_Base_Parameters 1
// #define Track_Host_TimeBase 1
#endif
class CAHostTimeBase
{
public:
static UInt64 ConvertToNanos(UInt64 inHostTime);
static UInt64 ConvertFromNanos(UInt64 inNanos);
static UInt64 GetTheCurrentTime();
#if TARGET_OS_MAC
static UInt64 GetCurrentTime() { return GetTheCurrentTime(); }
#endif
static UInt64 GetCurrentTimeInNanos();
static Float64 GetFrequency() { pthread_once(&sIsInited, Initialize); return sFrequency; }
static Float64 GetInverseFrequency() { pthread_once(&sIsInited, Initialize); return sInverseFrequency; }
static UInt32 GetMinimumDelta() { pthread_once(&sIsInited, Initialize); return sMinDelta; }
static UInt64 AbsoluteHostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime);
static SInt64 HostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime);
static UInt64 MultiplyByRatio(UInt64 inMuliplicand, UInt32 inNumerator, UInt32 inDenominator);
private:
static void Initialize();
static pthread_once_t sIsInited;
static Float64 sFrequency;
static Float64 sInverseFrequency;
static UInt32 sMinDelta;
static UInt32 sToNanosNumerator;
static UInt32 sToNanosDenominator;
#if Track_Host_TimeBase
static UInt64 sLastTime;
#endif
};
inline UInt64 CAHostTimeBase::GetTheCurrentTime()
{
UInt64 theTime = 0;
#if TARGET_OS_MAC
theTime = mach_absolute_time();
#elif TARGET_OS_WIN32
LARGE_INTEGER theValue;
QueryPerformanceCounter(&theValue);
theTime = *((UInt64*)&theValue);
#endif
#if Track_Host_TimeBase
if(sLastTime != 0)
{
if(theTime <= sLastTime)
{
DebugPrintf("CAHostTimeBase::GetTheCurrentTime: the current time is earlier than the last time, now: %qd, then: %qd", theTime, sLastTime);
}
sLastTime = theTime;
}
else
{
sLastTime = theTime;
}
#endif
return theTime;
}
inline UInt64 CAHostTimeBase::ConvertToNanos(UInt64 inHostTime)
{
pthread_once(&sIsInited, Initialize);
UInt64 theAnswer = MultiplyByRatio(inHostTime, sToNanosNumerator, sToNanosDenominator);
#if CoreAudio_Debug
if(((sToNanosNumerator > sToNanosDenominator) && (theAnswer < inHostTime)) || ((sToNanosDenominator > sToNanosNumerator) && (theAnswer > inHostTime)))
{
DebugPrintf("CAHostTimeBase::ConvertToNanos: The conversion wrapped");
}
#endif
return theAnswer;
}
inline UInt64 CAHostTimeBase::ConvertFromNanos(UInt64 inNanos)
{
pthread_once(&sIsInited, Initialize);
UInt64 theAnswer = MultiplyByRatio(inNanos, sToNanosDenominator, sToNanosNumerator);
#if CoreAudio_Debug
if(((sToNanosDenominator > sToNanosNumerator) && (theAnswer < inNanos)) || ((sToNanosNumerator > sToNanosDenominator) && (theAnswer > inNanos)))
{
DebugPrintf("CAHostTimeBase::ConvertFromNanos: The conversion wrapped");
}
#endif
return theAnswer;
}
inline UInt64 CAHostTimeBase::GetCurrentTimeInNanos()
{
return ConvertToNanos(GetTheCurrentTime());
}
inline UInt64 CAHostTimeBase::AbsoluteHostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime)
{
UInt64 theAnswer;
if(inStartTime <= inEndTime)
{
theAnswer = inEndTime - inStartTime;
}
else
{
theAnswer = inStartTime - inEndTime;
}
return ConvertToNanos(theAnswer);
}
inline SInt64 CAHostTimeBase::HostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime)
{
SInt64 theAnswer;
SInt64 theSign = 1;
if(inStartTime <= inEndTime)
{
theAnswer = static_cast<SInt64>(inEndTime - inStartTime);
}
else
{
theAnswer = static_cast<SInt64>(inStartTime - inEndTime);
theSign = -1;
}
return theSign * static_cast<SInt64>(ConvertToNanos(static_cast<UInt64>(theAnswer)));
}
inline UInt64 CAHostTimeBase::MultiplyByRatio(UInt64 inMuliplicand, UInt32 inNumerator, UInt32 inDenominator)
{
#if TARGET_OS_MAC && TARGET_RT_64_BIT
__uint128_t theAnswer = inMuliplicand;
#else
long double theAnswer = inMuliplicand;
#endif
if(inNumerator != inDenominator)
{
theAnswer *= inNumerator;
theAnswer /= inDenominator;
}
return static_cast<UInt64>(theAnswer);
}
#endif