* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <CoreAudio/CoreAudio.h>
-#include <string.h> /* strerror */
#include <pthread.h> /* pthread_X */
-#include "vl.h"
+#include "qemu-common.h"
+#include "audio.h"
#define AUDIO_CAP "coreaudio"
#include "audio_int.h"
-struct {
+#ifndef MAC_OS_X_VERSION_10_6
+#define MAC_OS_X_VERSION_10_6 1060
+#endif
+
+typedef struct {
int buffer_frames;
-} conf = {
- .buffer_frames = 512
-};
+ int nbuffers;
+} CoreaudioConf;
typedef struct coreaudioVoiceOut {
HWVoiceOut hw;
pthread_mutex_t mutex;
AudioDeviceID outputDeviceID;
- UInt32 audioDevicePropertyBufferSize;
+ UInt32 audioDevicePropertyBufferFrameSize;
AudioStreamBasicDescription outputStreamBasicDescription;
- int isPlaying;
+ AudioDeviceIOProcID ioprocid;
int live;
int decr;
int rpos;
} coreaudioVoiceOut;
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+/* The APIs used here only become available from 10.6 */
+
+static OSStatus coreaudio_get_voice(AudioDeviceID *id)
+{
+ UInt32 size = sizeof(*id);
+ AudioObjectPropertyAddress addr = {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+
+ return AudioObjectGetPropertyData(kAudioObjectSystemObject,
+ &addr,
+ 0,
+ NULL,
+ &size,
+ id);
+}
+
+static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
+ AudioValueRange *framerange)
+{
+ UInt32 size = sizeof(*framerange);
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyBufferFrameSizeRange,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
+
+ return AudioObjectGetPropertyData(id,
+ &addr,
+ 0,
+ NULL,
+ &size,
+ framerange);
+}
+
+static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
+{
+ UInt32 size = sizeof(*framesize);
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
+
+ return AudioObjectGetPropertyData(id,
+ &addr,
+ 0,
+ NULL,
+ &size,
+ framesize);
+}
+
+static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
+{
+ UInt32 size = sizeof(*framesize);
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
+
+ return AudioObjectSetPropertyData(id,
+ &addr,
+ 0,
+ NULL,
+ size,
+ framesize);
+}
+
+static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
+ AudioStreamBasicDescription *d)
+{
+ UInt32 size = sizeof(*d);
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyStreamFormat,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
+
+ return AudioObjectGetPropertyData(id,
+ &addr,
+ 0,
+ NULL,
+ &size,
+ d);
+}
+
+static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
+ AudioStreamBasicDescription *d)
+{
+ UInt32 size = sizeof(*d);
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyStreamFormat,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
+
+ return AudioObjectSetPropertyData(id,
+ &addr,
+ 0,
+ NULL,
+ size,
+ d);
+}
+
+static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
+{
+ UInt32 size = sizeof(*result);
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyDeviceIsRunning,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
+
+ return AudioObjectGetPropertyData(id,
+ &addr,
+ 0,
+ NULL,
+ &size,
+ result);
+}
+#else
+/* Legacy versions of functions using deprecated APIs */
+
+static OSStatus coreaudio_get_voice(AudioDeviceID *id)
+{
+ UInt32 size = sizeof(*id);
+
+ return AudioHardwareGetProperty(
+ kAudioHardwarePropertyDefaultOutputDevice,
+ &size,
+ id);
+}
+
+static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
+ AudioValueRange *framerange)
+{
+ UInt32 size = sizeof(*framerange);
+
+ return AudioDeviceGetProperty(
+ id,
+ 0,
+ 0,
+ kAudioDevicePropertyBufferFrameSizeRange,
+ &size,
+ framerange);
+}
+
+static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
+{
+ UInt32 size = sizeof(*framesize);
+
+ return AudioDeviceGetProperty(
+ id,
+ 0,
+ false,
+ kAudioDevicePropertyBufferFrameSize,
+ &size,
+ framesize);
+}
+
+static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
+{
+ UInt32 size = sizeof(*framesize);
+
+ return AudioDeviceSetProperty(
+ id,
+ NULL,
+ 0,
+ false,
+ kAudioDevicePropertyBufferFrameSize,
+ size,
+ framesize);
+}
+
+static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
+ AudioStreamBasicDescription *d)
+{
+ UInt32 size = sizeof(*d);
+
+ return AudioDeviceGetProperty(
+ id,
+ 0,
+ false,
+ kAudioDevicePropertyStreamFormat,
+ &size,
+ d);
+}
+
+static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
+ AudioStreamBasicDescription *d)
+{
+ UInt32 size = sizeof(*d);
+
+ return AudioDeviceSetProperty(
+ id,
+ 0,
+ 0,
+ 0,
+ kAudioDevicePropertyStreamFormat,
+ size,
+ d);
+}
+
+static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
+{
+ UInt32 size = sizeof(*result);
+
+ return AudioDeviceGetProperty(
+ id,
+ 0,
+ 0,
+ kAudioDevicePropertyDeviceIsRunning,
+ &size,
+ result);
+}
+#endif
+
static void coreaudio_logstatus (OSStatus status)
{
- char *str = "BUG";
+ const char *str = "BUG";
switch(status) {
case kAudioHardwareNoError:
break;
default:
- AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
+ AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
return;
}
coreaudio_logstatus (status);
}
+static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
+{
+ OSStatus status;
+ UInt32 result = 0;
+ status = coreaudio_get_isrunning(outputDeviceID, &result);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr(status,
+ "Could not determine whether Device is playing\n");
+ }
+ return result;
+}
+
static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
{
int err;
return 0;
}
-static int coreaudio_run_out (HWVoiceOut *hw)
+static int coreaudio_run_out (HWVoiceOut *hw, int live)
{
- int live, decr;
+ int decr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
if (coreaudio_lock (core, "coreaudio_run_out")) {
return 0;
}
- live = audio_pcm_hw_get_live_out (hw);
-
if (core->decr > live) {
ldebug ("core->decr %d live %d core->live %d\n",
core->decr,
const AudioTimeStamp* inOutputTime,
void* hwptr)
{
- unsigned int frame, frameCount;
+ UInt32 frame, frameCount;
float *out = outOutputData->mBuffers[0].mData;
HWVoiceOut *hw = hwptr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
int rpos, live;
- st_sample_t *src;
+ struct st_sample *src;
#ifndef FLOAT_MIXENG
#ifdef RECIPROCAL
const float scale = 1.f / UINT_MAX;
return 0;
}
- frameCount = conf.buffer_frames;
+ frameCount = core->audioDevicePropertyBufferFrameSize;
live = core->live;
/* if there are not enough samples, set signal and return */
#endif
}
- /* cleanup */
- mixeng_clear (src, frameCount);
rpos = (rpos + frameCount) % hw->samples;
- core->decr = frameCount;
+ core->decr += frameCount;
core->rpos = rpos;
coreaudio_unlock (core, "audioDeviceIOProc");
return audio_pcm_sw_write (sw, buf, len);
}
-static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
+static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
{
OSStatus status;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
- UInt32 propertySize;
int err;
- int bits = 8;
- int endianess = 0;
- const char *typ = "DAC";
+ const char *typ = "playback";
+ AudioValueRange frameRange;
+ CoreaudioConf *conf = drv_opaque;
/* create mutex */
err = pthread_mutex_init(&core->mutex, NULL);
return -1;
}
- if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
- bits = 16;
- endianess = 1;
- }
+ audio_pcm_init_info (&hw->info, as);
- audio_pcm_init_info (
- &hw->info,
- as,
- /* Following is irrelevant actually since we do not use
- mixengs clipping routines */
- audio_need_to_swap_endian (endianess)
- );
-
- /* open default output device */
- propertySize = sizeof(core->outputDeviceID);
- status = AudioHardwareGetProperty(
- kAudioHardwarePropertyDefaultOutputDevice,
- &propertySize,
- &core->outputDeviceID);
+ status = coreaudio_get_voice(&core->outputDeviceID);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ,
"Could not get default output Device\n");
return -1;
}
- /* set Buffersize to conf.buffer_frames frames */
- propertySize = sizeof(core->audioDevicePropertyBufferSize);
- core->audioDevicePropertyBufferSize =
- conf.buffer_frames * sizeof(float) << (as->nchannels == 2);
- status = AudioDeviceSetProperty(
- core->outputDeviceID,
- NULL,
- 0,
- false,
- kAudioDevicePropertyBufferSize,
- propertySize,
- &core->audioDevicePropertyBufferSize);
+ /* get minimum and maximum buffer frame sizes */
+ status = coreaudio_get_framesizerange(core->outputDeviceID,
+ &frameRange);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ,
- "Could not set device buffer size %d\n",
- kAudioDevicePropertyBufferSize);
+ "Could not get device buffer frame range\n");
return -1;
}
- /* get Buffersize */
- propertySize = sizeof(core->audioDevicePropertyBufferSize);
- status = AudioDeviceGetProperty(
- core->outputDeviceID,
- 0,
- false,
- kAudioDevicePropertyBufferSize,
- &propertySize,
- &core->audioDevicePropertyBufferSize);
+ if (frameRange.mMinimum > conf->buffer_frames) {
+ core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
+ dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
+ }
+ else if (frameRange.mMaximum < conf->buffer_frames) {
+ core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
+ dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
+ }
+ else {
+ core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
+ }
+
+ /* set Buffer Frame Size */
+ status = coreaudio_set_framesize(core->outputDeviceID,
+ &core->audioDevicePropertyBufferFrameSize);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not set device buffer frame size %" PRIu32 "\n",
+ (uint32_t)core->audioDevicePropertyBufferFrameSize);
+ return -1;
+ }
+
+ /* get Buffer Frame Size */
+ status = coreaudio_get_framesize(core->outputDeviceID,
+ &core->audioDevicePropertyBufferFrameSize);
if (status != kAudioHardwareNoError) {
- coreaudio_logerr2 (status, typ, "Could not get device buffer size\n");
+ coreaudio_logerr2 (status, typ,
+ "Could not get device buffer frame size\n");
return -1;
}
- hw->samples = (core->audioDevicePropertyBufferSize / sizeof (float))
- >> (as->nchannels == 2);
+ hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
/* get StreamFormat */
- propertySize = sizeof(core->outputStreamBasicDescription);
- status = AudioDeviceGetProperty(
- core->outputDeviceID,
- 0,
- false,
- kAudioDevicePropertyStreamFormat,
- &propertySize,
- &core->outputStreamBasicDescription);
+ status = coreaudio_get_streamformat(core->outputDeviceID,
+ &core->outputStreamBasicDescription);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ,
"Could not get Device Stream properties\n");
}
/* set Samplerate */
- core->outputStreamBasicDescription.mSampleRate = (Float64)as->freq;
- propertySize = sizeof(core->outputStreamBasicDescription);
- status = AudioDeviceSetProperty(
- core->outputDeviceID,
- 0,
- 0,
- 0,
- kAudioDevicePropertyStreamFormat,
- propertySize,
- &core->outputStreamBasicDescription);
+ core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
+ status = coreaudio_set_streamformat(core->outputDeviceID,
+ &core->outputStreamBasicDescription);
if (status != kAudioHardwareNoError) {
- coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", freq);
+ coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
+ as->freq);
core->outputDeviceID = kAudioDeviceUnknown;
return -1;
}
/* set Callback */
- status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
- if (status != kAudioHardwareNoError) {
+ core->ioprocid = NULL;
+ status = AudioDeviceCreateIOProcID(core->outputDeviceID,
+ audioDeviceIOProc,
+ hw,
+ &core->ioprocid);
+ if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
core->outputDeviceID = kAudioDeviceUnknown;
return -1;
}
/* start Playback */
- if (!core->isPlaying) {
- status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
+ if (!isPlaying(core->outputDeviceID)) {
+ status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ, "Could not start playback\n");
- AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
+ AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid);
core->outputDeviceID = kAudioDeviceUnknown;
return -1;
}
- core->isPlaying = 1;
}
return 0;
int err;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
- /* stop playback */
- if (core->isPlaying) {
- status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
- if (status != kAudioHardwareNoError) {
- coreaudio_logerr (status, "Could not stop playback\n");
+ if (!audio_is_cleaning_up()) {
+ /* stop playback */
+ if (isPlaying(core->outputDeviceID)) {
+ status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not stop playback\n");
+ }
}
- core->isPlaying = 0;
- }
- /* remove callback */
- status = AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
- if (status != kAudioHardwareNoError) {
- coreaudio_logerr (status, "Could not remove IOProc\n");
+ /* remove callback */
+ status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
+ core->ioprocid);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not remove IOProc\n");
+ }
}
core->outputDeviceID = kAudioDeviceUnknown;
switch (cmd) {
case VOICE_ENABLE:
/* start playback */
- if (!core->isPlaying) {
- status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
+ if (!isPlaying(core->outputDeviceID)) {
+ status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
if (status != kAudioHardwareNoError) {
- coreaudio_logerr (status, "Could not unpause playback\n");
+ coreaudio_logerr (status, "Could not resume playback\n");
}
- core->isPlaying = 1;
}
break;
case VOICE_DISABLE:
/* stop playback */
- if (core->isPlaying) {
- status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
- if (status != kAudioHardwareNoError) {
- coreaudio_logerr (status, "Could not pause playback\n");
+ if (!audio_is_cleaning_up()) {
+ if (isPlaying(core->outputDeviceID)) {
+ status = AudioDeviceStop(core->outputDeviceID,
+ core->ioprocid);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not pause playback\n");
+ }
}
- core->isPlaying = 0;
}
break;
}
return 0;
}
+static CoreaudioConf glob_conf = {
+ .buffer_frames = 512,
+ .nbuffers = 4,
+};
+
static void *coreaudio_audio_init (void)
{
- return &coreaudio_audio_init;
+ CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
+ *conf = glob_conf;
+
+ return conf;
}
static void coreaudio_audio_fini (void *opaque)
{
- (void) opaque;
+ g_free(opaque);
}
static struct audio_option coreaudio_options[] = {
- {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames,
- "Size of the buffer in frames", NULL, 0},
- {NULL, 0, NULL, NULL, NULL, 0}
+ {
+ .name = "BUFFER_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &glob_conf.buffer_frames,
+ .descr = "Size of the buffer in frames"
+ },
+ {
+ .name = "BUFFER_COUNT",
+ .tag = AUD_OPT_INT,
+ .valp = &glob_conf.nbuffers,
+ .descr = "Number of buffers"
+ },
+ { /* End of list */ }
};
static struct audio_pcm_ops coreaudio_pcm_ops = {
- coreaudio_init_out,
- coreaudio_fini_out,
- coreaudio_run_out,
- coreaudio_write,
- coreaudio_ctl_out,
-
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
+ .init_out = coreaudio_init_out,
+ .fini_out = coreaudio_fini_out,
+ .run_out = coreaudio_run_out,
+ .write = coreaudio_write,
+ .ctl_out = coreaudio_ctl_out
};
-struct audio_driver coreaudio_audio_driver = {
- INIT_FIELD (name = ) "coreaudio",
- INIT_FIELD (descr = )
- "CoreAudio http://developer.apple.com/audio/coreaudio.html",
- INIT_FIELD (options = ) coreaudio_options,
- INIT_FIELD (init = ) coreaudio_audio_init,
- INIT_FIELD (fini = ) coreaudio_audio_fini,
- INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops,
- INIT_FIELD (can_be_default = ) 1,
- INIT_FIELD (max_voices_out = ) 1,
- INIT_FIELD (max_voices_in = ) 0,
- INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
- INIT_FIELD (voice_size_in = ) 0
+static struct audio_driver coreaudio_audio_driver = {
+ .name = "coreaudio",
+ .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
+ .options = coreaudio_options,
+ .init = coreaudio_audio_init,
+ .fini = coreaudio_audio_fini,
+ .pcm_ops = &coreaudio_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = 1,
+ .max_voices_in = 0,
+ .voice_size_out = sizeof (coreaudioVoiceOut),
+ .voice_size_in = 0
};
+
+static void register_audio_coreaudio(void)
+{
+ audio_driver_register(&coreaudio_audio_driver);
+}
+type_init(register_audio_coreaudio);