aboutsummaryrefslogtreecommitdiff
path: root/desiredata/src/s_audio_asio.cpp
diff options
context:
space:
mode:
authorIOhannes m zmölnig <zmoelnig@users.sourceforge.net>2008-02-08 13:00:32 +0000
committerIOhannes m zmölnig <zmoelnig@users.sourceforge.net>2008-02-08 13:00:32 +0000
commit4d84d14ac1aa13958eaa2971b03f7f929a519105 (patch)
tree6579d3f2cea5410a10c4baac8d0f372fb0dff372 /desiredata/src/s_audio_asio.cpp
parentb334d38aefbd8e0e159d7af6c20d63c5d2b64859 (diff)
reorganized
svn path=/trunk/; revision=9400
Diffstat (limited to 'desiredata/src/s_audio_asio.cpp')
-rw-r--r--desiredata/src/s_audio_asio.cpp1014
1 files changed, 1014 insertions, 0 deletions
diff --git a/desiredata/src/s_audio_asio.cpp b/desiredata/src/s_audio_asio.cpp
new file mode 100644
index 00000000..fde2891d
--- /dev/null
+++ b/desiredata/src/s_audio_asio.cpp
@@ -0,0 +1,1014 @@
+/* Copyright (c) 2004, Tim Blechmann and others
+ * supported by vibrez.net
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt" in this distribution. */
+
+/* native ASIO interface for windows
+ * adapted from hostsample.cpp (ASIO SDK)
+ */
+
+#ifdef MSW
+#include "windows.h" /* for application window handle */
+#define IEEE754_64FLOAT 1
+#else
+#error This is for MS Windows (Intel CPU architecture) only!!
+#endif
+
+#ifdef _MSC_VER
+#pragma warning( disable : 4091 )
+#endif
+
+#include "m_pd.h"
+extern "C" {
+#include "s_stuff.h"
+#include "m_simd.h"
+}
+
+#include "asio.h" /* steinberg's header file */
+#include "asiodrivers.h" /* ASIODrivers class */
+#include "asiosys.h"
+#include "pthread.h"
+#include "stdio.h" /* for sprintf */
+
+#include <time.h>
+#include <sys/timeb.h>
+
+#include "assert.h"
+#define ASSERT assert
+
+
+/* fast float to integer conversion adapted from Erik de Castro Lopo */
+#define _ISOC9X_SOURCE 1
+#define _ISOC99_SOURCE 1
+#define __USE_ISOC9X 1
+#define __USE_ISOC99 1
+#include "math.h"
+
+// seconds to wait for driver to respond
+#define DRIVERWAIT 1
+
+#define ASIODEBUG
+
+/* public function prototypes */
+// extern "C" void asio_open_audio(int naudioindev, int *audioindev, int nchindev,
+// int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int srate, int scheduler);
+extern "C" void asio_close_audio();
+extern "C" void asio_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize);
+extern "C" int asio_send_dacs();
+
+/* asio callback prototypes for traditional scheduling*/
+static void asio_bufferSwitch(long db_idx, ASIOBool directprocess);
+static void asio_sampleRateDidChange(ASIOSampleRate srate);
+static long asio_messages(long selector, long value, void* message, double* opt);
+static ASIOTime *asio_bufferSwitchTimeInfo(ASIOTime *params, long db_idx, ASIOBool directprocess);
+
+/* asio callback prototypes for callback-based scheduling*/
+static void asio_bufferSwitch_cb(long db_idx, ASIOBool directprocess);
+static void asio_sampleRateDidChange_cb(ASIOSampleRate srate);
+static long asio_messages_cb(long selector, long value, void* message, double* opt);
+static ASIOTime *asio_bufferSwitchTimeInfo_cb(ASIOTime *params, long db_idx, ASIOBool directprocess);
+
+static void float32tofloat32(void* inbuffer, void* outbuffer, long frames);
+static void float32tofloat32_S(void* inbuffer, void* outbuffer, long frames);
+static void float32tofloat64(void* inbuffer, void* outbuffer, long frames);
+static void float64tofloat32(void* inbuffer, void* outbuffer, long frames);
+static void float32toInt16(void* inbuffer, void* outbuffer, long frames);
+static void Int16tofloat32(void* inbuffer, void* outbuffer, long frames);
+static void float32toInt24(void* inbuffer, void* outbuffer, long frames);
+static void Int24tofloat32(void* inbuffer, void* outbuffer, long frames);
+static void float32toInt32(void* inbuffer, void* outbuffer, long frames);
+static void Int32tofloat32(void* inbuffer, void* outbuffer, long frames);
+static void float32toInt16_S(void* inbuffer, void* outbuffer, long frames);
+static void Int16tofloat32_S(void* inbuffer, void* outbuffer, long frames);
+static void float32toInt24_S(void* inbuffer, void* outbuffer, long frames);
+static void Int24tofloat32_S(void* inbuffer, void* outbuffer, long frames);
+static void float32toInt32_S(void* inbuffer, void* outbuffer, long frames);
+static void Int32tofloat32_S(void* inbuffer, void* outbuffer, long frames);
+void asio_close_audio(void);
+
+typedef void converter_t(void* inbuffer, void* outbuffer, long frames);
+
+/* sample converting helper functions:
+ * - global send / receive functions
+ * - sample conversion functions (adapted from ASIOConvertSamples.cpp */
+static converter_t *asio_converter_send (ASIOSampleType format);
+static converter_t *asio_converter_receive (ASIOSampleType format);
+
+/* pointers to the converter functions of each channel are stored here */
+static converter_t **asio_converter = NULL;
+
+/* function to get sample width of data according to ASIOSampleType */
+static int asio_get_samplewidth(ASIOSampleType format);
+
+/* that's the sample width in bytes (per output channel) -
+ * it's only for silence when stopping the driver.... (please find a better solution) */
+static int *asio_samplewidth = NULL;
+
+
+/* some local helper functions */
+static void prepare_asio_drivernames();
+
+/* system dependent helper functions */
+static unsigned long get_sys_reference_time();
+
+/* global storage */
+static ASIODriverInfo * asio_driver = NULL;
+static ASIOBufferInfo * asio_bufferinfo = NULL;
+static ASIOChannelInfo* asio_channelinfo = NULL;
+static AsioTimeInfo * asio_timerinfo = NULL;
+static ASIOCallbacks asio_callbacks;
+extern AsioDrivers * asioDrivers; /* declared in asiodrivers.cpp */
+
+static char ** asio_drivernames = NULL;
+
+static ASIOSampleRate asio_srate;
+static long asio_inchannels;
+static long asio_outchannels;
+
+static long asio_minbufsize;
+static long asio_maxbufsize;
+static long asio_prefbufsize;
+static long asio_granularity;
+static unsigned char asio_useoutputready;
+static long asio_inputlatency;
+static long asio_outputlatency;
+
+static long asio_bufsize; /* hardware buffer size */
+static long asio_ticks_per_callback;
+
+unsigned long sys_reftime;
+
+/* ringbuffer stuff */
+static t_sample ** asio_ringbuffer = NULL; /* ringbuffers */
+static int asio_ringbuffer_inoffset; /* ringbuffer(in) pointer offset for dac */
+static int asio_ringbuffer_outoffset; /* ringbuffer(out) pointer offset */
+static int asio_ringbuffer_length; /* latency - hardware latency in samples*/
+
+/* i hope we can remove this to use callback based dsp scheduling */
+static pthread_mutex_t asio_ringbuf_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t asio_ringbuf_cond = PTHREAD_COND_INITIALIZER;
+
+/* some global definitions: */
+#define ASIOVERSION 2 /* i hope we are compatible with asio 2 */
+
+/* definitions from s_audio.c ... it should be save to use them */
+#define DEVDESCSIZE 80
+#define MAXNDEV 20
+
+/* from m_sched.c: */
+extern "C" double sys_time_per_dsp_tick;
+extern "C" double sys_time;
+
+
+/**************************************************************************/
+/* some function pointers for eventual fast copying when SIMD is possible */
+
+static void (*copyblock)(t_sample *dst,t_sample *src,int n);
+static void (*zeroblock)(t_sample *dst,int n);
+static t_int *(*clipblock)(t_int *w);
+
+static void copyvec_nrm(t_sample *dst,t_sample *src,int n) { memcpy(dst,src,n*sizeof(t_sample)); }
+static void zerovec_nrm(t_sample *dst,int n) { memset(dst,0,n*sizeof(t_sample)); }
+
+/*************************************************************************/
+
+
+/* open asio interface */
+/* todo: some more error messages */
+void asio_open_audio(int naudioindev, int *audioindev, int nchindev,
+int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int srate, int scheduler) {
+ ASIOError status = ASE_OK;
+ ASIOBufferInfo * buffers = NULL;
+ int i;
+ int channels;
+
+#ifdef IEEE754_64FLOAT
+ asio_srate=(ASIOSampleRate)srate;
+#else
+ sprintf(asio_srate,"%8d",srate);
+#endif
+ /* check, if the driver is still running */
+ if(asio_driver) asio_close_audio();
+ /* check, if we use the first asio device */
+ prepare_asio_drivernames();
+ asioDrivers->getDriverNames(asio_drivernames,MAXNDEV);
+
+ try {
+ asioDrivers->loadDriver(asio_drivernames[*audioindev]);
+ }
+ catch(...) {
+ error("ASIO: Error loading driver");
+ goto bailout;
+ }
+ /* initialize ASIO */
+ asio_driver = (ASIODriverInfo*) getbytes (sizeof(ASIODriverInfo));
+ asio_driver->asioVersion = ASIOVERSION;
+ asio_driver->sysRef = GetDesktopWindow();
+ status = ASIOInit(asio_driver);
+#ifdef ASIODEBUG
+ post("sysRef: %x", asio_driver->sysRef);
+ post("asioversion: %d", asio_driver->asioVersion);
+ post("driverversion: %d", asio_driver->driverVersion);
+ post("name: %s", asio_driver->name);
+#endif
+
+ switch (status) {
+ if(status) post("error: %s", asio_driver->errorMessage);
+ case ASE_NotPresent: error("ASIO: ASE_NotPresent"); goto bailout;
+ case ASE_NoMemory: error("ASIO: ASE_NoMemory"); goto bailout;
+ case ASE_HWMalfunction: error("ASIO: ASE_HWMalfunction"); goto bailout;
+ }
+#ifdef ASIODEBUG
+ post("ASIO initialized successfully");
+#endif
+
+
+ /* query driver */
+ status = ASIOGetChannels(&asio_inchannels, &asio_outchannels);
+ if(status != ASE_OK) {
+ error("ASIO: Couldn't get channel count");
+ goto bailout;
+ }
+
+#ifdef ASIODEBUG
+ post ("ASIOGetChannels\tinputs: %d, outputs: %d", asio_inchannels,
+ asio_outchannels);
+#endif
+
+ sys_inchannels = *chindev <= asio_inchannels ? *chindev : asio_inchannels;
+ sys_outchannels = *choutdev <= asio_outchannels ? *choutdev : asio_outchannels;
+ channels = sys_inchannels + sys_outchannels;
+ status = ASIOGetBufferSize(&asio_minbufsize, &asio_maxbufsize, &asio_prefbufsize, &asio_granularity);
+ if(status != ASE_OK) {
+ error("ASIO: Couldn't get buffer size");
+ goto bailout;
+ }
+#ifdef ASIODEBUG
+ post ("ASIOGetBufferSize\tmin: %d, max: %d, preferred: %d, granularity: "
+ "%d", asio_minbufsize, asio_maxbufsize, asio_prefbufsize,
+ asio_granularity);
+#endif
+
+ /* todo: buffer size hardcoded to asio hardware */
+ asio_bufsize = asio_prefbufsize;
+ if (scheduler) {
+ if ( asio_bufsize % sys_dacblocksize == 0 ) {
+ /* use callback scheduler */
+ sys_setscheduler(1);
+ asio_ticks_per_callback = asio_bufsize / sys_dacblocksize;
+ post("ASIO: using callback-based scheduler");
+ }
+ } else post("ASIO: using traditional scheduler");
+ /* try to set sample rate */
+ if(ASIOCanSampleRate( asio_srate ) != ASE_OK) {
+ error ("Samplerate not supported, using default");
+#ifdef IEEE754_64FLOAT
+ asio_srate = (ASIOSampleRate)44100.0;
+#else
+ sprintf(&asio_srate,"%8d",44100);
+#endif
+ srate=44100;
+ }
+
+ status = ASIOSetSampleRate( asio_srate );
+ if(status != ASE_OK)
+#ifdef IEEE754_64FLOAT
+ post("Setting ASIO sample rate to %lg failed... is the device in slave sync mode?", (double)asio_srate);
+#else
+ post("Setting ASIO sample rate to %s failed... is the device in slave sync mode?", asio_srate);
+#endif
+
+ if(ASIOOutputReady() == ASE_OK)
+ asio_useoutputready = 1;
+ else
+ asio_useoutputready = 0;
+
+
+ /* set callbacks */
+ if(sys_callbackscheduler) {
+ asio_callbacks.bufferSwitch = &asio_bufferSwitch_cb;
+ asio_callbacks.sampleRateDidChange = &asio_sampleRateDidChange_cb;
+ asio_callbacks.asioMessage = &asio_messages_cb;
+ asio_callbacks.bufferSwitchTimeInfo = &asio_bufferSwitchTimeInfo_cb;
+ } else {
+ asio_callbacks.bufferSwitch = &asio_bufferSwitch;
+ asio_callbacks.sampleRateDidChange = &asio_sampleRateDidChange;
+ asio_callbacks.asioMessage = &asio_messages;
+ asio_callbacks.bufferSwitchTimeInfo = &asio_bufferSwitchTimeInfo;
+ }
+ /* prepare, create and set up buffers */
+ asio_bufferinfo = (ASIOBufferInfo*) getbytes (channels*sizeof(ASIOBufferInfo));
+ asio_channelinfo = (ASIOChannelInfo*) getbytes(channels*sizeof(ASIOChannelInfo));
+ if (!(asio_bufferinfo && asio_channelinfo)) {
+ error("ASIO: couldn't allocate buffer or channel info");
+ goto bailout;
+ }
+
+ for (i = 0; i != sys_inchannels + sys_outchannels; ++i) {
+ if (i < sys_outchannels) {
+ asio_bufferinfo[i].isInput = ASIOFalse;
+ asio_bufferinfo[i].channelNum = i;
+ asio_bufferinfo[i].buffers[0] = asio_bufferinfo[i].buffers[1] = 0;
+ } else {
+ asio_bufferinfo[i].isInput = ASIOTrue;
+ asio_bufferinfo[i].channelNum = i - sys_outchannels;
+ asio_bufferinfo[i].buffers[0] = asio_bufferinfo[i].buffers[1] = 0;
+ }
+ }
+ status = ASIOCreateBuffers(asio_bufferinfo, sys_inchannels + sys_outchannels, asio_bufsize, &asio_callbacks);
+ if(status != ASE_OK) {
+ error("ASIO: couldn't allocate buffers");
+ goto bailout;
+ }
+#ifdef ASIODEBUG
+ post("ASIO: buffers allocated");
+#endif
+
+ asio_converter = (converter_t **)getbytes(channels * sizeof (converter_t *));
+ asio_samplewidth = (int *)getbytes((sys_outchannels + sys_inchannels) * sizeof (int));
+ for (i = 0; i != sys_outchannels + sys_inchannels; ++i) {
+ asio_channelinfo[i].channel = asio_bufferinfo[i].channelNum;
+ asio_channelinfo[i].isInput = asio_bufferinfo[i].isInput;
+ ASIOGetChannelInfo(&asio_channelinfo[i]);
+
+#ifdef ASIODEBUG
+ post("ASIO: channel %d type %d", i, asio_channelinfo[i].type);
+#endif
+ asio_samplewidth[i] = asio_get_samplewidth(asio_channelinfo[i].type);
+ if (i < sys_outchannels) asio_converter[i] = asio_converter_send( asio_channelinfo[i].type);
+ else asio_converter[i] = asio_converter_receive(asio_channelinfo[i].type);
+ }
+
+ /* get latencies */
+ ASIOGetLatencies(&asio_inputlatency, &asio_outputlatency);
+#ifdef ASIODEBUG
+ post("ASIO: input latency: %d, output latency: %d",asio_inputlatency, asio_outputlatency);
+#endif
+
+
+ /* we need a ringbuffer if we use the traditional scheduler */
+ if (!sys_callbackscheduler) {
+ /* a strange way to find the least common multiple, but works, since sys_dacblocksize (expt 2 x) */
+ asio_ringbuffer_length = asio_bufsize * sys_dacblocksize;
+ while ( !(asio_ringbuffer_length % sys_dacblocksize) && !(asio_ringbuffer_length % asio_bufsize)) {
+ asio_ringbuffer_length /= 2;
+ }
+ asio_ringbuffer_length *= 2;
+#ifdef ASIODEBUG
+ post("ASIO: ringbuffer size: %d",asio_ringbuffer_length);
+#endif
+ /* allocate ringbuffer */
+ asio_ringbuffer = (t_sample**) getbytes (channels * sizeof (t_sample*));
+ for (i = 0; i != channels; ++i) {
+ asio_ringbuffer[i] = (t_sample*)getalignedbytes(asio_ringbuffer_length * sizeof (t_sample));
+ if (!asio_ringbuffer[i])
+ error("ASIO: couldn't allocate ASIO ringbuffer");
+ memset(asio_ringbuffer[i], 0, asio_ringbuffer_length * sizeof (t_sample));
+ }
+ /* initialize ringbuffer pointers */
+ asio_ringbuffer_inoffset = asio_ringbuffer_outoffset = 0;
+ }
+ if(ASIOStart() != ASE_OK) goto bailout;
+ /* set block copy/zero/clip functions */
+ if(SIMD_CHKCNT(sys_dacblocksize) && simd_runtime_check()) {
+ // urgh... ugly cast
+ copyblock = (void (*)(t_sample *,t_sample *,int))&copyvec_simd;
+ zeroblock = &zerovec_simd;
+ clipblock = &clip_perf_simd;
+ } else {
+ copyblock = &copyvec_nrm;
+ zeroblock = &zerovec_nrm;
+ clipblock = &clip_perform;
+ }
+
+ post("ASIO: started");
+ return;
+
+bailout:
+ if(status) post("error: %s", asio_driver->errorMessage);
+ post("ASIO: couldn't start");
+ asio_close_audio();
+ return;
+}
+
+
+
+/* stop asio, free buffers and close asio interface */
+void asio_close_audio() {
+ if (asio_driver) {
+ pthread_cond_broadcast(&asio_ringbuf_cond);
+
+ ASIOError status;
+ int channels = sys_inchannels + sys_outchannels;
+ int i;
+
+ if(asio_useoutputready) {
+ // the DMA buffers would be played past ASIOStop
+ // -> clear output buffers and notify driver
+#if 0
+ if(asio_ringbuffer) {
+ // slow, blocking method
+ for(i = 0; i != sys_outchannels; ++i)
+ zerovec_simd(asio_ringbuffer[i], asio_ringbuffer_length);
+ // wait for bufferswitch to process silence (twice)
+ pthread_cond_wait(&asio_ringbuf_cond, &asio_ringbuf_mutex);
+ for(i = 0; i != sys_outchannels; ++i) memset(asio_ringbuffer[i], 0, asio_ringbuffer_length * sizeof (t_sample));
+ pthread_cond_wait(&asio_ringbuf_cond, &asio_ringbuf_mutex);
+ }
+#else
+ // direct method - clear both hardware buffers
+ if(asio_bufferinfo && asio_samplewidth) {
+ for(i = 0; i < sys_outchannels; ++i) {
+ long bytes = asio_bufsize*asio_samplewidth[i];
+ memset(asio_bufferinfo[i].buffers[0],0,bytes);
+ memset(asio_bufferinfo[i].buffers[1],0,bytes);
+ }
+ }
+ // notify driver
+ status = ASIOOutputReady();
+#endif
+ }
+
+ status = ASIOStop();
+ if(status == ASE_OK) post("ASIO: stopped");
+ status = ASIODisposeBuffers();
+ try {
+ // ASIOExit can crash if driver not really running
+ status = ASIOExit();
+ } catch(...) {}
+ // deallocate all memory
+ if(asio_ringbuffer) {
+ for(i = 0; i < channels; i++)
+ if(asio_ringbuffer[i]) freealignedbytes(asio_ringbuffer[i],asio_ringbuffer_length * sizeof (t_sample));
+ freebytes(asio_ringbuffer, channels * sizeof (t_sample *));
+ asio_ringbuffer = NULL;
+ }
+
+ if(asio_bufferinfo) {
+ freebytes(asio_bufferinfo, channels * sizeof (ASIOBufferInfo));
+ asio_bufferinfo = NULL;
+ }
+ if(asio_channelinfo) {
+ freebytes(asio_channelinfo, channels * sizeof (ASIOChannelInfo));
+ asio_channelinfo = NULL;
+ }
+ if(asio_converter) {
+ freebytes(asio_converter, channels * sizeof (converter_t *));
+ asio_converter = NULL;
+ }
+ if(asio_samplewidth) {
+ freebytes(asio_samplewidth, (sys_outchannels + sys_inchannels) * sizeof (int));
+ asio_samplewidth = NULL;
+ }
+ freebytes(asio_driver, sizeof (ASIODriverInfo));
+ asio_driver = NULL;
+ /* leave the scheduler and return to traditional mode */
+ if (sys_callbackscheduler) sys_setscheduler(0);
+ }
+}
+
+void asio_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) {
+ prepare_asio_drivernames();
+ *canmulti = 0; /* we will only support one asio device */
+ *nindevs = *noutdevs = (int)asioDrivers->getDriverNames(asio_drivernames, maxndev);
+ for(int i = 0; i!= *nindevs; ++i) {
+ sprintf(indevlist + i * devdescsize, "%s", asio_drivernames[i]);
+ sprintf(outdevlist + i * devdescsize, "%s", asio_drivernames[i]);
+ }
+}
+
+/* called on every dac~ send */
+int asio_send_dacs() {
+ t_sample * sp; /* sample pointer */
+ int i, j;
+ double timenow;
+ double timeref = sys_getrealtime();
+#ifdef ASIODEBUG
+ if (!asio_driver) {
+ static int written = 0;
+ if(written%100 == 0) error("ASIO not running");
+ written++;
+ return SENDDACS_NO;
+ }
+
+#endif
+
+ /* send sound to ringbuffer */
+ sp = sys_soundout;
+ for (i = 0; i < sys_outchannels; i++) {
+ t_float lo = -1.f;
+ t_float hi = 1.f;
+ t_int clipargs[6];
+ clipargs[1] = (t_int)sp;
+ clipargs[2] = (t_int)(asio_ringbuffer[i] + asio_ringbuffer_inoffset);
+ clipargs[3] = (t_int)&lo;
+ clipargs[4] = (t_int)&hi;
+ clipargs[5] = (t_int)sys_dacblocksize;
+ clipblock(clipargs);
+ zeroblock(sp,sys_dacblocksize);
+ sp+=sys_dacblocksize;
+ }
+ /* get sound from ringbuffer */
+ sp = sys_soundin;
+ for (j = 0; j < sys_inchannels; j++) {
+#if 0
+ /* we should be able to read from the ringbuffer on a different position
+ * to reduce latency for asio buffer sizes that aren't multiples of 64... */
+ int offset = asio_bufsize + sys_dacblocksize;
+ offset += sys_dacblocksize - offset % sys_dacblocksize;
+ if (asio_ringbuffer_inoffset < offset) {
+ memcpy(sp, asio_ringbuffer[i+j] + asio_ringbuffer_length +
+ asio_ringbuffer_inoffset - offset, 64 *sizeof(t_sample));
+ } else memcpy(sp, asio_ringbuffer[i+j] + asio_ringbuffer_inoffset - offset, 64*sizeof(t_sample));
+#else
+ /* working but higher latency */
+ copyblock(sp, asio_ringbuffer[i+j] + asio_ringbuffer_inoffset,sys_dacblocksize);
+#endif
+ sp+=sys_dacblocksize;
+ }
+ asio_ringbuffer_inoffset += sys_dacblocksize;
+#if 1
+ // blocking method
+ if (asio_ringbuffer_inoffset >= asio_ringbuffer_outoffset + asio_bufsize) {
+ struct timespec tm;
+ _timeb tmb;
+ _ftime(&tmb);
+ tm.tv_nsec = tmb.millitm*1000000;
+ tm.tv_sec = tmb.time+DRIVERWAIT; // delay
+ if(pthread_cond_timedwait(&asio_ringbuf_cond, &asio_ringbuf_mutex, &tm) == ETIMEDOUT) {
+ error("ASIO: ASIO driver non-responsive! - closing");
+ asio_close_audio();
+ return SENDDACS_SLEPT;
+ }
+ if (asio_ringbuffer_inoffset == asio_ringbuffer_length) {
+ asio_ringbuffer_outoffset = 0;
+ asio_ringbuffer_inoffset = 0;
+ } else asio_ringbuffer_outoffset += asio_bufsize;
+ }
+ if ((timenow = sys_getrealtime()) - timeref > 0.002) {
+ return SENDDACS_SLEPT;
+ }
+#else
+ // non-blocking... let PD wait -> doesn't work!
+ if (asio_ringbuffer_inoffset >= asio_ringbuffer_outoffset + asio_bufsize) return SENDDACS_NO;
+ if (asio_ringbuffer_inoffset == asio_ringbuffer_length) {
+ asio_ringbuffer_outoffset = 0;
+ asio_ringbuffer_inoffset = 0;
+ } else asio_ringbuffer_outoffset += asio_bufsize;
+#endif
+ return SENDDACS_YES;
+}
+
+/* buffer switch callback */
+static void asio_bufferSwitch(long db_idx, ASIOBool directprocess) {
+ ASIOTime time;
+#ifdef ASIODEBUG
+ static int written = 0;
+ if(written == 0) {
+ post("ASIO: asio_bufferSwitch_cb");
+ written = 1;
+ }
+#endif
+ memset (&time, 0, sizeof (time));
+ /* todo: do we need to syncronize with other media ??? */
+ asio_bufferSwitchTimeInfo(&time, db_idx, directprocess);
+}
+
+/* sample rate change callback */
+static void asio_sampleRateDidChange(ASIOSampleRate srate) {
+ asio_srate = srate;
+#ifdef ASIODEBUG
+ post("sample rate changed");
+#endif
+}
+
+/* asio messaging callback */
+static long asio_messages(long selector, long value, void* message, double* opt) {
+ switch (selector) {
+ case kAsioSelectorSupported:
+ if (value == kAsioResetRequest || value == kAsioSupportsTimeInfo) return 1L;
+ return 0L;
+ case kAsioEngineVersion:
+ return ASIOVERSION;
+ case kAsioResetRequest:
+ /* how to handle this without changing the dsp scheduler? */
+ post("ASIO: Reset request");
+ return 1L;
+ case kAsioBufferSizeChange:
+ /* todo */
+ post("ASIO: Buffer size changed");
+ sys_reopen_audio();
+ return 1L;
+ case kAsioResyncRequest:
+ post("ASIO: Resync request");
+ return 0L;
+ case kAsioLatenciesChanged:
+ /* we are not handling the latencies atm */
+ return 0L;
+ case kAsioSupportsTimeInfo:
+ return 1L;
+ case kAsioSupportsTimeCode:
+ /* we don't support that atm */
+ return 0L;
+ }
+ return 0L;
+}
+
+static ASIOTime *asio_bufferSwitchTimeInfo(ASIOTime *params, long db_idx, ASIOBool directprocess) {
+ /* todo: i'm not sure if we'll have to synchronize with other media ... probably yes ... */
+ /* sys_reftime = get_sys_reference_time(); */
+ /* perform the processing */
+ int timeout = sys_dacblocksize * (float)asio_ticks_per_callback / (float) sys_dacsr * 1e6;
+ if (sys_timedlock(timeout) == ETIMEDOUT) /* we're late */ {
+ post("timeout %d", timeout);
+ sys_log_error(ERR_SYSLOCK);
+ return 0;
+ }
+ for (long i = 0; i < sys_outchannels + sys_inchannels; i++) {
+ if(asio_converter[i])
+ if (asio_bufferinfo[i].isInput != ASIOTrue) {
+ asio_converter[i](asio_ringbuffer[i]+asio_ringbuffer_outoffset,
+ asio_bufferinfo[i].buffers[db_idx], asio_bufsize);
+ }
+ else /* these are the input channels */ {
+ asio_converter[i](asio_bufferinfo[i].buffers[db_idx],
+ asio_ringbuffer[i]+asio_ringbuffer_outoffset, asio_bufsize);
+ }
+ }
+ pthread_cond_broadcast(&asio_ringbuf_cond);
+ sys_unlock();
+ if(asio_useoutputready) ASIOOutputReady();
+ return 0L; /* time info!!! */
+}
+
+/* get system reference time on both platforms */
+static unsigned long get_sys_reference_time() {
+#if WINDOWS
+ return timeGetTime();
+#elif MAC
+ static const double twoRaisedTo32 = 4294967296.;
+ UnsignedWide ys;
+ Microseconds(&ys);
+ double r = ((double)ys.hi * twoRaisedTo32 + (double)ys.lo);
+ return (unsigned long)(r / 1000.);
+#endif
+}
+
+/* sample converting helper functions */
+static converter_t *asio_converter_send(ASIOSampleType format) {
+#ifdef ASIODEBUG
+ /* post("ASIO: Sample Type %d", format); */
+#endif
+ switch (format) {
+ case ASIOSTInt16LSB: return float32toInt16;
+ case ASIOSTInt24LSB: return float32toInt24; // used for 20 bits as well
+ case ASIOSTInt32LSB: return float32toInt32;
+ case ASIOSTInt16MSB: return float32toInt16_S;
+ case ASIOSTInt24MSB: return float32toInt24_S; // used for 20 bits as well
+ case ASIOSTInt32MSB: return float32toInt32_S;
+ case ASIOSTFloat32LSB:return float32tofloat32; // IEEE 754 32 bit float, as found on Intel x86 architecture
+ case ASIOSTFloat32MSB:return float32tofloat32_S;
+ case ASIOSTFloat64LSB:return float32tofloat64; // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB:
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+ case ASIOSTInt32MSB16: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ default:
+ post("Output sample Type %d not supported, yet!!!",format);
+ return NULL;
+ }
+}
+
+static converter_t *asio_converter_receive (ASIOSampleType format) {
+#ifdef ASIODEBUG
+ /* post("ASIO: Sample Type %d", format); */
+#endif
+ switch (format) {
+ case ASIOSTInt16LSB: return Int16tofloat32;
+ case ASIOSTInt24LSB: return Int24tofloat32; // used for 20 bits as well
+ case ASIOSTInt32LSB: return Int32tofloat32;
+ case ASIOSTInt16MSB: return Int16tofloat32_S;
+ case ASIOSTInt24MSB: return Int24tofloat32_S; // used for 20 bits as well
+ case ASIOSTInt32MSB: return Int32tofloat32_S;
+ case ASIOSTFloat32MSB:return float32tofloat32_S; // IEEE 754 32 bit float, as found on Intel x86 architecture
+ case ASIOSTFloat32LSB:return float32tofloat32; // IEEE 754 32 bit float, as found on Intel x86 architecture
+ case ASIOSTFloat64LSB:return float64tofloat32; // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB:
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+ case ASIOSTInt32LSB16: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+ case ASIOSTInt32MSB16: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ default:
+ post("Input sample Type %d not supported, yet!!!",format);
+ return NULL;
+ }
+}
+
+static int asio_get_samplewidth(ASIOSampleType format) {
+ switch (format) {
+ case ASIOSTInt16LSB: case ASIOSTInt16MSB: return 2;
+ case ASIOSTInt24LSB: case ASIOSTInt24MSB: return 3;
+ case ASIOSTFloat32LSB:case ASIOSTFloat32MSB:
+ case ASIOSTInt32LSB: case ASIOSTInt32MSB:
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+ case ASIOSTInt32LSB16:
+ case ASIOSTInt32LSB18:
+ case ASIOSTInt32LSB20:
+ case ASIOSTInt32LSB24:
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+ case ASIOSTInt32MSB16:
+ case ASIOSTInt32MSB18:
+ case ASIOSTInt32MSB20:
+ case ASIOSTInt32MSB24:
+ return 4;
+ case ASIOSTFloat64MSB:
+ case ASIOSTFloat64LSB:
+ return 8;
+ default:
+ post("Input sample Type %d not supported, yet!!!",format);
+ return 0;
+ }
+}
+
+/* dithering algo taken from Portaudio ASIO implementation */
+/*************************************************************
+** Calculate 2 LSB dither signal with a triangular distribution.
+** Ranged properly for adding to a 32 bit integer prior to >>15.
+*/
+#define DITHER_BITS (15)
+#define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1))
+inline static long triangulardither() {
+ static unsigned long previous = 0;
+ static unsigned long randSeed1 = 22222;
+ static unsigned long randSeed2 = 5555555;
+ long current, highPass;
+/* Generate two random numbers. */
+ randSeed1 = (randSeed1 * 196314165) + 907633515;
+ randSeed2 = (randSeed2 * 196314165) + 907633515;
+/* Generate triangular distribution about 0. */
+ current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS));
+ /* High pass filter to reduce audibility. */
+ highPass = current - previous;
+ previous = current;
+ return highPass;
+}
+
+/* sample conversion functions */
+
+#define SCALE_INT16 32767.f /* (- (expt 2 15) 1) */
+#define SCALE_INT24 8388607.f /* (- (expt 2 23) 1) */
+#define SCALE_INT32 2147483647.f /* (- (expt 2 31) 1) */
+
+/* Swap LSB to MSB and vice versa */
+inline __int32 swaplong(__int32 v) {
+ return ((v>>24)&0xFF)|((v>>8)&0xFF00)|((v&0xFF00)<<8)|((v&0xFF)<<24);
+}
+
+inline __int16 swapshort(__int16 v) {
+ return ((v>>8)&0xFF)|((v&0xFF)<<8);
+}
+
+/* todo: check dithering */
+
+static void float32tofloat32(void* inbuffer, void* outbuffer, long frames) {
+ if(SIMD_CHECK2(frames,inbuffer,outbuffer)) copyvec_simd((float *)outbuffer,(float *)inbuffer,frames);
+ else memcpy (outbuffer, inbuffer, frames* sizeof (float));
+}
+
+static void float32tofloat32_S(void* inbuffer, void* outbuffer, long frames) {
+ __int32 *in = (__int32 *)inbuffer;
+ __int32* out = (__int32*)outbuffer;
+ while (frames--) *out++ = swaplong(*(in++));
+}
+
+static void float32tofloat64(void* inbuffer, void* outbuffer, long frames) {
+ const float *in = (const float *)inbuffer;
+ double* out = (double*)outbuffer;
+ while (frames--) *(out++) = *(in++);
+}
+
+static void float64tofloat32(void* inbuffer, void* outbuffer, long frames) {
+ const double *in = (const double *)inbuffer;
+ float *out = (float *)outbuffer;
+ while (frames--) *(out++) = *(in++);
+}
+
+static void float32toInt16(void* inbuffer, void* outbuffer, long frames) {
+ const float *in = (const float *)inbuffer;
+ __int16* out = (__int16*)outbuffer;
+ while (frames--) {
+ float o = *(in++) * SCALE_INT16 + triangulardither() * DITHER_SCALE;
+ __int16 lng = lrintf(o);
+ *out++ = lng ;
+ }
+}
+
+static void Int16tofloat32(void* inbuffer, void* outbuffer, long frames) {
+ const __int16* in = (const __int16*)inbuffer;
+ float *out = (float *)outbuffer;
+ while (frames--) *(out++) = (float)*(in++) * (1.f / SCALE_INT16);
+}
+
+static void float32toInt24(void* inbuffer, void* outbuffer, long frames) {
+ const float *in = (const float *)inbuffer;
+ __int32* out = (__int32*)outbuffer;
+ while (frames--) {
+ float o = *(in++) * SCALE_INT24;
+ __int32 intg = (__int32)lrintf(o);
+ *(out++) = intg;
+ }
+}
+
+static void Int24tofloat32(void* inbuffer, void* outbuffer, long frames) {
+ const __int32* in = (const __int32*)inbuffer;
+ float *out = (float *)outbuffer;
+ while (frames--) *(out++) = (float)*(in++) * (1.f / SCALE_INT24);
+}
+
+static void float32toInt32(void* inbuffer, void* outbuffer, long frames) {
+ const float *in = (const float *)inbuffer;
+ __int32* out = (__int32*)outbuffer;
+ while (frames--) {
+ float o = (float)*(in++) * SCALE_INT32 + triangulardither() * DITHER_SCALE;
+ *out++ = lrintf(o);
+ }
+}
+
+static void Int32tofloat32(void* inbuffer, void* outbuffer, long frames) {
+ const __int32* in = (const __int32*)inbuffer;
+ float *out = (float *)outbuffer;
+ while (frames--) *(out++) = (float)*(in++) * (1.f / SCALE_INT32);
+}
+
+static void float32toInt16_S(void* inbuffer, void* outbuffer, long frames) {
+ const float *in = (const float *)inbuffer;
+ __int16* out = (__int16*)outbuffer;
+ while (frames--) {
+ float o = (float)*(in++) * SCALE_INT16 + triangulardither() * DITHER_SCALE;
+ __int16 reverse = (__int16)lrintf(o);
+ *out++ = swapshort(reverse);
+ }
+}
+
+static void Int16tofloat32_S(void* inbuffer, void* outbuffer, long frames) {
+ const __int16* in = (const __int16*)inbuffer;
+ float *out = (float *)outbuffer;
+ while (frames--) *(out++) = (float)swapshort(*(in++)) * (1.f / SCALE_INT16);
+}
+
+static void float32toInt24_S(void* inbuffer, void* outbuffer, long frames) {
+ const float *in = (const float *)inbuffer;
+ char* out = (char*)outbuffer;
+ while (frames--) {
+ float o = (float)*(in++) * SCALE_INT24;
+ __int32 reverse = (__int32)lrintf(o);
+ out[2] = ((char *)&reverse)[0];
+ out[1] = ((char *)&reverse)[1];
+ out[0] = ((char *)&reverse)[2];
+ out += 3;
+ }
+}
+
+static void Int24tofloat32_S(void* inbuffer, void* outbuffer, long frames) {
+ const char* in = (const char*)inbuffer;
+ float *out = (float *)outbuffer;
+ __int32 d = 0;
+ while (frames--) {
+ ((char *)&d)[1] = in[2];
+ ((char *)&d)[2] = in[1];
+ ((char *)&d)[3] = in[0];
+ *(out++) = (float)d * (1.f / SCALE_INT24);
+ in += 3;
+ }
+}
+
+static void float32toInt32_S(void* inbuffer, void* outbuffer, long frames) {
+ const float *in = (const float *)inbuffer;
+ __int32* out = (__int32*)outbuffer;
+ while (frames--) {
+ float o = (float)*(in++) * SCALE_INT32 + triangulardither() * DITHER_SCALE;
+ __int32 reverse = (__int32)lrintf(o);
+ *out++ = swaplong(reverse);
+ }
+}
+
+static void Int32tofloat32_S(void* inbuffer, void* outbuffer, long frames) {
+ const __int32* in = (const __int32*)inbuffer;
+ float *out = (float *)outbuffer;
+ while (frames--) *(out++) = (float)swaplong(*(in++)) * (1.f / SCALE_INT32);
+}
+
+
+/* some local helper functions */
+static void prepare_asio_drivernames() {
+ if (!asio_drivernames) {
+ asio_drivernames = (char**)getbytes(MAXNDEV * sizeof(char*));
+ for (int i = 0; i!= MAXNDEV; ++i) {
+ asio_drivernames[i] = (char*)getbytes (32 * sizeof(char));
+ }
+ }
+ /* load the driver */
+ if (!asioDrivers) asioDrivers = new AsioDrivers();
+ return;
+}
+
+/* callback-based scheduling callbacks: */
+
+/* buffer switch callback */
+static void asio_bufferSwitch_cb(long db_idx, ASIOBool directprocess) {
+ ASIOTime time;
+#ifdef ASIODEBUG
+ static int written = 0;
+ if(written == 0) {
+ post("ASIO: asio_bufferSwitch_cb");
+ written = 1;
+ }
+#endif
+ memset (&time, 0, sizeof (time));
+ /* todo: do we need to syncronize with other media ??? */
+ asio_bufferSwitchTimeInfo_cb(&time, db_idx, directprocess);
+}
+
+/* sample rate change callback */
+static void asio_sampleRateDidChange_cb(ASIOSampleRate srate) {
+ asio_sampleRateDidChange(srate);
+}
+
+/* asio messaging callback */
+static long asio_messages_cb(long selector, long value, void* message, double* opt) {
+ return asio_messages(selector, value, message, opt);
+}
+
+static ASIOTime *asio_bufferSwitchTimeInfo_cb(ASIOTime *params, long db_idx, ASIOBool directprocess) {
+ /* todo: i'm not sure if we'll have to synchronize with other media ... probably yes ... */
+ /* perform the processing */
+ int timeout = sys_dacblocksize * (float)asio_ticks_per_callback / (float) sys_dacsr * 1e6;
+ if (sys_timedlock(timeout) == ETIMEDOUT)
+ /* we're late ... lets hope that jack doesn't kick us out */
+ return 0;
+
+ for (int j = 0; j != asio_ticks_per_callback; j++) {
+ t_sample * sp = sys_soundin;
+ /* get sounds from input channels */
+ for (long i = 0; i < sys_outchannels + sys_inchannels; i++) {
+ if(asio_converter[i])
+ if (asio_bufferinfo[i].isInput == ASIOTrue) {
+ asio_converter[i]((char*)asio_bufferinfo[i].buffers[db_idx] +
+ asio_samplewidth[i] * j *sys_dacblocksize, sp, sys_dacblocksize);
+ sp += sys_dacblocksize;
+ }
+ }
+ /* run dsp */
+ sched_tick(sys_time + sys_time_per_dsp_tick);
+ sp = sys_soundout;
+ /* send sound to hardware */
+ for (long i = 0; i < sys_outchannels + sys_inchannels; i++) {
+ if (asio_bufferinfo[i].isInput != ASIOTrue) {
+ /* clip */
+ t_float lo = -1.f;
+ t_float hi = 1.f;
+ t_int clipargs[6];
+ clipargs[1] = clipargs[2] = (t_int)sp;
+ clipargs[3] = (t_int)&lo;
+ clipargs[4] = (t_int)&hi;
+ clipargs[5] = (t_int)sys_dacblocksize;
+ clipblock(clipargs);
+ /* send */
+ if(asio_converter[i])
+ asio_converter[i](sp, (char*)asio_bufferinfo[i].buffers[db_idx]
+ + asio_samplewidth[i] * j *sys_dacblocksize, sys_dacblocksize);
+ zeroblock(sp,sys_dacblocksize);
+ sp += sys_dacblocksize;
+ }
+ }
+ }
+ if(asio_useoutputready) ASIOOutputReady();
+ sys_unlock();
+ return 0L; /* time info!!! */
+}
+
+t_audioapi asio_api = {
+ asio_open_audio,
+ asio_close_audio,
+ asio_send_dacs,
+ asio_getdevs,
+};