diff --git a/common/JackFilters.h b/common/JackFilters.h new file mode 100644 index 00000000..b349dd23 --- /dev/null +++ b/common/JackFilters.h @@ -0,0 +1,64 @@ +/* +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. + +*/ + +#ifndef __JackFilters__ +#define __JackFilters__ + +#include "jack.h" + +namespace Jack +{ + + #define MAX_SIZE 64 + + struct JackFilter + { + + jack_time_t fTable[MAX_SIZE]; + + JackFilter() + { + for (int i = 0; i < MAX_SIZE; i++) + fTable[i] = 0; + } + + void AddValue(jack_time_t val) + { + memcpy(&fTable[1], &fTable[0], sizeof(jack_time_t) * (MAX_SIZE - 1)); + fTable[0] = val; + } + + jack_time_t GetVal() + { + jack_time_t mean = 0; + for (int i = 0; i < MAX_SIZE; i++) + mean += fTable[i]; + + return mean / MAX_SIZE; + } + }; + + inline float Range(float min, float max, float val) + { + return (val < min) ? min : ((val > max) ? max : val); + } + +} + +#endif diff --git a/common/JackIOAdapter.h b/common/JackIOAdapter.h index 0c38110f..b33f5727 100644 --- a/common/JackIOAdapter.h +++ b/common/JackIOAdapter.h @@ -25,6 +25,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackError.h" #include "JackResampler.h" #include +#include namespace Jack { diff --git a/common/JackNetIOAdapter.cpp b/common/JackNetIOAdapter.cpp index 85acb1a1..bfe4e84d 100644 --- a/common/JackNetIOAdapter.cpp +++ b/common/JackNetIOAdapter.cpp @@ -115,9 +115,10 @@ extern "C" return 1; } else { jack_log("Loading NetAudio Adapter"); - // adapter = new Jack::JackNetIOAdapter(jack_client, new Jack::JackCoreAudioIOAdapter(2, 2, 512, 44100.f), 2, 2); adapter = new Jack::JackCallbackNetIOAdapter(jack_client, - new Jack::JackPortAudioIOAdapter(2, 2, jack_get_buffer_size(jack_client), jack_get_sample_rate(jack_client)), 2, 2); + new Jack::JackCoreAudioIOAdapter(2, 2, jack_get_buffer_size(jack_client), jack_get_sample_rate(jack_client)), 2, 2); + //adapter = new Jack::JackCallbackNetIOAdapter(jack_client, + // new Jack::JackPortAudioIOAdapter(2, 2, jack_get_buffer_size(jack_client), jack_get_sample_rate(jack_client)), 2, 2); assert(adapter); if (adapter->Open() == 0) { diff --git a/common/JackResampler.cpp b/common/JackResampler.cpp new file mode 100644 index 00000000..6a2d0ef1 --- /dev/null +++ b/common/JackResampler.cpp @@ -0,0 +1,153 @@ +/* +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 "JackResampler.h" + +namespace Jack +{ + +JackResampler::JackResampler():fRatio(0) +{ + int error; + fResampler = src_new(SRC_LINEAR, 1, &error); + if (error != 0) + jack_error("JackResampler::JackResampler err = %s", src_strerror(error)); + fRingBuffer = jack_ringbuffer_create(sizeof(float) * DEFAULT_RB_SIZE); +} + +JackResampler::~JackResampler() +{ + src_delete(fResampler); + if (fRingBuffer) + jack_ringbuffer_free(fRingBuffer); +} + +int JackResampler::Read(float* buffer, unsigned int frames) +{ + size_t len = jack_ringbuffer_read_space(fRingBuffer); + jack_log("JackResampler::Read INPUT available = %ld", len / sizeof(float)); + + if (len < frames * sizeof(float)) { + jack_error("JackResampler::Read : producer too slow, skip frames = %d", frames); + //jack_ringbuffer_read(fRingBuffer, buffer, len); + return 0; + } else { + jack_ringbuffer_read(fRingBuffer, (char*)buffer, frames * sizeof(float)); + return frames; + } +} + +int JackResampler::Write(float* buffer, unsigned int frames) +{ + size_t len = jack_ringbuffer_write_space(fRingBuffer); + jack_log("JackResampler::Write OUTPUT available = %ld", len / sizeof(float)); + + if (len < frames * sizeof(float)) { + jack_error("JackResampler::Write : consumer too slow, missing frames = %d", frames); + //jack_ringbuffer_write(fRingBuffer, buffer, len); + return 0; + } else { + jack_ringbuffer_write(fRingBuffer, (char*)buffer, frames * sizeof(float)); + return frames; + } +} + +int JackResampler::ReadResample(float* buffer, unsigned int frames) +{ + jack_ringbuffer_data_t ring_buffer_data[2]; + SRC_DATA src_data; + unsigned int frames_to_write = frames; + unsigned int written_frames = 0; + int res; + + jack_ringbuffer_get_read_vector(fRingBuffer, ring_buffer_data); + unsigned int available_frames = (ring_buffer_data[0].len + ring_buffer_data[1].len) / sizeof(float); + jack_log("OUPUT available = %ld", available_frames); + + for (int j = 0; j < 2; j++) { + + if (ring_buffer_data[j].len > 0) { + + src_data.data_in = (float*)ring_buffer_data[j].buf; + src_data.data_out = &buffer[written_frames]; + src_data.input_frames = ring_buffer_data[j].len / sizeof(float); + src_data.output_frames = frames_to_write; + src_data.end_of_input = 0; + src_data.src_ratio = fRatio; + + res = src_process(fResampler, &src_data); + if (res != 0) + jack_error("JackPortAudioIOAdapter::Render err = %s", src_strerror(res)); + + frames_to_write -= src_data.output_frames_gen; + written_frames += src_data.output_frames_gen; + + jack_log("OUTPUT : j = %d input_frames_used = %ld output_frames_gen = %ld", j, src_data.input_frames_used, src_data.output_frames_gen); + jack_ringbuffer_read_advance(fRingBuffer, src_data.input_frames_used * sizeof(float)); + } + } + + if (written_frames < frames) + jack_error("JackPortAudioIOAdapter::Render error written_frames = %ld", written_frames); + + return written_frames; +} + +int JackResampler::WriteResample(float* buffer, unsigned int frames) +{ + jack_ringbuffer_data_t ring_buffer_data[2]; + SRC_DATA src_data; + unsigned int frames_to_read = frames; + unsigned int read_frames = 0; + int res; + + jack_ringbuffer_get_write_vector(fRingBuffer, ring_buffer_data); + unsigned int available_frames = (ring_buffer_data[0].len + ring_buffer_data[1].len) / sizeof(float); + jack_log("INPUT available = %ld", available_frames); + + for (int j = 0; j < 2; j++) { + + if (ring_buffer_data[j].len > 0) { + + src_data.data_in = &buffer[read_frames]; + src_data.data_out = (float*)ring_buffer_data[j].buf; + src_data.input_frames = frames_to_read; + src_data.output_frames = (ring_buffer_data[j].len / sizeof(float)); + src_data.end_of_input = 0; + src_data.src_ratio = fRatio; + + res = src_process(fResampler, &src_data); + if (res != 0) + jack_error("JackPortAudioIOAdapter::Render err = %s", src_strerror(res)); + + frames_to_read -= src_data.input_frames_used; + read_frames += src_data.input_frames_used; + + jack_log("INPUT : j = %d input_frames_used = %ld output_frames_gen = %ld", j, src_data.input_frames_used, src_data.output_frames_gen); + jack_ringbuffer_write_advance(fRingBuffer, src_data.output_frames_gen * sizeof(float)); + } + } + + if (read_frames < frames) + jack_error("JackPortAudioIOAdapter::Render error read_frames = %ld", read_frames); + + return read_frames; +} + +} diff --git a/common/JackResampler.h b/common/JackResampler.h new file mode 100644 index 00000000..bd2fd077 --- /dev/null +++ b/common/JackResampler.h @@ -0,0 +1,63 @@ +/* +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. + +*/ + +#ifndef __JackResampler__ +#define __JackResampler__ + +#include "ringbuffer.h" +#include "JackError.h" +#include + +namespace Jack +{ + + #define DEFAULT_RB_SIZE 16384 + + class JackResampler + { + + protected: + + SRC_STATE* fResampler; + jack_ringbuffer_t* fRingBuffer; + double fRatio; + + public: + + JackResampler(); + virtual ~JackResampler(); + + int ReadResample(float* buffer, unsigned int frames); + int WriteResample(float* buffer, unsigned int frames); + + int Read(float* buffer, unsigned int frames); + int Write(float* buffer, unsigned int frames); + + void SetRatio(double ratio) + { + fRatio = ratio; + } + double GetRatio() + { + return fRatio; + } + }; +} + +#endif diff --git a/common/wscript b/common/wscript index 178b3e1d..4cef7c30 100644 --- a/common/wscript +++ b/common/wscript @@ -194,9 +194,9 @@ def build(bld): create_jack_process_obj(bld, 'netmanager', 'JackNetManager.cpp', serverlib) - #create_jack_process_obj(bld, 'netioadapter', 'JackNetIOAdapter.cpp ../macosx/JackCoreAudioIOAdapter.cpp', serverlib) + process = create_jack_process_obj(bld, 'netioadapter', 'JackResampler.cpp JackIOAdapter.cpp JackNetIOAdapter.cpp JackCallbackNetIOAdapter.cpp ../macosx/JackCoreAudioIOAdapter.cpp', serverlib) + #process = create_jack_process_obj(bld, 'netioadapter', 'JackResampler.cpp JackIOAdapter.cpp JackNetIOAdapter.cpp JackCallbackNetIOAdapter.cpp ../windows/JackPortAudioIOAdapter.cpp', serverlib) - process = create_jack_process_obj(bld, 'netioadapter', 'JackResampler.cpp JackIOAdapter.cpp JackNetIOAdapter.cpp JackCallbackNetIOAdapter.cpp ../windows/JackPortAudioIOAdapter.cpp', serverlib) process.env.append_value("LINKFLAGS", "-lsamplerate") if bld.env()['IS_MACOSX']: process.env.append_value("LINKFLAGS", "../macosx/libportaudio.a -framework CoreAudio -framework AudioUnit -framework AudioToolbox -framework CoreServices") diff --git a/macosx/JackCoreAudioDriver.cpp b/macosx/JackCoreAudioDriver.cpp index afccf6b7..a3e5e94d 100644 --- a/macosx/JackCoreAudioDriver.cpp +++ b/macosx/JackCoreAudioDriver.cpp @@ -224,7 +224,7 @@ OSStatus JackCoreAudioDriver::SRNotificationCallback(AudioDeviceID inDevice, switch (inPropertyID) { case kAudioDevicePropertyNominalSampleRate: { - jack_log("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate "); + jack_log("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); driver->fState = true; break; } diff --git a/macosx/JackCoreAudioIOAdapter.cpp b/macosx/JackCoreAudioIOAdapter.cpp index e07f596f..eced1e34 100644 --- a/macosx/JackCoreAudioIOAdapter.cpp +++ b/macosx/JackCoreAudioIOAdapter.cpp @@ -19,110 +19,566 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackCoreAudioIOAdapter.h" #include "JackError.h" +#include namespace Jack { + +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 JackCoreAudioIOAdapter::SRNotificationCallback(AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + void* inClientData) +{ + JackCoreAudioIOAdapter* driver = static_cast(inClientData); + + switch (inPropertyID) { + + case kAudioDevicePropertyNominalSampleRate: { + jack_log("JackCoreAudioDriver::SRNotificationCallback kAudioDevicePropertyNominalSampleRate"); + driver->fState = true; + break; + } + } + + return noErr; +} OSStatus JackCoreAudioIOAdapter::Render(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData) + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) { - + JackCoreAudioIOAdapter* adapter = static_cast(inRefCon); + + + jack_log("JackCoreAudioIOAdapter::Render inNumberFrames %ld", inNumberFrames); - JackCoreAudioIOAdapter* driver = static_cast(inRefCon); + AudioUnitRender(adapter->fAUHAL, ioActionFlags, inTimeStamp, 1, inNumberFrames, adapter->fInputData); - /* - driver->fActionFags = ioActionFlags; - driver->fCurrentTime = (AudioTimeStamp *)inTimeStamp; - driver->fDriverOutputData = ioData; - driver->CycleTakeBeginTime(); - return driver->Process(); + adapter->fLastCallbackTime = adapter->fCurCallbackTime; + adapter->fCurCallbackTime = jack_get_time(); + adapter->fConsumerFilter.AddValue(adapter->fCurCallbackTime - adapter->fLastCallbackTime); + adapter->fProducerFilter.AddValue(adapter->fDeltaTime); + + jack_log("JackCoreAudioIOAdapter::Render delta %ld", adapter->fCurCallbackTime - adapter->fLastCallbackTime); + + if (!adapter->fRunning) { + adapter->fRunning = true; + float buffer[inNumberFrames]; + for (int i = 0; i < adapter->fCaptureChannels; i++) { + adapter->fCaptureRingBuffer[i].Read(buffer, inNumberFrames); + adapter->fCaptureRingBuffer[i].Read(buffer, inNumberFrames); + adapter->fCaptureRingBuffer[i].Read(buffer, inNumberFrames); + } + + for (int i = 0; i < adapter->fPlaybackChannels; i++) { + adapter->fPlaybackRingBuffer[i].Write(buffer, inNumberFrames); + adapter->fPlaybackRingBuffer[i].Write(buffer, inNumberFrames); + adapter->fPlaybackRingBuffer[i].Write(buffer, inNumberFrames); + } + } + + jack_time_t val2 = adapter->fConsumerFilter.GetVal(); + jack_time_t val1 = adapter->fProducerFilter.GetVal(); + double src_ratio_output = double(val1) / double(val2); + double src_ratio_input = double(val2) / double(val1); + + if (src_ratio_input < 0.8f || src_ratio_input > 1.2f) + jack_error("src_ratio_input = %f", src_ratio_input); - AudioUnitRender(fAUHAL, fActionFags, fCurrentTime, 1, fEngineControl->fBufferSize, fJackInputData); - */ + if (src_ratio_output < 0.8f || src_ratio_output > 1.2f) + jack_error("src_ratio_output = %f", src_ratio_output); + + src_ratio_input = Range(0.8f, 1.2f, src_ratio_input); + src_ratio_output = Range(0.8f, 1.2f, src_ratio_output); + + jack_log("Callback resampler src_ratio_input = %f src_ratio_output = %f", src_ratio_input, src_ratio_output); + + for (int i = 0; i < adapter->fCaptureChannels; i++) { + adapter->fCaptureRingBuffer[i].SetRatio(src_ratio_input); + //adapter->fCaptureRingBuffer[i].WriteResample((float*)adapter->fInputData->mBuffers[i].mData, inNumberFrames); + adapter->fCaptureRingBuffer[i].Write((float*)adapter->fInputData->mBuffers[i].mData, inNumberFrames); + } - ///AudioUnitRender(fAUHAL, ioActionFlags, inTimeStamp, 1, inNumberFrames, fJackInputData); + for (int i = 0; i < adapter->fPlaybackChannels; i++) { + adapter->fPlaybackRingBuffer[i].SetRatio(src_ratio_output); + //adapter->fPlaybackRingBuffer[i].ReadResample((float*)ioData->mBuffers[i].mData, inNumberFrames); + adapter->fPlaybackRingBuffer[i].Read((float*)ioData->mBuffers[i].mData, inNumberFrames); + } + return noErr; } -OSStatus JackCoreAudioIOAdapter::GetDeviceIDFromUID(const char* UID, AudioDeviceID* id) +OSStatus JackCoreAudioIOAdapter::GetDefaultDevice(AudioDeviceID* id) { - /* - UInt32 size = sizeof(AudioValueTranslation); - CFStringRef inIUD = CFStringCreateWithCString(NULL, UID, CFStringGetSystemEncoding()); - AudioValueTranslation value = { &inIUD, sizeof(CFStringRef), id, sizeof(AudioDeviceID) }; + 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; - if (inIUD == NULL) { - return kAudioHardwareUnspecifiedError; + jack_log("GetDefaultDevice: input = %ld output = %ld", inDefault, outDefault); + + // Get the device only if default input and ouput are the same + if (inDefault == outDefault) { + *id = inDefault; + return noErr; } else { - OSStatus res = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &value); - CFRelease(inIUD); - jack_log("get_device_id_from_uid %s %ld ", UID, *id); - return (*id == kAudioDeviceUnknown) ? kAudioHardwareBadDeviceError : res; + jack_error("Default input and output devices are not the same !!"); + return kAudioHardwareBadDeviceError; } - */ - return noErr; } -OSStatus JackCoreAudioIOAdapter::GetDefaultDevice(AudioDeviceID* id) +OSStatus JackCoreAudioIOAdapter::GetTotalChannels(AudioDeviceID device, int* channelCount, bool isInput) { - return noErr; + OSStatus err = noErr; + UInt32 outSize; + Boolean outWritable; + AudioBufferList* bufferList = 0; + + *channelCount = 0; + err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize, &outWritable); + if (err == noErr) { + bufferList = (AudioBufferList*)malloc(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; + } + + if (bufferList) + free(bufferList); + } + + return err; } -OSStatus JackCoreAudioIOAdapter::GetDefaultInputDevice(AudioDeviceID* id) + +// Setup +int JackCoreAudioIOAdapter::SetupDevices(const char* capture_driver_uid, + const char* playback_driver_uid, + char* capture_driver_name, + char* playback_driver_name) { - return noErr; + if (GetDefaultDevice(&fDeviceID) != noErr) { + jack_error("Cannot open default device"); + return -1; + } + + return 0; } -OSStatus JackCoreAudioIOAdapter::GetDefaultOutputDevice(AudioDeviceID* id) + +int JackCoreAudioIOAdapter::SetupChannels(bool capturing, + bool playing, + int& inchannels, + int& outchannels, + int& in_nChannels, + int& out_nChannels, + bool strict) { - return noErr; + OSStatus err = noErr; + + err = GetTotalChannels(fDeviceID, &in_nChannels, true); + if (err != noErr) { + jack_error("Cannot get input channel number"); + printError(err); + return -1; + } + + err = GetTotalChannels(fDeviceID, &out_nChannels, false); + if (err != noErr) { + jack_error("Cannot get output channel number"); + printError(err); + return -1; + } + + return 0; } -OSStatus JackCoreAudioIOAdapter::GetDeviceNameFromID(AudioDeviceID id, char* name) + +int JackCoreAudioIOAdapter::SetupBufferSizeAndSampleRate(jack_nframes_t nframes, jack_nframes_t samplerate) { - return noErr; + OSStatus err = noErr; + UInt32 outSize; + Float64 sampleRate; + + // Setting buffer size + outSize = sizeof(UInt32); + err = AudioDeviceSetProperty(fDeviceID, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, outSize, &nframes); + if (err != noErr) { + jack_error("Cannot set buffer size %ld", nframes); + printError(err); + return -1; + } + + // Get sample rate + outSize = sizeof(Float64); + err = AudioDeviceGetProperty(fDeviceID, 0, kAudioDeviceSectionGlobal, kAudioDevicePropertyNominalSampleRate, &outSize, &sampleRate); + if (err != noErr) { + jack_error("Cannot get current sample rate"); + printError(err); + return -1; + } + + // If needed, set new sample rate + if (samplerate != (jack_nframes_t)sampleRate) { + sampleRate = (Float64)samplerate; + + // To get SR change notification + err = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback, this); + if (err != noErr) { + jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDevicePropertyNominalSampleRate"); + printError(err); + return -1; + } + err = AudioDeviceSetProperty(fDeviceID, 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++ < 100) { + usleep(100000); + jack_log("Wait count = %ld", count); + } + + // Remove SR change notification + AudioDeviceRemovePropertyListener(fDeviceID, 0, true, kAudioDevicePropertyNominalSampleRate, SRNotificationCallback); + } + + return 0; } -OSStatus JackCoreAudioIOAdapter::GetTotalChannels(AudioDeviceID device, int* channelCount, bool isInput) + +int JackCoreAudioIOAdapter::SetupBuffers(int inchannels, int outchannels) { - return noErr; + jack_log("JackCoreAudioIOAdapter::SetupBuffers: input = %ld output = %ld", inchannels, outchannels); + + // Prepare buffers + fInputData = (AudioBufferList*)malloc(sizeof(UInt32) + inchannels * sizeof(AudioBuffer)); + if (fInputData == 0) { + jack_error("Cannot allocate memory for input buffers"); + return -1; + } + fInputData->mNumberBuffers = inchannels; + for (int i = 0; i < fCaptureChannels; i++) { + fInputData->mBuffers[i].mNumberChannels = 1; + fInputData->mBuffers[i].mDataByteSize = fBufferSize * sizeof(float); + fInputData->mBuffers[i].mData = malloc(fBufferSize * sizeof(float)); + } + return 0; } -// Setup -int JackCoreAudioIOAdapter::SetupDevices(const char* capture_driver_uid, - const char* playback_driver_uid, - char* capture_driver_name, - char* playback_driver_name) +void JackCoreAudioIOAdapter::DisposeBuffers() { - return 0; + if (fInputData) { + for (int i = 0; i < fCaptureChannels; i++) + free(fInputData->mBuffers[i].mData); + free(fInputData); + fInputData = 0; + } } -int JackCoreAudioIOAdapter::SetupChannels(bool capturing, - bool playing, - int& inchannels, - int& outchannels, - int& in_nChannels, - int& out_nChannels, - bool strict) +int JackCoreAudioIOAdapter::OpenAUHAL(bool capturing, + bool playing, + int inchannels, + int outchannels, + int in_nChannels, + int out_nChannels, + jack_nframes_t nframes, + jack_nframes_t samplerate, + bool strict) { + ComponentResult err1; + UInt32 enableIO; + AudioStreamBasicDescription srcFormat, dstFormat; + + jack_log("OpenAUHAL capturing = %ld playing = %ld playing = %ld outchannels = %ld in_nChannels = %ld out_nChannels = %ld ", capturing, playing, inchannels, inchannels, in_nChannels, out_nChannels); + + // AUHAL + 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); + return -1; + } + + err1 = AudioUnitInitialize(fAUHAL); + if (err1 != noErr) { + jack_error("Cannot initialize AUHAL unit"); + printError(err1); + return -1; + } + + // Start I/O + enableIO = 1; + if (capturing && inchannels > 0) { + jack_log("Setup AUHAL input"); + 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); + if (strict) + return -1; + } + } + + if (playing && outchannels > 0) { + jack_log("Setup AUHAL output"); + 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); + if (strict) + return -1; + } + } + + // 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); + if (strict) + return -1; + } + + // Set buffer size + if (capturing && inchannels > 0) { + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*) & nframes, sizeof(UInt32)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); + printError(err1); + if (strict) + return -1; + } + } + + if (playing && outchannels > 0) { + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*) & nframes, sizeof(UInt32)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice"); + printError(err1); + if (strict) + return -1; + } + } + + // 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); + } + } + + 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); + } + } + + // Setup stream converters + jack_log("Setup AUHAL input stream converter SR = %ld", samplerate); + srcFormat.mSampleRate = samplerate; + srcFormat.mFormatID = kAudioFormatLinearPCM; + srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; + srcFormat.mBytesPerPacket = sizeof(float); + srcFormat.mFramesPerPacket = 1; + srcFormat.mBytesPerFrame = sizeof(float); + srcFormat.mChannelsPerFrame = outchannels; + srcFormat.mBitsPerChannel = 32; + + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &srcFormat, sizeof(AudioStreamBasicDescription)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input"); + printError(err1); + } + + jack_log("Setup AUHAL output stream converter SR = %ld", samplerate); + dstFormat.mSampleRate = samplerate; + dstFormat.mFormatID = kAudioFormatLinearPCM; + dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; + dstFormat.mBytesPerPacket = sizeof(float); + dstFormat.mFramesPerPacket = 1; + dstFormat.mBytesPerFrame = sizeof(float); + dstFormat.mChannelsPerFrame = inchannels; + dstFormat.mBitsPerChannel = 32; + + err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &dstFormat, sizeof(AudioStreamBasicDescription)); + if (err1 != noErr) { + jack_error("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output"); + printError(err1); + } + + // 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); + return -1; + } + } 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); + return -1; + } + } + return 0; } - -int JackCoreAudioIOAdapter::SetupBufferSizeAndSampleRate(jack_nframes_t nframes, jack_nframes_t samplerate) +void JackCoreAudioIOAdapter::CloseAUHAL() { - return 0; + AudioUnitUninitialize(fAUHAL); + CloseComponent(fAUHAL); } int JackCoreAudioIOAdapter::Open() { + OSStatus err; + int in_nChannels = 0; + int out_nChannels = 0; + + if (SetupDevices("", "", "", "") < 0) + return -1; + + if (SetupChannels(true, true, fCaptureChannels, fPlaybackChannels, in_nChannels, out_nChannels, true) < 0) + return -1; + if (SetupBufferSizeAndSampleRate(fBufferSize, fSampleRate) < 0) + return -1; + + if (OpenAUHAL(true, true, fCaptureChannels, fPlaybackChannels, in_nChannels, out_nChannels, fBufferSize, fSampleRate, true) < 0) + goto error; + + if (SetupBuffers(fCaptureChannels, fPlaybackChannels) < 0) + goto error; + + err = AudioOutputUnitStart(fAUHAL); + if (err != noErr) + goto error; + + return noErr; + +error: + Close(); + return -1; } int JackCoreAudioIOAdapter::Close() { - + AudioOutputUnitStop(fAUHAL); + DisposeBuffers(); + CloseAUHAL(); + return 0; } diff --git a/macosx/JackCoreAudioIOAdapter.h b/macosx/JackCoreAudioIOAdapter.h index ed47e9dc..a821ce70 100644 --- a/macosx/JackCoreAudioIOAdapter.h +++ b/macosx/JackCoreAudioIOAdapter.h @@ -21,6 +21,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #define __JackCoreAudioIOAdapter__ #include "JackIOAdapter.h" +#include "JackFilters.h" #include "jack.h" #include #include @@ -29,12 +30,11 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. namespace Jack { -typedef UInt8 CAAudioHardwareDeviceSectionID; -#define kAudioDeviceSectionInput ((CAAudioHardwareDeviceSectionID)0x01) -#define kAudioDeviceSectionOutput ((CAAudioHardwareDeviceSectionID)0x00) -#define kAudioDeviceSectionGlobal ((CAAudioHardwareDeviceSectionID)0x00) -#define kAudioDeviceSectionWildcard ((CAAudioHardwareDeviceSectionID)0xFF) - + typedef UInt8 CAAudioHardwareDeviceSectionID; + #define kAudioDeviceSectionInput ((CAAudioHardwareDeviceSectionID)0x01) + #define kAudioDeviceSectionOutput ((CAAudioHardwareDeviceSectionID)0x00) + #define kAudioDeviceSectionGlobal ((CAAudioHardwareDeviceSectionID)0x00) + #define kAudioDeviceSectionWildcard ((CAAudioHardwareDeviceSectionID)0xFF) class JackCoreAudioIOAdapter : public JackIOAdapterInterface { @@ -42,24 +42,31 @@ typedef UInt8 CAAudioHardwareDeviceSectionID; private: AudioUnit fAUHAL; + AudioBufferList* fInputData; + + JackFilter fProducerFilter; + JackFilter fConsumerFilter; AudioDeviceID fDeviceID; + bool fState; AudioUnitRenderActionFlags* fActionFags; AudioTimeStamp* fCurrentTime; static OSStatus Render(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData); + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData); + + static OSStatus SRNotificationCallback(AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + void* inClientData); - OSStatus GetDeviceIDFromUID(const char* UID, AudioDeviceID* id); OSStatus GetDefaultDevice(AudioDeviceID* id); - OSStatus GetDefaultInputDevice(AudioDeviceID* id); - OSStatus GetDefaultOutputDevice(AudioDeviceID* id); - OSStatus GetDeviceNameFromID(AudioDeviceID id, char* name); OSStatus GetTotalChannels(AudioDeviceID device, int* channelCount, bool isInput); // Setup @@ -75,14 +82,26 @@ typedef UInt8 CAAudioHardwareDeviceSectionID; int& in_nChannels, int& out_nChannels, bool strict); - + + int OpenAUHAL(bool capturing, + bool playing, + int inchannels, + int outchannels, + int in_nChannels, + int out_nChannels, + jack_nframes_t nframes, + jack_nframes_t samplerate, + bool strict); int SetupBufferSizeAndSampleRate(jack_nframes_t nframes, jack_nframes_t samplerate); + int SetupBuffers(int inchannels, int outchannels); + void DisposeBuffers(); + void CloseAUHAL(); public: JackCoreAudioIOAdapter(int input, int output, int buffer_size, float sample_rate) - :JackIOAdapterInterface(input, output, buffer_size, sample_rate) + :JackIOAdapterInterface(input, output, buffer_size, sample_rate),fInputData(0),fState(false) {} ~JackCoreAudioIOAdapter() {} diff --git a/windows/JackPortAudioIOAdapter.cpp b/windows/JackPortAudioIOAdapter.cpp index c8daa4e5..805b61df 100644 --- a/windows/JackPortAudioIOAdapter.cpp +++ b/windows/JackPortAudioIOAdapter.cpp @@ -49,24 +49,20 @@ int JackPortAudioIOAdapter::Render(const void* inputBuffer, void* outputBuffer, if (!adapter->fRunning) { adapter->fRunning = true; - - paBuffer = (float**)inputBuffer; + float buffer[framesPerBuffer]; for (int i = 0; i < adapter->fCaptureChannels; i++) { - buffer = static_cast(paBuffer[i]); - adapter->fCaptureRingBuffer[i].Read(buffer, framesPerBuffer); - adapter->fCaptureRingBuffer[i].Read(buffer, framesPerBuffer); - adapter->fCaptureRingBuffer[i].Read(buffer, framesPerBuffer); + adapter->fCaptureRingBuffer[i].Read(buffer, inNumberFrames); + adapter->fCaptureRingBuffer[i].Read(buffer, inNumberFrames); + adapter->fCaptureRingBuffer[i].Read(buffer, inNumberFrames); } - paBuffer = (float**)outputBuffer; for (int i = 0; i < adapter->fPlaybackChannels; i++) { - buffer = static_cast(paBuffer[i]); - adapter->fPlaybackRingBuffer[i].Write(buffer, framesPerBuffer); - adapter->fPlaybackRingBuffer[i].Write(buffer, framesPerBuffer); - adapter->fPlaybackRingBuffer[i].Write(buffer, framesPerBuffer); + adapter->fPlaybackRingBuffer[i].Write(buffer, inNumberFrames); + adapter->fPlaybackRingBuffer[i].Write(buffer, inNumberFrames); + adapter->fPlaybackRingBuffer[i].Write(buffer, inNumberFrames); } - } - + } + /* double src_ratio_output = double(adapter->fCurCallbackTime - adapter->fLastCallbackTime) / double(adapter->fDeltaTime); double src_ratio_input = double(adapter->fDeltaTime) / double(adapter->fCurCallbackTime - adapter->fLastCallbackTime); @@ -91,16 +87,16 @@ int JackPortAudioIOAdapter::Render(const void* inputBuffer, void* outputBuffer, for (int i = 0; i < adapter->fCaptureChannels; i++) { buffer = (float*)paBuffer[i]; adapter->fCaptureRingBuffer[i].SetRatio(src_ratio_input); - //int len = adapter->fCaptureRingBuffer[i].WriteResample(buffer, framesPerBuffer); - int len = adapter->fCaptureRingBuffer[i].Write(buffer, framesPerBuffer); + //adapter->fCaptureRingBuffer[i].WriteResample(buffer, framesPerBuffer); + adapter->fCaptureRingBuffer[i].Write(buffer, framesPerBuffer); } paBuffer = (float**)outputBuffer; for (int i = 0; i < adapter->fPlaybackChannels; i++) { buffer = (float*)paBuffer[i]; adapter->fPlaybackRingBuffer[i].SetRatio(src_ratio_output); - //int len = adapter->fPlaybackRingBuffer[i].ReadResample(buffer, framesPerBuffer); - int len = adapter->fPlaybackRingBuffer[i].Read(buffer, framesPerBuffer); + //adapter->fPlaybackRingBuffer[i].ReadResample(buffer, framesPerBuffer); + adapter->fPlaybackRingBuffer[i].Read(buffer, framesPerBuffer); } return paContinue; diff --git a/windows/JackPortAudioIOAdapter.h b/windows/JackPortAudioIOAdapter.h index 5cd3fdea..b552ce25 100644 --- a/windows/JackPortAudioIOAdapter.h +++ b/windows/JackPortAudioIOAdapter.h @@ -21,40 +21,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #define __JackPortAudioIOAdapter__ #include "JackIOAdapter.h" +#include "JackFilters.h" #include "portaudio.h" namespace Jack { - #define MAX_SIZE 64 - - struct Filter { - - jack_time_t fTable[MAX_SIZE]; - - Filter() - { - for (int i = 0; i < MAX_SIZE; i++) - fTable[i] = 0; - } - - void AddValue(jack_time_t val) - { - memcpy(&fTable[1], &fTable[0], sizeof(jack_time_t) * (MAX_SIZE - 1)); - fTable[0] = val; - } - - jack_time_t GetVal() - { - jack_time_t mean = 0; - for (int i = 0; i < MAX_SIZE; i++) - mean += fTable[i]; - - return mean / MAX_SIZE; - } - }; - - class JackPortAudioIOAdapter : public JackIOAdapterInterface + class JackPortAudioIOAdapter : public JackIOAdapterInterface { private: @@ -63,8 +36,8 @@ namespace Jack PaDeviceIndex fInputDevice; PaDeviceIndex fOutputDevice; - Filter fProducerFilter; - Filter fConsumerFilter; + JackFilter fProducerFilter; + JackFilter fConsumerFilter; static int Render(const void* inputBuffer, void* outputBuffer, unsigned long framesPerBuffer,