From 63c68d36597b6637f313040dbaeb034efda3d747 Mon Sep 17 00:00:00 2001 From: "rafael@riseup.net" Date: Wed, 11 Nov 2009 16:02:38 +0000 Subject: [PATCH] initial coreaudio patch --- darkice/branches/darkice-macosx/configure.in | 14 + .../darkice-macosx/src/AudioSource.cpp | 6 + .../branches/darkice-macosx/src/AudioSource.h | 10 + .../darkice-macosx/src/CoreAudioDspSource.cpp | 807 ++++++++++++++++++ .../darkice-macosx/src/CoreAudioDspSource.h | 271 ++++++ .../branches/darkice-macosx/src/DarkIce.cpp | 2 +- .../branches/darkice-macosx/src/Makefile.am | 4 +- 7 files changed, 1112 insertions(+), 2 deletions(-) create mode 100644 darkice/branches/darkice-macosx/src/CoreAudioDspSource.cpp create mode 100644 darkice/branches/darkice-macosx/src/CoreAudioDspSource.h diff --git a/darkice/branches/darkice-macosx/configure.in b/darkice/branches/darkice-macosx/configure.in index 2a2d652..f0ce058 100644 --- a/darkice/branches/darkice-macosx/configure.in +++ b/darkice/branches/darkice-macosx/configure.in @@ -301,6 +301,20 @@ else fi +dnl----------------------------------------------------------------------------- +dnl link CoreAudio framework +dnl----------------------------------------------------------------------------- +AC_SUBST(COREAUDIO_LDFLAGS) + +# Look for Core flag +AC_ARG_WITH(core, [ --with-core = choose CoreAudio API support (mac only)], [ + AC_MSG_RESULT(using CoreAudio) + AC_CHECK_HEADER(CoreAudio/CoreAudio.h, [], [AC_MSG_ERROR(CoreAudio header files not found!)] ) + COREAUDIO_LDFLAGS="-framework CoreAudio -framework CoreFoundation" + AC_DEFINE( HAVE_COREAUDIO_LIB, 1, [build with CoreAudio support] ) ], ) + + + dnl----------------------------------------------------------------------------- dnl check for MSG_NOSIGNAL for the send() function in libsocket dnl----------------------------------------------------------------------------- diff --git a/darkice/branches/darkice-macosx/src/AudioSource.cpp b/darkice/branches/darkice-macosx/src/AudioSource.cpp index c5a8cfa..58c26d1 100644 --- a/darkice/branches/darkice-macosx/src/AudioSource.cpp +++ b/darkice/branches/darkice-macosx/src/AudioSource.cpp @@ -118,6 +118,12 @@ AudioSource :: createDspSource( const char * deviceName, sampleRate, bitsPerSample, channel); +#elif defined( SUPPORT_COREAUDIO_DSP ) + Reporter::reportEvent( 1, "Using CoreAudio as input device."); + return new CoreAudioDspSource( deviceName, + sampleRate, + bitsPerSample, + channel); #else throw Exception( __FILE__, __LINE__, "trying to open ALSA DSP device without " diff --git a/darkice/branches/darkice-macosx/src/AudioSource.h b/darkice/branches/darkice-macosx/src/AudioSource.h index 5f464f0..5be8e1c 100644 --- a/darkice/branches/darkice-macosx/src/AudioSource.h +++ b/darkice/branches/darkice-macosx/src/AudioSource.h @@ -71,6 +71,11 @@ #define SUPPORT_JACK_DSP 1 #endif +#if defined( HAVE_COREAUDIO_LIB ) +// we have CoreAudio framework +#define SUPPORT_COREAUDIO_DSP 1 +#endif + #if defined ( HAVE_TERMIOS_H ) #define SUPPORT_SERIAL_ULAW 1 #endif @@ -78,6 +83,7 @@ #if !defined( SUPPORT_ALSA_DSP ) \ && !defined( SUPPORT_OSS_DSP ) \ && !defined( SUPPORT_JACK_DSP ) \ + && !defined( SUPPORT_COREAUDIO_DSP ) \ && !defined( SUPPORT_SOLARIS_DSP ) \ && !defined( SUPPORT_SERIAL_ULAW) // there was no DSP audio system found @@ -301,6 +307,10 @@ class AudioSource : public Source, public virtual Reporter #include "JackDspSource.h" #endif +#if defined( SUPPORT_COREAUDIO_DSP ) +#include "CoreAudioDspSource.h" +#endif + #if defined ( SUPPORT_SERIAL_ULAW ) #include "SerialUlaw.h" #endif diff --git a/darkice/branches/darkice-macosx/src/CoreAudioDspSource.cpp b/darkice/branches/darkice-macosx/src/CoreAudioDspSource.cpp new file mode 100644 index 0000000..f2bc0eb --- /dev/null +++ b/darkice/branches/darkice-macosx/src/CoreAudioDspSource.cpp @@ -0,0 +1,807 @@ +#include "AudioSource.h" + +#ifdef SUPPORT_COREAUDIO_DSP +// only compile this code if there is support for it + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#else +#error need unistd.h +#endif + +#ifdef HAVE_STRING_H +#include +#else +#error need string.h +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#else +#error need sys/types.h +#endif + +#ifdef HAVE_MATH_H +#include +#else +#error need math.h +#endif + +#include +#include + +#include "Util.h" +#include "Exception.h" +#include "CoreAudioDspSource.h" + + +/* =================================================== local data structures */ + + +/* ================================================ local constants & macros */ + + +/*------------------------------------------------------------------------------ + * File identity + *----------------------------------------------------------------------------*/ +static const char fileid[] = "$Id: CoreAudioDspSource.cpp $"; + + +/* =============================================== local function prototypes */ +static unsigned int get_device_count(void); +static const char* get_error_name( OSStatus code ); + +/* ============================================================= module code */ + +/*------------------------------------------------------------------------------ + * Tell if source id big endian + *----------------------------------------------------------------------------*/ +bool +CoreAudioDspSource :: isBigEndian ( void ) const throw () +{ + //According to AudioHardware.h, the stream data will + // always be presented in native-endian format + CFByteOrder order = CFByteOrderGetCurrent(); + return (CFByteOrderBigEndian == order); +} + + +/*------------------------------------------------------------------------------ + * Initialize the object + *----------------------------------------------------------------------------*/ +void +CoreAudioDspSource :: init ( const char* name ) throw ( Exception ) +{ + // Set defaults + rb[0] = NULL; // Left Ring Buffer + rb[1] = NULL; // Right Ring Buffer + tmp_buffer = NULL; // Buffer big enough for one 'read' of audio + cnv_buffer = NULL; // For converting. + is_running = false; // + is_opened = false; + + // Check the sample size + if ( +#if 0 + // don't work. + getBitsPerSample() != 8 && +#endif + getBitsPerSample() != 16) { + throw Exception( __FILE__, __LINE__, + "CoreAudioDspSource doesn't support non 16-bit samples"); + } + Reporter::reportEvent( 10, "CoreAudioDspSource :: init():bitsPerSample ", getBitsPerSample()); + + unsigned int dev_count = get_device_count(); + if (dev_count == 0) { + throw Exception( __FILE__, __LINE__, "no input device found"); + } + + AudioDeviceID id; + UInt32 data_size = sizeof(AudioDeviceID); + OSStatus result = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, + &data_size, &id); + if (result != noErr) { + throw Exception( __FILE__, __LINE__, + "faild to get DefaultInputDevice:", get_error_name(result)); + } + + // Use default device ? + if ( Util::strEq( name, "default", 7) ) { + device_id = id; + char dev_name[256]; + data_size = sizeof(dev_name); + result = AudioDeviceGetProperty(device_id, 0, true, + kAudioDevicePropertyDeviceName, &data_size, &dev_name[0]); + if (result != noErr) { + throw Exception( __FILE__, __LINE__, + "faild to get device name:", get_error_name(result)); + } + dev_name[sizeof(dev_name) - 1] = '\0'; + Reporter::reportEvent( 1, "CoreAudioDspSource :: init():name ", &dev_name[0]); + return; + } + + data_size *= dev_count; + AudioDeviceID *device_list = (AudioDeviceID *)malloc(dev_count * sizeof(AudioDeviceID)); + if (!device_list) { + throw Exception( __FILE__, __LINE__, "faild to allocate memory for device_list"); + } + result = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, + &data_size, device_list); + if (result != noErr) { + free(device_list); + device_list = NULL; + throw Exception( __FILE__, __LINE__, "faild to get Devices:", get_error_name(result)); + } + + bool dev_found = false; + for (unsigned int i = 0; i < dev_count; ++i) { + id = device_list[i]; + char dev_name[256]; + data_size = sizeof(dev_name); + result = AudioDeviceGetProperty(id, 0, false, + kAudioDevicePropertyDeviceName, + &data_size, &dev_name[0]); + if (result != noErr) { + free(device_list); + device_list = NULL; + throw Exception( __FILE__, __LINE__, "faild to get Device name:", get_error_name(result)); + } + + if (strcmp(&dev_name[0], name) == 0) { + device_id = id; + dev_found = true; + + char dev_name[256]; + data_size = sizeof(dev_name); + result = AudioDeviceGetProperty(device_id, 0, true, + kAudioDevicePropertyDeviceName, &data_size, &dev_name[0]); + if (result != noErr) { + throw Exception( __FILE__, __LINE__, + "faild to get device name:", get_error_name(result)); + } + dev_name[sizeof(dev_name) - 1] = '\0'; + Reporter::reportEvent( 1, "CoreAudioDspSource :: init():name ", &dev_name[0]); + break; + } + } + free(device_list); + device_list = NULL; + + if (!dev_found) { + throw Exception( __FILE__, __LINE__, "device is not found."); + } +} + + + +/*------------------------------------------------------------------------------ + * De-initialize the object + *----------------------------------------------------------------------------*/ +void +CoreAudioDspSource :: strip ( void ) throw ( Exception ) +{ + if ( isOpen() ) { + close(); + } + + // Free the temporary buffer + if (tmp_buffer) { + free(tmp_buffer); + tmp_buffer = NULL; + } + if (cnv_buffer) { + free(cnv_buffer); + cnv_buffer = NULL; + } + +} + +/*------------------------------------------------------------------------------ + * Open the audio source + *----------------------------------------------------------------------------*/ +bool +CoreAudioDspSource :: open ( void ) throw ( Exception ) +{ + size_t rb_size = 0; + unsigned int c = 0;; + + if ( isOpen() ) { + return false; + } + + // Create a ring buffer for each channel + rb_size = 8 * getSampleRate() * sizeof(Float32); + for (c = 0; c < getChannel(); c++) { + rb[c] = jack_ringbuffer_create(rb_size); + if (!rb[c]) { + throw Exception( __FILE__, __LINE__, + "Failed to create ringbuffer for channel ", c); + } + } + + OSStatus result = noErr; + UInt32 data_size = 0;; + + // Set BufferFrameSize + UInt32 buffer_size = 512; + data_size = sizeof(UInt32); + result = AudioDeviceSetProperty(device_id, NULL, 0, true, + kAudioDevicePropertyBufferFrameSize, data_size, &buffer_size); + if (result != noErr) { + throw Exception( __FILE__, __LINE__, + "Can't set buffer frame size:", get_error_name(result)); + } + + data_size = sizeof(UInt32); + result = AudioDeviceGetProperty(device_id, 0, true, + kAudioDevicePropertyBufferFrameSize, &data_size, &buffer_size); + if (result != noErr) { + throw Exception( __FILE__, __LINE__, + "Can't get buffer frame size:", get_error_name(result)); + } + Reporter::reportEvent( 10, "CoreAudioDspSource :: open():buffer frame size: ", buffer_size); + + // Get the stream configration. + result = AudioDeviceGetPropertyInfo(device_id, 0, true, + kAudioDevicePropertyStreamConfiguration, + &data_size, NULL); + if (data_size == 0) { + throw Exception( __FILE__, __LINE__, + "Can't get stream configuration ", "data_size is zero"); + } + if (result != noErr) { + throw Exception( __FILE__, __LINE__, + "Can't get stream configuration ", get_error_name(result)); + } + + // Allocate buffer + AudioBufferList *buffer_list = (AudioBufferList *)malloc(data_size); + if (!buffer_list) { + throw Exception( __FILE__, __LINE__, "Can't allocate buffer ", get_error_name(result)); + } + + result = AudioDeviceGetProperty(device_id, 0, true, + kAudioDevicePropertyStreamConfiguration, + &data_size, buffer_list); + + if (result != noErr) { + free(buffer_list); + buffer_list = NULL; + throw Exception( __FILE__, __LINE__, + "Can't get stream configuration ", get_error_name(result)); + } + + UInt32 dev_channels = 0; + for (UInt32 i = 0; i < buffer_list->mNumberBuffers; i++) { + dev_channels += buffer_list->mBuffers[i].mNumberChannels; + } + if (getChannel() > dev_channels) { + free(buffer_list); + buffer_list = NULL; + throw Exception( __FILE__, __LINE__, + "Invalid number of channels", getChannel()); + } + + free(buffer_list); + buffer_list = NULL; + + // set stream format. + Float64 sample_rate = getSampleRate(); + data_size = sizeof(sample_rate); + result = AudioDeviceSetProperty(device_id, 0, true, 0, + kAudioDevicePropertyNominalSampleRate, data_size, &sample_rate); + if (result != noErr) { + throw Exception( __FILE__, __LINE__, "Can't set stream format:", get_error_name(result)); + } + + AudioStreamBasicDescription description; + data_size = sizeof(description); + result = AudioDeviceGetProperty(device_id, 0, true, + kAudioDevicePropertyStreamFormat, &data_size, &description); + if (result != noErr) { + throw Exception( __FILE__, __LINE__, "Can't get stream format:", get_error_name(result)); + } +#if 0 + fprintf(stderr, "stream format:sr=%f\nfid='%c%c%c%c'\nflags=0x%x\ncpf=%u\nbpf=%u\nbpp=%u\n", + description.mSampleRate, + (description.mFormatID >> 24) & 0x00ff, (description.mFormatID >> 16) & 0x00ff, + (description.mFormatID >> 8) & 0x00ff, description.mFormatID & 0x00ff, + description.mFormatFlags, + description.mChannelsPerFrame, + description.mBytesPerFrame, + description.mBytesPerPacket); +#endif + if (description.mChannelsPerFrame != 1 && description.mChannelsPerFrame != 2) { + throw Exception( __FILE__, __LINE__, "not supported device channels ", description.mChannelsPerFrame); + } + stream_description = description; + is_opened = true; + return true; +} + + +/*------------------------------------------------------------------------------ + * Check wether read() would return anything + *----------------------------------------------------------------------------*/ +bool +CoreAudioDspSource :: canRead ( unsigned int sec, + unsigned int usec ) throw ( Exception ) +{ + const unsigned int max_wait_time = sec * 1000000; + const unsigned int wait_increment = 10000; + unsigned int cur_wait = 0; + + if ( !isOpen() ) { + return false; + } + + if (!is_running) { + startDevice(); + } + + while (max_wait_time > cur_wait) { + bool canRead = true; + + for (unsigned int c = 0 ; c < getChannel() ; c++) { + if (jack_ringbuffer_read_space(rb[c]) <= 0) { + canRead = false; + } + } + + if (canRead) { + return true; + } + + cur_wait += wait_increment; + usleep ( wait_increment ); + } + + usleep( usec ); + for (unsigned int c = 0 ; c < getChannel() ; c++) { + if (jack_ringbuffer_read_space(rb[c]) <= 0) { + return false; + } + } + + return true; +} + + +/*------------------------------------------------------------------------------ + * Read from the audio source + *----------------------------------------------------------------------------*/ +unsigned int +CoreAudioDspSource :: read ( void * buf, + unsigned int len ) throw ( Exception ) +{ + unsigned int samples = len / (getBitsPerSample() >> 3) / getChannel(); + unsigned int samples_read[2] = {0,0}; + unsigned int c, n; + + if ( !isOpen() ) { + return 0; + } + + if (!is_running) { + startDevice(); + } + + // Ensure the temporary buffer is big enough + tmp_buffer = (char *)realloc(tmp_buffer, samples * sizeof(Float32)); + if (!tmp_buffer) { + throw Exception( __FILE__, __LINE__, "realloc on tmp_buffer failed"); + } + + // We must be sure to fetch as many data on both channels + int minBytesAvailable = samples * sizeof(Float32); + + for (c = 0; c < getChannel(); c++) { + int readable = jack_ringbuffer_read_space(rb[c]); + if (readable < minBytesAvailable) { + minBytesAvailable = readable; + } + } + + for (c = 0; c < getChannel(); c++) { + // Copy frames from ring buffer to temporary buffer + // and then convert samples to output buffer + int bytes_read = jack_ringbuffer_read(rb[c], + (char*)tmp_buffer, + minBytesAvailable); + samples_read[c] = bytes_read / sizeof(Float32); + + // Convert samples from float to char/short and put in output buffer + if (getBitsPerSample() == 8) { + char *output8 = (char*)buf; + Float32 *in = (Float32 *)tmp_buffer; + for (n = 0; n < samples_read[c]; n++) { + int tmp = lrintf(in[n] * 128.0f); + if (tmp > CHAR_MAX) { + output8[n * getChannel() + c] = CHAR_MAX; + } else if (tmp < CHAR_MIN) { + output8[n * getChannel() + c] = CHAR_MIN; + } else { + output8[n * getChannel() + c] = (char)tmp; + } + } + + } else { // 16 + short *output16 = (short*)buf; + Float32 *in = (Float32 *)tmp_buffer; + for (n = 0; n < samples_read[c]; n++) { + int tmp = lrintf(in[n] * 32768.0f); + if (tmp > SHRT_MAX) { + output16[n * getChannel() + c] = SHRT_MAX; + } else if (tmp < SHRT_MIN) { + output16[n * getChannel() + c] = SHRT_MIN; + } else { + output16[n * getChannel() + c] = (short)tmp; + } + } + } + } + + // Didn't get as many samples as we wanted ? + if (getChannel() == 2 && samples_read[0] != samples_read[1]) { + Reporter::reportEvent( 2, + "Warning: Read a different number of samples " + "for left and right channels"); + } + + // Return the number of bytes put in the output buffer + return samples_read[0] * (getBitsPerSample() >> 3) * getChannel(); +} + + +/*------------------------------------------------------------------------------ + * Close the audio source + *----------------------------------------------------------------------------*/ +void +CoreAudioDspSource :: close ( void ) throw ( Exception ) +{ + unsigned int i; + + if ( !isOpen() ) { + return; + } + + if (is_running) { + stopDevice(); + } + + for (i = 0; i < getChannel(); i++) { + if (rb[i]) { + jack_ringbuffer_free(rb[i]); + rb[i] = NULL; + } + } + is_opened = false; +} + + +/*------------------------------------------------------------------------------ + * Callback called by CoreAudio when audio is available + * + * Don't do anything too expensive here + * - just shove audio samples in ring buffer + *----------------------------------------------------------------------------*/ +OSStatus +CoreAudioDspSource :: callback_handler( AudioDeviceID inDevice, + const AudioTimeStamp *inNow, + const AudioBufferList *inInputData, + const AudioTimeStamp *inInputTime, + AudioBufferList *outOutputData, + const AudioTimeStamp *inOutputTime, + void *infoPointer ) +{ + CoreAudioDspSource *self = (CoreAudioDspSource *)infoPointer; + bool err = false; + + if (self->stream_description.mFormatFlags & kAudioFormatFlagIsNonInterleaved) { +#if 1 + Reporter::reportEvent( 1, "not supported."); + err = true; +#else + unsigned int c; + //XXX not tested. + /* copy data to ringbuffer; one per channel */ + if ((self->getChannel() == 2 && self->stream_description.mChannelsPerFrame == 2) + || (self->getChannel() == 1 && self->stream_description.mChannelsPerFrame == 1)) { + for (c = 0; c < self->getChannel(); c++) { + char *buf = (char*)inInputData->mBuffers[c].mData; + size_t to_write = inInputData->mBuffers[c].mDataByteSize; + size_t len = jack_ringbuffer_write(self->rb[c], buf, to_write); + if (len < to_write) { + Reporter::reportEvent( 1, "failed to write to ring buffer"); + err = true; + } + } + } else if (self->getChannel() == 1 && self->stream_description.mChannelsPerFrame == 2) { + Float32 *dataL = (Float32 *)inInputData->mBuffers[0].mData; + Float32 *dataR = (Float32 *)inInputData->mBuffers[1].mData; + size_t to_write = inInputData->mBuffers[0].mDataByteSize; + size_t sz = to_write / sizeof(Float32); + for (unsigned i = 0; i < sz; i++) { + dataL[i] = (dataL[i] + dataR[i]) / 2.0; + } + size_t len = jack_ringbuffer_write(self->rb[0], (char *)dataL, to_write); + if (len < to_write) { + Reporter::reportEvent( 1, "failed to write to ring buffer"); + err = true; + } + + } else if (self->getChannel() == 2 && self->stream_description.mChannelsPerFrame == 1) { + for (c = 0; c < self->getChannel(); c++) { + char *buf = (char*)inInputData->mBuffers[0].mData; + size_t to_write = inInputData->mBuffers[0].mDataByteSize; + size_t len = jack_ringbuffer_write(self->rb[c], buf, to_write); + if (len < to_write) { + Reporter::reportEvent( 1, "failed to write to ring buffer"); + err = true; + } + } + } else { + Reporter::reportEvent( 1, "not reach here."); + err = true; + } +#endif + } else { + + Float32 *data = (Float32 *)inInputData->mBuffers[0].mData; + if (self->getChannel() == 2 && self->stream_description.mChannelsPerFrame == 2) { + // Ensure the temporary buffer is big enough + size_t sz = inInputData->mBuffers[0].mDataByteSize; + self->cnv_buffer = (char *)realloc(self->cnv_buffer, sz); + if (!self->cnv_buffer) { + Reporter::reportEvent( 1, "realloc on cnv_buffer failed"); + return kAudioHardwareUnspecifiedError; + } + + size_t to_write = sz / 2; + Float32 *dataL = (Float32 *)self->cnv_buffer; + Float32 *dataR = (Float32 *)(self->cnv_buffer + to_write); + + sz /= sizeof(Float32); + for (unsigned int i = 0; i < sz; i += 2) { + dataL[i / 2] = data[i]; + dataR[i / 2] = data[i + 1]; + } + + size_t len = jack_ringbuffer_write(self->rb[0], (char *)dataL, to_write); + if (len < to_write) { + Reporter::reportEvent( 1, "failed to write to ring buffer"); + err = true; + } + + len = jack_ringbuffer_write(self->rb[1], (char *)dataR, to_write); + if (len < to_write) { + Reporter::reportEvent( 1, "failed to write to ring buffer"); + err = true; + } + } else if (self->getChannel() == 1 && self->stream_description.mChannelsPerFrame == 2) { + // Ensure the temporary buffer is big enough + size_t sz = inInputData->mBuffers[0].mDataByteSize; + self->cnv_buffer = (char *)realloc(self->cnv_buffer, sz / 2); + if (!self->cnv_buffer) { + Reporter::reportEvent( 1, "realloc on cnv_buffer failed"); + return kAudioHardwareUnspecifiedError; + } + + size_t to_write = sz / 2; + Float32 *dataM = (Float32 *)self->cnv_buffer; + + sz /= sizeof(Float32); + for (unsigned int i = 0; i < sz; i += 2) { + dataM[i / 2] = ((data[i] + data[i + 1]) / 2.0); + } + + size_t len = jack_ringbuffer_write(self->rb[0], (char *)dataM, to_write); + if (len < to_write) { + Reporter::reportEvent( 1, "failed to write to ring buffer"); + err = true; + } + + } else if (self->getChannel() == 2 && self->stream_description.mChannelsPerFrame == 1) { +#if 1 + Reporter::reportEvent( 1, "not supported."); + err = true; +#else + //XXX not tested. + // Ensure the temporary buffer is big enough + size_t sz = inInputData->mBuffers[0].mDataByteSize; + self->cnv_buffer = (char *)realloc(self->cnv_buffer, sz * 2); + if (!self->cnv_buffer) { + Reporter::reportEvent( 1, "realloc on cnv_buffer failed"); + return kAudioHardwareUnspecifiedError; + } + + size_t to_write = sz; + Float32 *dataL = (Float32 *)self->cnv_buffer; + Float32 *dataR = (Float32 *)(self->cnv_buffer + to_write); + + sz /= sizeof(Float32); + for (unsigned int i = 0; i < sz; i++) { + dataL[i] = data[i]; + dataR[i] = data[i]; + } + + size_t len = jack_ringbuffer_write(self->rb[0], (char *)dataL, to_write); + if (len < to_write) { + Reporter::reportEvent( 1, "failed to write to ring buffer"); + err = true; + } + + len = jack_ringbuffer_write(self->rb[1], (char *)dataR, to_write); + if (len < to_write) { + Reporter::reportEvent( 1, "failed to write to ring buffer"); + err = true; + } +#endif + } else if (self->getChannel() == 1 && self->stream_description.mChannelsPerFrame == 1) { +#if 1 + Reporter::reportEvent( 1, "not supported."); + err = true; +#else + //XXX not tested. + size_t to_write = inInputData->mBuffers[0].mDataByteSize; + size_t len = jack_ringbuffer_write(self->rb[0], (char *)data, to_write); + if (len < to_write) { + Reporter::reportEvent( 1, "failed to write to ring buffer"); + err = true; + } +#endif + } else { + Reporter::reportEvent( 1, "not reach here."); + err = true; + } + } + + if (err) { + return kAudioHardwareUnspecifiedError; + } + // Success + return kAudioHardwareNoError; +} + +/* + */ +OSStatus +CoreAudioDspSource :: device_listener( AudioDeviceID inDevice, + UInt32 channel, + Boolean isInput, + AudioDevicePropertyID propertyID, + void* handlePointer) +{ +#if 0 + CoreAudioDspSource* self = (CoreAudioDspSource*)handlePointer; + if ( propertyID == kAudioDeviceProcessorOverload ) { + Reporter::reportEvent( 1, "CoreAudioDspSource :: device_listener xrun"); + // xrun! + } +#endif + return kAudioHardwareNoError; +} + +/* + */ +static unsigned int get_device_count(void) +{ + unsigned int count = 0; + UInt32 data_size = 0; + OSStatus result = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &data_size, NULL); + if (result != noErr) { + Reporter::reportEvent( 1, "CoreAudioDspSource :: get_device_count():error:", + get_error_name(result)); + return 0; + } + + count = data_size / sizeof(AudioDeviceID); + + return count; +} + +/* + */ +bool CoreAudioDspSource :: startDevice() +{ + if (is_running) { + return true; + } + + OSStatus result = AudioDeviceAddIOProc(device_id, callback_handler, this); + if (result != noErr) { + Reporter::reportEvent( 1, "CoreAudioDspSource :: startDevice():error:AudioDeviceAddIOProc():", + get_error_name(result)); + return false; + } + + result = AudioDeviceStart(device_id, callback_handler); + if (result != noErr) { + Reporter::reportEvent( 1, "CoreAudioDspSource :: startDevice():error:AudioDeviceStart():", + get_error_name(result)); + return false; + } + is_running = true; + return true; +} + +/* + */ +bool CoreAudioDspSource :: stopDevice() +{ + if (!is_running) { + return true; + } + + OSStatus result = AudioDeviceRemoveIOProc(device_id, callback_handler); + if (result != noErr) { +#if 0 + Reporter::reportEvent( 1, "CoreAudioDspSource :: stopDevice():error:AudioDeviceRemoveIOProc():", + get_error_name(result)); + return false; +#endif + } + + result = AudioDeviceStop(device_id, callback_handler); + if (result != noErr) { +#if 0 + Reporter::reportEvent( 1, "CoreAudioDspSource :: stopDevice():error:AudioDeviceStop():", + get_error_name(result)); + return false; +#endif + } + is_running = false; + return true; +} + + +static const char* get_error_name( OSStatus code ) +{ + switch( code ) { + + case kAudioHardwareNotRunningError: + return "kAudioHardwareNotRunningError"; + + case kAudioHardwareUnspecifiedError: + return "kAudioHardwareUnspecifiedError"; + + case kAudioHardwareUnknownPropertyError: + return "kAudioHardwareUnknownPropertyError"; + + case kAudioHardwareBadPropertySizeError: + return "kAudioHardwareBadPropertySizeError"; + + case kAudioHardwareIllegalOperationError: + return "kAudioHardwareIllegalOperationError"; +#if 0 + case kAudioHardwareBadObjectError: + return "kAudioHardwareBadObjectError"; +#endif + case kAudioHardwareBadDeviceError: + return "kAudioHardwareBadDeviceError"; + + case kAudioHardwareBadStreamError: + return "kAudioHardwareBadStreamError"; + + case kAudioHardwareUnsupportedOperationError: + return "kAudioHardwareUnsupportedOperationError"; + + case kAudioDeviceUnsupportedFormatError: + return "kAudioDeviceUnsupportedFormatError"; + + case kAudioDevicePermissionsError: + return "kAudioDevicePermissionsError"; + + default: + return "CoreAudio unknown error"; + } +} + +#endif // SUPPORT_COREAUDIO_DSP + diff --git a/darkice/branches/darkice-macosx/src/CoreAudioDspSource.h b/darkice/branches/darkice-macosx/src/CoreAudioDspSource.h new file mode 100644 index 0000000..7954b96 --- /dev/null +++ b/darkice/branches/darkice-macosx/src/CoreAudioDspSource.h @@ -0,0 +1,271 @@ +#ifndef COREAUDIO_DSP_SOURCE_H +#define COREAUDIO_DSP_SOURCE_H + +#ifndef __cplusplus +#error This is a C++ include file +#endif + + +/* ============================================================ include files */ + + +#include "Reporter.h" +#include "AudioSource.h" + +#if defined( HAVE_COREAUDIO_LIB ) +#include +#include +#else +#error configure for CoreAudio +#endif + + +/* ================================================================ constants */ + + +/* =================================================================== macros */ + + +/* =============================================================== data types */ + +/** + * An audio input based on CoreAudio + * + * @author $Author: $ + * @version $Revision: $ + */ +class CoreAudioDspSource : public AudioSource, public virtual Reporter +{ + private: + /** + * The device ID + */ + AudioDeviceID device_id; + + /** + * The ring buffer. + */ + jack_ringbuffer_t * rb[2]; + + /** + * The audio sample buffer. + */ + char * tmp_buffer; + + /** + * The audio format convarting buffer. + */ + char * cnv_buffer; + + /** + * Device status whether running or not. + */ + bool is_running; + + /** + * Device status whether be opened or not. + */ + bool is_opened; + + /** + * Stream description. + */ + AudioStreamBasicDescription stream_description; + + protected: + + /** + * Default constructor. Always throws an Exception. + * + * @exception Exception + */ + inline + CoreAudioDspSource ( void ) throw ( Exception ) + { + throw Exception( __FILE__, __LINE__); + } + + /** + * Initialize the object + * + * @exception Exception + */ + void + init ( const char* name ) throw ( Exception ); + + /** + * De-initialize the object + * + * @exception Exception + */ + void + strip ( void ) throw ( Exception ); + + static OSStatus + callback_handler( AudioDeviceID inDevice, + const AudioTimeStamp *inNow, + const AudioBufferList *inInputData, + const AudioTimeStamp *inInputTime, + AudioBufferList *outOutputData, + const AudioTimeStamp *inOutputTime, + void *infoPointer ); + + static OSStatus + device_listener( AudioDeviceID inDevice, + UInt32 channel, + Boolean isInput, + AudioDevicePropertyID propertyID, + void* handlePointer); + + private: + /** + */ + bool + startDevice(void); + + /** + */ + bool + stopDevice(void); + + public: + + /** + * Constructor. + * + * @param name the name of the device + * @param sampleRate samples per second (e.g. 44100 for 44.1kHz). + * @param bitsPerSample bits per sample (e.g. 16 bits). + * @param channels number of channels of the audio source + * (e.g. 1 for mono, 2 for stereo, etc.). + * @exception Exception + */ + inline + CoreAudioDspSource ( const char * name, + int sampleRate = 44100, + int bitsPerSample = 16, + int channels = 2 ) + throw ( Exception ) + + : AudioSource( sampleRate, bitsPerSample, channels ) + { + init( name ); + } + + /** + * Copy Constructor. + * + * @param cds the object to copy. + * @exception Exception + */ + inline + CoreAudioDspSource ( const CoreAudioDspSource & cds ) throw ( Exception ) + : AudioSource( cds ) + { + throw Exception( __FILE__, __LINE__, "CoreAudioDspSource doesn't copy"); + } + + /** + * Destructor. + * + * @exception Exception + */ + inline virtual + ~CoreAudioDspSource ( void ) throw ( Exception ) + { + strip(); + } + + /** + * Assignment operator. + * + * @param ds the object to assign to this one. + * @return a reference to this object. + * @exception Exception + */ + inline virtual CoreAudioDspSource & + operator= ( const CoreAudioDspSource & ds ) throw ( Exception ) + { + throw Exception( __FILE__, __LINE__, "CoreAudioDspSource doesn't assign"); + } + + /** + * Tell if the data from this source comes in big or little endian. + * + * @return true if the source is big endian, false otherwise + */ + virtual bool + isBigEndian ( void ) const throw (); + + /** + * Open the CoreAudioDspSource. + * This does not put the device into recording mode. + * To start getting samples, call either canRead() or read(). + * + * @return true if opening was successful, false otherwise + * @exception Exception + * + * @see #canRead + * @see #read + */ + virtual bool + open ( void ) throw ( Exception ); + + /** + * Check if the CoreAudioDspSource is registered + * + * @return true if device is setup + */ + inline virtual bool + isOpen ( void ) const throw () + { + return is_opened; + } + + /** + * Check if the CoreAudioDspSource can be read from. + * Blocks until the specified time for data to be available. + * Puts the CoreAudio device into recording mode. + * + * @param sec the maximum seconds to block. + * @param usec micro seconds to block after the full seconds. + * @return true if the CoreAudioDspSource is ready to be read from, + * false otherwise. + * @exception Exception + */ + virtual bool + canRead ( unsigned int sec, + unsigned int usec ) throw ( Exception ); + + /** + * Read from the CoreAudioDspSource. + * Puts the CoreAudio device into recording mode. + * + * @param buf the buffer to read into. + * @param len the number of bytes to read into buf + * @return the number of bytes read (may be less than len). + * @exception Exception + */ + virtual unsigned int + read ( void * buf, + unsigned int len ) throw ( Exception ); + + /** + * Close the CoreAudioDspSource. + * + * @exception Exception + */ + virtual void + close ( void ) throw ( Exception ); + +}; + + +/* ================================================= external data structures */ + + +/* ====================================================== function prototypes */ + + + +#endif /* COREAUDIO_DSP_SOURCE_H */ diff --git a/darkice/branches/darkice-macosx/src/DarkIce.cpp b/darkice/branches/darkice-macosx/src/DarkIce.cpp index 0438473..8ac2e5c 100644 --- a/darkice/branches/darkice-macosx/src/DarkIce.cpp +++ b/darkice/branches/darkice-macosx/src/DarkIce.cpp @@ -1191,7 +1191,7 @@ DarkIce :: encode ( void ) throw ( Exception ) (dsp->getBitsPerSample() / 8UL) * dsp->getChannel() * duration; - + len = encConnector->transfer( bytes, 4096, 1, 0 ); reportEvent( 1, len, "bytes transfered to the encoders"); diff --git a/darkice/branches/darkice-macosx/src/Makefile.am b/darkice/branches/darkice-macosx/src/Makefile.am index b718b54..8a12e87 100644 --- a/darkice/branches/darkice-macosx/src/Makefile.am +++ b/darkice/branches/darkice-macosx/src/Makefile.am @@ -3,7 +3,7 @@ AM_CXXFLAGS = -O2 -pedantic -Wall @DEBUG_CXXFLAGS@ @PTHREAD_CFLAGS@ @JACK_CFLAGS@ INCLUDES = @LAME_INCFLAGS@ @VORBIS_INCFLAGS@ @FAAC_INCFLAGS@ @AACPLUS_INCFLAGS@ @TWOLAME_INCFLAGS@ @ALSA_INCFLAGS@ LDADD = @PTHREAD_LIBS@ @LAME_LDFLAGS@ @VORBIS_LDFLAGS@ @FAAC_LDFLAGS@ @AACPLUS_LDFLAGS@ @TWOLAME_LDFLAGS@ \ - @ALSA_LDFLAGS@ @JACK_LDFLAGS@ + @ALSA_LDFLAGS@ @JACK_LDFLAGS@ @COREAUDIO_LDFLAGS@ darkice_SOURCES = AudioEncoder.h\ AudioSource.h\ @@ -70,5 +70,7 @@ darkice_SOURCES = AudioEncoder.h\ AlsaDspSource.cpp\ JackDspSource.h\ JackDspSource.cpp\ + CoreAudioDspSource.h\ + CoreAudioDspSource.cpp\ main.cpp