summaryrefslogtreecommitdiff
path: root/modules/coreaudio
diff options
context:
space:
mode:
Diffstat (limited to 'modules/coreaudio')
-rw-r--r--modules/coreaudio/coreaudio.c128
-rw-r--r--modules/coreaudio/coreaudio.h20
-rw-r--r--modules/coreaudio/module.mk13
-rw-r--r--modules/coreaudio/player.c167
-rw-r--r--modules/coreaudio/recorder.c199
5 files changed, 527 insertions, 0 deletions
diff --git a/modules/coreaudio/coreaudio.c b/modules/coreaudio/coreaudio.c
new file mode 100644
index 0000000..bb6ea64
--- /dev/null
+++ b/modules/coreaudio/coreaudio.c
@@ -0,0 +1,128 @@
+/**
+ * @file coreaudio.c Apple Coreaudio sound driver
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <AudioToolbox/AudioToolbox.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include "coreaudio.h"
+
+
+static struct auplay *auplay;
+static struct ausrc *ausrc;
+
+
+int audio_fmt(enum aufmt fmt)
+{
+ switch (fmt) {
+
+ case AUFMT_S16LE: return kAudioFormatLinearPCM;
+ case AUFMT_PCMA: return kAudioFormatALaw;
+ case AUFMT_PCMU: return kAudioFormatULaw;
+ default:
+ warning("coreaudio: unknown format %d\n", fmt);
+ return -1;
+ }
+}
+
+
+int bytesps(enum aufmt fmt)
+{
+ switch (fmt) {
+
+ case AUFMT_S16LE: return 2;
+ case AUFMT_PCMA: return 1;
+ case AUFMT_PCMU: return 1;
+ default: return 0;
+ }
+}
+
+
+#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_2_0
+static void interruptionListener(void *data, UInt32 inInterruptionState)
+{
+ (void)data;
+
+ /* TODO: implement this properly */
+
+ if (inInterruptionState == kAudioSessionBeginInterruption) {
+ debug("coreaudio: player interrupt: Begin\n");
+ }
+ else if (inInterruptionState == kAudioSessionEndInterruption) {
+ debug("coreaudio: player interrupt: End\n");
+ }
+}
+
+
+int audio_session_enable(void)
+{
+ OSStatus res;
+ UInt32 category;
+
+ res = AudioSessionInitialize(NULL, NULL, interruptionListener, 0);
+ if (res && res != 1768843636)
+ return ENODEV;
+
+ category = kAudioSessionCategory_PlayAndRecord;
+ res = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
+ sizeof(category), &category);
+ if (res) {
+ warning("coreaudio: Audio Category: %d\n", res);
+ return ENODEV;
+ }
+
+ res = AudioSessionSetActive(true);
+ if (res) {
+ warning("coreaudio: AudioSessionSetActive: %d\n", res);
+ return ENODEV;
+ }
+
+ return 0;
+}
+
+
+void audio_session_disable(void)
+{
+ AudioSessionSetActive(false);
+}
+#else
+int audio_session_enable(void)
+{
+ return 0;
+}
+
+
+void audio_session_disable(void)
+{
+}
+#endif
+
+
+static int module_init(void)
+{
+ int err;
+
+ err = auplay_register(&auplay, "coreaudio", coreaudio_player_alloc);
+ err |= ausrc_register(&ausrc, "coreaudio", coreaudio_recorder_alloc);
+
+ return err;
+}
+
+
+static int module_close(void)
+{
+ auplay = mem_deref(auplay);
+ ausrc = mem_deref(ausrc);
+
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(coreaudio) = {
+ "coreaudio",
+ "audio",
+ module_init,
+ module_close,
+};
diff --git a/modules/coreaudio/coreaudio.h b/modules/coreaudio/coreaudio.h
new file mode 100644
index 0000000..530e45e
--- /dev/null
+++ b/modules/coreaudio/coreaudio.h
@@ -0,0 +1,20 @@
+/**
+ * @file coreaudio.h Apple Coreaudio sound driver -- internal API
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+int audio_session_enable(void);
+void audio_session_disable(void);
+
+int audio_fmt(enum aufmt fmt);
+int bytesps(enum aufmt fmt);
+
+int coreaudio_player_alloc(struct auplay_st **stp, struct auplay *ap,
+ struct auplay_prm *prm, const char *device,
+ auplay_write_h *wh, void *arg);
+int coreaudio_recorder_alloc(struct ausrc_st **stp, struct ausrc *as,
+ struct media_ctx **ctx,
+ struct ausrc_prm *prm, const char *device,
+ ausrc_read_h *rh, ausrc_error_h *errh, void *arg);
diff --git a/modules/coreaudio/module.mk b/modules/coreaudio/module.mk
new file mode 100644
index 0000000..35d51cf
--- /dev/null
+++ b/modules/coreaudio/module.mk
@@ -0,0 +1,13 @@
+#
+# module.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+MOD := coreaudio
+$(MOD)_SRCS += coreaudio.c
+$(MOD)_SRCS += player.c
+$(MOD)_SRCS += recorder.c
+$(MOD)_LFLAGS += -framework CoreAudio -framework AudioToolbox
+
+include mk/mod.mk
diff --git a/modules/coreaudio/player.c b/modules/coreaudio/player.c
new file mode 100644
index 0000000..68aca4e
--- /dev/null
+++ b/modules/coreaudio/player.c
@@ -0,0 +1,167 @@
+/**
+ * @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 {
+ 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);
+ }
+
+ mem_deref(st->ap);
+
+ 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;
+
+ if (!wh(outQB->mAudioData, outQB->mAudioDataByteSize, arg)) {
+ /* Set the buffer to silence */
+ memset(outQB->mAudioData, 0, outQB->mAudioDataByteSize);
+ }
+
+ AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);
+}
+
+
+int coreaudio_player_alloc(struct auplay_st **stp, 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;
+
+ st = mem_zalloc(sizeof(*st), auplay_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->ap = mem_ref(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 = audio_fmt(prm->fmt);
+ fmt.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
+ kAudioFormatFlagIsPacked;
+#ifdef __BIG_ENDIAN__
+ fmt.mFormatFlags |= kAudioFormatFlagIsBigEndian;
+#endif
+ fmt.mFramesPerPacket = 1;
+ fmt.mBytesPerFrame = prm->ch * bytesps(prm->fmt);
+ fmt.mBytesPerPacket = prm->ch * bytesps(prm->fmt);
+ fmt.mChannelsPerFrame = prm->ch;
+ fmt.mBitsPerChannel = 8*bytesps(prm->fmt);
+
+ 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 * bytesps(prm->fmt);
+
+ 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;
+}
diff --git a/modules/coreaudio/recorder.c b/modules/coreaudio/recorder.c
new file mode 100644
index 0000000..b1f91fc
--- /dev/null
+++ b/modules/coreaudio/recorder.c
@@ -0,0 +1,199 @@
+/**
+ * @file coreaudio/recorder.c Apple Coreaudio sound driver - recorder
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <AudioToolbox/AudioQueue.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include "coreaudio.h"
+
+
+#define BUFC 3
+
+
+struct ausrc_st {
+ struct ausrc *as; /* inheritance */
+ AudioQueueRef queue;
+ AudioQueueBufferRef buf[BUFC];
+ pthread_mutex_t mutex;
+ struct mbuf *mb;
+ ausrc_read_h *rh;
+ void *arg;
+ unsigned int ptime;
+};
+
+
+static void ausrc_destructor(void *arg)
+{
+ struct ausrc_st *st = arg;
+ uint32_t i;
+
+ pthread_mutex_lock(&st->mutex);
+ st->rh = 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);
+ }
+
+ mem_deref(st->mb);
+ mem_deref(st->as);
+
+ pthread_mutex_destroy(&st->mutex);
+}
+
+
+static void record_handler(void *userData, AudioQueueRef inQ,
+ AudioQueueBufferRef inQB,
+ const AudioTimeStamp *inStartTime,
+ UInt32 inNumPackets,
+ const AudioStreamPacketDescription *inPacketDesc)
+{
+ struct ausrc_st *st = userData;
+ struct mbuf *mb = st->mb;
+ unsigned int ptime;
+ ausrc_read_h *rh;
+ size_t sz, sp;
+ void *arg;
+ (void)inStartTime;
+ (void)inNumPackets;
+ (void)inPacketDesc;
+
+ pthread_mutex_lock(&st->mutex);
+ ptime = st->ptime;
+ rh = st->rh;
+ arg = st->arg;
+ pthread_mutex_unlock(&st->mutex);
+
+ if (!rh)
+ return;
+
+ sz = inQB->mAudioDataByteSize;
+ sp = mbuf_get_space(mb);
+
+ if (sz >= sp) {
+ mbuf_write_mem(mb, inQB->mAudioData, sp);
+ rh(mb->buf, (uint32_t)mb->size, arg);
+ mb->pos = 0;
+ mbuf_write_mem(mb, (uint8_t *)inQB->mAudioData + sp, sz - sp);
+ }
+ else {
+ mbuf_write_mem(mb, inQB->mAudioData, sz);
+ }
+
+ AudioQueueEnqueueBuffer(inQ, inQB, 0, NULL);
+
+ /* Force a sleep here, coreaudio's timing is too fast */
+#if !TARGET_OS_IPHONE
+#define ENCODE_TIME 1000
+ usleep((ptime * 1000) - ENCODE_TIME);
+#endif
+}
+
+
+int coreaudio_recorder_alloc(struct ausrc_st **stp, struct ausrc *as,
+ struct media_ctx **ctx,
+ struct ausrc_prm *prm, const char *device,
+ ausrc_read_h *rh, ausrc_error_h *errh, void *arg)
+{
+ AudioStreamBasicDescription fmt;
+ struct ausrc_st *st;
+ uint32_t sampc, bytc, i;
+ OSStatus status;
+ int err;
+
+ (void)ctx;
+ (void)device;
+ (void)errh;
+
+ if (!stp || !as || !prm)
+ return EINVAL;
+
+ st = mem_zalloc(sizeof(*st), ausrc_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->ptime = prm->ptime;
+ st->as = mem_ref(as);
+ st->rh = rh;
+ st->arg = arg;
+
+ sampc = prm->srate * prm->ch * prm->ptime / 1000;
+ bytc = sampc * bytesps(prm->fmt);
+
+ st->mb = mbuf_alloc(bytc);
+ if (!st->mb) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ 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 = audio_fmt(prm->fmt);
+ fmt.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
+ kAudioFormatFlagIsPacked;
+#ifdef __BIG_ENDIAN__
+ fmt.mFormatFlags |= kAudioFormatFlagIsBigEndian;
+#endif
+
+ fmt.mFramesPerPacket = 1;
+ fmt.mBytesPerFrame = prm->ch * bytesps(prm->fmt);
+ fmt.mBytesPerPacket = prm->ch * bytesps(prm->fmt);
+ fmt.mChannelsPerFrame = prm->ch;
+ fmt.mBitsPerChannel = 8*bytesps(prm->fmt);
+
+ status = AudioQueueNewInput(&fmt, record_handler, st, NULL,
+ kCFRunLoopCommonModes, 0, &st->queue);
+ if (status) {
+ warning("coreaudio: AudioQueueNewInput error: %i\n", status);
+ err = ENODEV;
+ goto out;
+ }
+
+ for (i=0; i<ARRAY_SIZE(st->buf); i++) {
+
+ status = AudioQueueAllocateBuffer(st->queue, bytc,
+ &st->buf[i]);
+ if (status) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ 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;
+}