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)
target_sources(boo PRIVATE
lib/audiodev/AQS.cpp
lib/audiodev/CADebugPrintf.cpp
lib/audiodev/CAHostTimeBase.cpp
${AudioMatrix_SRC}
lib/CFPointer.hpp
)
@ -192,7 +194,11 @@ elseif(APPLE)
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)
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)

View File

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