diff -Nru jackd2-1.9.11~20161209/ChangeLog jackd2-1.9.11+git20170722/ChangeLog --- jackd2-1.9.11~20161209/ChangeLog 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/ChangeLog 2017-07-22 08:02:50.000000000 +0000 @@ -42,6 +42,10 @@ Jackdmp changes log --------------------------- +2017-06-13 Filipe Coelho + + * Version 1.9.11-RC1 + 2015-07-19 Filipe Coelho * Implement new jack_port_rename API diff -Nru jackd2-1.9.11~20161209/common/jack/systemdeps.h jackd2-1.9.11+git20170722/common/jack/systemdeps.h --- jackd2-1.9.11~20161209/common/jack/systemdeps.h 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/jack/systemdeps.h 2017-07-22 08:02:50.000000000 +0000 @@ -60,6 +60,10 @@ #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(GNU_WIN32) + #ifdef __MINGW32__ + # include // mingw gives warning if we include windows.h before winsock2.h + #endif + #include #ifdef _MSC_VER /* Microsoft compiler */ diff -Nru jackd2-1.9.11~20161209/common/JackAudioDriver.cpp jackd2-1.9.11+git20170722/common/JackAudioDriver.cpp --- jackd2-1.9.11~20161209/common/JackAudioDriver.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/JackAudioDriver.cpp 2017-07-22 08:02:46.000000000 +0000 @@ -247,7 +247,6 @@ { // fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle if (!fEngine->Process(fBeginDateUst, fEndDateUst)) { - jack_error("JackAudioDriver::ProcessGraphAsyncMaster: Process error"); } if (ResumeRefNum() < 0) { diff -Nru jackd2-1.9.11~20161209/common/JackClient.cpp jackd2-1.9.11+git20170722/common/JackClient.cpp --- jackd2-1.9.11~20161209/common/JackClient.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/JackClient.cpp 2017-07-22 08:02:50.000000000 +0000 @@ -32,6 +32,37 @@ #include #include +/* Disables denormal numbers in floating point calculation. Denormal numbers + * happens often in IIR filters, and it can be very slow. + */ +/* Taken from cras/src/dsp/dsp_util.c in Chromium OS code. + * Copyright (c) 2013 The Chromium OS Authors. */ +static void dsp_enable_flush_denormal_to_zero() +{ +#if defined(__i386__) || defined(__x86_64__) + unsigned int mxcsr; + mxcsr = __builtin_ia32_stmxcsr(); + __builtin_ia32_ldmxcsr(mxcsr | 0x8040); +#elif defined(__aarch64__) + uint64_t cw; + __asm__ __volatile__ ( + "mrs %0, fpcr \n" + "orr %0, %0, #0x1000000 \n" + "msr fpcr, %0 \n" + "isb \n" + : "=r"(cw) :: "memory"); +#elif defined(__arm__) + uint32_t cw; + __asm__ __volatile__ ( + "vmrs %0, fpscr \n" + "orr %0, %0, #0x1000000 \n" + "vmsr fpscr, %0 \n" + : "=r"(cw) :: "memory"); +#else +#warning "Don't know how to disable denorms. Performace may suffer." +#endif +} + using namespace std; namespace Jack @@ -39,9 +70,6 @@ #define IsRealTime() ((fProcess != NULL) | (fThreadFun != NULL) | (fSync != NULL) | (fTimebase != NULL)) -JackClient::JackClient():fThread(this) -{} - JackClient::JackClient(JackSynchro* table):fThread(this) { fSynchroTable = table; @@ -492,6 +520,8 @@ void JackClient::InitAux() { + dsp_enable_flush_denormal_to_zero(); + if (fInit) { jack_log("JackClient::Init calling client thread init callback"); fInit(fInitArg); diff -Nru jackd2-1.9.11~20161209/common/JackClient.h jackd2-1.9.11+git20170722/common/JackClient.h --- jackd2-1.9.11~20161209/common/JackClient.h 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/JackClient.h 2017-07-22 08:02:50.000000000 +0000 @@ -124,7 +124,6 @@ public: - JackClient(); JackClient(JackSynchro* table); virtual ~JackClient(); diff -Nru jackd2-1.9.11~20161209/common/JackConstants.h jackd2-1.9.11+git20170722/common/JackConstants.h --- jackd2-1.9.11~20161209/common/JackConstants.h 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/JackConstants.h 2017-07-22 08:02:50.000000000 +0000 @@ -24,7 +24,7 @@ #include "config.h" #endif -#define VERSION "1.9.11" +#define VERSION "1.9.11-RC1" #define BUFFER_SIZE_MAX 8192 diff -Nru jackd2-1.9.11~20161209/common/JackDebugClient.cpp jackd2-1.9.11+git20170722/common/JackDebugClient.cpp --- jackd2-1.9.11~20161209/common/JackDebugClient.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/JackDebugClient.cpp 2017-07-22 08:02:50.000000000 +0000 @@ -35,6 +35,7 @@ { JackDebugClient::JackDebugClient(JackClient * client) + : JackClient(client->fSynchroTable) { fTotalPortNumber = 1; // The total number of port opened and maybe closed. Historical view. fOpenPortNumber = 0; // The current number of opened port. @@ -58,7 +59,7 @@ if (fOpenPortNumber != 0) *fStream << "!!! WARNING !!! Some ports have not been unregistered ! Incorrect exiting !" << endl; if (fIsDeactivated != fIsActivated) - *fStream << "!!! ERROR !!! Client seem to not perform symetric activation-deactivation ! (not the same number of activate and deactivate)" << endl; + *fStream << "!!! ERROR !!! Client seem to not perform symmetric activation-deactivation ! (not the same number of activate and deactivate)" << endl; if (fIsClosed == 0) *fStream << "!!! ERROR !!! Client have not been closed with jack_client_close() !" << endl; diff -Nru jackd2-1.9.11~20161209/common/Jackdmp.cpp jackd2-1.9.11+git20170722/common/Jackdmp.cpp --- jackd2-1.9.11~20161209/common/Jackdmp.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/Jackdmp.cpp 2017-07-22 08:02:50.000000000 +0000 @@ -28,6 +28,9 @@ #include #include +#include +#include + #include "types.h" #include "jack.h" #include "control.h" @@ -92,6 +95,7 @@ fprintf(file, "jackdmp " VERSION "\n" "Copyright 2001-2005 Paul Davis and others.\n" "Copyright 2004-2016 Grame.\n" + "Copyright 2016-2017 Filipe Coelho.\n" "jackdmp comes with ABSOLUTELY NO WARRANTY\n" "This is free software, and you are welcome to redistribute it\n" "under certain conditions; see the file COPYING for details\n"); @@ -241,6 +245,15 @@ // Prototype to be found in libjackserver extern "C" void silent_jack_error_callback(const char *desc); +void print_version() +{ + printf( "jackdmp version " VERSION " tmpdir " + jack_server_dir " protocol %d" "\n", + JACK_PROTOCOL_VERSION); + exit(-1); + +} + int main(int argc, char** argv) { jackctl_server_t * server_ctl; @@ -249,7 +262,13 @@ jackctl_driver_t * master_driver_ctl; jackctl_driver_t * loopback_driver_ctl = NULL; int replace_registry = 0; - const char *options = "-d:X:I:P:uvshVrRL:STFl:t:mn:p:" + + for(int a = 1; a < argc; ++a) { + if( !strcmp(argv[a], "--version") || !strcmp(argv[a], "-V") ) { + print_version(); + } + } + const char *options = "-d:X:I:P:uvshrRL:STFl:t:mn:p:" "a:" #ifdef __linux__ "c:" @@ -277,7 +296,6 @@ { "realtime-priority", 1, 0, 'P' }, { "timeout", 1, 0, 't' }, { "temporary", 0, 0, 'T' }, - { "version", 0, 0, 'V' }, { "silent", 0, 0, 's' }, { "sync", 0, 0, 'S' }, { "autoconnect", 1, 0, 'a' }, @@ -290,7 +308,6 @@ char** master_driver_args = NULL; int master_driver_nargs = 1; int loopback = 0; - bool show_version = false; jackctl_sigmask_t * sigmask; jackctl_parameter_t* param; union jackctl_parameter_value value; @@ -469,10 +486,6 @@ } break; - case 'V': - show_version = true; - break; - default: fprintf(stderr, "unknown option character %c\n", optopt); /*fallthru*/ @@ -490,14 +503,6 @@ jackctl_parameter_set_value(param, &value); } - if (show_version) { - printf( "jackdmp version " VERSION - " tmpdir " jack_server_dir - " protocol %d" - "\n", JACK_PROTOCOL_VERSION); - return -1; - } - if (!master_driver_name) { usage(stderr, server_ctl, false); goto destroy_server; @@ -538,6 +543,18 @@ goto destroy_server; } + /* + Require the fastest response time possible regarding OS latency, + See https://access.redhat.com/articles/65410 + */ + { + const int32_t v = 0; + const int fd = open("/dev/cpu_dma_latency", O_WRONLY); + + if (fd >= 0) + write(fd, &v, sizeof(v)); + } + // Setup signals sigmask = jackctl_setup_signals(0); diff -Nru jackd2-1.9.11~20161209/common/JackDriver.cpp jackd2-1.9.11+git20170722/common/JackDriver.cpp --- jackd2-1.9.11~20161209/common/JackDriver.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/JackDriver.cpp 2017-07-22 08:00:34.000000000 +0000 @@ -48,6 +48,7 @@ fEngine = engine; fGraphManager = NULL; fBeginDateUst = 0; + fEndDateUst = 0; fDelayedUsecs = 0.f; fIsMaster = true; fIsRunning = false; diff -Nru jackd2-1.9.11~20161209/common/JackDriverLoader.cpp jackd2-1.9.11+git20170722/common/JackDriverLoader.cpp --- jackd2-1.9.11~20161209/common/JackDriverLoader.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/JackDriverLoader.cpp 2017-07-22 08:02:50.000000000 +0000 @@ -36,11 +36,9 @@ static char* locate_dll_driver_dir() { -#ifdef _WIN64 - HMODULE libjack_handle = LoadLibrary("libjackserver64.dll"); -#else - HMODULE libjack_handle = LoadLibrary("libjackserver.dll"); -#endif + HMODULE libjack_handle = NULL; + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(locate_dll_driver_dir), &libjack_handle); // For WIN32 ADDON_DIR is defined in JackConstants.h as relative path char driver_dir_storage[512]; @@ -52,11 +50,9 @@ jack_info("Drivers/internals found in : %s", driver_dir_storage); strcat(driver_dir_storage, "/"); strcat(driver_dir_storage, ADDON_DIR); - FreeLibrary(libjack_handle); return strdup(driver_dir_storage); } else { jack_error("Cannot get JACK dll directory : %d", GetLastError()); - FreeLibrary(libjack_handle); return NULL; } } diff -Nru jackd2-1.9.11~20161209/common/JackEngine.cpp jackd2-1.9.11+git20170722/common/JackEngine.cpp --- jackd2-1.9.11~20161209/common/JackEngine.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/JackEngine.cpp 2017-07-22 08:02:46.000000000 +0000 @@ -218,12 +218,10 @@ jack_time_t finished_date = timing->fFinishedAt; if (status != NotTriggered && status != Finished) { - jack_error("JackEngine::XRun: client = %s was not finished, state = %s", client->GetClientControl()->fName, State2String(status)); fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0); // Notify all clients } if (status == Finished && (long)(finished_date - callback_usecs) > 0) { - jack_error("JackEngine::XRun: client %s finished after current callback", client->GetClientControl()->fName); fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0); // Notify all clients } } @@ -615,7 +613,7 @@ JackExternalClient* client = new JackExternalClient(); - if (!fSynchroTable[refnum].Allocate(real_name, fEngineControl->fServerName, 0)) { + if (!fSynchroTable[refnum].Allocate(real_name, fEngineControl->fServerName, 0, false)) { jack_error("Cannot allocate synchro"); goto error; } @@ -665,7 +663,7 @@ goto error; } - if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0)) { + if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0, true)) { jack_error("Cannot allocate synchro"); goto error; } diff -Nru jackd2-1.9.11~20161209/common/JackMidiAsyncWaitQueue.cpp jackd2-1.9.11+git20170722/common/JackMidiAsyncWaitQueue.cpp --- jackd2-1.9.11~20161209/common/JackMidiAsyncWaitQueue.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/JackMidiAsyncWaitQueue.cpp 2017-07-22 08:02:46.000000000 +0000 @@ -29,7 +29,7 @@ size_t max_messages): JackMidiAsyncQueue(max_bytes, max_messages) { - if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0)) { + if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0, true)) { throw std::bad_alloc(); } } diff -Nru jackd2-1.9.11~20161209/common/JackNetTool.cpp jackd2-1.9.11+git20170722/common/JackNetTool.cpp --- jackd2-1.9.11~20161209/common/JackNetTool.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/JackNetTool.cpp 2017-07-22 08:00:34.000000000 +0000 @@ -778,8 +778,8 @@ memset(fCompressedBuffer[port_index], 0, fCompressedMaxSizeByte * sizeof(char)); } - int res1 = (fNPorts * fCompressedMaxSizeByte + CDO) % PACKET_AVAILABLE_SIZE(params); - int res2 = (fNPorts * fCompressedMaxSizeByte + CDO) / PACKET_AVAILABLE_SIZE(params); + int res1 = (fNPorts * (fCompressedMaxSizeByte + CDO)) % PACKET_AVAILABLE_SIZE(params); + int res2 = (fNPorts * (fCompressedMaxSizeByte + CDO)) / PACKET_AVAILABLE_SIZE(params); fNumPackets = (res1) ? (res2 + 1) : res2; diff -Nru jackd2-1.9.11~20161209/common/JackNetTool.h jackd2-1.9.11+git20170722/common/JackNetTool.h --- jackd2-1.9.11~20161209/common/JackNetTool.h 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/JackNetTool.h 2017-07-22 08:00:34.000000000 +0000 @@ -49,8 +49,7 @@ #define UDP_HEADER_SIZE 64 // 40 bytes for IP header in IPV6, 20 in IPV4, 8 for UDP, so take 64 #define HEADER_SIZE (sizeof(packet_header_t)) -// TODO : change PACKET_AVAILABLE_SIZE definition ==> #define PACKET_AVAILABLE_SIZE(params) ((params)->fMtu - UDP_HEADER_SIZE - sizeof(packet_header_t)) -#define PACKET_AVAILABLE_SIZE(params) ((params)->fMtu - HEADER_SIZE) +#define PACKET_AVAILABLE_SIZE(params) ((params)->fMtu - UDP_HEADER_SIZE - HEADER_SIZE) namespace Jack { diff -Nru jackd2-1.9.11~20161209/common/JackRequest.h jackd2-1.9.11+git20170722/common/JackRequest.h --- jackd2-1.9.11~20161209/common/JackRequest.h 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/JackRequest.h 2017-07-22 08:00:34.000000000 +0000 @@ -163,11 +163,14 @@ int fUUID; int fOpen; - JackClientCheckRequest() - {} + JackClientCheckRequest() : fProtocol(0), fOptions(0), fUUID(0), fOpen(0) + { + memset(fName, 0, sizeof(fName)); + } JackClientCheckRequest(const char* name, int protocol, int options, int uuid, int open = false) : JackRequest(JackRequest::kClientCheck), fProtocol(protocol), fOptions(options), fUUID(uuid), fOpen(open) { + memset(fName, 0, sizeof(fName)); snprintf(fName, sizeof(fName), "%s", name); } @@ -206,10 +209,13 @@ int fStatus; JackClientCheckResult(): JackResult(), fStatus(0) - {} + { + memset(fName, 0, sizeof(fName)); + } JackClientCheckResult(int32_t result, const char* name, int status) : JackResult(result), fStatus(status) { + memset(fName, 0, sizeof(fName)); snprintf(fName, sizeof(fName), "%s", name); } @@ -242,10 +248,13 @@ int fUUID; char fName[JACK_CLIENT_NAME_SIZE+1]; - JackClientOpenRequest() - {} + JackClientOpenRequest() : fPID(0), fUUID(0) + { + memset(fName, 0, sizeof(fName)); + } JackClientOpenRequest(const char* name, int pid, int uuid): JackRequest(JackRequest::kClientOpen) { + memset(fName, 0, sizeof(fName)); snprintf(fName, sizeof(fName), "%s", name); fPID = pid; fUUID = uuid; @@ -318,7 +327,7 @@ int fRefNum; - JackClientCloseRequest() + JackClientCloseRequest() : fRefNum(0) {} JackClientCloseRequest(int refnum): JackRequest(JackRequest::kClientClose), fRefNum(refnum) {} @@ -348,7 +357,7 @@ int fRefNum; int fIsRealTime; - JackActivateRequest() + JackActivateRequest() : fRefNum(0), fIsRealTime(0) {} JackActivateRequest(int refnum, int is_real_time) : JackRequest(JackRequest::kActivateClient), fRefNum(refnum), fIsRealTime(is_real_time) @@ -380,7 +389,7 @@ int fRefNum; - JackDeactivateRequest() + JackDeactivateRequest() : fRefNum(0) {} JackDeactivateRequest(int refnum): JackRequest(JackRequest::kDeactivateClient), fRefNum(refnum) {} @@ -413,13 +422,18 @@ unsigned int fFlags; unsigned int fBufferSize; - JackPortRegisterRequest() - {} + JackPortRegisterRequest() : fRefNum(0), fFlags(0), fBufferSize(0) + { + memset(fName, 0, sizeof(fName)); + memset(fPortType, 0, sizeof(fPortType)); + } JackPortRegisterRequest(int refnum, const char* name, const char* port_type, unsigned int flags, unsigned int buffer_size) : JackRequest(JackRequest::kRegisterPort), fRefNum(refnum), fFlags(flags), fBufferSize(buffer_size) { - strcpy(fName, name); - strcpy(fPortType, port_type); + memset(fName, 0, sizeof(fName)); + memset(fPortType, 0, sizeof(fPortType)); + strncpy(fName, name, sizeof(fName)-1); + strncpy(fPortType, port_type, sizeof(fPortType)-1); } int Read(detail::JackChannelTransactionInterface* trans) @@ -484,7 +498,7 @@ int fRefNum; jack_port_id_t fPortIndex; - JackPortUnRegisterRequest() + JackPortUnRegisterRequest() : fRefNum(0), fPortIndex(0) {} JackPortUnRegisterRequest(int refnum, jack_port_id_t index) : JackRequest(JackRequest::kUnRegisterPort), fRefNum(refnum), fPortIndex(index) @@ -520,13 +534,18 @@ char fSrc[REAL_JACK_PORT_NAME_SIZE+1]; // port full name char fDst[REAL_JACK_PORT_NAME_SIZE+1]; // port full name - JackPortConnectNameRequest() - {} + JackPortConnectNameRequest() : fRefNum(0) + { + memset(fSrc, 0, sizeof(fSrc)); + memset(fDst, 0, sizeof(fDst)); + } JackPortConnectNameRequest(int refnum, const char* src_name, const char* dst_name) : JackRequest(JackRequest::kConnectNamePorts), fRefNum(refnum) { - strcpy(fSrc, src_name); - strcpy(fDst, dst_name); + memset(fSrc, 0, sizeof(fSrc)); + memset(fDst, 0, sizeof(fDst)); + strncpy(fSrc, src_name, sizeof(fSrc)-1); + strncpy(fDst, dst_name, sizeof(fDst)-1); } int Read(detail::JackChannelTransactionInterface* trans) @@ -562,13 +581,18 @@ char fSrc[REAL_JACK_PORT_NAME_SIZE+1]; // port full name char fDst[REAL_JACK_PORT_NAME_SIZE+1]; // port full name - JackPortDisconnectNameRequest() - {} + JackPortDisconnectNameRequest() : fRefNum(0) + { + memset(fSrc, 0, sizeof(fSrc)); + memset(fDst, 0, sizeof(fDst)); + } JackPortDisconnectNameRequest(int refnum, const char* src_name, const char* dst_name) : JackRequest(JackRequest::kDisconnectNamePorts), fRefNum(refnum) { - strcpy(fSrc, src_name); - strcpy(fDst, dst_name); + memset(fSrc, 0, sizeof(fSrc)); + memset(fDst, 0, sizeof(fDst)); + strncpy(fSrc, src_name, sizeof(fSrc)-1); + strncpy(fDst, dst_name, sizeof(fDst)-1); } int Read(detail::JackChannelTransactionInterface* trans) @@ -604,7 +628,7 @@ jack_port_id_t fSrc; jack_port_id_t fDst; - JackPortConnectRequest() + JackPortConnectRequest() : fRefNum(0), fSrc(0), fDst(0) {} JackPortConnectRequest(int refnum, jack_port_id_t src, jack_port_id_t dst) : JackRequest(JackRequest::kConnectPorts), fRefNum(refnum), fSrc(src), fDst(dst) @@ -642,7 +666,7 @@ jack_port_id_t fSrc; jack_port_id_t fDst; - JackPortDisconnectRequest() + JackPortDisconnectRequest() : fRefNum(0), fSrc(0), fDst(0) {} JackPortDisconnectRequest(int refnum, jack_port_id_t src, jack_port_id_t dst) : JackRequest(JackRequest::kDisconnectPorts), fRefNum(refnum), fSrc(src), fDst(dst) @@ -680,12 +704,15 @@ jack_port_id_t fPort; char fName[JACK_PORT_NAME_SIZE + 1]; // port short name - JackPortRenameRequest() - {} + JackPortRenameRequest() : fRefNum(0), fPort(0) + { + memset(fName, 0, sizeof(fName)); + } JackPortRenameRequest(int refnum, jack_port_id_t port, const char* name) : JackRequest(JackRequest::kPortRename), fRefNum(refnum), fPort(port) { - strcpy(fName, name); + memset(fName, 0, sizeof(fName)); + strncpy(fName, name, sizeof(fName)-1); } int Read(detail::JackChannelTransactionInterface* trans) @@ -719,7 +746,7 @@ jack_nframes_t fBufferSize; - JackSetBufferSizeRequest() + JackSetBufferSizeRequest() : fBufferSize(0) {} JackSetBufferSizeRequest(jack_nframes_t buffer_size) : JackRequest(JackRequest::kSetBufferSize), fBufferSize(buffer_size) @@ -749,7 +776,7 @@ int fOnOff; - JackSetFreeWheelRequest() + JackSetFreeWheelRequest() : fOnOff(0) {} JackSetFreeWheelRequest(int onoff) : JackRequest(JackRequest::kSetFreeWheel), fOnOff(onoff) @@ -806,7 +833,7 @@ int fRefNum; - JackReleaseTimebaseRequest() + JackReleaseTimebaseRequest() : fRefNum(0) {} JackReleaseTimebaseRequest(int refnum) : JackRequest(JackRequest::kReleaseTimebase), fRefNum(refnum) @@ -838,7 +865,7 @@ int fRefNum; int fConditionnal; - JackSetTimebaseCallbackRequest() + JackSetTimebaseCallbackRequest() : fRefNum(0), fConditionnal(0) {} JackSetTimebaseCallbackRequest(int refnum, int conditional) : JackRequest(JackRequest::kSetTimebaseCallback), fRefNum(refnum), fConditionnal(conditional) @@ -871,7 +898,7 @@ int fRefNum; int fIntRefNum; - JackGetInternalClientNameRequest() + JackGetInternalClientNameRequest() : fRefNum(0), fIntRefNum(0) {} JackGetInternalClientNameRequest(int refnum, int int_ref) : JackRequest(JackRequest::kGetInternalClientName), fRefNum(refnum), fIntRefNum(int_ref) @@ -904,10 +931,13 @@ char fName[JACK_CLIENT_NAME_SIZE+1]; JackGetInternalClientNameResult(): JackResult() - {} + { + memset(fName, 0, sizeof(fName)); + } JackGetInternalClientNameResult(int32_t result, const char* name) : JackResult(result) { + memset(fName, 0, sizeof(fName)); snprintf(fName, sizeof(fName), "%s", name); } @@ -938,11 +968,14 @@ int fRefNum; char fName[JACK_CLIENT_NAME_SIZE+1]; - JackInternalClientHandleRequest() - {} + JackInternalClientHandleRequest() : fRefNum(0) + { + memset(fName, 0, sizeof(fName)); + } JackInternalClientHandleRequest(int refnum, const char* client_name) : JackRequest(JackRequest::kInternalClientHandle), fRefNum(refnum) { + memset(fName, 0, sizeof(fName)); snprintf(fName, sizeof(fName), "%s", client_name); } @@ -973,7 +1006,7 @@ int fStatus; int fIntRefNum; - JackInternalClientHandleResult(): JackResult() + JackInternalClientHandleResult(): JackResult(), fStatus(0), fIntRefNum(0) {} JackInternalClientHandleResult(int32_t result, int status, int int_ref) : JackResult(result), fStatus(status), fIntRefNum(int_ref) @@ -1016,15 +1049,21 @@ int fOptions; int fUUID; - JackInternalClientLoadRequest() - {} + JackInternalClientLoadRequest() : fRefNum(0), fOptions(0), fUUID(0) + { + memset(fName, 0, sizeof(fName)); + memset(fDllName, 0, sizeof(fDllName)); + memset(fLoadInitName, 0, sizeof(fLoadInitName)); + } JackInternalClientLoadRequest(int refnum, const char* client_name, const char* so_name, const char* objet_data, int options, int uuid ) : JackRequest(JackRequest::kInternalClientLoad), fRefNum(refnum), fOptions(options), fUUID(uuid) { + memset(fName, 0, sizeof(fName)); + memset(fDllName, 0, sizeof(fDllName)); + memset(fLoadInitName, 0, sizeof(fLoadInitName)); snprintf(fName, sizeof(fName), "%s", client_name); snprintf(fDllName, sizeof(fDllName), "%s", so_name); snprintf(fLoadInitName, sizeof(fLoadInitName), "%s", objet_data); - snprintf(fLoadInitName, sizeof(fLoadInitName), "%s", objet_data); } int Read(detail::JackChannelTransactionInterface* trans) @@ -1062,7 +1101,7 @@ int fStatus; int fIntRefNum; - JackInternalClientLoadResult(): JackResult() + JackInternalClientLoadResult(): JackResult(), fStatus(0), fIntRefNum(0) {} JackInternalClientLoadResult(int32_t result, int status, int int_ref) : JackResult(result), fStatus(status), fIntRefNum(int_ref) @@ -1097,7 +1136,7 @@ int fRefNum; int fIntRefNum; - JackInternalClientUnloadRequest() + JackInternalClientUnloadRequest() : fRefNum(0), fIntRefNum(0) {} JackInternalClientUnloadRequest(int refnum, int int_ref) : JackRequest(JackRequest::kInternalClientUnload), fRefNum(refnum), fIntRefNum(int_ref) @@ -1129,7 +1168,7 @@ int fStatus; - JackInternalClientUnloadResult(): JackResult() + JackInternalClientUnloadResult(): JackResult(), fStatus(0) {} JackInternalClientUnloadResult(int32_t result, int status) : JackResult(result), fStatus(status) @@ -1163,7 +1202,7 @@ int fNotify; int fValue; - JackClientNotificationRequest() + JackClientNotificationRequest() : fRefNum(0), fNotify(0), fValue(0) {} JackClientNotificationRequest(int refnum, int notify, int value) : JackRequest(JackRequest::kNotification), fRefNum(refnum), fNotify(notify), fValue(value) @@ -1198,14 +1237,20 @@ char fCommand[JACK_SESSION_COMMAND_SIZE]; jack_session_flags_t fFlags; - JackSessionCommand() - {} - + JackSessionCommand() : fFlags(JackSessionSaveError) + { + memset(fUUID, 0, sizeof(fUUID)); + memset(fClientName, 0, sizeof(fClientName)); + memset(fCommand, 0, sizeof(fCommand)); + } JackSessionCommand(const char *uuid, const char *clientname, const char *command, jack_session_flags_t flags) { - strncpy(fUUID, uuid, sizeof(fUUID)); - strncpy(fClientName, clientname, sizeof(fClientName)); - strncpy(fCommand, command, sizeof(fCommand)); + memset(fUUID, 0, sizeof(fUUID)); + memset(fClientName, 0, sizeof(fClientName)); + memset(fCommand, 0, sizeof(fCommand)); + strncpy(fUUID, uuid, sizeof(fUUID)-1); + strncpy(fClientName, clientname, sizeof(fClientName)-1); + strncpy(fCommand, command, sizeof(fCommand)-1); fFlags = flags; } }; @@ -1310,16 +1355,18 @@ jack_session_event_type_t fEventType; int fRefNum; - JackSessionNotifyRequest() + JackSessionNotifyRequest() : fEventType(JackSessionSave), fRefNum(0) {} JackSessionNotifyRequest(int refnum, const char* path, jack_session_event_type_t type, const char* dst) : JackRequest(JackRequest::kSessionNotify), fEventType(type), fRefNum(refnum) { + memset(fPath, 0, sizeof(fPath)); + memset(fDst, 0, sizeof(fDst)); snprintf(fPath, sizeof(fPath), "%s", path); + fPath[JACK_MESSAGE_SIZE] = 0; if (dst) { snprintf(fDst, sizeof(fDst), "%s", dst); - } else { - fDst[0] = '\0'; + fDst[JACK_CLIENT_NAME_SIZE] = 0; } } @@ -1350,7 +1397,7 @@ { int fRefNum; - JackSessionReplyRequest() + JackSessionReplyRequest() : fRefNum(0) {} JackSessionReplyRequest(int refnum) @@ -1380,10 +1427,13 @@ char fName[JACK_CLIENT_NAME_SIZE+1]; JackClientNameResult(): JackResult() - {} + { + memset(fName, 0, sizeof(fName)); + } JackClientNameResult(int32_t result, const char* name) : JackResult(result) { + memset(fName, 0, sizeof(fName)); snprintf(fName, sizeof(fName), "%s", name); } @@ -1408,10 +1458,13 @@ char fUUID[JACK_UUID_SIZE]; JackUUIDResult(): JackResult() - {} + { + memset(fUUID, 0, sizeof(fUUID)); + } JackUUIDResult(int32_t result, const char* uuid) : JackResult(result) { + memset(fUUID, 0, sizeof(fUUID)); snprintf(fUUID, sizeof(fUUID), "%s", uuid); } @@ -1436,12 +1489,15 @@ char fName[JACK_CLIENT_NAME_SIZE+1]; JackGetUUIDRequest() - {} + { + memset(fName, 0, sizeof(fName)); + } JackGetUUIDRequest(const char* client_name) : JackRequest(JackRequest::kGetUUIDByClient) { - strncpy(fName, client_name, sizeof(fName)); + memset(fName, 0, sizeof(fName)); + strncpy(fName, client_name, sizeof(fName)-1); } int Read(detail::JackChannelTransactionInterface* trans) @@ -1467,12 +1523,15 @@ char fUUID[JACK_UUID_SIZE]; JackGetClientNameRequest() - {} + { + memset(fUUID, 0, sizeof(fUUID)); + } JackGetClientNameRequest(const char* uuid) : JackRequest(JackRequest::kGetClientByUUID) { - strncpy(fUUID, uuid, sizeof(fUUID)); + memset(fUUID, 0, sizeof(fUUID)); + strncpy(fUUID, uuid, sizeof(fUUID)-1); } int Read(detail::JackChannelTransactionInterface* trans) @@ -1499,14 +1558,19 @@ char fName[JACK_CLIENT_NAME_SIZE+1]; char fUUID[JACK_UUID_SIZE]; - JackReserveNameRequest() - {} + JackReserveNameRequest() : fRefNum(0) + { + memset(fName, 0, sizeof(fName)); + memset(fUUID, 0, sizeof(fUUID)); + } JackReserveNameRequest(int refnum, const char *name, const char* uuid) : JackRequest(JackRequest::kReserveClientName), fRefNum(refnum) { - strncpy(fName, name, sizeof(fName)); - strncpy(fUUID, uuid, sizeof(fUUID)); + memset(fName, 0, sizeof(fName)); + memset(fUUID, 0, sizeof(fUUID)); + strncpy(fName, name, sizeof(fName)-1); + strncpy(fUUID, uuid, sizeof(fUUID)-1); } int Read(detail::JackChannelTransactionInterface* trans) @@ -1536,12 +1600,15 @@ char fName[JACK_CLIENT_NAME_SIZE+1]; JackClientHasSessionCallbackRequest() - {} + { + memset(fName, 0, sizeof(fName)); + } JackClientHasSessionCallbackRequest(const char *name) : JackRequest(JackRequest::kClientHasSessionCallback) { - strncpy(fName, name, sizeof(fName)); + memset(fName, 0, sizeof(fName)); + strncpy(fName, name, sizeof(fName)-1); } int Read(detail::JackChannelTransactionInterface* trans) @@ -1577,11 +1644,16 @@ int fSync; char fMessage[JACK_MESSAGE_SIZE+1]; - JackClientNotification(): fNotify(-1), fValue1(-1), fValue2(-1) - {} + JackClientNotification(): fSize(0), fRefNum(0), fNotify(-1), fValue1(-1), fValue2(-1), fSync(0) + { + memset(fName, 0, sizeof(fName)); + memset(fMessage, 0, sizeof(fMessage)); + } JackClientNotification(const char* name, int refnum, int notify, int sync, const char* message, int value1, int value2) : fRefNum(refnum), fNotify(notify), fValue1(value1), fValue2(value2), fSync(sync) { + memset(fName, 0, sizeof(fName)); + memset(fMessage, 0, sizeof(fMessage)); snprintf(fName, sizeof(fName), "%s", name); snprintf(fMessage, sizeof(fMessage), "%s", message); fSize = Size(); diff -Nru jackd2-1.9.11~20161209/common/memops.c jackd2-1.9.11+git20170722/common/memops.c --- jackd2-1.9.11~20161209/common/memops.c 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/memops.c 2017-07-22 08:00:34.000000000 +0000 @@ -42,6 +42,10 @@ #endif #endif +#ifdef __ARM_NEON__ +#include +#endif + /* Notes about these *_SCALING values. the MAX_BIT values are floating point. when multiplied by @@ -172,7 +176,7 @@ /* generates same as _mm_set_ps(1.f, 1.f, 1f., 1f) but faster */ static inline __m128 gen_one(void) { - volatile __m128i x; + volatile __m128i x = { 0 }; /* shut up, GCC */ __m128i ones = _mm_cmpeq_epi32(x, x); return (__m128)_mm_slli_epi32 (_mm_srli_epi32(ones, 25), 23); } @@ -193,13 +197,42 @@ } #endif + +#ifdef __ARM_NEON__ + +static inline float32x4_t clip(float32x4_t s, float32x4_t min, float32x4_t max) +{ + return vminq_f32(max, vmaxq_f32(s, min)); +} + +static inline int32x4_t float_24_neon(float32x4_t s) +{ + const float32x4_t upper_bound = vdupq_n_f32(NORMALIZED_FLOAT_MAX); + const float32x4_t lower_bound = vdupq_n_f32(NORMALIZED_FLOAT_MIN); + + float32x4_t clipped = clip(s, lower_bound, upper_bound); + float32x4_t scaled = vmulq_f32(clipped, vdupq_n_f32(SAMPLE_24BIT_SCALING)); + return vcvtq_s32_f32(scaled); +} + +static inline int16x4_t float_16_neon(float32x4_t s) +{ + const float32x4_t upper_bound = vdupq_n_f32(NORMALIZED_FLOAT_MAX); + const float32x4_t lower_bound = vdupq_n_f32(NORMALIZED_FLOAT_MIN); + + float32x4_t clipped = clip(s, lower_bound, upper_bound); + float32x4_t scaled = vmulq_f32(clipped, vdupq_n_f32(SAMPLE_16BIT_SCALING)); + return vmovn_s32(vcvtq_s32_f32(scaled)); +} +#endif + /* Linear Congruential noise generator. From the music-dsp list * less random than rand(), but good enough and 10x faster */ static unsigned int seed = 22222; static inline unsigned int fast_rand() { - seed = (seed * 96314165) + 907633515; + seed = (seed * 196314165) + 907633515; return seed; } @@ -248,6 +281,32 @@ void sample_move_d32u24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { +#ifdef __ARM_NEON__ + unsigned long unrolled = nsamples / 4; + nsamples = nsamples & 3; + + while (unrolled--) { + float32x4_t samples = vld1q_f32(src); + int32x4_t converted = float_24_neon(samples); + int32x4_t shifted = vshlq_n_s32(converted, 8); + shifted = vreinterpretq_s32_u8(vrev32q_u8(vreinterpretq_u8_s32(shifted))); + + switch(dst_skip) { + case 4: + vst1q_s32((int32_t*)dst, shifted); + break; + default: + vst1q_lane_s32((int32_t*)(dst), shifted, 0); + vst1q_lane_s32((int32_t*)(dst+dst_skip), shifted, 1); + vst1q_lane_s32((int32_t*)(dst+2*dst_skip), shifted, 2); + vst1q_lane_s32((int32_t*)(dst+3*dst_skip), shifted, 3); + break; + } + dst += 4*dst_skip; + src+= 4; + } +#endif + int32_t z; while (nsamples--) { @@ -321,7 +380,33 @@ src++; } -#else +#elif defined(__ARM_NEON__) + unsigned long unrolled = nsamples / 4; + nsamples = nsamples & 3; + + while (unrolled--) { + float32x4_t samples = vld1q_f32(src); + int32x4_t converted = float_24_neon(samples); + int32x4_t shifted = vshlq_n_s32(converted, 8); + + switch(dst_skip) { + case 4: + vst1q_s32((int32_t*)dst, shifted); + break; + default: + vst1q_lane_s32((int32_t*)(dst), shifted, 0); + vst1q_lane_s32((int32_t*)(dst+dst_skip), shifted, 1); + vst1q_lane_s32((int32_t*)(dst+2*dst_skip), shifted, 2); + vst1q_lane_s32((int32_t*)(dst+3*dst_skip), shifted, 3); + break; + } + dst += 4*dst_skip; + + src+= 4; + } +#endif + +#if !defined (__SSE2__) while (nsamples--) { float_24u32 (*src, *((int32_t*) dst)); dst += dst_skip; @@ -332,6 +417,38 @@ void sample_move_dS_s32u24s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { +#ifdef __ARM_NEON__ + float32x4_t factor = vdupq_n_f32(1.0 / SAMPLE_24BIT_SCALING); + unsigned long unrolled = nsamples / 4; + while (unrolled--) { + int32x4_t src128; + switch(src_skip) + { + case 4: + src128 = vld1q_s32((int32_t*)src); + break; + case 8: + src128 = vld2q_s32((int32_t*)src).val[0]; + break; + default: + src128 = vld1q_lane_s32((int32_t*)src, src128, 0); + src128 = vld1q_lane_s32((int32_t*)(src+src_skip), src128, 1); + src128 = vld1q_lane_s32((int32_t*)(src+2*src_skip), src128, 2); + src128 = vld1q_lane_s32((int32_t*)(src+3*src_skip), src128, 3); + break; + } + src128 = vreinterpretq_s32_u8(vrev32q_u8(vreinterpretq_u8_s32(src128))); + int32x4_t shifted = vshrq_n_s32(src128, 8); + float32x4_t as_float = vcvtq_f32_s32(shifted); + float32x4_t divided = vmulq_f32(as_float, factor); + vst1q_f32(dst, divided); + + src += 4*src_skip; + dst += 4; + } + nsamples = nsamples & 3; +#endif + /* ALERT: signed sign-extension portability !!! */ const jack_default_audio_sample_t scaling = 1.0/SAMPLE_24BIT_SCALING; @@ -389,6 +506,34 @@ dst += 4; } nsamples = nsamples & 3; +#elif defined(__ARM_NEON__) + unsigned long unrolled = nsamples / 4; + float32x4_t factor = vdupq_n_f32(1.0 / SAMPLE_24BIT_SCALING); + while (unrolled--) { + int32x4_t src128; + switch(src_skip) { + case 4: + src128 = vld1q_s32((int32_t*)src); + break; + case 8: + src128 = vld2q_s32((int32_t*)src).val[0]; + break; + default: + src128 = vld1q_lane_s32((int32_t*)src, src128, 0); + src128 = vld1q_lane_s32((int32_t*)(src+src_skip), src128, 1); + src128 = vld1q_lane_s32((int32_t*)(src+2*src_skip), src128, 2); + src128 = vld1q_lane_s32((int32_t*)(src+3*src_skip), src128, 3); + break; + } + int32x4_t shifted = vshrq_n_s32(src128, 8); + float32x4_t as_float = vcvtq_f32_s32(shifted); + float32x4_t divided = vmulq_f32(as_float, factor); + vst1q_f32(dst, divided); + + src += 4*src_skip; + dst += 4; + } + nsamples = nsamples & 3; #endif /* ALERT: signed sign-extension portability !!! */ @@ -403,6 +548,25 @@ void sample_move_d24_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { +#ifdef __ARM_NEON__ + unsigned long unrolled = nsamples / 4; + while (unrolled--) { + int i; + int32_t z[4]; + float32x4_t samples = vld1q_f32(src); + int32x4_t converted = float_24_neon(samples); + converted = vreinterpretq_s32_u8(vrev32q_u8(vreinterpretq_u8_s32(converted))); + vst1q_s32(z, converted); + + for (i = 0; i != 4; ++i) { + memcpy (dst, ((char*)(z+i))+1, 3); + dst += dst_skip; + } + src += 4; + } + nsamples = nsamples & 3; +#endif + int32_t z; while (nsamples--) { @@ -455,6 +619,22 @@ nsamples -= 4; src += 4; } +#elif defined(__ARM_NEON__) + unsigned long unrolled = nsamples / 4; + while (unrolled--) { + int i; + int32_t z[4]; + float32x4_t samples = vld1q_f32(src); + int32x4_t converted = float_24_neon(samples); + vst1q_s32(z, converted); + + for (i = 0; i != 4; ++i) { + memcpy (dst, z+i, 3); + dst += dst_skip; + } + src += 4; + } + nsamples = nsamples & 3; #endif int32_t z; @@ -473,9 +653,41 @@ void sample_move_dS_s24s (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip) { + const jack_default_audio_sample_t scaling = 1.0/SAMPLE_24BIT_SCALING; + +#ifdef __ARM_NEON__ + // we shift 8 to the right by dividing by 256.0 -> no sign extra handling + const float32x4_t vscaling = vdupq_n_f32(scaling/256.0); + int32_t x[4]; + memset(x, 0, sizeof(x)); + unsigned long unrolled = nsamples / 4; + while (unrolled--) { +#if __BYTE_ORDER == __BIG_ENDIAN /* ARM big endian?? */ + // right aligned / inverse sequence below -> *256 + memcpy(((char*)&x[0])+1, src, 3); + memcpy(((char*)&x[1])+1, src+src_skip, 3); + memcpy(((char*)&x[2])+1, src+2*src_skip, 3); + memcpy(((char*)&x[3])+1, src+3*src_skip, 3); +#else + memcpy(&x[0], src, 3); + memcpy(&x[1], src+src_skip, 3); + memcpy(&x[2], src+2*src_skip, 3); + memcpy(&x[3], src+3*src_skip, 3); +#endif + src += 4 * src_skip; + + int32x4_t source = vld1q_s32(x); + source = vreinterpretq_s32_u8(vrev32q_u8(vreinterpretq_u8_s32(source))); + float32x4_t converted = vcvtq_f32_s32(source); + float32x4_t scaled = vmulq_f32(converted, vscaling); + vst1q_f32(dst, scaled); + dst += 4; + } + nsamples = nsamples & 3; +#endif + /* ALERT: signed sign-extension portability !!! */ - const jack_default_audio_sample_t scaling = 1.0/SAMPLE_24BIT_SCALING; while (nsamples--) { int x; #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -528,6 +740,34 @@ dst += 4; nsamples -= 4; } +#elif defined(__ARM_NEON__) + // we shift 8 to the right by dividing by 256.0 -> no sign extra handling + const float32x4_t vscaling = vdupq_n_f32(scaling/256.0); + int32_t x[4]; + memset(x, 0, sizeof(x)); + unsigned long unrolled = nsamples / 4; + while (unrolled--) { +#if __BYTE_ORDER == __BIG_ENDIAN /* ARM big endian?? */ + // left aligned -> *256 + memcpy(&x[0], src, 3); + memcpy(&x[1], src+src_skip, 3); + memcpy(&x[2], src+2*src_skip, 3); + memcpy(&x[3], src+3*src_skip, 3); +#else + memcpy(((char*)&x[0])+1, src, 3); + memcpy(((char*)&x[1])+1, src+src_skip, 3); + memcpy(((char*)&x[2])+1, src+2*src_skip, 3); + memcpy(((char*)&x[3])+1, src+3*src_skip, 3); +#endif + src += 4 * src_skip; + + int32x4_t source = vld1q_s32(x); + float32x4_t converted = vcvtq_f32_s32(source); + float32x4_t scaled = vmulq_f32(converted, vscaling); + vst1q_f32(dst, scaled); + dst += 4; + } + nsamples = nsamples & 3; #endif while (nsamples--) { @@ -547,6 +787,30 @@ void sample_move_d16_sSs (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { +#ifdef __ARM_NEON__ + unsigned long unrolled = nsamples / 4; + nsamples = nsamples & 3; + + while (unrolled--) { + float32x4_t samples = vld1q_f32(src); + int16x4_t converted = float_16_neon(samples); + converted = vreinterpret_s16_u8(vrev16_u8(vreinterpret_u8_s16(converted))); + + switch(dst_skip) { + case 2: + vst1_s16((int16_t*)dst, converted); + break; + default: + vst1_lane_s16((int16_t*)(dst), converted, 0); + vst1_lane_s16((int16_t*)(dst+dst_skip), converted, 1); + vst1_lane_s16((int16_t*)(dst+2*dst_skip), converted, 2); + vst1_lane_s16((int16_t*)(dst+3*dst_skip), converted, 3); + break; + } + dst += 4*dst_skip; + src+= 4; + } +#endif int16_t tmp; while (nsamples--) { @@ -574,6 +838,29 @@ void sample_move_d16_sS (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state) { +#ifdef __ARM_NEON__ + unsigned long unrolled = nsamples / 4; + nsamples = nsamples & 3; + + while (unrolled--) { + float32x4_t samples = vld1q_f32(src); + int16x4_t converted = float_16_neon(samples); + + switch(dst_skip) { + case 2: + vst1_s16((int16_t*)dst, converted); + break; + default: + vst1_lane_s16((int16_t*)(dst), converted, 0); + vst1_lane_s16((int16_t*)(dst+dst_skip), converted, 1); + vst1_lane_s16((int16_t*)(dst+2*dst_skip), converted, 2); + vst1_lane_s16((int16_t*)(dst+3*dst_skip), converted, 3); + break; + } + dst += 4*dst_skip; + src+= 4; + } +#endif while (nsamples--) { float_16 (*src, *((int16_t*) dst)); dst += dst_skip; @@ -730,6 +1017,36 @@ { short z; const jack_default_audio_sample_t scaling = 1.0/SAMPLE_16BIT_SCALING; +#ifdef __ARM_NEON__ + const float32x4_t vscaling = vdupq_n_f32(scaling); + unsigned long unrolled = nsamples / 4; + while (unrolled--) { + int16x4_t source16x4; + switch(src_skip) { + case 2: + source16x4 = vld1_s16((int16_t*)src); + break; + case 4: + source16x4 = vld2_s16((int16_t*)src).val[0]; + break; + default: + source16x4 = vld1_lane_s16((int16_t*)src, source16x4, 0); + source16x4 = vld1_lane_s16((int16_t*)(src+src_skip), source16x4, 1); + source16x4 = vld1_lane_s16((int16_t*)(src+2*src_skip), source16x4, 2); + source16x4 = vld1_lane_s16((int16_t*)(src+3*src_skip), source16x4, 3); + break; + } + source16x4 = vreinterpret_s16_u8(vrev16_u8(vreinterpret_u8_s16(source16x4))); + int32x4_t source32x4 = vmovl_s16(source16x4); + src += 4 * src_skip; + + float32x4_t converted = vcvtq_f32_s32(source32x4); + float32x4_t scaled = vmulq_f32(converted, vscaling); + vst1q_f32(dst, scaled); + dst += 4; + } + nsamples = nsamples & 3; +#endif /* ALERT: signed sign-extension portability !!! */ while (nsamples--) { @@ -752,6 +1069,36 @@ { /* ALERT: signed sign-extension portability !!! */ const jack_default_audio_sample_t scaling = 1.0/SAMPLE_16BIT_SCALING; +#ifdef __ARM_NEON__ + const float32x4_t vscaling = vdupq_n_f32(scaling); + unsigned long unrolled = nsamples / 4; + while (unrolled--) { + int16x4_t source16x4; + switch(src_skip) { + case 2: + source16x4 = vld1_s16((int16_t*)src); + break; + case 4: + source16x4 = vld2_s16((int16_t*)src).val[0]; + break; + default: + source16x4 = vld1_lane_s16((int16_t*)src, source16x4, 0); + source16x4 = vld1_lane_s16((int16_t*)(src+src_skip), source16x4, 1); + source16x4 = vld1_lane_s16((int16_t*)(src+2*src_skip), source16x4, 2); + source16x4 = vld1_lane_s16((int16_t*)(src+3*src_skip), source16x4, 3); + break; + } + int32x4_t source32x4 = vmovl_s16(source16x4); + src += 4 * src_skip; + + float32x4_t converted = vcvtq_f32_s32(source32x4); + float32x4_t scaled = vmulq_f32(converted, vscaling); + vst1q_f32(dst, scaled); + dst += 4; + } + nsamples = nsamples & 3; +#endif + while (nsamples--) { *dst = (*((short *) src)) * scaling; dst++; diff -Nru jackd2-1.9.11~20161209/common/wscript jackd2-1.9.11+git20170722/common/wscript --- jackd2-1.9.11~20161209/common/wscript 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/common/wscript 2017-07-22 08:02:50.000000000 +0000 @@ -10,19 +10,21 @@ if conf.env['IS_WINDOWS']: try: - conf.check(function_name='regcomp', header_name='regex.h', lib='regex', uselib_store="REGEX", define_name='HAVE_REGEX_H') + conf.check(function_name='regcomp', header_name='regex.h', lib='regex', uselib_store='REGEX', define_name='HAVE_REGEX_H') except: - conf.check(function_name='regcomp', header_name='regex.h', lib='tre', uselib_store="REGEX", define_name='HAVE_REGEX_H') - conf.check(function_name='htons', header_name='winsock2.h', lib='ws2_32', uselib_store="WS2_32", define_name='HAVE_WINSOCK2_H') - conf.check(function_name='timeGetDevCaps', header_name=['windows.h', 'mmsystem.h'], lib='winmm', uselib_store="WINMM", define_name='HAVE_MMSYSTEM_H') - conf.check(function_name='EnumProcesses', header_name=['windows.h', 'psapi.h'], lib='psapi', uselib_store="PSAPI", define_name='HAVE_PSAPI_H') + conf.check(function_name='regcomp', header_name='regex.h', lib='tre', uselib_store='REGEX', define_name='HAVE_REGEX_H') + conf.check(function_name='htons', header_name='winsock2.h', lib='ws2_32', uselib_store='WS2_32', define_name='HAVE_WINSOCK2_H') + conf.check(function_name='timeGetDevCaps', header_name=['windows.h', 'mmsystem.h'], lib='winmm', uselib_store='WINMM', define_name='HAVE_MMSYSTEM_H') + conf.check(function_name='EnumProcesses', header_name=['windows.h', 'psapi.h'], lib='psapi', uselib_store='PSAPI', define_name='HAVE_PSAPI_H') -def create_jack_process_obj(bld, target, sources, uselib = None): +def create_jack_process_obj(bld, target, sources, uselib = None, framework = None): process = bld(features = ['cxx', 'cxxshlib']) if not bld.env['IS_WINDOWS']: process.env['cxxshlib_PATTERN'] = '%s.so' process.defines = ['HAVE_CONFIG_H','SERVER_SIDE'] if bld.env['IS_MACOSX']: + if framework: + process.framework = framework env_includes = ['../macosx', '../posix', '../macosx/coreaudio'] if bld.env['IS_LINUX']: env_includes = ['../linux', '../posix', '../linux/alsa'] @@ -34,12 +36,8 @@ process.name = target process.target = target process.source = sources - if bld.env['IS_LINUX']: - process.env.append_value("CPPFLAGS", "-fvisibility=hidden") - if bld.env['IS_MACOSX']: - process.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") - #process.env.append_value("LINKFLAGS", "-arch i386 -arch ppc -arch x86_64") - process.env.append_value("CPPFLAGS", "-fvisibility=hidden") + if bld.env['IS_LINUX'] or bld.env['IS_MACOSX']: + process.env.append_value('CPPFLAGS', '-fvisibility=hidden') process.install_path = '${ADDON_DIR}/' process.use = [uselib.name] return process @@ -76,17 +74,17 @@ includes.append('..') else: includes.append('../..') - uselib = ["PTHREAD", "CELT", "OPUS"] + uselib = ['PTHREAD', 'CELT', 'OPUS'] if bld.env['IS_LINUX']: common_libsources += [ 'JackDebugClient.cpp', 'timestamps.c', '../posix/JackPosixThread.cpp', - '../posix/JackPosixSemaphore.cpp', '../posix/JackPosixProcessSync.cpp', '../posix/JackPosixMutex.cpp', '../posix/JackSocket.cpp', + '../linux/JackLinuxFutex.cpp', '../linux/JackLinuxTime.c', ] includes = ['../linux', '../posix'] + includes @@ -113,8 +111,10 @@ 'timestamps.c', '../posix/JackPosixProcessSync.cpp', '../posix/JackPosixThread.cpp', - '../macosx/JackMachThread.cpp', - '../macosx/JackMachSemaphore.cpp', + '../posix/JackPosixMutex.cpp', + '../macosx/JackMachThread.mm', + #'../macosx/JackMachSemaphore.mm', + '../posix/JackPosixSemaphore.cpp', '../posix/JackSocket.cpp', '../macosx/JackMachTime.c', ] @@ -135,13 +135,16 @@ uselib.append('WINMM') clientlib = bld(features = ['c', 'cxx', 'cxxshlib', 'cshlib']) + if bld.env['IS_MACOSX']: + clientlib.framework = ['CoreAudio', 'Accelerate'] clientlib.defines = 'HAVE_CONFIG_H' clientlib.use = uselib if bld.env['IS_WINDOWS']: + clientlib.env['cxxshlib_PATTERN'] = 'lib%s.dll' clientlib.install_path = '${BINDIR}' else: clientlib.install_path = '${LIBDIR}' - if bld.env['AUTOSTART_METHOD'] == "dbus": + if bld.env['AUTOSTART_METHOD'] == 'dbus': clientlib.use.append('DBUS-1') clientlib.includes = includes clientlib.name = 'clientlib' @@ -181,17 +184,14 @@ clientlib.vnum = bld.env['JACK_API_VERSION'] if bld.env['IS_LINUX']: - clientlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + clientlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_MACOSX']: - clientlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") - clientlib.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") - #clientlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework vecLib -single_module -arch i386 -arch ppc -arch x86_64" - clientlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework vecLib -single_module") - clientlib.env.append_value("LINKFLAGS", "-compatibility_version 1 -current_version 1") + clientlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') + clientlib.env.append_value('LINKFLAGS', '-single_module') if bld.env['IS_SUN']: - clientlib.env.append_value("LINKFLAGS", "-lnsl -lsocket") + clientlib.env.append_value('LINKFLAGS', '-lnsl -lsocket') if bld.variant: # if there is variant defined, we expect it to be the 32bit client lib one @@ -199,12 +199,15 @@ return serverlib = bld(features = ['c', 'cxx', 'cxxshlib', 'cshlib']) + if bld.env['IS_MACOSX']: + serverlib.framework = ['CoreAudio', 'CoreFoundation', 'Accelerate'] serverlib.defines = ['HAVE_CONFIG_H','SERVER_SIDE'] serverlib.includes = includes serverlib.name = 'serverlib' serverlib.target = 'jackserver' serverlib.use = uselib if bld.env['IS_WINDOWS']: + serverlib.env['cxxshlib_PATTERN'] = 'lib%s.dll' serverlib.install_path = '${BINDIR}' else: serverlib.install_path = '${LIBDIR}' @@ -281,28 +284,30 @@ serverlib.vnum = bld.env['JACK_API_VERSION'] if bld.env['IS_LINUX']: - serverlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + serverlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_MACOSX']: - serverlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") - serverlib.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") - #serverlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework vecLib -single_module -arch i386 -arch ppc -arch x86_64") - serverlib.env.append_value("LINKFLAGS", "-framework CoreAudio -framework CoreFoundation -framework vecLib -single_module") - serverlib.env.append_value("LINKFLAGS", "-compatibility_version 1 -current_version 1") + serverlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') + serverlib.env.append_value('LINKFLAGS', '-single_module') if bld.env['IS_SUN']: - serverlib.env.append_value("LINKFLAGS", "-lnsl -lsocket") + serverlib.env.append_value('LINKFLAGS', '-lnsl -lsocket') if bld.env['BUILD_NETLIB']: netlib = bld(features = ['c', 'cxx', 'cxxshlib', 'cshlib']) + if bld.env['IS_MACOSX']: + netlib.framework = ['CoreAudio'] netlib.defines = ['HAVE_CONFIG_H','SERVER_SIDE'] netlib.includes = includes netlib.name = 'netlib' netlib.target = 'jacknet' netlib.use = ['SAMPLERATE', 'CELT', 'OPUS', 'PTHREAD'] if bld.env['IS_WINDOWS']: + netlib.env['cxxshlib_PATTERN'] = 'lib%s.dll' netlib.install_path = '${BINDIR}' netlib.use += ['WS2_32', 'WINMM'] + elif bld.env['IS_MACOSX']: + netlib.install_path = '${LIBDIR}' else: netlib.use += ['RT'] netlib.install_path = '${LIBDIR}' @@ -319,16 +324,16 @@ if bld.env['IS_LINUX']: netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../linux/JackLinuxTime.c'] - netlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + netlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_SUN']: netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../solaris/JackSolarisTime.c'] - netlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") - + netlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') + if bld.env['IS_MACOSX']: - netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../macosx/JackMachThread.cpp', '../macosx/JackMachTime.c'] - netlib.env.append_value("LINKFLAGS", "-framework CoreAudio -single_module") + netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../macosx/JackMachThread.mm', '../macosx/JackMachTime.c'] + netlib.env.append_value('LINKFLAGS', '-single_module') if bld.env['IS_WINDOWS']: netlib.source += ['../windows/JackNetWinSocket.cpp','../windows/JackWinThread.cpp', '../windows/JackMMCSS.cpp', '../windows/JackWinTime.c'] @@ -360,10 +365,19 @@ ] if bld.env['BUILD_ADAPTER'] and bld.env['IS_MACOSX']: - audio_adapter_sources += ['../macosx/coreaudio/JackCoreAudioAdapter.cpp'] - process = create_jack_process_obj(bld, 'audioadapter', audio_adapter_sources, serverlib) - process.env.append_value("LINKFLAGS", "-framework CoreAudio -framework AudioUnit -framework AudioToolbox -framework CoreServices") - process.use = 'SAMPLERATE' + audio_adapter_sources += ['../macosx/coreaudio/JackCoreAudioAdapter.mm'] + process = create_jack_process_obj(bld, + 'audioadapter', + audio_adapter_sources, + serverlib, + framework = [ + 'CoreAudio', + 'AudioUnit', + 'AudioToolbox', + 'CoreServices' + ] + ) + process.use += ['SAMPLERATE'] if bld.env['BUILD_ADAPTER'] and bld.env['IS_LINUX'] and bld.env['BUILD_DRIVER_ALSA']: audio_adapter_sources += ['../linux/alsa/JackAlsaAdapter.cpp'] diff -Nru jackd2-1.9.11~20161209/dbus/audio_reserve.c jackd2-1.9.11+git20170722/dbus/audio_reserve.c --- jackd2-1.9.11~20161209/dbus/audio_reserve.c 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/dbus/audio_reserve.c 2017-07-22 08:02:50.000000000 +0000 @@ -47,7 +47,8 @@ dbus_error_init(&error); if (!(gConnection = dbus_bus_get(DBUS_BUS_SESSION, &error))) { - jack_error("Failed to connect to session bus for device reservation %s\n", error.message); + jack_error("Failed to connect to session bus for device reservation: %s\n", error.message); + jack_info("To bypass device reservation via session bus, set JACK_NO_AUDIO_RESERVATION=1 prior to starting jackd.\n"); return -1; } diff -Nru jackd2-1.9.11~20161209/dbus/sigsegv.c jackd2-1.9.11+git20170722/dbus/sigsegv.c --- jackd2-1.9.11~20161209/dbus/sigsegv.c 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/dbus/sigsegv.c 2017-07-22 08:00:34.000000000 +0000 @@ -27,7 +27,9 @@ #include #include #include -#include +#ifdef HAVE_EXECINFO_H +# include +#endif #include #ifndef NO_CPP_DEMANGLE char * __cxa_demangle(const char * __mangled_name, char * __output_buffer, size_t * __length, int * __status); @@ -104,20 +106,20 @@ jack_error("info.si_errno = %d", info->si_errno); jack_error("info.si_code = %d (%s)", info->si_code, si_code_str); jack_error("info.si_addr = %p", info->si_addr); -#if !defined(__alpha__) && !defined(__ia64__) && !defined(__FreeBSD_kernel__) && !defined(__arm__) && !defined(__hppa__) && !defined(__sh__) && !defined(__aarch64__) && !defined(nios2) +#if defined(HAVE_UCONTEXT) && defined(HAVE_NGREG) for(i = 0; i < NGREG; i++) jack_error("reg[%02d] = 0x" REGFORMAT, i, -#if defined(__powerpc64__) +#if defined(HAVE_UCONTEXT_GP_REGS) ucontext->uc_mcontext.gp_regs[i] -#elif defined(__powerpc__) +#elif defined(HAVE_UCONTEXT_UC_REGS) ucontext->uc_mcontext.uc_regs[i] -#elif defined(__sparc__) && defined(__arch64__) +#elif defined(HAVE_UCONTEXT_MC_GREGS) ucontext->uc_mcontext.mc_gregs[i] -#else +#elif defined(HAVE_UCONTEXT_GREGS) ucontext->uc_mcontext.gregs[i] #endif ); -#endif /* alpha, ia64, kFreeBSD, arm, hppa */ +#endif /* defined(HAVE_UCONTEXT) && defined(HAVE_NGREG) */ #if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64) # if defined(SIGSEGV_STACK_IA64) @@ -161,12 +163,16 @@ bp = (void**)bp[0]; } #else +# ifdef HAVE_EXECINFO_H jack_error("Stack trace (non-dedicated):"); sz = backtrace(bt, 20); strings = backtrace_symbols(bt, sz); for(i = 0; i < sz; ++i) jack_error("%s", strings[i]); +# else + jack_error("Stack trace not available"); +# endif #endif jack_error("End of stack trace"); exit (-1); diff -Nru jackd2-1.9.11~20161209/debian/changelog jackd2-1.9.11+git20170722/debian/changelog --- jackd2-1.9.11~20161209/debian/changelog 2016-12-30 15:41:27.000000000 +0000 +++ jackd2-1.9.11+git20170722/debian/changelog 2017-07-22 09:58:36.000000000 +0000 @@ -1,8 +1,14 @@ -jackd2 (2:1.9.11~20161209-1~kxstudio5v5) xenial; urgency=medium +jackd2 (2:1.9.11+git20170722-1~kxstudio2v5) xenial; urgency=medium * GCC5 build - -- falkTX Fri, 30 Dec 2016 16:41:13 +0100 + -- falkTX Sat, 22 Jul 2017 11:58:08 +0200 + +jackd2 (2:1.9.11+git20170722-1~kxstudio2) trusty; urgency=medium + + * Update + + -- falkTX Sat, 22 Jul 2017 10:26:53 +0200 jackd2 (2:1.9.11~20161209-1~kxstudio5) trusty; urgency=medium diff -Nru jackd2-1.9.11~20161209/debian/patches/fix-dbus-autostart-command.patch jackd2-1.9.11+git20170722/debian/patches/fix-dbus-autostart-command.patch --- jackd2-1.9.11~20161209/debian/patches/fix-dbus-autostart-command.patch 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/debian/patches/fix-dbus-autostart-command.patch 2017-07-22 09:45:47.000000000 +0000 @@ -0,0 +1,11 @@ +--- jackd2-1.9.11+git20170722.orig/wscript ++++ jackd2-1.9.11+git20170722/wscript +@@ -565,7 +565,7 @@ def configure(conf): + conf.env.append_unique('CFLAGS', '-g') + conf.env.append_unique('LINKFLAGS', '-g') + +- if not Options.options.autostart in ['default', 'classic', 'dbus' 'none']: ++ if not Options.options.autostart in ['default', 'classic', 'dbus', 'none']: + conf.fatal('Invalid autostart value "' + Options.options.autostart + '"') + + if Options.options.autostart == 'default': diff -Nru jackd2-1.9.11~20161209/debian/patches/mod/02-silent_xruns.patch jackd2-1.9.11+git20170722/debian/patches/mod/02-silent_xruns.patch --- jackd2-1.9.11~20161209/debian/patches/mod/02-silent_xruns.patch 2016-03-31 14:24:59.000000000 +0000 +++ jackd2-1.9.11+git20170722/debian/patches/mod/02-silent_xruns.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -diff --git a/common/JackAudioDriver.cpp b/common/JackAudioDriver.cpp -index e699bef..2202a61 100644 ---- a/common/JackAudioDriver.cpp -+++ b/common/JackAudioDriver.cpp -@@ -247,7 +247,6 @@ void JackAudioDriver::ProcessGraphAsyncMaster() - { - // fBeginDateUst is set in the "low level" layer, fEndDateUst is from previous cycle - if (!fEngine->Process(fBeginDateUst, fEndDateUst)) { -- jack_error("JackAudioDriver::ProcessGraphAsyncMaster: Process error"); - } - - if (ResumeRefNum() < 0) { -diff --git a/common/JackEngine.cpp b/common/JackEngine.cpp -index 1507659..8883663 100644 ---- a/common/JackEngine.cpp -+++ b/common/JackEngine.cpp -@@ -218,12 +218,10 @@ void JackEngine::CheckXRun(jack_time_t callback_usecs) // REVOIR les conditions - jack_time_t finished_date = timing->fFinishedAt; - - if (status != NotTriggered && status != Finished) { -- jack_error("JackEngine::XRun: client = %s was not finished, state = %s", client->GetClientControl()->fName, State2String(status)); - fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0); // Notify all clients - } - - if (status == Finished && (long)(finished_date - callback_usecs) > 0) { -- jack_error("JackEngine::XRun: client %s finished after current callback", client->GetClientControl()->fName); - fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0); // Notify all clients - } - } diff -Nru jackd2-1.9.11~20161209/debian/patches/mod/04-use_gettime_realtime.patch jackd2-1.9.11+git20170722/debian/patches/mod/04-use_gettime_realtime.patch --- jackd2-1.9.11~20161209/debian/patches/mod/04-use_gettime_realtime.patch 2016-12-09 10:11:03.000000000 +0000 +++ jackd2-1.9.11+git20170722/debian/patches/mod/04-use_gettime_realtime.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,149 +0,0 @@ -diff --git a/linux/JackLinuxTime.c b/linux/JackLinuxTime.c -index a5ece76..7109679 100644 ---- a/linux/JackLinuxTime.c -+++ b/linux/JackLinuxTime.c -@@ -143,7 +143,11 @@ static jack_time_t jack_get_microseconds_from_system (void) - jack_time_t jackTime; - struct timespec time; - -+#if defined(__arm__) -+ clock_gettime(CLOCK_MONOTONIC_RAW, &time); -+#else - clock_gettime(CLOCK_MONOTONIC, &time); -+#endif - jackTime = (jack_time_t) time.tv_sec * 1e6 + - (jack_time_t) time.tv_nsec / 1e3; - return jackTime; -diff --git a/posix/JackPosixProcessSync.cpp b/posix/JackPosixProcessSync.cpp -index 04c8785..43190a6 100644 ---- a/posix/JackPosixProcessSync.cpp -+++ b/posix/JackPosixProcessSync.cpp -@@ -108,67 +108,55 @@ bool JackPosixProcessSync::TimedWait(long usec) - ThrowIf(!pthread_equal(pthread_self(), fOwner), JackException("JackPosixProcessSync::TimedWait: a thread has to have locked a mutex before it can wait")); - fOwner = 0; - -- struct timeval T0, T1; -- timespec time; -- struct timeval now; - int res; -+ timespec now; -+ clock_gettime(CLOCK_REALTIME, &now); - -- jack_log("JackPosixProcessSync::TimedWait time out = %ld", usec); -- gettimeofday(&T0, 0); -- -- gettimeofday(&now, 0); -- unsigned int next_date_usec = now.tv_usec + usec; -- time.tv_sec = now.tv_sec + (next_date_usec / 1000000); -- time.tv_nsec = (next_date_usec % 1000000) * 1000; -+ timespec delta = { usec / 1000000, (usec % 1000000) * 1000 }; -+ timespec end = { now.tv_sec + delta.tv_sec, now.tv_nsec + delta.tv_nsec }; -+ if (end.tv_nsec >= 1000000000L) { -+ ++end.tv_sec; -+ end.tv_nsec -= 1000000000L; -+ } - -- res = pthread_cond_timedwait(&fCond, &fMutex, &time); -+ res = pthread_cond_timedwait(&fCond, &fMutex, &end); - if (res != 0) { - jack_error("JackPosixProcessSync::TimedWait error usec = %ld err = %s", usec, strerror(res)); - } else { - fOwner = pthread_self(); - } - -- gettimeofday(&T1, 0); -- jack_log("JackPosixProcessSync::TimedWait finished delta = %5.1lf", -- (1e6 * T1.tv_sec - 1e6 * T0.tv_sec + T1.tv_usec - T0.tv_usec)); -- - return (res == 0); - } - - // TO DO : check thread consistency? - bool JackPosixProcessSync::LockedTimedWait(long usec) - { -- struct timeval T0, T1; -- timespec time; -- struct timeval now; -- int res1, res2; -- -- res1 = pthread_mutex_lock(&fMutex); -+ int res1 = pthread_mutex_lock(&fMutex); - if (res1 != 0) { - jack_error("JackPosixProcessSync::LockedTimedWait error err = %s", usec, strerror(res1)); - } - -- jack_log("JackPosixProcessSync::TimedWait time out = %ld", usec); -- gettimeofday(&T0, 0); -+ timespec now; -+ clock_gettime(CLOCK_REALTIME, &now); - -- gettimeofday(&now, 0); -- unsigned int next_date_usec = now.tv_usec + usec; -- time.tv_sec = now.tv_sec + (next_date_usec / 1000000); -- time.tv_nsec = (next_date_usec % 1000000) * 1000; -- res2 = pthread_cond_timedwait(&fCond, &fMutex, &time); -+ timespec delta = { usec / 1000000, (usec % 1000000) * 1000 }; -+ timespec end = { now.tv_sec + delta.tv_sec, now.tv_nsec + delta.tv_nsec }; -+ if (end.tv_nsec >= 1000000000L) { -+ ++end.tv_sec; -+ end.tv_nsec -= 1000000000L; -+ } -+ -+ int res2 = pthread_cond_timedwait(&fCond, &fMutex, &end); - if (res2 != 0) { - jack_error("JackPosixProcessSync::LockedTimedWait error usec = %ld err = %s", usec, strerror(res2)); - } - -- gettimeofday(&T1, 0); - res1 = pthread_mutex_unlock(&fMutex); - if (res1 != 0) { - jack_error("JackPosixProcessSync::LockedTimedWait error err = %s", usec, strerror(res1)); - } - -- jack_log("JackPosixProcessSync::TimedWait finished delta = %5.1lf", -- (1e6 * T1.tv_sec - 1e6 * T0.tv_sec + T1.tv_usec - T0.tv_usec)); -- - return (res2 == 0); - } - -diff --git a/posix/JackPosixSemaphore.cpp b/posix/JackPosixSemaphore.cpp -index 6eccbe5..3f1d665 100644 ---- a/posix/JackPosixSemaphore.cpp -+++ b/posix/JackPosixSemaphore.cpp -@@ -115,24 +115,24 @@ bool JackPosixSemaphore::Wait() - - bool JackPosixSemaphore::TimedWait(long usec) - { -- int res; -- struct timeval now; -- timespec time; -- - if (!fSemaphore) { - jack_error("JackPosixSemaphore::TimedWait name = %s already deallocated!!", fName); - return false; - } -- gettimeofday(&now, 0); -- time.tv_sec = now.tv_sec + usec / 1000000; -- long tv_usec = (now.tv_usec + (usec % 1000000)); -- time.tv_sec += tv_usec / 1000000; -- time.tv_nsec = (tv_usec % 1000000) * 1000; - -- while ((res = sem_timedwait(fSemaphore, &time)) < 0) { -+ int res; -+ timespec now; -+ clock_gettime(CLOCK_REALTIME, &now); -+ -+ timespec delta = { usec / 1000000, (usec % 1000000) * 1000 }; -+ timespec end = { now.tv_sec + delta.tv_sec, now.tv_nsec + delta.tv_nsec }; -+ if (end.tv_nsec >= 1000000000L) { -+ ++end.tv_sec; -+ end.tv_nsec -= 1000000000L; -+ } -+ -+ while ((res = sem_timedwait(fSemaphore, &end)) < 0) { - jack_error("JackPosixSemaphore::TimedWait err = %s", strerror(errno)); -- jack_log("JackPosixSemaphore::TimedWait now : %ld %ld ", now.tv_sec, now.tv_usec); -- jack_log("JackPosixSemaphore::TimedWait next : %ld %ld ", time.tv_sec, time.tv_nsec/1000); - if (errno != EINTR) { - break; - } diff -Nru jackd2-1.9.11~20161209/debian/patches/mod/05-enable_flush_denormal_to_zero.patch jackd2-1.9.11+git20170722/debian/patches/mod/05-enable_flush_denormal_to_zero.patch --- jackd2-1.9.11~20161209/debian/patches/mod/05-enable_flush_denormal_to_zero.patch 2016-05-05 09:26:30.000000000 +0000 +++ jackd2-1.9.11+git20170722/debian/patches/mod/05-enable_flush_denormal_to_zero.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -diff --git a/common/JackClient.cpp b/common/JackClient.cpp -index 7334a57..de78ecd 100644 ---- a/common/JackClient.cpp -+++ b/common/JackClient.cpp -@@ -32,6 +32,37 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - #include - #include - -+/* Disables denormal numbers in floating point calculation. Denormal numbers -+ * happens often in IIR filters, and it can be very slow. -+ */ -+/* Taken from cras/src/dsp/dsp_util.c in Chromium OS code. -+ * Copyright (c) 2013 The Chromium OS Authors. */ -+static void dsp_enable_flush_denormal_to_zero() -+{ -+#if defined(__i386__) || defined(__x86_64__) -+ unsigned int mxcsr; -+ mxcsr = __builtin_ia32_stmxcsr(); -+ __builtin_ia32_ldmxcsr(mxcsr | 0x8040); -+#elif defined(__aarch64__) -+ uint64_t cw; -+ __asm__ __volatile__ ( -+ "mrs %0, fpcr \n" -+ "orr %0, %0, #0x1000000 \n" -+ "msr fpcr, %0 \n" -+ "isb \n" -+ : "=r"(cw) :: "memory"); -+#elif defined(__arm__) -+ uint32_t cw; -+ __asm__ __volatile__ ( -+ "vmrs %0, fpscr \n" -+ "orr %0, %0, #0x1000000 \n" -+ "vmsr fpscr, %0 \n" -+ : "=r"(cw) :: "memory"); -+#else -+#warning "Don't know how to disable denorms. Performace may suffer." -+#endif -+} -+ - using namespace std; - - namespace Jack -@@ -492,6 +523,8 @@ int JackClient::Deactivate() - - void JackClient::InitAux() - { -+ dsp_enable_flush_denormal_to_zero(); -+ - if (fInit) { - jack_log("JackClient::Init calling client thread init callback"); - fInit(fInitArg); diff -Nru jackd2-1.9.11~20161209/debian/patches/mod/06-ignore_xrun_delayed_usecs.patch jackd2-1.9.11+git20170722/debian/patches/mod/06-ignore_xrun_delayed_usecs.patch --- jackd2-1.9.11~20161209/debian/patches/mod/06-ignore_xrun_delayed_usecs.patch 2016-03-31 14:24:59.000000000 +0000 +++ jackd2-1.9.11+git20170722/debian/patches/mod/06-ignore_xrun_delayed_usecs.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp -index 62d9f43..082a79c 100644 ---- a/linux/alsa/JackAlsaDriver.cpp -+++ b/linux/alsa/JackAlsaDriver.cpp -@@ -409,7 +409,7 @@ int JackAlsaDriver::Read() - - retry: - -- nframes = alsa_driver_wait((alsa_driver_t *)fDriver, -1, &wait_status, &fDelayedUsecs); -+ nframes = alsa_driver_wait((alsa_driver_t *)fDriver, -1, &wait_status); - - if (wait_status < 0) - return -1; /* driver failed */ -diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c -index 7460efd..a6db009 100644 ---- a/linux/alsa/alsa_driver.c -+++ b/linux/alsa/alsa_driver.c -@@ -1169,7 +1169,7 @@ alsa_driver_restart (alsa_driver_t *driver) - } - - static int --alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) -+alsa_driver_xrun_recovery (alsa_driver_t *driver) - { - snd_pcm_status_t *status; - int res; -@@ -1206,13 +1206,7 @@ alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) - - if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN - && driver->process_count > XRUN_REPORT_DELAY) { -- struct timeval now, diff, tstamp; - driver->xrun_count++; -- snd_pcm_status_get_tstamp(status,&now); -- snd_pcm_status_get_trigger_tstamp(status, &tstamp); -- timersub(&now, &tstamp, &diff); -- *delayed_usecs = diff.tv_sec * 1000000.0 + diff.tv_usec; -- jack_log("**** alsa_pcm: xrun of at least %.3f msecs",*delayed_usecs / 1000.0); - } - - if (alsa_driver_restart (driver)) { -@@ -1251,8 +1245,7 @@ alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, - static int under_gdb = FALSE; - - jack_nframes_t --alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float -- *delayed_usecs) -+alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status) - { - snd_pcm_sframes_t avail = 0; - snd_pcm_sframes_t capture_avail = 0; -@@ -1265,7 +1258,6 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float - jack_time_t poll_ret = 0; - - *status = -1; -- *delayed_usecs = 0; - - need_capture = driver->capture_handle ? 1 : 0; - -@@ -1357,9 +1349,6 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float - SetTime(poll_ret); - - if (extra_fd < 0) { -- if (driver->poll_next && poll_ret > driver->poll_next) { -- *delayed_usecs = poll_ret - driver->poll_next; -- } - driver->poll_last = poll_ret; - driver->poll_next = poll_ret + driver->period_usecs; - // JACK2 -@@ -1479,7 +1468,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float - } - - if (xrun_detected) { -- *status = alsa_driver_xrun_recovery (driver, delayed_usecs); -+ *status = alsa_driver_xrun_recovery (driver); - return 0; - } - -diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h -index efe93de..b9d2daf 100644 ---- a/linux/alsa/alsa_driver.h -+++ b/linux/alsa/alsa_driver.h -@@ -260,8 +260,7 @@ int - alsa_driver_stop (alsa_driver_t *driver); - - jack_nframes_t --alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float -- *delayed_usecs); -+alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status); - - int - alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes); diff -Nru jackd2-1.9.11~20161209/debian/patches/mod/07-pm_qos_lowlatency.patch jackd2-1.9.11+git20170722/debian/patches/mod/07-pm_qos_lowlatency.patch --- jackd2-1.9.11~20161209/debian/patches/mod/07-pm_qos_lowlatency.patch 2016-03-31 14:24:59.000000000 +0000 +++ jackd2-1.9.11+git20170722/debian/patches/mod/07-pm_qos_lowlatency.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -diff --git a/common/Jackdmp.cpp b/common/Jackdmp.cpp -index a2ccd9c..a80e05e 100644 ---- a/common/Jackdmp.cpp -+++ b/common/Jackdmp.cpp -@@ -28,6 +28,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - #include - #include - -+#include -+#include -+ - #include "types.h" - #include "jack.h" - #include "control.h" -@@ -538,6 +541,18 @@ int main(int argc, char** argv) - goto destroy_server; - } - -+ /* -+ Require the fastest response time possible regarding OS latency, -+ See https://access.redhat.com/articles/65410 -+ */ -+ { -+ const int32_t v = 0; -+ const int fd = open("/dev/cpu_dma_latency", O_WRONLY); -+ -+ if (fd >= 0) -+ write(fd, &v, sizeof(v)); -+ } -+ - // Setup signals - sigmask = jackctl_setup_signals(0); - diff -Nru jackd2-1.9.11~20161209/debian/patches/mod/08-futex_sync.patch jackd2-1.9.11+git20170722/debian/patches/mod/08-futex_sync.patch --- jackd2-1.9.11~20161209/debian/patches/mod/08-futex_sync.patch 2016-12-12 14:32:49.000000000 +0000 +++ jackd2-1.9.11+git20170722/debian/patches/mod/08-futex_sync.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,410 +0,0 @@ -diff --git a/common/JackEngine.cpp b/common/JackEngine.cpp -index 1507659..0f05435 100644 ---- a/common/JackEngine.cpp -+++ b/common/JackEngine.cpp -@@ -615,7 +615,7 @@ int JackEngine::ClientExternalOpen(const char* name, int pid, int uuid, int* ref - - JackExternalClient* client = new JackExternalClient(); - -- if (!fSynchroTable[refnum].Allocate(real_name, fEngineControl->fServerName, 0)) { -+ if (!fSynchroTable[refnum].Allocate(real_name, fEngineControl->fServerName, 0, false)) { - jack_error("Cannot allocate synchro"); - goto error; - } -@@ -665,7 +665,7 @@ int JackEngine::ClientInternalOpen(const char* name, int* ref, JackEngineControl - goto error; - } - -- if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0)) { -+ if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0, false)) { - jack_error("Cannot allocate synchro"); - goto error; - } -diff --git a/common/JackMidiAsyncWaitQueue.cpp b/common/JackMidiAsyncWaitQueue.cpp -index 016737c..6d63a4d 100644 ---- a/common/JackMidiAsyncWaitQueue.cpp -+++ b/common/JackMidiAsyncWaitQueue.cpp -@@ -29,7 +29,7 @@ JackMidiAsyncWaitQueue::JackMidiAsyncWaitQueue(size_t max_bytes, - size_t max_messages): - JackMidiAsyncQueue(max_bytes, max_messages) - { -- if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0)) { -+ if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0, true)) { - throw std::bad_alloc(); - } - } -diff --git a/common/wscript b/common/wscript -index b64d275..2cff1b5 100644 ---- a/common/wscript -+++ b/common/wscript -@@ -83,10 +83,10 @@ def build(bld): - 'JackDebugClient.cpp', - 'timestamps.c', - '../posix/JackPosixThread.cpp', -- '../posix/JackPosixSemaphore.cpp', - '../posix/JackPosixProcessSync.cpp', - '../posix/JackPosixMutex.cpp', - '../posix/JackSocket.cpp', -+ '../linux/JackLinuxFutex.cpp', - '../linux/JackLinuxTime.c', - ] - includes = ['../linux', '../posix'] + includes -diff --git a/linux/JackLinuxFutex.cpp b/linux/JackLinuxFutex.cpp -new file mode 100644 -index 0000000..c5f4d09 ---- /dev/null -+++ b/linux/JackLinuxFutex.cpp -@@ -0,0 +1,247 @@ -+/* -+Copyright (C) 2004-2008 Grame -+Copyright (C) 2016 Filipe Coelho -+ -+This program is free software; you can redistribute it and/or modify -+it under the terms of the GNU Lesser General Public License as published by -+the Free Software Foundation; either version 2.1 of the License, or -+(at your option) any later version. -+ -+This program is distributed in the hope that it will be useful, -+but WITHOUT ANY WARRANTY; without even the implied warranty of -+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+GNU Lesser General Public License for more details. -+ -+You should have received a copy of the GNU Lesser General Public License -+along with this program; if not, write to the Free Software -+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ -+*/ -+ -+#include "JackLinuxFutex.h" -+#include "JackTools.h" -+#include "JackConstants.h" -+#include "JackError.h" -+#include -+#include -+#include -+#include -+#include -+ -+namespace Jack -+{ -+ -+void JackLinuxFutex::BuildName(const char* client_name, const char* server_name, char* res, int size) -+{ -+ char ext_client_name[SYNC_MAX_NAME_SIZE + 1]; -+ JackTools::RewriteName(client_name, ext_client_name); -+#if 1 -+ snprintf(res, size, "jack_sem.%s", ext_client_name); -+#else -+ if (getenv("JACK_PROMISCUOUS_SERVER")) { -+ snprintf(res, size, "jack_sem.%s_%s", server_name, ext_client_name); -+ } else { -+ snprintf(res, size, "jack_sem.%d_%s_%s", JackTools::GetUID(), server_name, ext_client_name); -+ } -+#endif -+} -+ -+bool JackLinuxFutex::Signal() -+{ -+ if (!fFutex) { -+ jack_error("JackLinuxFutex::Signal name = %s already deallocated!!", fName); -+ return false; -+ } -+ -+ if (fFlush) { -+ return true; -+ } -+ -+ if (! __sync_bool_compare_and_swap(&fFutex->futex, 0, 1)) -+ { -+ // already unlocked, do not wake futex -+ if (! fFutex->internal) return true; -+ } -+ -+ ::syscall(__NR_futex, fFutex, fFutex->internal ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1, NULL, NULL, 0); -+ return true; -+} -+ -+bool JackLinuxFutex::SignalAll() -+{ -+ return Signal(); -+} -+ -+bool JackLinuxFutex::Wait() -+{ -+ if (!fFutex) { -+ jack_error("JackLinuxFutex::Wait name = %s already deallocated!!", fName); -+ return false; -+ } -+ -+ if (fFutex->needsChange) -+ { -+ fFutex->needsChange = false; -+ fFutex->internal = !fFutex->internal; -+ } -+ -+ for (;;) -+ { -+ if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0)) -+ return true; -+ -+ if (::syscall(__NR_futex, fFutex, fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, 0, NULL, NULL, 0) != 0 && errno != EWOULDBLOCK) -+ return false; -+ } -+} -+ -+bool JackLinuxFutex::TimedWait(long usec) -+{ -+ if (!fFutex) { -+ jack_error("JackLinuxFutex::TimedWait name = %s already deallocated!!", fName); -+ return false; -+ } -+ -+ if (fFutex->needsChange) -+ { -+ fFutex->needsChange = false; -+ fFutex->internal = !fFutex->internal; -+ } -+ -+ const uint secs = usec / 1000000; -+ const int nsecs = (usec % 1000000) * 1000; -+ -+ const timespec timeout = { static_cast(secs), nsecs }; -+ -+ for (;;) -+ { -+ if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0)) -+ return true; -+ -+ if (::syscall(__NR_futex, fFutex, fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, 0, &timeout, NULL, 0) != 0 && errno != EWOULDBLOCK) -+ return false; -+ } -+} -+ -+// Server side : publish the futex in the global namespace -+bool JackLinuxFutex::Allocate(const char* name, const char* server_name, int value, bool internal) -+{ -+ BuildName(name, server_name, fName, sizeof(fName)); -+ jack_log("JackLinuxFutex::Allocate name = %s val = %ld", fName, value); -+ -+ if ((fSharedMem = shm_open(fName, O_CREAT | O_RDWR, 0777)) < 0) { -+ jack_error("Allocate: can't check in named futex name = %s err = %s", fName, strerror(errno)); -+ return false; -+ } -+ -+ ftruncate(fSharedMem, sizeof(FutexData)); -+ -+ if ((fFutex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fSharedMem, 0)) == NULL) { -+ jack_error("Allocate: can't check in named futex name = %s err = %s", fName, strerror(errno)); -+ close(fSharedMem); -+ fSharedMem = -1; -+ shm_unlink(fName); -+ return false; -+ } -+ -+ fPrivate = internal; -+ -+ fFutex->futex = value; -+ fFutex->internal = internal; -+ fFutex->wasInternal = internal; -+ fFutex->needsChange = false; -+ fFutex->externalCount = 0; -+ return true; -+} -+ -+// Client side : get the published futex from server -+bool JackLinuxFutex::Connect(const char* name, const char* server_name) -+{ -+ BuildName(name, server_name, fName, sizeof(fName)); -+ jack_log("JackLinuxFutex::Connect name = %s", fName); -+ -+ // Temporary... -+ if (fFutex) { -+ jack_log("Already connected name = %s", name); -+ return true; -+ } -+ -+ if ((fSharedMem = shm_open(fName, O_RDWR, 0)) < 0) { -+ jack_error("Connect: can't connect named futex name = %s err = %s", fName, strerror(errno)); -+ return false; -+ } -+ -+ if ((fFutex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fSharedMem, 0)) == NULL) { -+ jack_error("Connect: can't connect named futex name = %s err = %s", fName, strerror(errno)); -+ close(fSharedMem); -+ fSharedMem = -1; -+ return false; -+ } -+ -+ if (! fPrivate && fFutex->wasInternal) -+ { -+ const char* externalSync = getenv("JACK_INTERNAL_CLIENT_SYNC"); -+ -+ if (externalSync != NULL && strstr(fName, externalSync) != NULL && ++fFutex->externalCount == 1) -+ { -+ jack_error("Note: client %s running as external client temporarily", fName); -+ fFutex->needsChange = true; -+ } -+ } -+ -+ return true; -+} -+ -+bool JackLinuxFutex::ConnectInput(const char* name, const char* server_name) -+{ -+ return Connect(name, server_name); -+} -+ -+bool JackLinuxFutex::ConnectOutput(const char* name, const char* server_name) -+{ -+ return Connect(name, server_name); -+} -+ -+bool JackLinuxFutex::Disconnect() -+{ -+ if (!fFutex) { -+ return true; -+ } -+ -+ if (! fPrivate && fFutex->wasInternal) -+ { -+ const char* externalSync = getenv("JACK_INTERNAL_CLIENT_SYNC"); -+ -+ if (externalSync != NULL && strstr(fName, externalSync) != NULL && --fFutex->externalCount == 0) -+ { -+ jack_error("Note: client %s now running as internal client again", fName); -+ fFutex->needsChange = true; -+ } -+ } -+ -+ munmap(fFutex, sizeof(FutexData)); -+ fFutex = NULL; -+ -+ close(fSharedMem); -+ fSharedMem = -1; -+ return true; -+} -+ -+// Server side : destroy the futex -+void JackLinuxFutex::Destroy() -+{ -+ if (!fFutex) { -+ return; -+ } -+ -+ munmap(fFutex, sizeof(FutexData)); -+ fFutex = NULL; -+ -+ close(fSharedMem); -+ fSharedMem = -1; -+ -+ shm_unlink(fName); -+} -+ -+} // end of namespace -+ -diff --git a/linux/JackLinuxFutex.h b/linux/JackLinuxFutex.h -new file mode 100644 -index 0000000..d549f8c ---- /dev/null -+++ b/linux/JackLinuxFutex.h -@@ -0,0 +1,85 @@ -+/* -+Copyright (C) 2004-2008 Grame -+Copyright (C) 2016 Filipe Coelho -+ -+This program is free software; you can redistribute it and/or modify -+it under the terms of the GNU Lesser General Public License as published by -+the Free Software Foundation; either version 2.1 of the License, or -+(at your option) any later version. -+ -+This program is distributed in the hope that it will be useful, -+but WITHOUT ANY WARRANTY; without even the implied warranty of -+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+GNU Lesser General Public License for more details. -+ -+You should have received a copy of the GNU Lesser General Public License -+along with this program; if not, write to the Free Software -+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ -+*/ -+ -+#ifndef __JackLinuxFutex__ -+#define __JackLinuxFutex__ -+ -+#include "JackSynchro.h" -+#include "JackCompilerDeps.h" -+#include -+ -+namespace Jack -+{ -+ -+/*! -+\brief Inter process synchronization using Linux futex. -+ -+ Based on the JackPosixSemaphore class. -+ Adapted to work with linux futex to be as light as possible and also work in multiple architectures. -+ -+ Adds a new 'MakePrivate' function that makes the sync happen in the local process only, -+ making it even faster for internal clients. -+*/ -+ -+class SERVER_EXPORT JackLinuxFutex : public detail::JackSynchro -+{ -+ -+ private: -+ struct FutexData { -+ int futex; // futex, needs to be 1st member -+ bool internal; // current internal state -+ bool wasInternal; // initial internal state, only changes in allocate -+ bool needsChange; // change state on next wait call -+ int externalCount; // how many external clients have connected -+ }; -+ -+ int fSharedMem; -+ FutexData* fFutex; -+ bool fPrivate; -+ -+ protected: -+ -+ void BuildName(const char* name, const char* server_name, char* res, int size); -+ -+ public: -+ -+ JackLinuxFutex():JackSynchro(), fSharedMem(-1), fFutex(NULL), fPrivate(false) -+ {} -+ -+ bool Signal(); -+ bool SignalAll(); -+ bool Wait(); -+ bool TimedWait(long usec); -+ -+ bool Allocate(const char* name, const char* server_name, int value, bool internal); -+ bool Connect(const char* name, const char* server_name); -+ bool ConnectInput(const char* name, const char* server_name); -+ bool ConnectOutput(const char* name, const char* server_name); -+ bool Disconnect(); -+ void Destroy(); -+ -+ void MakePrivate(bool priv); -+}; -+ -+} // end of namespace -+ -+ -+#endif -+ -diff --git a/linux/JackPlatformPlug_os.h b/linux/JackPlatformPlug_os.h -index b480f45..60c9a58 100644 ---- a/linux/JackPlatformPlug_os.h -+++ b/linux/JackPlatformPlug_os.h -@@ -54,8 +54,8 @@ namespace Jack { typedef JackPosixThread JackThread; } - namespace Jack { typedef JackFifo JackSynchro; } - */ - --#include "JackPosixSemaphore.h" --namespace Jack { typedef JackPosixSemaphore JackSynchro; } -+#include "JackLinuxFutex.h" -+namespace Jack { typedef JackLinuxFutex JackSynchro; } - - /* __JackPlatformChannelTransaction__ */ - /* diff -Nru jackd2-1.9.11~20161209/debian/patches/mod/09-jack_load_autoclose.patch jackd2-1.9.11+git20170722/debian/patches/mod/09-jack_load_autoclose.patch --- jackd2-1.9.11~20161209/debian/patches/mod/09-jack_load_autoclose.patch 2016-10-07 13:26:37.000000000 +0000 +++ jackd2-1.9.11+git20170722/debian/patches/mod/09-jack_load_autoclose.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,120 +0,0 @@ -diff --git a/example-clients/ipload.c b/example-clients/ipload.c -index 4868de3..a124c5e 100644 ---- a/example-clients/ipload.c -+++ b/example-clients/ipload.c -@@ -32,11 +32,17 @@ char *intclient_name; - char *load_name; - char *load_init = ""; - char *server_name = NULL; -+int autoclose_opt = 0; - int wait_opt = 0; -+volatile int idling = 1; - - static void - signal_handler (int sig) - { -+ /* do nothing if internal client closed itself */ -+ if (idling == 0) -+ return; -+ - jack_status_t status; - - fprintf (stderr, "signal received, unloading..."); -@@ -45,17 +51,33 @@ signal_handler (int sig) - fprintf (stderr, "(failed), status = 0x%2.0x\n", status); - else - fprintf (stderr, "(succeeded)\n"); -+ if (autoclose_opt) -+ jack_deactivate(client); - jack_client_close (client); - exit (0); - } - - static void -+registration_callback (const char *name, int reg, void *arg) -+{ -+ if (reg || strcmp(intclient_name, name)) -+ return; -+ -+ /* this will stop the wait loop and thus close this application. */ -+ idling = 0; -+ -+ /* unused */ -+ return; (void)arg; -+} -+ -+static void - show_usage () - { - fprintf (stderr, "usage: %s [ options ] client-name [ load-name " - "[ init-string]]\n\noptions:\n", client_name); - fprintf (stderr, - "\t-h, --help \t\t print help message\n" -+ "\t-a, --autoclose\t automatically close when intclient is unloaded\n" - "\t-i, --init string\t initialize string\n" - "\t-s, --server name\t select JACK server\n" - "\t-w, --wait \t\t wait for signal, then unload\n" -@@ -68,9 +90,10 @@ parse_args (int argc, char *argv[]) - { - int c; - int option_index = 0; -- char *short_options = "hi:s:w"; -+ char *short_options = "hai:s:w"; - struct option long_options[] = { - { "help", 0, 0, 'h' }, -+ { "autoclose", 0, 0, 'a' }, - { "init", required_argument, 0, 'i' }, - { "server", required_argument, 0, 's' }, - { "wait", 0, 0, 'w' }, -@@ -87,6 +110,9 @@ parse_args (int argc, char *argv[]) - while ((c = getopt_long (argc, argv, short_options, long_options, - &option_index)) >= 0) { - switch (c) { -+ case 'a': -+ autoclose_opt = 1; -+ break; - case 'i': - load_init = optarg; - break; -@@ -103,6 +129,10 @@ parse_args (int argc, char *argv[]) - } - } - -+ /* autoclose makes no sense without wait */ -+ if (autoclose_opt && ! wait_opt) -+ autoclose_opt = 0; -+ - if (optind == argc) { /* no positional args? */ - show_usage (); - return 1; -@@ -176,6 +206,11 @@ main (int argc, char *argv[]) - free(name); - } - -+ if (autoclose_opt) { -+ jack_set_client_registration_callback(client, registration_callback, NULL); -+ jack_activate(client); -+ } -+ - if (wait_opt) { - /* define a signal handler to unload the client, then - * wait for it to exit */ -@@ -190,7 +225,7 @@ main (int argc, char *argv[]) - signal(SIGINT, signal_handler); - #endif - -- while (1) { -+ while (idling) { - #ifdef WIN32 - Sleep(1000); - #else -@@ -199,6 +234,9 @@ main (int argc, char *argv[]) - } - } - -+ if (autoclose_opt) -+ jack_deactivate(client); -+ - jack_client_close(client); - return 0; - } diff -Nru jackd2-1.9.11~20161209/debian/patches/mod/10-jack_wait_250ms.patch jackd2-1.9.11+git20170722/debian/patches/mod/10-jack_wait_250ms.patch --- jackd2-1.9.11~20161209/debian/patches/mod/10-jack_wait_250ms.patch 2016-12-02 10:29:20.000000000 +0000 +++ jackd2-1.9.11+git20170722/debian/patches/mod/10-jack_wait_250ms.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -diff --git a/example-clients/wait.c b/example-clients/wait.c -index f3b9cb5..ad6bcb0 100644 ---- a/example-clients/wait.c -+++ b/example-clients/wait.c -@@ -132,7 +132,7 @@ main(int argc, char *argv[]) - #ifdef WIN32 - Sleep(1*1000); - #else -- sleep(1); -+ usleep(250 * 1000); // 250ms - #endif - } - diff -Nru jackd2-1.9.11~20161209/debian/patches/rev-d69443d1950a44a3ffdc264fe98c08cfb0f21a96.patch jackd2-1.9.11+git20170722/debian/patches/rev-d69443d1950a44a3ffdc264fe98c08cfb0f21a96.patch --- jackd2-1.9.11~20161209/debian/patches/rev-d69443d1950a44a3ffdc264fe98c08cfb0f21a96.patch 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/debian/patches/rev-d69443d1950a44a3ffdc264fe98c08cfb0f21a96.patch 2017-07-22 08:23:51.000000000 +0000 @@ -0,0 +1,46 @@ +--- a/common/JackEngine.cpp ++++ b/common/JackEngine.cpp +@@ -613,7 +613,7 @@ int JackEngine::ClientExternalOpen(const char* name, int pid, int uuid, int* ref + + JackExternalClient* client = new JackExternalClient(); + +- if (!fSynchroTable[refnum].Allocate(real_name, fEngineControl->fServerName, 0, false)) { ++ if (!fSynchroTable[refnum].Allocate(real_name, fEngineControl->fServerName, 0)) { + jack_error("Cannot allocate synchro"); + goto error; + } +@@ -663,7 +663,7 @@ int JackEngine::ClientInternalOpen(const char* name, int* ref, JackEngineControl + goto error; + } + +- if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0, true)) { ++ if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0)) { + jack_error("Cannot allocate synchro"); + goto error; + } +diff --git a/common/JackMidiAsyncWaitQueue.cpp b/common/JackMidiAsyncWaitQueue.cpp +index 016737cb..6d63a4dd 100644 +--- a/common/JackMidiAsyncWaitQueue.cpp ++++ b/common/JackMidiAsyncWaitQueue.cpp +@@ -29,7 +29,7 @@ JackMidiAsyncWaitQueue::JackMidiAsyncWaitQueue(size_t max_bytes, + size_t max_messages): + JackMidiAsyncQueue(max_bytes, max_messages) + { +- if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0, true)) { ++ if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0)) { + throw std::bad_alloc(); + } + } +diff --git a/linux/JackLinuxFutex.h b/linux/JackLinuxFutex.h +index 5c8a7006..d549f8c1 100644 +--- a/linux/JackLinuxFutex.h ++++ b/linux/JackLinuxFutex.h +@@ -68,7 +68,7 @@ class SERVER_EXPORT JackLinuxFutex : public detail::JackSynchro + bool Wait(); + bool TimedWait(long usec); + +- bool Allocate(const char* name, const char* server_name, int value, bool internal); ++ bool Allocate(const char* name, const char* server_name, int value, bool internal = false); + bool Connect(const char* name, const char* server_name); + bool ConnectInput(const char* name, const char* server_name); + bool ConnectOutput(const char* name, const char* server_name); diff -Nru jackd2-1.9.11~20161209/debian/patches/series jackd2-1.9.11+git20170722/debian/patches/series --- jackd2-1.9.11~20161209/debian/patches/series 2016-12-30 15:26:07.000000000 +0000 +++ jackd2-1.9.11+git20170722/debian/patches/series 2017-07-22 09:45:40.000000000 +0000 @@ -1,10 +1,4 @@ -jack2-driver-name-1.9.9.5.patch -mod/02-silent_xruns.patch -mod/04-use_gettime_realtime.patch -mod/05-enable_flush_denormal_to_zero.patch -mod/06-ignore_xrun_delayed_usecs.patch -mod/07-pm_qos_lowlatency.patch -mod/08-futex_sync.patch -mod/09-jack_load_autoclose.patch -mod/10-jack_wait_250ms.patch ipload-fflush.patch +jack2-driver-name-1.9.9.5.patch +rev-d69443d1950a44a3ffdc264fe98c08cfb0f21a96.patch +fix-dbus-autostart-command.patch diff -Nru jackd2-1.9.11~20161209/example-clients/alsa_in.c jackd2-1.9.11+git20170722/example-clients/alsa_in.c --- jackd2-1.9.11~20161209/example-clients/alsa_in.c 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/example-clients/alsa_in.c 2017-07-22 08:00:34.000000000 +0000 @@ -353,7 +353,7 @@ delay = snd_pcm_avail( alsa_handle ); - delay -= jack_frames_since_cycle_start( client ); + delay -= round( jack_frames_since_cycle_start( client ) / static_resample_factor ); // Do it the hard way. // this is for compensating xruns etc... @@ -510,7 +510,7 @@ jack_latency_range_t range; JSList *node; - range.min = range.max = target_delay; + range.min = range.max = round(target_delay * static_resample_factor); if (mode == JackCaptureLatency) { for (node = capture_ports; node; node = jack_slist_next (node)) { diff -Nru jackd2-1.9.11~20161209/example-clients/alsa_out.c jackd2-1.9.11+git20170722/example-clients/alsa_out.c --- jackd2-1.9.11~20161209/example-clients/alsa_out.c 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/example-clients/alsa_out.c 2017-07-22 08:00:34.000000000 +0000 @@ -357,7 +357,7 @@ delay = (num_periods*period_size)-snd_pcm_avail( alsa_handle ) ; - delay -= jack_frames_since_cycle_start( client ); + delay -= round( jack_frames_since_cycle_start( client ) * static_resample_factor ); // Do it the hard way. // this is for compensating xruns etc... @@ -513,7 +513,7 @@ jack_latency_range_t range; JSList *node; - range.min = range.max = target_delay; + range.min = range.max = round(target_delay / static_resample_factor); if (mode == JackCaptureLatency) { for (node = capture_ports; node; node = jack_slist_next (node)) { diff -Nru jackd2-1.9.11~20161209/example-clients/ipload.c jackd2-1.9.11+git20170722/example-clients/ipload.c --- jackd2-1.9.11~20161209/example-clients/ipload.c 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/example-clients/ipload.c 2017-07-22 08:00:34.000000000 +0000 @@ -32,11 +32,17 @@ char *load_name; char *load_init = ""; char *server_name = NULL; +int autoclose_opt = 0; int wait_opt = 0; +volatile int idling = 1; static void signal_handler (int sig) { + /* do nothing if internal client closed itself */ + if (idling == 0) + return; + jack_status_t status; fprintf (stderr, "signal received, unloading..."); @@ -45,17 +51,34 @@ fprintf (stderr, "(failed), status = 0x%2.0x\n", status); else fprintf (stderr, "(succeeded)\n"); + if (autoclose_opt) + jack_deactivate(client); jack_client_close (client); exit (0); } static void +registration_callback (const char *name, int reg, void *arg) +{ + if (reg || strcmp(intclient_name, name)) + return; + + /* this will stop the wait loop and thus close this application. */ + idling = 0; + return; + + /* unused */ + (void)arg; +} + +static void show_usage () { fprintf (stderr, "usage: %s [ options ] client-name [ load-name " "[ init-string]]\n\noptions:\n", client_name); fprintf (stderr, "\t-h, --help \t\t print help message\n" + "\t-a, --autoclose\t automatically close when intclient is unloaded\n" "\t-i, --init string\t initialize string\n" "\t-s, --server name\t select JACK server\n" "\t-w, --wait \t\t wait for signal, then unload\n" @@ -68,9 +91,10 @@ { int c; int option_index = 0; - char *short_options = "hi:s:w"; + char *short_options = "hai:s:w"; struct option long_options[] = { { "help", 0, 0, 'h' }, + { "autoclose", 0, 0, 'a' }, { "init", required_argument, 0, 'i' }, { "server", required_argument, 0, 's' }, { "wait", 0, 0, 'w' }, @@ -87,6 +111,9 @@ while ((c = getopt_long (argc, argv, short_options, long_options, &option_index)) >= 0) { switch (c) { + case 'a': + autoclose_opt = 1; + break; case 'i': load_init = optarg; break; @@ -103,6 +130,10 @@ } } + /* autoclose makes no sense without wait */ + if (autoclose_opt && ! wait_opt) + autoclose_opt = 0; + if (optind == argc) { /* no positional args? */ show_usage (); return 1; @@ -176,6 +207,11 @@ free(name); } + if (autoclose_opt) { + jack_set_client_registration_callback(client, registration_callback, NULL); + jack_activate(client); + } + if (wait_opt) { /* define a signal handler to unload the client, then * wait for it to exit */ @@ -190,7 +226,7 @@ signal(SIGINT, signal_handler); #endif - while (1) { + while (idling) { #ifdef WIN32 Sleep(1000); #else @@ -199,7 +235,11 @@ } } - jack_client_close(client); + if (autoclose_opt) { + jack_deactivate(client); + } + + jack_client_close(client); return 0; } diff -Nru jackd2-1.9.11~20161209/example-clients/simdtests.cpp jackd2-1.9.11+git20170722/example-clients/simdtests.cpp --- jackd2-1.9.11~20161209/example-clients/simdtests.cpp 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/example-clients/simdtests.cpp 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,390 @@ +/* + * simdtests.c -- test accuraccy and performance of simd optimizations + * + * Copyright (C) 2017 Andreas Mueller. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* We must include all headers memops.c includes to avoid trouble with + * out namespace game below. + */ +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include "memops.h" + +#if defined (__SSE2__) && !defined (__sun__) +#include +#ifdef __SSE4_1__ +#include +#endif +#endif + +#ifdef __ARM_NEON__ +#include +#endif + +// our additional headers +#include + +/* Dirty: include mempos.c twice the second time with SIMD disabled + * so we can compare aceelerated non accelerated + */ +namespace accelerated { +#include "../common/memops.c" +} + +namespace origerated { +#ifdef __SSE2__ +#undef __SSE2__ +#endif + +#ifdef __ARM_NEON__ +#undef __ARM_NEON__ +#endif + +#include "../common/memops.c" +} + +// define conversion function types +typedef void (*t_jack_to_integer)( + char *dst, + jack_default_audio_sample_t *src, + unsigned long nsamples, + unsigned long dst_skip, + dither_state_t *state); + +typedef void (*t_integer_to_jack)( + jack_default_audio_sample_t *dst, + char *src, + unsigned long nsamples, + unsigned long src_skip); + +// define/setup test case data +typedef struct test_case_data { + uint32_t frame_size; + uint32_t sample_size; + bool reverse; + t_jack_to_integer jack_to_integer_accel; + t_jack_to_integer jack_to_integer_orig; + t_integer_to_jack integer_to_jack_accel; + t_integer_to_jack integer_to_jack_orig; + dither_state_t *ditherstate; + const char *name; +} test_case_data_t; + +test_case_data_t test_cases[] = { + { + 4, + 3, + true, + accelerated::sample_move_d32u24_sSs, + origerated::sample_move_d32u24_sSs, + accelerated::sample_move_dS_s32u24s, + origerated::sample_move_dS_s32u24s, + NULL, + "32u24s" }, + { + 4, + 3, + false, + accelerated::sample_move_d32u24_sS, + origerated::sample_move_d32u24_sS, + accelerated::sample_move_dS_s32u24, + origerated::sample_move_dS_s32u24, + NULL, + "32u24" }, + { + 3, + 3, + true, + accelerated::sample_move_d24_sSs, + origerated::sample_move_d24_sSs, + accelerated::sample_move_dS_s24s, + origerated::sample_move_dS_s24s, + NULL, + "24s" }, + { + 3, + 3, + false, + accelerated::sample_move_d24_sS, + origerated::sample_move_d24_sS, + accelerated::sample_move_dS_s24, + origerated::sample_move_dS_s24, + NULL, + "24" }, + { + 2, + 2, + true, + accelerated::sample_move_d16_sSs, + origerated::sample_move_d16_sSs, + accelerated::sample_move_dS_s16s, + origerated::sample_move_dS_s16s, + NULL, + "16s" }, + { + 2, + 2, + false, + accelerated::sample_move_d16_sS, + origerated::sample_move_d16_sS, + accelerated::sample_move_dS_s16, + origerated::sample_move_dS_s16, + NULL, + "16" }, +}; + +// we need to repeat for better accuracy at time measurement +const uint32_t retry_per_case = 1000; + +// setup test buffers +#define TESTBUFF_SIZE 1024 +jack_default_audio_sample_t jackbuffer_source[TESTBUFF_SIZE]; +// integer buffers: max 4 bytes per value / * 2 for stereo +char integerbuffer_accel[TESTBUFF_SIZE*4*2]; +char integerbuffer_orig[TESTBUFF_SIZE*4*2]; +// float buffers +jack_default_audio_sample_t jackfloatbuffer_accel[TESTBUFF_SIZE]; +jack_default_audio_sample_t jackfloatbuffer_orig[TESTBUFF_SIZE]; + +// comparing unsigned makes life easier +uint32_t extract_integer( + char* buff, + uint32_t offset, + uint32_t frame_size, + uint32_t sample_size, + bool big_endian) +{ + uint32_t retval = 0; + unsigned char* curr; + uint32_t mult = 1; + if(big_endian) { + curr = (unsigned char*)buff + offset + sample_size-1; + for(uint32_t i=0; i integer + + // clean target buffers + memset(integerbuffer_accel, 0, sizeof(integerbuffer_accel)); + memset(integerbuffer_orig, 0, sizeof(integerbuffer_orig)); + // accel + clock_t time_to_integer_accel = clock(); + for(uint32_t repetition=0; repetitionInteger @%7.7s/%u: Orig %7.6f sec / Accel %7.6f sec -> Win: %5.2f %%\n", + test_cases[testcase].name, + channels, + timediff_to_integer_orig, + timediff_to_integer_accel, + (timediff_to_integer_orig/timediff_to_integer_accel-1)*100.0); + uint32_t int_deviation_max = 0; + uint32_t int_error_count = 0; + // output error (avoid spam -> limit error lines per test case) + for(uint32_t sample=0; sample intval_orig) + int_deviation = intval_accel-intval_orig; + else + int_deviation = intval_orig-intval_accel; + if(int_deviation > int_deviation_max) + int_deviation_max = int_deviation; + } + } + printf( + "JackFloat->Integer @%7.7s/%u: Errors: %u Max deviation %u\n", + test_cases[testcase].name, + channels, + int_error_count, + int_deviation_max); + + ////////////////////////////////////////////////////////////////////////////// + // integer -> jackfloat + + // clean target buffers + memset(jackfloatbuffer_accel, 0, sizeof(jackfloatbuffer_accel)); + memset(jackfloatbuffer_orig, 0, sizeof(jackfloatbuffer_orig)); + // accel + clock_t time_to_float_accel = clock(); + for(uint32_t repetition=0; repetitionJackFloat @%7.7s/%u: Orig %7.6f sec / Accel %7.6f sec -> Win: %5.2f %%\n", + test_cases[testcase].name, + channels, + timediff_to_float_orig, + timediff_to_float_accel, + (timediff_to_float_orig/timediff_to_float_accel-1)*100.0); + jack_default_audio_sample_t float_deviation_max = 0.0; + uint32_t float_error_count = 0; + // output error (avoid spam -> limit error lines per test case) + for(uint32_t sample=0; sample floatval_orig) + float_deviation = floatval_accel-floatval_orig; + else + float_deviation = floatval_orig-floatval_accel; + if(float_deviation > float_deviation_max) + float_deviation_max = float_deviation; + // deviation > half bit => error + if(float_deviation > 0.5) { + if(float_error_countJackFloat @%7.7s/%u: Errors: %u Max deviation %f\n", + test_cases[testcase].name, + channels, + float_error_count, + float_deviation_max); + + printf("\n"); + } + } + return 0; +} diff -Nru jackd2-1.9.11~20161209/example-clients/transport.c jackd2-1.9.11+git20170722/example-clients/transport.c --- jackd2-1.9.11~20161209/example-clients/transport.c 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/example-clients/transport.c 2017-07-22 08:00:34.000000000 +0000 @@ -211,6 +211,29 @@ jack_set_sync_timeout(client, (jack_time_t) (timeout*1000000)); } +/* Toggle between play and stop state */ +static void com_toggle(char *arg) +{ + jack_position_t current; + jack_transport_state_t transport_state; + + transport_state = jack_transport_query (client, ¤t); + + switch (transport_state) { + case JackTransportStopped: + com_play( arg ); + break; + case JackTransportRolling: + com_stop( arg ); + break; + case JackTransportStarting: + fprintf(stderr, "state: Starting - no transport toggling"); + break; + default: + fprintf(stderr, "unexpected state: no transport toggling"); + } +} + /* Command parsing based on GNU readline info examples. */ @@ -238,6 +261,7 @@ {"stop", com_stop, "Stop transport"}, {"tempo", com_tempo, "Set beat tempo "}, {"timeout", com_timeout, "Set sync timeout in "}, + {"toggle", com_toggle, "Toggle transport rolling"}, {"?", com_help, "Synonym for `help'" }, {(char *)NULL, (cmd_function_t *)NULL, (char *)NULL } }; diff -Nru jackd2-1.9.11~20161209/example-clients/tw.c jackd2-1.9.11+git20170722/example-clients/tw.c --- jackd2-1.9.11~20161209/example-clients/tw.c 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/example-clients/tw.c 2017-07-22 08:02:50.000000000 +0000 @@ -9,6 +9,7 @@ #include #include #include +#include #include diff -Nru jackd2-1.9.11~20161209/example-clients/wait.c jackd2-1.9.11+git20170722/example-clients/wait.c --- jackd2-1.9.11~20161209/example-clients/wait.c 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/example-clients/wait.c 2017-07-22 08:02:46.000000000 +0000 @@ -128,11 +128,11 @@ } } - // Wait a second, and repeat + // Wait 250ms, and repeat #ifdef WIN32 - Sleep(1*1000); + Sleep(250); #else - sleep(1); + usleep(250*1000); #endif } diff -Nru jackd2-1.9.11~20161209/example-clients/wscript jackd2-1.9.11+git20170722/example-clients/wscript --- jackd2-1.9.11~20161209/example-clients/wscript 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/example-clients/wscript 2017-07-22 08:02:50.000000000 +0000 @@ -28,7 +28,8 @@ 'jack_net_master' : 'netmaster.c', 'jack_latent_client' : 'latent_client.c', 'jack_midi_dump' : 'midi_dump.c', - 'jack_midi_latency_test' : 'midi_latency_test.c' + 'jack_midi_latency_test' : 'midi_latency_test.c', + 'jack_simdtests' : 'simdtests.cpp' } example_libs = { @@ -65,30 +66,25 @@ else: use = ['clientlib'] - prog = bld(features='c cprogram') + if bld.env['IS_MACOSX']: + prog = bld(features='c cprogram', framework = ['Foundation']) + else: + prog = bld(features='c cprogram') prog.includes = os_incdir + ['../common/jack', '../common'] prog.source = example_program_source prog.use = use - if bld.env['IS_MACOSX']: - prog.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") - #prog.env.append_value("LINKFLAGS", "-arch i386 -arch ppc -arch x86_64") - prog.env.append_value("LINKFLAGS", "") if bld.env['IS_LINUX']: prog.use += ['RT', 'M'] if bld.env['IS_SUN']: prog.use += ['M'] prog.target = example_program - + if bld.env['BUILD_EXAMPLE_CLIENT_TRANSPORT']: prog = bld(features = 'c cprogram') prog.includes = os_incdir + ['../common/jack', '../common'] prog.source = 'transport.c' prog.use = ['clientlib'] - if bld.env['IS_MACOSX']: - prog.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") - #prog.env.append_value("LINKFLAGS", "-arch i386 -arch ppc -arch x86_64") - prog.env.append_value("LINKFLAGS", "") if bld.env['IS_LINUX']: prog.use += ['RT', 'READLINE'] if bld.env['IS_MACOSX']: @@ -103,9 +99,6 @@ prog.source = 'capture_client.c' prog.use = ['clientlib'] if bld.env['IS_MACOSX']: - prog.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") - #prog.env.append_value("LINKFLAGS", "-arch i386 -arch ppc -arch x86_64") - prog.env.append_value("LINKFLAGS", "") prog.use += ['SNDFILE'] if bld.env['IS_LINUX']: prog.use += ['RT', 'SNDFILE'] @@ -119,7 +112,7 @@ prog = bld(features = 'c cprogram') prog.includes = os_incdir + ['.', '..', '../common/jack', '../common'] prog.source = ['netsource.c', '../common/netjack_packet.c'] - prog.env.append_value("CFLAGS", "-DNO_JACK_ERROR") + prog.env.append_value('CFLAGS', '-DNO_JACK_ERROR') prog.use = ['CELT', 'SAMPLERATE', 'OPUS', 'M', 'clientlib'] prog.target = 'jack_netsource' prog.defines = ['HAVE_CONFIG_H'] @@ -128,14 +121,14 @@ prog = bld(features = 'c cprogram') prog.includes = os_incdir + ['../common/jack', '../common'] prog.source = ['alsa_in.c', '../common/memops.c'] - prog.env.append_value("CFLAGS", "-DNO_JACK_ERROR") + prog.env.append_value('CFLAGS', '-DNO_JACK_ERROR') prog.use = ['clientlib', 'ALSA', 'SAMPLERATE', 'M'] prog.target = 'alsa_in' prog = bld(features = 'c cprogram') prog.includes = os_incdir + ['../common/jack', '../common'] prog.source = ['alsa_out.c', '../common/memops.c'] - prog.env.append_value("CFLAGS", "-DNO_JACK_ERROR") + prog.env.append_value('CFLAGS', '-DNO_JACK_ERROR') prog.use = ['clientlib', 'ALSA', 'SAMPLERATE', 'M'] prog.target = 'alsa_out' @@ -146,12 +139,8 @@ lib.includes = os_incdir + ['../common/jack', '../common'] lib.target = example_lib lib.source = example_lib_source - if bld.env['IS_MACOSX']: - lib.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") - #lib.env.append_value("LINKFLAGS", "-arch i386 -arch ppc -arch x86_64") - lib.env.append_value("LINKFLAGS", "") if bld.env['IS_SUN']: - lib.env.append_value("LINKFLAGS", "-lm") + lib.env.append_value('LINKFLAGS', '-lm') lib.use = 'serverlib' lib.install_path = '${ADDON_DIR}/' diff -Nru jackd2-1.9.11~20161209/.gitignore jackd2-1.9.11+git20170722/.gitignore --- jackd2-1.9.11~20161209/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/.gitignore 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,8 @@ +build/ +man/*.1 +.lock* +__pycache__ +*.pyc +android/.server/ +android/.client/ +codeBlocks diff -Nru jackd2-1.9.11~20161209/linux/alsa/alsa_driver.c jackd2-1.9.11+git20170722/linux/alsa/alsa_driver.c --- jackd2-1.9.11~20161209/linux/alsa/alsa_driver.c 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/linux/alsa/alsa_driver.c 2017-07-22 08:02:46.000000000 +0000 @@ -1169,7 +1169,7 @@ } static int -alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) +alsa_driver_xrun_recovery (alsa_driver_t *driver) { snd_pcm_status_t *status; int res; @@ -1206,13 +1206,7 @@ if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN && driver->process_count > XRUN_REPORT_DELAY) { - struct timeval now, diff, tstamp; driver->xrun_count++; - snd_pcm_status_get_tstamp(status,&now); - snd_pcm_status_get_trigger_tstamp(status, &tstamp); - timersub(&now, &tstamp, &diff); - *delayed_usecs = diff.tv_sec * 1000000.0 + diff.tv_usec; - jack_log("**** alsa_pcm: xrun of at least %.3f msecs",*delayed_usecs / 1000.0); } if (alsa_driver_restart (driver)) { @@ -1251,8 +1245,7 @@ static int under_gdb = FALSE; jack_nframes_t -alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float - *delayed_usecs) +alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status) { snd_pcm_sframes_t avail = 0; snd_pcm_sframes_t capture_avail = 0; @@ -1265,7 +1258,6 @@ jack_time_t poll_ret = 0; *status = -1; - *delayed_usecs = 0; need_capture = driver->capture_handle ? 1 : 0; @@ -1357,9 +1349,6 @@ SetTime(poll_ret); if (extra_fd < 0) { - if (driver->poll_next && poll_ret > driver->poll_next) { - *delayed_usecs = poll_ret - driver->poll_next; - } driver->poll_last = poll_ret; driver->poll_next = poll_ret + driver->period_usecs; // JACK2 @@ -1479,7 +1468,7 @@ } if (xrun_detected) { - *status = alsa_driver_xrun_recovery (driver, delayed_usecs); + *status = alsa_driver_xrun_recovery (driver); return 0; } diff -Nru jackd2-1.9.11~20161209/linux/alsa/alsa_driver.h jackd2-1.9.11+git20170722/linux/alsa/alsa_driver.h --- jackd2-1.9.11~20161209/linux/alsa/alsa_driver.h 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/linux/alsa/alsa_driver.h 2017-07-22 08:02:46.000000000 +0000 @@ -260,8 +260,7 @@ alsa_driver_stop (alsa_driver_t *driver); jack_nframes_t -alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float - *delayed_usecs); +alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status); int alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes); diff -Nru jackd2-1.9.11~20161209/linux/alsa/JackAlsaDriver.cpp jackd2-1.9.11+git20170722/linux/alsa/JackAlsaDriver.cpp --- jackd2-1.9.11~20161209/linux/alsa/JackAlsaDriver.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/linux/alsa/JackAlsaDriver.cpp 2017-07-22 08:02:46.000000000 +0000 @@ -409,7 +409,7 @@ retry: - nframes = alsa_driver_wait((alsa_driver_t *)fDriver, -1, &wait_status, &fDelayedUsecs); + nframes = alsa_driver_wait((alsa_driver_t *)fDriver, -1, &wait_status); if (wait_status < 0) return -1; /* driver failed */ diff -Nru jackd2-1.9.11~20161209/linux/firewire/JackFFADODriver.cpp jackd2-1.9.11+git20170722/linux/firewire/JackFFADODriver.cpp --- jackd2-1.9.11~20161209/linux/firewire/JackFFADODriver.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/linux/firewire/JackFFADODriver.cpp 2017-07-22 08:00:34.000000000 +0000 @@ -403,6 +403,11 @@ // driver->client = client; driver->engine = NULL; + //from jack1 ffado_driver.c: put arg -dxxx to ffado device_info_t struct + driver->device_info.nb_device_spec_strings=1; + driver->device_info.device_spec_strings=(char**)calloc(1, sizeof(char *)); + driver->device_info.device_spec_strings[0]=strdup(params->device_info); + memset(&driver->device_options, 0, sizeof(driver->device_options)); driver->device_options.sample_rate = params->sample_rate; driver->device_options.period_size = params->period_size; diff -Nru jackd2-1.9.11~20161209/linux/JackLinuxFutex.cpp jackd2-1.9.11+git20170722/linux/JackLinuxFutex.cpp --- jackd2-1.9.11~20161209/linux/JackLinuxFutex.cpp 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/linux/JackLinuxFutex.cpp 2017-07-22 08:02:46.000000000 +0000 @@ -0,0 +1,247 @@ +/* +Copyright (C) 2004-2008 Grame +Copyright (C) 2016 Filipe Coelho + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "JackLinuxFutex.h" +#include "JackTools.h" +#include "JackConstants.h" +#include "JackError.h" +#include +#include +#include +#include +#include + +namespace Jack +{ + +void JackLinuxFutex::BuildName(const char* client_name, const char* server_name, char* res, int size) +{ + char ext_client_name[SYNC_MAX_NAME_SIZE + 1]; + JackTools::RewriteName(client_name, ext_client_name); +#if defined(__ARM_ARCH_7A__) + snprintf(res, size, "jack_sem.%s", ext_client_name); +#else + if (getenv("JACK_PROMISCUOUS_SERVER")) { + snprintf(res, size, "jack_sem.%s_%s", server_name, ext_client_name); + } else { + snprintf(res, size, "jack_sem.%d_%s_%s", JackTools::GetUID(), server_name, ext_client_name); + } +#endif +} + +bool JackLinuxFutex::Signal() +{ + if (!fFutex) { + jack_error("JackLinuxFutex::Signal name = %s already deallocated!!", fName); + return false; + } + + if (fFlush) { + return true; + } + + if (! __sync_bool_compare_and_swap(&fFutex->futex, 0, 1)) + { + // already unlocked, do not wake futex + if (! fFutex->internal) return true; + } + + ::syscall(__NR_futex, fFutex, fFutex->internal ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1, NULL, NULL, 0); + return true; +} + +bool JackLinuxFutex::SignalAll() +{ + return Signal(); +} + +bool JackLinuxFutex::Wait() +{ + if (!fFutex) { + jack_error("JackLinuxFutex::Wait name = %s already deallocated!!", fName); + return false; + } + + if (fFutex->needsChange) + { + fFutex->needsChange = false; + fFutex->internal = !fFutex->internal; + } + + for (;;) + { + if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0)) + return true; + + if (::syscall(__NR_futex, fFutex, fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, 0, NULL, NULL, 0) != 0 && errno != EWOULDBLOCK) + return false; + } +} + +bool JackLinuxFutex::TimedWait(long usec) +{ + if (!fFutex) { + jack_error("JackLinuxFutex::TimedWait name = %s already deallocated!!", fName); + return false; + } + + if (fFutex->needsChange) + { + fFutex->needsChange = false; + fFutex->internal = !fFutex->internal; + } + + const uint secs = usec / 1000000; + const int nsecs = (usec % 1000000) * 1000; + + const timespec timeout = { static_cast(secs), nsecs }; + + for (;;) + { + if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0)) + return true; + + if (::syscall(__NR_futex, fFutex, fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, 0, &timeout, NULL, 0) != 0 && errno != EWOULDBLOCK) + return false; + } +} + +// Server side : publish the futex in the global namespace +bool JackLinuxFutex::Allocate(const char* name, const char* server_name, int value, bool internal) +{ + BuildName(name, server_name, fName, sizeof(fName)); + jack_log("JackLinuxFutex::Allocate name = %s val = %ld", fName, value); + + if ((fSharedMem = shm_open(fName, O_CREAT | O_RDWR, 0777)) < 0) { + jack_error("Allocate: can't check in named futex name = %s err = %s", fName, strerror(errno)); + return false; + } + + ftruncate(fSharedMem, sizeof(FutexData)); + + if ((fFutex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fSharedMem, 0)) == NULL) { + jack_error("Allocate: can't check in named futex name = %s err = %s", fName, strerror(errno)); + close(fSharedMem); + fSharedMem = -1; + shm_unlink(fName); + return false; + } + + fPrivate = internal; + + fFutex->futex = value; + fFutex->internal = internal; + fFutex->wasInternal = internal; + fFutex->needsChange = false; + fFutex->externalCount = 0; + return true; +} + +// Client side : get the published futex from server +bool JackLinuxFutex::Connect(const char* name, const char* server_name) +{ + BuildName(name, server_name, fName, sizeof(fName)); + jack_log("JackLinuxFutex::Connect name = %s", fName); + + // Temporary... + if (fFutex) { + jack_log("Already connected name = %s", name); + return true; + } + + if ((fSharedMem = shm_open(fName, O_RDWR, 0)) < 0) { + jack_error("Connect: can't connect named futex name = %s err = %s", fName, strerror(errno)); + return false; + } + + if ((fFutex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fSharedMem, 0)) == NULL) { + jack_error("Connect: can't connect named futex name = %s err = %s", fName, strerror(errno)); + close(fSharedMem); + fSharedMem = -1; + return false; + } + + if (! fPrivate && fFutex->wasInternal) + { + const char* externalSync = getenv("JACK_INTERNAL_CLIENT_SYNC"); + + if (externalSync != NULL && strstr(fName, externalSync) != NULL && ++fFutex->externalCount == 1) + { + jack_error("Note: client %s running as external client temporarily", fName); + fFutex->needsChange = true; + } + } + + return true; +} + +bool JackLinuxFutex::ConnectInput(const char* name, const char* server_name) +{ + return Connect(name, server_name); +} + +bool JackLinuxFutex::ConnectOutput(const char* name, const char* server_name) +{ + return Connect(name, server_name); +} + +bool JackLinuxFutex::Disconnect() +{ + if (!fFutex) { + return true; + } + + if (! fPrivate && fFutex->wasInternal) + { + const char* externalSync = getenv("JACK_INTERNAL_CLIENT_SYNC"); + + if (externalSync != NULL && strstr(fName, externalSync) != NULL && --fFutex->externalCount == 0) + { + jack_error("Note: client %s now running as internal client again", fName); + fFutex->needsChange = true; + } + } + + munmap(fFutex, sizeof(FutexData)); + fFutex = NULL; + + close(fSharedMem); + fSharedMem = -1; + return true; +} + +// Server side : destroy the futex +void JackLinuxFutex::Destroy() +{ + if (!fFutex) { + return; + } + + munmap(fFutex, sizeof(FutexData)); + fFutex = NULL; + + close(fSharedMem); + fSharedMem = -1; + + shm_unlink(fName); +} + +} // end of namespace + diff -Nru jackd2-1.9.11~20161209/linux/JackLinuxFutex.h jackd2-1.9.11+git20170722/linux/JackLinuxFutex.h --- jackd2-1.9.11~20161209/linux/JackLinuxFutex.h 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/linux/JackLinuxFutex.h 2017-07-22 08:02:46.000000000 +0000 @@ -0,0 +1,85 @@ +/* +Copyright (C) 2004-2008 Grame +Copyright (C) 2016 Filipe Coelho + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackLinuxFutex__ +#define __JackLinuxFutex__ + +#include "JackSynchro.h" +#include "JackCompilerDeps.h" +#include + +namespace Jack +{ + +/*! +\brief Inter process synchronization using Linux futex. + + Based on the JackPosixSemaphore class. + Adapted to work with linux futex to be as light as possible and also work in multiple architectures. + + Adds a new 'MakePrivate' function that makes the sync happen in the local process only, + making it even faster for internal clients. +*/ + +class SERVER_EXPORT JackLinuxFutex : public detail::JackSynchro +{ + + private: + struct FutexData { + int futex; // futex, needs to be 1st member + bool internal; // current internal state + bool wasInternal; // initial internal state, only changes in allocate + bool needsChange; // change state on next wait call + int externalCount; // how many external clients have connected + }; + + int fSharedMem; + FutexData* fFutex; + bool fPrivate; + + protected: + + void BuildName(const char* name, const char* server_name, char* res, int size); + + public: + + JackLinuxFutex():JackSynchro(), fSharedMem(-1), fFutex(NULL), fPrivate(false) + {} + + bool Signal(); + bool SignalAll(); + bool Wait(); + bool TimedWait(long usec); + + bool Allocate(const char* name, const char* server_name, int value, bool internal); + bool Connect(const char* name, const char* server_name); + bool ConnectInput(const char* name, const char* server_name); + bool ConnectOutput(const char* name, const char* server_name); + bool Disconnect(); + void Destroy(); + + void MakePrivate(bool priv); +}; + +} // end of namespace + + +#endif + diff -Nru jackd2-1.9.11~20161209/linux/JackLinuxTime.c jackd2-1.9.11+git20170722/linux/JackLinuxTime.c --- jackd2-1.9.11~20161209/linux/JackLinuxTime.c 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/linux/JackLinuxTime.c 2017-07-22 08:02:46.000000000 +0000 @@ -143,7 +143,11 @@ jack_time_t jackTime; struct timespec time; +#if defined(__ARM_ARCH_7A__) + clock_gettime(CLOCK_MONOTONIC_RAW, &time); +#else clock_gettime(CLOCK_MONOTONIC, &time); +#endif jackTime = (jack_time_t) time.tv_sec * 1e6 + (jack_time_t) time.tv_nsec / 1e3; return jackTime; diff -Nru jackd2-1.9.11~20161209/linux/JackPlatformPlug_os.h jackd2-1.9.11+git20170722/linux/JackPlatformPlug_os.h --- jackd2-1.9.11~20161209/linux/JackPlatformPlug_os.h 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/linux/JackPlatformPlug_os.h 2017-07-22 08:00:34.000000000 +0000 @@ -54,8 +54,8 @@ namespace Jack { typedef JackFifo JackSynchro; } */ -#include "JackPosixSemaphore.h" -namespace Jack { typedef JackPosixSemaphore JackSynchro; } +#include "JackLinuxFutex.h" +namespace Jack { typedef JackLinuxFutex JackSynchro; } /* __JackPlatformChannelTransaction__ */ /* diff -Nru jackd2-1.9.11~20161209/macosx/coreaudio/JackCoreAudioAdapter.cpp jackd2-1.9.11+git20170722/macosx/coreaudio/JackCoreAudioAdapter.cpp --- jackd2-1.9.11~20161209/macosx/coreaudio/JackCoreAudioAdapter.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coreaudio/JackCoreAudioAdapter.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1755 +0,0 @@ -/* -Copyright (C) 2008 Grame - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "JackCoreAudioAdapter.h" -#include "JackError.h" -#include - -#include - -namespace Jack -{ - -static void PrintStreamDesc(AudioStreamBasicDescription *inDesc) -{ - jack_log("- - - - - - - - - - - - - - - - - - - -"); - jack_log(" Sample Rate:%f", inDesc->mSampleRate); - jack_log(" Format ID:%.*s", (int) sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID); - jack_log(" Format Flags:%lX", inDesc->mFormatFlags); - jack_log(" Bytes per Packet:%ld", inDesc->mBytesPerPacket); - jack_log(" Frames per Packet:%ld", inDesc->mFramesPerPacket); - jack_log(" Bytes per Frame:%ld", inDesc->mBytesPerFrame); - jack_log(" Channels per Frame:%ld", inDesc->mChannelsPerFrame); - jack_log(" Bits per Channel:%ld", inDesc->mBitsPerChannel); - jack_log("- - - - - - - - - - - - - - - - - - - -"); -} - -static OSStatus DisplayDeviceNames() -{ - UInt32 size; - Boolean isWritable; - int i, deviceNum; - OSStatus err; - CFStringRef UIname; - - err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); - if (err != noErr) { - return err; - } - - deviceNum = size / sizeof(AudioDeviceID); - AudioDeviceID devices[deviceNum]; - - err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); - if (err != noErr) { - return err; - } - - for (i = 0; i < deviceNum; i++) { - char device_name[256]; - char internal_name[256]; - - size = sizeof(CFStringRef); - UIname = NULL; - err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); - if (err == noErr) { - CFStringGetCString(UIname, internal_name, 256, CFStringGetSystemEncoding()); - } else { - goto error; - } - - size = 256; - err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name); - if (err != noErr) { - return err; - } - - jack_info("Device name = \'%s\', internal_name = \'%s\' (to be used as -C, -P, or -d parameter)", device_name, internal_name); - } - - return noErr; - -error: - if (UIname != NULL) { - CFRelease(UIname); - } - return err; -} - -static void printError(OSStatus err) -{ - switch (err) { - case kAudioHardwareNoError: - jack_log("error code : kAudioHardwareNoError"); - break; - case kAudioConverterErr_FormatNotSupported: - jack_log("error code : kAudioConverterErr_FormatNotSupported"); - break; - case kAudioConverterErr_OperationNotSupported: - jack_log("error code : kAudioConverterErr_OperationNotSupported"); - break; - case kAudioConverterErr_PropertyNotSupported: - jack_log("error code : kAudioConverterErr_PropertyNotSupported"); - break; - case kAudioConverterErr_InvalidInputSize: - jack_log("error code : kAudioConverterErr_InvalidInputSize"); - break; - case kAudioConverterErr_InvalidOutputSize: - jack_log("error code : kAudioConverterErr_InvalidOutputSize"); - break; - case kAudioConverterErr_UnspecifiedError: - jack_log("error code : kAudioConverterErr_UnspecifiedError"); - break; - case kAudioConverterErr_BadPropertySizeError: - jack_log("error code : kAudioConverterErr_BadPropertySizeError"); - break; - case kAudioConverterErr_RequiresPacketDescriptionsError: - jack_log("error code : kAudioConverterErr_RequiresPacketDescriptionsError"); - break; - case kAudioConverterErr_InputSampleRateOutOfRange: - jack_log("error code : kAudioConverterErr_InputSampleRateOutOfRange"); - break; - case kAudioConverterErr_OutputSampleRateOutOfRange: - jack_log("error code : kAudioConverterErr_OutputSampleRateOutOfRange"); - break; - case kAudioHardwareNotRunningError: - jack_log("error code : kAudioHardwareNotRunningError"); - break; - case kAudioHardwareUnknownPropertyError: - jack_log("error code : kAudioHardwareUnknownPropertyError"); - break; - case kAudioHardwareIllegalOperationError: - jack_log("error code : kAudioHardwareIllegalOperationError"); - break; - case kAudioHardwareBadDeviceError: - jack_log("error code : kAudioHardwareBadDeviceError"); - break; - case kAudioHardwareBadStreamError: - jack_log("error code : kAudioHardwareBadStreamError"); - break; - case kAudioDeviceUnsupportedFormatError: - jack_log("error code : kAudioDeviceUnsupportedFormatError"); - break; - case kAudioDevicePermissionsError: - jack_log("error code : kAudioDevicePermissionsError"); - break; - case kAudioHardwareBadObjectError: - jack_log("error code : kAudioHardwareBadObjectError"); - break; - case kAudioHardwareUnsupportedOperationError: - jack_log("error code : kAudioHardwareUnsupportedOperationError"); - break; - default: - jack_log("error code : unknown"); - break; - } -} - -OSStatus JackCoreAudioAdapter::AudioHardwareNotificationCallback(AudioHardwarePropertyID inPropertyID, void* inClientData) -{ - JackCoreAudioAdapter* driver = (JackCoreAudioAdapter*)inClientData; - - switch (inPropertyID) { - - case kAudioHardwarePropertyDevices: { - jack_log("JackCoreAudioAdapter::AudioHardwareNotificationCallback kAudioHardwarePropertyDevices"); - DisplayDeviceNames(); - break; - } - } - - return noErr; -} - -OSStatus JackCoreAudioAdapter::SRNotificationCallback(AudioDeviceID inDevice, - UInt32 inChannel, - Boolean isInput, - AudioDevicePropertyID inPropertyID, - void* inClientData) -{ - JackCoreAudioAdapter* driver = static_cast(inClientData); - - switch (inPropertyID) { - - case kAudioDevicePropertyNominalSampleRate: { - jack_log("JackCoreAudioAdapter::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); - driver->fState = true; - break; - } - } - - return noErr; -} - -// A better implementation would try to recover in case of hardware device change (see HALLAB HLFilePlayerWindowControllerAudioDevicePropertyListenerProc code) -OSStatus JackCoreAudioAdapter::DeviceNotificationCallback(AudioDeviceID inDevice, - UInt32 inChannel, - Boolean isInput, - AudioDevicePropertyID inPropertyID, - void* inClientData) -{ - - switch (inPropertyID) { - - case kAudioDeviceProcessorOverload: { - jack_error("JackCoreAudioAdapter::DeviceNotificationCallback kAudioDeviceProcessorOverload"); - break; - } - - case kAudioDevicePropertyStreamConfiguration: { - jack_error("Cannot handle kAudioDevicePropertyStreamConfiguration"); - return kAudioHardwareUnsupportedOperationError; - } - - case kAudioDevicePropertyNominalSampleRate: { - jack_error("Cannot handle kAudioDevicePropertyNominalSampleRate"); - return kAudioHardwareUnsupportedOperationError; - } - - } - return noErr; -} - -int JackCoreAudioAdapter::AddListeners() -{ - OSStatus err = noErr; - - // Add listeners - err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDeviceProcessorOverload"); - printError(err); - return -1; - } - - err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, AudioHardwareNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioHardwareAddPropertyListener with kAudioHardwarePropertyDevices"); - printError(err); - return -1; - } - - err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); - printError(err); - return -1; - } - - err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceIsRunning"); - printError(err); - return -1; - } - - err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration"); - printError(err); - return -1; - } - - err = AudioDeviceAddPropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration"); - printError(err); - return -1; - } - - return 0; -} - -void JackCoreAudioAdapter::RemoveListeners() -{ - AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback); - AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, AudioHardwareNotificationCallback); - AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback); - AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback); - AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback); - AudioDeviceRemovePropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback); -} - -OSStatus JackCoreAudioAdapter::Render(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData) -{ - return static_cast(inRefCon)->Render(ioActionFlags, inTimeStamp, inNumberFrames, ioData); -} - -OSStatus JackCoreAudioAdapter::Render(AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inNumberFrames, - AudioBufferList *ioData) -{ - OSStatus err = AudioUnitRender(fAUHAL, ioActionFlags, inTimeStamp, 1, inNumberFrames, fInputData); - - if (err == noErr) { - jack_default_audio_sample_t* inputBuffer[fCaptureChannels]; - jack_default_audio_sample_t* outputBuffer[fPlaybackChannels]; - - for (int i = 0; i < fCaptureChannels; i++) { - inputBuffer[i] = (jack_default_audio_sample_t*)fInputData->mBuffers[i].mData; - } - for (int i = 0; i < fPlaybackChannels; i++) { - outputBuffer[i] = (jack_default_audio_sample_t*)ioData->mBuffers[i].mData; - } - - PushAndPull((jack_default_audio_sample_t**)inputBuffer, (jack_default_audio_sample_t**)outputBuffer, inNumberFrames); - return noErr; - } else { - return err; - } -} - -JackCoreAudioAdapter::JackCoreAudioAdapter(jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params) - :JackAudioAdapterInterface(buffer_size, sample_rate), fInputData(0), fCapturing(false), fPlaying(false), fState(false) -{ - const JSList* node; - const jack_driver_param_t* param; - int in_nChannels = 0; - int out_nChannels = 0; - char captureName[256]; - char playbackName[256]; - fCaptureUID[0] = 0; - fPlaybackUID[0] = 0; - fClockDriftCompensate = false; - - // Default values - fCaptureChannels = -1; - fPlaybackChannels = -1; - - SInt32 major; - SInt32 minor; - Gestalt(gestaltSystemVersionMajor, &major); - Gestalt(gestaltSystemVersionMinor, &minor); - - // Starting with 10.6 systems, the HAL notification thread is created internally - if (major == 10 && minor >= 6) { - CFRunLoopRef theRunLoop = NULL; - AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - OSStatus theError = AudioObjectSetPropertyData (kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); - if (theError != noErr) { - jack_error("JackCoreAudioAdapter::Open kAudioHardwarePropertyRunLoop error"); - } - } - - for (node = params; node; node = jack_slist_next(node)) { - param = (const jack_driver_param_t*) node->data; - - switch (param->character) { - - case 'i': - fCaptureChannels = param->value.ui; - break; - - case 'o': - fPlaybackChannels = param->value.ui; - break; - - case 'C': - fCapturing = true; - strncpy(fCaptureUID, param->value.str, 256); - break; - - case 'P': - fPlaying = true; - strncpy(fPlaybackUID, param->value.str, 256); - break; - - case 'd': - strncpy(fCaptureUID, param->value.str, 256); - strncpy(fPlaybackUID, param->value.str, 256); - break; - - case 'D': - fCapturing = fPlaying = true; - break; - - case 'r': - SetAdaptedSampleRate(param->value.ui); - break; - - case 'p': - SetAdaptedBufferSize(param->value.ui); - break; - - case 'l': - DisplayDeviceNames(); - break; - - case 'q': - fQuality = param->value.ui; - break; - - case 'g': - fRingbufferCurSize = param->value.ui; - fAdaptative = false; - break; - - case 's': - fClockDriftCompensate = true; - break; - } - } - - /* duplex is the default */ - if (!fCapturing && !fPlaying) { - fCapturing = true; - fPlaying = true; - } - - if (SetupDevices(fCaptureUID, fPlaybackUID, captureName, playbackName, fAdaptedSampleRate) < 0) { - throw std::bad_alloc(); - } - - if (SetupChannels(fCapturing, fPlaying, fCaptureChannels, fPlaybackChannels, in_nChannels, out_nChannels, true) < 0) { - throw std::bad_alloc(); - } - - if (SetupBufferSize(fAdaptedBufferSize) < 0) { - throw std::bad_alloc(); - } - - if (SetupSampleRate(fAdaptedSampleRate) < 0) { - throw std::bad_alloc(); - } - - if (OpenAUHAL(fCapturing, fPlaying, fCaptureChannels, fPlaybackChannels, in_nChannels, out_nChannels, fAdaptedBufferSize, fAdaptedSampleRate) < 0) { - throw std::bad_alloc(); - } - - if (fCapturing && fCaptureChannels > 0) { - if (SetupBuffers(fCaptureChannels) < 0) { - throw std::bad_alloc(); - } - } - - if (AddListeners() < 0) { - throw std::bad_alloc(); - } - - GetStreamLatencies(fDeviceID, true, fInputLatencies); - GetStreamLatencies(fDeviceID, false, fOutputLatencies); -} - -OSStatus JackCoreAudioAdapter::GetDefaultDevice(AudioDeviceID* id) -{ - OSStatus res; - UInt32 theSize = sizeof(UInt32); - AudioDeviceID inDefault; - AudioDeviceID outDefault; - - if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) { - return res; - } - - if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) { - return res; - } - - jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault); - - // Get the device only if default input and output are the same - if (inDefault != outDefault) { - jack_error("Default input and output devices are not the same !!"); - return kAudioHardwareBadDeviceError; - } else if (inDefault == 0) { - jack_error("Default input and output devices are null !!"); - return kAudioHardwareBadDeviceError; - } else { - *id = inDefault; - return noErr; - } -} - -OSStatus JackCoreAudioAdapter::GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput) -{ - OSStatus err = noErr; - UInt32 outSize; - Boolean outWritable; - - channelCount = 0; - err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable); - if (err == noErr) { - AudioBufferList bufferList[outSize]; - err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList); - if (err == noErr) { - for (unsigned int i = 0; i < bufferList->mNumberBuffers; i++) { - channelCount += bufferList->mBuffers[i].mNumberChannels; - } - } - } - - return err; -} - -OSStatus JackCoreAudioAdapter::GetDeviceIDFromUID(const char* UID, AudioDeviceID* id) -{ - UInt32 size = sizeof(AudioValueTranslation); - CFStringRef inIUD = CFStringCreateWithCString(NULL, UID, CFStringGetSystemEncoding()); - AudioValueTranslation value = { &inIUD, sizeof(CFStringRef), id, sizeof(AudioDeviceID) }; - - if (inIUD == NULL) { - return kAudioHardwareUnspecifiedError; - } else { - OSStatus res = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &value); - CFRelease(inIUD); - jack_log("GetDeviceIDFromUID %s %ld", UID, *id); - return (*id == kAudioDeviceUnknown) ? kAudioHardwareBadDeviceError : res; - } -} - -OSStatus JackCoreAudioAdapter::GetDefaultInputDevice(AudioDeviceID* id) -{ - OSStatus res; - UInt32 theSize = sizeof(UInt32); - AudioDeviceID inDefault; - - if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) { - return res; - } - - if (inDefault == 0) { - jack_error("Error: default input device is 0, please select a correct one !!"); - return -1; - } - jack_log("GetDefaultInputDevice: input = %ld ", inDefault); - *id = inDefault; - return noErr; -} - -OSStatus JackCoreAudioAdapter::GetDefaultOutputDevice(AudioDeviceID* id) -{ - OSStatus res; - UInt32 theSize = sizeof(UInt32); - AudioDeviceID outDefault; - - if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) { - return res; - } - - if (outDefault == 0) { - jack_error("Error: default output device is 0, please select a correct one !!"); - return -1; - } - jack_log("GetDefaultOutputDevice: output = %ld", outDefault); - *id = outDefault; - return noErr; -} - -OSStatus JackCoreAudioAdapter::GetDeviceNameFromID(AudioDeviceID id, char* name) -{ - UInt32 size = 256; - return AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceName, &size, name); -} - -AudioDeviceID JackCoreAudioAdapter::GetDeviceIDFromName(const char* name) -{ - UInt32 size; - Boolean isWritable; - int i, deviceNum; - - OSStatus err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); - if (err != noErr) { - return -1; - } - - deviceNum = size / sizeof(AudioDeviceID); - AudioDeviceID devices[deviceNum]; - - err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); - if (err != noErr) { - return err; - } - - for (i = 0; i < deviceNum; i++) { - char device_name[256]; - size = 256; - err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name); - if (err != noErr) { - return -1; - } else if (strcmp(device_name, name) == 0) { - return devices[i]; - } - } - - return -1; -} - -// Setup -int JackCoreAudioAdapter::SetupDevices(const char* capture_driver_uid, - const char* playback_driver_uid, - char* capture_driver_name, - char* playback_driver_name, - jack_nframes_t samplerate) -{ - capture_driver_name[0] = 0; - playback_driver_name[0] = 0; - - // Duplex - if (strcmp(capture_driver_uid, "") != 0 && strcmp(playback_driver_uid, "") != 0) { - jack_log("JackCoreAudioDriver::Open duplex"); - - // Same device for capture and playback... - if (strcmp(capture_driver_uid, playback_driver_uid) == 0) { - - if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { - jack_log("Will take default in/out"); - if (GetDefaultDevice(&fDeviceID) != noErr) { - jack_error("Cannot open default device"); - return -1; - } - } - if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr || GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { - jack_error("Cannot get device name from device ID"); - return -1; - } - - } else { - - // Creates aggregate device - AudioDeviceID captureID, playbackID; - - if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { - jack_log("Will take default input"); - if (GetDefaultInputDevice(&captureID) != noErr) { - jack_error("Cannot open default input device"); - return -1; - } - } - - if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) { - jack_log("Will take default output"); - if (GetDefaultOutputDevice(&playbackID) != noErr) { - jack_error("Cannot open default output device"); - return -1; - } - } - - if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) { - return -1; - } - } - - // Capture only - } else if (strcmp(capture_driver_uid, "") != 0) { - jack_log("JackCoreAudioAdapter::Open capture only"); - if (GetDeviceIDFromUID(capture_driver_uid, &fDeviceID) != noErr) { - if (GetDefaultInputDevice(&fDeviceID) != noErr) { - jack_error("Cannot open default input device"); - return -1; - } - } - if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr) { - jack_error("Cannot get device name from device ID"); - return -1; - } - - // Playback only - } else if (strcmp(playback_driver_uid, "") != 0) { - jack_log("JackCoreAudioAdapter::Open playback only"); - if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { - if (GetDefaultOutputDevice(&fDeviceID) != noErr) { - jack_error("Cannot open default output device"); - return -1; - } - } - if (GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { - jack_error("Cannot get device name from device ID"); - return -1; - } - - // Use default driver in duplex mode - } else { - jack_log("JackCoreAudioAdapter::Open default driver"); - if (GetDefaultDevice(&fDeviceID) != noErr) { - jack_error("Cannot open default device in duplex mode, so aggregate default input and default output"); - - // Creates aggregate device - AudioDeviceID captureID = -1, playbackID = -1; - - if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { - jack_log("Will take default input"); - if (GetDefaultInputDevice(&captureID) != noErr) { - jack_error("Cannot open default input device"); - goto built_in; - } - } - - if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) { - jack_log("Will take default output"); - if (GetDefaultOutputDevice(&playbackID) != noErr) { - jack_error("Cannot open default output device"); - goto built_in; - } - } - - if (captureID > 0 && playbackID > 0) { - if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) { - goto built_in; - } - } else { - jack_error("Cannot use default input/output"); - goto built_in; - } - } - } - - return 0; - -built_in: - - // Aggregate built-in input and output - AudioDeviceID captureID = GetDeviceIDFromName("Built-in Input"); - AudioDeviceID playbackID = GetDeviceIDFromName("Built-in Output"); - - if (captureID > 0 && playbackID > 0) { - if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) { - return -1; - } - } else { - jack_error("Cannot aggregate built-in input and output"); - return -1; - } - - return 0; -} - -int JackCoreAudioAdapter::SetupChannels(bool capturing, - bool playing, - int& inchannels, - int& outchannels, - int& in_nChannels, - int& out_nChannels, - bool strict) -{ - OSStatus err = noErr; - - if (capturing) { - err = GetTotalChannels(fDeviceID, in_nChannels, true); - if (err != noErr) { - jack_error("Cannot get input channel number"); - printError(err); - return -1; - } else { - jack_log("Max input channels : %d", in_nChannels); - } - } - - if (playing) { - err = GetTotalChannels(fDeviceID, out_nChannels, false); - if (err != noErr) { - jack_error("Cannot get output channel number"); - printError(err); - return -1; - } else { - jack_log("Max output channels : %d", out_nChannels); - } - } - - if (inchannels > in_nChannels) { - jack_error("This device hasn't required input channels inchannels = %ld in_nChannels = %ld", inchannels, in_nChannels); - if (strict) { - return -1; - } - } - - if (outchannels > out_nChannels) { - jack_error("This device hasn't required output channels outchannels = %ld out_nChannels = %ld", outchannels, out_nChannels); - if (strict) { - return -1; - } - } - - if (inchannels == -1) { - jack_log("Setup max in channels = %ld", in_nChannels); - inchannels = in_nChannels; - } - - if (outchannels == -1) { - jack_log("Setup max out channels = %ld", out_nChannels); - outchannels = out_nChannels; - } - - return 0; -} - -int JackCoreAudioAdapter::SetupBufferSize(jack_nframes_t buffer_size) -{ - // Setting buffer size - UInt32 outSize = sizeof(UInt32); - OSStatus err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, outSize, &buffer_size); - if (err != noErr) { - jack_error("Cannot set buffer size %ld", buffer_size); - printError(err); - return -1; - } - - return 0; -} - -int JackCoreAudioAdapter::SetupSampleRate(jack_nframes_t samplerate) -{ - return SetupSampleRateAux(fDeviceID, samplerate); -} - -int JackCoreAudioAdapter::SetupSampleRateAux(AudioDeviceID inDevice, jack_nframes_t samplerate) -{ - OSStatus err = noErr; - UInt32 outSize; - Float64 sampleRate; - - // Get sample rate - outSize = sizeof(Float64); - err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &sampleRate); - if (err != noErr) { - jack_error("Cannot get current sample rate"); - printError(err); - return -1; - } else { - jack_log("Current sample rate = %f", sampleRate); - } - - // If needed, set new sample rate - if (samplerate != (jack_nframes_t)sampleRate) { - sampleRate = (Float64)samplerate; - - // To get SR change notification - err = AudioDeviceAddPropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); - printError(err); - return -1; - } - err = AudioDeviceSetProperty(inDevice, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outSize, &sampleRate); - if (err != noErr) { - jack_error("Cannot set sample rate = %ld", samplerate); - printError(err); - return -1; - } - - // Waiting for SR change notification - int count = 0; - while (!fState && count++ < WAIT_COUNTER) { - usleep(100000); - jack_log("Wait count = %d", count); - } - - // Remove SR change notification - AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback); - } - - return 0; -} - -int JackCoreAudioAdapter::SetupBuffers(int inchannels) -{ - jack_log("JackCoreAudioAdapter::SetupBuffers: input = %ld", inchannels); - - // Prepare buffers - fInputData = (AudioBufferList*)malloc(sizeof(UInt32) + inchannels * sizeof(AudioBuffer)); - fInputData->mNumberBuffers = inchannels; - for (int i = 0; i < fCaptureChannels; i++) { - fInputData->mBuffers[i].mNumberChannels = 1; - fInputData->mBuffers[i].mDataByteSize = fAdaptedBufferSize * sizeof(jack_default_audio_sample_t); - fInputData->mBuffers[i].mData = malloc(fAdaptedBufferSize * sizeof(jack_default_audio_sample_t)); - } - return 0; -} - -void JackCoreAudioAdapter::DisposeBuffers() -{ - if (fInputData) { - for (int i = 0; i < fCaptureChannels; i++) { - free(fInputData->mBuffers[i].mData); - } - free(fInputData); - fInputData = 0; - } -} - -int JackCoreAudioAdapter::OpenAUHAL(bool capturing, - bool playing, - int inchannels, - int outchannels, - int in_nChannels, - int out_nChannels, - jack_nframes_t buffer_size, - jack_nframes_t samplerate) -{ - ComponentResult err1; - UInt32 enableIO; - AudioStreamBasicDescription srcFormat, dstFormat; - AudioDeviceID currAudioDeviceID; - UInt32 size; - - jack_log("OpenAUHAL capturing = %d playing = %d inchannels = %d outchannels = %d in_nChannels = %d out_nChannels = %d", capturing, playing, inchannels, outchannels, in_nChannels, out_nChannels); - - if (inchannels == 0 && outchannels == 0) { - jack_error("No input and output channels..."); - return -1; - } - - // AUHAL -#ifdef MAC_OS_X_VERSION_10_5 - ComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0}; - Component HALOutput = FindNextComponent(NULL, &cd); - err1 = OpenAComponent(HALOutput, &fAUHAL); - if (err1 != noErr) { - jack_error("Error calling OpenAComponent"); - printError(err1); - goto error; - } -#else - AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0}; - AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd); - err1 = AudioComponentInstanceNew(HALOutput, &fAUHAL); - if (err1 != noErr) { - jack_error("Error calling AudioComponentInstanceNew"); - printError(err1); - goto error; - } -#endif - - err1 = AudioUnitInitialize(fAUHAL); - if (err1 != noErr) { - jack_error("Cannot initialize AUHAL unit"); - printError(err1); - goto error; - } - - // Start I/O - if (capturing && inchannels > 0) { - enableIO = 1; - jack_log("Setup AUHAL input on"); - } else { - enableIO = 0; - jack_log("Setup AUHAL input off"); - } - - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input"); - printError(err1); - goto error; - } - - if (playing && outchannels > 0) { - enableIO = 1; - jack_log("Setup AUHAL output on"); - } else { - enableIO = 0; - jack_log("Setup AUHAL output off"); - } - - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Output"); - printError(err1); - goto error; - } - - size = sizeof(AudioDeviceID); - err1 = AudioUnitGetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &currAudioDeviceID, &size); - if (err1 != noErr) { - jack_error("Error calling AudioUnitGetProperty - kAudioOutputUnitProperty_CurrentDevice"); - printError(err1); - goto error; - } else { - jack_log("AudioUnitGetPropertyCurrentDevice = %d", currAudioDeviceID); - } - - // Setup up choosen device, in both input and output cases - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &fDeviceID, sizeof(AudioDeviceID)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_CurrentDevice"); - printError(err1); - goto error; - } - - // Set buffer size - if (capturing && inchannels > 0) { - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&buffer_size, sizeof(UInt32)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); - printError(err1); - goto error; - } - } - - if (playing && outchannels > 0) { - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&buffer_size, sizeof(UInt32)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); - printError(err1); - goto error; - } - } - - // Setup channel map - if (capturing && inchannels > 0 && inchannels <= in_nChannels) { - SInt32 chanArr[in_nChannels]; - for (int i = 0; i < in_nChannels; i++) { - chanArr[i] = -1; - } - for (int i = 0; i < inchannels; i++) { - chanArr[i] = i; - } - AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap , kAudioUnitScope_Input, 1, chanArr, sizeof(SInt32) * in_nChannels); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 1"); - printError(err1); - goto error; - } - } - - if (playing && outchannels > 0 && outchannels <= out_nChannels) { - SInt32 chanArr[out_nChannels]; - for (int i = 0; i < out_nChannels; i++) { - chanArr[i] = -1; - } - for (int i = 0; i < outchannels; i++) { - chanArr[i] = i; - } - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 0, chanArr, sizeof(SInt32) * out_nChannels); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 0"); - printError(err1); - goto error; - } - } - - // Setup stream converters - if (capturing && inchannels > 0) { - - size = sizeof(AudioStreamBasicDescription); - err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &srcFormat, &size); - if (err1 != noErr) { - jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); - printError(err1); - goto error; - } - PrintStreamDesc(&srcFormat); - - jack_log("Setup AUHAL input stream converter SR = %ld", samplerate); - srcFormat.mSampleRate = samplerate; - srcFormat.mFormatID = kAudioFormatLinearPCM; - srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; - srcFormat.mBytesPerPacket = sizeof(jack_default_audio_sample_t); - srcFormat.mFramesPerPacket = 1; - srcFormat.mBytesPerFrame = sizeof(jack_default_audio_sample_t); - srcFormat.mChannelsPerFrame = inchannels; - srcFormat.mBitsPerChannel = 32; - PrintStreamDesc(&srcFormat); - - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription)); - - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); - printError(err1); - goto error; - } - } - - if (playing && outchannels > 0) { - - size = sizeof(AudioStreamBasicDescription); - err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &dstFormat, &size); - if (err1 != noErr) { - jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); - printError(err1); - goto error; - } - PrintStreamDesc(&dstFormat); - - jack_log("Setup AUHAL output stream converter SR = %ld", samplerate); - dstFormat.mSampleRate = samplerate; - dstFormat.mFormatID = kAudioFormatLinearPCM; - dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; - dstFormat.mBytesPerPacket = sizeof(jack_default_audio_sample_t); - dstFormat.mFramesPerPacket = 1; - dstFormat.mBytesPerFrame = sizeof(jack_default_audio_sample_t); - dstFormat.mChannelsPerFrame = outchannels; - dstFormat.mBitsPerChannel = 32; - PrintStreamDesc(&dstFormat); - - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription)); - - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); - printError(err1); - goto error; - } - } - - // Setup callbacks - if (inchannels > 0 && outchannels == 0) { - AURenderCallbackStruct output; - output.inputProc = Render; - output.inputProcRefCon = this; - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1"); - printError(err1); - goto error; - } - } else { - AURenderCallbackStruct output; - output.inputProc = Render; - output.inputProcRefCon = this; - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0"); - printError(err1); - goto error; - } - } - - return 0; - -error: - CloseAUHAL(); - return -1; -} - -OSStatus JackCoreAudioAdapter::DestroyAggregateDevice() -{ - OSStatus osErr = noErr; - AudioObjectPropertyAddress pluginAOPA; - pluginAOPA.mSelector = kAudioPlugInDestroyAggregateDevice; - pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; - pluginAOPA.mElement = kAudioObjectPropertyElementMaster; - UInt32 outDataSize; - - osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); - if (osErr != noErr) { - jack_error("JackCoreAudioAdapter::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); - printError(osErr); - return osErr; - } - - osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID); - if (osErr != noErr) { - jack_error("JackCoreAudioAdapter::DestroyAggregateDevice : AudioObjectGetPropertyData error"); - printError(osErr); - return osErr; - } - - return noErr; -} - -static CFStringRef GetDeviceName(AudioDeviceID id) -{ - UInt32 size = sizeof(CFStringRef); - CFStringRef UIname; - OSStatus err = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); - return (err == noErr) ? UIname : NULL; -} - -OSStatus JackCoreAudioAdapter::CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) -{ - OSStatus err = noErr; - AudioObjectID sub_device[32]; - UInt32 outSize = sizeof(sub_device); - - err = AudioDeviceGetProperty(captureDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); - vector captureDeviceIDArray; - - if (err != noErr) { - jack_log("Input device does not have subdevices"); - captureDeviceIDArray.push_back(captureDeviceID); - } else { - int num_devices = outSize / sizeof(AudioObjectID); - jack_log("Input device has %d subdevices", num_devices); - for (int i = 0; i < num_devices; i++) { - captureDeviceIDArray.push_back(sub_device[i]); - } - } - - outSize = sizeof(sub_device); - err = AudioDeviceGetProperty(playbackDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); - vector playbackDeviceIDArray; - - if (err != noErr) { - jack_log("Output device does not have subdevices"); - playbackDeviceIDArray.push_back(playbackDeviceID); - } else { - int num_devices = outSize / sizeof(AudioObjectID); - jack_log("Output device has %d subdevices", num_devices); - for (int i = 0; i < num_devices; i++) { - playbackDeviceIDArray.push_back(sub_device[i]); - } - } - - return CreateAggregateDeviceAux(captureDeviceIDArray, playbackDeviceIDArray, samplerate, outAggregateDevice); -} - -OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector captureDeviceID, vector playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) -{ - OSStatus osErr = noErr; - UInt32 outSize; - Boolean outWritable; - - // Prepare sub-devices for clock drift compensation - // Workaround for bug in the HAL : until 10.6.2 - AudioObjectPropertyAddress theAddressOwned = { kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - AudioObjectPropertyAddress theAddressDrift = { kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - UInt32 theQualifierDataSize = sizeof(AudioObjectID); - AudioClassID inClass = kAudioSubDeviceClassID; - void* theQualifierData = &inClass; - UInt32 subDevicesNum = 0; - - //--------------------------------------------------------------------------- - // Setup SR of both devices otherwise creating AD may fail... - //--------------------------------------------------------------------------- - UInt32 keptclockdomain = 0; - UInt32 clockdomain = 0; - outSize = sizeof(UInt32); - bool need_clock_drift_compensation = false; - - for (UInt32 i = 0; i < captureDeviceID.size(); i++) { - if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice : cannot set SR of input device"); - } else { - // Check clock domain - osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); - if (osErr != 0) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); - printError(osErr); - } else { - keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; - jack_log("JackCoreAudioAdapter::CreateAggregateDevice : input clockdomain = %d", clockdomain); - if (clockdomain != 0 && clockdomain != keptclockdomain) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); - need_clock_drift_compensation = true; - } - } - } - } - - for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { - if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice : cannot set SR of output device"); - } else { - // Check clock domain - osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); - if (osErr != 0) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); - printError(osErr); - } else { - keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; - jack_log("JackCoreAudioAdapter::CreateAggregateDevice : output clockdomain = %d", clockdomain); - if (clockdomain != 0 && clockdomain != keptclockdomain) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); - need_clock_drift_compensation = true; - } - } - } - } - - // If no valid clock domain was found, then assume we have to compensate... - if (keptclockdomain == 0) { - need_clock_drift_compensation = true; - } - - //--------------------------------------------------------------------------- - // Start to create a new aggregate by getting the base audio hardware plugin - //--------------------------------------------------------------------------- - - char device_name[256]; - for (UInt32 i = 0; i < captureDeviceID.size(); i++) { - GetDeviceNameFromID(captureDeviceID[i], device_name); - jack_info("Separated input = '%s' ", device_name); - } - - for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { - GetDeviceNameFromID(playbackDeviceID[i], device_name); - jack_info("Separated output = '%s' ", device_name); - } - - osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); - if (osErr != noErr) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); - printError(osErr); - return osErr; - } - - AudioValueTranslation pluginAVT; - - CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio"); - - pluginAVT.mInputData = &inBundleRef; - pluginAVT.mInputDataSize = sizeof(inBundleRef); - pluginAVT.mOutputData = &fPluginID; - pluginAVT.mOutputDataSize = sizeof(fPluginID); - - osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); - if (osErr != noErr) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); - printError(osErr); - return osErr; - } - - //------------------------------------------------- - // Create a CFDictionary for our aggregate device - //------------------------------------------------- - - CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - CFStringRef AggregateDeviceNameRef = CFSTR("JackDuplex"); - CFStringRef AggregateDeviceUIDRef = CFSTR("com.grame.JackDuplex"); - - // add the name of the device to the dictionary - CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef); - - // add our choice of UID for the aggregate device to the dictionary - CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef); - - // add a "private aggregate key" to the dictionary - int value = 1; - CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value); - - SInt32 system; - Gestalt(gestaltSystemVersion, &system); - - jack_log("JackCoreAudioAdapter::CreateAggregateDevice : system version = %x limit = %x", system, 0x00001054); - - // Starting with 10.5.4 systems, the AD can be internal... (better) - if (system < 0x00001054) { - jack_log("JackCoreAudioAdapter::CreateAggregateDevice : public aggregate device...."); - } else { - jack_log("JackCoreAudioAdapter::CreateAggregateDevice : private aggregate device...."); - CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); - } - - // Prepare sub-devices for clock drift compensation - CFMutableArrayRef subDevicesArrayClock = NULL; - - /* - if (fClockDriftCompensate) { - if (need_clock_drift_compensation) { - jack_info("Clock drift compensation activated..."); - subDevicesArrayClock = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - - for (UInt32 i = 0; i < captureDeviceID.size(); i++) { - CFStringRef UID = GetDeviceName(captureDeviceID[i]); - if (UID) { - CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID); - CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef); - //CFRelease(UID); - CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict); - } - } - - for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { - CFStringRef UID = GetDeviceName(playbackDeviceID[i]); - if (UID) { - CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID); - CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef); - //CFRelease(UID); - CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict); - } - } - - // add sub-device clock array for the aggregate device to the dictionary - CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock); - } else { - jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)"); - } - } - */ - - //------------------------------------------------- - // Create a CFMutableArray for our sub-device list - //------------------------------------------------- - - // we need to append the UID for each device to a CFMutableArray, so create one here - CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - - vector captureDeviceUID; - for (UInt32 i = 0; i < captureDeviceID.size(); i++) { - CFStringRef ref = GetDeviceName(captureDeviceID[i]); - if (ref == NULL) { - return -1; - } - captureDeviceUID.push_back(ref); - // input sub-devices in this example, so append the sub-device's UID to the CFArray - CFArrayAppendValue(subDevicesArray, ref); - } - - vector playbackDeviceUID; - for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { - CFStringRef ref = GetDeviceName(playbackDeviceID[i]); - if (ref == NULL) { - return -1; - } - playbackDeviceUID.push_back(ref); - // output sub-devices in this example, so append the sub-device's UID to the CFArray - CFArrayAppendValue(subDevicesArray, ref); - } - - //----------------------------------------------------------------------- - // Feed the dictionary to the plugin, to create a blank aggregate device - //----------------------------------------------------------------------- - - AudioObjectPropertyAddress pluginAOPA; - pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice; - pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; - pluginAOPA.mElement = kAudioObjectPropertyElementMaster; - UInt32 outDataSize; - - osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); - if (osErr != noErr) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectGetPropertyDataSize error"); - printError(osErr); - goto error; - } - - osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); - if (osErr != noErr) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectGetPropertyData error"); - printError(osErr); - goto error; - } - - // pause for a bit to make sure that everything completed correctly - // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); - - //------------------------- - // Set the sub-device list - //------------------------- - - pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList; - pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; - pluginAOPA.mElement = kAudioObjectPropertyElementMaster; - outDataSize = sizeof(CFMutableArrayRef); - osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); - if (osErr != noErr) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error"); - printError(osErr); - goto error; - } - - // pause again to give the changes time to take effect - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); - - //----------------------- - // Set the master device - //----------------------- - - // set the master device manually (this is the device which will act as the master clock for the aggregate device) - // pass in the UID of the device you want to use - pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice; - pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; - pluginAOPA.mElement = kAudioObjectPropertyElementMaster; - outDataSize = sizeof(CFStringRef); - osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &playbackDeviceUID[0]); // First playback is master... - if (osErr != noErr) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectSetPropertyData for master device error"); - printError(osErr); - goto error; - } - - // pause again to give the changes time to take effect - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); - - // Prepare sub-devices for clock drift compensation - // Workaround for bug in the HAL : until 10.6.2 - - if (fClockDriftCompensate) { - if (need_clock_drift_compensation) { - jack_info("Clock drift compensation activated..."); - - // Get the property data size - osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize); - if (osErr != noErr) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); - printError(osErr); - } - - // Calculate the number of object IDs - subDevicesNum = outSize / sizeof(AudioObjectID); - jack_info("JackCoreAudioAdapter::CreateAggregateDevice clock drift compensation, number of sub-devices = %d", subDevicesNum); - AudioObjectID subDevices[subDevicesNum]; - outSize = sizeof(subDevices); - - osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices); - if (osErr != noErr) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); - printError(osErr); - } - - // Set kAudioSubDevicePropertyDriftCompensation property... - for (UInt32 index = 0; index < subDevicesNum; ++index) { - UInt32 theDriftCompensationValue = 1; - osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue); - if (osErr != noErr) { - jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error"); - printError(osErr); - } - } - } else { - jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)"); - } - } - - // pause again to give the changes time to take effect - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); - - //---------- - // Clean up - //---------- - - // release the private AD key - CFRelease(AggregateDeviceNumberRef); - - // release the CF objects we have created - we don't need them any more - CFRelease(aggDeviceDict); - CFRelease(subDevicesArray); - - if (subDevicesArrayClock) { - CFRelease(subDevicesArrayClock); - } - - // release the device UID - for (UInt32 i = 0; i < captureDeviceUID.size(); i++) { - CFRelease(captureDeviceUID[i]); - } - - for (UInt32 i = 0; i < playbackDeviceUID.size(); i++) { - CFRelease(playbackDeviceUID[i]); - } - - jack_log("New aggregate device %ld", *outAggregateDevice); - return noErr; - -error: - DestroyAggregateDevice(); - return -1; -} - - -bool JackCoreAudioAdapter::IsAggregateDevice(AudioDeviceID device) -{ - OSStatus err = noErr; - AudioObjectID sub_device[32]; - UInt32 outSize = sizeof(sub_device); - err = AudioDeviceGetProperty(device, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); - - if (err != noErr) { - jack_log("Device does not have subdevices"); - return false; - } else { - int num_devices = outSize / sizeof(AudioObjectID); - jack_log("Device does has %d subdevices", num_devices); - return true; - } -} - -void JackCoreAudioAdapter::CloseAUHAL() -{ - AudioUnitUninitialize(fAUHAL); - CloseComponent(fAUHAL); -} - -int JackCoreAudioAdapter::Open() -{ - return (AudioOutputUnitStart(fAUHAL) != noErr) ? -1 : 0; -} - -int JackCoreAudioAdapter::Close() -{ -#ifdef JACK_MONITOR - fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize); -#endif - AudioOutputUnitStop(fAUHAL); - DisposeBuffers(); - CloseAUHAL(); - RemoveListeners(); - if (fPluginID > 0) { - DestroyAggregateDevice(); - } - return 0; -} - -int JackCoreAudioAdapter::SetSampleRate(jack_nframes_t sample_rate) -{ - JackAudioAdapterInterface::SetHostSampleRate(sample_rate); - Close(); - return Open(); -} - -int JackCoreAudioAdapter::SetBufferSize(jack_nframes_t buffer_size) -{ - JackAudioAdapterInterface::SetHostBufferSize(buffer_size); - Close(); - return Open(); -} - -OSStatus JackCoreAudioAdapter::GetStreamLatencies(AudioDeviceID device, bool isInput, vector& latencies) -{ - OSStatus err = noErr; - UInt32 outSize1, outSize2, outSize3; - Boolean outWritable; - - err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, &outWritable); - if (err == noErr) { - int stream_count = outSize1 / sizeof(UInt32); - AudioStreamID streamIDs[stream_count]; - AudioBufferList bufferList[stream_count]; - UInt32 streamLatency; - outSize2 = sizeof(UInt32); - - err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, streamIDs); - if (err != noErr) { - jack_error("GetStreamLatencies kAudioDevicePropertyStreams err = %d", err); - return err; - } - - err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, &outWritable); - if (err != noErr) { - jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err); - return err; - } - - for (int i = 0; i < stream_count; i++) { - err = AudioStreamGetProperty(streamIDs[i], 0, kAudioStreamPropertyLatency, &outSize2, &streamLatency); - if (err != noErr) { - jack_error("GetStreamLatencies kAudioStreamPropertyLatency err = %d", err); - return err; - } - err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, bufferList); - if (err != noErr) { - jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err); - return err; - } - // Push 'channel' time the stream latency - for (uint k = 0; k < bufferList->mBuffers[i].mNumberChannels; k++) { - latencies.push_back(streamLatency); - } - } - } - return err; -} - -int JackCoreAudioAdapter::GetLatency(int port_index, bool input) -{ - UInt32 size = sizeof(UInt32); - UInt32 value1 = 0; - UInt32 value2 = 0; - - OSStatus err = AudioDeviceGetProperty(fDeviceID, 0, input, kAudioDevicePropertyLatency, &size, &value1); - if (err != noErr) { - jack_log("AudioDeviceGetProperty kAudioDevicePropertyLatency error"); - } - err = AudioDeviceGetProperty(fDeviceID, 0, input, kAudioDevicePropertySafetyOffset, &size, &value2); - if (err != noErr) { - jack_log("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error"); - } - - // TODO : add stream latency - - return value1 + value2 + fAdaptedBufferSize; -} - -int JackCoreAudioAdapter::GetInputLatency(int port_index) -{ - if (port_index < int(fInputLatencies.size())) { - return GetLatency(port_index, true) + fInputLatencies[port_index]; - } else { - // No stream latency - return GetLatency(port_index, true); - } -} - -int JackCoreAudioAdapter::GetOutputLatency(int port_index) -{ - if (port_index < int(fOutputLatencies.size())) { - return GetLatency(port_index, false) + fOutputLatencies[port_index]; - } else { - // No stream latency - return GetLatency(port_index, false); - } -} - -} // namespace - -#ifdef __cplusplus -extern "C" -{ -#endif - - SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor() - { - jack_driver_desc_t * desc; - jack_driver_desc_filler_t filler; - jack_driver_param_value_t value; - - desc = jack_driver_descriptor_construct("audioadapter", JackDriverNone, "netjack audio <==> net backend adapter", &filler); - - value.i = -1; - jack_driver_descriptor_add_parameter(desc, &filler, "in-channels", 'i', JackDriverParamInt, &value, NULL, "Maximum number of input channels", "Maximum number of input channels. If -1, max possible number of input channels will be used"); - jack_driver_descriptor_add_parameter(desc, &filler, "out-channels", 'o', JackDriverParamInt, &value, NULL, "Maximum number of output channels", "Maximum number of output channels. If -1, max possible number of output channels will be used"); - - value.str[0] = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Input CoreAudio device name", NULL); - jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamString, &value, NULL, "Output CoreAudio device name", NULL); - - value.ui = 44100U; - jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL); - - value.ui = 512U; - jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL); - - value.i = true; - jack_driver_descriptor_add_parameter(desc, &filler, "duplex", 'D', JackDriverParamBool, &value, NULL, "Provide both capture and playback ports", NULL); - - value.str[0] = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "CoreAudio device name", NULL); - - value.i = true; - jack_driver_descriptor_add_parameter(desc, &filler, "list-devices", 'l', JackDriverParamBool, &value, NULL, "Display available CoreAudio devices", NULL); - - value.ui = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "quality", 'q', JackDriverParamInt, &value, NULL, "Resample algorithm quality (0 - 4)", NULL); - - value.ui = 32768; - jack_driver_descriptor_add_parameter(desc, &filler, "ring-buffer", 'g', JackDriverParamInt, &value, NULL, "Fixed ringbuffer size", "Fixed ringbuffer size (if not set => automatic adaptative)"); - - value.i = false; - jack_driver_descriptor_add_parameter(desc, &filler, "clock-drift", 's', JackDriverParamBool, &value, NULL, "Clock drift compensation", "Whether to compensate clock drift in dynamically created aggregate device"); - - value.i = false; - jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect audioadapter to system ports", NULL); - - return desc; - } - - -#ifdef __cplusplus -} -#endif - diff -Nru jackd2-1.9.11~20161209/macosx/coreaudio/JackCoreAudioAdapter.mm jackd2-1.9.11+git20170722/macosx/coreaudio/JackCoreAudioAdapter.mm --- jackd2-1.9.11~20161209/macosx/coreaudio/JackCoreAudioAdapter.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coreaudio/JackCoreAudioAdapter.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,1755 @@ +/* +Copyright (C) 2008 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackCoreAudioAdapter.h" +#include "JackError.h" +#include + +#include + +namespace Jack +{ + +static void PrintStreamDesc(AudioStreamBasicDescription *inDesc) +{ + jack_log("- - - - - - - - - - - - - - - - - - - -"); + jack_log(" Sample Rate:%f", inDesc->mSampleRate); + jack_log(" Format ID:%.*s", (int) sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID); + jack_log(" Format Flags:%lX", inDesc->mFormatFlags); + jack_log(" Bytes per Packet:%ld", inDesc->mBytesPerPacket); + jack_log(" Frames per Packet:%ld", inDesc->mFramesPerPacket); + jack_log(" Bytes per Frame:%ld", inDesc->mBytesPerFrame); + jack_log(" Channels per Frame:%ld", inDesc->mChannelsPerFrame); + jack_log(" Bits per Channel:%ld", inDesc->mBitsPerChannel); + jack_log("- - - - - - - - - - - - - - - - - - - -"); +} + +static OSStatus DisplayDeviceNames() +{ + UInt32 size; + Boolean isWritable; + int i, deviceNum; + OSStatus err; + CFStringRef UIname; + + err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); + if (err != noErr) { + return err; + } + + deviceNum = size / sizeof(AudioDeviceID); + AudioDeviceID devices[deviceNum]; + + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); + if (err != noErr) { + return err; + } + + for (i = 0; i < deviceNum; i++) { + char device_name[256]; + char internal_name[256]; + + size = sizeof(CFStringRef); + UIname = NULL; + err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); + if (err == noErr) { + CFStringGetCString(UIname, internal_name, 256, CFStringGetSystemEncoding()); + } else { + goto error; + } + + size = 256; + err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name); + if (err != noErr) { + return err; + } + + jack_info("Device name = \'%s\', internal_name = \'%s\' (to be used as -C, -P, or -d parameter)", device_name, internal_name); + } + + return noErr; + +error: + if (UIname != NULL) { + CFRelease(UIname); + } + return err; +} + +static void printError(OSStatus err) +{ + switch (err) { + case kAudioHardwareNoError: + jack_log("error code : kAudioHardwareNoError"); + break; + case kAudioConverterErr_FormatNotSupported: + jack_log("error code : kAudioConverterErr_FormatNotSupported"); + break; + case kAudioConverterErr_OperationNotSupported: + jack_log("error code : kAudioConverterErr_OperationNotSupported"); + break; + case kAudioConverterErr_PropertyNotSupported: + jack_log("error code : kAudioConverterErr_PropertyNotSupported"); + break; + case kAudioConverterErr_InvalidInputSize: + jack_log("error code : kAudioConverterErr_InvalidInputSize"); + break; + case kAudioConverterErr_InvalidOutputSize: + jack_log("error code : kAudioConverterErr_InvalidOutputSize"); + break; + case kAudioConverterErr_UnspecifiedError: + jack_log("error code : kAudioConverterErr_UnspecifiedError"); + break; + case kAudioConverterErr_BadPropertySizeError: + jack_log("error code : kAudioConverterErr_BadPropertySizeError"); + break; + case kAudioConverterErr_RequiresPacketDescriptionsError: + jack_log("error code : kAudioConverterErr_RequiresPacketDescriptionsError"); + break; + case kAudioConverterErr_InputSampleRateOutOfRange: + jack_log("error code : kAudioConverterErr_InputSampleRateOutOfRange"); + break; + case kAudioConverterErr_OutputSampleRateOutOfRange: + jack_log("error code : kAudioConverterErr_OutputSampleRateOutOfRange"); + break; + case kAudioHardwareNotRunningError: + jack_log("error code : kAudioHardwareNotRunningError"); + break; + case kAudioHardwareUnknownPropertyError: + jack_log("error code : kAudioHardwareUnknownPropertyError"); + break; + case kAudioHardwareIllegalOperationError: + jack_log("error code : kAudioHardwareIllegalOperationError"); + break; + case kAudioHardwareBadDeviceError: + jack_log("error code : kAudioHardwareBadDeviceError"); + break; + case kAudioHardwareBadStreamError: + jack_log("error code : kAudioHardwareBadStreamError"); + break; + case kAudioDeviceUnsupportedFormatError: + jack_log("error code : kAudioDeviceUnsupportedFormatError"); + break; + case kAudioDevicePermissionsError: + jack_log("error code : kAudioDevicePermissionsError"); + break; + case kAudioHardwareBadObjectError: + jack_log("error code : kAudioHardwareBadObjectError"); + break; + case kAudioHardwareUnsupportedOperationError: + jack_log("error code : kAudioHardwareUnsupportedOperationError"); + break; + default: + jack_log("error code : unknown"); + break; + } +} + +OSStatus JackCoreAudioAdapter::AudioHardwareNotificationCallback(AudioHardwarePropertyID inPropertyID, void* inClientData) +{ + JackCoreAudioAdapter* driver = (JackCoreAudioAdapter*)inClientData; + + switch (inPropertyID) { + + case kAudioHardwarePropertyDevices: { + jack_log("JackCoreAudioAdapter::AudioHardwareNotificationCallback kAudioHardwarePropertyDevices"); + DisplayDeviceNames(); + break; + } + } + + return noErr; +} + +OSStatus JackCoreAudioAdapter::SRNotificationCallback(AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + void* inClientData) +{ + JackCoreAudioAdapter* driver = static_cast(inClientData); + + switch (inPropertyID) { + + case kAudioDevicePropertyNominalSampleRate: { + jack_log("JackCoreAudioAdapter::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); + driver->fState = true; + break; + } + } + + return noErr; +} + +// A better implementation would try to recover in case of hardware device change (see HALLAB HLFilePlayerWindowControllerAudioDevicePropertyListenerProc code) +OSStatus JackCoreAudioAdapter::DeviceNotificationCallback(AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + void* inClientData) +{ + + switch (inPropertyID) { + + case kAudioDeviceProcessorOverload: { + jack_error("JackCoreAudioAdapter::DeviceNotificationCallback kAudioDeviceProcessorOverload"); + break; + } + + case kAudioDevicePropertyStreamConfiguration: { + jack_error("Cannot handle kAudioDevicePropertyStreamConfiguration"); + return kAudioHardwareUnsupportedOperationError; + } + + case kAudioDevicePropertyNominalSampleRate: { + jack_error("Cannot handle kAudioDevicePropertyNominalSampleRate"); + return kAudioHardwareUnsupportedOperationError; + } + + } + return noErr; +} + +int JackCoreAudioAdapter::AddListeners() +{ + OSStatus err = noErr; + + // Add listeners + err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDeviceProcessorOverload"); + printError(err); + return -1; + } + + err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, AudioHardwareNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioHardwareAddPropertyListener with kAudioHardwarePropertyDevices"); + printError(err); + return -1; + } + + err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); + printError(err); + return -1; + } + + err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceIsRunning"); + printError(err); + return -1; + } + + err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration"); + printError(err); + return -1; + } + + err = AudioDeviceAddPropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration"); + printError(err); + return -1; + } + + return 0; +} + +void JackCoreAudioAdapter::RemoveListeners() +{ + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback); + AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, AudioHardwareNotificationCallback); + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback); + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback); + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback); + AudioDeviceRemovePropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback); +} + +OSStatus JackCoreAudioAdapter::Render(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) +{ + return static_cast(inRefCon)->Render(ioActionFlags, inTimeStamp, inNumberFrames, ioData); +} + +OSStatus JackCoreAudioAdapter::Render(AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inNumberFrames, + AudioBufferList *ioData) +{ + OSStatus err = AudioUnitRender(fAUHAL, ioActionFlags, inTimeStamp, 1, inNumberFrames, fInputData); + + if (err == noErr) { + jack_default_audio_sample_t* inputBuffer[fCaptureChannels]; + jack_default_audio_sample_t* outputBuffer[fPlaybackChannels]; + + for (int i = 0; i < fCaptureChannels; i++) { + inputBuffer[i] = (jack_default_audio_sample_t*)fInputData->mBuffers[i].mData; + } + for (int i = 0; i < fPlaybackChannels; i++) { + outputBuffer[i] = (jack_default_audio_sample_t*)ioData->mBuffers[i].mData; + } + + PushAndPull((jack_default_audio_sample_t**)inputBuffer, (jack_default_audio_sample_t**)outputBuffer, inNumberFrames); + return noErr; + } else { + return err; + } +} + +JackCoreAudioAdapter::JackCoreAudioAdapter(jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params) + :JackAudioAdapterInterface(buffer_size, sample_rate), fInputData(0), fCapturing(false), fPlaying(false), fState(false) +{ + const JSList* node; + const jack_driver_param_t* param; + int in_nChannels = 0; + int out_nChannels = 0; + char captureName[256]; + char playbackName[256]; + fCaptureUID[0] = 0; + fPlaybackUID[0] = 0; + fClockDriftCompensate = false; + + // Default values + fCaptureChannels = -1; + fPlaybackChannels = -1; + + SInt32 major; + SInt32 minor; + Gestalt(gestaltSystemVersionMajor, &major); + Gestalt(gestaltSystemVersionMinor, &minor); + + // Starting with 10.6 systems, the HAL notification thread is created internally + if (major == 10 && minor >= 6) { + CFRunLoopRef theRunLoop = NULL; + AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + OSStatus theError = AudioObjectSetPropertyData (kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); + if (theError != noErr) { + jack_error("JackCoreAudioAdapter::Open kAudioHardwarePropertyRunLoop error"); + } + } + + for (node = params; node; node = jack_slist_next(node)) { + param = (const jack_driver_param_t*) node->data; + + switch (param->character) { + + case 'i': + fCaptureChannels = param->value.ui; + break; + + case 'o': + fPlaybackChannels = param->value.ui; + break; + + case 'C': + fCapturing = true; + strncpy(fCaptureUID, param->value.str, 256); + break; + + case 'P': + fPlaying = true; + strncpy(fPlaybackUID, param->value.str, 256); + break; + + case 'd': + strncpy(fCaptureUID, param->value.str, 256); + strncpy(fPlaybackUID, param->value.str, 256); + break; + + case 'D': + fCapturing = fPlaying = true; + break; + + case 'r': + SetAdaptedSampleRate(param->value.ui); + break; + + case 'p': + SetAdaptedBufferSize(param->value.ui); + break; + + case 'l': + DisplayDeviceNames(); + break; + + case 'q': + fQuality = param->value.ui; + break; + + case 'g': + fRingbufferCurSize = param->value.ui; + fAdaptative = false; + break; + + case 's': + fClockDriftCompensate = true; + break; + } + } + + /* duplex is the default */ + if (!fCapturing && !fPlaying) { + fCapturing = true; + fPlaying = true; + } + + if (SetupDevices(fCaptureUID, fPlaybackUID, captureName, playbackName, fAdaptedSampleRate) < 0) { + throw std::bad_alloc(); + } + + if (SetupChannels(fCapturing, fPlaying, fCaptureChannels, fPlaybackChannels, in_nChannels, out_nChannels, true) < 0) { + throw std::bad_alloc(); + } + + if (SetupBufferSize(fAdaptedBufferSize) < 0) { + throw std::bad_alloc(); + } + + if (SetupSampleRate(fAdaptedSampleRate) < 0) { + throw std::bad_alloc(); + } + + if (OpenAUHAL(fCapturing, fPlaying, fCaptureChannels, fPlaybackChannels, in_nChannels, out_nChannels, fAdaptedBufferSize, fAdaptedSampleRate) < 0) { + throw std::bad_alloc(); + } + + if (fCapturing && fCaptureChannels > 0) { + if (SetupBuffers(fCaptureChannels) < 0) { + throw std::bad_alloc(); + } + } + + if (AddListeners() < 0) { + throw std::bad_alloc(); + } + + GetStreamLatencies(fDeviceID, true, fInputLatencies); + GetStreamLatencies(fDeviceID, false, fOutputLatencies); +} + +OSStatus JackCoreAudioAdapter::GetDefaultDevice(AudioDeviceID* id) +{ + OSStatus res; + UInt32 theSize = sizeof(UInt32); + AudioDeviceID inDefault; + AudioDeviceID outDefault; + + if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) { + return res; + } + + if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) { + return res; + } + + jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault); + + // Get the device only if default input and output are the same + if (inDefault != outDefault) { + jack_error("Default input and output devices are not the same !!"); + return kAudioHardwareBadDeviceError; + } else if (inDefault == 0) { + jack_error("Default input and output devices are null !!"); + return kAudioHardwareBadDeviceError; + } else { + *id = inDefault; + return noErr; + } +} + +OSStatus JackCoreAudioAdapter::GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput) +{ + OSStatus err = noErr; + UInt32 outSize; + Boolean outWritable; + + channelCount = 0; + err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable); + if (err == noErr) { + AudioBufferList bufferList[outSize]; + err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList); + if (err == noErr) { + for (unsigned int i = 0; i < bufferList->mNumberBuffers; i++) { + channelCount += bufferList->mBuffers[i].mNumberChannels; + } + } + } + + return err; +} + +OSStatus JackCoreAudioAdapter::GetDeviceIDFromUID(const char* UID, AudioDeviceID* id) +{ + UInt32 size = sizeof(AudioValueTranslation); + CFStringRef inIUD = CFStringCreateWithCString(NULL, UID, CFStringGetSystemEncoding()); + AudioValueTranslation value = { &inIUD, sizeof(CFStringRef), id, sizeof(AudioDeviceID) }; + + if (inIUD == NULL) { + return kAudioHardwareUnspecifiedError; + } else { + OSStatus res = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &value); + CFRelease(inIUD); + jack_log("GetDeviceIDFromUID %s %ld", UID, *id); + return (*id == kAudioDeviceUnknown) ? kAudioHardwareBadDeviceError : res; + } +} + +OSStatus JackCoreAudioAdapter::GetDefaultInputDevice(AudioDeviceID* id) +{ + OSStatus res; + UInt32 theSize = sizeof(UInt32); + AudioDeviceID inDefault; + + if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) { + return res; + } + + if (inDefault == 0) { + jack_error("Error: default input device is 0, please select a correct one !!"); + return -1; + } + jack_log("GetDefaultInputDevice: input = %ld ", inDefault); + *id = inDefault; + return noErr; +} + +OSStatus JackCoreAudioAdapter::GetDefaultOutputDevice(AudioDeviceID* id) +{ + OSStatus res; + UInt32 theSize = sizeof(UInt32); + AudioDeviceID outDefault; + + if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) { + return res; + } + + if (outDefault == 0) { + jack_error("Error: default output device is 0, please select a correct one !!"); + return -1; + } + jack_log("GetDefaultOutputDevice: output = %ld", outDefault); + *id = outDefault; + return noErr; +} + +OSStatus JackCoreAudioAdapter::GetDeviceNameFromID(AudioDeviceID id, char* name) +{ + UInt32 size = 256; + return AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceName, &size, name); +} + +AudioDeviceID JackCoreAudioAdapter::GetDeviceIDFromName(const char* name) +{ + UInt32 size; + Boolean isWritable; + int i, deviceNum; + + OSStatus err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); + if (err != noErr) { + return -1; + } + + deviceNum = size / sizeof(AudioDeviceID); + AudioDeviceID devices[deviceNum]; + + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); + if (err != noErr) { + return err; + } + + for (i = 0; i < deviceNum; i++) { + char device_name[256]; + size = 256; + err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name); + if (err != noErr) { + return -1; + } else if (strcmp(device_name, name) == 0) { + return devices[i]; + } + } + + return -1; +} + +// Setup +int JackCoreAudioAdapter::SetupDevices(const char* capture_driver_uid, + const char* playback_driver_uid, + char* capture_driver_name, + char* playback_driver_name, + jack_nframes_t samplerate) +{ + capture_driver_name[0] = 0; + playback_driver_name[0] = 0; + + // Duplex + if (strcmp(capture_driver_uid, "") != 0 && strcmp(playback_driver_uid, "") != 0) { + jack_log("JackCoreAudioDriver::Open duplex"); + + // Same device for capture and playback... + if (strcmp(capture_driver_uid, playback_driver_uid) == 0) { + + if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { + jack_log("Will take default in/out"); + if (GetDefaultDevice(&fDeviceID) != noErr) { + jack_error("Cannot open default device"); + return -1; + } + } + if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr || GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { + jack_error("Cannot get device name from device ID"); + return -1; + } + + } else { + + // Creates aggregate device + AudioDeviceID captureID, playbackID; + + if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { + jack_log("Will take default input"); + if (GetDefaultInputDevice(&captureID) != noErr) { + jack_error("Cannot open default input device"); + return -1; + } + } + + if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) { + jack_log("Will take default output"); + if (GetDefaultOutputDevice(&playbackID) != noErr) { + jack_error("Cannot open default output device"); + return -1; + } + } + + if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) { + return -1; + } + } + + // Capture only + } else if (strcmp(capture_driver_uid, "") != 0) { + jack_log("JackCoreAudioAdapter::Open capture only"); + if (GetDeviceIDFromUID(capture_driver_uid, &fDeviceID) != noErr) { + if (GetDefaultInputDevice(&fDeviceID) != noErr) { + jack_error("Cannot open default input device"); + return -1; + } + } + if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr) { + jack_error("Cannot get device name from device ID"); + return -1; + } + + // Playback only + } else if (strcmp(playback_driver_uid, "") != 0) { + jack_log("JackCoreAudioAdapter::Open playback only"); + if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { + if (GetDefaultOutputDevice(&fDeviceID) != noErr) { + jack_error("Cannot open default output device"); + return -1; + } + } + if (GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { + jack_error("Cannot get device name from device ID"); + return -1; + } + + // Use default driver in duplex mode + } else { + jack_log("JackCoreAudioAdapter::Open default driver"); + if (GetDefaultDevice(&fDeviceID) != noErr) { + jack_error("Cannot open default device in duplex mode, so aggregate default input and default output"); + + // Creates aggregate device + AudioDeviceID captureID = -1, playbackID = -1; + + if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { + jack_log("Will take default input"); + if (GetDefaultInputDevice(&captureID) != noErr) { + jack_error("Cannot open default input device"); + goto built_in; + } + } + + if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) { + jack_log("Will take default output"); + if (GetDefaultOutputDevice(&playbackID) != noErr) { + jack_error("Cannot open default output device"); + goto built_in; + } + } + + if (captureID > 0 && playbackID > 0) { + if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) { + goto built_in; + } + } else { + jack_error("Cannot use default input/output"); + goto built_in; + } + } + } + + return 0; + +built_in: + + // Aggregate built-in input and output + AudioDeviceID captureID = GetDeviceIDFromName("Built-in Input"); + AudioDeviceID playbackID = GetDeviceIDFromName("Built-in Output"); + + if (captureID > 0 && playbackID > 0) { + if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) { + return -1; + } + } else { + jack_error("Cannot aggregate built-in input and output"); + return -1; + } + + return 0; +} + +int JackCoreAudioAdapter::SetupChannels(bool capturing, + bool playing, + int& inchannels, + int& outchannels, + int& in_nChannels, + int& out_nChannels, + bool strict) +{ + OSStatus err = noErr; + + if (capturing) { + err = GetTotalChannels(fDeviceID, in_nChannels, true); + if (err != noErr) { + jack_error("Cannot get input channel number"); + printError(err); + return -1; + } else { + jack_log("Max input channels : %d", in_nChannels); + } + } + + if (playing) { + err = GetTotalChannels(fDeviceID, out_nChannels, false); + if (err != noErr) { + jack_error("Cannot get output channel number"); + printError(err); + return -1; + } else { + jack_log("Max output channels : %d", out_nChannels); + } + } + + if (inchannels > in_nChannels) { + jack_error("This device hasn't required input channels inchannels = %ld in_nChannels = %ld", inchannels, in_nChannels); + if (strict) { + return -1; + } + } + + if (outchannels > out_nChannels) { + jack_error("This device hasn't required output channels outchannels = %ld out_nChannels = %ld", outchannels, out_nChannels); + if (strict) { + return -1; + } + } + + if (inchannels == -1) { + jack_log("Setup max in channels = %ld", in_nChannels); + inchannels = in_nChannels; + } + + if (outchannels == -1) { + jack_log("Setup max out channels = %ld", out_nChannels); + outchannels = out_nChannels; + } + + return 0; +} + +int JackCoreAudioAdapter::SetupBufferSize(jack_nframes_t buffer_size) +{ + // Setting buffer size + UInt32 outSize = sizeof(UInt32); + OSStatus err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, outSize, &buffer_size); + if (err != noErr) { + jack_error("Cannot set buffer size %ld", buffer_size); + printError(err); + return -1; + } + + return 0; +} + +int JackCoreAudioAdapter::SetupSampleRate(jack_nframes_t samplerate) +{ + return SetupSampleRateAux(fDeviceID, samplerate); +} + +int JackCoreAudioAdapter::SetupSampleRateAux(AudioDeviceID inDevice, jack_nframes_t samplerate) +{ + OSStatus err = noErr; + UInt32 outSize; + Float64 sampleRate; + + // Get sample rate + outSize = sizeof(Float64); + err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &sampleRate); + if (err != noErr) { + jack_error("Cannot get current sample rate"); + printError(err); + return -1; + } else { + jack_log("Current sample rate = %f", sampleRate); + } + + // If needed, set new sample rate + if (samplerate != (jack_nframes_t)sampleRate) { + sampleRate = (Float64)samplerate; + + // To get SR change notification + err = AudioDeviceAddPropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); + printError(err); + return -1; + } + err = AudioDeviceSetProperty(inDevice, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outSize, &sampleRate); + if (err != noErr) { + jack_error("Cannot set sample rate = %ld", samplerate); + printError(err); + return -1; + } + + // Waiting for SR change notification + int count = 0; + while (!fState && count++ < WAIT_COUNTER) { + usleep(100000); + jack_log("Wait count = %d", count); + } + + // Remove SR change notification + AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback); + } + + return 0; +} + +int JackCoreAudioAdapter::SetupBuffers(int inchannels) +{ + jack_log("JackCoreAudioAdapter::SetupBuffers: input = %ld", inchannels); + + // Prepare buffers + fInputData = (AudioBufferList*)malloc(sizeof(UInt32) + inchannels * sizeof(AudioBuffer)); + fInputData->mNumberBuffers = inchannels; + for (int i = 0; i < fCaptureChannels; i++) { + fInputData->mBuffers[i].mNumberChannels = 1; + fInputData->mBuffers[i].mDataByteSize = fAdaptedBufferSize * sizeof(jack_default_audio_sample_t); + fInputData->mBuffers[i].mData = malloc(fAdaptedBufferSize * sizeof(jack_default_audio_sample_t)); + } + return 0; +} + +void JackCoreAudioAdapter::DisposeBuffers() +{ + if (fInputData) { + for (int i = 0; i < fCaptureChannels; i++) { + free(fInputData->mBuffers[i].mData); + } + free(fInputData); + fInputData = 0; + } +} + +int JackCoreAudioAdapter::OpenAUHAL(bool capturing, + bool playing, + int inchannels, + int outchannels, + int in_nChannels, + int out_nChannels, + jack_nframes_t buffer_size, + jack_nframes_t samplerate) +{ + ComponentResult err1; + UInt32 enableIO; + AudioStreamBasicDescription srcFormat, dstFormat; + AudioDeviceID currAudioDeviceID; + UInt32 size; + + jack_log("OpenAUHAL capturing = %d playing = %d inchannels = %d outchannels = %d in_nChannels = %d out_nChannels = %d", capturing, playing, inchannels, outchannels, in_nChannels, out_nChannels); + + if (inchannels == 0 && outchannels == 0) { + jack_error("No input and output channels..."); + return -1; + } + + // AUHAL +#ifdef MAC_OS_X_VERSION_10_5 + ComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0}; + Component HALOutput = FindNextComponent(NULL, &cd); + err1 = OpenAComponent(HALOutput, &fAUHAL); + if (err1 != noErr) { + jack_error("Error calling OpenAComponent"); + printError(err1); + goto error; + } +#else + AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0}; + AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd); + err1 = AudioComponentInstanceNew(HALOutput, &fAUHAL); + if (err1 != noErr) { + jack_error("Error calling AudioComponentInstanceNew"); + printError(err1); + goto error; + } +#endif + + err1 = AudioUnitInitialize(fAUHAL); + if (err1 != noErr) { + jack_error("Cannot initialize AUHAL unit"); + printError(err1); + goto error; + } + + // Start I/O + if (capturing && inchannels > 0) { + enableIO = 1; + jack_log("Setup AUHAL input on"); + } else { + enableIO = 0; + jack_log("Setup AUHAL input off"); + } + + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input"); + printError(err1); + goto error; + } + + if (playing && outchannels > 0) { + enableIO = 1; + jack_log("Setup AUHAL output on"); + } else { + enableIO = 0; + jack_log("Setup AUHAL output off"); + } + + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Output"); + printError(err1); + goto error; + } + + size = sizeof(AudioDeviceID); + err1 = AudioUnitGetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &currAudioDeviceID, &size); + if (err1 != noErr) { + jack_error("Error calling AudioUnitGetProperty - kAudioOutputUnitProperty_CurrentDevice"); + printError(err1); + goto error; + } else { + jack_log("AudioUnitGetPropertyCurrentDevice = %d", currAudioDeviceID); + } + + // Setup up choosen device, in both input and output cases + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &fDeviceID, sizeof(AudioDeviceID)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_CurrentDevice"); + printError(err1); + goto error; + } + + // Set buffer size + if (capturing && inchannels > 0) { + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&buffer_size, sizeof(UInt32)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); + printError(err1); + goto error; + } + } + + if (playing && outchannels > 0) { + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&buffer_size, sizeof(UInt32)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); + printError(err1); + goto error; + } + } + + // Setup channel map + if (capturing && inchannels > 0 && inchannels <= in_nChannels) { + SInt32 chanArr[in_nChannels]; + for (int i = 0; i < in_nChannels; i++) { + chanArr[i] = -1; + } + for (int i = 0; i < inchannels; i++) { + chanArr[i] = i; + } + AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap , kAudioUnitScope_Input, 1, chanArr, sizeof(SInt32) * in_nChannels); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 1"); + printError(err1); + goto error; + } + } + + if (playing && outchannels > 0 && outchannels <= out_nChannels) { + SInt32 chanArr[out_nChannels]; + for (int i = 0; i < out_nChannels; i++) { + chanArr[i] = -1; + } + for (int i = 0; i < outchannels; i++) { + chanArr[i] = i; + } + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 0, chanArr, sizeof(SInt32) * out_nChannels); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap 0"); + printError(err1); + goto error; + } + } + + // Setup stream converters + if (capturing && inchannels > 0) { + + size = sizeof(AudioStreamBasicDescription); + err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &srcFormat, &size); + if (err1 != noErr) { + jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); + printError(err1); + goto error; + } + PrintStreamDesc(&srcFormat); + + jack_log("Setup AUHAL input stream converter SR = %ld", samplerate); + srcFormat.mSampleRate = samplerate; + srcFormat.mFormatID = kAudioFormatLinearPCM; + srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; + srcFormat.mBytesPerPacket = sizeof(jack_default_audio_sample_t); + srcFormat.mFramesPerPacket = 1; + srcFormat.mBytesPerFrame = sizeof(jack_default_audio_sample_t); + srcFormat.mChannelsPerFrame = inchannels; + srcFormat.mBitsPerChannel = 32; + PrintStreamDesc(&srcFormat); + + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription)); + + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); + printError(err1); + goto error; + } + } + + if (playing && outchannels > 0) { + + size = sizeof(AudioStreamBasicDescription); + err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &dstFormat, &size); + if (err1 != noErr) { + jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); + printError(err1); + goto error; + } + PrintStreamDesc(&dstFormat); + + jack_log("Setup AUHAL output stream converter SR = %ld", samplerate); + dstFormat.mSampleRate = samplerate; + dstFormat.mFormatID = kAudioFormatLinearPCM; + dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; + dstFormat.mBytesPerPacket = sizeof(jack_default_audio_sample_t); + dstFormat.mFramesPerPacket = 1; + dstFormat.mBytesPerFrame = sizeof(jack_default_audio_sample_t); + dstFormat.mChannelsPerFrame = outchannels; + dstFormat.mBitsPerChannel = 32; + PrintStreamDesc(&dstFormat); + + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription)); + + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); + printError(err1); + goto error; + } + } + + // Setup callbacks + if (inchannels > 0 && outchannels == 0) { + AURenderCallbackStruct output; + output.inputProc = Render; + output.inputProcRefCon = this; + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1"); + printError(err1); + goto error; + } + } else { + AURenderCallbackStruct output; + output.inputProc = Render; + output.inputProcRefCon = this; + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0"); + printError(err1); + goto error; + } + } + + return 0; + +error: + CloseAUHAL(); + return -1; +} + +OSStatus JackCoreAudioAdapter::DestroyAggregateDevice() +{ + OSStatus osErr = noErr; + AudioObjectPropertyAddress pluginAOPA; + pluginAOPA.mSelector = kAudioPlugInDestroyAggregateDevice; + pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; + pluginAOPA.mElement = kAudioObjectPropertyElementMaster; + UInt32 outDataSize; + + osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); + if (osErr != noErr) { + jack_error("JackCoreAudioAdapter::DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); + printError(osErr); + return osErr; + } + + osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID); + if (osErr != noErr) { + jack_error("JackCoreAudioAdapter::DestroyAggregateDevice : AudioObjectGetPropertyData error"); + printError(osErr); + return osErr; + } + + return noErr; +} + +static CFStringRef GetDeviceName(AudioDeviceID id) +{ + UInt32 size = sizeof(CFStringRef); + CFStringRef UIname; + OSStatus err = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); + return (err == noErr) ? UIname : NULL; +} + +OSStatus JackCoreAudioAdapter::CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) +{ + OSStatus err = noErr; + AudioObjectID sub_device[32]; + UInt32 outSize = sizeof(sub_device); + + err = AudioDeviceGetProperty(captureDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); + vector captureDeviceIDArray; + + if (err != noErr) { + jack_log("Input device does not have subdevices"); + captureDeviceIDArray.push_back(captureDeviceID); + } else { + int num_devices = outSize / sizeof(AudioObjectID); + jack_log("Input device has %d subdevices", num_devices); + for (int i = 0; i < num_devices; i++) { + captureDeviceIDArray.push_back(sub_device[i]); + } + } + + outSize = sizeof(sub_device); + err = AudioDeviceGetProperty(playbackDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); + vector playbackDeviceIDArray; + + if (err != noErr) { + jack_log("Output device does not have subdevices"); + playbackDeviceIDArray.push_back(playbackDeviceID); + } else { + int num_devices = outSize / sizeof(AudioObjectID); + jack_log("Output device has %d subdevices", num_devices); + for (int i = 0; i < num_devices; i++) { + playbackDeviceIDArray.push_back(sub_device[i]); + } + } + + return CreateAggregateDeviceAux(captureDeviceIDArray, playbackDeviceIDArray, samplerate, outAggregateDevice); +} + +OSStatus JackCoreAudioAdapter::CreateAggregateDeviceAux(vector captureDeviceID, vector playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) +{ + OSStatus osErr = noErr; + UInt32 outSize; + Boolean outWritable; + + // Prepare sub-devices for clock drift compensation + // Workaround for bug in the HAL : until 10.6.2 + AudioObjectPropertyAddress theAddressOwned = { kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress theAddressDrift = { kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + UInt32 theQualifierDataSize = sizeof(AudioObjectID); + AudioClassID inClass = kAudioSubDeviceClassID; + void* theQualifierData = &inClass; + UInt32 subDevicesNum = 0; + + //--------------------------------------------------------------------------- + // Setup SR of both devices otherwise creating AD may fail... + //--------------------------------------------------------------------------- + UInt32 keptclockdomain = 0; + UInt32 clockdomain = 0; + outSize = sizeof(UInt32); + bool need_clock_drift_compensation = false; + + for (UInt32 i = 0; i < captureDeviceID.size(); i++) { + if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : cannot set SR of input device"); + } else { + // Check clock domain + osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); + if (osErr != 0) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); + printError(osErr); + } else { + keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; + jack_log("JackCoreAudioAdapter::CreateAggregateDevice : input clockdomain = %d", clockdomain); + if (clockdomain != 0 && clockdomain != keptclockdomain) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); + need_clock_drift_compensation = true; + } + } + } + } + + for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { + if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : cannot set SR of output device"); + } else { + // Check clock domain + osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); + if (osErr != 0) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : kAudioDevicePropertyClockDomain error"); + printError(osErr); + } else { + keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; + jack_log("JackCoreAudioAdapter::CreateAggregateDevice : output clockdomain = %d", clockdomain); + if (clockdomain != 0 && clockdomain != keptclockdomain) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : devices do not share the same clock!! clock drift compensation would be needed..."); + need_clock_drift_compensation = true; + } + } + } + } + + // If no valid clock domain was found, then assume we have to compensate... + if (keptclockdomain == 0) { + need_clock_drift_compensation = true; + } + + //--------------------------------------------------------------------------- + // Start to create a new aggregate by getting the base audio hardware plugin + //--------------------------------------------------------------------------- + + char device_name[256]; + for (UInt32 i = 0; i < captureDeviceID.size(); i++) { + GetDeviceNameFromID(captureDeviceID[i], device_name); + jack_info("Separated input = '%s' ", device_name); + } + + for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { + GetDeviceNameFromID(playbackDeviceID[i], device_name); + jack_info("Separated output = '%s' ", device_name); + } + + osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); + if (osErr != noErr) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); + printError(osErr); + return osErr; + } + + AudioValueTranslation pluginAVT; + + CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio"); + + pluginAVT.mInputData = &inBundleRef; + pluginAVT.mInputDataSize = sizeof(inBundleRef); + pluginAVT.mOutputData = &fPluginID; + pluginAVT.mOutputDataSize = sizeof(fPluginID); + + osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); + if (osErr != noErr) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); + printError(osErr); + return osErr; + } + + //------------------------------------------------- + // Create a CFDictionary for our aggregate device + //------------------------------------------------- + + CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFStringRef AggregateDeviceNameRef = CFSTR("JackDuplex"); + CFStringRef AggregateDeviceUIDRef = CFSTR("com.grame.JackDuplex"); + + // add the name of the device to the dictionary + CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef); + + // add our choice of UID for the aggregate device to the dictionary + CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef); + + // add a "private aggregate key" to the dictionary + int value = 1; + CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value); + + SInt32 system; + Gestalt(gestaltSystemVersion, &system); + + jack_log("JackCoreAudioAdapter::CreateAggregateDevice : system version = %x limit = %x", system, 0x00001054); + + // Starting with 10.5.4 systems, the AD can be internal... (better) + if (system < 0x00001054) { + jack_log("JackCoreAudioAdapter::CreateAggregateDevice : public aggregate device...."); + } else { + jack_log("JackCoreAudioAdapter::CreateAggregateDevice : private aggregate device...."); + CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); + } + + // Prepare sub-devices for clock drift compensation + CFMutableArrayRef subDevicesArrayClock = NULL; + + /* + if (fClockDriftCompensate) { + if (need_clock_drift_compensation) { + jack_info("Clock drift compensation activated..."); + subDevicesArrayClock = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + for (UInt32 i = 0; i < captureDeviceID.size(); i++) { + CFStringRef UID = GetDeviceName(captureDeviceID[i]); + if (UID) { + CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID); + CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef); + //CFRelease(UID); + CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict); + } + } + + for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { + CFStringRef UID = GetDeviceName(playbackDeviceID[i]); + if (UID) { + CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID); + CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef); + //CFRelease(UID); + CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict); + } + } + + // add sub-device clock array for the aggregate device to the dictionary + CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock); + } else { + jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)"); + } + } + */ + + //------------------------------------------------- + // Create a CFMutableArray for our sub-device list + //------------------------------------------------- + + // we need to append the UID for each device to a CFMutableArray, so create one here + CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + vector captureDeviceUID; + for (UInt32 i = 0; i < captureDeviceID.size(); i++) { + CFStringRef ref = GetDeviceName(captureDeviceID[i]); + if (ref == NULL) { + return -1; + } + captureDeviceUID.push_back(ref); + // input sub-devices in this example, so append the sub-device's UID to the CFArray + CFArrayAppendValue(subDevicesArray, ref); + } + + vector playbackDeviceUID; + for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { + CFStringRef ref = GetDeviceName(playbackDeviceID[i]); + if (ref == NULL) { + return -1; + } + playbackDeviceUID.push_back(ref); + // output sub-devices in this example, so append the sub-device's UID to the CFArray + CFArrayAppendValue(subDevicesArray, ref); + } + + //----------------------------------------------------------------------- + // Feed the dictionary to the plugin, to create a blank aggregate device + //----------------------------------------------------------------------- + + AudioObjectPropertyAddress pluginAOPA; + pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice; + pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; + pluginAOPA.mElement = kAudioObjectPropertyElementMaster; + UInt32 outDataSize; + + osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); + if (osErr != noErr) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectGetPropertyDataSize error"); + printError(osErr); + goto error; + } + + osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); + if (osErr != noErr) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectGetPropertyData error"); + printError(osErr); + goto error; + } + + // pause for a bit to make sure that everything completed correctly + // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); + + //------------------------- + // Set the sub-device list + //------------------------- + + pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList; + pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; + pluginAOPA.mElement = kAudioObjectPropertyElementMaster; + outDataSize = sizeof(CFMutableArrayRef); + osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); + if (osErr != noErr) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectSetPropertyData for sub-device list error"); + printError(osErr); + goto error; + } + + // pause again to give the changes time to take effect + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); + + //----------------------- + // Set the master device + //----------------------- + + // set the master device manually (this is the device which will act as the master clock for the aggregate device) + // pass in the UID of the device you want to use + pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice; + pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; + pluginAOPA.mElement = kAudioObjectPropertyElementMaster; + outDataSize = sizeof(CFStringRef); + osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &playbackDeviceUID[0]); // First playback is master... + if (osErr != noErr) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice : AudioObjectSetPropertyData for master device error"); + printError(osErr); + goto error; + } + + // pause again to give the changes time to take effect + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); + + // Prepare sub-devices for clock drift compensation + // Workaround for bug in the HAL : until 10.6.2 + + if (fClockDriftCompensate) { + if (need_clock_drift_compensation) { + jack_info("Clock drift compensation activated..."); + + // Get the property data size + osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize); + if (osErr != noErr) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); + printError(osErr); + } + + // Calculate the number of object IDs + subDevicesNum = outSize / sizeof(AudioObjectID); + jack_info("JackCoreAudioAdapter::CreateAggregateDevice clock drift compensation, number of sub-devices = %d", subDevicesNum); + AudioObjectID subDevices[subDevicesNum]; + outSize = sizeof(subDevices); + + osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices); + if (osErr != noErr) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioObjectPropertyOwnedObjects error"); + printError(osErr); + } + + // Set kAudioSubDevicePropertyDriftCompensation property... + for (UInt32 index = 0; index < subDevicesNum; ++index) { + UInt32 theDriftCompensationValue = 1; + osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue); + if (osErr != noErr) { + jack_error("JackCoreAudioAdapter::CreateAggregateDevice kAudioSubDevicePropertyDriftCompensation error"); + printError(osErr); + } + } + } else { + jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)"); + } + } + + // pause again to give the changes time to take effect + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); + + //---------- + // Clean up + //---------- + + // release the private AD key + CFRelease(AggregateDeviceNumberRef); + + // release the CF objects we have created - we don't need them any more + CFRelease(aggDeviceDict); + CFRelease(subDevicesArray); + + if (subDevicesArrayClock) { + CFRelease(subDevicesArrayClock); + } + + // release the device UID + for (UInt32 i = 0; i < captureDeviceUID.size(); i++) { + CFRelease(captureDeviceUID[i]); + } + + for (UInt32 i = 0; i < playbackDeviceUID.size(); i++) { + CFRelease(playbackDeviceUID[i]); + } + + jack_log("New aggregate device %ld", *outAggregateDevice); + return noErr; + +error: + DestroyAggregateDevice(); + return -1; +} + + +bool JackCoreAudioAdapter::IsAggregateDevice(AudioDeviceID device) +{ + OSStatus err = noErr; + AudioObjectID sub_device[32]; + UInt32 outSize = sizeof(sub_device); + err = AudioDeviceGetProperty(device, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); + + if (err != noErr) { + jack_log("Device does not have subdevices"); + return false; + } else { + int num_devices = outSize / sizeof(AudioObjectID); + jack_log("Device does has %d subdevices", num_devices); + return true; + } +} + +void JackCoreAudioAdapter::CloseAUHAL() +{ + AudioUnitUninitialize(fAUHAL); + CloseComponent(fAUHAL); +} + +int JackCoreAudioAdapter::Open() +{ + return (AudioOutputUnitStart(fAUHAL) != noErr) ? -1 : 0; +} + +int JackCoreAudioAdapter::Close() +{ +#ifdef JACK_MONITOR + fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize); +#endif + AudioOutputUnitStop(fAUHAL); + DisposeBuffers(); + CloseAUHAL(); + RemoveListeners(); + if (fPluginID > 0) { + DestroyAggregateDevice(); + } + return 0; +} + +int JackCoreAudioAdapter::SetSampleRate(jack_nframes_t sample_rate) +{ + JackAudioAdapterInterface::SetHostSampleRate(sample_rate); + Close(); + return Open(); +} + +int JackCoreAudioAdapter::SetBufferSize(jack_nframes_t buffer_size) +{ + JackAudioAdapterInterface::SetHostBufferSize(buffer_size); + Close(); + return Open(); +} + +OSStatus JackCoreAudioAdapter::GetStreamLatencies(AudioDeviceID device, bool isInput, vector& latencies) +{ + OSStatus err = noErr; + UInt32 outSize1, outSize2, outSize3; + Boolean outWritable; + + err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, &outWritable); + if (err == noErr) { + int stream_count = outSize1 / sizeof(UInt32); + AudioStreamID streamIDs[stream_count]; + AudioBufferList bufferList[stream_count]; + UInt32 streamLatency; + outSize2 = sizeof(UInt32); + + err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, streamIDs); + if (err != noErr) { + jack_error("GetStreamLatencies kAudioDevicePropertyStreams err = %d", err); + return err; + } + + err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, &outWritable); + if (err != noErr) { + jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err); + return err; + } + + for (int i = 0; i < stream_count; i++) { + err = AudioStreamGetProperty(streamIDs[i], 0, kAudioStreamPropertyLatency, &outSize2, &streamLatency); + if (err != noErr) { + jack_error("GetStreamLatencies kAudioStreamPropertyLatency err = %d", err); + return err; + } + err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, bufferList); + if (err != noErr) { + jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err); + return err; + } + // Push 'channel' time the stream latency + for (uint k = 0; k < bufferList->mBuffers[i].mNumberChannels; k++) { + latencies.push_back(streamLatency); + } + } + } + return err; +} + +int JackCoreAudioAdapter::GetLatency(int port_index, bool input) +{ + UInt32 size = sizeof(UInt32); + UInt32 value1 = 0; + UInt32 value2 = 0; + + OSStatus err = AudioDeviceGetProperty(fDeviceID, 0, input, kAudioDevicePropertyLatency, &size, &value1); + if (err != noErr) { + jack_log("AudioDeviceGetProperty kAudioDevicePropertyLatency error"); + } + err = AudioDeviceGetProperty(fDeviceID, 0, input, kAudioDevicePropertySafetyOffset, &size, &value2); + if (err != noErr) { + jack_log("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error"); + } + + // TODO : add stream latency + + return value1 + value2 + fAdaptedBufferSize; +} + +int JackCoreAudioAdapter::GetInputLatency(int port_index) +{ + if (port_index < int(fInputLatencies.size())) { + return GetLatency(port_index, true) + fInputLatencies[port_index]; + } else { + // No stream latency + return GetLatency(port_index, true); + } +} + +int JackCoreAudioAdapter::GetOutputLatency(int port_index) +{ + if (port_index < int(fOutputLatencies.size())) { + return GetLatency(port_index, false) + fOutputLatencies[port_index]; + } else { + // No stream latency + return GetLatency(port_index, false); + } +} + +} // namespace + +#ifdef __cplusplus +extern "C" +{ +#endif + + SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor() + { + jack_driver_desc_t * desc; + jack_driver_desc_filler_t filler; + jack_driver_param_value_t value; + + desc = jack_driver_descriptor_construct("audioadapter", JackDriverNone, "netjack audio <==> net backend adapter", &filler); + + value.i = -1; + jack_driver_descriptor_add_parameter(desc, &filler, "in-channels", 'i', JackDriverParamInt, &value, NULL, "Maximum number of input channels", "Maximum number of input channels. If -1, max possible number of input channels will be used"); + jack_driver_descriptor_add_parameter(desc, &filler, "out-channels", 'o', JackDriverParamInt, &value, NULL, "Maximum number of output channels", "Maximum number of output channels. If -1, max possible number of output channels will be used"); + + value.str[0] = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Input CoreAudio device name", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamString, &value, NULL, "Output CoreAudio device name", NULL); + + value.ui = 44100U; + jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL); + + value.ui = 512U; + jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL); + + value.i = true; + jack_driver_descriptor_add_parameter(desc, &filler, "duplex", 'D', JackDriverParamBool, &value, NULL, "Provide both capture and playback ports", NULL); + + value.str[0] = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "CoreAudio device name", NULL); + + value.i = true; + jack_driver_descriptor_add_parameter(desc, &filler, "list-devices", 'l', JackDriverParamBool, &value, NULL, "Display available CoreAudio devices", NULL); + + value.ui = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "quality", 'q', JackDriverParamInt, &value, NULL, "Resample algorithm quality (0 - 4)", NULL); + + value.ui = 32768; + jack_driver_descriptor_add_parameter(desc, &filler, "ring-buffer", 'g', JackDriverParamInt, &value, NULL, "Fixed ringbuffer size", "Fixed ringbuffer size (if not set => automatic adaptative)"); + + value.i = false; + jack_driver_descriptor_add_parameter(desc, &filler, "clock-drift", 's', JackDriverParamBool, &value, NULL, "Clock drift compensation", "Whether to compensate clock drift in dynamically created aggregate device"); + + value.i = false; + jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect audioadapter to system ports", NULL); + + return desc; + } + + +#ifdef __cplusplus +} +#endif + diff -Nru jackd2-1.9.11~20161209/macosx/coreaudio/JackCoreAudioDriver.cpp jackd2-1.9.11+git20170722/macosx/coreaudio/JackCoreAudioDriver.cpp --- jackd2-1.9.11~20161209/macosx/coreaudio/JackCoreAudioDriver.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coreaudio/JackCoreAudioDriver.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,2723 +0,0 @@ -/* -Copyright (C) 2004-2008 Grame - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "JackCoreAudioDriver.h" -#include "JackEngineControl.h" -#include "JackMachThread.h" -#include "JackGraphManager.h" -#include "JackError.h" -#include "JackClientControl.h" -#include "JackDriverLoader.h" -#include "JackGlobals.h" -#include "JackTools.h" -#include "JackLockedEngine.h" -#include "JackAC3Encoder.h" - -#include -#include -#include -#include - -namespace Jack -{ - -static void Print4CharCode(const char* msg, long c) -{ - UInt32 __4CC_number = (c); - char __4CC_string[5]; - *((SInt32*)__4CC_string) = EndianU32_NtoB(__4CC_number); - __4CC_string[4] = 0; - jack_log("%s'%s'", (msg), __4CC_string); -} - -static void PrintStreamDesc(AudioStreamBasicDescription *inDesc) -{ - jack_log("- - - - - - - - - - - - - - - - - - - -"); - jack_log(" Sample Rate:%f", inDesc->mSampleRate); - jack_log(" Format ID:%.*s", (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID); - jack_log(" Format Flags:%lX", inDesc->mFormatFlags); - jack_log(" Bytes per Packet:%ld", inDesc->mBytesPerPacket); - jack_log(" Frames per Packet:%ld", inDesc->mFramesPerPacket); - jack_log(" Bytes per Frame:%ld", inDesc->mBytesPerFrame); - jack_log(" Channels per Frame:%ld", inDesc->mChannelsPerFrame); - jack_log(" Bits per Channel:%ld", inDesc->mBitsPerChannel); - jack_log("- - - - - - - - - - - - - - - - - - - -"); -} - -static void printError(OSStatus err) -{ - switch (err) { - case kAudioHardwareNoError: - jack_log("error code : kAudioHardwareNoError"); - break; - case kAudioConverterErr_FormatNotSupported: - jack_log("error code : kAudioConverterErr_FormatNotSupported"); - break; - case kAudioConverterErr_OperationNotSupported: - jack_log("error code : kAudioConverterErr_OperationNotSupported"); - break; - case kAudioConverterErr_PropertyNotSupported: - jack_log("error code : kAudioConverterErr_PropertyNotSupported"); - break; - case kAudioConverterErr_InvalidInputSize: - jack_log("error code : kAudioConverterErr_InvalidInputSize"); - break; - case kAudioConverterErr_InvalidOutputSize: - jack_log("error code : kAudioConverterErr_InvalidOutputSize"); - break; - case kAudioConverterErr_UnspecifiedError: - jack_log("error code : kAudioConverterErr_UnspecifiedError"); - break; - case kAudioConverterErr_BadPropertySizeError: - jack_log("error code : kAudioConverterErr_BadPropertySizeError"); - break; - case kAudioConverterErr_RequiresPacketDescriptionsError: - jack_log("error code : kAudioConverterErr_RequiresPacketDescriptionsError"); - break; - case kAudioConverterErr_InputSampleRateOutOfRange: - jack_log("error code : kAudioConverterErr_InputSampleRateOutOfRange"); - break; - case kAudioConverterErr_OutputSampleRateOutOfRange: - jack_log("error code : kAudioConverterErr_OutputSampleRateOutOfRange"); - break; - case kAudioHardwareNotRunningError: - jack_log("error code : kAudioHardwareNotRunningError"); - break; - case kAudioHardwareUnknownPropertyError: - jack_log("error code : kAudioHardwareUnknownPropertyError"); - break; - case kAudioHardwareIllegalOperationError: - jack_log("error code : kAudioHardwareIllegalOperationError"); - break; - case kAudioHardwareBadDeviceError: - jack_log("error code : kAudioHardwareBadDeviceError"); - break; - case kAudioHardwareBadStreamError: - jack_log("error code : kAudioHardwareBadStreamError"); - break; - case kAudioDeviceUnsupportedFormatError: - jack_log("error code : kAudioDeviceUnsupportedFormatError"); - break; - case kAudioDevicePermissionsError: - jack_log("error code : kAudioDevicePermissionsError"); - break; - case kAudioHardwareBadObjectError: - jack_log("error code : kAudioHardwareBadObjectError"); - break; - case kAudioHardwareUnsupportedOperationError: - jack_log("error code : kAudioHardwareUnsupportedOperationError"); - break; - default: - Print4CharCode("error code : unknown ", err); - break; - } -} - -static bool CheckAvailableDeviceName(const char* device_name, AudioDeviceID* device_id) -{ - UInt32 size; - Boolean isWritable; - int i, deviceNum; - OSStatus err; - - err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); - if (err != noErr) { - return false; - } - - deviceNum = size / sizeof(AudioDeviceID); - AudioDeviceID devices[deviceNum]; - - err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); - if (err != noErr) { - return false; - } - - for (i = 0; i < deviceNum; i++) { - char device_name_aux[256]; - - size = 256; - err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name_aux); - if (err != noErr) { - return false; - } - - if (strncmp(device_name_aux, device_name, strlen(device_name)) == 0) { - *device_id = devices[i]; - return true; - } - } - - return false; -} - -static bool CheckAvailableDevice(AudioDeviceID device_id) -{ - UInt32 size; - Boolean isWritable; - int i, deviceNum; - OSStatus err; - - err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); - if (err != noErr) { - return false; - } - - deviceNum = size / sizeof(AudioDeviceID); - AudioDeviceID devices[deviceNum]; - - err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); - if (err != noErr) { - return false; - } - - for (i = 0; i < deviceNum; i++) { - if (device_id == devices[i]) { - return true; - } - } - - return false; -} - -static OSStatus DisplayDeviceNames() -{ - UInt32 size; - Boolean isWritable; - int i, deviceNum; - OSStatus err; - CFStringRef UIname; - - err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); - if (err != noErr) { - return err; - } - - deviceNum = size / sizeof(AudioDeviceID); - AudioDeviceID devices[deviceNum]; - - err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); - if (err != noErr) { - return err; - } - - for (i = 0; i < deviceNum; i++) { - char device_name[256]; - char internal_name[256]; - - size = sizeof(CFStringRef); - UIname = NULL; - err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); - if (err == noErr) { - CFStringGetCString(UIname, internal_name, 256, CFStringGetSystemEncoding()); - } else { - goto error; - } - - size = 256; - err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name); - if (err != noErr) { - return err; - } - - jack_info("Device ID = \'%d\' name = \'%s\', internal name = \'%s\' (to be used as -C, -P, or -d parameter)", devices[i], device_name, internal_name); - } - - return noErr; - -error: - if (UIname != NULL) { - CFRelease(UIname); - } - return err; -} - -static CFStringRef GetDeviceName(AudioDeviceID id) -{ - UInt32 size = sizeof(CFStringRef); - CFStringRef UIname; - OSStatus err = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); - return (err == noErr) ? UIname : NULL; -} - -static void ParseChannelList(const string& list, vector& result, int max_chan) -{ - stringstream ss(list); - string token; - int chan; - - while (ss >> token) { - istringstream ins; - ins.str(token); - ins >> chan; - if (chan < 0 || chan >= max_chan) { - jack_error("Ignore incorrect channel mapping value = %d", chan); - } else { - result.push_back(chan); - } - } -} - -OSStatus JackCoreAudioDriver::Render(void* inRefCon, - AudioUnitRenderActionFlags* ioActionFlags, - const AudioTimeStamp* inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList* ioData) -{ - return static_cast(inRefCon)->Render(ioActionFlags, inTimeStamp, ioData); -} - -OSStatus JackCoreAudioDriver::Render(AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, AudioBufferList* ioData) -{ - fActionFags = ioActionFlags; - fCurrentTime = inTimeStamp; - fDriverOutputData = ioData; - - // Setup threaded based log function et get RT thread parameters once... - if (set_threaded_log_function()) { - - jack_log("JackCoreAudioDriver::Render : set_threaded_log_function"); - JackMachThread::GetParams(pthread_self(), &fEngineControl->fPeriod, &fEngineControl->fComputation, &fEngineControl->fConstraint); - - if (fComputationGrain > 0) { - jack_log("JackCoreAudioDriver::Render : RT thread computation setup to %d percent of period", int(fComputationGrain * 100)); - fEngineControl->fComputation = fEngineControl->fPeriod * fComputationGrain; - } - } - - // Signal waiting start function... - fState = true; - - CycleTakeBeginTime(); - - if (Process() < 0) { - jack_error("Process error, stopping driver"); - NotifyFailure(JackFailure | JackBackendError, "Process error, stopping driver"); // Message length limited to JACK_MESSAGE_SIZE - Stop(); - kill(JackTools::GetPID(), SIGINT); - return kAudioHardwareUnsupportedOperationError; - } else { - return noErr; - } -} - -int JackCoreAudioDriver::Read() -{ - if (fCaptureChannels > 0) { // Calling AudioUnitRender with no input returns a '????' error (callback setting issue ??), so hack to avoid it here... - return (AudioUnitRender(fAUHAL, fActionFags, fCurrentTime, 1, fEngineControl->fBufferSize, fJackInputData) == noErr) ? 0 : -1; - } else { - return 0; - } -} - -int JackCoreAudioDriver::Write() -{ - int size = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize; - - if (fAC3Encoder) { - - // AC3 encoding and SPDIF write - jack_default_audio_sample_t* AC3_inputs[MAX_AC3_CHANNELS]; - jack_default_audio_sample_t* AC3_outputs[2]; - for (int i = 0; i < fPlaybackChannels; i++) { - AC3_inputs[i] = GetOutputBuffer(i); - // If not connected, clear the buffer - if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) == 0) { - memset(AC3_inputs[i], 0, size); - } - } - AC3_outputs[0] = (jack_default_audio_sample_t*)fDriverOutputData->mBuffers[0].mData; - AC3_outputs[1] = (jack_default_audio_sample_t*)fDriverOutputData->mBuffers[1].mData; - fAC3Encoder->Process(AC3_inputs, AC3_outputs, fEngineControl->fBufferSize); - - } else { - - // Standard write - for (int i = 0; i < fPlaybackChannels; i++) { - if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) { - jack_default_audio_sample_t* buffer = GetOutputBuffer(i); - memcpy((jack_default_audio_sample_t*)fDriverOutputData->mBuffers[i].mData, buffer, size); - // Monitor ports - if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[i]) > 0) { - memcpy(GetMonitorBuffer(i), buffer, size); - } - } else { - memset((jack_default_audio_sample_t*)fDriverOutputData->mBuffers[i].mData, 0, size); - } - } - } - return 0; -} - -OSStatus JackCoreAudioDriver::SRNotificationCallback(AudioDeviceID inDevice, - UInt32 inChannel, - Boolean isInput, - AudioDevicePropertyID inPropertyID, - void* inClientData) -{ - JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData; - - switch (inPropertyID) { - - case kAudioDevicePropertyNominalSampleRate: { - jack_log("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); - // Check new sample rate - Float64 tmp_sample_rate; - UInt32 outSize = sizeof(Float64); - OSStatus err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &tmp_sample_rate); - if (err != noErr) { - jack_error("Cannot get current sample rate"); - printError(err); - } else { - jack_log("JackCoreAudioDriver::SRNotificationCallback : checked sample rate = %f", tmp_sample_rate); - } - driver->fState = true; - break; - } - } - - return noErr; -} - -OSStatus JackCoreAudioDriver::BSNotificationCallback(AudioDeviceID inDevice, - UInt32 inChannel, - Boolean isInput, - AudioDevicePropertyID inPropertyID, - void* inClientData) -{ - JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData; - - switch (inPropertyID) { - - case kAudioDevicePropertyBufferFrameSize: { - jack_log("JackCoreAudioDriver::BSNotificationCallback kAudioDevicePropertyBufferFrameSize"); - // Check new buffer size - UInt32 tmp_buffer_size; - UInt32 outSize = sizeof(UInt32); - OSStatus err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, &outSize, &tmp_buffer_size); - if (err != noErr) { - jack_error("Cannot get current buffer size"); - printError(err); - } else { - jack_log("JackCoreAudioDriver::BSNotificationCallback : checked buffer size = %d", tmp_buffer_size); - } - driver->fState = true; - break; - } - } - - return noErr; -} - -// A better implementation would possibly try to recover in case of hardware device change (see HALLAB HLFilePlayerWindowControllerAudioDevicePropertyListenerProc code) -OSStatus JackCoreAudioDriver::AudioHardwareNotificationCallback(AudioHardwarePropertyID inPropertyID, void* inClientData) -{ - JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData; - - switch (inPropertyID) { - - case kAudioHardwarePropertyDevices: { - jack_log("JackCoreAudioDriver::AudioHardwareNotificationCallback kAudioHardwarePropertyDevices"); - DisplayDeviceNames(); - AudioDeviceID captureID, playbackID; - if (CheckAvailableDevice(driver->fDeviceID) || - (CheckAvailableDeviceName(driver->fCaptureUID, &captureID) - && CheckAvailableDeviceName(driver->fPlaybackUID, &playbackID))) { - - } - break; - } - } - - return noErr; -} - -OSStatus JackCoreAudioDriver::DeviceNotificationCallback(AudioDeviceID inDevice, - UInt32 inChannel, - Boolean isInput, - AudioDevicePropertyID inPropertyID, - void* inClientData) -{ - JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData; - - switch (inPropertyID) { - - case kAudioDevicePropertyDeviceIsRunning: { - UInt32 isrunning = 0; - UInt32 outsize = sizeof(UInt32); - if (AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyDeviceIsRunning, &outsize, &isrunning) == noErr) { - jack_log("JackCoreAudioDriver::DeviceNotificationCallback kAudioDevicePropertyDeviceIsRunning = %d", isrunning); - } - break; - } - - case kAudioDevicePropertyDeviceIsAlive: { - UInt32 isalive = 0; - UInt32 outsize = sizeof(UInt32); - if (AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyDeviceIsAlive, &outsize, &isalive) == noErr) { - jack_log("JackCoreAudioDriver::DeviceNotificationCallback kAudioDevicePropertyDeviceIsAlive = %d", isalive); - } - break; - } - - case kAudioDevicePropertyDeviceHasChanged: { - UInt32 hachanged = 0; - UInt32 outsize = sizeof(UInt32); - if (AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyDeviceHasChanged, &outsize, &hachanged) == noErr) { - jack_log("JackCoreAudioDriver::DeviceNotificationCallback kAudioDevicePropertyDeviceHasChanged = %d", hachanged); - } - break; - } - - case kAudioDeviceProcessorOverload: { - jack_error("DeviceNotificationCallback kAudioDeviceProcessorOverload"); - jack_time_t cur_time = GetMicroSeconds(); - driver->NotifyXRun(cur_time, float(cur_time - driver->fBeginDateUst)); // Better this value than nothing... - break; - } - - case kAudioDevicePropertyStreamConfiguration: { - jack_error("Cannot handle kAudioDevicePropertyStreamConfiguration : server will quit..."); - driver->NotifyFailure(JackFailure | JackBackendError, "Another application has changed the device configuration"); // Message length limited to JACK_MESSAGE_SIZE - driver->CloseAUHAL(); - kill(JackTools::GetPID(), SIGINT); - return kAudioHardwareUnsupportedOperationError; - } - - case kAudioDevicePropertyNominalSampleRate: { - Float64 sample_rate = 0; - UInt32 outsize = sizeof(Float64); - OSStatus err = AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outsize, &sample_rate); - if (err != noErr) { - return kAudioHardwareUnsupportedOperationError; - } - - char device_name[256]; - const char* digidesign_name = "Digidesign"; - driver->GetDeviceNameFromID(driver->fDeviceID, device_name); - - if (sample_rate != driver->fEngineControl->fSampleRate) { - - // Digidesign hardware, so "special" code : change the SR again here - if (strncmp(device_name, digidesign_name, 10) == 0) { - - jack_log("JackCoreAudioDriver::DeviceNotificationCallback Digidesign HW = %s", device_name); - - // Set sample rate again... - sample_rate = driver->fEngineControl->fSampleRate; - err = AudioDeviceSetProperty(driver->fDeviceID, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outsize, &sample_rate); - if (err != noErr) { - jack_error("Cannot set sample rate = %f", sample_rate); - printError(err); - } else { - jack_log("JackCoreAudioDriver::DeviceNotificationCallback : set sample rate = %f", sample_rate); - } - - // Check new sample rate again... - outsize = sizeof(Float64); - err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outsize, &sample_rate); - if (err != noErr) { - jack_error("Cannot get current sample rate"); - printError(err); - } else { - jack_log("JackCoreAudioDriver::DeviceNotificationCallback : checked sample rate = %f", sample_rate); - } - return noErr; - - } else { - driver->NotifyFailure(JackFailure | JackBackendError, "Another application has changed the sample rate"); // Message length limited to JACK_MESSAGE_SIZE - driver->CloseAUHAL(); - kill(JackTools::GetPID(), SIGINT); - return kAudioHardwareUnsupportedOperationError; - } - } - } - - } - return noErr; -} - -OSStatus JackCoreAudioDriver::GetDeviceIDFromUID(const char* UID, AudioDeviceID* id) -{ - UInt32 size = sizeof(AudioValueTranslation); - CFStringRef inIUD = CFStringCreateWithCString(NULL, UID, CFStringGetSystemEncoding()); - AudioValueTranslation value = { &inIUD, sizeof(CFStringRef), id, sizeof(AudioDeviceID) }; - - if (inIUD == NULL) { - return kAudioHardwareUnspecifiedError; - } else { - OSStatus res = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &value); - CFRelease(inIUD); - jack_log("JackCoreAudioDriver::GetDeviceIDFromUID %s %ld", UID, *id); - return (*id == kAudioDeviceUnknown) ? kAudioHardwareBadDeviceError : res; - } -} - -OSStatus JackCoreAudioDriver::GetDefaultDevice(AudioDeviceID* id) -{ - OSStatus res; - UInt32 theSize = sizeof(UInt32); - AudioDeviceID inDefault; - AudioDeviceID outDefault; - - if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) { - return res; - } - - if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) { - return res; - } - - jack_log("JackCoreAudioDriver::GetDefaultDevice : input = %ld output = %ld", inDefault, outDefault); - - // Get the device only if default input and output are the same - if (inDefault != outDefault) { - jack_error("Default input and output devices are not the same !!"); - return kAudioHardwareBadDeviceError; - } else if (inDefault == 0) { - jack_error("Default input and output devices are null !!"); - return kAudioHardwareBadDeviceError; - } else { - *id = inDefault; - return noErr; - } -} - -OSStatus JackCoreAudioDriver::GetDefaultInputDevice(AudioDeviceID* id) -{ - OSStatus res; - UInt32 theSize = sizeof(UInt32); - AudioDeviceID inDefault; - - if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) { - return res; - } - - if (inDefault == 0) { - jack_error("Error default input device is 0, will take 'Built-in'..."); - if (CheckAvailableDeviceName("Built-in Microphone", id) - || CheckAvailableDeviceName("Built-in Line", id)) { - jack_log("JackCoreAudioDriver::GetDefaultInputDevice : output = %ld", *id); - return noErr; - } else { - jack_error("Cannot find any input device to use..."); - return -1; - } - } - jack_log("JackCoreAudioDriver::GetDefaultInputDevice : input = %ld ", inDefault); - *id = inDefault; - return noErr; -} - -OSStatus JackCoreAudioDriver::GetDefaultOutputDevice(AudioDeviceID* id) -{ - OSStatus res; - UInt32 theSize = sizeof(UInt32); - AudioDeviceID outDefault; - - if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) { - return res; - } - - if (outDefault == 0) { - jack_error("Error default ouput device is 0, will take 'Built-in'..."); - if (CheckAvailableDeviceName("Built-in Output", id)) { - jack_log("JackCoreAudioDriver::GetDefaultOutputDevice : output = %ld", *id); - return noErr; - } else { - jack_error("Cannot find any output device to use..."); - return -1; - } - } - jack_log("JackCoreAudioDriver::GetDefaultOutputDevice : output = %ld", outDefault); - *id = outDefault; - return noErr; -} - -OSStatus JackCoreAudioDriver::GetDeviceNameFromID(AudioDeviceID id, char* name) -{ - UInt32 size = 256; - return AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceName, &size, name); -} - -OSStatus JackCoreAudioDriver::GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput) -{ - OSStatus err = noErr; - UInt32 outSize; - Boolean outWritable; - - channelCount = 0; - err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable); - if (err == noErr) { - int stream_count = outSize / sizeof(AudioBufferList); - jack_log("JackCoreAudioDriver::GetTotalChannels stream_count = %d", stream_count); - AudioBufferList bufferList[stream_count]; - err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList); - if (err == noErr) { - for (uint i = 0; i < bufferList->mNumberBuffers; i++) { - channelCount += bufferList->mBuffers[i].mNumberChannels; - jack_log("JackCoreAudioDriver::GetTotalChannels stream = %d channels = %d", i, bufferList->mBuffers[i].mNumberChannels); - } - } - } - return err; -} - -OSStatus JackCoreAudioDriver::GetStreamLatencies(AudioDeviceID device, bool isInput, vector& latencies) -{ - OSStatus err = noErr; - UInt32 outSize1, outSize2, outSize3; - Boolean outWritable; - - err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, &outWritable); - if (err == noErr) { - int stream_count = outSize1 / sizeof(UInt32); - AudioStreamID streamIDs[stream_count]; - AudioBufferList bufferList[stream_count]; - UInt32 streamLatency; - outSize2 = sizeof(UInt32); - - err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, streamIDs); - if (err != noErr) { - jack_error("GetStreamLatencies kAudioDevicePropertyStreams err = %d", err); - return err; - } - - err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, &outWritable); - if (err != noErr) { - jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err); - return err; - } - - for (int i = 0; i < stream_count; i++) { - err = AudioStreamGetProperty(streamIDs[i], 0, kAudioStreamPropertyLatency, &outSize2, &streamLatency); - if (err != noErr) { - jack_error("GetStreamLatencies kAudioStreamPropertyLatency err = %d", err); - return err; - } - err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, bufferList); - if (err != noErr) { - jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err); - return err; - } - // Push 'channel' time the stream latency - for (uint k = 0; k < bufferList->mBuffers[i].mNumberChannels; k++) { - latencies.push_back(streamLatency); - } - } - } - return err; -} - -bool JackCoreAudioDriver::IsDigitalDevice(AudioDeviceID device) -{ - OSStatus err = noErr; - UInt32 outSize1; - bool is_digital = false; - - /* Get a list of all the streams on this device */ - AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster }; - err = AudioObjectGetPropertyDataSize(device, &streamsAddress, 0, NULL, &outSize1); - if (err != noErr) { - jack_error("IsDigitalDevice kAudioDevicePropertyStreams err = %d", err); - return false; - } - - int stream_count = outSize1 / sizeof(AudioStreamID); - AudioStreamID streamIDs[stream_count]; - - err = AudioObjectGetPropertyData(device, &streamsAddress, 0, NULL, &outSize1, streamIDs); - - if (err != noErr) { - jack_error("IsDigitalDevice kAudioDevicePropertyStreams list err = %d", err); - return false; - } - - AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 }; - - for (int i = 0; i < stream_count ; i++) { - - /* Find a stream with a cac3 stream */ - int format_num = 0; - - /* Retrieve all the stream formats supported by each output stream */ - err = AudioObjectGetPropertyDataSize(streamIDs[i], &physicalFormatsAddress, 0, NULL, &outSize1); - - if (err != noErr) { - jack_error("IsDigitalDevice kAudioStreamPropertyAvailablePhysicalFormats err = %d", err); - return false; - } - - format_num = outSize1 / sizeof(AudioStreamRangedDescription); - AudioStreamRangedDescription format_list[format_num]; - - err = AudioObjectGetPropertyData(streamIDs[i], &physicalFormatsAddress, 0, NULL, &outSize1, format_list); - - if (err != noErr) { - jack_error("IsDigitalDevice could not get the list of streamformats err = %d", err); - return false; - } - - /* Check if one of the supported formats is a digital format */ - for (int j = 0; j < format_num; j++) { - - PrintStreamDesc(&format_list[j].mFormat); - - if (format_list[j].mFormat.mFormatID == 'IAC3' || - format_list[j].mFormat.mFormatID == 'iac3' || - format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 || - format_list[j].mFormat.mFormatID == kAudioFormatAC3) - { - is_digital = true; - break; - } - } - } - - return is_digital; -} - -JackCoreAudioDriver::JackCoreAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) - : JackAudioDriver(name, alias, engine, table), - fAC3Encoder(NULL), - fJackInputData(NULL), - fDriverOutputData(NULL), - fPluginID(0), - fState(false), - fHogged(false), - fIOUsage(1.f), - fComputationGrain(-1.f), - fClockDriftCompensate(false), - fDigitalPlayback(false) -{} - -JackCoreAudioDriver::~JackCoreAudioDriver() -{ - delete fAC3Encoder; -} - -OSStatus JackCoreAudioDriver::DestroyAggregateDevice() -{ - OSStatus osErr = noErr; - AudioObjectPropertyAddress pluginAOPA; - pluginAOPA.mSelector = kAudioPlugInDestroyAggregateDevice; - pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; - pluginAOPA.mElement = kAudioObjectPropertyElementMaster; - UInt32 outDataSize; - - if (fPluginID > 0) { - - osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); - if (osErr != noErr) { - jack_error("DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); - printError(osErr); - return osErr; - } - - osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID); - if (osErr != noErr) { - jack_error("DestroyAggregateDevice : AudioObjectGetPropertyData error"); - printError(osErr); - return osErr; - } - - } - - return noErr; -} - -OSStatus JackCoreAudioDriver::CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) -{ - OSStatus err = noErr; - AudioObjectID sub_device[32]; - UInt32 outSize = sizeof(sub_device); - - err = AudioDeviceGetProperty(captureDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); - vector captureDeviceIDArray; - - jack_log("JackCoreAudioDriver::CreateAggregateDevice : input device %d", captureDeviceID); - - if (err != noErr) { - jack_log("JackCoreAudioDriver::CreateAggregateDevice : input device does not have subdevices"); - captureDeviceIDArray.push_back(captureDeviceID); - } else { - int num_devices = outSize / sizeof(AudioObjectID); - jack_log("JackCoreAudioDriver::CreateAggregateDevice : input device has %d subdevices", num_devices); - for (int i = 0; i < num_devices; i++) { - jack_log("JackCoreAudioDriver::CreateAggregateDevice : input sub_device %d", sub_device[i]); - captureDeviceIDArray.push_back(sub_device[i]); - } - } - - outSize = sizeof(sub_device); - err = AudioDeviceGetProperty(playbackDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); - vector playbackDeviceIDArray; - - jack_log("JackCoreAudioDriver::CreateAggregateDevice : output device %d", playbackDeviceID); - - if (err != noErr) { - jack_log("JackCoreAudioDriver::CreateAggregateDevice : output device does not have subdevices"); - playbackDeviceIDArray.push_back(playbackDeviceID); - } else { - int num_devices = outSize / sizeof(AudioObjectID); - jack_log("JackCoreAudioDriver::CreateAggregateDevice : output device has %d subdevices", num_devices); - for (int i = 0; i < num_devices; i++) { - jack_log("JackCoreAudioDriver::CreateAggregateDevice : output sub_device %d", sub_device[i]); - playbackDeviceIDArray.push_back(sub_device[i]); - } - } - - return CreateAggregateDeviceAux(captureDeviceIDArray, playbackDeviceIDArray, samplerate, outAggregateDevice); -} - -OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector captureDeviceID, vector playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) -{ - OSStatus osErr = noErr; - UInt32 outSize; - Boolean outWritable; - - // Prepare sub-devices for clock drift compensation - // Workaround for bug in the HAL : until 10.6.2 - AudioObjectPropertyAddress theAddressOwned = { kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - AudioObjectPropertyAddress theAddressDrift = { kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - UInt32 theQualifierDataSize = sizeof(AudioObjectID); - AudioClassID inClass = kAudioSubDeviceClassID; - void* theQualifierData = &inClass; - UInt32 subDevicesNum = 0; - - //--------------------------------------------------------------------------- - // Setup SR of both devices otherwise creating AD may fail... - //--------------------------------------------------------------------------- - UInt32 keptclockdomain = 0; - UInt32 clockdomain = 0; - outSize = sizeof(UInt32); - bool need_clock_drift_compensation = false; - - for (UInt32 i = 0; i < captureDeviceID.size(); i++) { - if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) { - jack_error("CreateAggregateDeviceAux : cannot set SR of input device"); - } else { - // Check clock domain - osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); - if (osErr != 0) { - jack_error("CreateAggregateDeviceAux : kAudioDevicePropertyClockDomain error"); - printError(osErr); - } else { - keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; - jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : input clockdomain = %d", clockdomain); - if (clockdomain != 0 && clockdomain != keptclockdomain) { - jack_error("CreateAggregateDeviceAux : devices do not share the same clock!! clock drift compensation would be needed..."); - need_clock_drift_compensation = true; - } - } - } - } - - for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { - if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) { - jack_error("CreateAggregateDeviceAux : cannot set SR of output device"); - } else { - // Check clock domain - osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); - if (osErr != 0) { - jack_error("CreateAggregateDeviceAux : kAudioDevicePropertyClockDomain error"); - printError(osErr); - } else { - keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; - jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : output clockdomain = %d", clockdomain); - if (clockdomain != 0 && clockdomain != keptclockdomain) { - jack_error("CreateAggregateDeviceAux : devices do not share the same clock!! clock drift compensation would be needed..."); - need_clock_drift_compensation = true; - } - } - } - } - - // If no valid clock domain was found, then assume we have to compensate... - if (keptclockdomain == 0) { - need_clock_drift_compensation = true; - } - - //--------------------------------------------------------------------------- - // Start to create a new aggregate by getting the base audio hardware plugin - //--------------------------------------------------------------------------- - - char device_name[256]; - for (UInt32 i = 0; i < captureDeviceID.size(); i++) { - GetDeviceNameFromID(captureDeviceID[i], device_name); - jack_info("Separated input = '%s' ", device_name); - } - - for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { - GetDeviceNameFromID(playbackDeviceID[i], device_name); - jack_info("Separated output = '%s' ", device_name); - } - - osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); - if (osErr != noErr) { - jack_error("CreateAggregateDeviceAux : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); - printError(osErr); - return osErr; - } - - AudioValueTranslation pluginAVT; - - CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio"); - - pluginAVT.mInputData = &inBundleRef; - pluginAVT.mInputDataSize = sizeof(inBundleRef); - pluginAVT.mOutputData = &fPluginID; - pluginAVT.mOutputDataSize = sizeof(fPluginID); - - osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); - if (osErr != noErr) { - jack_error("CreateAggregateDeviceAux : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); - printError(osErr); - return osErr; - } - - //------------------------------------------------- - // Create a CFDictionary for our aggregate device - //------------------------------------------------- - - CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - CFStringRef AggregateDeviceNameRef = CFSTR("JackDuplex"); - CFStringRef AggregateDeviceUIDRef = CFSTR("com.grame.JackDuplex"); - - // add the name of the device to the dictionary - CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef); - - // add our choice of UID for the aggregate device to the dictionary - CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef); - - // add a "private aggregate key" to the dictionary - int value = 1; - CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value); - - SInt32 system; - Gestalt(gestaltSystemVersion, &system); - - jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : system version = %x limit = %x", system, 0x00001054); - - // Starting with 10.5.4 systems, the AD can be internal... (better) - if (system < 0x00001054) { - jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : public aggregate device...."); - } else { - jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : private aggregate device...."); - CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); - } - - // Prepare sub-devices for clock drift compensation - CFMutableArrayRef subDevicesArrayClock = NULL; - - /* - if (fClockDriftCompensate) { - if (need_clock_drift_compensation) { - jack_info("Clock drift compensation activated..."); - subDevicesArrayClock = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - - for (UInt32 i = 0; i < captureDeviceID.size(); i++) { - CFStringRef UID = GetDeviceName(captureDeviceID[i]); - if (UID) { - CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID); - CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef); - //CFRelease(UID); - CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict); - } - } - - for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { - CFStringRef UID = GetDeviceName(playbackDeviceID[i]); - if (UID) { - CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID); - CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef); - //CFRelease(UID); - CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict); - } - } - - // add sub-device clock array for the aggregate device to the dictionary - CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock); - } else { - jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)"); - } - } - */ - - //------------------------------------------------- - // Create a CFMutableArray for our sub-device list - //------------------------------------------------- - - // we need to append the UID for each device to a CFMutableArray, so create one here - CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - - vector captureDeviceUID; - for (UInt32 i = 0; i < captureDeviceID.size(); i++) { - CFStringRef ref = GetDeviceName(captureDeviceID[i]); - if (ref == NULL) { - return -1; - } - captureDeviceUID.push_back(ref); - // input sub-devices in this example, so append the sub-device's UID to the CFArray - CFArrayAppendValue(subDevicesArray, ref); - } - - vector playbackDeviceUID; - for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { - CFStringRef ref = GetDeviceName(playbackDeviceID[i]); - if (ref == NULL) { - return -1; - } - playbackDeviceUID.push_back(ref); - // output sub-devices in this example, so append the sub-device's UID to the CFArray - CFArrayAppendValue(subDevicesArray, ref); - } - - //----------------------------------------------------------------------- - // Feed the dictionary to the plugin, to create a blank aggregate device - //----------------------------------------------------------------------- - - AudioObjectPropertyAddress pluginAOPA; - pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice; - pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; - pluginAOPA.mElement = kAudioObjectPropertyElementMaster; - UInt32 outDataSize; - - osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); - if (osErr != noErr) { - jack_error("CreateAggregateDeviceAux : AudioObjectGetPropertyDataSize error"); - printError(osErr); - goto error; - } - - osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); - if (osErr != noErr) { - jack_error("CreateAggregateDeviceAux : AudioObjectGetPropertyData error"); - printError(osErr); - goto error; - } - - // pause for a bit to make sure that everything completed correctly - // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); - - //------------------------- - // Set the sub-device list - //------------------------- - - pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList; - pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; - pluginAOPA.mElement = kAudioObjectPropertyElementMaster; - outDataSize = sizeof(CFMutableArrayRef); - osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); - if (osErr != noErr) { - jack_error("CreateAggregateDeviceAux : AudioObjectSetPropertyData for sub-device list error"); - printError(osErr); - goto error; - } - - // pause again to give the changes time to take effect - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); - - //----------------------- - // Set the master device - //----------------------- - - // set the master device manually (this is the device which will act as the master clock for the aggregate device) - // pass in the UID of the device you want to use - pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice; - pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; - pluginAOPA.mElement = kAudioObjectPropertyElementMaster; - outDataSize = sizeof(CFStringRef); - osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &playbackDeviceUID[0]); // First playback is master... - if (osErr != noErr) { - jack_error("CreateAggregateDeviceAux : AudioObjectSetPropertyData for master device error"); - printError(osErr); - goto error; - } - - // pause again to give the changes time to take effect - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); - - // Prepare sub-devices for clock drift compensation - // Workaround for bug in the HAL : until 10.6.2 - - if (fClockDriftCompensate) { - if (need_clock_drift_compensation) { - jack_info("Clock drift compensation activated..."); - - // Get the property data size - osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize); - if (osErr != noErr) { - jack_error("CreateAggregateDeviceAux kAudioObjectPropertyOwnedObjects error"); - printError(osErr); - } - - // Calculate the number of object IDs - subDevicesNum = outSize / sizeof(AudioObjectID); - jack_info("JackCoreAudioDriver::CreateAggregateDeviceAux clock drift compensation, number of sub-devices = %d", subDevicesNum); - AudioObjectID subDevices[subDevicesNum]; - outSize = sizeof(subDevices); - - osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices); - if (osErr != noErr) { - jack_error("CreateAggregateDeviceAux kAudioObjectPropertyOwnedObjects error"); - printError(osErr); - } - - // Set kAudioSubDevicePropertyDriftCompensation property... - for (UInt32 index = 0; index < subDevicesNum; ++index) { - UInt32 theDriftCompensationValue = 1; - osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue); - if (osErr != noErr) { - jack_error("CreateAggregateDeviceAux kAudioSubDevicePropertyDriftCompensation error"); - printError(osErr); - } - } - } else { - jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)"); - } - } - - // pause again to give the changes time to take effect - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); - - //---------- - // Clean up - //---------- - - // release the private AD key - CFRelease(AggregateDeviceNumberRef); - - // release the CF objects we have created - we don't need them any more - CFRelease(aggDeviceDict); - CFRelease(subDevicesArray); - - if (subDevicesArrayClock) { - CFRelease(subDevicesArrayClock); - } - - // release the device UID - for (UInt32 i = 0; i < captureDeviceUID.size(); i++) { - CFRelease(captureDeviceUID[i]); - } - - for (UInt32 i = 0; i < playbackDeviceUID.size(); i++) { - CFRelease(playbackDeviceUID[i]); - } - - jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : new aggregate device %ld", *outAggregateDevice); - return noErr; - -error: - DestroyAggregateDevice(); - return -1; -} - -int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, - const char* playback_driver_uid, - char* capture_driver_name, - char* playback_driver_name, - jack_nframes_t samplerate, - bool ac3_encoding) -{ - capture_driver_name[0] = 0; - playback_driver_name[0] = 0; - - // Duplex - if (strcmp(capture_driver_uid, "") != 0 && strcmp(playback_driver_uid, "") != 0) { - jack_log("JackCoreAudioDriver::SetupDevices : duplex"); - - // Same device for capture and playback... - if (strcmp(capture_driver_uid, playback_driver_uid) == 0) { - - if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { - jack_log("JackCoreAudioDriver::SetupDevices : will take default in/out"); - if (GetDefaultDevice(&fDeviceID) != noErr) { - jack_error("Cannot open default device"); - return -1; - } - } - - if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr || GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { - jack_error("Cannot get device name from device ID"); - return -1; - } - - if (fHogged) { - if (!TakeHogAux(fDeviceID, false)) { - jack_error("Cannot take hog mode"); - } - if (ac3_encoding) { - fDigitalPlayback = IsDigitalDevice(fDeviceID); - } - } - - } else { - - // Creates aggregate device - AudioDeviceID captureID = -1; - AudioDeviceID playbackID = -1; - - if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { - jack_log("JackCoreAudioDriver::SetupDevices : will take default input"); - if (GetDefaultInputDevice(&captureID) != noErr) { - jack_error("Cannot open default input device"); - return -1; - } - } - - if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) { - jack_log("JackCoreAudioDriver::SetupDevices : will take default output"); - if (GetDefaultOutputDevice(&playbackID) != noErr) { - jack_error("Cannot open default output device"); - return -1; - } - } - - if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) { - return -1; - } - - GetDeviceNameFromID(captureID, fCaptureUID); - GetDeviceNameFromID(playbackID, fPlaybackUID); - - if (fHogged) { - if (!TakeHogAux(captureID, true)) { - jack_error("Cannot take hog mode for capture device"); - } - if (!TakeHogAux(playbackID, false)) { - jack_error("Cannot take hog mode for playback device"); - } - if (ac3_encoding) { - fDigitalPlayback = IsDigitalDevice(playbackID); - } - } - - } - - // Capture only - } else if (strcmp(capture_driver_uid, "") != 0) { - - jack_log("JackCoreAudioDriver::SetupDevices : capture only"); - if (GetDeviceIDFromUID(capture_driver_uid, &fDeviceID) != noErr) { - jack_log("JackCoreAudioDriver::SetupDevices : will take default input"); - if (GetDefaultInputDevice(&fDeviceID) != noErr) { - jack_error("Cannot open default input device"); - return -1; - } - } - - if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr) { - jack_error("Cannot get device name from device ID"); - return -1; - } - - if (fHogged) { - if (!TakeHogAux(fDeviceID, true)) { - jack_error("Cannot take hog mode for capture device"); - } - } - - // Playback only - } else if (strcmp(playback_driver_uid, "") != 0) { - - jack_log("JackCoreAudioDriver::SetupDevices : playback only"); - if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { - jack_log("JackCoreAudioDriver::SetupDevices : will take default output"); - if (GetDefaultOutputDevice(&fDeviceID) != noErr) { - jack_error("Cannot open default output device"); - return -1; - } - } - - if (GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { - jack_error("Cannot get device name from device ID"); - return -1; - } - - if (fHogged) { - if (!TakeHogAux(fDeviceID, false)) { - jack_error("Cannot take hog mode for playback device"); - } - if (ac3_encoding) { - fDigitalPlayback = IsDigitalDevice(fDeviceID); - } - } - - // Use default driver in duplex mode - } else { - jack_log("JackCoreAudioDriver::SetupDevices : default driver"); - if (GetDefaultDevice(&fDeviceID) != noErr) { - jack_error("Cannot open default device in duplex mode, so aggregate default input and default output"); - - // Creates aggregate device - AudioDeviceID captureID = -1; - AudioDeviceID playbackID = -1; - - if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { - jack_log("JackCoreAudioDriver::SetupDevices : will take default input"); - if (GetDefaultInputDevice(&captureID) != noErr) { - jack_error("Cannot open default input device"); - return -1; - } - } - - if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) { - jack_log("JackCoreAudioDriver::SetupDevices : will take default output"); - if (GetDefaultOutputDevice(&playbackID) != noErr) { - jack_error("Cannot open default output device"); - return -1; - } - } - - if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) { - return -1; - } - - GetDeviceNameFromID(captureID, fCaptureUID); - GetDeviceNameFromID(playbackID, fPlaybackUID); - - if (fHogged) { - if (!TakeHogAux(captureID, true)) { - jack_error("Cannot take hog mode for capture device"); - } - if (!TakeHogAux(playbackID, false)) { - jack_error("Cannot take hog mode for playback device"); - } - if (ac3_encoding) { - fDigitalPlayback = IsDigitalDevice(playbackID); - } - } - } - } - - return 0; -} - -/* -Return the max possible input channels in in_maxChannels and output channels in out_maxChannels. -*/ -int JackCoreAudioDriver::SetupChannels(bool capturing, bool playing, int& inchannels, int& outchannels, int& in_maxChannels, int& out_maxChannels, bool strict) -{ - OSStatus err = noErr; - - jack_log("JackCoreAudioDriver::SetupChannels : fDeviceID = %d", fDeviceID); - - if (capturing) { - err = GetTotalChannels(fDeviceID, in_maxChannels, true); - if (err != noErr) { - jack_error("SetupChannels : cannot get input channel number"); - printError(err); - return -1; - } else { - jack_log("JackCoreAudioDriver::SetupChannels : max input channels : %d", in_maxChannels); - } - } - - if (playing) { - err = GetTotalChannels(fDeviceID, out_maxChannels, false); - if (err != noErr) { - jack_error("Cannot get output channel number"); - printError(err); - return -1; - } else { - jack_log("JackCoreAudioDriver::SetupChannels : max output channels : %d", out_maxChannels); - } - } - - if (inchannels > in_maxChannels) { - jack_error("This device hasn't required input channels inchannels = %d in_maxChannels = %d", inchannels, in_maxChannels); - if (strict) { - return -1; - } - } - - if (outchannels > out_maxChannels) { - jack_error("This device hasn't required output channels outchannels = %d out_maxChannels = %d", outchannels, out_maxChannels); - if (strict) { - return -1; - } - } - - if (inchannels == -1) { - jack_log("JackCoreAudioDriver::SetupChannels : setup max in channels = %d", in_maxChannels); - inchannels = in_maxChannels; - } - - if (outchannels == -1) { - jack_log("JackCoreAudioDriver::SetupChannels : setup max out channels = %d", out_maxChannels); - outchannels = out_maxChannels; - } - - return 0; -} - -int JackCoreAudioDriver::SetupBufferSize(jack_nframes_t buffer_size) -{ - // Setting buffer size - OSStatus err = noErr; - UInt32 tmp_buffer_size = buffer_size; - UInt32 outSize = sizeof(UInt32); - - err = AudioDeviceGetProperty(fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, &outSize, &tmp_buffer_size); - if (err != noErr) { - jack_error("Cannot get buffer size %ld", buffer_size); - printError(err); - return -1; - } else { - jack_log("JackCoreAudioDriver::SetupBufferSize : current buffer size = %ld", tmp_buffer_size); - } - - // If needed, set new buffer size - if (buffer_size != tmp_buffer_size) { - tmp_buffer_size = buffer_size; - - // To get BS change notification - err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyBufferFrameSize, BSNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyBufferFrameSize"); - printError(err); - return -1; - } - - // Waiting for BS change notification - int count = 0; - fState = false; - - err = AudioDeviceSetProperty(fDeviceID, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, outSize, &tmp_buffer_size); - if (err != noErr) { - jack_error("SetupBufferSize : cannot set buffer size = %ld", tmp_buffer_size); - printError(err); - goto error; - } - - while (!fState && count++ < WAIT_NOTIFICATION_COUNTER) { - usleep(100000); - jack_log("JackCoreAudioDriver::SetupBufferSize : wait count = %d", count); - } - - if (count >= WAIT_NOTIFICATION_COUNTER) { - jack_error("Did not get buffer size notification..."); - goto error; - } - - // Check new buffer size - outSize = sizeof(UInt32); - err = AudioDeviceGetProperty(fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, &outSize, &tmp_buffer_size); - if (err != noErr) { - jack_error("Cannot get current buffer size"); - printError(err); - } else { - jack_log("JackCoreAudioDriver::SetupBufferSize : checked buffer size = %ld", tmp_buffer_size); - } - - // Remove BS change notification - AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyBufferFrameSize, BSNotificationCallback); - } - - return 0; - -error: - - // Remove BS change notification - AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyBufferFrameSize, BSNotificationCallback); - return -1; -} - -int JackCoreAudioDriver::SetupSampleRate(jack_nframes_t sample_rate) -{ - return SetupSampleRateAux(fDeviceID, sample_rate); -} - -int JackCoreAudioDriver::SetupSampleRateAux(AudioDeviceID inDevice, jack_nframes_t sample_rate) -{ - OSStatus err = noErr; - UInt32 outSize; - Float64 tmp_sample_rate; - - // Get sample rate - outSize = sizeof(Float64); - err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &tmp_sample_rate); - if (err != noErr) { - jack_error("Cannot get current sample rate"); - printError(err); - return -1; - } else { - jack_log("JackCoreAudioDriver::SetupSampleRateAux : current sample rate = %f", tmp_sample_rate); - } - - // If needed, set new sample rate - if (sample_rate != (jack_nframes_t)tmp_sample_rate) { - tmp_sample_rate = (Float64)sample_rate; - - // To get SR change notification - err = AudioDeviceAddPropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); - printError(err); - return -1; - } - - // Waiting for SR change notification - int count = 0; - fState = false; - - err = AudioDeviceSetProperty(inDevice, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outSize, &tmp_sample_rate); - if (err != noErr) { - jack_error("Cannot set sample rate = %ld", sample_rate); - printError(err); - goto error; - } - - while (!fState && count++ < WAIT_NOTIFICATION_COUNTER) { - usleep(100000); - jack_log("JackCoreAudioDriver::SetupSampleRateAux : wait count = %d", count); - } - - if (count >= WAIT_NOTIFICATION_COUNTER) { - jack_error("Did not get sample rate notification..."); - goto error; - } - - // Check new sample rate - outSize = sizeof(Float64); - err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &tmp_sample_rate); - if (err != noErr) { - jack_error("Cannot get current sample rate"); - printError(err); - } else { - jack_log("JackCoreAudioDriver::SetupSampleRateAux : checked sample rate = %f", tmp_sample_rate); - } - - // Remove SR change notification - AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback); - } - - return 0; - -error: - - // Remove SR change notification - AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback); - return -1; -} - -int JackCoreAudioDriver::OpenAUHAL(bool capturing, - bool playing, - int inchannels, - int outchannels, - int in_maxChannels, - int out_maxChannels, - const vector& chan_in_list, - const vector& chan_out_list, - jack_nframes_t buffer_size, - jack_nframes_t sample_rate) -{ - ComponentResult err1; - UInt32 enableIO; - AudioStreamBasicDescription srcFormat, dstFormat; - AudioDeviceID currAudioDeviceID; - UInt32 size; - - jack_log("JackCoreAudioDriver::OpenAUHAL : capturing = %d playing = %d inchannels = %d outchannels = %d in_maxChannels = %d out_maxChannels = %d chan_in_list = %d chan_out_list = %d", - capturing, playing, inchannels, outchannels, in_maxChannels, out_maxChannels, chan_in_list.size(), chan_out_list.size()); - - if (inchannels == 0 && outchannels == 0) { - jack_error("No input and output channels..."); - return -1; - } - - // AUHAL -#ifdef MAC_OS_X_VERSION_10_5 - ComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0}; - Component HALOutput = FindNextComponent(NULL, &cd); - err1 = OpenAComponent(HALOutput, &fAUHAL); - if (err1 != noErr) { - jack_error("Error calling OpenAComponent"); - printError(err1); - goto error; - } -#else - AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0}; - AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd); - err1 = AudioComponentInstanceNew(HALOutput, &fAUHAL); - if (err1 != noErr) { - jack_error("Error calling AudioComponentInstanceNew"); - printError(err1); - goto error; - } -#endif - - err1 = AudioUnitInitialize(fAUHAL); - if (err1 != noErr) { - jack_error("Cannot initialize AUHAL unit"); - printError(err1); - goto error; - } - - // Start I/O - if (capturing && inchannels > 0) { - enableIO = 1; - jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL input on"); - } else { - enableIO = 0; - jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL input off"); - } - - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input"); - printError(err1); - goto error; - } - - if (playing && outchannels > 0) { - enableIO = 1; - jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL output on"); - } else { - enableIO = 0; - jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL output off"); - } - - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output"); - printError(err1); - goto error; - } - - size = sizeof(AudioDeviceID); - err1 = AudioUnitGetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &currAudioDeviceID, &size); - if (err1 != noErr) { - jack_error("Error calling AudioUnitGetProperty - kAudioOutputUnitProperty_CurrentDevice"); - printError(err1); - goto error; - } else { - jack_log("JackCoreAudioDriver::OpenAUHAL : AudioUnitGetPropertyCurrentDevice = %d", currAudioDeviceID); - } - - // Setup up choosen device, in both input and output cases - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &fDeviceID, sizeof(AudioDeviceID)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_CurrentDevice"); - printError(err1); - goto error; - } - - // Set buffer size - if (capturing && inchannels > 0) { - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&buffer_size, sizeof(UInt32)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); - printError(err1); - goto error; - } - } - - if (playing && outchannels > 0) { - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&buffer_size, sizeof(UInt32)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); - printError(err1); - goto error; - } - } - - // Setup input channel map - if (capturing && inchannels > 0 && inchannels <= in_maxChannels) { - SInt32 chanArr[in_maxChannels]; - for (int i = 0; i < in_maxChannels; i++) { - chanArr[i] = -1; - } - // Explicit mapping - if (chan_in_list.size() > 0) { - for (uint i = 0; i < chan_in_list.size(); i++) { - int chan = chan_in_list[i]; - if (chan < in_maxChannels) { - // The wanted JACK input index for the 'chan' channel value - chanArr[chan] = i; - jack_info("Input channel = %d ==> JACK input port = %d", chan, i); - } else { - jack_info("Error input channel number is incorrect : %d", chan); - goto error; - } - } - } else { - for (int i = 0; i < inchannels; i++) { - chanArr[i] = i; - jack_info("Input channel = %d ==> JACK input port = %d", chanArr[i], i); - } - } - - AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap , kAudioUnitScope_Input, 1, chanArr, sizeof(SInt32) * in_maxChannels); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap for input"); - printError(err1); - goto error; - } - } - - // Setup output channel map - if (playing && outchannels > 0 && outchannels <= out_maxChannels) { - SInt32 chanArr[out_maxChannels]; - for (int i = 0; i < out_maxChannels; i++) { - chanArr[i] = -1; - } - // Explicit mapping - if (chan_out_list.size() > 0) { - for (uint i = 0; i < chan_out_list.size(); i++) { - int chan = chan_out_list[i]; - if (chan < out_maxChannels) { - // The wanted JACK output index for the 'chan' channel value - chanArr[chan] = i; - jack_info("JACK output port = %d ==> output channel = %d", i, chan); - } else { - jack_info("Error output channel number is incorrect : %d", chan); - goto error; - } - } - } else { - for (int i = 0; i < outchannels; i++) { - chanArr[i] = i; - jack_info("JACK output port = %d ==> output channel = %d", i, chanArr[i]); - } - } - - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 0, chanArr, sizeof(SInt32) * out_maxChannels); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap for output"); - printError(err1); - goto error; - } - } - - // Setup stream converters - if (capturing && inchannels > 0) { - - size = sizeof(AudioStreamBasicDescription); - err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, &size); - if (err1 != noErr) { - jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); - printError(err1); - goto error; - } - PrintStreamDesc(&srcFormat); - - jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL input stream converter SR = %ld", sample_rate); - srcFormat.mSampleRate = sample_rate; - srcFormat.mFormatID = kAudioFormatLinearPCM; - srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; - srcFormat.mBytesPerPacket = sizeof(jack_default_audio_sample_t); - srcFormat.mFramesPerPacket = 1; - srcFormat.mBytesPerFrame = sizeof(jack_default_audio_sample_t); - srcFormat.mChannelsPerFrame = inchannels; - srcFormat.mBitsPerChannel = 32; - PrintStreamDesc(&srcFormat); - - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); - printError(err1); - goto error; - } - } - - if (playing && outchannels > 0) { - - size = sizeof(AudioStreamBasicDescription); - err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, &size); - if (err1 != noErr) { - jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); - printError(err1); - goto error; - } - PrintStreamDesc(&dstFormat); - - jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL output stream converter SR = %ld", sample_rate); - dstFormat.mSampleRate = sample_rate; - dstFormat.mFormatID = kAudioFormatLinearPCM; - dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; - dstFormat.mBytesPerPacket = sizeof(jack_default_audio_sample_t); - dstFormat.mFramesPerPacket = 1; - dstFormat.mBytesPerFrame = sizeof(jack_default_audio_sample_t); - dstFormat.mChannelsPerFrame = outchannels; - dstFormat.mBitsPerChannel = 32; - PrintStreamDesc(&dstFormat); - - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); - printError(err1); - goto error; - } - } - - // Setup callbacks - if (inchannels > 0 && outchannels == 0) { - AURenderCallbackStruct output; - output.inputProc = Render; - output.inputProcRefCon = this; - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1"); - printError(err1); - goto error; - } - } else { - AURenderCallbackStruct output; - output.inputProc = Render; - output.inputProcRefCon = this; - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output)); - if (err1 != noErr) { - jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0"); - printError(err1); - goto error; - } - } - - return 0; - -error: - CloseAUHAL(); - return -1; -} - -int JackCoreAudioDriver::SetupBuffers(int inchannels) -{ - // Prepare buffers - fJackInputData = (AudioBufferList*)malloc(sizeof(UInt32) + inchannels * sizeof(AudioBuffer)); - fJackInputData->mNumberBuffers = inchannels; - for (int i = 0; i < inchannels; i++) { - fJackInputData->mBuffers[i].mNumberChannels = 1; - fJackInputData->mBuffers[i].mDataByteSize = fEngineControl->fBufferSize * sizeof(jack_default_audio_sample_t); - } - return 0; -} - -void JackCoreAudioDriver::DisposeBuffers() -{ - if (fJackInputData) { - free(fJackInputData); - fJackInputData = 0; - } -} - -void JackCoreAudioDriver::CloseAUHAL() -{ - AudioOutputUnitStop(fAUHAL); - AudioUnitUninitialize(fAUHAL); - CloseComponent(fAUHAL); -} - -int JackCoreAudioDriver::AddListeners() -{ - OSStatus err = noErr; - - // Add listeners - err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDeviceProcessorOverload"); - printError(err); - return -1; - } - - err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, AudioHardwareNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioHardwareAddPropertyListener with kAudioHardwarePropertyDevices"); - printError(err); - return -1; - } - - err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); - printError(err); - return -1; - } - - err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceIsRunning"); - printError(err); - return -1; - } - - err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsAlive, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceIsAlive"); - printError(err); - return -1; - } - - err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceHasChanged, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceHasChanged"); - printError(err); - return -1; - } - - err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration"); - printError(err); - return -1; - } - - err = AudioDeviceAddPropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration"); - printError(err); - return -1; - } - - if (!fEngineControl->fSyncMode && fIOUsage != 1.f) { - UInt32 outSize = sizeof(float); - err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyIOCycleUsage, outSize, &fIOUsage); - if (err != noErr) { - jack_error("Error calling AudioDeviceSetProperty kAudioDevicePropertyIOCycleUsage"); - printError(err); - } - } - - return 0; -} - -void JackCoreAudioDriver::RemoveListeners() -{ - AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback); - AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, AudioHardwareNotificationCallback); - AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback); - AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback); - AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsAlive, DeviceNotificationCallback); - AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceHasChanged, DeviceNotificationCallback); - AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback); - AudioDeviceRemovePropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback); -} - -int JackCoreAudioDriver::Open(jack_nframes_t buffer_size, - jack_nframes_t sample_rate, - bool capturing, - bool playing, - int inchannels, - int outchannels, - const char* chan_in_list, - const char* chan_out_list, - bool monitor, - const char* capture_driver_uid, - const char* playback_driver_uid, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency, - int async_output_latency, - int computation_grain, - bool hogged, - bool clock_drift, - bool ac3_encoding, - int ac3_bitrate, - bool ac3_lfe) -{ - int in_maxChannels = 0; - int out_maxChannels = 0; - char capture_driver_name[256]; - char playback_driver_name[256]; - - fCaptureLatency = capture_latency; - fPlaybackLatency = playback_latency; - fIOUsage = float(async_output_latency) / 100.f; - fComputationGrain = float(computation_grain) / 100.f; - fHogged = hogged; - fClockDriftCompensate = clock_drift; - - SInt32 major; - SInt32 minor; - Gestalt(gestaltSystemVersionMajor, &major); - Gestalt(gestaltSystemVersionMinor, &minor); - - vector parsed_chan_in_list; - vector parsed_chan_out_list; - - // Starting with 10.6 systems, the HAL notification thread is created internally - if (major == 10 && minor >= 6) { - CFRunLoopRef theRunLoop = NULL; - AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - OSStatus osErr = AudioObjectSetPropertyData (kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); - if (osErr != noErr) { - jack_error("Open kAudioHardwarePropertyRunLoop error"); - printError(osErr); - } - } - - if (SetupDevices(capture_driver_uid, playback_driver_uid, capture_driver_name, playback_driver_name, sample_rate, ac3_encoding) < 0) { - goto error; - } - - // Generic JackAudioDriver Open - if (JackAudioDriver::Open(buffer_size, sample_rate, - capturing, playing, - inchannels, outchannels, - monitor, - capture_driver_name, - playback_driver_name, - capture_latency, - playback_latency) != 0) { - goto error; - } - - if (SetupChannels(capturing, playing, inchannels, outchannels, in_maxChannels, out_maxChannels, !ac3_encoding) < 0) { - goto error; - } - - ParseChannelList(chan_in_list, parsed_chan_in_list, in_maxChannels); - if (parsed_chan_in_list.size() > 0) { - jack_info("Explicit input channel list size = %d", parsed_chan_in_list.size()); - inchannels = parsed_chan_in_list.size(); - } - - ParseChannelList(chan_out_list, parsed_chan_out_list, out_maxChannels); - if (parsed_chan_out_list.size() > 0) { - jack_info("Explicit output channel list size = %d", parsed_chan_out_list.size()); - outchannels = parsed_chan_out_list.size(); - } - - if (SetupBufferSize(buffer_size) < 0) { - goto error; - } - - if (SetupSampleRate(sample_rate) < 0) { - goto error; - } - - if (ac3_encoding) { - - if (!fDigitalPlayback) { - jack_error("AC3 encoding can only be used with a digital device"); - goto error; - } - - JackAC3EncoderParams params; - memset(¶ms, 0, sizeof(JackAC3EncoderParams)); - params.bitrate = ac3_bitrate; - params.channels = outchannels; - params.sample_rate = sample_rate; - params.lfe = ac3_lfe; - fAC3Encoder = new JackAC3Encoder(params); - - if (!fAC3Encoder || !fAC3Encoder->Init(sample_rate)) { - jack_error("Cannot allocate or init AC3 encoder"); - goto error; - } - - // Setup AC3 channel number - fPlaybackChannels = outchannels; - if (ac3_lfe) { - fPlaybackChannels++; - } - - if (fPlaybackChannels < 2 || fPlaybackChannels > 6) { - jack_error("AC3 encoder channels must be between 2 and 6"); - goto error; - } - - // Force real output channel number to 2 - outchannels = out_maxChannels = 2; - - } else { - fPlaybackChannels = outchannels; - } - - // Core driver may have changed the in/out values - fCaptureChannels = inchannels; - - if (OpenAUHAL(capturing, playing, inchannels, outchannels, in_maxChannels, out_maxChannels, parsed_chan_in_list, parsed_chan_out_list, buffer_size, sample_rate) < 0) { - goto error; - } - - if (capturing && inchannels > 0) { - if (SetupBuffers(inchannels) < 0) { - goto error; - } - } - - if (AddListeners() < 0) { - goto error; - } - - return noErr; - -error: - Close(); - return -1; -} - -int JackCoreAudioDriver::Close() -{ - jack_log("JackCoreAudioDriver::Close"); - - // Generic audio driver close - int res = JackAudioDriver::Close(); - - RemoveListeners(); - DisposeBuffers(); - CloseAUHAL(); - DestroyAggregateDevice(); - return res; -} - -void JackCoreAudioDriver::UpdateLatencies() -{ - UInt32 size; - OSStatus err; - jack_latency_range_t input_range; - jack_latency_range_t output_range; - jack_latency_range_t monitor_range; - - // Get Input latency - size = sizeof(UInt32); - UInt32 value1 = 0; - UInt32 value2 = 0; - err = AudioDeviceGetProperty(fDeviceID, 0, true, kAudioDevicePropertyLatency, &size, &value1); - if (err != noErr) { - jack_error("AudioDeviceGetProperty kAudioDevicePropertyLatency error"); - } - err = AudioDeviceGetProperty(fDeviceID, 0, true, kAudioDevicePropertySafetyOffset, &size, &value2); - if (err != noErr) { - jack_error("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error"); - } - - input_range.min = input_range.max = fEngineControl->fBufferSize + value1 + value2 + fCaptureLatency; - - // Get input stream latencies - vector input_latencies; - err = GetStreamLatencies(fDeviceID, true, input_latencies); - - for (int i = 0; i < fCaptureChannels; i++) { - if (err != noErr) { - input_range.min += input_latencies[i]; - input_range.max += input_latencies[i]; - } - fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &input_range); - } - - // Get Output latency - size = sizeof(UInt32); - value1 = 0; - value2 = 0; - err = AudioDeviceGetProperty(fDeviceID, 0, false, kAudioDevicePropertyLatency, &size, &value1); - if (err != noErr) { - jack_error("AudioDeviceGetProperty kAudioDevicePropertyLatency error"); - } - err = AudioDeviceGetProperty(fDeviceID, 0, false, kAudioDevicePropertySafetyOffset, &size, &value2); - if (err != noErr) { - jack_error("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error"); - } - - // Get output stream latencies - vector output_latencies; - err = GetStreamLatencies(fDeviceID, false, output_latencies); - - // Add more latency if "async" mode is used... - output_range.min = output_range.max = fEngineControl->fBufferSize + ((fEngineControl->fSyncMode) - ? 0 : fEngineControl->fBufferSize * fIOUsage) + value1 + value2 + fPlaybackLatency; - - for (int i = 0; i < fPlaybackChannels; i++) { - if (err != noErr) { - output_range.min += output_latencies[i]; - output_range.max += output_latencies[i]; - } - fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &output_range); - - // Monitor port - if (fWithMonitorPorts) { - monitor_range.min = monitor_range.max = fEngineControl->fBufferSize; - fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &monitor_range); - } - } -} - -int JackCoreAudioDriver::Attach() -{ - OSStatus err; - JackPort* port; - jack_port_id_t port_index; - UInt32 size; - Boolean isWritable; - char channel_name[64]; - char name[REAL_JACK_PORT_NAME_SIZE+1]; - char alias[REAL_JACK_PORT_NAME_SIZE+1]; - - jack_log("JackCoreAudioDriver::Attach : fBufferSize %ld fSampleRate %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); - - for (int i = 0; i < fCaptureChannels; i++) { - - err = AudioDeviceGetPropertyInfo(fDeviceID, i + 1, true, kAudioDevicePropertyChannelName, &size, &isWritable); - if (err != noErr) { - jack_log("JackCoreAudioDriver::Attach : AudioDeviceGetPropertyInfo kAudioDevicePropertyChannelName error"); - } - if (err == noErr && size > 0) { - err = AudioDeviceGetProperty(fDeviceID, i + 1, true, kAudioDevicePropertyChannelName, &size, channel_name); - if (err != noErr) { - jack_log("JackCoreAudioDriver::Attach : AudioDeviceGetProperty kAudioDevicePropertyChannelName error"); - } - snprintf(alias, sizeof(alias), "%s:%s:out_%s%u", fAliasName, fCaptureDriverName, channel_name, i + 1); - } else { - snprintf(alias, sizeof(alias), "%s:%s:out%u", fAliasName, fCaptureDriverName, i + 1); - } - - snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, i + 1); - - if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { - jack_error("Cannot register port for %s", name); - return -1; - } - - port = fGraphManager->GetPort(port_index); - port->SetAlias(alias); - fCapturePortList[i] = port_index; - } - - for (int i = 0; i < fPlaybackChannels; i++) { - - err = AudioDeviceGetPropertyInfo(fDeviceID, i + 1, false, kAudioDevicePropertyChannelName, &size, &isWritable); - if (err != noErr) { - jack_log("JackCoreAudioDriver::Attach : AudioDeviceGetPropertyInfo kAudioDevicePropertyChannelName error"); - } - if (err == noErr && size > 0) { - err = AudioDeviceGetProperty(fDeviceID, i + 1, false, kAudioDevicePropertyChannelName, &size, channel_name); - if (err != noErr) { - jack_log("JackCoreAudioDriver::Attach : AudioDeviceGetProperty kAudioDevicePropertyChannelName error"); - } - snprintf(alias, sizeof(alias), "%s:%s:in_%s%u", fAliasName, fPlaybackDriverName, channel_name, i + 1); - } else { - snprintf(alias, sizeof(alias), "%s:%s:in%u", fAliasName, fPlaybackDriverName, i + 1); - } - - snprintf(name, sizeof(name), "%s:playback_%d", fClientControl.fName, i + 1); - - if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { - jack_error("Cannot register port for %s", name); - return -1; - } - - port = fGraphManager->GetPort(port_index); - port->SetAlias(alias); - fPlaybackPortList[i] = port_index; - - // Monitor ports - if (fWithMonitorPorts) { - jack_log("JackCoreAudioDriver::Attach : create monitor port"); - snprintf(name, sizeof(name), "%s:monitor_%u", fClientControl.fName, i + 1); - if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, MonitorDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { - jack_error("Cannot register monitor port for %s", name); - return -1; - } else { - fMonitorPortList[i] = port_index; - } - } - } - - if (fAC3Encoder) { - // Setup specific AC3 channels names - for (int i = 0; i < fPlaybackChannels; i++) { - fAC3Encoder->GetChannelName("coreaudio", "", alias, i); - port = fGraphManager->GetPort(fPlaybackPortList[i]); - port->SetAlias(alias); - } - } - - UpdateLatencies(); - - // Input buffers do no change : prepare them only once - for (int i = 0; i < fCaptureChannels; i++) { - fJackInputData->mBuffers[i].mData = GetInputBuffer(i); - } - - return 0; -} - -int JackCoreAudioDriver::Start() -{ - jack_log("JackCoreAudioDriver::Start"); - if (JackAudioDriver::Start() == 0) { - - // Waiting for Render callback to be called (= driver has started) - fState = false; - int count = 0; - - if (AudioOutputUnitStart(fAUHAL) == noErr) { - - while (!fState && count++ < WAIT_COUNTER) { - usleep(100000); - jack_log("JackCoreAudioDriver::Start : wait count = %d", count); - } - - if (count < WAIT_COUNTER) { - jack_info("CoreAudio driver is running..."); - return 0; - } - - jack_error("CoreAudio driver cannot start..."); - } - JackAudioDriver::Stop(); - } - return -1; -} - -int JackCoreAudioDriver::Stop() -{ - jack_log("JackCoreAudioDriver::Stop"); - int res = (AudioOutputUnitStop(fAUHAL) == noErr) ? 0 : -1; - if (JackAudioDriver::Stop() < 0) { - res = -1; - } - return res; -} - -int JackCoreAudioDriver::SetBufferSize(jack_nframes_t buffer_size) -{ - if (SetupBufferSize(buffer_size) < 0) { - return -1; - } - - JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails - - // CoreAudio specific - UpdateLatencies(); - - // Input buffers do no change : prepare them only once - for (int i = 0; i < fCaptureChannels; i++) { - fJackInputData->mBuffers[i].mNumberChannels = 1; - fJackInputData->mBuffers[i].mDataByteSize = fEngineControl->fBufferSize * sizeof(jack_default_audio_sample_t); - fJackInputData->mBuffers[i].mData = GetInputBuffer(i); - } - - return 0; -} - -bool JackCoreAudioDriver::TakeHogAux(AudioDeviceID deviceID, bool isInput) -{ - pid_t hog_pid; - UInt32 propSize = sizeof(hog_pid); - - OSStatus err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyHogMode, &propSize, &hog_pid); - if (err) { - jack_error("Cannot read hog state..."); - printError(err); - } - - jack_log("JackCoreAudioDriver::TakeHogAux : deviceID = %d", deviceID); - - if (hog_pid != getpid()) { - hog_pid = getpid(); - err = AudioDeviceSetProperty(deviceID, 0, 0, isInput, kAudioDevicePropertyHogMode, propSize, &hog_pid); - if (err != noErr) { - jack_error("Can't hog device = %d because it's being hogged by another program or cannot be hogged", deviceID); - return false; - } - } - - return true; -} - -bool JackCoreAudioDriver::TakeHog() -{ - OSStatus err = noErr; - AudioObjectID sub_device[32]; - UInt32 outSize = sizeof(sub_device); - err = AudioDeviceGetProperty(fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); - - if (err != noErr) { - jack_log("JackCoreAudioDriver::TakeHog : device does not have subdevices"); - return TakeHogAux(fDeviceID, true); - } else { - int num_devices = outSize / sizeof(AudioObjectID); - jack_log("JackCoreAudioDriver::TakeHog : device does has %d subdevices", num_devices); - for (int i = 0; i < num_devices; i++) { - if (!TakeHogAux(sub_device[i], true)) { - return false; - } - } - return true; - } -} - -bool JackCoreAudioDriver::IsAggregateDevice(AudioDeviceID device) -{ - UInt32 deviceType, outSize = sizeof(UInt32); - OSStatus err = AudioDeviceGetProperty(device, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyTransportType, &outSize, &deviceType); - - if (err != noErr) { - jack_log("JackCoreAudioDriver::IsAggregateDevice kAudioDevicePropertyTransportType error"); - return false; - } else { - return (deviceType == kAudioDeviceTransportTypeAggregate); - } -} - - -} // end of namespace - - -#ifdef __cplusplus -extern "C" -{ -#endif - - SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor() - { - jack_driver_desc_t * desc; - jack_driver_desc_filler_t filler; - jack_driver_param_value_t value; - - desc = jack_driver_descriptor_construct("coreaudio", JackDriverMaster, "Apple CoreAudio API based audio backend", &filler); - - value.i = -1; - jack_driver_descriptor_add_parameter(desc, &filler, "channels", 'c', JackDriverParamInt, &value, NULL, "Maximum number of channels", "Maximum number of channels. If -1, max possible number of channels will be used"); - jack_driver_descriptor_add_parameter(desc, &filler, "in-channels", 'i', JackDriverParamInt, &value, NULL, "Maximum number of input channels", "Maximum number of input channels. If -1, max possible number of input channels will be used"); - jack_driver_descriptor_add_parameter(desc, &filler, "out-channels", 'o', JackDriverParamInt, &value, NULL, "Maximum number of output channels", "Maximum number of output channels. If -1, max possible number of output channels will be used"); - - value.str[0] = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "input-list", 'n', JackDriverParamString, &value, NULL, "Input channel list for channel mapping", "List of input channel number to be opened (syntax like : \"0 3 2\")"); - jack_driver_descriptor_add_parameter(desc, &filler, "output-list", 'N', JackDriverParamString, &value, NULL, "Output channel list for channel mapping", "List of output channel number to be opened (syntax like : \"0 3 2\")"); - - value.str[0] = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Input CoreAudio device name", NULL); - jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamString, &value, NULL, "Output CoreAudio device name", NULL); - - value.i = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "monitor", 'm', JackDriverParamBool, &value, NULL, "Provide monitor ports for the output", NULL); - -#ifndef __ppc__ - value.i = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "AC3-encoding", 'a', JackDriverParamBool, &value, NULL, "AC3 multi-channels encoding", NULL); - - value.i = 448; - jack_driver_descriptor_add_parameter(desc, &filler, "AC3-bitrate", 'b', JackDriverParamUInt, &value, NULL, "AC3 bitrate", NULL); - - value.i = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "AC3-LFE", 'f', JackDriverParamBool, &value, NULL, "AC3 LFE channel", NULL); -#endif - value.i = true; - jack_driver_descriptor_add_parameter(desc, &filler, "duplex", 'D', JackDriverParamBool, &value, NULL, "Provide both capture and playback ports", NULL); - - value.ui = 44100U; - jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL); - - value.ui = 256U; - jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL); - - value.str[0] = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "CoreAudio device name", NULL); - - value.ui = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "input-latency", 'I', JackDriverParamUInt, &value, NULL, "Extra input latency (frames)", NULL); - jack_driver_descriptor_add_parameter(desc, &filler, "output-latency", 'O', JackDriverParamUInt, &value, NULL, "Extra output latency (frames)", NULL); - - value.i = false; - jack_driver_descriptor_add_parameter(desc, &filler, "list-devices", 'l', JackDriverParamBool, &value, NULL, "Display available CoreAudio devices", NULL); - - value.i = false; - jack_driver_descriptor_add_parameter(desc, &filler, "hog", 'H', JackDriverParamBool, &value, NULL, "Take exclusive access of the audio device", NULL); - - value.ui = 100; - jack_driver_descriptor_add_parameter(desc, &filler, "async-latency", 'L', JackDriverParamUInt, &value, NULL, "Extra output latency in asynchronous mode (percent)", NULL); - - value.ui = 100; - jack_driver_descriptor_add_parameter(desc, &filler, "grain", 'G', JackDriverParamUInt, &value, NULL, "Computation grain in RT thread (percent)", NULL); - - value.i = false; - jack_driver_descriptor_add_parameter(desc, &filler, "clock-drift", 's', JackDriverParamBool, &value, NULL, "Clock drift compensation", "Whether to compensate clock drift in dynamically created aggregate device"); - - return desc; - } - - SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) - { - jack_nframes_t srate = 44100; - jack_nframes_t frames_per_interrupt = 256; - bool capture = false; - bool playback = false; - int chan_in = -1; // Default: if not explicitely set, then max possible will be used... - int chan_out = -1; // Default: if not explicitely set, then max possible will be used... - const char* chan_in_list = ""; - const char* chan_out_list = ""; - bool monitor = false; - const char* capture_driver_uid = ""; - const char* playback_driver_uid = ""; - const JSList *node; - const jack_driver_param_t *param; - jack_nframes_t systemic_input_latency = 0; - jack_nframes_t systemic_output_latency = 0; - int async_output_latency = 100; - int computation_grain = -1; - bool hogged = false; - bool clock_drift = false; - bool ac3_encoding = false; - int ac3_bitrate = 448; - bool ac3_lfe = false; - - for (node = params; node; node = jack_slist_next(node)) { - param = (const jack_driver_param_t *) node->data; - - switch (param->character) { - - case 'd': - capture_driver_uid = param->value.str; - playback_driver_uid = param->value.str; - break; - - case 'D': - capture = true; - playback = true; - break; - - case 'c': - chan_in = chan_out = param->value.i; - break; - - case 'i': - chan_in = param->value.i; - break; - - case 'o': - chan_out = param->value.i; - break; - - case 'n': - chan_in_list = param->value.str; - break; - - case 'N': - chan_out_list = param->value.str; - break; - - case 'C': - capture = true; - if (strcmp(param->value.str, "none") != 0) { - capture_driver_uid = param->value.str; - } - break; - - case 'P': - playback = true; - if (strcmp(param->value.str, "none") != 0) { - playback_driver_uid = param->value.str; - } - break; - - case 'm': - monitor = param->value.i; - break; - - #ifndef __ppc__ - case 'a': - ac3_encoding = param->value.i; - break; - - case 'b': - ac3_bitrate = param->value.i; - break; - - case 'f': - ac3_lfe = param->value.i; - break; - #endif - - case 'r': - srate = param->value.ui; - break; - - case 'p': - frames_per_interrupt = (unsigned int)param->value.ui; - break; - - case 'I': - systemic_input_latency = param->value.ui; - break; - - case 'O': - systemic_output_latency = param->value.ui; - break; - - case 'l': - Jack::DisplayDeviceNames(); - // Stops the server in this case - return NULL; - - case 'H': - hogged = true; - break; - - case 'L': - async_output_latency = param->value.ui; - break; - - case 'G': - computation_grain = param->value.ui; - break; - - case 's': - clock_drift = true; - break; - } - } - - /* duplex is the default */ - if (!capture && !playback) { - capture = true; - playback = true; - } - - if (strcmp(chan_in_list, "") != 0 && chan_in >= 0) { - printf("Input channel list and in channels are both specified, input channel list will take over...\n"); - } - - if (strcmp(chan_out_list, "") != 0 && chan_out >= 0) { - printf("Output channel list and out channels are both specified, output channel list will take over...\n"); - } - - Jack::JackCoreAudioDriver* driver = new Jack::JackCoreAudioDriver("system", "coreaudio", engine, table); - if (driver->Open(frames_per_interrupt, - srate, capture, - playback, chan_in, - chan_out, chan_in_list, - chan_out_list, monitor, - capture_driver_uid, - playback_driver_uid, - systemic_input_latency, - systemic_output_latency, - async_output_latency, - computation_grain, - hogged, clock_drift, - ac3_encoding, ac3_bitrate, ac3_lfe) == 0) { - return driver; - } else { - delete driver; - return NULL; - } - } - -#ifdef __cplusplus -} -#endif - - diff -Nru jackd2-1.9.11~20161209/macosx/coreaudio/JackCoreAudioDriver.mm jackd2-1.9.11+git20170722/macosx/coreaudio/JackCoreAudioDriver.mm --- jackd2-1.9.11~20161209/macosx/coreaudio/JackCoreAudioDriver.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coreaudio/JackCoreAudioDriver.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,2723 @@ +/* +Copyright (C) 2004-2008 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackCoreAudioDriver.h" +#include "JackEngineControl.h" +#include "JackMachThread.h" +#include "JackGraphManager.h" +#include "JackError.h" +#include "JackClientControl.h" +#include "JackDriverLoader.h" +#include "JackGlobals.h" +#include "JackTools.h" +#include "JackLockedEngine.h" +#include "JackAC3Encoder.h" + +#include +#include +#include +#include + +namespace Jack +{ + +static void Print4CharCode(const char* msg, long c) +{ + UInt32 __4CC_number = (c); + char __4CC_string[5]; + *((SInt32*)__4CC_string) = EndianU32_NtoB(__4CC_number); + __4CC_string[4] = 0; + jack_log("%s'%s'", (msg), __4CC_string); +} + +static void PrintStreamDesc(AudioStreamBasicDescription *inDesc) +{ + jack_log("- - - - - - - - - - - - - - - - - - - -"); + jack_log(" Sample Rate:%f", inDesc->mSampleRate); + jack_log(" Format ID:%.*s", (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID); + jack_log(" Format Flags:%lX", inDesc->mFormatFlags); + jack_log(" Bytes per Packet:%ld", inDesc->mBytesPerPacket); + jack_log(" Frames per Packet:%ld", inDesc->mFramesPerPacket); + jack_log(" Bytes per Frame:%ld", inDesc->mBytesPerFrame); + jack_log(" Channels per Frame:%ld", inDesc->mChannelsPerFrame); + jack_log(" Bits per Channel:%ld", inDesc->mBitsPerChannel); + jack_log("- - - - - - - - - - - - - - - - - - - -"); +} + +static void printError(OSStatus err) +{ + switch (err) { + case kAudioHardwareNoError: + jack_log("error code : kAudioHardwareNoError"); + break; + case kAudioConverterErr_FormatNotSupported: + jack_log("error code : kAudioConverterErr_FormatNotSupported"); + break; + case kAudioConverterErr_OperationNotSupported: + jack_log("error code : kAudioConverterErr_OperationNotSupported"); + break; + case kAudioConverterErr_PropertyNotSupported: + jack_log("error code : kAudioConverterErr_PropertyNotSupported"); + break; + case kAudioConverterErr_InvalidInputSize: + jack_log("error code : kAudioConverterErr_InvalidInputSize"); + break; + case kAudioConverterErr_InvalidOutputSize: + jack_log("error code : kAudioConverterErr_InvalidOutputSize"); + break; + case kAudioConverterErr_UnspecifiedError: + jack_log("error code : kAudioConverterErr_UnspecifiedError"); + break; + case kAudioConverterErr_BadPropertySizeError: + jack_log("error code : kAudioConverterErr_BadPropertySizeError"); + break; + case kAudioConverterErr_RequiresPacketDescriptionsError: + jack_log("error code : kAudioConverterErr_RequiresPacketDescriptionsError"); + break; + case kAudioConverterErr_InputSampleRateOutOfRange: + jack_log("error code : kAudioConverterErr_InputSampleRateOutOfRange"); + break; + case kAudioConverterErr_OutputSampleRateOutOfRange: + jack_log("error code : kAudioConverterErr_OutputSampleRateOutOfRange"); + break; + case kAudioHardwareNotRunningError: + jack_log("error code : kAudioHardwareNotRunningError"); + break; + case kAudioHardwareUnknownPropertyError: + jack_log("error code : kAudioHardwareUnknownPropertyError"); + break; + case kAudioHardwareIllegalOperationError: + jack_log("error code : kAudioHardwareIllegalOperationError"); + break; + case kAudioHardwareBadDeviceError: + jack_log("error code : kAudioHardwareBadDeviceError"); + break; + case kAudioHardwareBadStreamError: + jack_log("error code : kAudioHardwareBadStreamError"); + break; + case kAudioDeviceUnsupportedFormatError: + jack_log("error code : kAudioDeviceUnsupportedFormatError"); + break; + case kAudioDevicePermissionsError: + jack_log("error code : kAudioDevicePermissionsError"); + break; + case kAudioHardwareBadObjectError: + jack_log("error code : kAudioHardwareBadObjectError"); + break; + case kAudioHardwareUnsupportedOperationError: + jack_log("error code : kAudioHardwareUnsupportedOperationError"); + break; + default: + Print4CharCode("error code : unknown ", err); + break; + } +} + +static bool CheckAvailableDeviceName(const char* device_name, AudioDeviceID* device_id) +{ + UInt32 size; + Boolean isWritable; + int i, deviceNum; + OSStatus err; + + err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); + if (err != noErr) { + return false; + } + + deviceNum = size / sizeof(AudioDeviceID); + AudioDeviceID devices[deviceNum]; + + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); + if (err != noErr) { + return false; + } + + for (i = 0; i < deviceNum; i++) { + char device_name_aux[256]; + + size = 256; + err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name_aux); + if (err != noErr) { + return false; + } + + if (strncmp(device_name_aux, device_name, strlen(device_name)) == 0) { + *device_id = devices[i]; + return true; + } + } + + return false; +} + +static bool CheckAvailableDevice(AudioDeviceID device_id) +{ + UInt32 size; + Boolean isWritable; + int i, deviceNum; + OSStatus err; + + err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); + if (err != noErr) { + return false; + } + + deviceNum = size / sizeof(AudioDeviceID); + AudioDeviceID devices[deviceNum]; + + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); + if (err != noErr) { + return false; + } + + for (i = 0; i < deviceNum; i++) { + if (device_id == devices[i]) { + return true; + } + } + + return false; +} + +static OSStatus DisplayDeviceNames() +{ + UInt32 size; + Boolean isWritable; + int i, deviceNum; + OSStatus err; + CFStringRef UIname; + + err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, &isWritable); + if (err != noErr) { + return err; + } + + deviceNum = size / sizeof(AudioDeviceID); + AudioDeviceID devices[deviceNum]; + + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices); + if (err != noErr) { + return err; + } + + for (i = 0; i < deviceNum; i++) { + char device_name[256]; + char internal_name[256]; + + size = sizeof(CFStringRef); + UIname = NULL; + err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); + if (err == noErr) { + CFStringGetCString(UIname, internal_name, 256, CFStringGetSystemEncoding()); + } else { + goto error; + } + + size = 256; + err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &size, device_name); + if (err != noErr) { + return err; + } + + jack_info("Device ID = \'%d\' name = \'%s\', internal name = \'%s\' (to be used as -C, -P, or -d parameter)", devices[i], device_name, internal_name); + } + + return noErr; + +error: + if (UIname != NULL) { + CFRelease(UIname); + } + return err; +} + +static CFStringRef GetDeviceName(AudioDeviceID id) +{ + UInt32 size = sizeof(CFStringRef); + CFStringRef UIname; + OSStatus err = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); + return (err == noErr) ? UIname : NULL; +} + +static void ParseChannelList(const string& list, vector& result, int max_chan) +{ + stringstream ss(list); + string token; + int chan; + + while (ss >> token) { + istringstream ins; + ins.str(token); + ins >> chan; + if (chan < 0 || chan >= max_chan) { + jack_error("Ignore incorrect channel mapping value = %d", chan); + } else { + result.push_back(chan); + } + } +} + +OSStatus JackCoreAudioDriver::Render(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) +{ + return static_cast(inRefCon)->Render(ioActionFlags, inTimeStamp, ioData); +} + +OSStatus JackCoreAudioDriver::Render(AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, AudioBufferList* ioData) +{ + fActionFags = ioActionFlags; + fCurrentTime = inTimeStamp; + fDriverOutputData = ioData; + + // Setup threaded based log function et get RT thread parameters once... + if (set_threaded_log_function()) { + + jack_log("JackCoreAudioDriver::Render : set_threaded_log_function"); + JackMachThread::GetParams(pthread_self(), &fEngineControl->fPeriod, &fEngineControl->fComputation, &fEngineControl->fConstraint); + + if (fComputationGrain > 0) { + jack_log("JackCoreAudioDriver::Render : RT thread computation setup to %d percent of period", int(fComputationGrain * 100)); + fEngineControl->fComputation = fEngineControl->fPeriod * fComputationGrain; + } + } + + // Signal waiting start function... + fState = true; + + CycleTakeBeginTime(); + + if (Process() < 0) { + jack_error("Process error, stopping driver"); + NotifyFailure(JackFailure | JackBackendError, "Process error, stopping driver"); // Message length limited to JACK_MESSAGE_SIZE + Stop(); + kill(JackTools::GetPID(), SIGINT); + return kAudioHardwareUnsupportedOperationError; + } else { + return noErr; + } +} + +int JackCoreAudioDriver::Read() +{ + if (fCaptureChannels > 0) { // Calling AudioUnitRender with no input returns a '????' error (callback setting issue ??), so hack to avoid it here... + return (AudioUnitRender(fAUHAL, fActionFags, fCurrentTime, 1, fEngineControl->fBufferSize, fJackInputData) == noErr) ? 0 : -1; + } else { + return 0; + } +} + +int JackCoreAudioDriver::Write() +{ + int size = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize; + + if (fAC3Encoder) { + + // AC3 encoding and SPDIF write + jack_default_audio_sample_t* AC3_inputs[MAX_AC3_CHANNELS]; + jack_default_audio_sample_t* AC3_outputs[2]; + for (int i = 0; i < fPlaybackChannels; i++) { + AC3_inputs[i] = GetOutputBuffer(i); + // If not connected, clear the buffer + if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) == 0) { + memset(AC3_inputs[i], 0, size); + } + } + AC3_outputs[0] = (jack_default_audio_sample_t*)fDriverOutputData->mBuffers[0].mData; + AC3_outputs[1] = (jack_default_audio_sample_t*)fDriverOutputData->mBuffers[1].mData; + fAC3Encoder->Process(AC3_inputs, AC3_outputs, fEngineControl->fBufferSize); + + } else { + + // Standard write + for (int i = 0; i < fPlaybackChannels; i++) { + if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) { + jack_default_audio_sample_t* buffer = GetOutputBuffer(i); + memcpy((jack_default_audio_sample_t*)fDriverOutputData->mBuffers[i].mData, buffer, size); + // Monitor ports + if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[i]) > 0) { + memcpy(GetMonitorBuffer(i), buffer, size); + } + } else { + memset((jack_default_audio_sample_t*)fDriverOutputData->mBuffers[i].mData, 0, size); + } + } + } + return 0; +} + +OSStatus JackCoreAudioDriver::SRNotificationCallback(AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + void* inClientData) +{ + JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData; + + switch (inPropertyID) { + + case kAudioDevicePropertyNominalSampleRate: { + jack_log("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); + // Check new sample rate + Float64 tmp_sample_rate; + UInt32 outSize = sizeof(Float64); + OSStatus err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &tmp_sample_rate); + if (err != noErr) { + jack_error("Cannot get current sample rate"); + printError(err); + } else { + jack_log("JackCoreAudioDriver::SRNotificationCallback : checked sample rate = %f", tmp_sample_rate); + } + driver->fState = true; + break; + } + } + + return noErr; +} + +OSStatus JackCoreAudioDriver::BSNotificationCallback(AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + void* inClientData) +{ + JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData; + + switch (inPropertyID) { + + case kAudioDevicePropertyBufferFrameSize: { + jack_log("JackCoreAudioDriver::BSNotificationCallback kAudioDevicePropertyBufferFrameSize"); + // Check new buffer size + UInt32 tmp_buffer_size; + UInt32 outSize = sizeof(UInt32); + OSStatus err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, &outSize, &tmp_buffer_size); + if (err != noErr) { + jack_error("Cannot get current buffer size"); + printError(err); + } else { + jack_log("JackCoreAudioDriver::BSNotificationCallback : checked buffer size = %d", tmp_buffer_size); + } + driver->fState = true; + break; + } + } + + return noErr; +} + +// A better implementation would possibly try to recover in case of hardware device change (see HALLAB HLFilePlayerWindowControllerAudioDevicePropertyListenerProc code) +OSStatus JackCoreAudioDriver::AudioHardwareNotificationCallback(AudioHardwarePropertyID inPropertyID, void* inClientData) +{ + JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData; + + switch (inPropertyID) { + + case kAudioHardwarePropertyDevices: { + jack_log("JackCoreAudioDriver::AudioHardwareNotificationCallback kAudioHardwarePropertyDevices"); + DisplayDeviceNames(); + AudioDeviceID captureID, playbackID; + if (CheckAvailableDevice(driver->fDeviceID) || + (CheckAvailableDeviceName(driver->fCaptureUID, &captureID) + && CheckAvailableDeviceName(driver->fPlaybackUID, &playbackID))) { + + } + break; + } + } + + return noErr; +} + +OSStatus JackCoreAudioDriver::DeviceNotificationCallback(AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + void* inClientData) +{ + JackCoreAudioDriver* driver = (JackCoreAudioDriver*)inClientData; + + switch (inPropertyID) { + + case kAudioDevicePropertyDeviceIsRunning: { + UInt32 isrunning = 0; + UInt32 outsize = sizeof(UInt32); + if (AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyDeviceIsRunning, &outsize, &isrunning) == noErr) { + jack_log("JackCoreAudioDriver::DeviceNotificationCallback kAudioDevicePropertyDeviceIsRunning = %d", isrunning); + } + break; + } + + case kAudioDevicePropertyDeviceIsAlive: { + UInt32 isalive = 0; + UInt32 outsize = sizeof(UInt32); + if (AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyDeviceIsAlive, &outsize, &isalive) == noErr) { + jack_log("JackCoreAudioDriver::DeviceNotificationCallback kAudioDevicePropertyDeviceIsAlive = %d", isalive); + } + break; + } + + case kAudioDevicePropertyDeviceHasChanged: { + UInt32 hachanged = 0; + UInt32 outsize = sizeof(UInt32); + if (AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyDeviceHasChanged, &outsize, &hachanged) == noErr) { + jack_log("JackCoreAudioDriver::DeviceNotificationCallback kAudioDevicePropertyDeviceHasChanged = %d", hachanged); + } + break; + } + + case kAudioDeviceProcessorOverload: { + jack_error("DeviceNotificationCallback kAudioDeviceProcessorOverload"); + jack_time_t cur_time = GetMicroSeconds(); + driver->NotifyXRun(cur_time, float(cur_time - driver->fBeginDateUst)); // Better this value than nothing... + break; + } + + case kAudioDevicePropertyStreamConfiguration: { + jack_error("Cannot handle kAudioDevicePropertyStreamConfiguration : server will quit..."); + driver->NotifyFailure(JackFailure | JackBackendError, "Another application has changed the device configuration"); // Message length limited to JACK_MESSAGE_SIZE + driver->CloseAUHAL(); + kill(JackTools::GetPID(), SIGINT); + return kAudioHardwareUnsupportedOperationError; + } + + case kAudioDevicePropertyNominalSampleRate: { + Float64 sample_rate = 0; + UInt32 outsize = sizeof(Float64); + OSStatus err = AudioDeviceGetProperty(driver->fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outsize, &sample_rate); + if (err != noErr) { + return kAudioHardwareUnsupportedOperationError; + } + + char device_name[256]; + const char* digidesign_name = "Digidesign"; + driver->GetDeviceNameFromID(driver->fDeviceID, device_name); + + if (sample_rate != driver->fEngineControl->fSampleRate) { + + // Digidesign hardware, so "special" code : change the SR again here + if (strncmp(device_name, digidesign_name, 10) == 0) { + + jack_log("JackCoreAudioDriver::DeviceNotificationCallback Digidesign HW = %s", device_name); + + // Set sample rate again... + sample_rate = driver->fEngineControl->fSampleRate; + err = AudioDeviceSetProperty(driver->fDeviceID, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outsize, &sample_rate); + if (err != noErr) { + jack_error("Cannot set sample rate = %f", sample_rate); + printError(err); + } else { + jack_log("JackCoreAudioDriver::DeviceNotificationCallback : set sample rate = %f", sample_rate); + } + + // Check new sample rate again... + outsize = sizeof(Float64); + err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outsize, &sample_rate); + if (err != noErr) { + jack_error("Cannot get current sample rate"); + printError(err); + } else { + jack_log("JackCoreAudioDriver::DeviceNotificationCallback : checked sample rate = %f", sample_rate); + } + return noErr; + + } else { + driver->NotifyFailure(JackFailure | JackBackendError, "Another application has changed the sample rate"); // Message length limited to JACK_MESSAGE_SIZE + driver->CloseAUHAL(); + kill(JackTools::GetPID(), SIGINT); + return kAudioHardwareUnsupportedOperationError; + } + } + } + + } + return noErr; +} + +OSStatus JackCoreAudioDriver::GetDeviceIDFromUID(const char* UID, AudioDeviceID* id) +{ + UInt32 size = sizeof(AudioValueTranslation); + CFStringRef inIUD = CFStringCreateWithCString(NULL, UID, CFStringGetSystemEncoding()); + AudioValueTranslation value = { &inIUD, sizeof(CFStringRef), id, sizeof(AudioDeviceID) }; + + if (inIUD == NULL) { + return kAudioHardwareUnspecifiedError; + } else { + OSStatus res = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &value); + CFRelease(inIUD); + jack_log("JackCoreAudioDriver::GetDeviceIDFromUID %s %ld", UID, *id); + return (*id == kAudioDeviceUnknown) ? kAudioHardwareBadDeviceError : res; + } +} + +OSStatus JackCoreAudioDriver::GetDefaultDevice(AudioDeviceID* id) +{ + OSStatus res; + UInt32 theSize = sizeof(UInt32); + AudioDeviceID inDefault; + AudioDeviceID outDefault; + + if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) { + return res; + } + + if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) { + return res; + } + + jack_log("JackCoreAudioDriver::GetDefaultDevice : input = %ld output = %ld", inDefault, outDefault); + + // Get the device only if default input and output are the same + if (inDefault != outDefault) { + jack_error("Default input and output devices are not the same !!"); + return kAudioHardwareBadDeviceError; + } else if (inDefault == 0) { + jack_error("Default input and output devices are null !!"); + return kAudioHardwareBadDeviceError; + } else { + *id = inDefault; + return noErr; + } +} + +OSStatus JackCoreAudioDriver::GetDefaultInputDevice(AudioDeviceID* id) +{ + OSStatus res; + UInt32 theSize = sizeof(UInt32); + AudioDeviceID inDefault; + + if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &theSize, &inDefault)) != noErr) { + return res; + } + + if (inDefault == 0) { + jack_error("Error default input device is 0, will take 'Built-in'..."); + if (CheckAvailableDeviceName("Built-in Microphone", id) + || CheckAvailableDeviceName("Built-in Line", id)) { + jack_log("JackCoreAudioDriver::GetDefaultInputDevice : output = %ld", *id); + return noErr; + } else { + jack_error("Cannot find any input device to use..."); + return -1; + } + } + jack_log("JackCoreAudioDriver::GetDefaultInputDevice : input = %ld ", inDefault); + *id = inDefault; + return noErr; +} + +OSStatus JackCoreAudioDriver::GetDefaultOutputDevice(AudioDeviceID* id) +{ + OSStatus res; + UInt32 theSize = sizeof(UInt32); + AudioDeviceID outDefault; + + if ((res = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &theSize, &outDefault)) != noErr) { + return res; + } + + if (outDefault == 0) { + jack_error("Error default ouput device is 0, will take 'Built-in'..."); + if (CheckAvailableDeviceName("Built-in Output", id)) { + jack_log("JackCoreAudioDriver::GetDefaultOutputDevice : output = %ld", *id); + return noErr; + } else { + jack_error("Cannot find any output device to use..."); + return -1; + } + } + jack_log("JackCoreAudioDriver::GetDefaultOutputDevice : output = %ld", outDefault); + *id = outDefault; + return noErr; +} + +OSStatus JackCoreAudioDriver::GetDeviceNameFromID(AudioDeviceID id, char* name) +{ + UInt32 size = 256; + return AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceName, &size, name); +} + +OSStatus JackCoreAudioDriver::GetTotalChannels(AudioDeviceID device, int& channelCount, bool isInput) +{ + OSStatus err = noErr; + UInt32 outSize; + Boolean outWritable; + + channelCount = 0; + err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable); + if (err == noErr) { + int stream_count = outSize / sizeof(AudioBufferList); + jack_log("JackCoreAudioDriver::GetTotalChannels stream_count = %d", stream_count); + AudioBufferList bufferList[stream_count]; + err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, bufferList); + if (err == noErr) { + for (uint i = 0; i < bufferList->mNumberBuffers; i++) { + channelCount += bufferList->mBuffers[i].mNumberChannels; + jack_log("JackCoreAudioDriver::GetTotalChannels stream = %d channels = %d", i, bufferList->mBuffers[i].mNumberChannels); + } + } + } + return err; +} + +OSStatus JackCoreAudioDriver::GetStreamLatencies(AudioDeviceID device, bool isInput, vector& latencies) +{ + OSStatus err = noErr; + UInt32 outSize1, outSize2, outSize3; + Boolean outWritable; + + err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, &outWritable); + if (err == noErr) { + int stream_count = outSize1 / sizeof(UInt32); + AudioStreamID streamIDs[stream_count]; + AudioBufferList bufferList[stream_count]; + UInt32 streamLatency; + outSize2 = sizeof(UInt32); + + err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, streamIDs); + if (err != noErr) { + jack_error("GetStreamLatencies kAudioDevicePropertyStreams err = %d", err); + return err; + } + + err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, &outWritable); + if (err != noErr) { + jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err); + return err; + } + + for (int i = 0; i < stream_count; i++) { + err = AudioStreamGetProperty(streamIDs[i], 0, kAudioStreamPropertyLatency, &outSize2, &streamLatency); + if (err != noErr) { + jack_error("GetStreamLatencies kAudioStreamPropertyLatency err = %d", err); + return err; + } + err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, bufferList); + if (err != noErr) { + jack_error("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = %d", err); + return err; + } + // Push 'channel' time the stream latency + for (uint k = 0; k < bufferList->mBuffers[i].mNumberChannels; k++) { + latencies.push_back(streamLatency); + } + } + } + return err; +} + +bool JackCoreAudioDriver::IsDigitalDevice(AudioDeviceID device) +{ + OSStatus err = noErr; + UInt32 outSize1; + bool is_digital = false; + + /* Get a list of all the streams on this device */ + AudioObjectPropertyAddress streamsAddress = { kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster }; + err = AudioObjectGetPropertyDataSize(device, &streamsAddress, 0, NULL, &outSize1); + if (err != noErr) { + jack_error("IsDigitalDevice kAudioDevicePropertyStreams err = %d", err); + return false; + } + + int stream_count = outSize1 / sizeof(AudioStreamID); + AudioStreamID streamIDs[stream_count]; + + err = AudioObjectGetPropertyData(device, &streamsAddress, 0, NULL, &outSize1, streamIDs); + + if (err != noErr) { + jack_error("IsDigitalDevice kAudioDevicePropertyStreams list err = %d", err); + return false; + } + + AudioObjectPropertyAddress physicalFormatsAddress = { kAudioStreamPropertyAvailablePhysicalFormats, kAudioObjectPropertyScopeGlobal, 0 }; + + for (int i = 0; i < stream_count ; i++) { + + /* Find a stream with a cac3 stream */ + int format_num = 0; + + /* Retrieve all the stream formats supported by each output stream */ + err = AudioObjectGetPropertyDataSize(streamIDs[i], &physicalFormatsAddress, 0, NULL, &outSize1); + + if (err != noErr) { + jack_error("IsDigitalDevice kAudioStreamPropertyAvailablePhysicalFormats err = %d", err); + return false; + } + + format_num = outSize1 / sizeof(AudioStreamRangedDescription); + AudioStreamRangedDescription format_list[format_num]; + + err = AudioObjectGetPropertyData(streamIDs[i], &physicalFormatsAddress, 0, NULL, &outSize1, format_list); + + if (err != noErr) { + jack_error("IsDigitalDevice could not get the list of streamformats err = %d", err); + return false; + } + + /* Check if one of the supported formats is a digital format */ + for (int j = 0; j < format_num; j++) { + + PrintStreamDesc(&format_list[j].mFormat); + + if (format_list[j].mFormat.mFormatID == 'IAC3' || + format_list[j].mFormat.mFormatID == 'iac3' || + format_list[j].mFormat.mFormatID == kAudioFormat60958AC3 || + format_list[j].mFormat.mFormatID == kAudioFormatAC3) + { + is_digital = true; + break; + } + } + } + + return is_digital; +} + +JackCoreAudioDriver::JackCoreAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) + : JackAudioDriver(name, alias, engine, table), + fAC3Encoder(NULL), + fJackInputData(NULL), + fDriverOutputData(NULL), + fPluginID(0), + fState(false), + fHogged(false), + fIOUsage(1.f), + fComputationGrain(-1.f), + fClockDriftCompensate(false), + fDigitalPlayback(false) +{} + +JackCoreAudioDriver::~JackCoreAudioDriver() +{ + delete fAC3Encoder; +} + +OSStatus JackCoreAudioDriver::DestroyAggregateDevice() +{ + OSStatus osErr = noErr; + AudioObjectPropertyAddress pluginAOPA; + pluginAOPA.mSelector = kAudioPlugInDestroyAggregateDevice; + pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; + pluginAOPA.mElement = kAudioObjectPropertyElementMaster; + UInt32 outDataSize; + + if (fPluginID > 0) { + + osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); + if (osErr != noErr) { + jack_error("DestroyAggregateDevice : AudioObjectGetPropertyDataSize error"); + printError(osErr); + return osErr; + } + + osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, 0, NULL, &outDataSize, &fDeviceID); + if (osErr != noErr) { + jack_error("DestroyAggregateDevice : AudioObjectGetPropertyData error"); + printError(osErr); + return osErr; + } + + } + + return noErr; +} + +OSStatus JackCoreAudioDriver::CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) +{ + OSStatus err = noErr; + AudioObjectID sub_device[32]; + UInt32 outSize = sizeof(sub_device); + + err = AudioDeviceGetProperty(captureDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); + vector captureDeviceIDArray; + + jack_log("JackCoreAudioDriver::CreateAggregateDevice : input device %d", captureDeviceID); + + if (err != noErr) { + jack_log("JackCoreAudioDriver::CreateAggregateDevice : input device does not have subdevices"); + captureDeviceIDArray.push_back(captureDeviceID); + } else { + int num_devices = outSize / sizeof(AudioObjectID); + jack_log("JackCoreAudioDriver::CreateAggregateDevice : input device has %d subdevices", num_devices); + for (int i = 0; i < num_devices; i++) { + jack_log("JackCoreAudioDriver::CreateAggregateDevice : input sub_device %d", sub_device[i]); + captureDeviceIDArray.push_back(sub_device[i]); + } + } + + outSize = sizeof(sub_device); + err = AudioDeviceGetProperty(playbackDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); + vector playbackDeviceIDArray; + + jack_log("JackCoreAudioDriver::CreateAggregateDevice : output device %d", playbackDeviceID); + + if (err != noErr) { + jack_log("JackCoreAudioDriver::CreateAggregateDevice : output device does not have subdevices"); + playbackDeviceIDArray.push_back(playbackDeviceID); + } else { + int num_devices = outSize / sizeof(AudioObjectID); + jack_log("JackCoreAudioDriver::CreateAggregateDevice : output device has %d subdevices", num_devices); + for (int i = 0; i < num_devices; i++) { + jack_log("JackCoreAudioDriver::CreateAggregateDevice : output sub_device %d", sub_device[i]); + playbackDeviceIDArray.push_back(sub_device[i]); + } + } + + return CreateAggregateDeviceAux(captureDeviceIDArray, playbackDeviceIDArray, samplerate, outAggregateDevice); +} + +OSStatus JackCoreAudioDriver::CreateAggregateDeviceAux(vector captureDeviceID, vector playbackDeviceID, jack_nframes_t samplerate, AudioDeviceID* outAggregateDevice) +{ + OSStatus osErr = noErr; + UInt32 outSize; + Boolean outWritable; + + // Prepare sub-devices for clock drift compensation + // Workaround for bug in the HAL : until 10.6.2 + AudioObjectPropertyAddress theAddressOwned = { kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress theAddressDrift = { kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + UInt32 theQualifierDataSize = sizeof(AudioObjectID); + AudioClassID inClass = kAudioSubDeviceClassID; + void* theQualifierData = &inClass; + UInt32 subDevicesNum = 0; + + //--------------------------------------------------------------------------- + // Setup SR of both devices otherwise creating AD may fail... + //--------------------------------------------------------------------------- + UInt32 keptclockdomain = 0; + UInt32 clockdomain = 0; + outSize = sizeof(UInt32); + bool need_clock_drift_compensation = false; + + for (UInt32 i = 0; i < captureDeviceID.size(); i++) { + if (SetupSampleRateAux(captureDeviceID[i], samplerate) < 0) { + jack_error("CreateAggregateDeviceAux : cannot set SR of input device"); + } else { + // Check clock domain + osErr = AudioDeviceGetProperty(captureDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); + if (osErr != 0) { + jack_error("CreateAggregateDeviceAux : kAudioDevicePropertyClockDomain error"); + printError(osErr); + } else { + keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; + jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : input clockdomain = %d", clockdomain); + if (clockdomain != 0 && clockdomain != keptclockdomain) { + jack_error("CreateAggregateDeviceAux : devices do not share the same clock!! clock drift compensation would be needed..."); + need_clock_drift_compensation = true; + } + } + } + } + + for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { + if (SetupSampleRateAux(playbackDeviceID[i], samplerate) < 0) { + jack_error("CreateAggregateDeviceAux : cannot set SR of output device"); + } else { + // Check clock domain + osErr = AudioDeviceGetProperty(playbackDeviceID[i], 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyClockDomain, &outSize, &clockdomain); + if (osErr != 0) { + jack_error("CreateAggregateDeviceAux : kAudioDevicePropertyClockDomain error"); + printError(osErr); + } else { + keptclockdomain = (keptclockdomain == 0) ? clockdomain : keptclockdomain; + jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : output clockdomain = %d", clockdomain); + if (clockdomain != 0 && clockdomain != keptclockdomain) { + jack_error("CreateAggregateDeviceAux : devices do not share the same clock!! clock drift compensation would be needed..."); + need_clock_drift_compensation = true; + } + } + } + } + + // If no valid clock domain was found, then assume we have to compensate... + if (keptclockdomain == 0) { + need_clock_drift_compensation = true; + } + + //--------------------------------------------------------------------------- + // Start to create a new aggregate by getting the base audio hardware plugin + //--------------------------------------------------------------------------- + + char device_name[256]; + for (UInt32 i = 0; i < captureDeviceID.size(); i++) { + GetDeviceNameFromID(captureDeviceID[i], device_name); + jack_info("Separated input = '%s' ", device_name); + } + + for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { + GetDeviceNameFromID(playbackDeviceID[i], device_name); + jack_info("Separated output = '%s' ", device_name); + } + + osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); + if (osErr != noErr) { + jack_error("CreateAggregateDeviceAux : AudioHardwareGetPropertyInfo kAudioHardwarePropertyPlugInForBundleID error"); + printError(osErr); + return osErr; + } + + AudioValueTranslation pluginAVT; + + CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio"); + + pluginAVT.mInputData = &inBundleRef; + pluginAVT.mInputDataSize = sizeof(inBundleRef); + pluginAVT.mOutputData = &fPluginID; + pluginAVT.mOutputDataSize = sizeof(fPluginID); + + osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); + if (osErr != noErr) { + jack_error("CreateAggregateDeviceAux : AudioHardwareGetProperty kAudioHardwarePropertyPlugInForBundleID error"); + printError(osErr); + return osErr; + } + + //------------------------------------------------- + // Create a CFDictionary for our aggregate device + //------------------------------------------------- + + CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFStringRef AggregateDeviceNameRef = CFSTR("JackDuplex"); + CFStringRef AggregateDeviceUIDRef = CFSTR("com.grame.JackDuplex"); + + // add the name of the device to the dictionary + CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef); + + // add our choice of UID for the aggregate device to the dictionary + CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef); + + // add a "private aggregate key" to the dictionary + int value = 1; + CFNumberRef AggregateDeviceNumberRef = CFNumberCreate(NULL, kCFNumberIntType, &value); + + SInt32 system; + Gestalt(gestaltSystemVersion, &system); + + jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : system version = %x limit = %x", system, 0x00001054); + + // Starting with 10.5.4 systems, the AD can be internal... (better) + if (system < 0x00001054) { + jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : public aggregate device...."); + } else { + jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : private aggregate device...."); + CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceIsPrivateKey), AggregateDeviceNumberRef); + } + + // Prepare sub-devices for clock drift compensation + CFMutableArrayRef subDevicesArrayClock = NULL; + + /* + if (fClockDriftCompensate) { + if (need_clock_drift_compensation) { + jack_info("Clock drift compensation activated..."); + subDevicesArrayClock = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + for (UInt32 i = 0; i < captureDeviceID.size(); i++) { + CFStringRef UID = GetDeviceName(captureDeviceID[i]); + if (UID) { + CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID); + CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef); + //CFRelease(UID); + CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict); + } + } + + for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { + CFStringRef UID = GetDeviceName(playbackDeviceID[i]); + if (UID) { + CFMutableDictionaryRef subdeviceAggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceUIDKey), UID); + CFDictionaryAddValue(subdeviceAggDeviceDict, CFSTR(kAudioSubDeviceDriftCompensationKey), AggregateDeviceNumberRef); + //CFRelease(UID); + CFArrayAppendValue(subDevicesArrayClock, subdeviceAggDeviceDict); + } + } + + // add sub-device clock array for the aggregate device to the dictionary + CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceSubDeviceListKey), subDevicesArrayClock); + } else { + jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)"); + } + } + */ + + //------------------------------------------------- + // Create a CFMutableArray for our sub-device list + //------------------------------------------------- + + // we need to append the UID for each device to a CFMutableArray, so create one here + CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + vector captureDeviceUID; + for (UInt32 i = 0; i < captureDeviceID.size(); i++) { + CFStringRef ref = GetDeviceName(captureDeviceID[i]); + if (ref == NULL) { + return -1; + } + captureDeviceUID.push_back(ref); + // input sub-devices in this example, so append the sub-device's UID to the CFArray + CFArrayAppendValue(subDevicesArray, ref); + } + + vector playbackDeviceUID; + for (UInt32 i = 0; i < playbackDeviceID.size(); i++) { + CFStringRef ref = GetDeviceName(playbackDeviceID[i]); + if (ref == NULL) { + return -1; + } + playbackDeviceUID.push_back(ref); + // output sub-devices in this example, so append the sub-device's UID to the CFArray + CFArrayAppendValue(subDevicesArray, ref); + } + + //----------------------------------------------------------------------- + // Feed the dictionary to the plugin, to create a blank aggregate device + //----------------------------------------------------------------------- + + AudioObjectPropertyAddress pluginAOPA; + pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice; + pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; + pluginAOPA.mElement = kAudioObjectPropertyElementMaster; + UInt32 outDataSize; + + osErr = AudioObjectGetPropertyDataSize(fPluginID, &pluginAOPA, 0, NULL, &outDataSize); + if (osErr != noErr) { + jack_error("CreateAggregateDeviceAux : AudioObjectGetPropertyDataSize error"); + printError(osErr); + goto error; + } + + osErr = AudioObjectGetPropertyData(fPluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); + if (osErr != noErr) { + jack_error("CreateAggregateDeviceAux : AudioObjectGetPropertyData error"); + printError(osErr); + goto error; + } + + // pause for a bit to make sure that everything completed correctly + // this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); + + //------------------------- + // Set the sub-device list + //------------------------- + + pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList; + pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; + pluginAOPA.mElement = kAudioObjectPropertyElementMaster; + outDataSize = sizeof(CFMutableArrayRef); + osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); + if (osErr != noErr) { + jack_error("CreateAggregateDeviceAux : AudioObjectSetPropertyData for sub-device list error"); + printError(osErr); + goto error; + } + + // pause again to give the changes time to take effect + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); + + //----------------------- + // Set the master device + //----------------------- + + // set the master device manually (this is the device which will act as the master clock for the aggregate device) + // pass in the UID of the device you want to use + pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice; + pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; + pluginAOPA.mElement = kAudioObjectPropertyElementMaster; + outDataSize = sizeof(CFStringRef); + osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &playbackDeviceUID[0]); // First playback is master... + if (osErr != noErr) { + jack_error("CreateAggregateDeviceAux : AudioObjectSetPropertyData for master device error"); + printError(osErr); + goto error; + } + + // pause again to give the changes time to take effect + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); + + // Prepare sub-devices for clock drift compensation + // Workaround for bug in the HAL : until 10.6.2 + + if (fClockDriftCompensate) { + if (need_clock_drift_compensation) { + jack_info("Clock drift compensation activated..."); + + // Get the property data size + osErr = AudioObjectGetPropertyDataSize(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize); + if (osErr != noErr) { + jack_error("CreateAggregateDeviceAux kAudioObjectPropertyOwnedObjects error"); + printError(osErr); + } + + // Calculate the number of object IDs + subDevicesNum = outSize / sizeof(AudioObjectID); + jack_info("JackCoreAudioDriver::CreateAggregateDeviceAux clock drift compensation, number of sub-devices = %d", subDevicesNum); + AudioObjectID subDevices[subDevicesNum]; + outSize = sizeof(subDevices); + + osErr = AudioObjectGetPropertyData(*outAggregateDevice, &theAddressOwned, theQualifierDataSize, theQualifierData, &outSize, subDevices); + if (osErr != noErr) { + jack_error("CreateAggregateDeviceAux kAudioObjectPropertyOwnedObjects error"); + printError(osErr); + } + + // Set kAudioSubDevicePropertyDriftCompensation property... + for (UInt32 index = 0; index < subDevicesNum; ++index) { + UInt32 theDriftCompensationValue = 1; + osErr = AudioObjectSetPropertyData(subDevices[index], &theAddressDrift, 0, NULL, sizeof(UInt32), &theDriftCompensationValue); + if (osErr != noErr) { + jack_error("CreateAggregateDeviceAux kAudioSubDevicePropertyDriftCompensation error"); + printError(osErr); + } + } + } else { + jack_info("Clock drift compensation was asked but is not needed (devices use the same clock domain)"); + } + } + + // pause again to give the changes time to take effect + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); + + //---------- + // Clean up + //---------- + + // release the private AD key + CFRelease(AggregateDeviceNumberRef); + + // release the CF objects we have created - we don't need them any more + CFRelease(aggDeviceDict); + CFRelease(subDevicesArray); + + if (subDevicesArrayClock) { + CFRelease(subDevicesArrayClock); + } + + // release the device UID + for (UInt32 i = 0; i < captureDeviceUID.size(); i++) { + CFRelease(captureDeviceUID[i]); + } + + for (UInt32 i = 0; i < playbackDeviceUID.size(); i++) { + CFRelease(playbackDeviceUID[i]); + } + + jack_log("JackCoreAudioDriver::CreateAggregateDeviceAux : new aggregate device %ld", *outAggregateDevice); + return noErr; + +error: + DestroyAggregateDevice(); + return -1; +} + +int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, + const char* playback_driver_uid, + char* capture_driver_name, + char* playback_driver_name, + jack_nframes_t samplerate, + bool ac3_encoding) +{ + capture_driver_name[0] = 0; + playback_driver_name[0] = 0; + + // Duplex + if (strcmp(capture_driver_uid, "") != 0 && strcmp(playback_driver_uid, "") != 0) { + jack_log("JackCoreAudioDriver::SetupDevices : duplex"); + + // Same device for capture and playback... + if (strcmp(capture_driver_uid, playback_driver_uid) == 0) { + + if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { + jack_log("JackCoreAudioDriver::SetupDevices : will take default in/out"); + if (GetDefaultDevice(&fDeviceID) != noErr) { + jack_error("Cannot open default device"); + return -1; + } + } + + if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr || GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { + jack_error("Cannot get device name from device ID"); + return -1; + } + + if (fHogged) { + if (!TakeHogAux(fDeviceID, false)) { + jack_error("Cannot take hog mode"); + } + if (ac3_encoding) { + fDigitalPlayback = IsDigitalDevice(fDeviceID); + } + } + + } else { + + // Creates aggregate device + AudioDeviceID captureID = -1; + AudioDeviceID playbackID = -1; + + if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { + jack_log("JackCoreAudioDriver::SetupDevices : will take default input"); + if (GetDefaultInputDevice(&captureID) != noErr) { + jack_error("Cannot open default input device"); + return -1; + } + } + + if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) { + jack_log("JackCoreAudioDriver::SetupDevices : will take default output"); + if (GetDefaultOutputDevice(&playbackID) != noErr) { + jack_error("Cannot open default output device"); + return -1; + } + } + + if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) { + return -1; + } + + GetDeviceNameFromID(captureID, fCaptureUID); + GetDeviceNameFromID(playbackID, fPlaybackUID); + + if (fHogged) { + if (!TakeHogAux(captureID, true)) { + jack_error("Cannot take hog mode for capture device"); + } + if (!TakeHogAux(playbackID, false)) { + jack_error("Cannot take hog mode for playback device"); + } + if (ac3_encoding) { + fDigitalPlayback = IsDigitalDevice(playbackID); + } + } + + } + + // Capture only + } else if (strcmp(capture_driver_uid, "") != 0) { + + jack_log("JackCoreAudioDriver::SetupDevices : capture only"); + if (GetDeviceIDFromUID(capture_driver_uid, &fDeviceID) != noErr) { + jack_log("JackCoreAudioDriver::SetupDevices : will take default input"); + if (GetDefaultInputDevice(&fDeviceID) != noErr) { + jack_error("Cannot open default input device"); + return -1; + } + } + + if (GetDeviceNameFromID(fDeviceID, capture_driver_name) != noErr) { + jack_error("Cannot get device name from device ID"); + return -1; + } + + if (fHogged) { + if (!TakeHogAux(fDeviceID, true)) { + jack_error("Cannot take hog mode for capture device"); + } + } + + // Playback only + } else if (strcmp(playback_driver_uid, "") != 0) { + + jack_log("JackCoreAudioDriver::SetupDevices : playback only"); + if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { + jack_log("JackCoreAudioDriver::SetupDevices : will take default output"); + if (GetDefaultOutputDevice(&fDeviceID) != noErr) { + jack_error("Cannot open default output device"); + return -1; + } + } + + if (GetDeviceNameFromID(fDeviceID, playback_driver_name) != noErr) { + jack_error("Cannot get device name from device ID"); + return -1; + } + + if (fHogged) { + if (!TakeHogAux(fDeviceID, false)) { + jack_error("Cannot take hog mode for playback device"); + } + if (ac3_encoding) { + fDigitalPlayback = IsDigitalDevice(fDeviceID); + } + } + + // Use default driver in duplex mode + } else { + jack_log("JackCoreAudioDriver::SetupDevices : default driver"); + if (GetDefaultDevice(&fDeviceID) != noErr) { + jack_error("Cannot open default device in duplex mode, so aggregate default input and default output"); + + // Creates aggregate device + AudioDeviceID captureID = -1; + AudioDeviceID playbackID = -1; + + if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) { + jack_log("JackCoreAudioDriver::SetupDevices : will take default input"); + if (GetDefaultInputDevice(&captureID) != noErr) { + jack_error("Cannot open default input device"); + return -1; + } + } + + if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) { + jack_log("JackCoreAudioDriver::SetupDevices : will take default output"); + if (GetDefaultOutputDevice(&playbackID) != noErr) { + jack_error("Cannot open default output device"); + return -1; + } + } + + if (CreateAggregateDevice(captureID, playbackID, samplerate, &fDeviceID) != noErr) { + return -1; + } + + GetDeviceNameFromID(captureID, fCaptureUID); + GetDeviceNameFromID(playbackID, fPlaybackUID); + + if (fHogged) { + if (!TakeHogAux(captureID, true)) { + jack_error("Cannot take hog mode for capture device"); + } + if (!TakeHogAux(playbackID, false)) { + jack_error("Cannot take hog mode for playback device"); + } + if (ac3_encoding) { + fDigitalPlayback = IsDigitalDevice(playbackID); + } + } + } + } + + return 0; +} + +/* +Return the max possible input channels in in_maxChannels and output channels in out_maxChannels. +*/ +int JackCoreAudioDriver::SetupChannels(bool capturing, bool playing, int& inchannels, int& outchannels, int& in_maxChannels, int& out_maxChannels, bool strict) +{ + OSStatus err = noErr; + + jack_log("JackCoreAudioDriver::SetupChannels : fDeviceID = %d", fDeviceID); + + if (capturing) { + err = GetTotalChannels(fDeviceID, in_maxChannels, true); + if (err != noErr) { + jack_error("SetupChannels : cannot get input channel number"); + printError(err); + return -1; + } else { + jack_log("JackCoreAudioDriver::SetupChannels : max input channels : %d", in_maxChannels); + } + } + + if (playing) { + err = GetTotalChannels(fDeviceID, out_maxChannels, false); + if (err != noErr) { + jack_error("Cannot get output channel number"); + printError(err); + return -1; + } else { + jack_log("JackCoreAudioDriver::SetupChannels : max output channels : %d", out_maxChannels); + } + } + + if (inchannels > in_maxChannels) { + jack_error("This device hasn't required input channels inchannels = %d in_maxChannels = %d", inchannels, in_maxChannels); + if (strict) { + return -1; + } + } + + if (outchannels > out_maxChannels) { + jack_error("This device hasn't required output channels outchannels = %d out_maxChannels = %d", outchannels, out_maxChannels); + if (strict) { + return -1; + } + } + + if (inchannels == -1) { + jack_log("JackCoreAudioDriver::SetupChannels : setup max in channels = %d", in_maxChannels); + inchannels = in_maxChannels; + } + + if (outchannels == -1) { + jack_log("JackCoreAudioDriver::SetupChannels : setup max out channels = %d", out_maxChannels); + outchannels = out_maxChannels; + } + + return 0; +} + +int JackCoreAudioDriver::SetupBufferSize(jack_nframes_t buffer_size) +{ + // Setting buffer size + OSStatus err = noErr; + UInt32 tmp_buffer_size = buffer_size; + UInt32 outSize = sizeof(UInt32); + + err = AudioDeviceGetProperty(fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, &outSize, &tmp_buffer_size); + if (err != noErr) { + jack_error("Cannot get buffer size %ld", buffer_size); + printError(err); + return -1; + } else { + jack_log("JackCoreAudioDriver::SetupBufferSize : current buffer size = %ld", tmp_buffer_size); + } + + // If needed, set new buffer size + if (buffer_size != tmp_buffer_size) { + tmp_buffer_size = buffer_size; + + // To get BS change notification + err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyBufferFrameSize, BSNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyBufferFrameSize"); + printError(err); + return -1; + } + + // Waiting for BS change notification + int count = 0; + fState = false; + + err = AudioDeviceSetProperty(fDeviceID, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, outSize, &tmp_buffer_size); + if (err != noErr) { + jack_error("SetupBufferSize : cannot set buffer size = %ld", tmp_buffer_size); + printError(err); + goto error; + } + + while (!fState && count++ < WAIT_NOTIFICATION_COUNTER) { + usleep(100000); + jack_log("JackCoreAudioDriver::SetupBufferSize : wait count = %d", count); + } + + if (count >= WAIT_NOTIFICATION_COUNTER) { + jack_error("Did not get buffer size notification..."); + goto error; + } + + // Check new buffer size + outSize = sizeof(UInt32); + err = AudioDeviceGetProperty(fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyBufferFrameSize, &outSize, &tmp_buffer_size); + if (err != noErr) { + jack_error("Cannot get current buffer size"); + printError(err); + } else { + jack_log("JackCoreAudioDriver::SetupBufferSize : checked buffer size = %ld", tmp_buffer_size); + } + + // Remove BS change notification + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyBufferFrameSize, BSNotificationCallback); + } + + return 0; + +error: + + // Remove BS change notification + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyBufferFrameSize, BSNotificationCallback); + return -1; +} + +int JackCoreAudioDriver::SetupSampleRate(jack_nframes_t sample_rate) +{ + return SetupSampleRateAux(fDeviceID, sample_rate); +} + +int JackCoreAudioDriver::SetupSampleRateAux(AudioDeviceID inDevice, jack_nframes_t sample_rate) +{ + OSStatus err = noErr; + UInt32 outSize; + Float64 tmp_sample_rate; + + // Get sample rate + outSize = sizeof(Float64); + err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &tmp_sample_rate); + if (err != noErr) { + jack_error("Cannot get current sample rate"); + printError(err); + return -1; + } else { + jack_log("JackCoreAudioDriver::SetupSampleRateAux : current sample rate = %f", tmp_sample_rate); + } + + // If needed, set new sample rate + if (sample_rate != (jack_nframes_t)tmp_sample_rate) { + tmp_sample_rate = (Float64)sample_rate; + + // To get SR change notification + err = AudioDeviceAddPropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); + printError(err); + return -1; + } + + // Waiting for SR change notification + int count = 0; + fState = false; + + err = AudioDeviceSetProperty(inDevice, NULL, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, outSize, &tmp_sample_rate); + if (err != noErr) { + jack_error("Cannot set sample rate = %ld", sample_rate); + printError(err); + goto error; + } + + while (!fState && count++ < WAIT_NOTIFICATION_COUNTER) { + usleep(100000); + jack_log("JackCoreAudioDriver::SetupSampleRateAux : wait count = %d", count); + } + + if (count >= WAIT_NOTIFICATION_COUNTER) { + jack_error("Did not get sample rate notification..."); + goto error; + } + + // Check new sample rate + outSize = sizeof(Float64); + err = AudioDeviceGetProperty(inDevice, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &tmp_sample_rate); + if (err != noErr) { + jack_error("Cannot get current sample rate"); + printError(err); + } else { + jack_log("JackCoreAudioDriver::SetupSampleRateAux : checked sample rate = %f", tmp_sample_rate); + } + + // Remove SR change notification + AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback); + } + + return 0; + +error: + + // Remove SR change notification + AudioDeviceRemovePropertyListener(inDevice, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback); + return -1; +} + +int JackCoreAudioDriver::OpenAUHAL(bool capturing, + bool playing, + int inchannels, + int outchannels, + int in_maxChannels, + int out_maxChannels, + const vector& chan_in_list, + const vector& chan_out_list, + jack_nframes_t buffer_size, + jack_nframes_t sample_rate) +{ + ComponentResult err1; + UInt32 enableIO; + AudioStreamBasicDescription srcFormat, dstFormat; + AudioDeviceID currAudioDeviceID; + UInt32 size; + + jack_log("JackCoreAudioDriver::OpenAUHAL : capturing = %d playing = %d inchannels = %d outchannels = %d in_maxChannels = %d out_maxChannels = %d chan_in_list = %d chan_out_list = %d", + capturing, playing, inchannels, outchannels, in_maxChannels, out_maxChannels, chan_in_list.size(), chan_out_list.size()); + + if (inchannels == 0 && outchannels == 0) { + jack_error("No input and output channels..."); + return -1; + } + + // AUHAL +#ifdef MAC_OS_X_VERSION_10_5 + ComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0}; + Component HALOutput = FindNextComponent(NULL, &cd); + err1 = OpenAComponent(HALOutput, &fAUHAL); + if (err1 != noErr) { + jack_error("Error calling OpenAComponent"); + printError(err1); + goto error; + } +#else + AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0}; + AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd); + err1 = AudioComponentInstanceNew(HALOutput, &fAUHAL); + if (err1 != noErr) { + jack_error("Error calling AudioComponentInstanceNew"); + printError(err1); + goto error; + } +#endif + + err1 = AudioUnitInitialize(fAUHAL); + if (err1 != noErr) { + jack_error("Cannot initialize AUHAL unit"); + printError(err1); + goto error; + } + + // Start I/O + if (capturing && inchannels > 0) { + enableIO = 1; + jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL input on"); + } else { + enableIO = 0; + jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL input off"); + } + + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input"); + printError(err1); + goto error; + } + + if (playing && outchannels > 0) { + enableIO = 1; + jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL output on"); + } else { + enableIO = 0; + jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL output off"); + } + + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output"); + printError(err1); + goto error; + } + + size = sizeof(AudioDeviceID); + err1 = AudioUnitGetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &currAudioDeviceID, &size); + if (err1 != noErr) { + jack_error("Error calling AudioUnitGetProperty - kAudioOutputUnitProperty_CurrentDevice"); + printError(err1); + goto error; + } else { + jack_log("JackCoreAudioDriver::OpenAUHAL : AudioUnitGetPropertyCurrentDevice = %d", currAudioDeviceID); + } + + // Setup up choosen device, in both input and output cases + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &fDeviceID, sizeof(AudioDeviceID)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_CurrentDevice"); + printError(err1); + goto error; + } + + // Set buffer size + if (capturing && inchannels > 0) { + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&buffer_size, sizeof(UInt32)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); + printError(err1); + goto error; + } + } + + if (playing && outchannels > 0) { + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&buffer_size, sizeof(UInt32)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); + printError(err1); + goto error; + } + } + + // Setup input channel map + if (capturing && inchannels > 0 && inchannels <= in_maxChannels) { + SInt32 chanArr[in_maxChannels]; + for (int i = 0; i < in_maxChannels; i++) { + chanArr[i] = -1; + } + // Explicit mapping + if (chan_in_list.size() > 0) { + for (uint i = 0; i < chan_in_list.size(); i++) { + int chan = chan_in_list[i]; + if (chan < in_maxChannels) { + // The wanted JACK input index for the 'chan' channel value + chanArr[chan] = i; + jack_info("Input channel = %d ==> JACK input port = %d", chan, i); + } else { + jack_info("Error input channel number is incorrect : %d", chan); + goto error; + } + } + } else { + for (int i = 0; i < inchannels; i++) { + chanArr[i] = i; + jack_info("Input channel = %d ==> JACK input port = %d", chanArr[i], i); + } + } + + AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap , kAudioUnitScope_Input, 1, chanArr, sizeof(SInt32) * in_maxChannels); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap for input"); + printError(err1); + goto error; + } + } + + // Setup output channel map + if (playing && outchannels > 0 && outchannels <= out_maxChannels) { + SInt32 chanArr[out_maxChannels]; + for (int i = 0; i < out_maxChannels; i++) { + chanArr[i] = -1; + } + // Explicit mapping + if (chan_out_list.size() > 0) { + for (uint i = 0; i < chan_out_list.size(); i++) { + int chan = chan_out_list[i]; + if (chan < out_maxChannels) { + // The wanted JACK output index for the 'chan' channel value + chanArr[chan] = i; + jack_info("JACK output port = %d ==> output channel = %d", i, chan); + } else { + jack_info("Error output channel number is incorrect : %d", chan); + goto error; + } + } + } else { + for (int i = 0; i < outchannels; i++) { + chanArr[i] = i; + jack_info("JACK output port = %d ==> output channel = %d", i, chanArr[i]); + } + } + + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Output, 0, chanArr, sizeof(SInt32) * out_maxChannels); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_ChannelMap for output"); + printError(err1); + goto error; + } + } + + // Setup stream converters + if (capturing && inchannels > 0) { + + size = sizeof(AudioStreamBasicDescription); + err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, &size); + if (err1 != noErr) { + jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); + printError(err1); + goto error; + } + PrintStreamDesc(&srcFormat); + + jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL input stream converter SR = %ld", sample_rate); + srcFormat.mSampleRate = sample_rate; + srcFormat.mFormatID = kAudioFormatLinearPCM; + srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; + srcFormat.mBytesPerPacket = sizeof(jack_default_audio_sample_t); + srcFormat.mFramesPerPacket = 1; + srcFormat.mBytesPerFrame = sizeof(jack_default_audio_sample_t); + srcFormat.mChannelsPerFrame = inchannels; + srcFormat.mBitsPerChannel = 32; + PrintStreamDesc(&srcFormat); + + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); + printError(err1); + goto error; + } + } + + if (playing && outchannels > 0) { + + size = sizeof(AudioStreamBasicDescription); + err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, &size); + if (err1 != noErr) { + jack_error("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); + printError(err1); + goto error; + } + PrintStreamDesc(&dstFormat); + + jack_log("JackCoreAudioDriver::OpenAUHAL : setup AUHAL output stream converter SR = %ld", sample_rate); + dstFormat.mSampleRate = sample_rate; + dstFormat.mFormatID = kAudioFormatLinearPCM; + dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; + dstFormat.mBytesPerPacket = sizeof(jack_default_audio_sample_t); + dstFormat.mFramesPerPacket = 1; + dstFormat.mBytesPerFrame = sizeof(jack_default_audio_sample_t); + dstFormat.mChannelsPerFrame = outchannels; + dstFormat.mBitsPerChannel = 32; + PrintStreamDesc(&dstFormat); + + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); + printError(err1); + goto error; + } + } + + // Setup callbacks + if (inchannels > 0 && outchannels == 0) { + AURenderCallbackStruct output; + output.inputProc = Render; + output.inputProcRefCon = this; + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1"); + printError(err1); + goto error; + } + } else { + AURenderCallbackStruct output; + output.inputProc = Render; + output.inputProcRefCon = this; + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0"); + printError(err1); + goto error; + } + } + + return 0; + +error: + CloseAUHAL(); + return -1; +} + +int JackCoreAudioDriver::SetupBuffers(int inchannels) +{ + // Prepare buffers + fJackInputData = (AudioBufferList*)malloc(sizeof(UInt32) + inchannels * sizeof(AudioBuffer)); + fJackInputData->mNumberBuffers = inchannels; + for (int i = 0; i < inchannels; i++) { + fJackInputData->mBuffers[i].mNumberChannels = 1; + fJackInputData->mBuffers[i].mDataByteSize = fEngineControl->fBufferSize * sizeof(jack_default_audio_sample_t); + } + return 0; +} + +void JackCoreAudioDriver::DisposeBuffers() +{ + if (fJackInputData) { + free(fJackInputData); + fJackInputData = 0; + } +} + +void JackCoreAudioDriver::CloseAUHAL() +{ + AudioOutputUnitStop(fAUHAL); + AudioUnitUninitialize(fAUHAL); + CloseComponent(fAUHAL); +} + +int JackCoreAudioDriver::AddListeners() +{ + OSStatus err = noErr; + + // Add listeners + err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDeviceProcessorOverload"); + printError(err); + return -1; + } + + err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, AudioHardwareNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioHardwareAddPropertyListener with kAudioHardwarePropertyDevices"); + printError(err); + return -1; + } + + err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); + printError(err); + return -1; + } + + err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceIsRunning"); + printError(err); + return -1; + } + + err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsAlive, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceIsAlive"); + printError(err); + return -1; + } + + err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceHasChanged, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyDeviceHasChanged"); + printError(err); + return -1; + } + + err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration"); + printError(err); + return -1; + } + + err = AudioDeviceAddPropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyStreamConfiguration"); + printError(err); + return -1; + } + + if (!fEngineControl->fSyncMode && fIOUsage != 1.f) { + UInt32 outSize = sizeof(float); + err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyIOCycleUsage, outSize, &fIOUsage); + if (err != noErr) { + jack_error("Error calling AudioDeviceSetProperty kAudioDevicePropertyIOCycleUsage"); + printError(err); + } + } + + return 0; +} + +void JackCoreAudioDriver::RemoveListeners() +{ + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback); + AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, AudioHardwareNotificationCallback); + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceNotificationCallback); + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceNotificationCallback); + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceIsAlive, DeviceNotificationCallback); + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyDeviceHasChanged, DeviceNotificationCallback); + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback); + AudioDeviceRemovePropertyListener(fDeviceID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceNotificationCallback); +} + +int JackCoreAudioDriver::Open(jack_nframes_t buffer_size, + jack_nframes_t sample_rate, + bool capturing, + bool playing, + int inchannels, + int outchannels, + const char* chan_in_list, + const char* chan_out_list, + bool monitor, + const char* capture_driver_uid, + const char* playback_driver_uid, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency, + int async_output_latency, + int computation_grain, + bool hogged, + bool clock_drift, + bool ac3_encoding, + int ac3_bitrate, + bool ac3_lfe) +{ + int in_maxChannels = 0; + int out_maxChannels = 0; + char capture_driver_name[256]; + char playback_driver_name[256]; + + fCaptureLatency = capture_latency; + fPlaybackLatency = playback_latency; + fIOUsage = float(async_output_latency) / 100.f; + fComputationGrain = float(computation_grain) / 100.f; + fHogged = hogged; + fClockDriftCompensate = clock_drift; + + SInt32 major; + SInt32 minor; + Gestalt(gestaltSystemVersionMajor, &major); + Gestalt(gestaltSystemVersionMinor, &minor); + + vector parsed_chan_in_list; + vector parsed_chan_out_list; + + // Starting with 10.6 systems, the HAL notification thread is created internally + if (major == 10 && minor >= 6) { + CFRunLoopRef theRunLoop = NULL; + AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + OSStatus osErr = AudioObjectSetPropertyData (kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); + if (osErr != noErr) { + jack_error("Open kAudioHardwarePropertyRunLoop error"); + printError(osErr); + } + } + + if (SetupDevices(capture_driver_uid, playback_driver_uid, capture_driver_name, playback_driver_name, sample_rate, ac3_encoding) < 0) { + goto error; + } + + // Generic JackAudioDriver Open + if (JackAudioDriver::Open(buffer_size, sample_rate, + capturing, playing, + inchannels, outchannels, + monitor, + capture_driver_name, + playback_driver_name, + capture_latency, + playback_latency) != 0) { + goto error; + } + + if (SetupChannels(capturing, playing, inchannels, outchannels, in_maxChannels, out_maxChannels, !ac3_encoding) < 0) { + goto error; + } + + ParseChannelList(chan_in_list, parsed_chan_in_list, in_maxChannels); + if (parsed_chan_in_list.size() > 0) { + jack_info("Explicit input channel list size = %d", parsed_chan_in_list.size()); + inchannels = parsed_chan_in_list.size(); + } + + ParseChannelList(chan_out_list, parsed_chan_out_list, out_maxChannels); + if (parsed_chan_out_list.size() > 0) { + jack_info("Explicit output channel list size = %d", parsed_chan_out_list.size()); + outchannels = parsed_chan_out_list.size(); + } + + if (SetupBufferSize(buffer_size) < 0) { + goto error; + } + + if (SetupSampleRate(sample_rate) < 0) { + goto error; + } + + if (ac3_encoding) { + + if (!fDigitalPlayback) { + jack_error("AC3 encoding can only be used with a digital device"); + goto error; + } + + JackAC3EncoderParams params; + memset(¶ms, 0, sizeof(JackAC3EncoderParams)); + params.bitrate = ac3_bitrate; + params.channels = outchannels; + params.sample_rate = sample_rate; + params.lfe = ac3_lfe; + fAC3Encoder = new JackAC3Encoder(params); + + if (!fAC3Encoder || !fAC3Encoder->Init(sample_rate)) { + jack_error("Cannot allocate or init AC3 encoder"); + goto error; + } + + // Setup AC3 channel number + fPlaybackChannels = outchannels; + if (ac3_lfe) { + fPlaybackChannels++; + } + + if (fPlaybackChannels < 2 || fPlaybackChannels > 6) { + jack_error("AC3 encoder channels must be between 2 and 6"); + goto error; + } + + // Force real output channel number to 2 + outchannels = out_maxChannels = 2; + + } else { + fPlaybackChannels = outchannels; + } + + // Core driver may have changed the in/out values + fCaptureChannels = inchannels; + + if (OpenAUHAL(capturing, playing, inchannels, outchannels, in_maxChannels, out_maxChannels, parsed_chan_in_list, parsed_chan_out_list, buffer_size, sample_rate) < 0) { + goto error; + } + + if (capturing && inchannels > 0) { + if (SetupBuffers(inchannels) < 0) { + goto error; + } + } + + if (AddListeners() < 0) { + goto error; + } + + return noErr; + +error: + Close(); + return -1; +} + +int JackCoreAudioDriver::Close() +{ + jack_log("JackCoreAudioDriver::Close"); + + // Generic audio driver close + int res = JackAudioDriver::Close(); + + RemoveListeners(); + DisposeBuffers(); + CloseAUHAL(); + DestroyAggregateDevice(); + return res; +} + +void JackCoreAudioDriver::UpdateLatencies() +{ + UInt32 size; + OSStatus err; + jack_latency_range_t input_range; + jack_latency_range_t output_range; + jack_latency_range_t monitor_range; + + // Get Input latency + size = sizeof(UInt32); + UInt32 value1 = 0; + UInt32 value2 = 0; + err = AudioDeviceGetProperty(fDeviceID, 0, true, kAudioDevicePropertyLatency, &size, &value1); + if (err != noErr) { + jack_error("AudioDeviceGetProperty kAudioDevicePropertyLatency error"); + } + err = AudioDeviceGetProperty(fDeviceID, 0, true, kAudioDevicePropertySafetyOffset, &size, &value2); + if (err != noErr) { + jack_error("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error"); + } + + input_range.min = input_range.max = fEngineControl->fBufferSize + value1 + value2 + fCaptureLatency; + + // Get input stream latencies + vector input_latencies; + err = GetStreamLatencies(fDeviceID, true, input_latencies); + + for (int i = 0; i < fCaptureChannels; i++) { + if (err != noErr) { + input_range.min += input_latencies[i]; + input_range.max += input_latencies[i]; + } + fGraphManager->GetPort(fCapturePortList[i])->SetLatencyRange(JackCaptureLatency, &input_range); + } + + // Get Output latency + size = sizeof(UInt32); + value1 = 0; + value2 = 0; + err = AudioDeviceGetProperty(fDeviceID, 0, false, kAudioDevicePropertyLatency, &size, &value1); + if (err != noErr) { + jack_error("AudioDeviceGetProperty kAudioDevicePropertyLatency error"); + } + err = AudioDeviceGetProperty(fDeviceID, 0, false, kAudioDevicePropertySafetyOffset, &size, &value2); + if (err != noErr) { + jack_error("AudioDeviceGetProperty kAudioDevicePropertySafetyOffset error"); + } + + // Get output stream latencies + vector output_latencies; + err = GetStreamLatencies(fDeviceID, false, output_latencies); + + // Add more latency if "async" mode is used... + output_range.min = output_range.max = fEngineControl->fBufferSize + ((fEngineControl->fSyncMode) + ? 0 : fEngineControl->fBufferSize * fIOUsage) + value1 + value2 + fPlaybackLatency; + + for (int i = 0; i < fPlaybackChannels; i++) { + if (err != noErr) { + output_range.min += output_latencies[i]; + output_range.max += output_latencies[i]; + } + fGraphManager->GetPort(fPlaybackPortList[i])->SetLatencyRange(JackPlaybackLatency, &output_range); + + // Monitor port + if (fWithMonitorPorts) { + monitor_range.min = monitor_range.max = fEngineControl->fBufferSize; + fGraphManager->GetPort(fMonitorPortList[i])->SetLatencyRange(JackCaptureLatency, &monitor_range); + } + } +} + +int JackCoreAudioDriver::Attach() +{ + OSStatus err; + JackPort* port; + jack_port_id_t port_index; + UInt32 size; + Boolean isWritable; + char channel_name[64]; + char name[REAL_JACK_PORT_NAME_SIZE+1]; + char alias[REAL_JACK_PORT_NAME_SIZE+1]; + + jack_log("JackCoreAudioDriver::Attach : fBufferSize %ld fSampleRate %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); + + for (int i = 0; i < fCaptureChannels; i++) { + + err = AudioDeviceGetPropertyInfo(fDeviceID, i + 1, true, kAudioDevicePropertyChannelName, &size, &isWritable); + if (err != noErr) { + jack_log("JackCoreAudioDriver::Attach : AudioDeviceGetPropertyInfo kAudioDevicePropertyChannelName error"); + } + if (err == noErr && size > 0) { + err = AudioDeviceGetProperty(fDeviceID, i + 1, true, kAudioDevicePropertyChannelName, &size, channel_name); + if (err != noErr) { + jack_log("JackCoreAudioDriver::Attach : AudioDeviceGetProperty kAudioDevicePropertyChannelName error"); + } + snprintf(alias, sizeof(alias), "%s:%s:out_%s%u", fAliasName, fCaptureDriverName, channel_name, i + 1); + } else { + snprintf(alias, sizeof(alias), "%s:%s:out%u", fAliasName, fCaptureDriverName, i + 1); + } + + snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, i + 1); + + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { + jack_error("Cannot register port for %s", name); + return -1; + } + + port = fGraphManager->GetPort(port_index); + port->SetAlias(alias); + fCapturePortList[i] = port_index; + } + + for (int i = 0; i < fPlaybackChannels; i++) { + + err = AudioDeviceGetPropertyInfo(fDeviceID, i + 1, false, kAudioDevicePropertyChannelName, &size, &isWritable); + if (err != noErr) { + jack_log("JackCoreAudioDriver::Attach : AudioDeviceGetPropertyInfo kAudioDevicePropertyChannelName error"); + } + if (err == noErr && size > 0) { + err = AudioDeviceGetProperty(fDeviceID, i + 1, false, kAudioDevicePropertyChannelName, &size, channel_name); + if (err != noErr) { + jack_log("JackCoreAudioDriver::Attach : AudioDeviceGetProperty kAudioDevicePropertyChannelName error"); + } + snprintf(alias, sizeof(alias), "%s:%s:in_%s%u", fAliasName, fPlaybackDriverName, channel_name, i + 1); + } else { + snprintf(alias, sizeof(alias), "%s:%s:in%u", fAliasName, fPlaybackDriverName, i + 1); + } + + snprintf(name, sizeof(name), "%s:playback_%d", fClientControl.fName, i + 1); + + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { + jack_error("Cannot register port for %s", name); + return -1; + } + + port = fGraphManager->GetPort(port_index); + port->SetAlias(alias); + fPlaybackPortList[i] = port_index; + + // Monitor ports + if (fWithMonitorPorts) { + jack_log("JackCoreAudioDriver::Attach : create monitor port"); + snprintf(name, sizeof(name), "%s:monitor_%u", fClientControl.fName, i + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, MonitorDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { + jack_error("Cannot register monitor port for %s", name); + return -1; + } else { + fMonitorPortList[i] = port_index; + } + } + } + + if (fAC3Encoder) { + // Setup specific AC3 channels names + for (int i = 0; i < fPlaybackChannels; i++) { + fAC3Encoder->GetChannelName("coreaudio", "", alias, i); + port = fGraphManager->GetPort(fPlaybackPortList[i]); + port->SetAlias(alias); + } + } + + UpdateLatencies(); + + // Input buffers do no change : prepare them only once + for (int i = 0; i < fCaptureChannels; i++) { + fJackInputData->mBuffers[i].mData = GetInputBuffer(i); + } + + return 0; +} + +int JackCoreAudioDriver::Start() +{ + jack_log("JackCoreAudioDriver::Start"); + if (JackAudioDriver::Start() == 0) { + + // Waiting for Render callback to be called (= driver has started) + fState = false; + int count = 0; + + if (AudioOutputUnitStart(fAUHAL) == noErr) { + + while (!fState && count++ < WAIT_COUNTER) { + usleep(100000); + jack_log("JackCoreAudioDriver::Start : wait count = %d", count); + } + + if (count < WAIT_COUNTER) { + jack_info("CoreAudio driver is running..."); + return 0; + } + + jack_error("CoreAudio driver cannot start..."); + } + JackAudioDriver::Stop(); + } + return -1; +} + +int JackCoreAudioDriver::Stop() +{ + jack_log("JackCoreAudioDriver::Stop"); + int res = (AudioOutputUnitStop(fAUHAL) == noErr) ? 0 : -1; + if (JackAudioDriver::Stop() < 0) { + res = -1; + } + return res; +} + +int JackCoreAudioDriver::SetBufferSize(jack_nframes_t buffer_size) +{ + if (SetupBufferSize(buffer_size) < 0) { + return -1; + } + + JackAudioDriver::SetBufferSize(buffer_size); // Generic change, never fails + + // CoreAudio specific + UpdateLatencies(); + + // Input buffers do no change : prepare them only once + for (int i = 0; i < fCaptureChannels; i++) { + fJackInputData->mBuffers[i].mNumberChannels = 1; + fJackInputData->mBuffers[i].mDataByteSize = fEngineControl->fBufferSize * sizeof(jack_default_audio_sample_t); + fJackInputData->mBuffers[i].mData = GetInputBuffer(i); + } + + return 0; +} + +bool JackCoreAudioDriver::TakeHogAux(AudioDeviceID deviceID, bool isInput) +{ + pid_t hog_pid; + UInt32 propSize = sizeof(hog_pid); + + OSStatus err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyHogMode, &propSize, &hog_pid); + if (err) { + jack_error("Cannot read hog state..."); + printError(err); + } + + jack_log("JackCoreAudioDriver::TakeHogAux : deviceID = %d", deviceID); + + if (hog_pid != getpid()) { + hog_pid = getpid(); + err = AudioDeviceSetProperty(deviceID, 0, 0, isInput, kAudioDevicePropertyHogMode, propSize, &hog_pid); + if (err != noErr) { + jack_error("Can't hog device = %d because it's being hogged by another program or cannot be hogged", deviceID); + return false; + } + } + + return true; +} + +bool JackCoreAudioDriver::TakeHog() +{ + OSStatus err = noErr; + AudioObjectID sub_device[32]; + UInt32 outSize = sizeof(sub_device); + err = AudioDeviceGetProperty(fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioAggregateDevicePropertyActiveSubDeviceList, &outSize, sub_device); + + if (err != noErr) { + jack_log("JackCoreAudioDriver::TakeHog : device does not have subdevices"); + return TakeHogAux(fDeviceID, true); + } else { + int num_devices = outSize / sizeof(AudioObjectID); + jack_log("JackCoreAudioDriver::TakeHog : device does has %d subdevices", num_devices); + for (int i = 0; i < num_devices; i++) { + if (!TakeHogAux(sub_device[i], true)) { + return false; + } + } + return true; + } +} + +bool JackCoreAudioDriver::IsAggregateDevice(AudioDeviceID device) +{ + UInt32 deviceType, outSize = sizeof(UInt32); + OSStatus err = AudioDeviceGetProperty(device, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyTransportType, &outSize, &deviceType); + + if (err != noErr) { + jack_log("JackCoreAudioDriver::IsAggregateDevice kAudioDevicePropertyTransportType error"); + return false; + } else { + return (deviceType == kAudioDeviceTransportTypeAggregate); + } +} + + +} // end of namespace + + +#ifdef __cplusplus +extern "C" +{ +#endif + + SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor() + { + jack_driver_desc_t * desc; + jack_driver_desc_filler_t filler; + jack_driver_param_value_t value; + + desc = jack_driver_descriptor_construct("coreaudio", JackDriverMaster, "Apple CoreAudio API based audio backend", &filler); + + value.i = -1; + jack_driver_descriptor_add_parameter(desc, &filler, "channels", 'c', JackDriverParamInt, &value, NULL, "Maximum number of channels", "Maximum number of channels. If -1, max possible number of channels will be used"); + jack_driver_descriptor_add_parameter(desc, &filler, "in-channels", 'i', JackDriverParamInt, &value, NULL, "Maximum number of input channels", "Maximum number of input channels. If -1, max possible number of input channels will be used"); + jack_driver_descriptor_add_parameter(desc, &filler, "out-channels", 'o', JackDriverParamInt, &value, NULL, "Maximum number of output channels", "Maximum number of output channels. If -1, max possible number of output channels will be used"); + + value.str[0] = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "input-list", 'n', JackDriverParamString, &value, NULL, "Input channel list for channel mapping", "List of input channel number to be opened (syntax like : \"0 3 2\")"); + jack_driver_descriptor_add_parameter(desc, &filler, "output-list", 'N', JackDriverParamString, &value, NULL, "Output channel list for channel mapping", "List of output channel number to be opened (syntax like : \"0 3 2\")"); + + value.str[0] = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Input CoreAudio device name", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamString, &value, NULL, "Output CoreAudio device name", NULL); + + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "monitor", 'm', JackDriverParamBool, &value, NULL, "Provide monitor ports for the output", NULL); + +#ifndef __ppc__ + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "AC3-encoding", 'a', JackDriverParamBool, &value, NULL, "AC3 multi-channels encoding", NULL); + + value.i = 448; + jack_driver_descriptor_add_parameter(desc, &filler, "AC3-bitrate", 'b', JackDriverParamUInt, &value, NULL, "AC3 bitrate", NULL); + + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "AC3-LFE", 'f', JackDriverParamBool, &value, NULL, "AC3 LFE channel", NULL); +#endif + value.i = true; + jack_driver_descriptor_add_parameter(desc, &filler, "duplex", 'D', JackDriverParamBool, &value, NULL, "Provide both capture and playback ports", NULL); + + value.ui = 44100U; + jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL); + + value.ui = 256U; + jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL); + + value.str[0] = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "CoreAudio device name", NULL); + + value.ui = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "input-latency", 'I', JackDriverParamUInt, &value, NULL, "Extra input latency (frames)", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "output-latency", 'O', JackDriverParamUInt, &value, NULL, "Extra output latency (frames)", NULL); + + value.i = false; + jack_driver_descriptor_add_parameter(desc, &filler, "list-devices", 'l', JackDriverParamBool, &value, NULL, "Display available CoreAudio devices", NULL); + + value.i = false; + jack_driver_descriptor_add_parameter(desc, &filler, "hog", 'H', JackDriverParamBool, &value, NULL, "Take exclusive access of the audio device", NULL); + + value.ui = 100; + jack_driver_descriptor_add_parameter(desc, &filler, "async-latency", 'L', JackDriverParamUInt, &value, NULL, "Extra output latency in asynchronous mode (percent)", NULL); + + value.ui = 100; + jack_driver_descriptor_add_parameter(desc, &filler, "grain", 'G', JackDriverParamUInt, &value, NULL, "Computation grain in RT thread (percent)", NULL); + + value.i = false; + jack_driver_descriptor_add_parameter(desc, &filler, "clock-drift", 's', JackDriverParamBool, &value, NULL, "Clock drift compensation", "Whether to compensate clock drift in dynamically created aggregate device"); + + return desc; + } + + SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) + { + jack_nframes_t srate = 44100; + jack_nframes_t frames_per_interrupt = 256; + bool capture = false; + bool playback = false; + int chan_in = -1; // Default: if not explicitely set, then max possible will be used... + int chan_out = -1; // Default: if not explicitely set, then max possible will be used... + const char* chan_in_list = ""; + const char* chan_out_list = ""; + bool monitor = false; + const char* capture_driver_uid = ""; + const char* playback_driver_uid = ""; + const JSList *node; + const jack_driver_param_t *param; + jack_nframes_t systemic_input_latency = 0; + jack_nframes_t systemic_output_latency = 0; + int async_output_latency = 100; + int computation_grain = -1; + bool hogged = false; + bool clock_drift = false; + bool ac3_encoding = false; + int ac3_bitrate = 448; + bool ac3_lfe = false; + + for (node = params; node; node = jack_slist_next(node)) { + param = (const jack_driver_param_t *) node->data; + + switch (param->character) { + + case 'd': + capture_driver_uid = param->value.str; + playback_driver_uid = param->value.str; + break; + + case 'D': + capture = true; + playback = true; + break; + + case 'c': + chan_in = chan_out = param->value.i; + break; + + case 'i': + chan_in = param->value.i; + break; + + case 'o': + chan_out = param->value.i; + break; + + case 'n': + chan_in_list = param->value.str; + break; + + case 'N': + chan_out_list = param->value.str; + break; + + case 'C': + capture = true; + if (strcmp(param->value.str, "none") != 0) { + capture_driver_uid = param->value.str; + } + break; + + case 'P': + playback = true; + if (strcmp(param->value.str, "none") != 0) { + playback_driver_uid = param->value.str; + } + break; + + case 'm': + monitor = param->value.i; + break; + + #ifndef __ppc__ + case 'a': + ac3_encoding = param->value.i; + break; + + case 'b': + ac3_bitrate = param->value.i; + break; + + case 'f': + ac3_lfe = param->value.i; + break; + #endif + + case 'r': + srate = param->value.ui; + break; + + case 'p': + frames_per_interrupt = (unsigned int)param->value.ui; + break; + + case 'I': + systemic_input_latency = param->value.ui; + break; + + case 'O': + systemic_output_latency = param->value.ui; + break; + + case 'l': + Jack::DisplayDeviceNames(); + // Stops the server in this case + return NULL; + + case 'H': + hogged = true; + break; + + case 'L': + async_output_latency = param->value.ui; + break; + + case 'G': + computation_grain = param->value.ui; + break; + + case 's': + clock_drift = true; + break; + } + } + + /* duplex is the default */ + if (!capture && !playback) { + capture = true; + playback = true; + } + + if (strcmp(chan_in_list, "") != 0 && chan_in >= 0) { + printf("Input channel list and in channels are both specified, input channel list will take over...\n"); + } + + if (strcmp(chan_out_list, "") != 0 && chan_out >= 0) { + printf("Output channel list and out channels are both specified, output channel list will take over...\n"); + } + + Jack::JackCoreAudioDriver* driver = new Jack::JackCoreAudioDriver("system", "coreaudio", engine, table); + if (driver->Open(frames_per_interrupt, + srate, capture, + playback, chan_in, + chan_out, chan_in_list, + chan_out_list, monitor, + capture_driver_uid, + playback_driver_uid, + systemic_input_latency, + systemic_output_latency, + async_output_latency, + computation_grain, + hogged, clock_drift, + ac3_encoding, ac3_bitrate, ac3_lfe) == 0) { + return driver; + } else { + delete driver; + return NULL; + } + } + +#ifdef __cplusplus +} +#endif + + diff -Nru jackd2-1.9.11~20161209/macosx/coreaudio/TiPhoneCoreAudioRenderer.cpp jackd2-1.9.11+git20170722/macosx/coreaudio/TiPhoneCoreAudioRenderer.cpp --- jackd2-1.9.11~20161209/macosx/coreaudio/TiPhoneCoreAudioRenderer.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coreaudio/TiPhoneCoreAudioRenderer.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,416 +0,0 @@ -/* -Copyright (C) 2010 Grame - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "TiPhoneCoreAudioRenderer.h" - -static void PrintStreamDesc(AudioStreamBasicDescription *inDesc) -{ - printf("- - - - - - - - - - - - - - - - - - - -\n"); - printf(" Sample Rate:%f\n", inDesc->mSampleRate); - printf(" Format ID:%.*s\n", (int) sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID); - printf(" Format Flags:%lX\n", inDesc->mFormatFlags); - printf(" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket); - printf(" Frames per Packet:%ld\n", inDesc->mFramesPerPacket); - printf(" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame); - printf(" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame); - printf(" Bits per Channel:%ld\n", inDesc->mBitsPerChannel); - printf("- - - - - - - - - - - - - - - - - - - -\n"); -} - -static void printError(OSStatus err) -{ - switch (err) { - case kAudioConverterErr_FormatNotSupported: - printf("error code : kAudioConverterErr_FormatNotSupported\n"); - break; - case kAudioConverterErr_OperationNotSupported: - printf("error code : kAudioConverterErr_OperationNotSupported\n"); - break; - case kAudioConverterErr_PropertyNotSupported: - printf("error code : kAudioConverterErr_PropertyNotSupported\n"); - break; - case kAudioConverterErr_InvalidInputSize: - printf("error code : kAudioConverterErr_InvalidInputSize\n"); - break; - case kAudioConverterErr_InvalidOutputSize: - printf("error code : kAudioConverterErr_InvalidOutputSize\n"); - break; - case kAudioConverterErr_UnspecifiedError: - printf("error code : kAudioConverterErr_UnspecifiedError\n"); - break; - case kAudioConverterErr_BadPropertySizeError: - printf("error code : kAudioConverterErr_BadPropertySizeError\n"); - break; - case kAudioConverterErr_RequiresPacketDescriptionsError: - printf("error code : kAudioConverterErr_RequiresPacketDescriptionsError\n"); - break; - case kAudioConverterErr_InputSampleRateOutOfRange: - printf("error code : kAudioConverterErr_InputSampleRateOutOfRange\n"); - break; - case kAudioConverterErr_OutputSampleRateOutOfRange: - printf("error code : kAudioConverterErr_OutputSampleRateOutOfRange\n"); - break; - default: - printf("error code : unknown\n"); - break; - } -} - -OSStatus TiPhoneCoreAudioRenderer::Render(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32, - UInt32 inNumberFrames, - AudioBufferList *ioData) -{ - TiPhoneCoreAudioRendererPtr renderer = (TiPhoneCoreAudioRendererPtr)inRefCon; - AudioUnitRender(renderer->fAUHAL, ioActionFlags, inTimeStamp, 1, inNumberFrames, renderer->fCAInputData); - - float coef = float(LONG_MAX); - float inv_coef = 1.f/float(LONG_MAX); - - for (int chan = 0; chan < renderer->fDevNumInChans; chan++) { - for (int frame = 0; frame < inNumberFrames; frame++) { - renderer->fInChannel[chan][frame] = float(((int*)renderer->fCAInputData->mBuffers[chan].mData)[frame]) * inv_coef; - } - } - - renderer->PerformAudioCallback((int)inNumberFrames); - - for (int chan = 0; chan < renderer->fDevNumOutChans; chan++) { - for (int frame = 0; frame < inNumberFrames; frame++) { - ((int*)ioData->mBuffers[chan].mData)[frame] = int(renderer->fOutChannel[chan][frame] * coef); - } - } - - return 0; -} - -void TiPhoneCoreAudioRenderer::InterruptionListener(void *inClientData, UInt32 inInterruption) -{ - TiPhoneCoreAudioRenderer *obj = (TiPhoneCoreAudioRenderer*)inClientData; - printf("Session interrupted! --- %s ---", inInterruption == kAudioSessionBeginInterruption ? "Begin Interruption" : "End Interruption"); - - if (inInterruption == kAudioSessionEndInterruption) { - // make sure we are again the active session - AudioSessionSetActive(true); - AudioOutputUnitStart(obj->fAUHAL); - } - - if (inInterruption == kAudioSessionBeginInterruption) { - AudioOutputUnitStop(obj->fAUHAL); - } -} - -int TiPhoneCoreAudioRenderer::Open(int bufferSize, int samplerate) -{ - OSStatus err1; - UInt32 outSize; - UInt32 enableIO; - AudioStreamBasicDescription srcFormat, dstFormat; - - printf("Open fDevNumInChans = %ld fDevNumOutChans = %ld bufferSize = %ld samplerate = %ld\n", fDevNumInChans, fDevNumOutChans, bufferSize, samplerate); - - // Initialize and configure the audio session - err1 = AudioSessionInitialize(NULL, NULL, InterruptionListener, this); - if (err1 != noErr) { - printf("Couldn't initialize audio session\n"); - printError(err1); - return OPEN_ERR; - } - - err1 = AudioSessionSetActive(true); - if (err1 != noErr) { - printf("Couldn't set audio session active\n"); - printError(err1); - return OPEN_ERR; - } - - UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord; - err1 = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory); - if (err1 != noErr) { - printf("Couldn't set audio category\n"); - printError(err1); - return OPEN_ERR; - } - - //err1 = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, propListener, self), "couldn't set property listener"); - - Float64 hwSampleRate; - outSize = sizeof(hwSampleRate); - err1 = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &outSize, &hwSampleRate); - if (err1 != noErr) { - printf("Couldn't get hw sample rate\n"); - printError(err1); - return OPEN_ERR; - } else { - printf("Get hw sample rate %f\n", hwSampleRate); - } - - Float32 hwBufferSize; - outSize = sizeof(hwBufferSize); - err1 = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration, &outSize, &hwBufferSize); - if (err1 != noErr) { - printf("Couldn't get hw buffer duration\n"); - printError(err1); - return OPEN_ERR; - } else { - printf("Get hw buffer duration %f\n", hwBufferSize); - } - - UInt32 hwInput; - outSize = sizeof(hwInput); - err1 = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels, &outSize, &hwInput); - if (err1 != noErr) { - printf("Couldn't get hw input channels\n"); - printError(err1); - return OPEN_ERR; - } else { - printf("Get hw input channels %d\n", hwInput); - } - - UInt32 hwOutput; - outSize = sizeof(hwOutput); - err1 = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputNumberChannels, &outSize, &hwOutput); - if (err1 != noErr) { - printf("Couldn't get hw output channels\n"); - printError(err1); - return OPEN_ERR; - } else { - printf("Get hw output channels %d\n", hwOutput); - } - - Float32 preferredBufferSize = float(bufferSize) / float(samplerate); - printf("preferredBufferSize %f \n", preferredBufferSize); - - err1 = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(preferredBufferSize), &preferredBufferSize); - if (err1 != noErr) { - printf("Couldn't set i/o buffer duration\n"); - printError(err1); - return OPEN_ERR; - } - - Float64 preferredSamplerate = float(samplerate); - err1 = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareSampleRate, sizeof(preferredSamplerate), &preferredSamplerate); - if (err1 != noErr) { - printf("Couldn't set i/o sample rate\n"); - printError(err1); - return OPEN_ERR; - } - - // AUHAL - AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_RemoteIO, kAudioUnitManufacturer_Apple, 0, 0}; - AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd); - - err1 = AudioComponentInstanceNew(HALOutput, &fAUHAL); - if (err1 != noErr) { - printf("Error calling OpenAComponent\n"); - printError(err1); - goto error; - } - - enableIO = 1; - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO)); - if (err1 != noErr) { - printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output\n"); - printError(err1); - goto error; - } - - enableIO = 1; - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO)); - if (err1 != noErr) { - printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input\n"); - printError(err1); - goto error; - } - - UInt32 maxFPS; - outSize = sizeof(maxFPS); - err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFPS, &outSize); - if (err1 != noErr) { - printf("Couldn't get kAudioUnitProperty_MaximumFramesPerSlice\n"); - printError(err1); - goto error; - } else { - printf("Get kAudioUnitProperty_MaximumFramesPerSlice %d\n", maxFPS); - } - - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&bufferSize, sizeof(UInt32)); - if (err1 != noErr) { - printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice\n"); - printError(err1); - goto error; - } - - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&bufferSize, sizeof(UInt32)); - if (err1 != noErr) { - printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice\n"); - printError(err1); - goto error; - } - - err1 = AudioUnitInitialize(fAUHAL); - if (err1 != noErr) { - printf("Cannot initialize AUHAL unit\n"); - printError(err1); - goto error; - } - - // Setting format - - if (fDevNumInChans > 0) { - outSize = sizeof(AudioStreamBasicDescription); - err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, &outSize); - if (err1 != noErr) { - printf("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n"); - printError(err1); - } - PrintStreamDesc(&srcFormat); - - srcFormat.mFormatID = kAudioFormatLinearPCM; - srcFormat.mFormatFlags = kAudioFormatFlagsCanonical | kLinearPCMFormatFlagIsNonInterleaved; - srcFormat.mBytesPerPacket = sizeof(AudioUnitSampleType); - srcFormat.mFramesPerPacket = 1; - srcFormat.mBytesPerFrame = sizeof(AudioUnitSampleType); - srcFormat.mChannelsPerFrame = fDevNumInChans; - srcFormat.mBitsPerChannel = 32; - - PrintStreamDesc(&srcFormat); - - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription)); - if (err1 != noErr) { - printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n"); - printError(err1); - } - } - - if (fDevNumOutChans > 0) { - outSize = sizeof(AudioStreamBasicDescription); - err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, &outSize); - if (err1 != noErr) { - printf("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input\n"); - printError(err1); - } - PrintStreamDesc(&dstFormat); - - dstFormat.mFormatID = kAudioFormatLinearPCM; - dstFormat.mFormatFlags = kAudioFormatFlagsCanonical | kLinearPCMFormatFlagIsNonInterleaved; - dstFormat.mBytesPerPacket = sizeof(AudioUnitSampleType); - dstFormat.mFramesPerPacket = 1; - dstFormat.mBytesPerFrame = sizeof(AudioUnitSampleType); - dstFormat.mChannelsPerFrame = fDevNumOutChans; - dstFormat.mBitsPerChannel = 32; - - PrintStreamDesc(&dstFormat); - - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription)); - if (err1 != noErr) { - printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input\n"); - printError(err1); - } - } - - if (fDevNumInChans > 0 && fDevNumOutChans == 0) { - AURenderCallbackStruct output; - output.inputProc = Render; - output.inputProcRefCon = this; - err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output)); - if (err1 != noErr) { - printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1\n"); - printError(err1); - goto error; - } - } else { - AURenderCallbackStruct output; - output.inputProc = Render; - output.inputProcRefCon = this; - err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output)); - if (err1 != noErr) { - printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0\n"); - printError(err1); - goto error; - } - } - - // Prepare buffers - fCAInputData = (AudioBufferList*)malloc(sizeof(UInt32) + fDevNumInChans * sizeof(AudioBuffer)); - fCAInputData->mNumberBuffers = fDevNumInChans; - for (int i = 0; i < fDevNumInChans; i++) { - fCAInputData->mBuffers[i].mNumberChannels = 1; - fCAInputData->mBuffers[i].mDataByteSize = bufferSize * sizeof(int); - fCAInputData->mBuffers[i].mData = malloc(bufferSize * sizeof(int)); - } - - /* - // Add listeners - err1 = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback, this); - if (err != noErr) { - jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDeviceProcessorOverload"); - printError(err); - return -1; - } - */ - - return NO_ERR; - -error: - AudioUnitUninitialize(fAUHAL); - AudioComponentInstanceDispose(fAUHAL); - return OPEN_ERR; -} - -int TiPhoneCoreAudioRenderer::Close() -{ - AudioUnitUninitialize(fAUHAL); - AudioComponentInstanceDispose(fAUHAL); - if (fCAInputData) { - // Free buffers and list - for (int i = 0; i < fDevNumInChans; i++) { - free(fCAInputData->mBuffers[i].mData); - } - free(fCAInputData); - } - return NO_ERR; -} - -int TiPhoneCoreAudioRenderer::Start() -{ - AudioSessionSetActive(true); - OSStatus err = AudioOutputUnitStart(fAUHAL); - - if (err != noErr) { - printf("Error while opening device : device open error \n"); - return OPEN_ERR; - } else { - return NO_ERR; - } -} - -int TiPhoneCoreAudioRenderer::Stop() -{ - OSStatus err = AudioOutputUnitStop(fAUHAL); - - if (err != noErr) { - printf("Error while closing device : device close error \n"); - return OPEN_ERR; - } else { - return NO_ERR; - } -} diff -Nru jackd2-1.9.11~20161209/macosx/coreaudio/TiPhoneCoreAudioRenderer.mm jackd2-1.9.11+git20170722/macosx/coreaudio/TiPhoneCoreAudioRenderer.mm --- jackd2-1.9.11~20161209/macosx/coreaudio/TiPhoneCoreAudioRenderer.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coreaudio/TiPhoneCoreAudioRenderer.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,416 @@ +/* +Copyright (C) 2010 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "TiPhoneCoreAudioRenderer.h" + +static void PrintStreamDesc(AudioStreamBasicDescription *inDesc) +{ + printf("- - - - - - - - - - - - - - - - - - - -\n"); + printf(" Sample Rate:%f\n", inDesc->mSampleRate); + printf(" Format ID:%.*s\n", (int) sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID); + printf(" Format Flags:%lX\n", inDesc->mFormatFlags); + printf(" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket); + printf(" Frames per Packet:%ld\n", inDesc->mFramesPerPacket); + printf(" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame); + printf(" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame); + printf(" Bits per Channel:%ld\n", inDesc->mBitsPerChannel); + printf("- - - - - - - - - - - - - - - - - - - -\n"); +} + +static void printError(OSStatus err) +{ + switch (err) { + case kAudioConverterErr_FormatNotSupported: + printf("error code : kAudioConverterErr_FormatNotSupported\n"); + break; + case kAudioConverterErr_OperationNotSupported: + printf("error code : kAudioConverterErr_OperationNotSupported\n"); + break; + case kAudioConverterErr_PropertyNotSupported: + printf("error code : kAudioConverterErr_PropertyNotSupported\n"); + break; + case kAudioConverterErr_InvalidInputSize: + printf("error code : kAudioConverterErr_InvalidInputSize\n"); + break; + case kAudioConverterErr_InvalidOutputSize: + printf("error code : kAudioConverterErr_InvalidOutputSize\n"); + break; + case kAudioConverterErr_UnspecifiedError: + printf("error code : kAudioConverterErr_UnspecifiedError\n"); + break; + case kAudioConverterErr_BadPropertySizeError: + printf("error code : kAudioConverterErr_BadPropertySizeError\n"); + break; + case kAudioConverterErr_RequiresPacketDescriptionsError: + printf("error code : kAudioConverterErr_RequiresPacketDescriptionsError\n"); + break; + case kAudioConverterErr_InputSampleRateOutOfRange: + printf("error code : kAudioConverterErr_InputSampleRateOutOfRange\n"); + break; + case kAudioConverterErr_OutputSampleRateOutOfRange: + printf("error code : kAudioConverterErr_OutputSampleRateOutOfRange\n"); + break; + default: + printf("error code : unknown\n"); + break; + } +} + +OSStatus TiPhoneCoreAudioRenderer::Render(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32, + UInt32 inNumberFrames, + AudioBufferList *ioData) +{ + TiPhoneCoreAudioRendererPtr renderer = (TiPhoneCoreAudioRendererPtr)inRefCon; + AudioUnitRender(renderer->fAUHAL, ioActionFlags, inTimeStamp, 1, inNumberFrames, renderer->fCAInputData); + + float coef = float(LONG_MAX); + float inv_coef = 1.f/float(LONG_MAX); + + for (int chan = 0; chan < renderer->fDevNumInChans; chan++) { + for (int frame = 0; frame < inNumberFrames; frame++) { + renderer->fInChannel[chan][frame] = float(((int*)renderer->fCAInputData->mBuffers[chan].mData)[frame]) * inv_coef; + } + } + + renderer->PerformAudioCallback((int)inNumberFrames); + + for (int chan = 0; chan < renderer->fDevNumOutChans; chan++) { + for (int frame = 0; frame < inNumberFrames; frame++) { + ((int*)ioData->mBuffers[chan].mData)[frame] = int(renderer->fOutChannel[chan][frame] * coef); + } + } + + return 0; +} + +void TiPhoneCoreAudioRenderer::InterruptionListener(void *inClientData, UInt32 inInterruption) +{ + TiPhoneCoreAudioRenderer *obj = (TiPhoneCoreAudioRenderer*)inClientData; + printf("Session interrupted! --- %s ---", inInterruption == kAudioSessionBeginInterruption ? "Begin Interruption" : "End Interruption"); + + if (inInterruption == kAudioSessionEndInterruption) { + // make sure we are again the active session + AudioSessionSetActive(true); + AudioOutputUnitStart(obj->fAUHAL); + } + + if (inInterruption == kAudioSessionBeginInterruption) { + AudioOutputUnitStop(obj->fAUHAL); + } +} + +int TiPhoneCoreAudioRenderer::Open(int bufferSize, int samplerate) +{ + OSStatus err1; + UInt32 outSize; + UInt32 enableIO; + AudioStreamBasicDescription srcFormat, dstFormat; + + printf("Open fDevNumInChans = %ld fDevNumOutChans = %ld bufferSize = %ld samplerate = %ld\n", fDevNumInChans, fDevNumOutChans, bufferSize, samplerate); + + // Initialize and configure the audio session + err1 = AudioSessionInitialize(NULL, NULL, InterruptionListener, this); + if (err1 != noErr) { + printf("Couldn't initialize audio session\n"); + printError(err1); + return OPEN_ERR; + } + + err1 = AudioSessionSetActive(true); + if (err1 != noErr) { + printf("Couldn't set audio session active\n"); + printError(err1); + return OPEN_ERR; + } + + UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord; + err1 = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory); + if (err1 != noErr) { + printf("Couldn't set audio category\n"); + printError(err1); + return OPEN_ERR; + } + + //err1 = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, propListener, self), "couldn't set property listener"); + + Float64 hwSampleRate; + outSize = sizeof(hwSampleRate); + err1 = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &outSize, &hwSampleRate); + if (err1 != noErr) { + printf("Couldn't get hw sample rate\n"); + printError(err1); + return OPEN_ERR; + } else { + printf("Get hw sample rate %f\n", hwSampleRate); + } + + Float32 hwBufferSize; + outSize = sizeof(hwBufferSize); + err1 = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration, &outSize, &hwBufferSize); + if (err1 != noErr) { + printf("Couldn't get hw buffer duration\n"); + printError(err1); + return OPEN_ERR; + } else { + printf("Get hw buffer duration %f\n", hwBufferSize); + } + + UInt32 hwInput; + outSize = sizeof(hwInput); + err1 = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels, &outSize, &hwInput); + if (err1 != noErr) { + printf("Couldn't get hw input channels\n"); + printError(err1); + return OPEN_ERR; + } else { + printf("Get hw input channels %d\n", hwInput); + } + + UInt32 hwOutput; + outSize = sizeof(hwOutput); + err1 = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputNumberChannels, &outSize, &hwOutput); + if (err1 != noErr) { + printf("Couldn't get hw output channels\n"); + printError(err1); + return OPEN_ERR; + } else { + printf("Get hw output channels %d\n", hwOutput); + } + + Float32 preferredBufferSize = float(bufferSize) / float(samplerate); + printf("preferredBufferSize %f \n", preferredBufferSize); + + err1 = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(preferredBufferSize), &preferredBufferSize); + if (err1 != noErr) { + printf("Couldn't set i/o buffer duration\n"); + printError(err1); + return OPEN_ERR; + } + + Float64 preferredSamplerate = float(samplerate); + err1 = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareSampleRate, sizeof(preferredSamplerate), &preferredSamplerate); + if (err1 != noErr) { + printf("Couldn't set i/o sample rate\n"); + printError(err1); + return OPEN_ERR; + } + + // AUHAL + AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_RemoteIO, kAudioUnitManufacturer_Apple, 0, 0}; + AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd); + + err1 = AudioComponentInstanceNew(HALOutput, &fAUHAL); + if (err1 != noErr) { + printf("Error calling OpenAComponent\n"); + printError(err1); + goto error; + } + + enableIO = 1; + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO)); + if (err1 != noErr) { + printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output\n"); + printError(err1); + goto error; + } + + enableIO = 1; + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO)); + if (err1 != noErr) { + printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input\n"); + printError(err1); + goto error; + } + + UInt32 maxFPS; + outSize = sizeof(maxFPS); + err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFPS, &outSize); + if (err1 != noErr) { + printf("Couldn't get kAudioUnitProperty_MaximumFramesPerSlice\n"); + printError(err1); + goto error; + } else { + printf("Get kAudioUnitProperty_MaximumFramesPerSlice %d\n", maxFPS); + } + + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&bufferSize, sizeof(UInt32)); + if (err1 != noErr) { + printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice\n"); + printError(err1); + goto error; + } + + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&bufferSize, sizeof(UInt32)); + if (err1 != noErr) { + printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice\n"); + printError(err1); + goto error; + } + + err1 = AudioUnitInitialize(fAUHAL); + if (err1 != noErr) { + printf("Cannot initialize AUHAL unit\n"); + printError(err1); + goto error; + } + + // Setting format + + if (fDevNumInChans > 0) { + outSize = sizeof(AudioStreamBasicDescription); + err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, &outSize); + if (err1 != noErr) { + printf("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n"); + printError(err1); + } + PrintStreamDesc(&srcFormat); + + srcFormat.mFormatID = kAudioFormatLinearPCM; + srcFormat.mFormatFlags = kAudioFormatFlagsCanonical | kLinearPCMFormatFlagIsNonInterleaved; + srcFormat.mBytesPerPacket = sizeof(AudioUnitSampleType); + srcFormat.mFramesPerPacket = 1; + srcFormat.mBytesPerFrame = sizeof(AudioUnitSampleType); + srcFormat.mChannelsPerFrame = fDevNumInChans; + srcFormat.mBitsPerChannel = 32; + + PrintStreamDesc(&srcFormat); + + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription)); + if (err1 != noErr) { + printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n"); + printError(err1); + } + } + + if (fDevNumOutChans > 0) { + outSize = sizeof(AudioStreamBasicDescription); + err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, &outSize); + if (err1 != noErr) { + printf("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input\n"); + printError(err1); + } + PrintStreamDesc(&dstFormat); + + dstFormat.mFormatID = kAudioFormatLinearPCM; + dstFormat.mFormatFlags = kAudioFormatFlagsCanonical | kLinearPCMFormatFlagIsNonInterleaved; + dstFormat.mBytesPerPacket = sizeof(AudioUnitSampleType); + dstFormat.mFramesPerPacket = 1; + dstFormat.mBytesPerFrame = sizeof(AudioUnitSampleType); + dstFormat.mChannelsPerFrame = fDevNumOutChans; + dstFormat.mBitsPerChannel = 32; + + PrintStreamDesc(&dstFormat); + + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription)); + if (err1 != noErr) { + printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input\n"); + printError(err1); + } + } + + if (fDevNumInChans > 0 && fDevNumOutChans == 0) { + AURenderCallbackStruct output; + output.inputProc = Render; + output.inputProcRefCon = this; + err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output)); + if (err1 != noErr) { + printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1\n"); + printError(err1); + goto error; + } + } else { + AURenderCallbackStruct output; + output.inputProc = Render; + output.inputProcRefCon = this; + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output)); + if (err1 != noErr) { + printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0\n"); + printError(err1); + goto error; + } + } + + // Prepare buffers + fCAInputData = (AudioBufferList*)malloc(sizeof(UInt32) + fDevNumInChans * sizeof(AudioBuffer)); + fCAInputData->mNumberBuffers = fDevNumInChans; + for (int i = 0; i < fDevNumInChans; i++) { + fCAInputData->mBuffers[i].mNumberChannels = 1; + fCAInputData->mBuffers[i].mDataByteSize = bufferSize * sizeof(int); + fCAInputData->mBuffers[i].mData = malloc(bufferSize * sizeof(int)); + } + + /* + // Add listeners + err1 = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDeviceProcessorOverload"); + printError(err); + return -1; + } + */ + + return NO_ERR; + +error: + AudioUnitUninitialize(fAUHAL); + AudioComponentInstanceDispose(fAUHAL); + return OPEN_ERR; +} + +int TiPhoneCoreAudioRenderer::Close() +{ + AudioUnitUninitialize(fAUHAL); + AudioComponentInstanceDispose(fAUHAL); + if (fCAInputData) { + // Free buffers and list + for (int i = 0; i < fDevNumInChans; i++) { + free(fCAInputData->mBuffers[i].mData); + } + free(fCAInputData); + } + return NO_ERR; +} + +int TiPhoneCoreAudioRenderer::Start() +{ + AudioSessionSetActive(true); + OSStatus err = AudioOutputUnitStart(fAUHAL); + + if (err != noErr) { + printf("Error while opening device : device open error \n"); + return OPEN_ERR; + } else { + return NO_ERR; + } +} + +int TiPhoneCoreAudioRenderer::Stop() +{ + OSStatus err = AudioOutputUnitStop(fAUHAL); + + if (err != noErr) { + printf("Error while closing device : device close error \n"); + return OPEN_ERR; + } else { + return NO_ERR; + } +} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiDriver.cpp jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiDriver.cpp --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiDriver.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiDriver.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,866 +0,0 @@ -/* -Copyright (C) 2009 Grame -Copyright (C) 2011 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "JackCompilerDeps.h" -#include "JackCoreMidiDriver.h" -#include "JackCoreMidiUtil.h" -#include "JackEngineControl.h" -#include "driver_interface.h" - -#include -#include -#include - -using Jack::JackCoreMidiDriver; - -static char capture_driver_name[256]; -static char playback_driver_name[256]; - -static int in_channels, out_channels; -static bool capturing, playing, monitor; - -static jack_nframes_t capture_latency, playback_latency; - -/////////////////////////////////////////////////////////////////////////////// -// Static callbacks -/////////////////////////////////////////////////////////////////////////////// - -void -JackCoreMidiDriver::HandleInputEvent(const MIDIPacketList *packet_list, - void *driver, void *port) -{ - ((JackCoreMidiPhysicalInputPort *) port)->ProcessCoreMidi(packet_list); -} - -void -JackCoreMidiDriver::HandleNotificationEvent(const MIDINotification *message, - void *driver) -{ - ((JackCoreMidiDriver *) driver)->HandleNotification(message); -} - -/////////////////////////////////////////////////////////////////////////////// -// Class -/////////////////////////////////////////////////////////////////////////////// - -JackCoreMidiDriver::JackCoreMidiDriver(const char *name, const char *alias, - JackLockedEngine *engine, - JackSynchro *table): - JackMidiDriver(name, alias, engine, table),fThread(this) -{ - mach_timebase_info_data_t info; - kern_return_t result = mach_timebase_info(&info); - if (result != KERN_SUCCESS) { - throw std::runtime_error(mach_error_string(result)); - } - client = 0; - fCaptureChannels = 0; - fPlaybackChannels = 0; - num_physical_inputs = 0; - num_physical_outputs = 0; - num_virtual_inputs = 0; - num_virtual_outputs = 0; - physical_input_ports = 0; - physical_output_ports = 0; - time_ratio = (((double) info.numer) / info.denom) / 1000.0; - virtual_input_ports = 0; - virtual_output_ports = 0; - internal_input = 0; - internal_output = 0; -} - -JackCoreMidiDriver::~JackCoreMidiDriver() -{} - -bool JackCoreMidiDriver::Init() -{ - return OpenAux(); -} - -bool JackCoreMidiDriver::OpenAux() -{ - int pi_count = 0; - int po_count = 0; - int vi_count = 0; - int vo_count = 0; - ItemCount potential_po_count; - ItemCount potential_pi_count; - - CFStringRef name = CFStringCreateWithCString(0, "JackMidi", - CFStringGetSystemEncoding()); - if (! name) { - jack_error("JackCoreMidiDriver::Open - failed to allocate memory for " - "client name string"); - return false; - } - - OSStatus status = MIDIClientCreate(name, HandleNotificationEvent, this, - &client); - CFRelease(name); - - if (status != noErr) { - WriteMacOSError("JackCoreMidiDriver::Open", "MIDIClientCreate", - status); - return false; - } - - char *client_name = fClientControl.fName; - - // Allocate and connect physical inputs - potential_pi_count = MIDIGetNumberOfSources(); - if (potential_pi_count) { - status = MIDIInputPortCreate(client, CFSTR("Physical Input Port"), - HandleInputEvent, this, &internal_input); - if (status != noErr) { - WriteMacOSError("JackCoreMidiDriver::Open", "MIDIInputPortCreate", - status); - goto destroy; - } - - try { - physical_input_ports = - new JackCoreMidiPhysicalInputPort*[potential_pi_count]; - } catch (std::exception& e) { - jack_error("JackCoreMidiDriver::Open - while creating physical " - "input port array: %s", e.what()); - goto destroy; - } - - for (ItemCount i = 0; i < potential_pi_count; i++) { - try { - physical_input_ports[pi_count] = - new JackCoreMidiPhysicalInputPort(fAliasName, client_name, - capture_driver_name, i, - client, internal_input, - time_ratio); - } catch (std::exception& e) { - jack_error("JackCoreMidiDriver::Open - while creating " - "physical input port: %s", e.what()); - goto destroy; - } - pi_count++; - } - } - - // Allocate and connect physical outputs - potential_po_count = MIDIGetNumberOfDestinations(); - if (potential_po_count) { - status = MIDIOutputPortCreate(client, CFSTR("Physical Output Port"), - &internal_output); - if (status != noErr) { - WriteMacOSError("JackCoreMidiDriver::Open", "MIDIOutputPortCreate", - status); - goto destroy; - } - - try { - physical_output_ports = - new JackCoreMidiPhysicalOutputPort*[potential_po_count]; - } catch (std::exception& e) { - jack_error("JackCoreMidiDriver::Open - while creating physical " - "output port array: %s", e.what()); - goto destroy; - } - - for (ItemCount i = 0; i < potential_po_count; i++) { - try { - physical_output_ports[po_count] = - new JackCoreMidiPhysicalOutputPort(fAliasName, client_name, - playback_driver_name, i, - client, internal_output, - time_ratio); - } catch (std::exception& e) { - jack_error("JackCoreMidiDriver::Open - while creating " - "physical output port: %s", e.what()); - goto destroy; - } - po_count++; - } - } - - // Allocate and connect virtual inputs - if (in_channels) { - try { - virtual_input_ports = - new JackCoreMidiVirtualInputPort*[in_channels]; - } catch (std::exception& e) { - jack_error("JackCoreMidiDriver::Open - while creating virtual " - "input port array: %s", e.what()); - goto destroy; - - } - for (vi_count = 0; vi_count < in_channels; vi_count++) { - try { - virtual_input_ports[vi_count] = - new JackCoreMidiVirtualInputPort(fAliasName, client_name, - capture_driver_name, - vi_count, vi_count + pi_count, client, - time_ratio); - } catch (std::exception& e) { - jack_error("JackCoreMidiDriver::Open - while creating virtual " - "input port: %s", e.what()); - goto destroy; - } - } - } - - // Allocate and connect virtual outputs - if (out_channels) { - try { - virtual_output_ports = - new JackCoreMidiVirtualOutputPort*[out_channels]; - } catch (std::exception& e) { - jack_error("JackCoreMidiDriver::Open - while creating virtual " - "output port array: %s", e.what()); - goto destroy; - } - for (vo_count = 0; vo_count < out_channels; vo_count++) { - try { - virtual_output_ports[vo_count] = - new JackCoreMidiVirtualOutputPort(fAliasName, client_name, - playback_driver_name, - vo_count, vo_count + po_count, client, - time_ratio); - } catch (std::exception& e) { - jack_error("JackCoreMidiDriver::Open - while creating virtual " - "output port: %s", e.what()); - goto destroy; - } - } - } - - if (! (pi_count || po_count || in_channels || out_channels)) { - jack_error("JackCoreMidiDriver::Open - no CoreMIDI inputs or outputs " - "found, and no virtual ports allocated."); - } - - if (! JackMidiDriver::Open(capturing, playing, - in_channels + pi_count, - out_channels + po_count, monitor, - capture_driver_name, - playback_driver_name, capture_latency, - playback_latency)) { - num_physical_inputs = pi_count; - num_physical_outputs = po_count; - num_virtual_inputs = in_channels; - num_virtual_outputs = out_channels; - return true; - } - - destroy: - - if (physical_input_ports) { - for (int i = 0; i < pi_count; i++) { - delete physical_input_ports[i]; - } - delete[] physical_input_ports; - physical_input_ports = 0; - } - - if (physical_output_ports) { - for (int i = 0; i < po_count; i++) { - delete physical_output_ports[i]; - } - delete[] physical_output_ports; - physical_output_ports = 0; - } - - if (virtual_input_ports) { - for (int i = 0; i < vi_count; i++) { - delete virtual_input_ports[i]; - } - delete[] virtual_input_ports; - virtual_input_ports = 0; - } - - if (virtual_output_ports) { - for (int i = 0; i < vo_count; i++) { - delete virtual_output_ports[i]; - } - delete[] virtual_output_ports; - virtual_output_ports = 0; - } - - if (internal_output) { - status = MIDIPortDispose(internal_output); - if (status != noErr) { - WriteMacOSError("JackCoreMidiDriver::Open", "MIDIPortDispose", status); - } - } - - if (internal_input) { - status = MIDIPortDispose(internal_input); - if (status != noErr) { - WriteMacOSError("JackCoreMidiDriver::Open", "MIDIPortDispose", status); - } - } - - if (client) { - status = MIDIClientDispose(client); - if (status != noErr) { - WriteMacOSError("JackCoreMidiDriver::Open", "MIDIClientDispose", - status); - } - } - - // Default open - if (! JackMidiDriver::Open(capturing, playing, - in_channels + pi_count, - out_channels + po_count, monitor, - capture_driver_name, - playback_driver_name, capture_latency, - playback_latency)) { - client = 0; - num_physical_inputs = 0; - num_physical_outputs = 0; - num_virtual_inputs = 0; - num_virtual_outputs = 0; - return true; - } else { - return false; - } -} - -bool JackCoreMidiDriver::Execute() -{ - CFRunLoopRun(); - return false; -} - -int -JackCoreMidiDriver::Attach() -{ - jack_nframes_t buffer_size = fEngineControl->fBufferSize; - jack_port_id_t index; - jack_nframes_t latency = buffer_size; - jack_latency_range_t latency_range; - const char *name; - JackPort *port; - JackCoreMidiPort *port_obj; - latency_range.max = latency; - latency_range.min = latency; - - // Physical inputs - for (int i = 0; i < num_physical_inputs; i++) { - port_obj = physical_input_ports[i]; - name = port_obj->GetName(); - if (fEngine->PortRegister(fClientControl.fRefNum, name, - JACK_DEFAULT_MIDI_TYPE, - CaptureDriverFlags, buffer_size, &index) < 0) { - jack_error("JackCoreMidiDriver::Attach - cannot register physical " - "input port with name '%s'.", name); - // X: Do we need to deallocate ports? - return -1; - } - port = fGraphManager->GetPort(index); - port->SetAlias(port_obj->GetAlias()); - port->SetLatencyRange(JackCaptureLatency, &latency_range); - fCapturePortList[i] = index; - } - - // Virtual inputs - for (int i = 0; i < num_virtual_inputs; i++) { - port_obj = virtual_input_ports[i]; - name = port_obj->GetName(); - if (fEngine->PortRegister(fClientControl.fRefNum, name, - JACK_DEFAULT_MIDI_TYPE, - CaptureDriverFlags, buffer_size, &index) < 0) { - jack_error("JackCoreMidiDriver::Attach - cannot register virtual " - "input port with name '%s'.", name); - // X: Do we need to deallocate ports? - return -1; - } - port = fGraphManager->GetPort(index); - port->SetAlias(port_obj->GetAlias()); - port->SetLatencyRange(JackCaptureLatency, &latency_range); - fCapturePortList[num_physical_inputs + i] = index; - } - - if (! fEngineControl->fSyncMode) { - latency += buffer_size; - latency_range.max = latency; - latency_range.min = latency; - } - - // Physical outputs - for (int i = 0; i < num_physical_outputs; i++) { - port_obj = physical_output_ports[i]; - name = port_obj->GetName(); - fEngine->PortRegister(fClientControl.fRefNum, name, - JACK_DEFAULT_MIDI_TYPE, - PlaybackDriverFlags, buffer_size, &index); - if (index == NO_PORT) { - jack_error("JackCoreMidiDriver::Attach - cannot register physical " - "output port with name '%s'.", name); - // X: Do we need to deallocate ports? - return -1; - } - port = fGraphManager->GetPort(index); - port->SetAlias(port_obj->GetAlias()); - port->SetLatencyRange(JackPlaybackLatency, &latency_range); - fPlaybackPortList[i] = index; - } - - // Virtual outputs - for (int i = 0; i < num_virtual_outputs; i++) { - port_obj = virtual_output_ports[i]; - name = port_obj->GetName(); - fEngine->PortRegister(fClientControl.fRefNum, name, - JACK_DEFAULT_MIDI_TYPE, - PlaybackDriverFlags, buffer_size, &index); - if (index == NO_PORT) { - jack_error("JackCoreMidiDriver::Attach - cannot register virtual " - "output port with name '%s'.", name); - // X: Do we need to deallocate ports? - return -1; - } - port = fGraphManager->GetPort(index); - port->SetAlias(port_obj->GetAlias()); - port->SetLatencyRange(JackPlaybackLatency, &latency_range); - fPlaybackPortList[num_physical_outputs + i] = index; - } - - return 0; -} - -int -JackCoreMidiDriver::Close() -{ - fThread.Kill(); - return CloseAux(); -} - -int -JackCoreMidiDriver::CloseAux() -{ - // Generic MIDI driver close - int result = JackMidiDriver::Close(); - - OSStatus status; - if (physical_input_ports) { - for (int i = 0; i < num_physical_inputs; i++) { - delete physical_input_ports[i]; - } - delete[] physical_input_ports; - num_physical_inputs = 0; - physical_input_ports = 0; - if (internal_input) { - status = MIDIPortDispose(internal_input); - if (status != noErr) { - WriteMacOSError("JackCoreMidiDriver::Close", "MIDIPortDispose", - status); - result = -1; - } - internal_input = 0; - } - } - if (physical_output_ports) { - for (int i = 0; i < num_physical_outputs; i++) { - delete physical_output_ports[i]; - } - delete[] physical_output_ports; - num_physical_outputs = 0; - physical_output_ports = 0; - if (internal_output) { - status = MIDIPortDispose(internal_output); - if (status != noErr) { - WriteMacOSError("JackCoreMidiDriver::Close", "MIDIPortDispose", - status); - result = -1; - } - internal_output = 0; - } - } - if (virtual_input_ports) { - for (int i = 0; i < num_virtual_inputs; i++) { - delete virtual_input_ports[i]; - } - delete[] virtual_input_ports; - num_virtual_inputs = 0; - virtual_input_ports = 0; - } - if (virtual_output_ports) { - for (int i = 0; i < num_virtual_outputs; i++) { - delete virtual_output_ports[i]; - } - delete[] virtual_output_ports; - num_virtual_outputs = 0; - virtual_output_ports = 0; - } - - if (client) { - status = MIDIClientDispose(client); - if (status != noErr) { - WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientDispose", - status); - result = -1; - } - client = 0; - } - return result; -} - -void -JackCoreMidiDriver::Restart() -{ - // Lock between this thread and RT thread - JackLock lock(this); - - // Lock between this thread and request thread - if (fEngine->Lock()) { - // Use first alias - SaveConnections(1); - Stop(); - Detach(); - CloseAux(); - OpenAux(); - Attach(); - Start(); - // Use first alias and partial port naming - LoadConnections(1, false); - fEngine->Unlock(); - } else { - jack_error("Cannot acquire engine lock..."); - } -} - -void -JackCoreMidiDriver::HandleNotification(const MIDINotification *message) -{ - switch (message->messageID) { - - case kMIDIMsgObjectAdded: { - /* - We don't want to restart when our internal virtual in/out are created. - */ - const MIDIObjectAddRemoveNotification* add_message = reinterpret_cast(message); - if (!JackCoreMidiPort::IsInternalPort(add_message->child)) { - Restart(); - } - break; - } - - case kMIDIMsgObjectRemoved: { - /* - We don't want to restart when our internal virtual in/out are created. - */ - const MIDIObjectAddRemoveNotification* remove_message = reinterpret_cast(message); - if (!JackCoreMidiPort::IsInternalPort(remove_message->child)) { - Restart(); - } - break; - } - } -} - -int -JackCoreMidiDriver::Open(bool capturing_aux, bool playing_aux, int in_channels_aux, - int out_channels_aux, bool monitor_aux, - const char* capture_driver_name_aux, - const char* playback_driver_name_aux, - jack_nframes_t capture_latency_aux, - jack_nframes_t playback_latency_aux) -{ - - strcpy(capture_driver_name, capture_driver_name_aux); - strcpy(playback_driver_name, playback_driver_name_aux); - - capturing = capturing_aux; - playing = playing_aux; - in_channels = in_channels_aux; - out_channels = out_channels_aux; - monitor = monitor_aux; - capture_latency = capture_latency_aux; - playback_latency = playback_latency_aux; - - fThread.StartSync(); - - int count = 0; - while (fThread.GetStatus() != JackThread::kRunning && ++count < WAIT_COUNTER) { - JackSleep(100000); - jack_log("JackCoreMidiDriver::Open wait count = %d", count); - - } - if (count == WAIT_COUNTER) { - jack_info("Cannot open CoreMIDI driver"); - fThread.Kill(); - return -1; - } else { - JackSleep(10000); - jack_info("CoreMIDI driver is opened..."); - } - - return 0; -} - -int -JackCoreMidiDriver::Start() -{ - jack_info("JackCoreMidiDriver::Start - Starting driver."); - - JackMidiDriver::Start(); - - int pi_count = 0; - int po_count = 0; - int vi_count = 0; - int vo_count = 0; - - jack_info("JackCoreMidiDriver::Start - Enabling physical input ports."); - - for (; pi_count < num_physical_inputs; pi_count++) { - if (physical_input_ports[pi_count]->Start() < 0) { - jack_error("JackCoreMidiDriver::Start - Failed to enable physical " - "input port."); - goto stop_physical_input_ports; - } - } - - jack_info("JackCoreMidiDriver::Start - Enabling physical output ports."); - - for (; po_count < num_physical_outputs; po_count++) { - if (physical_output_ports[po_count]->Start() < 0) { - jack_error("JackCoreMidiDriver::Start - Failed to enable physical " - "output port."); - goto stop_physical_output_ports; - } - } - - jack_info("JackCoreMidiDriver::Start - Enabling virtual input ports."); - - for (; vi_count < num_virtual_inputs; vi_count++) { - if (virtual_input_ports[vi_count]->Start() < 0) { - jack_error("JackCoreMidiDriver::Start - Failed to enable virtual " - "input port."); - goto stop_virtual_input_ports; - } - } - - jack_info("JackCoreMidiDriver::Start - Enabling virtual output ports."); - - for (; vo_count < num_virtual_outputs; vo_count++) { - if (virtual_output_ports[vo_count]->Start() < 0) { - jack_error("JackCoreMidiDriver::Start - Failed to enable virtual " - "output port."); - goto stop_virtual_output_ports; - } - } - - jack_info("JackCoreMidiDriver::Start - Driver started."); - - return 0; - - stop_virtual_output_ports: - for (int i = 0; i < vo_count; i++) { - if (virtual_output_ports[i]->Stop() < 0) { - jack_error("JackCoreMidiDriver::Start - Failed to disable virtual " - "output port."); - } - } - stop_virtual_input_ports: - for (int i = 0; i < vi_count; i++) { - if (virtual_input_ports[i]->Stop() < 0) { - jack_error("JackCoreMidiDriver::Start - Failed to disable virtual " - "input port."); - } - } - stop_physical_output_ports: - for (int i = 0; i < po_count; i++) { - if (physical_output_ports[i]->Stop() < 0) { - jack_error("JackCoreMidiDriver::Start - Failed to disable " - "physical output port."); - } - } - stop_physical_input_ports: - for (int i = 0; i < pi_count; i++) { - if (physical_input_ports[i]->Stop() < 0) { - jack_error("JackCoreMidiDriver::Start - Failed to disable " - "physical input port."); - } - } - - return -1; -} - -int -JackCoreMidiDriver::Stop() -{ - int result = 0; - - JackMidiDriver::Stop(); - - jack_info("JackCoreMidiDriver::Stop - disabling physical input ports."); - - for (int i = 0; i < num_physical_inputs; i++) { - if (physical_input_ports[i]->Stop() < 0) { - jack_error("JackCoreMidiDriver::Stop - Failed to disable physical " - "input port."); - result = -1; - } - } - - jack_info("JackCoreMidiDriver::Stop - disabling physical output ports."); - - for (int i = 0; i < num_physical_outputs; i++) { - if (physical_output_ports[i]->Stop() < 0) { - jack_error("JackCoreMidiDriver::Stop - Failed to disable physical " - "output port."); - result = -1; - } - } - - jack_info("JackCoreMidiDriver::Stop - disabling virtual input ports."); - - for (int i = 0; i < num_virtual_inputs; i++) { - if (virtual_input_ports[i]->Stop() < 0) { - jack_error("JackCoreMidiDriver::Stop - Failed to disable virtual " - "input port."); - result = -1; - } - } - - jack_info("JackCoreMidiDriver::Stop - disabling virtual output ports."); - - for (int i = 0; i < num_virtual_outputs; i++) { - if (virtual_output_ports[i]->Stop() < 0) { - jack_error("JackCoreMidiDriver::Stop - Failed to disable virtual " - "output port."); - result = -1; - } - } - - return result; -} - -int -JackCoreMidiDriver::ProcessRead() -{ - int res; - if (Trylock()) { - res = (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); - Unlock(); - } else { - res = -1; - } - return res; -} - -int -JackCoreMidiDriver::ProcessWrite() -{ - int res; - if (Trylock()) { - res = (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); - Unlock(); - } else { - res = -1; - } - return res; -} - -int -JackCoreMidiDriver::Read() -{ - jack_nframes_t buffer_size = fEngineControl->fBufferSize; - for (int i = 0; i < num_physical_inputs; i++) { - physical_input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size); - } - for (int i = 0; i < num_virtual_inputs; i++) { - virtual_input_ports[i]-> - ProcessJack(GetInputBuffer(num_physical_inputs + i), buffer_size); - } - return 0; -} - -int -JackCoreMidiDriver::Write() -{ - jack_nframes_t buffer_size = fEngineControl->fBufferSize; - for (int i = 0; i < num_physical_outputs; i++) { - physical_output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size); - } - for (int i = 0; i < num_virtual_outputs; i++) { - virtual_output_ports[i]-> - ProcessJack(GetOutputBuffer(num_physical_outputs + i), buffer_size); - } - return 0; -} - -#ifdef __cplusplus -extern "C" { -#endif - - // singleton kind of driver - static Jack::JackCoreMidiDriver* driver = NULL; - - SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor() - { - jack_driver_desc_t * desc; - jack_driver_desc_filler_t filler; - jack_driver_param_value_t value; - - desc = jack_driver_descriptor_construct("coremidi", JackDriverSlave, "Apple CoreMIDI API based MIDI backend", &filler); - - value.ui = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamUInt, &value, NULL, "CoreMIDI virtual bus", NULL); - jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamUInt, &value, NULL, "CoreMIDI virtual bus", NULL); - - return desc; - } - - SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) - { - const JSList * node; - const jack_driver_param_t * param; - int virtual_in = 2; - int virtual_out = 2; - - for (node = params; node; node = jack_slist_next (node)) { - param = (const jack_driver_param_t *) node->data; - - switch (param->character) { - - case 'i': - virtual_in = param->value.ui; - break; - - case 'o': - virtual_out = param->value.ui; - break; - } - } - - // singleton kind of driver - if (!driver) { - driver = new Jack::JackCoreMidiDriver("system_midi", "coremidi", engine, table); - if (driver->Open(1, 1, virtual_in, virtual_out, false, "in", "out", 0, 0) == 0) { - return driver; - } else { - delete driver; - return NULL; - } - } else { - jack_info("JackCoreMidiDriver already allocated, cannot be loaded twice"); - return NULL; - } - } - -#ifdef __cplusplus -} -#endif diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiDriver.mm jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiDriver.mm --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiDriver.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiDriver.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,866 @@ +/* +Copyright (C) 2009 Grame +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "JackCompilerDeps.h" +#include "JackCoreMidiDriver.h" +#include "JackCoreMidiUtil.h" +#include "JackEngineControl.h" +#include "driver_interface.h" + +#include +#include +#include + +using Jack::JackCoreMidiDriver; + +static char capture_driver_name[256]; +static char playback_driver_name[256]; + +static int in_channels, out_channels; +static bool capturing, playing, monitor; + +static jack_nframes_t capture_latency, playback_latency; + +/////////////////////////////////////////////////////////////////////////////// +// Static callbacks +/////////////////////////////////////////////////////////////////////////////// + +void +JackCoreMidiDriver::HandleInputEvent(const MIDIPacketList *packet_list, + void *driver, void *port) +{ + ((JackCoreMidiPhysicalInputPort *) port)->ProcessCoreMidi(packet_list); +} + +void +JackCoreMidiDriver::HandleNotificationEvent(const MIDINotification *message, + void *driver) +{ + ((JackCoreMidiDriver *) driver)->HandleNotification(message); +} + +/////////////////////////////////////////////////////////////////////////////// +// Class +/////////////////////////////////////////////////////////////////////////////// + +JackCoreMidiDriver::JackCoreMidiDriver(const char *name, const char *alias, + JackLockedEngine *engine, + JackSynchro *table): + JackMidiDriver(name, alias, engine, table),fThread(this) +{ + mach_timebase_info_data_t info; + kern_return_t result = mach_timebase_info(&info); + if (result != KERN_SUCCESS) { + throw std::runtime_error(mach_error_string(result)); + } + client = 0; + fCaptureChannels = 0; + fPlaybackChannels = 0; + num_physical_inputs = 0; + num_physical_outputs = 0; + num_virtual_inputs = 0; + num_virtual_outputs = 0; + physical_input_ports = 0; + physical_output_ports = 0; + time_ratio = (((double) info.numer) / info.denom) / 1000.0; + virtual_input_ports = 0; + virtual_output_ports = 0; + internal_input = 0; + internal_output = 0; +} + +JackCoreMidiDriver::~JackCoreMidiDriver() +{} + +bool JackCoreMidiDriver::Init() +{ + return OpenAux(); +} + +bool JackCoreMidiDriver::OpenAux() +{ + int pi_count = 0; + int po_count = 0; + int vi_count = 0; + int vo_count = 0; + ItemCount potential_po_count; + ItemCount potential_pi_count; + + CFStringRef name = CFStringCreateWithCString(0, "JackMidi", + CFStringGetSystemEncoding()); + if (! name) { + jack_error("JackCoreMidiDriver::Open - failed to allocate memory for " + "client name string"); + return false; + } + + OSStatus status = MIDIClientCreate(name, HandleNotificationEvent, this, + &client); + CFRelease(name); + + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Open", "MIDIClientCreate", + status); + return false; + } + + char *client_name = fClientControl.fName; + + // Allocate and connect physical inputs + potential_pi_count = MIDIGetNumberOfSources(); + if (potential_pi_count) { + status = MIDIInputPortCreate(client, CFSTR("Physical Input Port"), + HandleInputEvent, this, &internal_input); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Open", "MIDIInputPortCreate", + status); + goto destroy; + } + + try { + physical_input_ports = + new JackCoreMidiPhysicalInputPort*[potential_pi_count]; + } catch (std::exception& e) { + jack_error("JackCoreMidiDriver::Open - while creating physical " + "input port array: %s", e.what()); + goto destroy; + } + + for (ItemCount i = 0; i < potential_pi_count; i++) { + try { + physical_input_ports[pi_count] = + new JackCoreMidiPhysicalInputPort(fAliasName, client_name, + capture_driver_name, i, + client, internal_input, + time_ratio); + } catch (std::exception& e) { + jack_error("JackCoreMidiDriver::Open - while creating " + "physical input port: %s", e.what()); + goto destroy; + } + pi_count++; + } + } + + // Allocate and connect physical outputs + potential_po_count = MIDIGetNumberOfDestinations(); + if (potential_po_count) { + status = MIDIOutputPortCreate(client, CFSTR("Physical Output Port"), + &internal_output); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Open", "MIDIOutputPortCreate", + status); + goto destroy; + } + + try { + physical_output_ports = + new JackCoreMidiPhysicalOutputPort*[potential_po_count]; + } catch (std::exception& e) { + jack_error("JackCoreMidiDriver::Open - while creating physical " + "output port array: %s", e.what()); + goto destroy; + } + + for (ItemCount i = 0; i < potential_po_count; i++) { + try { + physical_output_ports[po_count] = + new JackCoreMidiPhysicalOutputPort(fAliasName, client_name, + playback_driver_name, i, + client, internal_output, + time_ratio); + } catch (std::exception& e) { + jack_error("JackCoreMidiDriver::Open - while creating " + "physical output port: %s", e.what()); + goto destroy; + } + po_count++; + } + } + + // Allocate and connect virtual inputs + if (in_channels) { + try { + virtual_input_ports = + new JackCoreMidiVirtualInputPort*[in_channels]; + } catch (std::exception& e) { + jack_error("JackCoreMidiDriver::Open - while creating virtual " + "input port array: %s", e.what()); + goto destroy; + + } + for (vi_count = 0; vi_count < in_channels; vi_count++) { + try { + virtual_input_ports[vi_count] = + new JackCoreMidiVirtualInputPort(fAliasName, client_name, + capture_driver_name, + vi_count, vi_count + pi_count, client, + time_ratio); + } catch (std::exception& e) { + jack_error("JackCoreMidiDriver::Open - while creating virtual " + "input port: %s", e.what()); + goto destroy; + } + } + } + + // Allocate and connect virtual outputs + if (out_channels) { + try { + virtual_output_ports = + new JackCoreMidiVirtualOutputPort*[out_channels]; + } catch (std::exception& e) { + jack_error("JackCoreMidiDriver::Open - while creating virtual " + "output port array: %s", e.what()); + goto destroy; + } + for (vo_count = 0; vo_count < out_channels; vo_count++) { + try { + virtual_output_ports[vo_count] = + new JackCoreMidiVirtualOutputPort(fAliasName, client_name, + playback_driver_name, + vo_count, vo_count + po_count, client, + time_ratio); + } catch (std::exception& e) { + jack_error("JackCoreMidiDriver::Open - while creating virtual " + "output port: %s", e.what()); + goto destroy; + } + } + } + + if (! (pi_count || po_count || in_channels || out_channels)) { + jack_error("JackCoreMidiDriver::Open - no CoreMIDI inputs or outputs " + "found, and no virtual ports allocated."); + } + + if (! JackMidiDriver::Open(capturing, playing, + in_channels + pi_count, + out_channels + po_count, monitor, + capture_driver_name, + playback_driver_name, capture_latency, + playback_latency)) { + num_physical_inputs = pi_count; + num_physical_outputs = po_count; + num_virtual_inputs = in_channels; + num_virtual_outputs = out_channels; + return true; + } + + destroy: + + if (physical_input_ports) { + for (int i = 0; i < pi_count; i++) { + delete physical_input_ports[i]; + } + delete[] physical_input_ports; + physical_input_ports = 0; + } + + if (physical_output_ports) { + for (int i = 0; i < po_count; i++) { + delete physical_output_ports[i]; + } + delete[] physical_output_ports; + physical_output_ports = 0; + } + + if (virtual_input_ports) { + for (int i = 0; i < vi_count; i++) { + delete virtual_input_ports[i]; + } + delete[] virtual_input_ports; + virtual_input_ports = 0; + } + + if (virtual_output_ports) { + for (int i = 0; i < vo_count; i++) { + delete virtual_output_ports[i]; + } + delete[] virtual_output_ports; + virtual_output_ports = 0; + } + + if (internal_output) { + status = MIDIPortDispose(internal_output); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Open", "MIDIPortDispose", status); + } + } + + if (internal_input) { + status = MIDIPortDispose(internal_input); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Open", "MIDIPortDispose", status); + } + } + + if (client) { + status = MIDIClientDispose(client); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Open", "MIDIClientDispose", + status); + } + } + + // Default open + if (! JackMidiDriver::Open(capturing, playing, + in_channels + pi_count, + out_channels + po_count, monitor, + capture_driver_name, + playback_driver_name, capture_latency, + playback_latency)) { + client = 0; + num_physical_inputs = 0; + num_physical_outputs = 0; + num_virtual_inputs = 0; + num_virtual_outputs = 0; + return true; + } else { + return false; + } +} + +bool JackCoreMidiDriver::Execute() +{ + CFRunLoopRun(); + return false; +} + +int +JackCoreMidiDriver::Attach() +{ + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + jack_port_id_t index; + jack_nframes_t latency = buffer_size; + jack_latency_range_t latency_range; + const char *name; + JackPort *port; + JackCoreMidiPort *port_obj; + latency_range.max = latency; + latency_range.min = latency; + + // Physical inputs + for (int i = 0; i < num_physical_inputs; i++) { + port_obj = physical_input_ports[i]; + name = port_obj->GetName(); + if (fEngine->PortRegister(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + CaptureDriverFlags, buffer_size, &index) < 0) { + jack_error("JackCoreMidiDriver::Attach - cannot register physical " + "input port with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(port_obj->GetAlias()); + port->SetLatencyRange(JackCaptureLatency, &latency_range); + fCapturePortList[i] = index; + } + + // Virtual inputs + for (int i = 0; i < num_virtual_inputs; i++) { + port_obj = virtual_input_ports[i]; + name = port_obj->GetName(); + if (fEngine->PortRegister(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + CaptureDriverFlags, buffer_size, &index) < 0) { + jack_error("JackCoreMidiDriver::Attach - cannot register virtual " + "input port with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(port_obj->GetAlias()); + port->SetLatencyRange(JackCaptureLatency, &latency_range); + fCapturePortList[num_physical_inputs + i] = index; + } + + if (! fEngineControl->fSyncMode) { + latency += buffer_size; + latency_range.max = latency; + latency_range.min = latency; + } + + // Physical outputs + for (int i = 0; i < num_physical_outputs; i++) { + port_obj = physical_output_ports[i]; + name = port_obj->GetName(); + fEngine->PortRegister(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + PlaybackDriverFlags, buffer_size, &index); + if (index == NO_PORT) { + jack_error("JackCoreMidiDriver::Attach - cannot register physical " + "output port with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(port_obj->GetAlias()); + port->SetLatencyRange(JackPlaybackLatency, &latency_range); + fPlaybackPortList[i] = index; + } + + // Virtual outputs + for (int i = 0; i < num_virtual_outputs; i++) { + port_obj = virtual_output_ports[i]; + name = port_obj->GetName(); + fEngine->PortRegister(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + PlaybackDriverFlags, buffer_size, &index); + if (index == NO_PORT) { + jack_error("JackCoreMidiDriver::Attach - cannot register virtual " + "output port with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(port_obj->GetAlias()); + port->SetLatencyRange(JackPlaybackLatency, &latency_range); + fPlaybackPortList[num_physical_outputs + i] = index; + } + + return 0; +} + +int +JackCoreMidiDriver::Close() +{ + fThread.Kill(); + return CloseAux(); +} + +int +JackCoreMidiDriver::CloseAux() +{ + // Generic MIDI driver close + int result = JackMidiDriver::Close(); + + OSStatus status; + if (physical_input_ports) { + for (int i = 0; i < num_physical_inputs; i++) { + delete physical_input_ports[i]; + } + delete[] physical_input_ports; + num_physical_inputs = 0; + physical_input_ports = 0; + if (internal_input) { + status = MIDIPortDispose(internal_input); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Close", "MIDIPortDispose", + status); + result = -1; + } + internal_input = 0; + } + } + if (physical_output_ports) { + for (int i = 0; i < num_physical_outputs; i++) { + delete physical_output_ports[i]; + } + delete[] physical_output_ports; + num_physical_outputs = 0; + physical_output_ports = 0; + if (internal_output) { + status = MIDIPortDispose(internal_output); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Close", "MIDIPortDispose", + status); + result = -1; + } + internal_output = 0; + } + } + if (virtual_input_ports) { + for (int i = 0; i < num_virtual_inputs; i++) { + delete virtual_input_ports[i]; + } + delete[] virtual_input_ports; + num_virtual_inputs = 0; + virtual_input_ports = 0; + } + if (virtual_output_ports) { + for (int i = 0; i < num_virtual_outputs; i++) { + delete virtual_output_ports[i]; + } + delete[] virtual_output_ports; + num_virtual_outputs = 0; + virtual_output_ports = 0; + } + + if (client) { + status = MIDIClientDispose(client); + if (status != noErr) { + WriteMacOSError("JackCoreMidiDriver::Close", "MIDIClientDispose", + status); + result = -1; + } + client = 0; + } + return result; +} + +void +JackCoreMidiDriver::Restart() +{ + // Lock between this thread and RT thread + JackLock lock(this); + + // Lock between this thread and request thread + if (fEngine->Lock()) { + // Use first alias + SaveConnections(1); + Stop(); + Detach(); + CloseAux(); + OpenAux(); + Attach(); + Start(); + // Use first alias and partial port naming + LoadConnections(1, false); + fEngine->Unlock(); + } else { + jack_error("Cannot acquire engine lock..."); + } +} + +void +JackCoreMidiDriver::HandleNotification(const MIDINotification *message) +{ + switch (message->messageID) { + + case kMIDIMsgObjectAdded: { + /* + We don't want to restart when our internal virtual in/out are created. + */ + const MIDIObjectAddRemoveNotification* add_message = reinterpret_cast(message); + if (!JackCoreMidiPort::IsInternalPort(add_message->child)) { + Restart(); + } + break; + } + + case kMIDIMsgObjectRemoved: { + /* + We don't want to restart when our internal virtual in/out are created. + */ + const MIDIObjectAddRemoveNotification* remove_message = reinterpret_cast(message); + if (!JackCoreMidiPort::IsInternalPort(remove_message->child)) { + Restart(); + } + break; + } + } +} + +int +JackCoreMidiDriver::Open(bool capturing_aux, bool playing_aux, int in_channels_aux, + int out_channels_aux, bool monitor_aux, + const char* capture_driver_name_aux, + const char* playback_driver_name_aux, + jack_nframes_t capture_latency_aux, + jack_nframes_t playback_latency_aux) +{ + + strcpy(capture_driver_name, capture_driver_name_aux); + strcpy(playback_driver_name, playback_driver_name_aux); + + capturing = capturing_aux; + playing = playing_aux; + in_channels = in_channels_aux; + out_channels = out_channels_aux; + monitor = monitor_aux; + capture_latency = capture_latency_aux; + playback_latency = playback_latency_aux; + + fThread.StartSync(); + + int count = 0; + while (fThread.GetStatus() != JackThread::kRunning && ++count < WAIT_COUNTER) { + JackSleep(100000); + jack_log("JackCoreMidiDriver::Open wait count = %d", count); + + } + if (count == WAIT_COUNTER) { + jack_info("Cannot open CoreMIDI driver"); + fThread.Kill(); + return -1; + } else { + JackSleep(10000); + jack_info("CoreMIDI driver is opened..."); + } + + return 0; +} + +int +JackCoreMidiDriver::Start() +{ + jack_info("JackCoreMidiDriver::Start - Starting driver."); + + JackMidiDriver::Start(); + + int pi_count = 0; + int po_count = 0; + int vi_count = 0; + int vo_count = 0; + + jack_info("JackCoreMidiDriver::Start - Enabling physical input ports."); + + for (; pi_count < num_physical_inputs; pi_count++) { + if (physical_input_ports[pi_count]->Start() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to enable physical " + "input port."); + goto stop_physical_input_ports; + } + } + + jack_info("JackCoreMidiDriver::Start - Enabling physical output ports."); + + for (; po_count < num_physical_outputs; po_count++) { + if (physical_output_ports[po_count]->Start() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to enable physical " + "output port."); + goto stop_physical_output_ports; + } + } + + jack_info("JackCoreMidiDriver::Start - Enabling virtual input ports."); + + for (; vi_count < num_virtual_inputs; vi_count++) { + if (virtual_input_ports[vi_count]->Start() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to enable virtual " + "input port."); + goto stop_virtual_input_ports; + } + } + + jack_info("JackCoreMidiDriver::Start - Enabling virtual output ports."); + + for (; vo_count < num_virtual_outputs; vo_count++) { + if (virtual_output_ports[vo_count]->Start() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to enable virtual " + "output port."); + goto stop_virtual_output_ports; + } + } + + jack_info("JackCoreMidiDriver::Start - Driver started."); + + return 0; + + stop_virtual_output_ports: + for (int i = 0; i < vo_count; i++) { + if (virtual_output_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to disable virtual " + "output port."); + } + } + stop_virtual_input_ports: + for (int i = 0; i < vi_count; i++) { + if (virtual_input_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to disable virtual " + "input port."); + } + } + stop_physical_output_ports: + for (int i = 0; i < po_count; i++) { + if (physical_output_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to disable " + "physical output port."); + } + } + stop_physical_input_ports: + for (int i = 0; i < pi_count; i++) { + if (physical_input_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Start - Failed to disable " + "physical input port."); + } + } + + return -1; +} + +int +JackCoreMidiDriver::Stop() +{ + int result = 0; + + JackMidiDriver::Stop(); + + jack_info("JackCoreMidiDriver::Stop - disabling physical input ports."); + + for (int i = 0; i < num_physical_inputs; i++) { + if (physical_input_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Stop - Failed to disable physical " + "input port."); + result = -1; + } + } + + jack_info("JackCoreMidiDriver::Stop - disabling physical output ports."); + + for (int i = 0; i < num_physical_outputs; i++) { + if (physical_output_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Stop - Failed to disable physical " + "output port."); + result = -1; + } + } + + jack_info("JackCoreMidiDriver::Stop - disabling virtual input ports."); + + for (int i = 0; i < num_virtual_inputs; i++) { + if (virtual_input_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Stop - Failed to disable virtual " + "input port."); + result = -1; + } + } + + jack_info("JackCoreMidiDriver::Stop - disabling virtual output ports."); + + for (int i = 0; i < num_virtual_outputs; i++) { + if (virtual_output_ports[i]->Stop() < 0) { + jack_error("JackCoreMidiDriver::Stop - Failed to disable virtual " + "output port."); + result = -1; + } + } + + return result; +} + +int +JackCoreMidiDriver::ProcessRead() +{ + int res; + if (Trylock()) { + res = (fEngineControl->fSyncMode) ? ProcessReadSync() : ProcessReadAsync(); + Unlock(); + } else { + res = -1; + } + return res; +} + +int +JackCoreMidiDriver::ProcessWrite() +{ + int res; + if (Trylock()) { + res = (fEngineControl->fSyncMode) ? ProcessWriteSync() : ProcessWriteAsync(); + Unlock(); + } else { + res = -1; + } + return res; +} + +int +JackCoreMidiDriver::Read() +{ + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + for (int i = 0; i < num_physical_inputs; i++) { + physical_input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size); + } + for (int i = 0; i < num_virtual_inputs; i++) { + virtual_input_ports[i]-> + ProcessJack(GetInputBuffer(num_physical_inputs + i), buffer_size); + } + return 0; +} + +int +JackCoreMidiDriver::Write() +{ + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + for (int i = 0; i < num_physical_outputs; i++) { + physical_output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size); + } + for (int i = 0; i < num_virtual_outputs; i++) { + virtual_output_ports[i]-> + ProcessJack(GetOutputBuffer(num_physical_outputs + i), buffer_size); + } + return 0; +} + +#ifdef __cplusplus +extern "C" { +#endif + + // singleton kind of driver + static Jack::JackCoreMidiDriver* driver = NULL; + + SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor() + { + jack_driver_desc_t * desc; + jack_driver_desc_filler_t filler; + jack_driver_param_value_t value; + + desc = jack_driver_descriptor_construct("coremidi", JackDriverSlave, "Apple CoreMIDI API based MIDI backend", &filler); + + value.ui = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamUInt, &value, NULL, "CoreMIDI virtual bus", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamUInt, &value, NULL, "CoreMIDI virtual bus", NULL); + + return desc; + } + + SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) + { + const JSList * node; + const jack_driver_param_t * param; + int virtual_in = 2; + int virtual_out = 2; + + for (node = params; node; node = jack_slist_next (node)) { + param = (const jack_driver_param_t *) node->data; + + switch (param->character) { + + case 'i': + virtual_in = param->value.ui; + break; + + case 'o': + virtual_out = param->value.ui; + break; + } + } + + // singleton kind of driver + if (!driver) { + driver = new Jack::JackCoreMidiDriver("system_midi", "coremidi", engine, table); + if (driver->Open(1, 1, virtual_in, virtual_out, false, "in", "out", 0, 0) == 0) { + return driver; + } else { + delete driver; + return NULL; + } + } else { + jack_info("JackCoreMidiDriver already allocated, cannot be loaded twice"); + return NULL; + } + } + +#ifdef __cplusplus +} +#endif diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiInputPort.cpp jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiInputPort.cpp --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiInputPort.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiInputPort.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,257 +0,0 @@ -/* -Copyright (C) 2011 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include - -#include "JackCoreMidiInputPort.h" -#include "JackMidiUtil.h" -#include "JackError.h" - -using Jack::JackCoreMidiInputPort; - -/** - * Takes a MIDI status byte as argument and returns the expected size of the - * associated MIDI event. Returns -1 on invalid status bytes AND on variable - * size events (SysEx events). - */ -inline static int _expectedEventSize(const unsigned char& byte) { - if (byte < 0x80) return -1; // not a valid status byte - if (byte < 0xC0) return 3; // note on/off, note pressure, control change - if (byte < 0xE0) return 2; // program change, channel pressure - if (byte < 0xF0) return 3; // pitch wheel - if (byte == 0xF0) return -1; // sysex message (variable size) - if (byte == 0xF1) return 2; // time code per quarter frame - if (byte == 0xF2) return 3; // sys. common song position pointer - if (byte == 0xF3) return 2; // sys. common song select - if (byte == 0xF4) return -1; // sys. common undefined / reserved - if (byte == 0xF5) return -1; // sys. common undefined / reserved - return 1; // tune request, end of SysEx, system real-time events -} - -JackCoreMidiInputPort::JackCoreMidiInputPort(double time_ratio, - size_t max_bytes, - size_t max_messages): - JackCoreMidiPort(time_ratio) -{ - thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); - std::auto_ptr thread_queue_ptr(thread_queue); - write_queue = new JackMidiBufferWriteQueue(); - std::auto_ptr write_queue_ptr(write_queue); - sysex_buffer = new jack_midi_data_t[max_bytes]; - write_queue_ptr.release(); - thread_queue_ptr.release(); - jack_event = 0; - running_status_buf[0] = 0; -} - -JackCoreMidiInputPort::~JackCoreMidiInputPort() -{ - delete thread_queue; - delete write_queue; - delete[] sysex_buffer; -} - -jack_nframes_t -JackCoreMidiInputPort::GetFramesFromTimeStamp(MIDITimeStamp timestamp) -{ - return GetFramesFromTime((jack_time_t) (timestamp * time_ratio)); -} - -void -JackCoreMidiInputPort::Initialize(const char *alias_name, - const char *client_name, - const char *driver_name, int index, - MIDIEndpointRef endpoint) -{ - JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, endpoint, false); -} - -void -JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list) -{ - set_threaded_log_function(); - - // TODO: maybe parsing should be done by JackMidiRawInputWriteQueue instead - - unsigned int packet_count = packet_list->numPackets; - assert(packet_count); - MIDIPacket *packet = (MIDIPacket *) packet_list->packet; - for (unsigned int i = 0; i < packet_count; i++) { - jack_midi_data_t *data = packet->data; - size_t size = packet->length; - assert(size); - jack_midi_event_t event; - // In a MIDIPacket there can be more than one (non SysEx) MIDI event. - // However if the packet contains a SysEx event, it is guaranteed that - // there are no other events in the same MIDIPacket. - int k = 0; // index of the current MIDI event within current MIDIPacket - int eventSize = 0; // theoretical size of the current MIDI event - int chunkSize = 0; // actual size of the current MIDI event data consumed - - // XX: There might be dragons in my spaghetti. This code is begging - // for a rewrite. - - if (sysex_bytes_sent) { - if (data[0] & 0x80) { - jack_error("JackCoreMidiInputPort::ProcessCoreMidi - System " - "exclusive message aborted."); - sysex_bytes_sent = 0; - goto parse_event; - } - buffer_sysex_bytes: - if ((sysex_bytes_sent + size) <= sizeof(sysex_buffer)) { - memcpy(sysex_buffer + sysex_bytes_sent, packet, - size * sizeof(jack_midi_data_t)); - } - sysex_bytes_sent += size; - if (data[size - 1] == 0xf7) { - if (sysex_bytes_sent > sizeof(sysex_buffer)) { - jack_error("JackCoreMidiInputPort::ProcessCoreMidi - " - "Could not buffer a %d-byte system exclusive " - "message. Discarding message.", - sysex_bytes_sent); - sysex_bytes_sent = 0; - goto get_next_packet; - } - event.buffer = sysex_buffer; - event.size = sysex_bytes_sent; - sysex_bytes_sent = 0; - k = size; // don't loop in a MIDIPacket if its a SysEx - goto send_event; - } - goto get_next_packet; - } - - parse_event: - if (data[k+0] == 0xf0) { - // Must actually never happen, since CoreMIDI guarantees a SysEx - // message to be alone in one MIDIPaket, but safety first. The SysEx - // buffer code is not written to handle this case, so skip packet. - if (k != 0) { - jack_error("JackCoreMidiInputPort::ProcessCoreMidi - Non " - "isolated SysEx message in one packet, discarding."); - goto get_next_packet; - } - - if (data[size - 1] != 0xf7) { - goto buffer_sysex_bytes; - } - } - - // not a regular status byte ? - if (!(data[k+0] & 0x80) && running_status_buf[0]) { // "running status" mode ... - eventSize = _expectedEventSize(running_status_buf[0]); - chunkSize = (eventSize < 0) ? size - k : eventSize - 1; - if (chunkSize <= 0) goto get_next_packet; - if (chunkSize + 1 <= sizeof(running_status_buf)) { - memcpy(&running_status_buf[1], &data[k], chunkSize); - event.buffer = running_status_buf; - event.size = chunkSize + 1; - k += chunkSize; - goto send_event; - } - } - - // valid status byte (or invalid "running status") ... - - eventSize = _expectedEventSize(data[k+0]); - if (eventSize < 0) eventSize = size - k; - if (eventSize <= 0) goto get_next_packet; - event.buffer = &data[k]; - event.size = eventSize; - // store status byte for eventual "running status" in next event - if (data[k+0] & 0x80) { - if (data[k+0] < 0xf0) { - // "running status" is only allowed for channel messages - running_status_buf[0] = data[k+0]; - } else if (data[k+0] < 0xf8) { - // "system common" messages (0xf0..0xf7) shall reset any running - // status, however "realtime" messages (0xf8..0xff) shall be - // ignored here - running_status_buf[0] = 0; - } - } - k += eventSize; - - send_event: - event.time = GetFramesFromTimeStamp(packet->timeStamp); - switch (thread_queue->EnqueueEvent(&event)) { - case JackMidiWriteQueue::BUFFER_FULL: - jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread " - "queue buffer is full. Dropping event."); - break; - case JackMidiWriteQueue::BUFFER_TOO_SMALL: - jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread " - "queue couldn't enqueue a %d-byte packet. Dropping " - "event.", event.size); - break; - default: - ; - } - if (k < size) goto parse_event; - - get_next_packet: - packet = MIDIPacketNext(packet); - assert(packet); - } -} - -void -JackCoreMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer, - jack_nframes_t frames) -{ - write_queue->ResetMidiBuffer(port_buffer, frames); - if (! jack_event) { - jack_event = thread_queue->DequeueEvent(); - } - - for (; jack_event; jack_event = thread_queue->DequeueEvent()) { - // Add 'frames' to MIDI events to align with audio. - switch (write_queue->EnqueueEvent(jack_event, frames)) { - case JackMidiWriteQueue::BUFFER_TOO_SMALL: - jack_error("JackCoreMidiInputPort::ProcessJack - The write queue " - "couldn't enqueue a %d-byte event. Dropping event.", - jack_event->size); - // Fallthrough on purpose - case JackMidiWriteQueue::OK: - continue; - default: - ; - } - break; - } -} - -bool -JackCoreMidiInputPort::Start() -{ - // Hack: Get rid of any messages that might have come in before starting - // the engine. - while (thread_queue->DequeueEvent()); - sysex_bytes_sent = 0; - running_status_buf[0] = 0; - return true; -} - -bool -JackCoreMidiInputPort::Stop() -{ - return true; -} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiInputPort.mm jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiInputPort.mm --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiInputPort.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiInputPort.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,257 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackCoreMidiInputPort.h" +#include "JackMidiUtil.h" +#include "JackError.h" + +using Jack::JackCoreMidiInputPort; + +/** + * Takes a MIDI status byte as argument and returns the expected size of the + * associated MIDI event. Returns -1 on invalid status bytes AND on variable + * size events (SysEx events). + */ +inline static int _expectedEventSize(const unsigned char& byte) { + if (byte < 0x80) return -1; // not a valid status byte + if (byte < 0xC0) return 3; // note on/off, note pressure, control change + if (byte < 0xE0) return 2; // program change, channel pressure + if (byte < 0xF0) return 3; // pitch wheel + if (byte == 0xF0) return -1; // sysex message (variable size) + if (byte == 0xF1) return 2; // time code per quarter frame + if (byte == 0xF2) return 3; // sys. common song position pointer + if (byte == 0xF3) return 2; // sys. common song select + if (byte == 0xF4) return -1; // sys. common undefined / reserved + if (byte == 0xF5) return -1; // sys. common undefined / reserved + return 1; // tune request, end of SysEx, system real-time events +} + +JackCoreMidiInputPort::JackCoreMidiInputPort(double time_ratio, + size_t max_bytes, + size_t max_messages): + JackCoreMidiPort(time_ratio) +{ + thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); + std::auto_ptr thread_queue_ptr(thread_queue); + write_queue = new JackMidiBufferWriteQueue(); + std::auto_ptr write_queue_ptr(write_queue); + sysex_buffer = new jack_midi_data_t[max_bytes]; + write_queue_ptr.release(); + thread_queue_ptr.release(); + jack_event = 0; + running_status_buf[0] = 0; +} + +JackCoreMidiInputPort::~JackCoreMidiInputPort() +{ + delete thread_queue; + delete write_queue; + delete[] sysex_buffer; +} + +jack_nframes_t +JackCoreMidiInputPort::GetFramesFromTimeStamp(MIDITimeStamp timestamp) +{ + return GetFramesFromTime((jack_time_t) (timestamp * time_ratio)); +} + +void +JackCoreMidiInputPort::Initialize(const char *alias_name, + const char *client_name, + const char *driver_name, int index, + MIDIEndpointRef endpoint) +{ + JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, endpoint, false); +} + +void +JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list) +{ + set_threaded_log_function(); + + // TODO: maybe parsing should be done by JackMidiRawInputWriteQueue instead + + unsigned int packet_count = packet_list->numPackets; + assert(packet_count); + MIDIPacket *packet = (MIDIPacket *) packet_list->packet; + for (unsigned int i = 0; i < packet_count; i++) { + jack_midi_data_t *data = packet->data; + size_t size = packet->length; + assert(size); + jack_midi_event_t event; + // In a MIDIPacket there can be more than one (non SysEx) MIDI event. + // However if the packet contains a SysEx event, it is guaranteed that + // there are no other events in the same MIDIPacket. + int k = 0; // index of the current MIDI event within current MIDIPacket + int eventSize = 0; // theoretical size of the current MIDI event + int chunkSize = 0; // actual size of the current MIDI event data consumed + + // XX: There might be dragons in my spaghetti. This code is begging + // for a rewrite. + + if (sysex_bytes_sent) { + if (data[0] & 0x80) { + jack_error("JackCoreMidiInputPort::ProcessCoreMidi - System " + "exclusive message aborted."); + sysex_bytes_sent = 0; + goto parse_event; + } + buffer_sysex_bytes: + if ((sysex_bytes_sent + size) <= sizeof(sysex_buffer)) { + memcpy(sysex_buffer + sysex_bytes_sent, packet, + size * sizeof(jack_midi_data_t)); + } + sysex_bytes_sent += size; + if (data[size - 1] == 0xf7) { + if (sysex_bytes_sent > sizeof(sysex_buffer)) { + jack_error("JackCoreMidiInputPort::ProcessCoreMidi - " + "Could not buffer a %d-byte system exclusive " + "message. Discarding message.", + sysex_bytes_sent); + sysex_bytes_sent = 0; + goto get_next_packet; + } + event.buffer = sysex_buffer; + event.size = sysex_bytes_sent; + sysex_bytes_sent = 0; + k = size; // don't loop in a MIDIPacket if its a SysEx + goto send_event; + } + goto get_next_packet; + } + + parse_event: + if (data[k+0] == 0xf0) { + // Must actually never happen, since CoreMIDI guarantees a SysEx + // message to be alone in one MIDIPaket, but safety first. The SysEx + // buffer code is not written to handle this case, so skip packet. + if (k != 0) { + jack_error("JackCoreMidiInputPort::ProcessCoreMidi - Non " + "isolated SysEx message in one packet, discarding."); + goto get_next_packet; + } + + if (data[size - 1] != 0xf7) { + goto buffer_sysex_bytes; + } + } + + // not a regular status byte ? + if (!(data[k+0] & 0x80) && running_status_buf[0]) { // "running status" mode ... + eventSize = _expectedEventSize(running_status_buf[0]); + chunkSize = (eventSize < 0) ? size - k : eventSize - 1; + if (chunkSize <= 0) goto get_next_packet; + if (chunkSize + 1 <= sizeof(running_status_buf)) { + memcpy(&running_status_buf[1], &data[k], chunkSize); + event.buffer = running_status_buf; + event.size = chunkSize + 1; + k += chunkSize; + goto send_event; + } + } + + // valid status byte (or invalid "running status") ... + + eventSize = _expectedEventSize(data[k+0]); + if (eventSize < 0) eventSize = size - k; + if (eventSize <= 0) goto get_next_packet; + event.buffer = &data[k]; + event.size = eventSize; + // store status byte for eventual "running status" in next event + if (data[k+0] & 0x80) { + if (data[k+0] < 0xf0) { + // "running status" is only allowed for channel messages + running_status_buf[0] = data[k+0]; + } else if (data[k+0] < 0xf8) { + // "system common" messages (0xf0..0xf7) shall reset any running + // status, however "realtime" messages (0xf8..0xff) shall be + // ignored here + running_status_buf[0] = 0; + } + } + k += eventSize; + + send_event: + event.time = GetFramesFromTimeStamp(packet->timeStamp); + switch (thread_queue->EnqueueEvent(&event)) { + case JackMidiWriteQueue::BUFFER_FULL: + jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread " + "queue buffer is full. Dropping event."); + break; + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread " + "queue couldn't enqueue a %d-byte packet. Dropping " + "event.", event.size); + break; + default: + ; + } + if (k < size) goto parse_event; + + get_next_packet: + packet = MIDIPacketNext(packet); + assert(packet); + } +} + +void +JackCoreMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer, + jack_nframes_t frames) +{ + write_queue->ResetMidiBuffer(port_buffer, frames); + if (! jack_event) { + jack_event = thread_queue->DequeueEvent(); + } + + for (; jack_event; jack_event = thread_queue->DequeueEvent()) { + // Add 'frames' to MIDI events to align with audio. + switch (write_queue->EnqueueEvent(jack_event, frames)) { + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackCoreMidiInputPort::ProcessJack - The write queue " + "couldn't enqueue a %d-byte event. Dropping event.", + jack_event->size); + // Fallthrough on purpose + case JackMidiWriteQueue::OK: + continue; + default: + ; + } + break; + } +} + +bool +JackCoreMidiInputPort::Start() +{ + // Hack: Get rid of any messages that might have come in before starting + // the engine. + while (thread_queue->DequeueEvent()); + sysex_bytes_sent = 0; + running_status_buf[0] = 0; + return true; +} + +bool +JackCoreMidiInputPort::Stop() +{ + return true; +} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiOutputPort.cpp jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiOutputPort.cpp --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiOutputPort.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiOutputPort.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,251 +0,0 @@ -/* -Copyright (C) 2011 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include -#include -#include -#include - -#include "JackCoreMidiOutputPort.h" -#include "JackMidiUtil.h" -#include "JackTime.h" -#include "JackError.h" - -using Jack::JackCoreMidiOutputPort; - -JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio, - size_t max_bytes, - size_t max_messages): - JackCoreMidiPort(time_ratio) -{ - read_queue = new JackMidiBufferReadQueue(); - std::auto_ptr read_queue_ptr(read_queue); - thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); - std::auto_ptr thread_queue_ptr(thread_queue); - thread = new JackThread(this); - std::auto_ptr thread_ptr(thread); - snprintf(semaphore_name, sizeof(semaphore_name), "coremidi_%p", this); - thread_queue_semaphore = sem_open(semaphore_name, O_CREAT, 0777, 0); - if (thread_queue_semaphore == (sem_t *) SEM_FAILED) { - throw std::runtime_error(strerror(errno)); - } - advance_schedule_time = 0; - thread_ptr.release(); - thread_queue_ptr.release(); - read_queue_ptr.release(); -} - -JackCoreMidiOutputPort::~JackCoreMidiOutputPort() -{ - delete thread; - sem_close(thread_queue_semaphore); - sem_unlink(semaphore_name); - delete read_queue; - delete thread_queue; -} - -bool -JackCoreMidiOutputPort::Execute() -{ - jack_midi_event_t *event = 0; - MIDIPacketList *packet_list = (MIDIPacketList *) packet_buffer; - for (;;) { - MIDIPacket *packet = MIDIPacketListInit(packet_list); - assert(packet); - if (! event) { - event = GetCoreMidiEvent(true); - } - jack_midi_data_t *data = event->buffer; - jack_nframes_t send_frame = event->time; - jack_time_t send_time = - GetTimeFromFrames(send_frame) - advance_schedule_time; - size_t size = event->size; - MIDITimeStamp timestamp = GetTimeStampFromFrames(send_frame); - packet = MIDIPacketListAdd(packet_list, PACKET_BUFFER_SIZE, packet, - timestamp, size, data); - if (packet) { - do { - if (GetMicroSeconds() >= send_time) { - event = 0; - break; - } - event = GetCoreMidiEvent(false); - if (! event) { - break; - } - packet = MIDIPacketListAdd(packet_list, sizeof(packet_buffer), - packet, - GetTimeStampFromFrames(event->time), - event->size, event->buffer); - } while (packet); - SendPacketList(packet_list); - } else { - - // We have a large system exclusive event. We'll have to send it - // out in multiple packets. - size_t bytes_sent = 0; - do { - packet = MIDIPacketListInit(packet_list); - assert(packet); - size_t num_bytes = 0; - for (; bytes_sent < size; bytes_sent += num_bytes) { - size_t num_bytes = size - bytes_sent; - - // We use 256 because the MIDIPacket struct defines the - // size of the 'data' member to be 256 bytes. I believe - // this prevents packets from being dynamically allocated - // by 'MIDIPacketListAdd', but I might be wrong. - if (num_bytes > 256) { - num_bytes = 256; - } - packet = MIDIPacketListAdd(packet_list, - sizeof(packet_buffer), packet, - timestamp, num_bytes, - data + bytes_sent); - if (! packet) { - break; - } - } - if (! SendPacketList(packet_list)) { - // An error occurred. The error message has already been - // output. We lick our wounds and move along. - break; - } - } while (bytes_sent < size); - event = 0; - } - } - return false; -} - -jack_midi_event_t * -JackCoreMidiOutputPort::GetCoreMidiEvent(bool block) -{ - if (! block) { - if (sem_trywait(thread_queue_semaphore)) { - if (errno != EAGAIN) { - jack_error("JackCoreMidiOutputPort::Execute - sem_trywait: %s", - strerror(errno)); - } - return 0; - } - } else { - while (sem_wait(thread_queue_semaphore)) { - if (errno != EINTR) { - jack_error("JackCoreMidiOutputPort::Execute - sem_wait: %s", - strerror(errno)); - return 0; - } - } - } - return thread_queue->DequeueEvent(); -} - -MIDITimeStamp -JackCoreMidiOutputPort::GetTimeStampFromFrames(jack_nframes_t frames) -{ - return GetTimeFromFrames(frames) / time_ratio; -} - -bool -JackCoreMidiOutputPort::Init() -{ - set_threaded_log_function(); - - // OSX only, values read in RT CoreMIDI thread - UInt64 period = 0; - UInt64 computation = 250 * 1000; - UInt64 constraint = 500 * 1000; - thread->SetParams(period, computation, constraint); - - if (thread->AcquireSelfRealTime()) { - jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime " - "scheduling. Continuing anyway."); - } - return true; -} - -void -JackCoreMidiOutputPort::Initialize(const char *alias_name, - const char *client_name, - const char *driver_name, int index, - MIDIEndpointRef endpoint, - SInt32 advance_schedule_time) -{ - JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, - endpoint, true); - assert(advance_schedule_time >= 0); - this->advance_schedule_time = advance_schedule_time; -} - -void -JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer, - jack_nframes_t frames) -{ - read_queue->ResetMidiBuffer(port_buffer); - for (jack_midi_event_t *event = read_queue->DequeueEvent(); event; - event = read_queue->DequeueEvent()) { - switch (thread_queue->EnqueueEvent(event, frames)) { - case JackMidiWriteQueue::BUFFER_FULL: - jack_error("JackCoreMidiOutputPort::ProcessJack - The thread " - "queue buffer is full. Dropping event."); - break; - case JackMidiWriteQueue::BUFFER_TOO_SMALL: - jack_error("JackCoreMidiOutputPort::ProcessJack - The thread " - "queue couldn't enqueue a %d-byte event. Dropping " - "event.", event->size); - break; - default: - if (sem_post(thread_queue_semaphore)) { - jack_error("JackCoreMidiOutputPort::ProcessJack - unexpected " - "error while posting to thread queue semaphore: %s", - strerror(errno)); - } - } - } -} - -bool -JackCoreMidiOutputPort::Start() -{ - bool result = thread->GetStatus() != JackThread::kIdle; - if (! result) { - result = ! thread->StartSync(); - if (! result) { - jack_error("JackCoreMidiOutputPort::Start - failed to start MIDI " - "processing thread."); - } - } - return result; -} - -bool -JackCoreMidiOutputPort::Stop() -{ - bool result = thread->GetStatus() == JackThread::kIdle; - if (! result) { - result = ! thread->Kill(); - if (! result) { - jack_error("JackCoreMidiOutputPort::Stop - failed to stop MIDI " - "processing thread."); - } - } - return result; -} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiOutputPort.mm jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiOutputPort.mm --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiOutputPort.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiOutputPort.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,251 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include + +#include "JackCoreMidiOutputPort.h" +#include "JackMidiUtil.h" +#include "JackTime.h" +#include "JackError.h" + +using Jack::JackCoreMidiOutputPort; + +JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio, + size_t max_bytes, + size_t max_messages): + JackCoreMidiPort(time_ratio) +{ + read_queue = new JackMidiBufferReadQueue(); + std::auto_ptr read_queue_ptr(read_queue); + thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); + std::auto_ptr thread_queue_ptr(thread_queue); + thread = new JackThread(this); + std::auto_ptr thread_ptr(thread); + snprintf(semaphore_name, sizeof(semaphore_name), "coremidi_%p", this); + thread_queue_semaphore = sem_open(semaphore_name, O_CREAT, 0777, 0); + if (thread_queue_semaphore == (sem_t *) SEM_FAILED) { + throw std::runtime_error(strerror(errno)); + } + advance_schedule_time = 0; + thread_ptr.release(); + thread_queue_ptr.release(); + read_queue_ptr.release(); +} + +JackCoreMidiOutputPort::~JackCoreMidiOutputPort() +{ + delete thread; + sem_close(thread_queue_semaphore); + sem_unlink(semaphore_name); + delete read_queue; + delete thread_queue; +} + +bool +JackCoreMidiOutputPort::Execute() +{ + jack_midi_event_t *event = 0; + MIDIPacketList *packet_list = (MIDIPacketList *) packet_buffer; + for (;;) { + MIDIPacket *packet = MIDIPacketListInit(packet_list); + assert(packet); + if (! event) { + event = GetCoreMidiEvent(true); + } + jack_midi_data_t *data = event->buffer; + jack_nframes_t send_frame = event->time; + jack_time_t send_time = + GetTimeFromFrames(send_frame) - advance_schedule_time; + size_t size = event->size; + MIDITimeStamp timestamp = GetTimeStampFromFrames(send_frame); + packet = MIDIPacketListAdd(packet_list, PACKET_BUFFER_SIZE, packet, + timestamp, size, data); + if (packet) { + do { + if (GetMicroSeconds() >= send_time) { + event = 0; + break; + } + event = GetCoreMidiEvent(false); + if (! event) { + break; + } + packet = MIDIPacketListAdd(packet_list, sizeof(packet_buffer), + packet, + GetTimeStampFromFrames(event->time), + event->size, event->buffer); + } while (packet); + SendPacketList(packet_list); + } else { + + // We have a large system exclusive event. We'll have to send it + // out in multiple packets. + size_t bytes_sent = 0; + do { + packet = MIDIPacketListInit(packet_list); + assert(packet); + size_t num_bytes = 0; + for (; bytes_sent < size; bytes_sent += num_bytes) { + size_t num_bytes = size - bytes_sent; + + // We use 256 because the MIDIPacket struct defines the + // size of the 'data' member to be 256 bytes. I believe + // this prevents packets from being dynamically allocated + // by 'MIDIPacketListAdd', but I might be wrong. + if (num_bytes > 256) { + num_bytes = 256; + } + packet = MIDIPacketListAdd(packet_list, + sizeof(packet_buffer), packet, + timestamp, num_bytes, + data + bytes_sent); + if (! packet) { + break; + } + } + if (! SendPacketList(packet_list)) { + // An error occurred. The error message has already been + // output. We lick our wounds and move along. + break; + } + } while (bytes_sent < size); + event = 0; + } + } + return false; +} + +jack_midi_event_t * +JackCoreMidiOutputPort::GetCoreMidiEvent(bool block) +{ + if (! block) { + if (sem_trywait(thread_queue_semaphore)) { + if (errno != EAGAIN) { + jack_error("JackCoreMidiOutputPort::Execute - sem_trywait: %s", + strerror(errno)); + } + return 0; + } + } else { + while (sem_wait(thread_queue_semaphore)) { + if (errno != EINTR) { + jack_error("JackCoreMidiOutputPort::Execute - sem_wait: %s", + strerror(errno)); + return 0; + } + } + } + return thread_queue->DequeueEvent(); +} + +MIDITimeStamp +JackCoreMidiOutputPort::GetTimeStampFromFrames(jack_nframes_t frames) +{ + return GetTimeFromFrames(frames) / time_ratio; +} + +bool +JackCoreMidiOutputPort::Init() +{ + set_threaded_log_function(); + + // OSX only, values read in RT CoreMIDI thread + UInt64 period = 0; + UInt64 computation = 250 * 1000; + UInt64 constraint = 500 * 1000; + thread->SetParams(period, computation, constraint); + + if (thread->AcquireSelfRealTime()) { + jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime " + "scheduling. Continuing anyway."); + } + return true; +} + +void +JackCoreMidiOutputPort::Initialize(const char *alias_name, + const char *client_name, + const char *driver_name, int index, + MIDIEndpointRef endpoint, + SInt32 advance_schedule_time) +{ + JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, + endpoint, true); + assert(advance_schedule_time >= 0); + this->advance_schedule_time = advance_schedule_time; +} + +void +JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer, + jack_nframes_t frames) +{ + read_queue->ResetMidiBuffer(port_buffer); + for (jack_midi_event_t *event = read_queue->DequeueEvent(); event; + event = read_queue->DequeueEvent()) { + switch (thread_queue->EnqueueEvent(event, frames)) { + case JackMidiWriteQueue::BUFFER_FULL: + jack_error("JackCoreMidiOutputPort::ProcessJack - The thread " + "queue buffer is full. Dropping event."); + break; + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackCoreMidiOutputPort::ProcessJack - The thread " + "queue couldn't enqueue a %d-byte event. Dropping " + "event.", event->size); + break; + default: + if (sem_post(thread_queue_semaphore)) { + jack_error("JackCoreMidiOutputPort::ProcessJack - unexpected " + "error while posting to thread queue semaphore: %s", + strerror(errno)); + } + } + } +} + +bool +JackCoreMidiOutputPort::Start() +{ + bool result = thread->GetStatus() != JackThread::kIdle; + if (! result) { + result = ! thread->StartSync(); + if (! result) { + jack_error("JackCoreMidiOutputPort::Start - failed to start MIDI " + "processing thread."); + } + } + return result; +} + +bool +JackCoreMidiOutputPort::Stop() +{ + bool result = thread->GetStatus() == JackThread::kIdle; + if (! result) { + result = ! thread->Kill(); + if (! result) { + jack_error("JackCoreMidiOutputPort::Stop - failed to stop MIDI " + "processing thread."); + } + } + return result; +} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiPhysicalInputPort.cpp jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiPhysicalInputPort.cpp --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiPhysicalInputPort.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiPhysicalInputPort.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -/* -Copyright (C) 2011 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include - -#include "JackCoreMidiPhysicalInputPort.h" -#include "JackCoreMidiUtil.h" - -using Jack::JackCoreMidiPhysicalInputPort; - -JackCoreMidiPhysicalInputPort:: -JackCoreMidiPhysicalInputPort(const char *alias_name, const char *client_name, - const char *driver_name, int index, - MIDIClientRef client, MIDIPortRef internal_input, - double time_ratio, size_t max_bytes, - size_t max_messages): - JackCoreMidiInputPort(time_ratio, max_bytes, max_messages) -{ - MIDIEndpointRef source = MIDIGetSource(index); - if (! source) { - // X: Is there a way to get a better error message? - std::stringstream stream; - stream << "The source at index '" << index << "' is not available"; - throw std::runtime_error(stream.str().c_str()); - } - OSStatus status = MIDIPortConnectSource(internal_input, source, this); - if (status != noErr) { - throw std::runtime_error(GetMacOSErrorString(status)); - } - Initialize(alias_name, client_name, driver_name, index, source); -} - -JackCoreMidiPhysicalInputPort::~JackCoreMidiPhysicalInputPort() -{ - // Empty -} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiPhysicalInputPort.mm jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiPhysicalInputPort.mm --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiPhysicalInputPort.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiPhysicalInputPort.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,53 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackCoreMidiPhysicalInputPort.h" +#include "JackCoreMidiUtil.h" + +using Jack::JackCoreMidiPhysicalInputPort; + +JackCoreMidiPhysicalInputPort:: +JackCoreMidiPhysicalInputPort(const char *alias_name, const char *client_name, + const char *driver_name, int index, + MIDIClientRef client, MIDIPortRef internal_input, + double time_ratio, size_t max_bytes, + size_t max_messages): + JackCoreMidiInputPort(time_ratio, max_bytes, max_messages) +{ + MIDIEndpointRef source = MIDIGetSource(index); + if (! source) { + // X: Is there a way to get a better error message? + std::stringstream stream; + stream << "The source at index '" << index << "' is not available"; + throw std::runtime_error(stream.str().c_str()); + } + OSStatus status = MIDIPortConnectSource(internal_input, source, this); + if (status != noErr) { + throw std::runtime_error(GetMacOSErrorString(status)); + } + Initialize(alias_name, client_name, driver_name, index, source); +} + +JackCoreMidiPhysicalInputPort::~JackCoreMidiPhysicalInputPort() +{ + // Empty +} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -/* -Copyright (C) 2011 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include - -#include "JackCoreMidiPhysicalOutputPort.h" -#include "JackCoreMidiUtil.h" - -using Jack::JackCoreMidiPhysicalOutputPort; - -JackCoreMidiPhysicalOutputPort:: -JackCoreMidiPhysicalOutputPort(const char *alias_name, const char *client_name, - const char *driver_name, int index, - MIDIClientRef client, - MIDIPortRef internal_output, double time_ratio, - size_t max_bytes, - size_t max_messages): - JackCoreMidiOutputPort(time_ratio, max_bytes, - max_messages) -{ - MIDIEndpointRef destination = MIDIGetDestination(index); - if (! destination) { - // X: Can we get a better error message? - std::stringstream stream; - stream << "The destination at index '" << index - << "' is not available"; - throw std::runtime_error(stream.str().c_str()); - } - SInt32 advance_schedule_time; - OSStatus status = - MIDIObjectGetIntegerProperty(destination, - kMIDIPropertyAdvanceScheduleTimeMuSec, - &advance_schedule_time); - if (status != noErr) { - WriteMacOSError("JackCoreMidiPhysicalOutputPort [constructor]", - "MIDIObjectGetIntegerProperty", status); - advance_schedule_time = 0; - } else if (advance_schedule_time < 0) { - advance_schedule_time = 0; - } - Initialize(alias_name, client_name, driver_name, index, destination, - advance_schedule_time); - this->internal_output = internal_output; -} - -JackCoreMidiPhysicalOutputPort::~JackCoreMidiPhysicalOutputPort() -{ - // Empty -} - -bool -JackCoreMidiPhysicalOutputPort::SendPacketList(MIDIPacketList *packet_list) -{ - OSStatus status = MIDISend(internal_output, endpoint, packet_list); - bool result = status == noErr; - if (! result) { - WriteMacOSError("JackCoreMidiPhysicalOutputPort::SendPacketList", - "MIDISend", status); - } - return result; -} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiPhysicalOutputPort.mm jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiPhysicalOutputPort.mm --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiPhysicalOutputPort.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiPhysicalOutputPort.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,78 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackCoreMidiPhysicalOutputPort.h" +#include "JackCoreMidiUtil.h" + +using Jack::JackCoreMidiPhysicalOutputPort; + +JackCoreMidiPhysicalOutputPort:: +JackCoreMidiPhysicalOutputPort(const char *alias_name, const char *client_name, + const char *driver_name, int index, + MIDIClientRef client, + MIDIPortRef internal_output, double time_ratio, + size_t max_bytes, + size_t max_messages): + JackCoreMidiOutputPort(time_ratio, max_bytes, + max_messages) +{ + MIDIEndpointRef destination = MIDIGetDestination(index); + if (! destination) { + // X: Can we get a better error message? + std::stringstream stream; + stream << "The destination at index '" << index + << "' is not available"; + throw std::runtime_error(stream.str().c_str()); + } + SInt32 advance_schedule_time; + OSStatus status = + MIDIObjectGetIntegerProperty(destination, + kMIDIPropertyAdvanceScheduleTimeMuSec, + &advance_schedule_time); + if (status != noErr) { + WriteMacOSError("JackCoreMidiPhysicalOutputPort [constructor]", + "MIDIObjectGetIntegerProperty", status); + advance_schedule_time = 0; + } else if (advance_schedule_time < 0) { + advance_schedule_time = 0; + } + Initialize(alias_name, client_name, driver_name, index, destination, + advance_schedule_time); + this->internal_output = internal_output; +} + +JackCoreMidiPhysicalOutputPort::~JackCoreMidiPhysicalOutputPort() +{ + // Empty +} + +bool +JackCoreMidiPhysicalOutputPort::SendPacketList(MIDIPacketList *packet_list) +{ + OSStatus status = MIDISend(internal_output, endpoint, packet_list); + bool result = status == noErr; + if (! result) { + WriteMacOSError("JackCoreMidiPhysicalOutputPort::SendPacketList", + "MIDISend", status); + } + return result; +} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiPort.cpp jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiPort.cpp --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiPort.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiPort.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,101 +0,0 @@ -/* -Copyright (C) 2011 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include - -#include "JackCoreMidiPort.h" -#include "JackCoreMidiUtil.h" -#include "JackError.h" - -using Jack::JackCoreMidiPort; - -std::set JackCoreMidiPort::endpoint_list; - -bool JackCoreMidiPort::IsInternalPort(MIDIObjectRef port_aux) -{ - MIDIEndpointRef port = (MIDIEndpointRef)port_aux; - return std::find(endpoint_list.begin(), endpoint_list.end(), port) != endpoint_list.end(); -} - -JackCoreMidiPort::JackCoreMidiPort(double time_ratio) -{ - initialized = false; - this->time_ratio = time_ratio; -} - -JackCoreMidiPort::~JackCoreMidiPort() -{ - // Empty -} - -const char * -JackCoreMidiPort::GetAlias() -{ - assert(initialized); - return alias; -} - -MIDIEndpointRef -JackCoreMidiPort::GetEndpoint() -{ - assert(initialized); - return endpoint; -} - -const char * -JackCoreMidiPort::GetName() -{ - assert(initialized); - return name; -} - -void -JackCoreMidiPort::Initialize(const char *alias_name, const char *client_name, - const char *driver_name, int index, - MIDIEndpointRef endpoint, bool is_output) -{ - char endpoint_name[REAL_JACK_PORT_NAME_SIZE+1]; - CFStringRef endpoint_name_ref; - int num = index + 1; - Boolean res; - OSStatus result = MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, - &endpoint_name_ref); - if (result != noErr) { - WriteMacOSError("JackCoreMidiPort::Initialize", - "MIDIObjectGetStringProperty", result); - goto get_basic_alias; - } - res = CFStringGetCString(endpoint_name_ref, endpoint_name, - sizeof(endpoint_name), 0); - CFRelease(endpoint_name_ref); - if (!res) { - jack_error("JackCoreMidiPort::Initialize - failed to allocate memory " - "for endpoint name."); - get_basic_alias: - snprintf(alias, sizeof(alias), "%s:%s:%s%d", alias_name, - driver_name, is_output ? "in" : "out", num); - } else { - snprintf(alias, sizeof(alias), "%s:%s:%s%d", alias_name, - endpoint_name, is_output ? "in" : "out", num); - } - snprintf(name, sizeof(name), "%s:%s_%d", client_name, - is_output ? "playback" : "capture", num); - this->endpoint = endpoint; - initialized = true; -} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiPort.mm jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiPort.mm --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiPort.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiPort.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,101 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include "JackCoreMidiPort.h" +#include "JackCoreMidiUtil.h" +#include "JackError.h" + +using Jack::JackCoreMidiPort; + +std::set JackCoreMidiPort::endpoint_list; + +bool JackCoreMidiPort::IsInternalPort(MIDIObjectRef port_aux) +{ + MIDIEndpointRef port = (MIDIEndpointRef)port_aux; + return std::find(endpoint_list.begin(), endpoint_list.end(), port) != endpoint_list.end(); +} + +JackCoreMidiPort::JackCoreMidiPort(double time_ratio) +{ + initialized = false; + this->time_ratio = time_ratio; +} + +JackCoreMidiPort::~JackCoreMidiPort() +{ + // Empty +} + +const char * +JackCoreMidiPort::GetAlias() +{ + assert(initialized); + return alias; +} + +MIDIEndpointRef +JackCoreMidiPort::GetEndpoint() +{ + assert(initialized); + return endpoint; +} + +const char * +JackCoreMidiPort::GetName() +{ + assert(initialized); + return name; +} + +void +JackCoreMidiPort::Initialize(const char *alias_name, const char *client_name, + const char *driver_name, int index, + MIDIEndpointRef endpoint, bool is_output) +{ + char endpoint_name[REAL_JACK_PORT_NAME_SIZE+1]; + CFStringRef endpoint_name_ref; + int num = index + 1; + Boolean res; + OSStatus result = MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, + &endpoint_name_ref); + if (result != noErr) { + WriteMacOSError("JackCoreMidiPort::Initialize", + "MIDIObjectGetStringProperty", result); + goto get_basic_alias; + } + res = CFStringGetCString(endpoint_name_ref, endpoint_name, + sizeof(endpoint_name), 0); + CFRelease(endpoint_name_ref); + if (!res) { + jack_error("JackCoreMidiPort::Initialize - failed to allocate memory " + "for endpoint name."); + get_basic_alias: + snprintf(alias, sizeof(alias), "%s:%s:%s%d", alias_name, + driver_name, is_output ? "in" : "out", num); + } else { + snprintf(alias, sizeof(alias), "%s:%s:%s%d", alias_name, + endpoint_name, is_output ? "in" : "out", num); + } + snprintf(name, sizeof(name), "%s:%s_%d", client_name, + is_output ? "playback" : "capture", num); + this->endpoint = endpoint; + initialized = true; +} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiUtil.cpp jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiUtil.cpp --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiUtil.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiUtil.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -/* -Copyright (C) 2011 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include - -#include "JackError.h" -#include "JackCoreMidiUtil.h" - -std::string -Jack::GetMacOSErrorString(OSStatus status) -{ - const char *message = GetMacOSStatusErrorString(status); - if (! message) { - std::stringstream stream; - stream << "error (code: '" << status << "')"; - return stream.str(); - } - return std::string(message); -} - -void -Jack::WriteMacOSError(const char *jack_function, const char *mac_function, - OSStatus status) -{ - jack_error("%s - %s: %s", jack_function, mac_function, - GetMacOSErrorString(status).c_str()); -} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiUtil.h jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiUtil.h --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiUtil.h 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiUtil.h 2017-07-22 08:00:34.000000000 +0000 @@ -22,8 +22,7 @@ #include -#include -#include +#include namespace Jack { diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiUtil.mm jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiUtil.mm --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiUtil.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiUtil.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,46 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackError.h" +#include "JackCoreMidiUtil.h" + +std::string +Jack::GetMacOSErrorString(OSStatus status) +{ + NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + NSString *errorString = [error localizedDescription]; + std::string returnString; + if (errorString){ + returnString = std::string([errorString UTF8String]); + } else { + returnString = std::string("No error"); + } + return returnString; +} + +void +Jack::WriteMacOSError(const char *jack_function, const char *mac_function, + OSStatus status) +{ + jack_error("%s - %s: %s", jack_function, mac_function, + GetMacOSErrorString(status).c_str()); +} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiVirtualInputPort.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ -/* -Copyright (C) 2011 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include - -#include "JackError.h" -#include "JackCoreMidiUtil.h" -#include "JackCoreMidiVirtualInputPort.h" - -using Jack::JackCoreMidiVirtualInputPort; - -/////////////////////////////////////////////////////////////////////////////// -// Static callbacks -/////////////////////////////////////////////////////////////////////////////// - -void -JackCoreMidiVirtualInputPort:: -HandleInputEvent(const MIDIPacketList *packet_list, void *port, - void */*src_ref*/) -{ - ((JackCoreMidiVirtualInputPort *) port)->ProcessCoreMidi(packet_list); -} - -/////////////////////////////////////////////////////////////////////////////// -// Class -/////////////////////////////////////////////////////////////////////////////// - -JackCoreMidiVirtualInputPort:: -JackCoreMidiVirtualInputPort(const char *alias_name, const char *client_name, - const char *driver_name, int base_index, int index, - MIDIClientRef client, double time_ratio, - size_t max_bytes, size_t max_messages): - JackCoreMidiInputPort(time_ratio, max_bytes, max_messages) -{ - std::stringstream stream; - stream << "virtual" << (base_index + 1); - CFStringRef name = CFStringCreateWithCString(0, stream.str().c_str(), - CFStringGetSystemEncoding()); - if (! name) { - throw std::bad_alloc(); - } - MIDIEndpointRef destination; - OSStatus status = MIDIDestinationCreate(client, name, HandleInputEvent, - this, &destination); - - /* - SInt32 value; - status = MIDIObjectGetIntegerProperty(destination, kMIDIPropertyUniqueID, &value); - if (status == noErr) { - jack_info("kMIDIPropertyUniqueID %d", value); - } - */ - - CFRelease(name); - if (status != noErr) { - throw std::runtime_error(GetMacOSErrorString(status)); - } - Initialize(alias_name, client_name, driver_name, index, destination); - - // Keep in global list (that keeps growing during the whole session...) - endpoint_list.insert(endpoint); -} - -JackCoreMidiVirtualInputPort::~JackCoreMidiVirtualInputPort() -{ - OSStatus status = MIDIEndpointDispose(GetEndpoint()); - if (status != noErr) { - WriteMacOSError("JackCoreMidiVirtualInputPort [destructor]", - "MIDIEndpointDispose", status); - } -} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiVirtualInputPort.mm jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiVirtualInputPort.mm --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiVirtualInputPort.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiVirtualInputPort.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,88 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackError.h" +#include "JackCoreMidiUtil.h" +#include "JackCoreMidiVirtualInputPort.h" + +using Jack::JackCoreMidiVirtualInputPort; + +/////////////////////////////////////////////////////////////////////////////// +// Static callbacks +/////////////////////////////////////////////////////////////////////////////// + +void +JackCoreMidiVirtualInputPort:: +HandleInputEvent(const MIDIPacketList *packet_list, void *port, + void */*src_ref*/) +{ + ((JackCoreMidiVirtualInputPort *) port)->ProcessCoreMidi(packet_list); +} + +/////////////////////////////////////////////////////////////////////////////// +// Class +/////////////////////////////////////////////////////////////////////////////// + +JackCoreMidiVirtualInputPort:: +JackCoreMidiVirtualInputPort(const char *alias_name, const char *client_name, + const char *driver_name, int base_index, int index, + MIDIClientRef client, double time_ratio, + size_t max_bytes, size_t max_messages): + JackCoreMidiInputPort(time_ratio, max_bytes, max_messages) +{ + std::stringstream stream; + stream << "virtual" << (base_index + 1); + CFStringRef name = CFStringCreateWithCString(0, stream.str().c_str(), + CFStringGetSystemEncoding()); + if (! name) { + throw std::bad_alloc(); + } + MIDIEndpointRef destination; + OSStatus status = MIDIDestinationCreate(client, name, HandleInputEvent, + this, &destination); + + /* + SInt32 value; + status = MIDIObjectGetIntegerProperty(destination, kMIDIPropertyUniqueID, &value); + if (status == noErr) { + jack_info("kMIDIPropertyUniqueID %d", value); + } + */ + + CFRelease(name); + if (status != noErr) { + throw std::runtime_error(GetMacOSErrorString(status)); + } + Initialize(alias_name, client_name, driver_name, index, destination); + + // Keep in global list (that keeps growing during the whole session...) + endpoint_list.insert(endpoint); +} + +JackCoreMidiVirtualInputPort::~JackCoreMidiVirtualInputPort() +{ + OSStatus status = MIDIEndpointDispose(GetEndpoint()); + if (status != noErr) { + WriteMacOSError("JackCoreMidiVirtualInputPort [destructor]", + "MIDIEndpointDispose", status); + } +} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -/* -Copyright (C) 2011 Devin Anderson - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include - -#include "JackError.h" -#include "JackCoreMidiUtil.h" -#include "JackCoreMidiVirtualOutputPort.h" - -using Jack::JackCoreMidiVirtualOutputPort; - -JackCoreMidiVirtualOutputPort:: -JackCoreMidiVirtualOutputPort(const char *alias_name, const char *client_name, - const char *driver_name, int base_index, int index, - MIDIClientRef client, double time_ratio, - size_t max_bytes, - size_t max_messages): - JackCoreMidiOutputPort(time_ratio, max_bytes, - max_messages) -{ - std::stringstream stream; - stream << "virtual" << (base_index + 1); - CFStringRef name = CFStringCreateWithCString(0, stream.str().c_str(), - CFStringGetSystemEncoding()); - if (! name) { - throw std::bad_alloc(); - } - MIDIEndpointRef source; - OSStatus status = MIDISourceCreate(client, name, &source); - - /* - SInt32 value; - status = MIDIObjectGetIntegerProperty(source, kMIDIPropertyUniqueID, &value); - if (status == noErr) { - jack_info("kMIDIPropertyUniqueID %d", value); - } - */ - - CFRelease(name); - if (status != noErr) { - throw std::runtime_error(GetMacOSErrorString(status)); - } - Initialize(alias_name, client_name, driver_name, index, source, 0); - - // Keep in global list (that keeps growing during the whole session...) - endpoint_list.insert(GetEndpoint()); -} - -JackCoreMidiVirtualOutputPort::~JackCoreMidiVirtualOutputPort() -{ - OSStatus status = MIDIEndpointDispose(GetEndpoint()); - if (status != noErr) { - WriteMacOSError("JackCoreMidiVirtualOutputPort [destructor]", - "MIDIEndpointDispose", status); - } -} - -bool -JackCoreMidiVirtualOutputPort::SendPacketList(MIDIPacketList *packet_list) -{ - OSStatus status = MIDIReceived(endpoint, packet_list); - bool result = status == noErr; - if (! result) { - WriteMacOSError("JackCoreMidiVirtualOutputPort::SendPacketList", - "MIDIReceived", status); - } - return result; -} diff -Nru jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiVirtualOutputPort.mm jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiVirtualOutputPort.mm --- jackd2-1.9.11~20161209/macosx/coremidi/JackCoreMidiVirtualOutputPort.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/coremidi/JackCoreMidiVirtualOutputPort.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,85 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackError.h" +#include "JackCoreMidiUtil.h" +#include "JackCoreMidiVirtualOutputPort.h" + +using Jack::JackCoreMidiVirtualOutputPort; + +JackCoreMidiVirtualOutputPort:: +JackCoreMidiVirtualOutputPort(const char *alias_name, const char *client_name, + const char *driver_name, int base_index, int index, + MIDIClientRef client, double time_ratio, + size_t max_bytes, + size_t max_messages): + JackCoreMidiOutputPort(time_ratio, max_bytes, + max_messages) +{ + std::stringstream stream; + stream << "virtual" << (base_index + 1); + CFStringRef name = CFStringCreateWithCString(0, stream.str().c_str(), + CFStringGetSystemEncoding()); + if (! name) { + throw std::bad_alloc(); + } + MIDIEndpointRef source; + OSStatus status = MIDISourceCreate(client, name, &source); + + /* + SInt32 value; + status = MIDIObjectGetIntegerProperty(source, kMIDIPropertyUniqueID, &value); + if (status == noErr) { + jack_info("kMIDIPropertyUniqueID %d", value); + } + */ + + CFRelease(name); + if (status != noErr) { + throw std::runtime_error(GetMacOSErrorString(status)); + } + Initialize(alias_name, client_name, driver_name, index, source, 0); + + // Keep in global list (that keeps growing during the whole session...) + endpoint_list.insert(GetEndpoint()); +} + +JackCoreMidiVirtualOutputPort::~JackCoreMidiVirtualOutputPort() +{ + OSStatus status = MIDIEndpointDispose(GetEndpoint()); + if (status != noErr) { + WriteMacOSError("JackCoreMidiVirtualOutputPort [destructor]", + "MIDIEndpointDispose", status); + } +} + +bool +JackCoreMidiVirtualOutputPort::SendPacketList(MIDIPacketList *packet_list) +{ + OSStatus status = MIDIReceived(endpoint, packet_list); + bool result = status == noErr; + if (! result) { + WriteMacOSError("JackCoreMidiVirtualOutputPort::SendPacketList", + "MIDIReceived", status); + } + return result; +} diff -Nru jackd2-1.9.11~20161209/macosx/Jack-Info.plist jackd2-1.9.11+git20170722/macosx/Jack-Info.plist --- jackd2-1.9.11~20161209/macosx/Jack-Info.plist 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/Jack-Info.plist 2017-07-22 08:02:50.000000000 +0000 @@ -7,7 +7,7 @@ CFBundleExecutable Jackservermp CFBundleGetInfoString - Jackdmp 1.9.11, @03-16 Paul Davis, Grame + Jackdmp 1.9.11-RC1, @03-16 Paul Davis, Grame CFBundleIdentifier com.grame.Jackmp CFBundleInfoDictionaryVersion @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.9.11 + 1.9.11-RC1 diff -Nru jackd2-1.9.11~20161209/macosx/JackMachSemaphore.cpp jackd2-1.9.11+git20170722/macosx/JackMachSemaphore.cpp --- jackd2-1.9.11~20161209/macosx/JackMachSemaphore.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/JackMachSemaphore.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,208 +0,0 @@ -/* -Copyright (C) 2004-2008 Grame - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "JackMachSemaphore.h" -#include "JackConstants.h" -#include "JackTools.h" -#include "JackError.h" -#include - -namespace Jack -{ - -void JackMachSemaphore::BuildName(const char* client_name, const char* server_name, char* res, int size) -{ - char ext_client_name[SYNC_MAX_NAME_SIZE + 1]; - JackTools::RewriteName(client_name, ext_client_name); - snprintf(res, size, "jack_mach_sem.%d_%s_%s", JackTools::GetUID(), server_name, ext_client_name); -} - -bool JackMachSemaphore::Signal() -{ - if (!fSemaphore) { - jack_error("JackMachSemaphore::Signal name = %s already deallocated!!", fName); - return false; - } - - if (fFlush) { - return true; - } - - kern_return_t res; - if ((res = semaphore_signal(fSemaphore)) != KERN_SUCCESS) { - jack_error("JackMachSemaphore::Signal name = %s err = %s", fName, mach_error_string(res)); - } - return (res == KERN_SUCCESS); -} - -bool JackMachSemaphore::SignalAll() -{ - if (!fSemaphore) { - jack_error("JackMachSemaphore::SignalAll name = %s already deallocated!!", fName); - return false; - } - - if (fFlush) { - return true; - } - - kern_return_t res; - // When signaled several times, do not accumulate signals... - if ((res = semaphore_signal_all(fSemaphore)) != KERN_SUCCESS) { - jack_error("JackMachSemaphore::SignalAll name = %s err = %s", fName, mach_error_string(res)); - } - return (res == KERN_SUCCESS); -} - -bool JackMachSemaphore::Wait() -{ - if (!fSemaphore) { - jack_error("JackMachSemaphore::Wait name = %s already deallocated!!", fName); - return false; - } - - kern_return_t res; - if ((res = semaphore_wait(fSemaphore)) != KERN_SUCCESS) { - jack_error("JackMachSemaphore::Wait name = %s err = %s", fName, mach_error_string(res)); - } - return (res == KERN_SUCCESS); -} - -bool JackMachSemaphore::TimedWait(long usec) -{ - if (!fSemaphore) { - jack_error("JackMachSemaphore::TimedWait name = %s already deallocated!!", fName); - return false; - } - - kern_return_t res; - mach_timespec time; - time.tv_sec = usec / 1000000; - time.tv_nsec = (usec % 1000000) * 1000; - - if ((res = semaphore_timedwait(fSemaphore, time)) != KERN_SUCCESS) { - jack_error("JackMachSemaphore::TimedWait name = %s usec = %ld err = %s", fName, usec, mach_error_string(res)); - } - return (res == KERN_SUCCESS); -} - -// Server side : publish the semaphore in the global namespace -bool JackMachSemaphore::Allocate(const char* name, const char* server_name, int value) -{ - BuildName(name, server_name, fName, sizeof(fName)); - mach_port_t task = mach_task_self(); - kern_return_t res; - - if (fBootPort == 0) { - if ((res = task_get_bootstrap_port(task, &fBootPort)) != KERN_SUCCESS) { - jack_error("Allocate: Can't find bootstrap mach port err = %s", mach_error_string(res)); - return false; - } - } - - if ((res = semaphore_create(task, &fSemaphore, SYNC_POLICY_FIFO, value)) != KERN_SUCCESS) { - jack_error("Allocate: can create semaphore err = %s", mach_error_string(res)); - return false; - } - - if ((res = bootstrap_register(fBootPort, fName, fSemaphore)) != KERN_SUCCESS) { - jack_error("Allocate: can't check in mach semaphore name = %s err = %s", fName, mach_error_string(res)); - - switch (res) { - case BOOTSTRAP_SUCCESS : - /* service not currently registered, "a good thing" (tm) */ - break; - case BOOTSTRAP_NOT_PRIVILEGED : - jack_log("bootstrap_register(): bootstrap not privileged"); - break; - case BOOTSTRAP_SERVICE_ACTIVE : - jack_log("bootstrap_register(): bootstrap service active"); - break; - default : - jack_log("bootstrap_register() err = %s", mach_error_string(res)); - break; - } - - return false; - } - - jack_log("JackMachSemaphore::Allocate name = %s", fName); - return true; -} - -// Client side : get the published semaphore from server -bool JackMachSemaphore::ConnectInput(const char* name, const char* server_name) -{ - BuildName(name, server_name, fName, sizeof(fName)); - kern_return_t res; - - if (fBootPort == 0) { - if ((res = task_get_bootstrap_port(mach_task_self(), &fBootPort)) != KERN_SUCCESS) { - jack_error("Connect: can't find bootstrap port err = %s", mach_error_string(res)); - return false; - } - } - - if ((res = bootstrap_look_up(fBootPort, fName, &fSemaphore)) != KERN_SUCCESS) { - jack_error("Connect: can't find mach semaphore name = %s err = %s", fName, mach_error_string(res)); - return false; - } - - jack_log("JackMachSemaphore::Connect name = %s ", fName); - return true; -} - -bool JackMachSemaphore::Connect(const char* name, const char* server_name) -{ - return ConnectInput(name, server_name); -} - -bool JackMachSemaphore::ConnectOutput(const char* name, const char* server_name) -{ - return ConnectInput(name, server_name); -} - -bool JackMachSemaphore::Disconnect() -{ - if (fSemaphore > 0) { - jack_log("JackMachSemaphore::Disconnect name = %s", fName); - fSemaphore = 0; - } - // Nothing to do - return true; -} - -// Server side : destroy the JackGlobals -void JackMachSemaphore::Destroy() -{ - kern_return_t res; - - if (fSemaphore > 0) { - jack_log("JackMachSemaphore::Destroy name = %s", fName); - if ((res = semaphore_destroy(mach_task_self(), fSemaphore)) != KERN_SUCCESS) { - jack_error("JackMachSemaphore::Destroy can't destroy semaphore err = %s", mach_error_string(res)); - } - fSemaphore = 0; - } else { - jack_error("JackMachSemaphore::Destroy semaphore < 0"); - } -} - -} // end of namespace - diff -Nru jackd2-1.9.11~20161209/macosx/JackMachSemaphore.mm jackd2-1.9.11+git20170722/macosx/JackMachSemaphore.mm --- jackd2-1.9.11~20161209/macosx/JackMachSemaphore.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/JackMachSemaphore.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,208 @@ +/* +Copyright (C) 2004-2008 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "JackMachSemaphore.h" +#include "JackConstants.h" +#include "JackTools.h" +#include "JackError.h" +#include + +namespace Jack +{ + +void JackMachSemaphore::BuildName(const char* client_name, const char* server_name, char* res, int size) +{ + char ext_client_name[SYNC_MAX_NAME_SIZE + 1]; + JackTools::RewriteName(client_name, ext_client_name); + snprintf(res, size, "jack_mach_sem.%d_%s_%s", JackTools::GetUID(), server_name, ext_client_name); +} + +bool JackMachSemaphore::Signal() +{ + if (!fSemaphore) { + jack_error("JackMachSemaphore::Signal name = %s already deallocated!!", fName); + return false; + } + + if (fFlush) { + return true; + } + + kern_return_t res; + if ((res = semaphore_signal(fSemaphore)) != KERN_SUCCESS) { + jack_error("JackMachSemaphore::Signal name = %s err = %s", fName, mach_error_string(res)); + } + return (res == KERN_SUCCESS); +} + +bool JackMachSemaphore::SignalAll() +{ + if (!fSemaphore) { + jack_error("JackMachSemaphore::SignalAll name = %s already deallocated!!", fName); + return false; + } + + if (fFlush) { + return true; + } + + kern_return_t res; + // When signaled several times, do not accumulate signals... + if ((res = semaphore_signal_all(fSemaphore)) != KERN_SUCCESS) { + jack_error("JackMachSemaphore::SignalAll name = %s err = %s", fName, mach_error_string(res)); + } + return (res == KERN_SUCCESS); +} + +bool JackMachSemaphore::Wait() +{ + if (!fSemaphore) { + jack_error("JackMachSemaphore::Wait name = %s already deallocated!!", fName); + return false; + } + + kern_return_t res; + if ((res = semaphore_wait(fSemaphore)) != KERN_SUCCESS) { + jack_error("JackMachSemaphore::Wait name = %s err = %s", fName, mach_error_string(res)); + } + return (res == KERN_SUCCESS); +} + +bool JackMachSemaphore::TimedWait(long usec) +{ + if (!fSemaphore) { + jack_error("JackMachSemaphore::TimedWait name = %s already deallocated!!", fName); + return false; + } + + kern_return_t res; + mach_timespec time; + time.tv_sec = usec / 1000000; + time.tv_nsec = (usec % 1000000) * 1000; + + if ((res = semaphore_timedwait(fSemaphore, time)) != KERN_SUCCESS) { + jack_error("JackMachSemaphore::TimedWait name = %s usec = %ld err = %s", fName, usec, mach_error_string(res)); + } + return (res == KERN_SUCCESS); +} + +// Server side : publish the semaphore in the global namespace +bool JackMachSemaphore::Allocate(const char* name, const char* server_name, int value) +{ + BuildName(name, server_name, fName, sizeof(fName)); + mach_port_t task = mach_task_self(); + kern_return_t res; + + if (fBootPort == 0) { + if ((res = task_get_bootstrap_port(task, &fBootPort)) != KERN_SUCCESS) { + jack_error("Allocate: Can't find bootstrap mach port err = %s", mach_error_string(res)); + return false; + } + } + + if ((res = semaphore_create(task, &fSemaphore, SYNC_POLICY_FIFO, value)) != KERN_SUCCESS) { + jack_error("Allocate: can create semaphore err = %s", mach_error_string(res)); + return false; + } + + if ((res = bootstrap_register(fBootPort, fName, fSemaphore)) != KERN_SUCCESS) { + jack_error("Allocate: can't check in mach semaphore name = %s err = %s", fName, mach_error_string(res)); + + switch (res) { + case BOOTSTRAP_SUCCESS : + /* service not currently registered, "a good thing" (tm) */ + break; + case BOOTSTRAP_NOT_PRIVILEGED : + jack_log("bootstrap_register(): bootstrap not privileged"); + break; + case BOOTSTRAP_SERVICE_ACTIVE : + jack_log("bootstrap_register(): bootstrap service active"); + break; + default : + jack_log("bootstrap_register() err = %s", mach_error_string(res)); + break; + } + + return false; + } + + jack_log("JackMachSemaphore::Allocate name = %s", fName); + return true; +} + +// Client side : get the published semaphore from server +bool JackMachSemaphore::ConnectInput(const char* name, const char* server_name) +{ + BuildName(name, server_name, fName, sizeof(fName)); + kern_return_t res; + + if (fBootPort == 0) { + if ((res = task_get_bootstrap_port(mach_task_self(), &fBootPort)) != KERN_SUCCESS) { + jack_error("Connect: can't find bootstrap port err = %s", mach_error_string(res)); + return false; + } + } + + if ((res = bootstrap_look_up(fBootPort, fName, &fSemaphore)) != KERN_SUCCESS) { + jack_error("Connect: can't find mach semaphore name = %s err = %s", fName, mach_error_string(res)); + return false; + } + + jack_log("JackMachSemaphore::Connect name = %s ", fName); + return true; +} + +bool JackMachSemaphore::Connect(const char* name, const char* server_name) +{ + return ConnectInput(name, server_name); +} + +bool JackMachSemaphore::ConnectOutput(const char* name, const char* server_name) +{ + return ConnectInput(name, server_name); +} + +bool JackMachSemaphore::Disconnect() +{ + if (fSemaphore > 0) { + jack_log("JackMachSemaphore::Disconnect name = %s", fName); + fSemaphore = 0; + } + // Nothing to do + return true; +} + +// Server side : destroy the JackGlobals +void JackMachSemaphore::Destroy() +{ + kern_return_t res; + + if (fSemaphore > 0) { + jack_log("JackMachSemaphore::Destroy name = %s", fName); + if ((res = semaphore_destroy(mach_task_self(), fSemaphore)) != KERN_SUCCESS) { + jack_error("JackMachSemaphore::Destroy can't destroy semaphore err = %s", mach_error_string(res)); + } + fSemaphore = 0; + } else { + jack_error("JackMachSemaphore::Destroy semaphore < 0"); + } +} + +} // end of namespace + diff -Nru jackd2-1.9.11~20161209/macosx/JackMachThread.cpp jackd2-1.9.11+git20170722/macosx/JackMachThread.cpp --- jackd2-1.9.11~20161209/macosx/JackMachThread.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/JackMachThread.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,231 +0,0 @@ -/* -Copyright (C) 2001 Paul Davis -Copyright (C) 2004-2008 Grame - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - - -#include "JackMachThread.h" -#include "JackError.h" - -#ifdef MY_TARGET_OS_IPHONE -#include "/Developer/Extras/CoreAudio/PublicUtility/CAHostTimeBase.h" -#endif - -namespace Jack -{ - -int JackMachThread::SetThreadToPriority(jack_native_thread_t thread, UInt32 inPriority, Boolean inIsFixed, UInt64 period, UInt64 computation, UInt64 constraint) -{ - if (inPriority == 96) { - // REAL-TIME / TIME-CONSTRAINT THREAD - thread_time_constraint_policy_data_t theTCPolicy; - -#ifdef MY_TARGET_OS_IPHONE - theTCPolicy.period = CAHostTimeBase::ConvertFromNanos(period); - theTCPolicy.computation = CAHostTimeBase::ConvertFromNanos(computation); - theTCPolicy.constraint = CAHostTimeBase::ConvertFromNanos(constraint); -#else - theTCPolicy.period = AudioConvertNanosToHostTime(period); - theTCPolicy.computation = AudioConvertNanosToHostTime(computation); - theTCPolicy.constraint = AudioConvertNanosToHostTime(constraint); -#endif - theTCPolicy.preemptible = true; - kern_return_t res = thread_policy_set(pthread_mach_thread_np(thread), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t) &theTCPolicy, THREAD_TIME_CONSTRAINT_POLICY_COUNT); - jack_log("JackMachThread::thread_policy_set res = %ld", res); - return (res == KERN_SUCCESS) ? 0 : -1; - } else { - // OTHER THREADS - thread_extended_policy_data_t theFixedPolicy; - thread_precedence_policy_data_t thePrecedencePolicy; - SInt32 relativePriority; - - // [1] SET FIXED / NOT FIXED - theFixedPolicy.timeshare = !inIsFixed; - thread_policy_set(pthread_mach_thread_np(thread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT); - - // [2] SET PRECEDENCE - // N.B.: We expect that if thread A created thread B, and the program wishes to change - // the priority of thread B, then the call to change the priority of thread B must be - // made by thread A. - // This assumption allows us to use pthread_self() to correctly calculate the priority - // of the feeder thread (since precedency policy's importance is relative to the - // spawning thread's priority.) - relativePriority = inPriority - GetThreadSetPriority(pthread_self()); - - thePrecedencePolicy.importance = relativePriority; - kern_return_t res = thread_policy_set(pthread_mach_thread_np(thread), THREAD_PRECEDENCE_POLICY, (thread_policy_t) &thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT); - jack_log("JackMachThread::thread_policy_set res = %ld", res); - return (res == KERN_SUCCESS) ? 0 : -1; - } -} - -// returns the thread's priority as it was last set by the API -UInt32 JackMachThread::GetThreadSetPriority(jack_native_thread_t thread) -{ - return GetThreadPriority(thread, THREAD_SET_PRIORITY); -} - -// returns the thread's priority as it was last scheduled by the Kernel -UInt32 JackMachThread::GetThreadScheduledPriority(jack_native_thread_t thread) -{ - return GetThreadPriority(thread, THREAD_SCHEDULED_PRIORITY); -} - -UInt32 JackMachThread::GetThreadPriority(jack_native_thread_t thread, int inWhichPriority) -{ - thread_basic_info_data_t threadInfo; - policy_info_data_t thePolicyInfo; - unsigned int count; - - // get basic info - count = THREAD_BASIC_INFO_COUNT; - thread_info(pthread_mach_thread_np(thread), THREAD_BASIC_INFO, (thread_info_t)&threadInfo, &count); - - switch (threadInfo.policy) { - case POLICY_TIMESHARE: - count = POLICY_TIMESHARE_INFO_COUNT; - thread_info(pthread_mach_thread_np(thread), THREAD_SCHED_TIMESHARE_INFO, (thread_info_t)&(thePolicyInfo.ts), &count); - if (inWhichPriority == THREAD_SCHEDULED_PRIORITY) { - return thePolicyInfo.ts.cur_priority; - } else { - return thePolicyInfo.ts.base_priority; - } - break; - - case POLICY_FIFO: - count = POLICY_FIFO_INFO_COUNT; - thread_info(pthread_mach_thread_np(thread), THREAD_SCHED_FIFO_INFO, (thread_info_t)&(thePolicyInfo.fifo), &count); - if ( (thePolicyInfo.fifo.depressed) && (inWhichPriority == THREAD_SCHEDULED_PRIORITY) ) { - return thePolicyInfo.fifo.depress_priority; - } - return thePolicyInfo.fifo.base_priority; - break; - - case POLICY_RR: - count = POLICY_RR_INFO_COUNT; - thread_info(pthread_mach_thread_np(thread), THREAD_SCHED_RR_INFO, (thread_info_t)&(thePolicyInfo.rr), &count); - if ( (thePolicyInfo.rr.depressed) && (inWhichPriority == THREAD_SCHEDULED_PRIORITY) ) { - return thePolicyInfo.rr.depress_priority; - } - return thePolicyInfo.rr.base_priority; - break; - } - - return 0; -} - -int JackMachThread::GetParams(jack_native_thread_t thread, UInt64* period, UInt64* computation, UInt64* constraint) -{ - thread_time_constraint_policy_data_t theTCPolicy; - mach_msg_type_number_t count = THREAD_TIME_CONSTRAINT_POLICY_COUNT; - boolean_t get_default = false; - - kern_return_t res = thread_policy_get(pthread_mach_thread_np(thread), - THREAD_TIME_CONSTRAINT_POLICY, - (thread_policy_t) & theTCPolicy, - &count, - &get_default); - if (res == KERN_SUCCESS) { - #ifdef MY_TARGET_OS_IPHONE - *period = CAHostTimeBase::ConvertToNanos(theTCPolicy.period); - *computation = CAHostTimeBase::ConvertToNanos(theTCPolicy.computation); - *constraint = CAHostTimeBase::ConvertToNanos(theTCPolicy.constraint); - #else - *period = AudioConvertHostTimeToNanos(theTCPolicy.period); - *computation = AudioConvertHostTimeToNanos(theTCPolicy.computation); - *constraint = AudioConvertHostTimeToNanos(theTCPolicy.constraint); - #endif - - jack_log("JackMachThread::GetParams period = %ld computation = %ld constraint = %ld", long(*period / 1000.0f), long(*computation / 1000.0f), long(*constraint / 1000.0f)); - return 0; - } else { - return -1; - } -} - -int JackMachThread::Kill() -{ - if (fThread != (jack_native_thread_t)NULL) { // If thread has been started - jack_log("JackMachThread::Kill"); - mach_port_t machThread = pthread_mach_thread_np(fThread); - int res = (thread_terminate(machThread) == KERN_SUCCESS) ? 0 : -1; - fStatus = kIdle; - fThread = (jack_native_thread_t)NULL; - return res; - } else { - return -1; - } -} - -int JackMachThread::AcquireRealTime() -{ - jack_log("JackMachThread::AcquireRealTime fPeriod = %ld fComputation = %ld fConstraint = %ld", - long(fPeriod / 1000), long(fComputation / 1000), long(fConstraint / 1000)); - return (fThread != (jack_native_thread_t)NULL) ? AcquireRealTimeImp(fThread, fPeriod, fComputation, fConstraint) : -1; -} - -int JackMachThread::AcquireSelfRealTime() -{ - jack_log("JackMachThread::AcquireSelfRealTime fPeriod = %ld fComputation = %ld fConstraint = %ld", - long(fPeriod / 1000), long(fComputation / 1000), long(fConstraint / 1000)); - return AcquireRealTimeImp(pthread_self(), fPeriod, fComputation, fConstraint); -} - -int JackMachThread::AcquireRealTime(int priority) -{ - fPriority = priority; - return AcquireRealTime(); -} - -int JackMachThread::AcquireSelfRealTime(int priority) -{ - fPriority = priority; - return AcquireSelfRealTime(); -} - -int JackMachThread::AcquireRealTimeImp(jack_native_thread_t thread, UInt64 period, UInt64 computation, UInt64 constraint) -{ - SetThreadToPriority(thread, 96, true, period, computation, constraint); - return 0; -} - -int JackMachThread::DropRealTime() -{ - return (fThread != (jack_native_thread_t)NULL) ? DropRealTimeImp(fThread) : -1; -} - -int JackMachThread::DropSelfRealTime() -{ - return DropRealTimeImp(pthread_self()); -} - -int JackMachThread::DropRealTimeImp(jack_native_thread_t thread) -{ - SetThreadToPriority(thread, 63, false, 0, 0, 0); - return 0; -} - -void JackMachThread::SetParams(UInt64 period, UInt64 computation, UInt64 constraint) -{ - fPeriod = period; - fComputation = computation; - fConstraint = constraint; -} - -} // end of namespace - diff -Nru jackd2-1.9.11~20161209/macosx/JackMachThread.h jackd2-1.9.11+git20170722/macosx/JackMachThread.h --- jackd2-1.9.11~20161209/macosx/JackMachThread.h 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/JackMachThread.h 2017-07-22 08:00:34.000000000 +0000 @@ -70,7 +70,7 @@ #include "JackPosixThread.h" #ifndef MY_TARGET_OS_IPHONE -#include +#include #endif #include diff -Nru jackd2-1.9.11~20161209/macosx/JackMachThread.mm jackd2-1.9.11+git20170722/macosx/JackMachThread.mm --- jackd2-1.9.11~20161209/macosx/JackMachThread.mm 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/macosx/JackMachThread.mm 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,231 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2004-2008 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + + +#include "JackMachThread.h" +#include "JackError.h" + +#ifdef MY_TARGET_OS_IPHONE +#include "/Developer/Extras/CoreAudio/PublicUtility/CAHostTimeBase.h" +#endif + +namespace Jack +{ + +int JackMachThread::SetThreadToPriority(jack_native_thread_t thread, UInt32 inPriority, Boolean inIsFixed, UInt64 period, UInt64 computation, UInt64 constraint) +{ + if (inPriority == 96) { + // REAL-TIME / TIME-CONSTRAINT THREAD + thread_time_constraint_policy_data_t theTCPolicy; + +#ifdef MY_TARGET_OS_IPHONE + theTCPolicy.period = CAHostTimeBase::ConvertFromNanos(period); + theTCPolicy.computation = CAHostTimeBase::ConvertFromNanos(computation); + theTCPolicy.constraint = CAHostTimeBase::ConvertFromNanos(constraint); +#else + theTCPolicy.period = AudioConvertNanosToHostTime(period); + theTCPolicy.computation = AudioConvertNanosToHostTime(computation); + theTCPolicy.constraint = AudioConvertNanosToHostTime(constraint); +#endif + theTCPolicy.preemptible = true; + kern_return_t res = thread_policy_set(pthread_mach_thread_np(thread), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t) &theTCPolicy, THREAD_TIME_CONSTRAINT_POLICY_COUNT); + jack_log("JackMachThread::thread_policy_set res = %ld", res); + return (res == KERN_SUCCESS) ? 0 : -1; + } else { + // OTHER THREADS + thread_extended_policy_data_t theFixedPolicy; + thread_precedence_policy_data_t thePrecedencePolicy; + SInt32 relativePriority; + + // [1] SET FIXED / NOT FIXED + theFixedPolicy.timeshare = !inIsFixed; + thread_policy_set(pthread_mach_thread_np(thread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT); + + // [2] SET PRECEDENCE + // N.B.: We expect that if thread A created thread B, and the program wishes to change + // the priority of thread B, then the call to change the priority of thread B must be + // made by thread A. + // This assumption allows us to use pthread_self() to correctly calculate the priority + // of the feeder thread (since precedency policy's importance is relative to the + // spawning thread's priority.) + relativePriority = inPriority - GetThreadSetPriority(pthread_self()); + + thePrecedencePolicy.importance = relativePriority; + kern_return_t res = thread_policy_set(pthread_mach_thread_np(thread), THREAD_PRECEDENCE_POLICY, (thread_policy_t) &thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT); + jack_log("JackMachThread::thread_policy_set res = %ld", res); + return (res == KERN_SUCCESS) ? 0 : -1; + } +} + +// returns the thread's priority as it was last set by the API +UInt32 JackMachThread::GetThreadSetPriority(jack_native_thread_t thread) +{ + return GetThreadPriority(thread, THREAD_SET_PRIORITY); +} + +// returns the thread's priority as it was last scheduled by the Kernel +UInt32 JackMachThread::GetThreadScheduledPriority(jack_native_thread_t thread) +{ + return GetThreadPriority(thread, THREAD_SCHEDULED_PRIORITY); +} + +UInt32 JackMachThread::GetThreadPriority(jack_native_thread_t thread, int inWhichPriority) +{ + thread_basic_info_data_t threadInfo; + policy_info_data_t thePolicyInfo; + unsigned int count; + + // get basic info + count = THREAD_BASIC_INFO_COUNT; + thread_info(pthread_mach_thread_np(thread), THREAD_BASIC_INFO, (thread_info_t)&threadInfo, &count); + + switch (threadInfo.policy) { + case POLICY_TIMESHARE: + count = POLICY_TIMESHARE_INFO_COUNT; + thread_info(pthread_mach_thread_np(thread), THREAD_SCHED_TIMESHARE_INFO, (thread_info_t)&(thePolicyInfo.ts), &count); + if (inWhichPriority == THREAD_SCHEDULED_PRIORITY) { + return thePolicyInfo.ts.cur_priority; + } else { + return thePolicyInfo.ts.base_priority; + } + break; + + case POLICY_FIFO: + count = POLICY_FIFO_INFO_COUNT; + thread_info(pthread_mach_thread_np(thread), THREAD_SCHED_FIFO_INFO, (thread_info_t)&(thePolicyInfo.fifo), &count); + if ( (thePolicyInfo.fifo.depressed) && (inWhichPriority == THREAD_SCHEDULED_PRIORITY) ) { + return thePolicyInfo.fifo.depress_priority; + } + return thePolicyInfo.fifo.base_priority; + break; + + case POLICY_RR: + count = POLICY_RR_INFO_COUNT; + thread_info(pthread_mach_thread_np(thread), THREAD_SCHED_RR_INFO, (thread_info_t)&(thePolicyInfo.rr), &count); + if ( (thePolicyInfo.rr.depressed) && (inWhichPriority == THREAD_SCHEDULED_PRIORITY) ) { + return thePolicyInfo.rr.depress_priority; + } + return thePolicyInfo.rr.base_priority; + break; + } + + return 0; +} + +int JackMachThread::GetParams(jack_native_thread_t thread, UInt64* period, UInt64* computation, UInt64* constraint) +{ + thread_time_constraint_policy_data_t theTCPolicy; + mach_msg_type_number_t count = THREAD_TIME_CONSTRAINT_POLICY_COUNT; + boolean_t get_default = false; + + kern_return_t res = thread_policy_get(pthread_mach_thread_np(thread), + THREAD_TIME_CONSTRAINT_POLICY, + (thread_policy_t) & theTCPolicy, + &count, + &get_default); + if (res == KERN_SUCCESS) { + #ifdef MY_TARGET_OS_IPHONE + *period = CAHostTimeBase::ConvertToNanos(theTCPolicy.period); + *computation = CAHostTimeBase::ConvertToNanos(theTCPolicy.computation); + *constraint = CAHostTimeBase::ConvertToNanos(theTCPolicy.constraint); + #else + *period = AudioConvertHostTimeToNanos(theTCPolicy.period); + *computation = AudioConvertHostTimeToNanos(theTCPolicy.computation); + *constraint = AudioConvertHostTimeToNanos(theTCPolicy.constraint); + #endif + + jack_log("JackMachThread::GetParams period = %ld computation = %ld constraint = %ld", long(*period / 1000.0f), long(*computation / 1000.0f), long(*constraint / 1000.0f)); + return 0; + } else { + return -1; + } +} + +int JackMachThread::Kill() +{ + if (fThread != (jack_native_thread_t)NULL) { // If thread has been started + jack_log("JackMachThread::Kill"); + mach_port_t machThread = pthread_mach_thread_np(fThread); + int res = (thread_terminate(machThread) == KERN_SUCCESS) ? 0 : -1; + fStatus = kIdle; + fThread = (jack_native_thread_t)NULL; + return res; + } else { + return -1; + } +} + +int JackMachThread::AcquireRealTime() +{ + jack_log("JackMachThread::AcquireRealTime fPeriod = %ld fComputation = %ld fConstraint = %ld", + long(fPeriod / 1000), long(fComputation / 1000), long(fConstraint / 1000)); + return (fThread != (jack_native_thread_t)NULL) ? AcquireRealTimeImp(fThread, fPeriod, fComputation, fConstraint) : -1; +} + +int JackMachThread::AcquireSelfRealTime() +{ + jack_log("JackMachThread::AcquireSelfRealTime fPeriod = %ld fComputation = %ld fConstraint = %ld", + long(fPeriod / 1000), long(fComputation / 1000), long(fConstraint / 1000)); + return AcquireRealTimeImp(pthread_self(), fPeriod, fComputation, fConstraint); +} + +int JackMachThread::AcquireRealTime(int priority) +{ + fPriority = priority; + return AcquireRealTime(); +} + +int JackMachThread::AcquireSelfRealTime(int priority) +{ + fPriority = priority; + return AcquireSelfRealTime(); +} + +int JackMachThread::AcquireRealTimeImp(jack_native_thread_t thread, UInt64 period, UInt64 computation, UInt64 constraint) +{ + SetThreadToPriority(thread, 96, true, period, computation, constraint); + return 0; +} + +int JackMachThread::DropRealTime() +{ + return (fThread != (jack_native_thread_t)NULL) ? DropRealTimeImp(fThread) : -1; +} + +int JackMachThread::DropSelfRealTime() +{ + return DropRealTimeImp(pthread_self()); +} + +int JackMachThread::DropRealTimeImp(jack_native_thread_t thread) +{ + SetThreadToPriority(thread, 63, false, 0, 0, 0); + return 0; +} + +void JackMachThread::SetParams(UInt64 period, UInt64 computation, UInt64 constraint) +{ + fPeriod = period; + fComputation = computation; + fConstraint = constraint; +} + +} // end of namespace + diff -Nru jackd2-1.9.11~20161209/man/jackd.0 jackd2-1.9.11+git20170722/man/jackd.0 --- jackd2-1.9.11~20161209/man/jackd.0 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/man/jackd.0 2017-07-22 08:02:50.000000000 +0000 @@ -548,6 +548,10 @@ parameter is set, and all JACK clients unless they pass an explicit name to \fBjack_client_open()\fR. +Defining \fB$JACK_NO_AUDIO_RESERVATION\fR will bypass audio device +reservation via session bus (DBus). This can be useful if JACK +was compiled with DBus support but should run on a headless system. + .SH "SEE ALSO:" .PP .I http://www.jackaudio.org diff -Nru jackd2-1.9.11~20161209/man/wscript jackd2-1.9.11+git20170722/man/wscript --- jackd2-1.9.11~20161209/man/wscript 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/man/wscript 2017-07-22 08:02:50.000000000 +0000 @@ -6,6 +6,6 @@ def build(bld): - bld.exec_command("cd man ; sh fill_template %s" % bld.env['JACK_VERSION']) + bld.exec_command('cd man ; sh fill_template %s' % bld.env['JACK_VERSION']) bld.install_files(bld.env['MANDIR'], bld.path.ant_glob('*.1')) diff -Nru jackd2-1.9.11~20161209/posix/JackPosixProcessSync.cpp jackd2-1.9.11+git20170722/posix/JackPosixProcessSync.cpp --- jackd2-1.9.11~20161209/posix/JackPosixProcessSync.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/posix/JackPosixProcessSync.cpp 2017-07-22 08:02:46.000000000 +0000 @@ -108,67 +108,55 @@ ThrowIf(!pthread_equal(pthread_self(), fOwner), JackException("JackPosixProcessSync::TimedWait: a thread has to have locked a mutex before it can wait")); fOwner = 0; - struct timeval T0, T1; - timespec time; - struct timeval now; int res; + timespec now; + clock_gettime(CLOCK_REALTIME, &now); - jack_log("JackPosixProcessSync::TimedWait time out = %ld", usec); - gettimeofday(&T0, 0); - - gettimeofday(&now, 0); - unsigned int next_date_usec = now.tv_usec + usec; - time.tv_sec = now.tv_sec + (next_date_usec / 1000000); - time.tv_nsec = (next_date_usec % 1000000) * 1000; + timespec delta = { usec / 1000000, (usec % 1000000) * 1000 }; + timespec end = { now.tv_sec + delta.tv_sec, now.tv_nsec + delta.tv_nsec }; + if (end.tv_nsec >= 1000000000L) { + ++end.tv_sec; + end.tv_nsec -= 1000000000L; + } - res = pthread_cond_timedwait(&fCond, &fMutex, &time); + res = pthread_cond_timedwait(&fCond, &fMutex, &end); if (res != 0) { jack_error("JackPosixProcessSync::TimedWait error usec = %ld err = %s", usec, strerror(res)); } else { fOwner = pthread_self(); } - gettimeofday(&T1, 0); - jack_log("JackPosixProcessSync::TimedWait finished delta = %5.1lf", - (1e6 * T1.tv_sec - 1e6 * T0.tv_sec + T1.tv_usec - T0.tv_usec)); - return (res == 0); } // TO DO : check thread consistency? bool JackPosixProcessSync::LockedTimedWait(long usec) { - struct timeval T0, T1; - timespec time; - struct timeval now; - int res1, res2; - - res1 = pthread_mutex_lock(&fMutex); + int res1 = pthread_mutex_lock(&fMutex); if (res1 != 0) { jack_error("JackPosixProcessSync::LockedTimedWait error err = %s", usec, strerror(res1)); } - jack_log("JackPosixProcessSync::TimedWait time out = %ld", usec); - gettimeofday(&T0, 0); + timespec now; + clock_gettime(CLOCK_REALTIME, &now); - gettimeofday(&now, 0); - unsigned int next_date_usec = now.tv_usec + usec; - time.tv_sec = now.tv_sec + (next_date_usec / 1000000); - time.tv_nsec = (next_date_usec % 1000000) * 1000; - res2 = pthread_cond_timedwait(&fCond, &fMutex, &time); + timespec delta = { usec / 1000000, (usec % 1000000) * 1000 }; + timespec end = { now.tv_sec + delta.tv_sec, now.tv_nsec + delta.tv_nsec }; + if (end.tv_nsec >= 1000000000L) { + ++end.tv_sec; + end.tv_nsec -= 1000000000L; + } + + int res2 = pthread_cond_timedwait(&fCond, &fMutex, &end); if (res2 != 0) { jack_error("JackPosixProcessSync::LockedTimedWait error usec = %ld err = %s", usec, strerror(res2)); } - gettimeofday(&T1, 0); res1 = pthread_mutex_unlock(&fMutex); if (res1 != 0) { jack_error("JackPosixProcessSync::LockedTimedWait error err = %s", usec, strerror(res1)); } - jack_log("JackPosixProcessSync::TimedWait finished delta = %5.1lf", - (1e6 * T1.tv_sec - 1e6 * T0.tv_sec + T1.tv_usec - T0.tv_usec)); - return (res2 == 0); } diff -Nru jackd2-1.9.11~20161209/posix/JackPosixSemaphore.cpp jackd2-1.9.11+git20170722/posix/JackPosixSemaphore.cpp --- jackd2-1.9.11~20161209/posix/JackPosixSemaphore.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/posix/JackPosixSemaphore.cpp 2017-07-22 08:02:46.000000000 +0000 @@ -81,7 +81,6 @@ return (res == 0); } -/* bool JackPosixSemaphore::Wait() { int res; @@ -91,17 +90,6 @@ return false; } - if ((res = sem_wait(fSemaphore)) != 0) { - jack_error("JackPosixSemaphore::Wait name = %s err = %s", fName, strerror(errno)); - } - return (res == 0); -} -*/ - -bool JackPosixSemaphore::Wait() -{ - int res; - while ((res = sem_wait(fSemaphore) < 0)) { jack_error("JackPosixSemaphore::Wait name = %s err = %s", fName, strerror(errno)); if (errno != EINTR) { @@ -115,24 +103,24 @@ bool JackPosixSemaphore::TimedWait(long usec) { - int res; - struct timeval now; - timespec time; - if (!fSemaphore) { jack_error("JackPosixSemaphore::TimedWait name = %s already deallocated!!", fName); return false; } - gettimeofday(&now, 0); - time.tv_sec = now.tv_sec + usec / 1000000; - long tv_usec = (now.tv_usec + (usec % 1000000)); - time.tv_sec += tv_usec / 1000000; - time.tv_nsec = (tv_usec % 1000000) * 1000; - while ((res = sem_timedwait(fSemaphore, &time)) < 0) { + int res; + timespec now; + clock_gettime(CLOCK_REALTIME, &now); + + timespec delta = { usec / 1000000, (usec % 1000000) * 1000 }; + timespec end = { now.tv_sec + delta.tv_sec, now.tv_nsec + delta.tv_nsec }; + if (end.tv_nsec >= 1000000000L) { + ++end.tv_sec; + end.tv_nsec -= 1000000000L; + } + + while ((res = sem_timedwait(fSemaphore, &end)) < 0) { jack_error("JackPosixSemaphore::TimedWait err = %s", strerror(errno)); - jack_log("JackPosixSemaphore::TimedWait now : %ld %ld ", now.tv_sec, now.tv_usec); - jack_log("JackPosixSemaphore::TimedWait next : %ld %ld ", time.tv_sec, time.tv_nsec/1000); if (errno != EINTR) { break; } diff -Nru jackd2-1.9.11~20161209/README jackd2-1.9.11+git20170722/README --- jackd2-1.9.11~20161209/README 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/README 2017-07-22 08:02:50.000000000 +0000 @@ -106,6 +106,17 @@ The published version uses named event for server/client synchronization. Named pipes are used for server/client communications. A PortAudio (V19) based driver is used. It allows to access either MME, DirectSound or ASIO supported cards. + +To cross compile using mingw, do the following in the top folder: + +ARCH=x86_64-w64-mingw32.shared # e.g. + +PKGCONFIG=$ARCH-pkg-config CC=$ARCH-gcc CXX=$ARCH-g++ ./waf configure --platform=win32 + +PKGCONFIG=$ARCH-pkg-config CC=$ARCH-gcc CXX=$ARCH-g++ ./waf build -v + + + The binary elements are : - jackd.exe : the JACK server @@ -216,7 +227,8 @@ 1.9.8 : Merge newer-midi branch (Devin Anderson redesign of the MIDI drivers: alsarawmidi, ffado, coremidi and winmme). Correction in jackdmp.cpp: notify_server_stop should be done after server destruction. Correct driver lifetime management. Add XRun detection in PortAudio driver. CELT code for NetJack2. Merge branch switch-master-port-registration-notifications: correct driver port registration. Libjacknet in progress. Correct MIDI in NetJack2. Correct OSX real-time thread setup. Correct rd_acquire in dbus code. Correct NetJack2 connection handling. SaveConnections/RestoreConnections in NetDriver and JackAudioDriver. Special version of jack_attach_shm/jack_release_shm on client side for POSIX shared memory, to solve a memory leak issue. Another round of code improvements to handle completely buggy Digidesign CoreAudio user-land driver. Special CATCH_CLOSE_EXCEPTION_RETURN to handle Close API calls. Add JACK_NETJACK_PORT and JACK_NETJACK_MULTICAST environment variables for NetJack2. NetJack2 now only send data on network only is ports are connected both sides. Fix for "starting two instances of same app in parallel does not work" bug. Enable explicit channel mapping in CoreAudio driver. New JackTimedDriver class to be used by JackDummyDriver, JackNetDriver and JackNetOneDriver classes. More robust code in synchronization primitives and in JackMessageBuffer. More robust Control API implementation. Add jackctl_driver_get_type in Control API. Singleton behaviour for JackCoreMidiDriver and JackWinMMEDriver. John Emmas patch for DSP CPU computation. John Emmas Windows server launching patch. Fix jack_set_port_name API. Enable local access in NetJack2 code. Dynamic port management in JACK/CoreMidi bridge. 1.9.9 : Adrian Knoth fix in midiseq.c. Fix library symbols export issue. Cleanup drivers and internals loading code. jackctl_driver_params_parse API moved in public control.h. More general drivers/internals loading model on Windows. Factorize code the server/client request in JackRequestDecoder class. More robust server/client protocol. Implement shutdown for in server clients. Better time-out management in NetJack2. Experimental system port alias use in Windows JackRouter. Improve ShutDown in NetManager. Correct ShutDown in JackInternalClient and JackLibClient. Fix NetJack2 initialisation bug. Add EndTime function (especially for Windows). Rename JackProcessSync in JackPosixProcessSync. A bit more robust JackMessageBuffer implementation (in progress). Check server API callback from notification thread. Use a time-out in notification channel write function. Fix lock management in JackEngine. In control API, UNIX like sigset_t replaced by more abstract jackctl_sigmask_t * opaque struct. Improve libjacknet master mode. Remove JACK_32_64 flag, so POST_PACKED_STRUCTURE now always used. POST_PACKED_STRUCTURE used for jack_latency_range_t type. Rework JackMessageBuffer. [firewire] Introduce UpdateLatencies() in FFADO backend. [firewire] Allow FFADO backend to change the buffer size. Update waf. New jack_get_cycle_times() implementation from Fons Adriennsen. Align buffers to 32 byte boundaries to allow AVX processing. Extend jack_control to have parameter reset commands. Fix alsa driver parameter order. Control API: Enforce driver/internal parameter order. Fix in ALSA adapter. Devin Anderson patch for Jack/CoreMIDI duplicated messages. Change framework installation hierarchy for OSX Mountain Lion. Update JackCoreAudioDriver and JackCoreAudioAdapter with more recent API. jack_control: fix epr command. Add opus support to NetJack2. More robust channel mapping handling in JackCoreAudioDriver. netjack1/netone opus support. controlapi: fix double free on master switch. Use string ids in the alsa device list. netjack/opus: don't re-init en/decoders. Correct JackPortAudioDriver::Open : special case for ASIO drivers. 1.9.10 : More robust code in JackPortAudioDriver to handle buffer size change and backend switching. Fix bus error on ARM platforms. Dynamically scan and print backend and internal names in jackd. CoreMIDI driver fixes. Rework NetJack2 code (OPUS codec on OSX, latency management, libjacknet code). Correct auto-connect for audioadapter. Add IIO driver. Merge of Nedko no-self-connect branch. Fix freewheel mode. JackServer::SwitchMaster now correctly notify buffer_size and sample_rate changes, cleanup/improvements in JackNetDriver. Tim Mayberry : Add support for building with mingw compiler. Merge of Kim Jeong Yeon Android branch. Partial port of metadata API. -1.9.11 : Various corrections in NetJack2 code. Partial buffers can now be transmitted with libjacknet API. Including S24_LE/BE formats to linux ALSA driver. More robust shared memory allocator. Allow autostart of jackd on OSX where device-names can contain spaces. Correct CoreAudio devices aggregation code. Waf and wscripts improvement and update. More flexible RT priority setup on Windows. New JackProxyDriver. Various fixes in JACK MIDI code. Fix return value of SetTimebaseCallback(). Correct netmanager latency reporting. Implement new jack_port_rename and JackPortRenameCallback API. For OSX El Capitan support, use of Posix semaphore and move of Frameworks in /Library folder. Fix CPU hogging of the midi_thread(). Release audio devices when alsa_driver_new fails. String management fix. Correct JackDriver::Open : call to fGraphManager->SetBufferSize has to use current fEngineControl->fBufferSize value. Use ARM neon intrinsics for AudioBufferMixdown. +1.9.11 : Various corrections in NetJack2 code. Partial buffers can now be transmitted with libjacknet API. Including S24_LE/BE formats to linux ALSA driver. More robust shared memory allocator. Allow autostart of jackd on OSX where device-names can contain spaces. Correct CoreAudio devices aggregation code. Waf and wscripts improvement and update. More flexible RT priority setup on Windows. New JackProxyDriver. Various fixes in JACK MIDI code. Fix return value of SetTimebaseCallback(). Correct netmanager latency reporting. Implement new jack_port_rename and JackPortRenameCallback API. For OSX El Capitan support, use of Posix semaphore and move of Frameworks in /Library folder. Fix CPU hogging of the midi_thread(). Release audio devices when alsa_driver_new fails. String management fix. Correct JackDriver::Open : call to fGraphManager->SetBufferSize has to use current fEngineControl->fBufferSize value. Use ARM neon intrinsics for AudioBufferMixdown. Fix Netjack alignment. Various wscript improvements and cleanup. Fix initialization of several class variables. Heap-allocate client matrix in topo sort. Add a toggle command to transport utility, to allow toggling between play and stop state. Avoid side effects from parsing of "version" option in jackd. Allow firewire device be selected via -d. Add ARM-NEON acceleration for all non-dithering sample conversion functions. Add jack_simdtest utility. Use Linux futex as JackSynchro. Add autoclose option to jack_load. + This is a work in progress but the implementation is now stable enough to be tested. jackdmp has been used successfully with the following applications : Ardour, Hydrogen, Jamin, QjackCtl, Jack-Rack, SooperLooper, AlsaPlayer... diff -Nru jackd2-1.9.11~20161209/tests/iodelay.cpp jackd2-1.9.11+git20170722/tests/iodelay.cpp --- jackd2-1.9.11~20161209/tests/iodelay.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/tests/iodelay.cpp 2017-07-22 08:00:34.000000000 +0000 @@ -20,6 +20,7 @@ #include #include +#define __STDC_LIMIT_MACROS #include #include #include diff -Nru jackd2-1.9.11~20161209/tests/test.cpp jackd2-1.9.11+git20170722/tests/test.cpp --- jackd2-1.9.11~20161209/tests/test.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/tests/test.cpp 2017-07-22 08:02:50.000000000 +0000 @@ -479,7 +479,7 @@ jack_nframes_t delta_time = cur_time - last_time; Log("calling process4 callback : jack_frame_time = %ld delta_time = %ld\n", cur_time, delta_time); - if (delta_time > 0 && (jack_nframes_t)abs(delta_time - cur_buffer_size) > tolerance) { + if (delta_time > 0 && abs((int64_t)delta_time - (int64_t)cur_buffer_size) > (int64_t)tolerance) { printf("!!! ERROR !!! jack_frame_time seems to return incorrect values cur_buffer_size = %d, delta_time = %d tolerance %d\n", cur_buffer_size, delta_time, tolerance); } diff -Nru jackd2-1.9.11~20161209/tests/wscript jackd2-1.9.11+git20170722/tests/wscript --- jackd2-1.9.11~20161209/tests/wscript 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/tests/wscript 2017-07-22 08:02:50.000000000 +0000 @@ -17,16 +17,13 @@ for test_program, test_program_sources in list(test_programs.items()): prog = bld(features = 'cxx cxxprogram') if bld.env['IS_MACOSX']: - prog.includes = ['..','../macosx', '../posix', '../common/jack', '../common'] + prog.includes = ['..','../macosx', '../posix', '../common/jack', '../common'] if bld.env['IS_LINUX']: - prog.includes = ['..','../linux', '../posix', '../common/jack', '../common'] + prog.includes = ['..','../linux', '../posix', '../common/jack', '../common'] if bld.env['IS_SUN']: - prog.includes = ['..','../solaris', '../posix', '../common/jack', '../common'] + prog.includes = ['..','../solaris', '../posix', '../common/jack', '../common'] prog.source = test_program_sources if bld.env['IS_LINUX']: prog.uselib = 'RT' - if bld.env['IS_MACOSX']: - prog.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") - #prog.env.append_value("LINKFLAGS", "-arch i386 -arch ppc -arch x86_64") prog.use = 'clientlib' prog.target = test_program diff -Nru jackd2-1.9.11~20161209/.travis.yml jackd2-1.9.11+git20170722/.travis.yml --- jackd2-1.9.11~20161209/.travis.yml 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/.travis.yml 2017-07-22 08:00:34.000000000 +0000 @@ -1,9 +1,34 @@ +sudo: false +os: + - osx + - linux language: - - cpp + - cpp compiler: - - gcc -install: - - sudo apt-get install libsamplerate-dev libsndfile-dev libasound2-dev + - gcc + - clang +addons: + apt: + packages: + - libsamplerate-dev + - libsndfile-dev + - libasound2-dev + +before_install: + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update; fi + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew outdated pkg-config || brew upgrade pkg-config; fi + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install aften; fi + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libsamplerate; fi + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libsndfile; fi + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install opus; fi + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install readline; fi + script: - - ./waf configure --alsa - - ./waf build + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then ./waf configure --alsa; fi + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then ./waf configure --opus=no --readline=no; fi + - ./waf build + +matrix: + exclude: + - os: osx + compiler: gcc diff -Nru jackd2-1.9.11~20161209/waflib/extras/xcode6.py jackd2-1.9.11+git20170722/waflib/extras/xcode6.py --- jackd2-1.9.11~20161209/waflib/extras/xcode6.py 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/waflib/extras/xcode6.py 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,656 @@ +#! /usr/bin/env python +# encoding: utf-8 +# XCode 3/XCode 4 generator for Waf +# Based on work by Nicolas Mercier 2011 +# Extended by Simon Warg 2015, https://github.com/mimon +# XCode project file format based on http://www.monobjc.net/xcode-project-file-format.html + +""" +Usage: + +See also demos/xcode6/ folder + +def options(opt): + opt.load('xcode6') + +def configure(cnf): + # + + # For example + cnf.env.SDKROOT = 'macosx10.9' + + # Use cnf.PROJ_CONFIGURATION to completely set/override + # global project settings + # cnf.env.PROJ_CONFIGURATION = { + # 'Debug': { + # 'SDKROOT': 'macosx10.9' + # } + # 'MyCustomReleaseConfig': { + # 'SDKROOT': 'macosx10.10' + # } + # } + + # In the end of configure() do + cnf.load('xcode6') + +def build(bld): + + # Make a Framework target + bld.framework( + source_files={ + 'Include': bld.path.ant_glob('include/MyLib/*.h'), + 'Source': bld.path.ant_glob('src/MyLib/*.cpp') + }, + includes='include', + export_headers=bld.path.ant_glob('include/MyLib/*.h'), + target='MyLib', + ) + + # You can also make bld.dylib, bld.app, bld.stlib ... + +$ waf configure xcode6 +""" + +# TODO: support iOS projects + +from waflib import Context, TaskGen, Build, Utils, ConfigSet, Configure, Errors +from waflib.Build import BuildContext +import os, sys, random, time + +HEADERS_GLOB = '**/(*.h|*.hpp|*.H|*.inl)' + +MAP_EXT = { + '': "folder", + '.h' : "sourcecode.c.h", + + '.hh': "sourcecode.cpp.h", + '.inl': "sourcecode.cpp.h", + '.hpp': "sourcecode.cpp.h", + + '.c': "sourcecode.c.c", + + '.m': "sourcecode.c.objc", + + '.mm': "sourcecode.cpp.objcpp", + + '.cc': "sourcecode.cpp.cpp", + + '.cpp': "sourcecode.cpp.cpp", + '.C': "sourcecode.cpp.cpp", + '.cxx': "sourcecode.cpp.cpp", + '.c++': "sourcecode.cpp.cpp", + + '.l': "sourcecode.lex", # luthor + '.ll': "sourcecode.lex", + + '.y': "sourcecode.yacc", + '.yy': "sourcecode.yacc", + + '.plist': "text.plist.xml", + ".nib": "wrapper.nib", + ".xib": "text.xib", +} + +# Used in PBXNativeTarget elements +PRODUCT_TYPE_APPLICATION = 'com.apple.product-type.application' +PRODUCT_TYPE_FRAMEWORK = 'com.apple.product-type.framework' +PRODUCT_TYPE_EXECUTABLE = 'com.apple.product-type.tool' +PRODUCT_TYPE_LIB_STATIC = 'com.apple.product-type.library.static' +PRODUCT_TYPE_LIB_DYNAMIC = 'com.apple.product-type.library.dynamic' +PRODUCT_TYPE_EXTENSION = 'com.apple.product-type.kernel-extension' +PRODUCT_TYPE_IOKIT = 'com.apple.product-type.kernel-extension.iokit' + +# Used in PBXFileReference elements +FILE_TYPE_APPLICATION = 'wrapper.cfbundle' +FILE_TYPE_FRAMEWORK = 'wrapper.framework' +FILE_TYPE_LIB_DYNAMIC = 'compiled.mach-o.dylib' +FILE_TYPE_LIB_STATIC = 'archive.ar' +FILE_TYPE_EXECUTABLE = 'compiled.mach-o.executable' + +# Tuple packs of the above +TARGET_TYPE_FRAMEWORK = (PRODUCT_TYPE_FRAMEWORK, FILE_TYPE_FRAMEWORK, '.framework') +TARGET_TYPE_APPLICATION = (PRODUCT_TYPE_APPLICATION, FILE_TYPE_APPLICATION, '.app') +TARGET_TYPE_DYNAMIC_LIB = (PRODUCT_TYPE_LIB_DYNAMIC, FILE_TYPE_LIB_DYNAMIC, '.dylib') +TARGET_TYPE_STATIC_LIB = (PRODUCT_TYPE_LIB_STATIC, FILE_TYPE_LIB_STATIC, '.a') +TARGET_TYPE_EXECUTABLE = (PRODUCT_TYPE_EXECUTABLE, FILE_TYPE_EXECUTABLE, '') + +# Maps target type string to its data +TARGET_TYPES = { + 'framework': TARGET_TYPE_FRAMEWORK, + 'app': TARGET_TYPE_APPLICATION, + 'dylib': TARGET_TYPE_DYNAMIC_LIB, + 'stlib': TARGET_TYPE_STATIC_LIB, + 'exe' :TARGET_TYPE_EXECUTABLE, +} + +""" +Configuration of the global project settings. Sets an environment variable 'PROJ_CONFIGURATION' +which is a dictionary of configuration name and buildsettings pair. +E.g.: +env.PROJ_CONFIGURATION = { + 'Debug': { + 'ARCHS': 'x86', + ... + } + 'Release': { + 'ARCHS' x86_64' + ... + } +} +The user can define a completely customized dictionary in configure() stage. Otherwise a default Debug/Release will be created +based on env variable +""" +def configure(self): + if not self.env.PROJ_CONFIGURATION: + self.to_log("A default project configuration was created since no custom one was given in the configure(conf) stage. Define your custom project settings by adding PROJ_CONFIGURATION to env. The env.PROJ_CONFIGURATION must be a dictionary with at least one key, where each key is the configuration name, and the value is a dictionary of key/value settings.\n") + + # Check for any added config files added by the tool 'c_config'. + if 'cfg_files' in self.env: + self.env.INCLUDES = Utils.to_list(self.env.INCLUDES) + [os.path.abspath(os.path.dirname(f)) for f in self.env.cfg_files] + + # Create default project configuration? + if 'PROJ_CONFIGURATION' not in self.env: + self.env.PROJ_CONFIGURATION = { + "Debug": self.env.get_merged_dict(), + "Release": self.env.get_merged_dict(), + } + + # Some build settings are required to be present by XCode. We will supply default values + # if user hasn't defined any. + defaults_required = [('PRODUCT_NAME', '$(TARGET_NAME)')] + for cfgname,settings in self.env.PROJ_CONFIGURATION.iteritems(): + for default_var, default_val in defaults_required: + if default_var not in settings: + settings[default_var] = default_val + + # Error check customization + if not isinstance(self.env.PROJ_CONFIGURATION, dict): + raise Errors.ConfigurationError("The env.PROJ_CONFIGURATION must be a dictionary with at least one key, where each key is the configuration name, and the value is a dictionary of key/value settings.") + +part1 = 0 +part2 = 10000 +part3 = 0 +id = 562000999 +def newid(): + global id + id = id + 1 + return "%04X%04X%04X%012d" % (0, 10000, 0, id) + +class XCodeNode: + def __init__(self): + self._id = newid() + self._been_written = False + + def tostring(self, value): + if isinstance(value, dict): + result = "{\n" + for k,v in value.items(): + result = result + "\t\t\t%s = %s;\n" % (k, self.tostring(v)) + result = result + "\t\t}" + return result + elif isinstance(value, str): + return "\"%s\"" % value + elif isinstance(value, list): + result = "(\n" + for i in value: + result = result + "\t\t\t%s,\n" % self.tostring(i) + result = result + "\t\t)" + return result + elif isinstance(value, XCodeNode): + return value._id + else: + return str(value) + + def write_recursive(self, value, file): + if isinstance(value, dict): + for k,v in value.items(): + self.write_recursive(v, file) + elif isinstance(value, list): + for i in value: + self.write_recursive(i, file) + elif isinstance(value, XCodeNode): + value.write(file) + + def write(self, file): + if not self._been_written: + self._been_written = True + for attribute,value in self.__dict__.items(): + if attribute[0] != '_': + self.write_recursive(value, file) + w = file.write + w("\t%s = {\n" % self._id) + w("\t\tisa = %s;\n" % self.__class__.__name__) + for attribute,value in self.__dict__.items(): + if attribute[0] != '_': + w("\t\t%s = %s;\n" % (attribute, self.tostring(value))) + w("\t};\n\n") + +# Configurations +class XCBuildConfiguration(XCodeNode): + def __init__(self, name, settings = {}, env=None): + XCodeNode.__init__(self) + self.baseConfigurationReference = "" + self.buildSettings = settings + self.name = name + if env and env.ARCH: + settings['ARCHS'] = " ".join(env.ARCH) + + +class XCConfigurationList(XCodeNode): + def __init__(self, configlst): + """ :param configlst: list of XCConfigurationList """ + XCodeNode.__init__(self) + self.buildConfigurations = configlst + self.defaultConfigurationIsVisible = 0 + self.defaultConfigurationName = configlst and configlst[0].name or "" + +# Group/Files +class PBXFileReference(XCodeNode): + def __init__(self, name, path, filetype = '', sourcetree = "SOURCE_ROOT"): + XCodeNode.__init__(self) + self.fileEncoding = 4 + if not filetype: + _, ext = os.path.splitext(name) + filetype = MAP_EXT.get(ext, 'text') + self.lastKnownFileType = filetype + self.name = name + self.path = path + self.sourceTree = sourcetree + + def __hash__(self): + return (self.path+self.name).__hash__() + + def __eq__(self, other): + return (self.path, self.name) == (other.path, other.name) + +class PBXBuildFile(XCodeNode): + """ This element indicate a file reference that is used in a PBXBuildPhase (either as an include or resource). """ + def __init__(self, fileRef, settings={}): + XCodeNode.__init__(self) + + # fileRef is a reference to a PBXFileReference object + self.fileRef = fileRef + + # A map of key/value pairs for additionnal settings. + self.settings = settings + + def __hash__(self): + return (self.fileRef).__hash__() + + def __eq__(self, other): + return self.fileRef == other.fileRef + +class PBXGroup(XCodeNode): + def __init__(self, name, sourcetree = ""): + XCodeNode.__init__(self) + self.children = [] + self.name = name + self.sourceTree = sourcetree + + def add(self, sources): + """ sources param should be a list of PBXFileReference objects """ + self.children.extend(sources) + +class PBXContainerItemProxy(XCodeNode): + """ This is the element for to decorate a target item. """ + def __init__(self, containerPortal, remoteGlobalIDString, remoteInfo='', proxyType=1): + XCodeNode.__init__(self) + self.containerPortal = containerPortal # PBXProject + self.remoteGlobalIDString = remoteGlobalIDString # PBXNativeTarget + self.remoteInfo = remoteInfo # Target name + self.proxyType = proxyType + + +class PBXTargetDependency(XCodeNode): + """ This is the element for referencing other target through content proxies. """ + def __init__(self, native_target, proxy): + XCodeNode.__init__(self) + self.target = native_target + self.targetProxy = proxy + +class PBXFrameworksBuildPhase(XCodeNode): + """ This is the element for the framework link build phase, i.e. linking to frameworks """ + def __init__(self, pbxbuildfiles): + XCodeNode.__init__(self) + self.buildActionMask = 2147483647 + self.runOnlyForDeploymentPostprocessing = 0 + self.files = pbxbuildfiles #List of PBXBuildFile (.o, .framework, .dylib) + +class PBXHeadersBuildPhase(XCodeNode): + """ This is the element for adding header files to be packaged into the .framework """ + def __init__(self, pbxbuildfiles): + XCodeNode.__init__(self) + self.buildActionMask = 2147483647 + self.runOnlyForDeploymentPostprocessing = 0 + self.files = pbxbuildfiles #List of PBXBuildFile (.o, .framework, .dylib) + +class PBXCopyFilesBuildPhase(XCodeNode): + """ + Represents the PBXCopyFilesBuildPhase section. PBXBuildFile + can be added to this node to copy files after build is done. + """ + def __init__(self, pbxbuildfiles, dstpath, dstSubpathSpec=0, *args, **kwargs): + XCodeNode.__init__(self) + self.files = pbxbuildfiles + self.dstPath = dstpath + self.dstSubfolderSpec = dstSubpathSpec + +class PBXSourcesBuildPhase(XCodeNode): + """ Represents the 'Compile Sources' build phase in a Xcode target """ + def __init__(self, buildfiles): + XCodeNode.__init__(self) + self.files = buildfiles # List of PBXBuildFile objects + +class PBXLegacyTarget(XCodeNode): + def __init__(self, action, target=''): + XCodeNode.__init__(self) + self.buildConfigurationList = XCConfigurationList([XCBuildConfiguration('waf', {})]) + if not target: + self.buildArgumentsString = "%s %s" % (sys.argv[0], action) + else: + self.buildArgumentsString = "%s %s --targets=%s" % (sys.argv[0], action, target) + self.buildPhases = [] + self.buildToolPath = sys.executable + self.buildWorkingDirectory = "" + self.dependencies = [] + self.name = target or action + self.productName = target or action + self.passBuildSettingsInEnvironment = 0 + +class PBXShellScriptBuildPhase(XCodeNode): + def __init__(self, action, target): + XCodeNode.__init__(self) + self.buildActionMask = 2147483647 + self.files = [] + self.inputPaths = [] + self.outputPaths = [] + self.runOnlyForDeploymentPostProcessing = 0 + self.shellPath = "/bin/sh" + self.shellScript = "%s %s %s --targets=%s" % (sys.executable, sys.argv[0], action, target) + +class PBXNativeTarget(XCodeNode): + """ Represents a target in XCode, e.g. App, DyLib, Framework etc. """ + def __init__(self, target, node, target_type=TARGET_TYPE_APPLICATION, configlist=[], buildphases=[]): + XCodeNode.__init__(self) + product_type = target_type[0] + file_type = target_type[1] + + self.buildConfigurationList = XCConfigurationList(configlist) + self.buildPhases = buildphases + self.buildRules = [] + self.dependencies = [] + self.name = target + self.productName = target + self.productType = product_type # See TARGET_TYPE_ tuples constants + self.productReference = PBXFileReference(node.name, node.abspath(), file_type, '') + + def add_configuration(self, cf): + """ :type cf: XCBuildConfiguration """ + self.buildConfigurationList.buildConfigurations.append(cf) + + def add_build_phase(self, phase): + # Some build phase types may appear only once. If a phase type already exists, then merge them. + if ( (phase.__class__ == PBXFrameworksBuildPhase) + or (phase.__class__ == PBXSourcesBuildPhase) ): + for b in self.buildPhases: + if b.__class__ == phase.__class__: + b.files.extend(phase.files) + return + self.buildPhases.append(phase) + + def add_dependency(self, depnd): + self.dependencies.append(depnd) + +# Root project object +class PBXProject(XCodeNode): + def __init__(self, name, version, env): + XCodeNode.__init__(self) + + if not isinstance(env.PROJ_CONFIGURATION, dict): + raise Errors.WafError("Error: env.PROJ_CONFIGURATION must be a dictionary. This is done for you if you do not define one yourself. However, did you load the xcode module at the end of your wscript configure() ?") + + # Retreive project configuration + configurations = [] + for config_name, settings in env.PROJ_CONFIGURATION.items(): + cf = XCBuildConfiguration(config_name, settings) + configurations.append(cf) + + self.buildConfigurationList = XCConfigurationList(configurations) + self.compatibilityVersion = version[0] + self.hasScannedForEncodings = 1; + self.mainGroup = PBXGroup(name) + self.projectRoot = "" + self.projectDirPath = "" + self.targets = [] + self._objectVersion = version[1] + + def create_target_dependency(self, target, name): + """ : param target : PXBNativeTarget """ + proxy = PBXContainerItemProxy(self, target, name) + dependecy = PBXTargetDependency(target, proxy) + return dependecy + + def write(self, file): + + # Make sure this is written only once + if self._been_written: + return + + w = file.write + w("// !$*UTF8*$!\n") + w("{\n") + w("\tarchiveVersion = 1;\n") + w("\tclasses = {\n") + w("\t};\n") + w("\tobjectVersion = %d;\n" % self._objectVersion) + w("\tobjects = {\n\n") + + XCodeNode.write(self, file) + + w("\t};\n") + w("\trootObject = %s;\n" % self._id) + w("}\n") + + def add_target(self, target): + self.targets.append(target) + + def get_target(self, name): + """ Get a reference to PBXNativeTarget if it exists """ + for t in self.targets: + if t.name == name: + return t + return None + +class xcode(Build.BuildContext): + cmd = 'xcode6' + fun = 'build' + + file_refs = dict() + build_files = dict() + + def as_nodes(self, files): + """ Returns a list of waflib.Nodes from a list of string of file paths """ + nodes = [] + for x in files: + if not isinstance(x, str): + d = x + else: + d = self.srcnode.find_node(x) + nodes.append(d) + return nodes + + def create_group(self, name, files): + """ + Returns a new PBXGroup containing the files (paths) passed in the files arg + :type files: string + """ + group = PBXGroup(name) + """ + Do not use unique file reference here, since XCode seem to allow only one file reference + to be referenced by a group. + """ + files = [(PBXFileReference(d.name, d.abspath())) for d in self.as_nodes(files)] + group.add(files) + return group + + def unique_filereference(self, fileref): + """ + Returns a unique fileref, possibly an existing one if the paths are the same. + Use this after you've constructed a PBXFileReference to make sure there is + only one PBXFileReference for the same file in the same project. + """ + if fileref not in self.file_refs: + self.file_refs[fileref] = fileref + return self.file_refs[fileref] + + def unique_buildfile(self, buildfile): + """ + Returns a unique buildfile, possibly an existing one. + Use this after you've constructed a PBXBuildFile to make sure there is + only one PBXBuildFile for the same file in the same project. + """ + if buildfile not in self.build_files: + self.build_files[buildfile] = buildfile + return self.build_files[buildfile] + + def execute(self): + """ + Entry point + """ + self.restore() + if not self.all_envs: + self.load_envs() + self.recurse([self.run_dir]) + + appname = getattr(Context.g_module, Context.APPNAME, os.path.basename(self.srcnode.abspath())) + + p = PBXProject(appname, ('Xcode 3.2', 46), self.env) + + # If we don't create a Products group, then + # XCode will create one, which entails that + # we'll start to see duplicate files in the UI + # for some reason. + products_group = PBXGroup('Products') + p.mainGroup.children.append(products_group) + + for g in self.groups: + for tg in g: + if not isinstance(tg, TaskGen.task_gen): + continue + + tg.post() + + target_group = PBXGroup(tg.name) + p.mainGroup.children.append(target_group) + + # Determine what type to build - framework, app bundle etc. + target_type = getattr(tg, 'target_type', 'app') + if target_type not in TARGET_TYPES: + raise Errors.WafError("Target type '%s' does not exists. Available options are '%s'. In target '%s'" % (target_type, "', '".join(TARGET_TYPES.keys()), tg.name)) + else: + target_type = TARGET_TYPES[target_type] + file_ext = target_type[2] + + # Create the output node + target_node = tg.path.find_or_declare(tg.name+file_ext) + + target = PBXNativeTarget(tg.name, target_node, target_type, [], []) + + products_group.children.append(target.productReference) + + if hasattr(tg, 'source_files'): + # Create list of PBXFileReferences + sources = [] + if isinstance(tg.source_files, dict): + for grpname,files in tg.source_files.items(): + group = self.create_group(grpname, files) + target_group.children.append(group) + sources.extend(group.children) + elif isinstance(tg.source_files, list): + group = self.create_group("Source", tg.source_files) + target_group.children.append(group) + sources.extend(group.children) + else: + self.to_log("Argument 'source_files' passed to target '%s' was not a dictionary. Hence, some source files may not be included. Please provide a dictionary of source files, with group name as key and list of source files as value.\n" % tg.name) + + supported_extensions = ['.c', '.cpp', '.m', '.mm'] + sources = filter(lambda fileref: os.path.splitext(fileref.path)[1] in supported_extensions, sources) + buildfiles = [self.unique_buildfile(PBXBuildFile(fileref)) for fileref in sources] + target.add_build_phase(PBXSourcesBuildPhase(buildfiles)) + + # Create build settings which can override the project settings. Defaults to none if user + # did not pass argument. However, this will be filled up further below with target specfic + # search paths, libs to link etc. + settings = getattr(tg, 'settings', {}) + + # Check if any framework to link against is some other target we've made + libs = getattr(tg, 'tmp_use_seen', []) + for lib in libs: + use_target = p.get_target(lib) + if use_target: + # Create an XCode dependency so that XCode knows to build the other target before this target + target.add_dependency(p.create_target_dependency(use_target, use_target.name)) + target.add_build_phase(PBXFrameworksBuildPhase([PBXBuildFile(use_target.productReference)])) + if lib in tg.env.LIB: + tg.env.LIB = list(filter(lambda x: x != lib, tg.env.LIB)) + + # If 'export_headers' is present, add files to the Headers build phase in xcode. + # These are files that'll get packed into the Framework for instance. + exp_hdrs = getattr(tg, 'export_headers', []) + hdrs = self.as_nodes(Utils.to_list(exp_hdrs)) + files = [self.unique_filereference(PBXFileReference(n.name, n.abspath())) for n in hdrs] + target.add_build_phase(PBXHeadersBuildPhase([PBXBuildFile(f, {'ATTRIBUTES': ('Public',)}) for f in files])) + + # Install path + installpaths = Utils.to_list(getattr(tg, 'install', [])) + prodbuildfile = PBXBuildFile(target.productReference) + for instpath in installpaths: + target.add_build_phase(PBXCopyFilesBuildPhase([prodbuildfile], instpath)) + + # Merge frameworks and libs into one list, and prefix the frameworks + ld_flags = ['-framework %s' % lib.split('.framework')[0] for lib in Utils.to_list(tg.env.FRAMEWORK)] + ld_flags.extend(Utils.to_list(tg.env.STLIB) + Utils.to_list(tg.env.LIB)) + + # Override target specfic build settings + bldsettings = { + 'HEADER_SEARCH_PATHS': ['$(inherited)'] + tg.env['INCPATHS'], + 'LIBRARY_SEARCH_PATHS': ['$(inherited)'] + Utils.to_list(tg.env.LIBPATH) + Utils.to_list(tg.env.STLIBPATH), + 'FRAMEWORK_SEARCH_PATHS': ['$(inherited)'] + Utils.to_list(tg.env.FRAMEWORKPATH), + 'OTHER_LDFLAGS': r'\n'.join(ld_flags) + } + + # The keys represents different build configuration, e.g. Debug, Release and so on.. + # Insert our generated build settings to all configuration names + keys = set(settings.keys() + self.env.PROJ_CONFIGURATION.keys()) + for k in keys: + if k in settings: + settings[k].update(bldsettings) + else: + settings[k] = bldsettings + + for k,v in settings.items(): + target.add_configuration(XCBuildConfiguration(k, v)) + + p.add_target(target) + + node = self.bldnode.make_node('%s.xcodeproj' % appname) + node.mkdir() + node = node.make_node('project.pbxproj') + p.write(open(node.abspath(), 'w')) + + def build_target(self, tgtype, *k, **kw): + """ + Provide user-friendly methods to build different target types + E.g. bld.framework(source='..', ...) to build a Framework target. + E.g. bld.dylib(source='..', ...) to build a Dynamic library target. etc... + """ + self.load('ccroot') + kw['features'] = 'cxx cxxprogram' + kw['target_type'] = tgtype + return self(*k, **kw) + + def app(self, *k, **kw): return self.build_target('app', *k, **kw) + def framework(self, *k, **kw): return self.build_target('framework', *k, **kw) + def dylib(self, *k, **kw): return self.build_target('dylib', *k, **kw) + def stlib(self, *k, **kw): return self.build_target('stlib', *k, **kw) + def exe(self, *k, **kw): return self.build_target('exe', *k, **kw) diff -Nru jackd2-1.9.11~20161209/waflib/extras/xcode.py jackd2-1.9.11+git20170722/waflib/extras/xcode.py --- jackd2-1.9.11~20161209/waflib/extras/xcode.py 1970-01-01 00:00:00.000000000 +0000 +++ jackd2-1.9.11+git20170722/waflib/extras/xcode.py 2017-07-22 08:00:34.000000000 +0000 @@ -0,0 +1,312 @@ +#! /usr/bin/env python +# encoding: utf-8 +# XCode 3/XCode 4 generator for Waf +# Nicolas Mercier 2011 + +""" +Usage: + +def options(opt): + opt.load('xcode') + +$ waf configure xcode +""" + +# TODO: support iOS projects + +from waflib import Context, TaskGen, Build, Utils +import os, sys + +HEADERS_GLOB = '**/(*.h|*.hpp|*.H|*.inl)' + +MAP_EXT = { + '.h' : "sourcecode.c.h", + + '.hh': "sourcecode.cpp.h", + '.inl': "sourcecode.cpp.h", + '.hpp': "sourcecode.cpp.h", + + '.c': "sourcecode.c.c", + + '.m': "sourcecode.c.objc", + + '.mm': "sourcecode.cpp.objcpp", + + '.cc': "sourcecode.cpp.cpp", + + '.cpp': "sourcecode.cpp.cpp", + '.C': "sourcecode.cpp.cpp", + '.cxx': "sourcecode.cpp.cpp", + '.c++': "sourcecode.cpp.cpp", + + '.l': "sourcecode.lex", # luthor + '.ll': "sourcecode.lex", + + '.y': "sourcecode.yacc", + '.yy': "sourcecode.yacc", + + '.plist': "text.plist.xml", + ".nib": "wrapper.nib", + ".xib": "text.xib", +} + + +part1 = 0 +part2 = 10000 +part3 = 0 +id = 562000999 +def newid(): + global id + id = id + 1 + return "%04X%04X%04X%012d" % (0, 10000, 0, id) + +class XCodeNode: + def __init__(self): + self._id = newid() + + def tostring(self, value): + if isinstance(value, dict): + result = "{\n" + for k,v in value.items(): + result = result + "\t\t\t%s = %s;\n" % (k, self.tostring(v)) + result = result + "\t\t}" + return result + elif isinstance(value, str): + return "\"%s\"" % value + elif isinstance(value, list): + result = "(\n" + for i in value: + result = result + "\t\t\t%s,\n" % self.tostring(i) + result = result + "\t\t)" + return result + elif isinstance(value, XCodeNode): + return value._id + else: + return str(value) + + def write_recursive(self, value, file): + if isinstance(value, dict): + for k,v in value.items(): + self.write_recursive(v, file) + elif isinstance(value, list): + for i in value: + self.write_recursive(i, file) + elif isinstance(value, XCodeNode): + value.write(file) + + def write(self, file): + for attribute,value in self.__dict__.items(): + if attribute[0] != '_': + self.write_recursive(value, file) + + w = file.write + w("\t%s = {\n" % self._id) + w("\t\tisa = %s;\n" % self.__class__.__name__) + for attribute,value in self.__dict__.items(): + if attribute[0] != '_': + w("\t\t%s = %s;\n" % (attribute, self.tostring(value))) + w("\t};\n\n") + + + +# Configurations +class XCBuildConfiguration(XCodeNode): + def __init__(self, name, settings = {}, env=None): + XCodeNode.__init__(self) + self.baseConfigurationReference = "" + self.buildSettings = settings + self.name = name + if env and env.ARCH: + settings['ARCHS'] = " ".join(env.ARCH) + + +class XCConfigurationList(XCodeNode): + def __init__(self, settings): + XCodeNode.__init__(self) + self.buildConfigurations = settings + self.defaultConfigurationIsVisible = 0 + self.defaultConfigurationName = settings and settings[0].name or "" + +# Group/Files +class PBXFileReference(XCodeNode): + def __init__(self, name, path, filetype = '', sourcetree = "SOURCE_ROOT"): + XCodeNode.__init__(self) + self.fileEncoding = 4 + if not filetype: + _, ext = os.path.splitext(name) + filetype = MAP_EXT.get(ext, 'text') + self.lastKnownFileType = filetype + self.name = name + self.path = path + self.sourceTree = sourcetree + +class PBXGroup(XCodeNode): + def __init__(self, name, sourcetree = ""): + XCodeNode.__init__(self) + self.children = [] + self.name = name + self.sourceTree = sourcetree + + def add(self, root, sources): + folders = {} + def folder(n): + if not n.is_child_of(root): + return self + try: + return folders[n] + except KeyError: + f = PBXGroup(n.name) + p = folder(n.parent) + folders[n] = f + p.children.append(f) + return f + for s in sources: + f = folder(s.parent) + source = PBXFileReference(s.name, s.abspath()) + f.children.append(source) + + +# Targets +class PBXLegacyTarget(XCodeNode): + def __init__(self, action, target=''): + XCodeNode.__init__(self) + self.buildConfigurationList = XCConfigurationList([XCBuildConfiguration('waf', {})]) + if not target: + self.buildArgumentsString = "%s %s" % (sys.argv[0], action) + else: + self.buildArgumentsString = "%s %s --targets=%s" % (sys.argv[0], action, target) + self.buildPhases = [] + self.buildToolPath = sys.executable + self.buildWorkingDirectory = "" + self.dependencies = [] + self.name = target or action + self.productName = target or action + self.passBuildSettingsInEnvironment = 0 + +class PBXShellScriptBuildPhase(XCodeNode): + def __init__(self, action, target): + XCodeNode.__init__(self) + self.buildActionMask = 2147483647 + self.files = [] + self.inputPaths = [] + self.outputPaths = [] + self.runOnlyForDeploymentPostProcessing = 0 + self.shellPath = "/bin/sh" + self.shellScript = "%s %s %s --targets=%s" % (sys.executable, sys.argv[0], action, target) + +class PBXNativeTarget(XCodeNode): + def __init__(self, action, target, node, env): + XCodeNode.__init__(self) + conf = XCBuildConfiguration('waf', {'PRODUCT_NAME':target, 'CONFIGURATION_BUILD_DIR':node.parent.abspath()}, env) + self.buildConfigurationList = XCConfigurationList([conf]) + self.buildPhases = [PBXShellScriptBuildPhase(action, target)] + self.buildRules = [] + self.dependencies = [] + self.name = target + self.productName = target + self.productType = "com.apple.product-type.application" + self.productReference = PBXFileReference(target, node.abspath(), 'wrapper.application', 'BUILT_PRODUCTS_DIR') + +# Root project object +class PBXProject(XCodeNode): + def __init__(self, name, version): + XCodeNode.__init__(self) + self.buildConfigurationList = XCConfigurationList([XCBuildConfiguration('waf', {})]) + self.compatibilityVersion = version[0] + self.hasScannedForEncodings = 1; + self.mainGroup = PBXGroup(name) + self.projectRoot = "" + self.projectDirPath = "" + self.targets = [] + self._objectVersion = version[1] + self._output = PBXGroup('out') + self.mainGroup.children.append(self._output) + + def write(self, file): + w = file.write + w("// !$*UTF8*$!\n") + w("{\n") + w("\tarchiveVersion = 1;\n") + w("\tclasses = {\n") + w("\t};\n") + w("\tobjectVersion = %d;\n" % self._objectVersion) + w("\tobjects = {\n\n") + + XCodeNode.write(self, file) + + w("\t};\n") + w("\trootObject = %s;\n" % self._id) + w("}\n") + + def add_task_gen(self, tg): + if not getattr(tg, 'mac_app', False): + self.targets.append(PBXLegacyTarget('build', tg.name)) + else: + target = PBXNativeTarget('build', tg.name, tg.link_task.outputs[0].change_ext('.app'), tg.env) + self.targets.append(target) + self._output.children.append(target.productReference) + +class xcode(Build.BuildContext): + cmd = 'xcode' + fun = 'build' + + def collect_source(self, tg): + source_files = tg.to_nodes(getattr(tg, 'source', [])) + plist_files = tg.to_nodes(getattr(tg, 'mac_plist', [])) + resource_files = [tg.path.find_node(i) for i in Utils.to_list(getattr(tg, 'mac_resources', []))] + include_dirs = Utils.to_list(getattr(tg, 'includes', [])) + Utils.to_list(getattr(tg, 'export_dirs', [])) + include_files = [] + for x in include_dirs: + if not isinstance(x, str): + include_files.append(x) + continue + d = tg.path.find_node(x) + if d: + lst = [y for y in d.ant_glob(HEADERS_GLOB, flat=False)] + include_files.extend(lst) + + # remove duplicates + source = list(set(source_files + plist_files + resource_files + include_files)) + source.sort(key=lambda x: x.abspath()) + return source + + def execute(self): + """ + Entry point + """ + self.restore() + if not self.all_envs: + self.load_envs() + self.recurse([self.run_dir]) + + appname = getattr(Context.g_module, Context.APPNAME, os.path.basename(self.srcnode.abspath())) + p = PBXProject(appname, ('Xcode 3.2', 46)) + + for g in self.groups: + for tg in g: + if not isinstance(tg, TaskGen.task_gen): + continue + + tg.post() + + features = Utils.to_list(getattr(tg, 'features', '')) + + group = PBXGroup(tg.name) + group.add(tg.path, self.collect_source(tg)) + p.mainGroup.children.append(group) + + if 'cprogram' or 'cxxprogram' in features: + p.add_task_gen(tg) + + + # targets that don't produce the executable but that you might want to run + p.targets.append(PBXLegacyTarget('configure')) + p.targets.append(PBXLegacyTarget('dist')) + p.targets.append(PBXLegacyTarget('install')) + p.targets.append(PBXLegacyTarget('check')) + node = self.srcnode.make_node('%s.xcodeproj' % appname) + node.mkdir() + node = node.make_node('project.pbxproj') + p.write(open(node.abspath(), 'w')) + + diff -Nru jackd2-1.9.11~20161209/.wafupdaterc jackd2-1.9.11+git20170722/.wafupdaterc --- jackd2-1.9.11~20161209/.wafupdaterc 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/.wafupdaterc 2017-07-22 08:00:34.000000000 +0000 @@ -133,6 +133,4 @@ valadoc why win32_opts - xcode - xcode6 " diff -Nru jackd2-1.9.11~20161209/windows/JackCompilerDeps_os.h jackd2-1.9.11+git20170722/windows/JackCompilerDeps_os.h --- jackd2-1.9.11~20161209/windows/JackCompilerDeps_os.h 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/windows/JackCompilerDeps_os.h 2017-07-22 08:02:50.000000000 +0000 @@ -40,9 +40,29 @@ #endif #if defined(_MSC_VER) /* Added by JE - 31-01-2012 */ -#define vsnprintf _vsnprintf -#define snprintf _snprintf #define strdup _strdup +#if _MSC_VER < 1900 +// This wrapper is not fully standard-compliant. _snprintf() does not +// distinguish whether a result is truncated or a format error occurs. +inline int vsnprintf(char* buf, size_t buf_len, const char* fmt, va_list args) +{ + int str_len = _vsnprintf(buf, buf_len - 1, fmt, args); + if (str_len == buf_len - 1 || str_len < 0) { + buf[buf_len - 1] = '\0'; + return buf_len - 1; + } + return str_len; +} + +inline int snprintf(char* buf, size_t buf_len, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + int str_len = vsnprintf(buf, buf_len, fmt, args); + va_end(args); + return str_len; +} +#endif #endif #endif diff -Nru jackd2-1.9.11~20161209/windows/JackSystemDeps_os.h jackd2-1.9.11+git20170722/windows/JackSystemDeps_os.h --- jackd2-1.9.11~20161209/windows/JackSystemDeps_os.h 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/windows/JackSystemDeps_os.h 2017-07-22 08:02:50.000000000 +0000 @@ -22,6 +22,7 @@ #define __JackSystemDeps_WIN32__ #include +#include "JackCompilerDeps.h" #ifndef PATH_MAX #define PATH_MAX 512 @@ -49,9 +50,18 @@ #define JACK_DEBUG false #endif -#if defined(_MSC_VER) -#define snprintf _snprintf -#endif +inline int setenv(const char* name, const char* value, int overwrite) +{ + if (overwrite == 0 && getenv(name) != NULL) { + return 0; + } + return _putenv_s(name, value); +} + +inline int unsetenv(const char* name) +{ + return _putenv_s(name, ""); +} #endif diff -Nru jackd2-1.9.11~20161209/windows/portaudio/JackPortAudioDriver.cpp jackd2-1.9.11+git20170722/windows/portaudio/JackPortAudioDriver.cpp --- jackd2-1.9.11~20161209/windows/portaudio/JackPortAudioDriver.cpp 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/windows/portaudio/JackPortAudioDriver.cpp 2017-07-22 08:02:50.000000000 +0000 @@ -196,8 +196,12 @@ } // Generic JackAudioDriver Open + char capture_driver_name[JACK_CLIENT_NAME_SIZE]; + char playback_driver_name[JACK_CLIENT_NAME_SIZE]; + snprintf(capture_driver_name, sizeof(capture_driver_name), "%s", capture_driver_uid); + snprintf(playback_driver_name, sizeof(playback_driver_name), "%s", playback_driver_uid); if (JackAudioDriver::Open(buffer_size, samplerate, capturing, playing, inchannels, outchannels, monitor, - capture_driver_uid, playback_driver_uid, capture_latency, playback_latency) != 0) { + capture_driver_name, playback_driver_name, capture_latency, playback_latency) != 0) { return -1; } @@ -239,12 +243,6 @@ fEngineControl->fConstraint = fEngineControl->fPeriodUsecs * 1000; #endif - assert(strlen(capture_driver_uid) < JACK_CLIENT_NAME_SIZE); - assert(strlen(playback_driver_uid) < JACK_CLIENT_NAME_SIZE); - - strcpy(fCaptureDriverName, capture_driver_uid); - strcpy(fPlaybackDriverName, playback_driver_uid); - return 0; error: diff -Nru jackd2-1.9.11~20161209/wscript jackd2-1.9.11+git20170722/wscript --- jackd2-1.9.11~20161209/wscript 2016-12-09 10:31:55.000000000 +0000 +++ jackd2-1.9.11+git20170722/wscript 2017-07-22 08:02:50.000000000 +0000 @@ -11,7 +11,7 @@ from waflib import Logs, Options, Task, Utils from waflib.Build import BuildContext, CleanContext, InstallContext, UninstallContext -VERSION='1.9.11' +VERSION='1.9.11-RC1' APPNAME='jack' JACK_API_VERSION = '0.1.0' @@ -387,11 +387,14 @@ opt.load('compiler_cxx') opt.load('compiler_c') + opt.load('xcode') + opt.load('xcode6') + # install directories - opt.add_option('--htmldir', type='string', default=None, help="HTML documentation directory [Default: /share/jack-audio-connection-kit/reference/html/") - opt.add_option('--libdir', type='string', help="Library directory [Default: /lib]") - opt.add_option('--libdir32', type='string', help="32bit Library directory [Default: /lib32]") - opt.add_option('--mandir', type='string', help="Manpage directory [Default: /share/man/man1]") + opt.add_option('--htmldir', type='string', default=None, help='HTML documentation directory [Default: /share/jack-audio-connection-kit/reference/html/') + opt.add_option('--libdir', type='string', help='Library directory [Default: /lib]') + opt.add_option('--libdir32', type='string', help='32bit Library directory [Default: /lib32]') + opt.add_option('--mandir', type='string', help='Manpage directory [Default: /share/man/man1]') # options affecting binaries opt.add_option('--platform', type='string', default=sys.platform, help='Target platform for cross-compiling, e.g. cygwin or win32') @@ -401,10 +404,10 @@ # options affecting general jack functionality opt.add_option('--classic', action='store_true', default=False, help='Force enable standard JACK (jackd) even if D-Bus JACK (jackdbus) is enabled too') opt.add_option('--dbus', action='store_true', default=False, help='Enable D-Bus JACK (jackdbus)') - opt.add_option('--autostart', type='string', default="default", help='Autostart method. Possible values: "default", "classic", "dbus", "none"') + opt.add_option('--autostart', type='string', default='default', help='Autostart method. Possible values: "default", "classic", "dbus", "none"') opt.add_option('--profile', action='store_true', default=False, help='Build with engine profiling') - opt.add_option('--clients', default=64, type="int", dest="clients", help='Maximum number of JACK clients') - opt.add_option('--ports-per-application', default=768, type="int", dest="application_ports", help='Maximum number of ports per application') + opt.add_option('--clients', default=64, type='int', dest='clients', help='Maximum number of JACK clients') + opt.add_option('--ports-per-application', default=768, type='int', dest='application_ports', help='Maximum number of ports per application') # options with third party dependencies doxygen = add_auto_option(opt, 'doxygen', help='Build doxygen documentation', conf_dest='BUILD_DOXYGEN_DOCS') @@ -450,7 +453,7 @@ ('IS_LINUX', 'Linux', ['gnu0', 'gnukfreebsd', 'linux', 'posix']), ('IS_MACOSX', 'MacOS X', ['darwin']), ('IS_SUN', 'SunOS', ['sunos']), - ('IS_WINDOWS', 'Windows', ['cygwin', 'win32']) + ('IS_WINDOWS', 'Windows', ['cygwin', 'msys', 'win32']) ] for key,name,strings in platforms: @@ -478,6 +481,9 @@ conf.env.append_unique('CXXFLAGS', '-Wall') conf.env.append_unique('CFLAGS', '-Wall') + if conf.env['IS_MACOSX']: + conf.check(lib='aften', uselib='AFTEN', define_name='AFTEN') + # configure all auto options configure_auto_options(conf) @@ -488,6 +494,12 @@ defines=['_GNU_SOURCE'], mandatory=False) + # Check for backtrace support + conf.check( + header_name='execinfo.h', + define_name='HAVE_EXECINFO_H', + mandatory=False) + conf.recurse('common') if Options.options.dbus: conf.recurse('dbus') @@ -496,6 +508,21 @@ conf.recurse('example-clients') + # test for the availability of ucontext, and how it should be used + for t in ['gp_regs', 'uc_regs', 'mc_gregs', 'gregs']: + fragment = '#include \n' + fragment += 'int main() { ucontext_t *ucontext; return (int) ucontext->uc_mcontext.%s[0]; }' % t + confvar = 'HAVE_UCONTEXT_%s' % t.upper() + conf.check_cc(fragment=fragment, define_name=confvar, mandatory=False, + msg='Checking for ucontext->uc_mcontext.%s' % t) + if conf.is_defined(confvar): + conf.define('HAVE_UCONTEXT', 1) + + fragment = '#include \n' + fragment += 'int main() { return NGREG; }' + conf.check_cc(fragment=fragment, define_name='HAVE_NGREG', mandatory=False, + msg='Checking for NGREG') + conf.env['LIB_PTHREAD'] = ['pthread'] conf.env['LIB_DL'] = ['dl'] conf.env['LIB_RT'] = ['rt'] @@ -538,10 +565,10 @@ conf.env.append_unique('CFLAGS', '-g') conf.env.append_unique('LINKFLAGS', '-g') - if not Options.options.autostart in ["default", "classic", "dbus", "none"]: - conf.fatal("Invalid autostart value \"" + Options.options.autostart + "\"") + if not Options.options.autostart in ['default', 'classic', 'dbus' 'none']: + conf.fatal('Invalid autostart value "' + Options.options.autostart + '"') - if Options.options.autostart == "default": + if Options.options.autostart == 'default': if conf.env['BUILD_JACKD']: conf.env['AUTOSTART_METHOD'] = 'classic' else: @@ -549,14 +576,14 @@ else: conf.env['AUTOSTART_METHOD'] = Options.options.autostart - if conf.env['AUTOSTART_METHOD'] == "dbus" and not conf.env['BUILD_JACKDBUS']: - conf.fatal("D-Bus autostart mode was specified but jackdbus will not be built") - if conf.env['AUTOSTART_METHOD'] == "classic" and not conf.env['BUILD_JACKD']: - conf.fatal("Classic autostart mode was specified but jackd will not be built") + if conf.env['AUTOSTART_METHOD'] == 'dbus' and not conf.env['BUILD_JACKDBUS']: + conf.fatal('D-Bus autostart mode was specified but jackdbus will not be built') + if conf.env['AUTOSTART_METHOD'] == 'classic' and not conf.env['BUILD_JACKD']: + conf.fatal('Classic autostart mode was specified but jackd will not be built') - if conf.env['AUTOSTART_METHOD'] == "dbus": + if conf.env['AUTOSTART_METHOD'] == 'dbus': conf.define('USE_LIBDBUS_AUTOLAUNCH', 1) - elif conf.env['AUTOSTART_METHOD'] == "classic": + elif conf.env['AUTOSTART_METHOD'] == 'classic': conf.define('USE_CLASSIC_AUTOLAUNCH', 1) conf.define('CLIENT_NUM', Options.options.clients) @@ -607,18 +634,18 @@ print() print('==================') - version_msg = "JACK " + VERSION + version_msg = 'JACK ' + VERSION if svnrev: - version_msg += " exported from r" + svnrev + version_msg += ' exported from r' + svnrev else: - version_msg += " svn revision will checked and eventually updated during build" + version_msg += ' svn revision will checked and eventually updated during build' print(version_msg) conf.msg('Maximum JACK clients', Options.options.clients, color='NORMAL') conf.msg('Maximum ports per application', Options.options.application_ports, color='NORMAL') conf.msg('Install prefix', conf.env['PREFIX'], color='CYAN') - conf.msg('Library directory', conf.all_envs[""]['LIBDIR'], color='CYAN') + conf.msg('Library directory', conf.all_envs['']['LIBDIR'], color='CYAN') if conf.env['BUILD_WITH_32_64']: conf.msg('32-bit library directory', conf.all_envs[lib32]['LIBDIR'], color='CYAN') conf.msg('Drivers directory', conf.env['ADDON_DIR'], color='CYAN') @@ -632,7 +659,7 @@ for name,vars in tool_flags: flags = [] for var in vars: - flags += conf.all_envs[""][var] + flags += conf.all_envs[''][var] conf.msg(name, repr(flags), color='NORMAL') if conf.env['BUILD_WITH_32_64']: @@ -658,11 +685,11 @@ if conf.env['DBUS_SERVICES_DIR'] != conf.env['DBUS_SERVICES_DIR_REAL']: print() - print(Logs.colors.RED + "WARNING: D-Bus session services directory as reported by pkg-config is") - print(Logs.colors.RED + "WARNING:", end=' ') + print(Logs.colors.RED + 'WARNING: D-Bus session services directory as reported by pkg-config is') + print(Logs.colors.RED + 'WARNING:', end=' ') print(Logs.colors.CYAN + conf.env['DBUS_SERVICES_DIR_REAL']) print(Logs.colors.RED + 'WARNING: but service file will be installed in') - print(Logs.colors.RED + "WARNING:", end=' ') + print(Logs.colors.RED + 'WARNING:', end=' ') print(Logs.colors.CYAN + conf.env['DBUS_SERVICES_DIR']) print(Logs.colors.RED + 'WARNING: You may need to adjust your D-Bus configuration after installing jackdbus') print('WARNING: You can override dbus service install directory') @@ -701,7 +728,8 @@ includes = ['.', 'common', 'common/jack'], target = 'jackd', source = ['common/Jackdmp.cpp'], - use = ['serverlib']) + use = ['serverlib'] + ) if bld.env['BUILD_JACKDBUS']: jackd.source += ['dbus/audio_reserve.c', 'dbus/reserve.c'] @@ -711,8 +739,8 @@ jackd.use += ['DL', 'M', 'PTHREAD', 'RT', 'STDC++'] if bld.env['IS_MACOSX']: - bld.framework = ['CoreFoundation'] jackd.use += ['DL', 'PTHREAD'] + jackd.framework = ['CoreFoundation'] if bld.env['IS_SUN']: jackd.use += ['DL', 'PTHREAD'] @@ -723,8 +751,17 @@ # FIXME: Is SERVER_SIDE needed? def create_driver_obj(bld, **kw): + if bld.env['IS_MACOSX'] or bld.env['IS_WINDOWS']: + # On MacOSX this is necessary. + # I do not know if this is necessary on Windows. + # Note added on 2015-12-13 by lilrc. + if 'use' in kw: + kw['use'] += ['serverlib'] + else: + kw['use'] = ['serverlib'] + driver = bld( - features = ['c', 'cshlib', 'cxx', 'cxxshlib'], + features = ['c', 'cxx', 'cshlib', 'cxxshlib'], defines = ['HAVE_CONFIG_H', 'SERVER_SIDE'], includes = ['.', 'common', 'common/jack'], install_path = '${ADDON_DIR}/', @@ -793,19 +830,20 @@ ] coreaudio_src = [ - 'macosx/coreaudio/JackCoreAudioDriver.cpp' + 'macosx/coreaudio/JackCoreAudioDriver.mm', + 'common/JackAC3Encoder.cpp' ] coremidi_src = [ - 'macosx/coremidi/JackCoreMidiInputPort.cpp', - 'macosx/coremidi/JackCoreMidiOutputPort.cpp', - 'macosx/coremidi/JackCoreMidiPhysicalInputPort.cpp', - 'macosx/coremidi/JackCoreMidiPhysicalOutputPort.cpp', - 'macosx/coremidi/JackCoreMidiVirtualInputPort.cpp', - 'macosx/coremidi/JackCoreMidiVirtualOutputPort.cpp', - 'macosx/coremidi/JackCoreMidiPort.cpp', - 'macosx/coremidi/JackCoreMidiUtil.cpp', - 'macosx/coremidi/JackCoreMidiDriver.cpp' + 'macosx/coremidi/JackCoreMidiInputPort.mm', + 'macosx/coremidi/JackCoreMidiOutputPort.mm', + 'macosx/coremidi/JackCoreMidiPhysicalInputPort.mm', + 'macosx/coremidi/JackCoreMidiPhysicalOutputPort.mm', + 'macosx/coremidi/JackCoreMidiVirtualInputPort.mm', + 'macosx/coremidi/JackCoreMidiVirtualOutputPort.mm', + 'macosx/coremidi/JackCoreMidiPort.mm', + 'macosx/coremidi/JackCoreMidiUtil.mm', + 'macosx/coremidi/JackCoreMidiDriver.mm' ] ffado_src = [ @@ -834,7 +872,7 @@ 'windows/portaudio/JackPortAudioDriver.cpp', ] - winmme_driver_src = [ + winmme_src = [ 'windows/winmme/JackWinMMEDriver.cpp', 'windows/winmme/JackWinMMEInputPort.cpp', 'windows/winmme/JackWinMMEOutputPort.cpp', @@ -908,21 +946,21 @@ bld, target = 'portaudio', source = portaudio_src, - use = ['serverlib', 'PORTAUDIO']) # FIXME: Is serverlib needed here? + use = ['PORTAUDIO']) if bld.env['BUILD_DRIVER_WINMME']: create_driver_obj( bld, target = 'winmme', source = winmme_src, - use = ['serverlib', 'WINMME']) # FIXME: Is serverlib needed here? + use = ['WINMME']) if bld.env['IS_MACOSX']: create_driver_obj( bld, target = 'coreaudio', source = coreaudio_src, - use = ['serverlib'], # FIXME: Is this needed? + use = ['AFTEN'], framework = ['AudioUnit', 'CoreAudio', 'CoreServices']) create_driver_obj( @@ -930,7 +968,7 @@ target = 'coremidi', source = coremidi_src, use = ['serverlib'], # FIXME: Is this needed? - framework = ['AudioUnit', 'CoreMIDI', 'CoreServices']) + framework = ['AudioUnit', 'CoreMIDI', 'CoreServices', 'Foundation']) if bld.env['IS_SUN']: create_driver_obj( @@ -1029,24 +1067,30 @@ if bld.cmd == 'install': if os.path.isdir(html_install_dir): - Logs.pprint('CYAN', "Removing old doxygen documentation installation...") + Logs.pprint('CYAN', 'Removing old doxygen documentation installation...') shutil.rmtree(html_install_dir) - Logs.pprint('CYAN', "Removing old doxygen documentation installation done.") - Logs.pprint('CYAN', "Installing doxygen documentation...") + Logs.pprint('CYAN', 'Removing old doxygen documentation installation done.') + Logs.pprint('CYAN', 'Installing doxygen documentation...') shutil.copytree(html_build_dir, html_install_dir) - Logs.pprint('CYAN', "Installing doxygen documentation done.") + Logs.pprint('CYAN', 'Installing doxygen documentation done.') elif bld.cmd =='uninstall': - Logs.pprint('CYAN', "Uninstalling doxygen documentation...") + Logs.pprint('CYAN', 'Uninstalling doxygen documentation...') if os.path.isdir(share_dir): shutil.rmtree(share_dir) - Logs.pprint('CYAN', "Uninstalling doxygen documentation done.") + Logs.pprint('CYAN', 'Uninstalling doxygen documentation done.') elif bld.cmd =='clean': if os.access(html_build_dir, os.R_OK): - Logs.pprint('CYAN', "Removing doxygen generated documentation...") + Logs.pprint('CYAN', 'Removing doxygen generated documentation...') shutil.rmtree(html_build_dir) - Logs.pprint('CYAN', "Removing doxygen generated documentation done.") + Logs.pprint('CYAN', 'Removing doxygen generated documentation done.') def dist(ctx): # This code blindly assumes it is working in the toplevel source directory. if not os.path.exists('svnversion.h'): os.system('./svnversion_regenerate.sh svnversion.h') + +from waflib import TaskGen +@TaskGen.extension('.mm') +def mm_hook(self, node): + """Alias .mm files to be compiled the same as .cpp files, gcc will do the right thing.""" + return self.create_compiled_task('cxx', node)