aboutsummaryrefslogtreecommitdiff
path: root/desiredata/src/s_audio_pa.c
diff options
context:
space:
mode:
Diffstat (limited to 'desiredata/src/s_audio_pa.c')
-rw-r--r--desiredata/src/s_audio_pa.c332
1 files changed, 332 insertions, 0 deletions
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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <portaudio.h>
+#include <errno.h>
+
+#ifdef MSW
+/* jmz thinks that we have to include:
+ * on Windows: <malloc.h> (probably this is already included?)
+ * on linux: <alloca.h> (might be true for osX too, no way to check right now...)
+ */
+#zinclude <malloc.h>
+#else
+# include <alloca.h>
+#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; 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);
+#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,
+};