summaryrefslogtreecommitdiff
path: root/modules/coreaudio/player.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/coreaudio/player.c')
-rw-r--r--modules/coreaudio/player.c165
1 files changed, 165 insertions, 0 deletions
diff --git a/modules/coreaudio/player.c b/modules/coreaudio/player.c
new file mode 100644
index 0000000..7d247cd
--- /dev/null
+++ b/modules/coreaudio/player.c
@@ -0,0 +1,165 @@
+/**
+ * @file coreaudio/player.c Apple Coreaudio sound driver - player
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <AudioToolbox/AudioQueue.h>
+#include <pthread.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include "coreaudio.h"
+
+
+/* This value can be tuned */
+#if TARGET_OS_IPHONE
+#define BUFC 20
+#else
+#define BUFC 6
+#endif
+
+
+struct auplay_st {
+ const struct auplay *ap; /* inheritance */
+ AudioQueueRef queue;
+ AudioQueueBufferRef buf[BUFC];
+ pthread_mutex_t mutex;
+ auplay_write_h *wh;
+ void *arg;
+};
+
+
+static void auplay_destructor(void *arg)
+{
+ struct auplay_st *st = arg;
+ uint32_t i;
+
+ pthread_mutex_lock(&st->mutex);
+ st->wh = NULL;
+ pthread_mutex_unlock(&st->mutex);
+
+ audio_session_disable();
+
+ if (st->queue) {
+ AudioQueuePause(st->queue);
+ AudioQueueStop(st->queue, true);
+
+ for (i=0; i<ARRAY_SIZE(st->buf); i++)
+ if (st->buf[i])
+ AudioQueueFreeBuffer(st->queue, st->buf[i]);
+
+ AudioQueueDispose(st->queue, true);
+ }
+
+ pthread_mutex_destroy(&st->mutex);
+}
+
+
+static void play_handler(void *userData, AudioQueueRef outQ,
+ AudioQueueBufferRef outQB)
+{
+ struct auplay_st *st = userData;
+ auplay_write_h *wh;
+ void *arg;
+
+ pthread_mutex_lock(&st->mutex);
+ wh = st->wh;
+ arg = st->arg;
+ pthread_mutex_unlock(&st->mutex);
+
+ if (!wh)
+ return;
+
+ wh(outQB->mAudioData, outQB->mAudioDataByteSize/2, arg);
+
+ AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);
+}
+
+
+int coreaudio_player_alloc(struct auplay_st **stp, const struct auplay *ap,
+ struct auplay_prm *prm, const char *device,
+ auplay_write_h *wh, void *arg)
+{
+ AudioStreamBasicDescription fmt;
+ struct auplay_st *st;
+ uint32_t sampc, bytc, i;
+ OSStatus status;
+ int err;
+
+ (void)device;
+
+ if (!stp || !ap || !prm || prm->fmt != AUFMT_S16LE)
+ return EINVAL;
+
+ st = mem_zalloc(sizeof(*st), auplay_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->ap = ap;
+ st->wh = wh;
+ st->arg = arg;
+
+ err = pthread_mutex_init(&st->mutex, NULL);
+ if (err)
+ goto out;
+
+ err = audio_session_enable();
+ if (err)
+ goto out;
+
+ fmt.mSampleRate = (Float64)prm->srate;
+ fmt.mFormatID = kAudioFormatLinearPCM;
+ fmt.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
+ kAudioFormatFlagIsPacked;
+#ifdef __BIG_ENDIAN__
+ fmt.mFormatFlags |= kAudioFormatFlagIsBigEndian;
+#endif
+ fmt.mFramesPerPacket = 1;
+ fmt.mBytesPerFrame = prm->ch * 2;
+ fmt.mBytesPerPacket = prm->ch * 2;
+ fmt.mChannelsPerFrame = prm->ch;
+ fmt.mBitsPerChannel = 16;
+
+ status = AudioQueueNewOutput(&fmt, play_handler, st, NULL,
+ kCFRunLoopCommonModes, 0, &st->queue);
+ if (status) {
+ warning("coreaudio: AudioQueueNewOutput error: %i\n", status);
+ err = ENODEV;
+ goto out;
+ }
+
+ sampc = prm->srate * prm->ch * prm->ptime / 1000;
+ bytc = sampc * 2;
+
+ for (i=0; i<ARRAY_SIZE(st->buf); i++) {
+
+ status = AudioQueueAllocateBuffer(st->queue, bytc,
+ &st->buf[i]);
+ if (status) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ st->buf[i]->mAudioDataByteSize = bytc;
+
+ memset(st->buf[i]->mAudioData, 0,
+ st->buf[i]->mAudioDataByteSize);
+
+ (void)AudioQueueEnqueueBuffer(st->queue, st->buf[i], 0, NULL);
+ }
+
+ status = AudioQueueStart(st->queue, NULL);
+ if (status) {
+ warning("coreaudio: AudioQueueStart error %i\n", status);
+ err = ENODEV;
+ goto out;
+ }
+
+ out:
+ if (err)
+ mem_deref(st);
+ else
+ *stp = st;
+
+ return err;
+}