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_pa.c | 332 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 desiredata/src/s_audio_pa.c (limited to 'desiredata/src/s_audio_pa.c') diff --git a/desiredata/src/s_audio_pa.c b/desiredata/src/s_audio_pa.c new file mode 100644 index 00000000..d1641501 --- /dev/null +++ b/desiredata/src/s_audio_pa.c @@ -0,0 +1,332 @@ +/* Copyright (c) 2001 Miller Puckette and others. +* 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 +#include +#include +#include +#include + +#ifdef MSW +/* jmz thinks that we have to include: + * on Windows: (probably this is already included?) + * on linux: (might be true for osX too, no way to check right now...) + */ +#zinclude +#else +# include +#endif + +#ifdef MSW +#include "pthread.h" /* for ETIMEDOUT */ +#endif + +#define MAX_PA_CHANS 32 + +/* portaudio's blocking api is not working, yet: */ +/* #define PABLOCKING */ + +//#ifndef PABLOCKING +#include "s_audio_pablio.h" +//#endif + +static int pa_inchans, pa_outchans; + +static PaStream *pa_stream; +static PABLIO_Stream *pablio_stream; +static PaStreamCallback *pa_callback = NULL; + +int process (const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, +PaStreamCallbackFlags statusFlags, void *userData); + +int pa_open_audio(int inchans, int outchans, int rate, int advance, int indeviceno, int outdeviceno, int schedmode) { + PaError err; + int j, devno, pa_indev = -1, pa_outdev = -1; + pa_callback = schedmode==1 ? process : NULL; + sys_setscheduler(schedmode); + /* Initialize PortAudio */ + err = Pa_Initialize(); + if (err != paNoError) { + error("Error number %d occured initializing portaudio: %s", err, Pa_GetErrorText(err)); + return 1; + } + /* 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;} + if (inchans > 0) { + for (j = 0, devno = 0; j < Pa_GetDeviceCount(); j++) { + const PaDeviceInfo *info = Pa_GetDeviceInfo(j); + int maxchans = info->maxInputChannels; + if (maxchans > 0) { + if (devno == indeviceno) { + if (maxchans < inchans) inchans = maxchans; + pa_indev = j; + break; + } + devno++; + } + } + } + if (outchans > 0) { + for (j = 0, devno = 0; j < Pa_GetDeviceCount(); j++) { + const PaDeviceInfo *info = Pa_GetDeviceInfo(j); + int maxchans = info->maxOutputChannels; + if (maxchans > 0) { + if (devno == outdeviceno) { + if (maxchans < outchans) outchans = maxchans; + pa_outdev = j; + break; + } + devno++; + } + } + } + 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) { +#ifndef PABLOCKING + if (schedmode == 1) { +#endif + PaStreamParameters inputParameters, outputParameters; + /* initialize input */ + inputParameters.device = pa_indev ; + inputParameters.channelCount = inchans; + inputParameters.sampleFormat = paFloat32 | paNonInterleaved; + inputParameters.suggestedLatency = advance * 0.001; + inputParameters.hostApiSpecificStreamInfo = NULL; + /* initialize output */ + outputParameters.device = pa_outdev; + outputParameters.channelCount = outchans; + outputParameters.sampleFormat = paFloat32 | paNonInterleaved; + outputParameters.suggestedLatency = advance * 0.001; + outputParameters.hostApiSpecificStreamInfo = NULL; + /* report to portaudio */ + err = Pa_OpenStream(&pa_stream, + ( pa_indev!=-1 ? &inputParameters : 0), + (pa_outdev!=-1 ? &outputParameters : 0), + rate, sys_dacblocksize, paClipOff, /* tb: we should be faster ;-) */ pa_callback, NULL); + if (err == paNoError) { + const PaStreamInfo * streaminfo = Pa_GetStreamInfo (pa_stream); + sys_schedadvance = 1e6 * streaminfo->outputLatency; + } +#ifndef PABLOCKING + } else { + int nbuffers = sys_advance_samples / sys_dacblocksize; + err = PD_OpenAudioStream( &pablio_stream, rate, paFloat32, + inchans, outchans, sys_dacblocksize, nbuffers, pa_indev, pa_outdev); + } +#endif + } else err = 0; + if (err != paNoError) { + post("Error number %d occured opening portaudio stream", err); + post("Error message: %s", Pa_GetErrorText(err)); + Pa_Terminate(); + sys_inchannels = sys_outchannels = 0; + return 1; + } else if (sys_verbose) post("... opened OK."); + pa_inchans = inchans; + pa_outchans = outchans; +#ifndef PABLOCKING + if (schedmode) +#endif + err = Pa_StartStream(pa_stream); + if (err != paNoError) { + post("Error number %d occured starting portaudio stream", err); + post("Error message: %s", Pa_GetErrorText(err)); + Pa_Terminate(); + sys_inchannels = sys_outchannels = 0; + return 1; + } + post("successfully started"); + return 0; +} + +void sys_peakmeters(void); +extern int sys_meters; /* true if we're metering */ + +int process (const void *input, void *output, unsigned long frameCount, +const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { + int i,j; + int timeout = (float)frameCount / (float) sys_dacsr * 1e6; + if (sys_timedlock(timeout) == ETIMEDOUT) /* we're late */ { + post("timeout %d", timeout); + sys_log_error(ERR_SYSLOCK); + return 0; + } + for (j = 0; j < sys_inchannels; j++) { + t_sample * in = ((t_sample**)input)[j]; + copyvec(sys_soundin + j * sys_dacblocksize,in,sys_dacblocksize); + } + sched_tick(sys_time + sys_time_per_dsp_tick); + for (j = 0; j < sys_outchannels; j++) { + t_sample * out = ((t_sample**)output)[j]; + copyvec(out,sys_soundout + j * sys_dacblocksize,sys_dacblocksize); + } + /* update peak meters */ + if (sys_meters) sys_peakmeters(); + /* clear the output buffer */ + zerovec(sys_soundout, pa_outchans * sys_dacblocksize); + sys_unlock(); + return 0; +} + +void pa_close_audio() { + post("closing portaudio"); + if (pa_inchans || pa_outchans) { + if (pa_stream) { + Pa_StopStream(pa_stream); + Pa_CloseStream(pa_stream); + pa_stream = NULL; + } + if (pablio_stream) { + PD_CloseAudioStream(pablio_stream); + pablio_stream = NULL; + } + } + post("portaudio closed"); + pa_inchans = pa_outchans = 0; +} + +/* for blocked IO */ +int pa_send_dacs() { +#ifdef PABLOCKING /* tb: blocking IO isn't really working for v19 yet */ + double timenow, timebefore; + if (( pa_inchans && Pa_GetStreamReadAvailable(pa_stream) < sys_dacblocksize*0.8) && + (pa_outchans && Pa_GetStreamWriteAvailable(pa_stream) < sys_dacblocksize*0.8)) { + /* we can't transfer data ... wait in the scheduler */ + return SENDDACS_NO; + } + timebefore = sys_getrealtime(); + if (pa_outchans) Pa_WriteStream(pa_stream, &sys_soundout, sys_dacblocksize); + if ( pa_inchans) Pa_ReadStream(pa_stream, &sys_soundin, sys_dacblocksize); + zerovec(sys_soundout, pa_inchans * sys_dacblocksize); + while (( pa_inchans && Pa_GetStreamReadAvailable(pa_stream) < sys_dacblocksize*0.8) && + (pa_outchans && Pa_GetStreamWriteAvailable(pa_stream) < sys_dacblocksize*0.8)) { + if (pa_outchans) Pa_WriteStream(pa_stream, &sys_soundout, sys_dacblocksize); + if ( pa_inchans) Pa_ReadStream(pa_stream, &sys_soundin, sys_dacblocksize); + zerovec(sys_soundout, pa_inchans * sys_dacblocksize); + sched_tick(sys_time + sys_time_per_dsp_tick); + } + if (sys_getrealtime() > timebefore + sys_sleepgrain * 1e-6) { + return SENDDACS_SLEPT; + } else return SENDDACS_YES; +#else /* for now we're using pablio */ + float *samples, *fp1, *fp2; + int i, j; + double timebefore; + samples=(float*)alloca(sizeof(float) * MAX_PA_CHANS * sys_dacblocksize); + timebefore = sys_getrealtime(); + if ((pa_inchans && PD_GetAudioStreamReadable(pablio_stream) < sys_dacblocksize) || + (pa_outchans && PD_GetAudioStreamWriteable(pablio_stream) < sys_dacblocksize)) { + if (pa_inchans && pa_outchans) { + int synced = 0; + while (PD_GetAudioStreamWriteable(pablio_stream) > 2*sys_dacblocksize) { + for (j = 0; j < pa_outchans; j++) + for (i = 0, fp2 = samples + j; i < sys_dacblocksize; i++, fp2 += pa_outchans) + *fp2 = 0; + synced = 1; + PD_WriteAudioStream(pablio_stream, samples, sys_dacblocksize); + } + while (PD_GetAudioStreamReadable(pablio_stream) > 2*sys_dacblocksize) { + synced = 1; + PD_ReadAudioStream(pablio_stream, samples, sys_dacblocksize); + } +/* if (synced) post("sync"); */ + } + return SENDDACS_NO; + } + if (pa_inchans) { + PD_ReadAudioStream(pablio_stream, samples, sys_dacblocksize); + for (j = 0, fp1 = sys_soundin; j < pa_inchans; j++, fp1 += sys_dacblocksize) + for (i = 0, fp2 = samples + j; i < sys_dacblocksize; i++, fp2 += pa_inchans) + fp1[i] = *fp2; + } + if (pa_outchans) { + for (j = 0, fp1 = sys_soundout; j < pa_outchans; j++, fp1 += sys_dacblocksize) + for (i = 0, fp2 = samples + j; i < sys_dacblocksize; i++, fp2 += pa_outchans) { + *fp2 = fp1[i]; + fp1[i] = 0; + } + PD_WriteAudioStream(pablio_stream, samples, sys_dacblocksize); + } + if (sys_getrealtime() > timebefore + sys_sleepgrain * 1e-6) { + /* post("slept"); */ + return SENDDACS_SLEPT; + } else return SENDDACS_YES; +#endif +} + +void pa_listdevs() /* lifted from pa_devs.c in portaudio */ { + int j, numDevices; + const PaDeviceInfo *pdi; + PaError err; + Pa_Initialize(); + numDevices = Pa_GetDeviceCount(); + if (numDevices<0) { + error("ERROR: Pa_GetDeviceCount returned %d", numDevices); + err = numDevices; + goto error; + } + post("Audio Devices:"); + for (int i=0; iname); + post("device %d:", i+1); + post(" %s;", pdi->name); + post("%d inputs, ", pdi->maxInputChannels); + post("%d outputs", pdi->maxOutputChannels); +#ifdef PA19 + if (i == Pa_GetDefaultInputDevice ()) post(" (Default Input)"); + if (i == Pa_GetDefaultOutputDevice()) post(" (Default Output)"); +#else + if (i == Pa_GetDefaultInputDeviceID ()) post(" (Default Input)"); + if (i == Pa_GetDefaultOutputDeviceID()) post(" (Default Output)"); +#endif + post(""); + } + post(""); + return; +error: + error("An error occured while using the portaudio stream: #%d: %s",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 i, nin = 0, nout = 0, ndev; + *canmulti = 1; /* one dev each for input and output */ + Pa_Initialize(); + ndev = Pa_GetDeviceCount(); + for (i = 0; i < ndev; i++) { + const PaDeviceInfo *pdi = Pa_GetDeviceInfo(i); + if (pdi->maxInputChannels > 0 && nin < maxndev) { + sprintf(indevlist + nin * devdescsize, "(%d)%s", pdi->hostApi,pdi->name); + /* strcpy(indevlist + nin * devdescsize, pdi->name); */ + nin++; + } + if (pdi->maxOutputChannels > 0 && nout < maxndev) { + sprintf(outdevlist + nout * devdescsize, "(%d)%s", pdi->hostApi,pdi->name); + /* strcpy(outdevlist + nout * devdescsize, pdi->name); */ + nout++; + } + } + *nindevs = nin; + *noutdevs = nout; +} + +t_audioapi pa_api = { + pa_open_audio, + pa_close_audio, + pa_send_dacs, + pa_getdevs, +}; -- cgit v1.2.1