/* 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. For the polling scheduler, we call portaudio in callback mode and manage our own FIFO, so we can offer Pd "blocking" I/O calls. We can choose between two methods for waiting on the I/O to complete, either correct thread synchronization (by defining THREADSIGNAL) or just sleeping and polling. For the callback scheduler, we call portaudio in callback mode and simply provide a callback function that ticks the scheduler, polls the GUI, etc. */ #include "m_pd.h" #include "s_stuff.h" #include "s_utf8.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <portaudio.h> #ifdef _WIN32 #include <pa_win_wasapi.h> #endif #include "m_private_utils.h" #include "s_audio_paring.h" #ifndef _WIN32 /* for the "dup2" workaround -- do we still need it? */ #include <unistd.h> #endif /* enable proper thread synchronization instead of polling */ #if 1 #define THREADSIGNAL #endif /* LATER try to figure out how to handle default devices in portaudio; the way s_audio.c handles them isn't going to work here. */ /* public interface declared in m_imp.h */ /* implementation */ static PaStream *pa_stream; static int pa_inchans, pa_outchans; static t_sample *pa_soundin, *pa_soundout; static t_audiocallback pa_callback; static int pa_started; static volatile int pa_dio_error; static char *pa_outbuf; static sys_ringbuf pa_outring; static char *pa_inbuf; static sys_ringbuf pa_inring; #ifdef THREADSIGNAL t_semaphore *pa_sem; #endif /* number of seconds to wait before checking the audio device (and possibly trying to reopen it). */ #ifndef POLL_TIMEOUT #define POLL_TIMEOUT … #endif static double pa_lastdactime; static void pa_init(void) /* Initialize PortAudio */ { … } static int pa_lowlevel_callback(const void *inputBuffer, void *outputBuffer, unsigned long nframes, const PaStreamCallbackTimeInfo *outTime, PaStreamCallbackFlags myflags, void *userData) { … } /* callback for "non-callback" case in which we actually open portaudio in callback mode but fake "blocking mode". We communicate with the main thread via two FIFOs: the audio input buffer is copied to our input FIFO and the audio output buffer is filled with samples from our output FIFO. If the input FIFO is full or the output FIFO is empty, the callback does not wait for the scheduler; instead, the input buffer is discarded and the output buffer is filled with zeros. After each scheduler tick, the main thread tries to drain the input FIFO and fill the output FIFO. It can either wait on a semaphore, or poll in regular intervals; the first option should be more responsive. */ static int pa_fifo_callback(const void *inputBuffer, void *outputBuffer, unsigned long nframes, const PaStreamCallbackTimeInfo *outTime, PaStreamCallbackFlags myflags, void *userData) { … } PaError pa_open_callback(double samplerate, int inchannels, int outchannels, int framesperbuf, int indeviceno, int outdeviceno, PaStreamCallback *callbackfn) { … } int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin, t_sample *soundout, int framesperbuf, int nbuffers, int indeviceno, int outdeviceno, t_audiocallback callbackfn) { … } void pa_close_audio(void) { … } void sys_do_close_audio(void); void sys_do_reopen_audio(void); int pa_reopen_audio(void) { … } int sched_idletask(void); int pa_send_dacs(void) { … } static char*pdi2devname(const PaDeviceInfo*pdi, char*buf, size_t bufsize) { … } /* scanning for devices */ void pa_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { … }