diff options
author | IOhannes m zmölnig <zmoelnig@users.sourceforge.net> | 2008-02-08 13:00:32 +0000 |
---|---|---|
committer | IOhannes m zmölnig <zmoelnig@users.sourceforge.net> | 2008-02-08 13:00:32 +0000 |
commit | 4d84d14ac1aa13958eaa2971b03f7f929a519105 (patch) | |
tree | 6579d3f2cea5410a10c4baac8d0f372fb0dff372 /desiredata/src/s_audio_portaudio.c | |
parent | b334d38aefbd8e0e159d7af6c20d63c5d2b64859 (diff) |
reorganized
svn path=/trunk/; revision=9400
Diffstat (limited to 'desiredata/src/s_audio_portaudio.c')
-rwxr-xr-x | desiredata/src/s_audio_portaudio.c | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/desiredata/src/s_audio_portaudio.c b/desiredata/src/s_audio_portaudio.c new file mode 100755 index 00000000..05b11643 --- /dev/null +++ b/desiredata/src/s_audio_portaudio.c @@ -0,0 +1,416 @@ +/* Copyright (c) 2001 Miller Puckette and others. + * Copyright (c) 2005-2006 Tim Blechmann + * 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. */ + +/* this file calls Ross Bencina's and Phil Burk's Portaudio package. It's + the main way in for Mac OS and, with Michael Casey's help, also into + ASIO in Windows. */ + +/* tb: requires portaudio >= V19 */ + +#include "m_pd.h" +#include "s_stuff.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <portaudio.h> +#include <errno.h> +#include "assert.h" +#ifdef MSW +# include <malloc.h> +# include <pa_asio.h> +#else +# include <alloca.h> +#endif +#include "pthread.h" +/* for M_PI */ +#if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES) +#define _USE_MATH_DEFINES +#endif +#include <math.h> +#define MAX_PA_CHANS 32 + +static int pa_inchans, pa_outchans; +static int pa_blocksize; + +static PaStream *pa_stream; +/* Initialize PortAudio */ +PaError pa_status = -1; +int pa_initialized = 0; + +void pa_initialize() { +// if (pa_initialized) return; + pa_status = Pa_Initialize(); + if (pa_status!=paNoError) { + error("Error number %d occured initializing portaudio: %s", pa_status, Pa_GetErrorText(pa_status)); + return; + } + pa_initialized = 1; +} + +static float* pa_inbuffer[MAX_PA_CHANS]; +static float* pa_outbuffer[MAX_PA_CHANS]; +static int pa_bufferpos; +static int pddev2padev(int pdindev,int isinput); +static int padev2pddev(int padev,int isinput); +int process (const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, void *userData); + +static int pa_indev = -1, pa_outdev = -1; + +int pa_open_audio(int inchans, int outchans, int rate, int advance, +int indeviceno, int outdeviceno, int schedmode) { + PaError err; + const PaDeviceInfo *pdi,*pdo; + schedmode = 1; /* we don't support blocking io */ + pa_initialize(); + sys_setscheduler(schedmode); + /* post("in %d out %d rate %d device %d", inchans, outchans, rate, deviceno); */ + if ( inchans > MAX_PA_CHANS) {post( "input channels reduced to maximum %d", MAX_PA_CHANS); inchans = MAX_PA_CHANS;} + if (outchans > MAX_PA_CHANS) {post("output channels reduced to maximum %d", MAX_PA_CHANS); outchans = MAX_PA_CHANS;} + pdi = NULL; + if (inchans > 0) { + pa_indev = pddev2padev(indeviceno,1); + if(pa_indev >= 0) { + pdi = Pa_GetDeviceInfo(pa_indev); + if(pdi->maxInputChannels < inchans) inchans = pdi->maxInputChannels; + } + } + pdo = NULL; + if (outchans > 0) { + pa_outdev = pddev2padev(outdeviceno,0); + if(pa_outdev >= 0) { + pdo = Pa_GetDeviceInfo(pa_outdev); + if(pdo->maxOutputChannels < outchans) outchans = pdo->maxOutputChannels; + } + } + if (sys_verbose) { + post("input device %d, channels %d", pa_indev, inchans); + post("output device %d, channels %d", pa_outdev, outchans); + post("latency advance %d", advance); + } + if (inchans || outchans) { + int blocksize; + PaStreamParameters iparam,oparam; + /* initialize input */ + iparam.device = pa_indev; + iparam.channelCount = inchans; + iparam.sampleFormat = paFloat32 | paNonInterleaved; + iparam.suggestedLatency = advance * 0.001; + iparam.hostApiSpecificStreamInfo = NULL; + /* initialize output */ + oparam.device = pa_outdev; + oparam.channelCount = outchans; + oparam.sampleFormat = paFloat32 | paNonInterleaved; + oparam.suggestedLatency = advance * 0.001; + oparam.hostApiSpecificStreamInfo = NULL; + /* set block size */ + blocksize=64; + while ((float)blocksize/(float)rate*1000*2 < advance && blocksize==1024) blocksize *= 2; + pa_blocksize = blocksize; + /* initialize io buffer */ + for (int j=0; j != MAX_PA_CHANS;++j) { + if (pa_inbuffer[j]) freealignedbytes(pa_inbuffer[j], 0); + if (pa_outbuffer[j]) freealignedbytes(pa_outbuffer[j], 0); + pa_inbuffer[j] = (float *)getalignedbytes((blocksize + sys_dacblocksize)*sizeof(float)); + pa_outbuffer[j] = (float *)getalignedbytes((blocksize + sys_dacblocksize)*sizeof(float)); + } + pa_bufferpos = 0; + /* report to portaudio */ + err = Pa_OpenStream(&pa_stream, + (( pa_indev!=-1) ? &iparam : 0), + ((pa_outdev!=-1) ? &oparam : 0), + rate, pa_blocksize, paClipOff, /* tb: we should be faster ;-) */ process /* patestCallback */, NULL); + if (err == paNoError) { + const PaStreamInfo *streaminfo = Pa_GetStreamInfo(pa_stream); + t_atom atoms[4]; + t_symbol *pd = gensym("pd"); + t_symbol *selector1 = gensym("audiocurrentininfo"); + t_symbol *selector2 = gensym("audiocurrentoutinfo"); + sys_schedadvance = int(1e-6 * streaminfo->outputLatency); + + SETFLOAT(atoms, (float)indeviceno); + SETFLOAT(atoms+1, (float)inchans); + SETFLOAT(atoms+2, (float)rate); + SETFLOAT(atoms+3, (float)streaminfo->inputLatency * 1000.f); + typedmess(pd->s_thing, selector1, 4, atoms); + + SETFLOAT(atoms, (float)outdeviceno); + SETFLOAT(atoms+1, (float)outchans); + SETFLOAT(atoms+2, (float)rate); + SETFLOAT(atoms+3, (float)streaminfo->outputLatency * 1000.f); + typedmess(pd->s_thing, selector2, 4, atoms); + } + } else err = 0; + + if (err != paNoError) { + error("Error number %d occured opening portaudio stream: %s", err, Pa_GetErrorText(err)); + sys_inchannels = sys_outchannels = 0; + pa_indev = pa_outdev = -1; + pa_inchans = pa_outchans = 0; + return 1; + } else if (sys_verbose) post("... opened OK."); + pa_inchans = inchans; + pa_outchans = outchans; + + /* we might have adapted the channel count */ + sys_setchsr(inchans, outchans, rate, sys_dacblocksize); + err = Pa_StartStream(pa_stream); + if (err!=paNoError) { + post("Error number %d occured starting portaudio stream: %s", err, Pa_GetErrorText(err)); + sys_inchannels = sys_outchannels = 0; + return 1; + } + if(sys_verbose) post("successfully started"); + return 0; +} + +void sys_peakmeters(); +extern int sys_meters; /* true if we're metering */ + +void run_all_idle_callbacks(); +void sys_xrun_notification(); /* in m_sched.c */ +void sys_lock_timeout_notification(); + +int process (const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, +PaStreamCallbackFlags statusFlags, void *userData) { + int timeout = int((float)frameCount / (float) sys_dacsr * 1e6); + if (statusFlags) sys_xrun_notification(); + if (sys_timedlock(timeout) == ETIMEDOUT) /* we're late */ { + sys_lock_timeout_notification(); + return 0; + } + for (int i=0; (unsigned)i < frameCount / sys_dacblocksize; ++i) { + for (int j=0; j < sys_inchannels; j++) { + t_sample *in = ((t_sample**)input)[j] + i * sys_dacblocksize; + copyvec(sys_soundin + j * sys_dacblocksize, in, sys_dacblocksize); + } + sched_tick(sys_time + sys_time_per_dsp_tick); + for (int j=0; j < sys_outchannels; j++) { + t_sample *out = ((t_sample**)output)[j] + i * sys_dacblocksize; + copyvec(out, sys_soundout + j * sys_dacblocksize, sys_dacblocksize); + } + if (sys_meters) sys_peakmeters(); + zerovec(sys_soundout, pa_outchans * sys_dacblocksize); + } + run_all_idle_callbacks(); + sys_unlock(); + return 0; +} + +void pa_close_audio() { + if(sys_verbose) post("closing portaudio"); + if (pa_inchans || pa_outchans) { + if (pa_stream) { + int status = Pa_StopStream(pa_stream); + if (status) post("error closing audio: %d", status); + Pa_CloseStream(pa_stream); + pa_stream = NULL; + } + } + sys_setscheduler(0); + if(sys_verbose) post("portaudio closed"); + pa_inchans = pa_outchans = 0; + pa_indev = pa_outdev = -1; +} + +/* for blocked IO */ +int pa_send_dacs() { + /* we don't support blocking i/o */ + return SENDDACS_NO; +} + +/* lifted from pa_devs.c in portaudio */ +void pa_listdevs() { + PaError err; + pa_initialize(); + int numDevices = Pa_GetDeviceCount(); + if(numDevices < 0) { + error("ERROR: Pa_GetDeviceCount returned %d", numDevices); + err = numDevices; + goto error; + } + post("Audio Devices:"); + for(int i=0; i<numDevices; i++) { + const PaDeviceInfo *pdi = Pa_GetDeviceInfo(i); + post ("device %s", pdi->name); + post("device %d:", i+1); + post(" %s;", pdi->name); + post("%d inputs, ", pdi->maxInputChannels); + post("%d outputs ", pdi->maxOutputChannels); + if (i == Pa_GetDefaultInputDevice()) post(" (Default Input)"); + if (i == Pa_GetDefaultOutputDevice()) post(" (Default Output)"); + post(""); + } + post(""); + return; + error: + error("Error #%d occurred while using the portaudio stream: %s\n", err, Pa_GetErrorText(err)); +} + +/* scanning for devices */ +void pa_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { + int nin = 0, nout = 0, ndev; + *canmulti = 1; /* one dev each for input and output */ + pa_initialize(); + ndev = Pa_GetDeviceCount(); + for (int i=0; i<ndev; i++) { + const PaDeviceInfo *pdi = Pa_GetDeviceInfo(i); + if (pdi->maxInputChannels > 0 && nin < maxndev) { + PaHostApiIndex api = pdi->hostApi; + const PaHostApiInfo *info = Pa_GetHostApiInfo(api); + const char *apiName = info->name; + unsigned int apiNameLen = strlen(apiName); + strcpy(indevlist + nin * devdescsize, apiName); + indevlist[nin * devdescsize + apiNameLen] = '/'; + strcpy(indevlist + nin * devdescsize + apiNameLen + 1, pdi->name); + nin++; + } + if (pdi->maxOutputChannels > 0 && nout < maxndev) { + PaHostApiIndex api = pdi->hostApi; + const PaHostApiInfo *info = Pa_GetHostApiInfo(api); + const char *apiName = info->name; + unsigned int apiNameLen = strlen(apiName); + strcpy(outdevlist + nout * devdescsize, apiName); + outdevlist[nout * devdescsize + apiNameLen] = '/'; + strcpy(outdevlist + nout * devdescsize + apiNameLen + 1, pdi->name); + nout++; + } + } + *nindevs = nin; + *noutdevs = nout; +} + +void pa_getaudioininfo(t_float f) { + int i = pddev2padev((int)f,1); + const PaDeviceInfo *pdi; + pa_initialize(); + pdi = Pa_GetDeviceInfo(i); + if (pdi) { + t_symbol *selector = gensym("audioininfo"); + t_symbol *pd = gensym("pd"); + t_atom argv[4]; + SETFLOAT(argv, pdi->maxInputChannels); + SETFLOAT(argv+1, pdi->defaultSampleRate); + SETFLOAT(argv+2, pdi->defaultLowInputLatency*1000.f); + SETFLOAT(argv+3, pdi->defaultHighInputLatency*1000.f); + typedmess(pd->s_thing, selector, 4, argv); + } +} + +void pa_getaudiooutinfo(t_float f) { + int i = pddev2padev((int)f,0); + const PaDeviceInfo *pdi; + pa_initialize(); + pdi = Pa_GetDeviceInfo(i); + if (pdi) { + t_symbol *selector = gensym("audiooutinfo"); + t_symbol *pd = gensym("pd"); + t_atom argv[4]; + SETFLOAT(argv, pdi->maxOutputChannels); + SETFLOAT(argv+1, pdi->defaultSampleRate); + SETFLOAT(argv+2, pdi->defaultLowOutputLatency*1000.f); + SETFLOAT(argv+3, pdi->defaultHighOutputLatency*1000.f); + typedmess(pd->s_thing, selector, 4, argv); + } +} + +void pa_getcurrent_devices() { + t_symbol *pd = gensym("pd"); + t_symbol *selector = gensym("audiodevice"); + t_atom argv[2]; + SETFLOAT(argv, padev2pddev(pa_indev,1)); + SETFLOAT(argv+1, padev2pddev(pa_outdev,0)); + typedmess(pd->s_thing, selector, 2, argv); +} + +void pa_test_setting (int ac, t_atom *av) { + int indev = atom_getintarg(0, ac, av); + int outdev = atom_getintarg(1, ac, av); + int samplerate = atom_getintarg(2, ac, av); + int inchans = atom_getintarg(3, ac, av); + int outchans = atom_getintarg(4, ac, av); + int advance = atom_getintarg(5, ac, av); + t_symbol *pd = gensym("pd"); + t_symbol *selector = gensym("testaudiosettingresult"); + t_atom argv[1]; + pa_initialize(); + indev = pddev2padev(indev,1); + outdev = pddev2padev(outdev,0); + if (pa_indev==-1 && pa_outdev==-1) { + int ret; + PaStreamParameters iparam, oparam; + iparam.device = indev; + iparam.channelCount = inchans; + iparam.sampleFormat = paFloat32 | paNonInterleaved; + iparam.suggestedLatency = advance * 0.001; + iparam.hostApiSpecificStreamInfo = NULL; + oparam.device = outdev; + oparam.channelCount = outchans; + oparam.sampleFormat = paFloat32 | paNonInterleaved; + oparam.suggestedLatency = advance * 0.001; + oparam.hostApiSpecificStreamInfo = NULL; + ret = Pa_IsFormatSupported(&iparam, &oparam, samplerate); + SETFLOAT(argv, ret == paNoError?1:0); + typedmess(pd->s_thing, selector, 1, argv); + } +} + +static int pddev2padev(int pddev,int input) { + pa_initialize(); + for (int j=0, devno=0; j < Pa_GetDeviceCount(); j++) { + const PaDeviceInfo *info = Pa_GetDeviceInfo(j); + int maxchans = input?info->maxInputChannels:info->maxOutputChannels; + if (maxchans > 0) { + if (devno == pddev) return j; + devno++; + } + } + return -1; +} + +static int padev2pddev(int padev,int input) { + int count = Pa_GetDeviceCount(); + for (int j=0, devno=0; j < count; j++) { + const PaDeviceInfo *info = Pa_GetDeviceInfo(j); + int chans = input?info->maxInputChannels:info->maxOutputChannels; + if (chans > 0) { + if(j == padev) return devno; + devno++; + } + } + return -1; // no found +} + +void pa_get_asio_latencies(t_float f) { + int index = pddev2padev((int)f,0); + const PaDeviceInfo *pdi = Pa_GetDeviceInfo(index); + const PaHostApiInfo *phi = Pa_GetHostApiInfo(pdi->hostApi); + if (phi->type != paASIO) { + post("device not an asio device"); + return; + } +#ifdef WIN32 + else { + long minlat, maxlat, preflat, gran; + t_atom argv[4]; + t_symbol *selector = gensym("asiolatency"); + t_symbol *pd = gensym("pd"); + PaAsio_GetAvailableLatencyValues(index, &minlat, &maxlat, &preflat, &gran); + SETFLOAT(argv, (float) minlat); + SETFLOAT(argv + 1, (float) maxlat); + SETFLOAT(argv + 2, (float) preflat); + SETFLOAT(argv + 3, (float) gran); + typedmess(pd->s_thing, selector, 4, argv); + } +#endif +} + +t_audioapi pa_api = { + 0 /* pa_open_audio */, + pa_close_audio, + pa_send_dacs, + pa_getdevs, +}; |