chiark / gitweb /
Uniform audio backend for Core Audio. There is an untested OSS one
[disorder] / lib / uaudio-coreaudio.c
diff --git a/lib/uaudio-coreaudio.c b/lib/uaudio-coreaudio.c
new file mode 100644 (file)
index 0000000..129a66e
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/uaudio-coreaudio.c
+ * @brief Support for Core Audio backend */
+#include "common.h"
+
+#if HAVE_COREAUDIO_AUDIOHARDWARE_H
+
+#include "coreaudio.h"
+#include "uaudio.h"
+#include "mem.h"
+#include "log.h"
+#include "syscalls.h"
+
+/** @brief Callback to request sample data */
+static uaudio_callback *coreaudio_callback;
+
+/** @brief Userdata for @ref coreaudio_callback */
+static void *coreaudio_userdata;
+
+/** @brief Core Audio device ID */
+static AudioDeviceID coreaudio_adid;
+
+/** @brief Core Audio option names */
+static const char *const coreaudio_options[] = {
+  "device",
+  NULL
+};
+
+/** @brief Callback from Core Audio
+ *
+ * Core Audio demands floating point samples but we provide integers.
+ * So there is a conversion step in here.
+ */
+static OSStatus coreaudio_adioproc
+    (AudioDeviceID attribute((unused)) inDevice,
+     const AudioTimeStamp attribute((unused)) *inNow,
+     const AudioBufferList attribute((unused)) *inInputData,
+     const AudioTimeStamp attribute((unused)) *inInputTime,
+     AudioBufferList *outOutputData,
+     const AudioTimeStamp attribute((unused)) *inOutputTime,
+     void attribute((unused)) *inClientData) {
+  /* Number of buffers we must fill */
+  unsigned nbuffers = outOutputData->mNumberBuffers;
+  /* Pointer to buffer to fill */
+  AudioBuffer *ab = outOutputData->mBuffers;
+  
+  while(nbuffers > 0) {
+    /* Where to store converted sample data */
+    float *samples = ab->mData;
+    /* Number of samples left to fill */
+    size_t nsamples = ab->mDataByteSize / sizeof (float);
+
+    while(nsamples > 0) {
+      /* Integer-format input buffer */
+      int16_t input[1024], *ptr = input;
+      /* How many samples we'll ask for */
+      const int ask = nsamples > 1024 ? 1024 : (int)nsamples;
+      /* How many we get */
+      int got;
+
+      got = coreaudio_callback(input, ask, coreaudio_userdata);
+      /* Convert the samples and store in the output buffer */
+      nsamples -= got;
+      while(got > 0) {
+        --got;
+        *samples++ = *ptr++ * (0.5 / 32767);
+      }
+    }
+    /* Move on to the next buffer */
+    ++ab;
+    --nbuffers;
+  }
+  return 0;
+}
+
+static void coreaudio_start(uaudio_callback *callback,
+                            void *userdata) {
+  OSStatus status;
+  UInt32 propertySize;
+  AudioStreamBasicDescription asbd;
+  const char *device;
+
+  coreaudio_callback = callback;
+  coreaudio_userdata = userdata;
+  device = uaudio_get("device");
+  coreaudio_adid = coreaudio_getdevice(device);
+  propertySize = sizeof asbd;
+  status = AudioDeviceGetProperty(coreaudio_adid, 0, false,
+                                 kAudioDevicePropertyStreamFormat,
+                                 &propertySize, &asbd);
+  if(status)
+    coreaudio_fatal(status, "AudioHardwareGetProperty");
+  D(("mSampleRate       %f", asbd.mSampleRate));
+  D(("mFormatID         %08lx", asbd.mFormatID));
+  D(("mFormatFlags      %08lx", asbd.mFormatFlags));
+  D(("mBytesPerPacket   %08lx", asbd.mBytesPerPacket));
+  D(("mFramesPerPacket  %08lx", asbd.mFramesPerPacket));
+  D(("mBytesPerFrame    %08lx", asbd.mBytesPerFrame));
+  D(("mChannelsPerFrame %08lx", asbd.mChannelsPerFrame));
+  D(("mBitsPerChannel   %08lx", asbd.mBitsPerChannel));
+  D(("mReserved         %08lx", asbd.mReserved));
+  if(asbd.mFormatID != kAudioFormatLinearPCM)
+    disorder_fatal(0, "audio device does not support kAudioFormatLinearPCM");
+  status = AudioDeviceAddIOProc(coreaudio_adid, coreaudio_adioproc, 0);
+  if(status)
+    coreaudio_fatal(status, "AudioDeviceAddIOProc");
+}
+
+static void coreaudio_stop(void) {
+}
+
+static void coreaudio_activate(void) {
+  OSStatus status;
+
+  status = AudioDeviceStart(coreaudio_adid, coreaudio_adioproc);
+  if(status)
+    coreaudio_fatal(status, "AudioDeviceStart");
+}
+
+static void coreaudio_deactivate(void) {
+  OSStatus status;
+
+  status = AudioDeviceStop(coreaudio_adid, coreaudio_adioproc);
+  if(status)
+    coreaudio_fatal(status, "AudioDeviceStop");
+}
+
+const struct uaudio uaudio_coreaudio = {
+  .name = "coreaudio",
+  .options = coreaudio_options,
+  .start = coreaudio_start,
+  .stop = coreaudio_stop,
+  .activate = coreaudio_activate,
+  .deactivate = coreaudio_deactivate
+};
+
+#endif
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/