initial coreaudio patch
This commit is contained in:
parent
6014ba09bd
commit
63c68d3659
|
@ -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-----------------------------------------------------------------------------
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <unistd.h>
|
||||
#else
|
||||
#error need unistd.h
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#else
|
||||
#error need string.h
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#else
|
||||
#error need sys/types.h
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MATH_H
|
||||
#include <math.h>
|
||||
#else
|
||||
#error need math.h
|
||||
#endif
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreAudio/AudioHardware.h>
|
||||
|
||||
#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
|
||||
|
|
@ -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 <jack/ringbuffer.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#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 */
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue