From 4d84d14ac1aa13958eaa2971b03f7f929a519105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 8 Feb 2008 13:00:32 +0000 Subject: reorganized svn path=/trunk/; revision=9400 --- desiredata/src/s_audio_asio.cpp | 1014 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1014 insertions(+) create mode 100644 desiredata/src/s_audio_asio.cpp (limited to 'desiredata/src/s_audio_asio.cpp') 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 +#include + +#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))©vec_simd; + zeroblock = &zerovec_simd; + clipblock = &clip_perf_simd; + } else { + copyblock = ©vec_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<>(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, +}; -- cgit v1.2.1