From c84701024f03c241fb915edb33a45a11463ee339 Mon Sep 17 00:00:00 2001 From: Miller Puckette <msp@ucsd.edu> Date: Wed, 15 Aug 2007 10:37:44 -0700 Subject: [PATCH] first cut at callback scheduler --- src/m_sched.c | 58 ++++++++++++++----- src/s_audio.c | 143 +++++++++++++++++++++++----------------------- src/s_audio_pa.c | 146 +++++++++++++++++++++++++++++++++++++++++++---- src/s_file.c | 4 +- src/s_main.c | 10 ++-- src/s_stuff.h | 16 ++++-- 6 files changed, 269 insertions(+), 108 deletions(-) diff --git a/src/m_sched.c b/src/m_sched.c index bce50cd5b..b69d8f60b 100644 --- a/src/m_sched.c +++ b/src/m_sched.c @@ -15,7 +15,8 @@ #define THREAD_LOCKING #include "pthread.h" - +#define SYS_QUIT_QUIT 1 +#define SYS_QUIT_RESTART 2 static int sys_quit; double sys_time; static double sys_time_per_msec = TIMEUNITPERSEC / 1000.; @@ -326,14 +327,14 @@ void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) void dsp_tick(void); -static int sched_usedacs = 1; +static int sched_useaudio = SCHED_AUDIO_POLL; static double sched_referencerealtime, sched_referencelogicaltime; double sys_time_per_dsp_tick; -void sched_set_using_dacs(int flag) +void sched_set_using_audio(int flag) { - sched_usedacs = flag; - if (!flag) + sched_useaudio = flag; + if (flag == SCHED_AUDIO_NONE) { sched_referencerealtime = sys_getrealtime(); sched_referencelogicaltime = clock_getlogicaltime(); @@ -385,7 +386,7 @@ nonzero if you actually used the time; otherwise we're really really idle and will now sleep. */ int (*sys_idlehook)(void); -int m_scheduler( void) +static void m_pollingscheduler( void) { int idlecount = 0; sys_time_per_dsp_tick = (TIMEUNITPERSEC) * @@ -410,7 +411,7 @@ int m_scheduler( void) sys_addhist(0); waitfortick: - if (sched_usedacs) + if (sched_useaudio != SCHED_AUDIO_NONE) { #ifdef THREAD_LOCKING /* T.Grill - send_dacs may sleep -> @@ -441,7 +442,7 @@ int m_scheduler( void) { post("audio I/O stuck... closing audio\n"); sys_close_audio(); - sched_set_using_dacs(0); + sched_set_using_audio(SCHED_AUDIO_NONE); goto waitfortick; } } @@ -475,7 +476,6 @@ int m_scheduler( void) { sched_pollformeters(); sys_reportidle(); - #ifdef THREAD_LOCKING sys_unlock(); /* unlock while we idle */ #endif @@ -489,20 +489,52 @@ int m_scheduler( void) #ifdef THREAD_LOCKING sys_lock(); #endif - sys_addhist(5); sched_didnothing++; - } } #ifdef THREAD_LOCKING sys_unlock(); #endif +} - return (0); +void sched_audio_callbackfn(void) +{ + sys_setmiditimediff(0, 1e-6 * sys_schedadvance); + sys_addhist(1); + sched_tick(sys_time + sys_time_per_dsp_tick); + sys_addhist(2); + sys_pollmidiqueue(); + sys_addhist(3); + sys_pollgui(); + sys_addhist(5); + sched_pollformeters(); + sys_addhist(0); +} + +static void m_callbackscheduler(void) +{ + sys_initmidiqueue(); + while (1) + { + sleep(1); + if (sys_idlehook) + sys_idlehook(); + } } +int m_mainloop(void) +{ + while (sys_quit != SYS_QUIT_QUIT) + { + post("sched %d", sched_useaudio); + if (sched_useaudio == SCHED_AUDIO_CALLBACK) + m_callbackscheduler(); + else m_pollingscheduler(); + } + return (0); +} /* ------------ thread locking ------------------- */ @@ -534,5 +566,5 @@ int sys_trylock(void) {} void sys_exit(void) { - sys_quit = 1; + sys_quit = SYS_QUIT_QUIT; } diff --git a/src/s_audio.c b/src/s_audio.c index ed4c9776d..b33c85a55 100644 --- a/src/s_audio.c +++ b/src/s_audio.c @@ -67,7 +67,7 @@ static int audio_rate; static int audio_advance; static int audio_callback; -void sched_set_callback(int flag); +void sched_audio_callbackfn(void); static int audio_isopen(void) { @@ -170,14 +170,14 @@ void sys_setchsr(int chin, int chout, int sr) /* ----------------------- public routines ----------------------- */ - /* open audio devices (after cleaning up the specified device and channel - vectors). The audio devices are "zero based" (i.e. "0" means the first - one.) We also save the cleaned-up device specification so that we - can later re-open audio and/or show the settings on a dialog window. */ + /* set audio device settings (after cleaning up the specified device and + channel vectors). The audio devices are "zero based" (i.e. "0" means the + first one.) We can later re-open audio and/or show the settings on a\ + dialog window. */ -void sys_open_audio(int naudioindev, int *audioindev, int nchindev, +void sys_set_audio_settings(int naudioindev, int *audioindev, int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, - int *choutdev, int rate, int advance, int callback, int enable) + int *choutdev, int rate, int advance, int callback) { int i, *ip; int defaultchannels = SYS_DEFAULTCH; @@ -189,7 +189,6 @@ void sys_open_audio(int naudioindev, int *audioindev, int nchindev, int indevs = 0, outdevs = 0, canmulti = 0, cancallback = 0; audio_getdevs(indevlist, &indevs, outdevlist, &outdevs, &canmulti, &cancallback, MAXNDEV, DEVDESCSIZE); - if (sys_externalschedlib) { return; @@ -324,62 +323,11 @@ void sys_open_audio(int naudioindev, int *audioindev, int nchindev, outchans += choutdev[i]; nrealoutdev++; } - /* if no input or output devices seem to have been specified, - this really means just disable audio, which we now do. */ - if (!inchans && !outchans) - enable = 0; sys_schedadvance = advance * 1000; sys_setchsr(inchans, outchans, rate); sys_log_error(ERR_NOTHING); - if (enable) - { -#ifdef USEAPI_PORTAUDIO - if (sys_audioapi == API_PORTAUDIO) - { - int blksize = (sys_blocksize ? sys_blocksize : 64); - pa_open_audio(inchans, outchans, rate, sys_soundin, sys_soundout, - blksize, sys_advance_samples/blksize, callback, - (naudiooutdev > 0 ? audioindev[0] : 0), - (naudiooutdev > 0 ? audiooutdev[0] : 0)); - } -else -#endif -#ifdef USEAPI_JACK - if (sys_audioapi == API_JACK) - jack_open_audio((nrealindev > 0 ? realinchans[0] : 0), - (nrealoutdev > 0 ? realoutchans[0] : 0), rate); - - else -#endif -#ifdef USEAPI_OSS - if (sys_audioapi == API_OSS) - oss_open_audio(nrealindev, realindev, nrealindev, realinchans, - nrealoutdev, realoutdev, nrealoutdev, realoutchans, rate); - else -#endif -#ifdef USEAPI_ALSA - /* for alsa, only one device is supported; it may - be open for both input and output. */ - if (sys_audioapi == API_ALSA) - alsa_open_audio(nrealindev, audioindev, nrealindev, realinchans, - nrealoutdev, audiooutdev, nrealoutdev, realoutchans, rate); - else -#endif -#ifdef USEAPI_MMIO - if (sys_audioapi == API_MMIO) - mmio_open_audio(nrealindev, audioindev, nrealindev, realinchans, - nrealoutdev, audiooutdev, nrealoutdev, realoutchans, rate); - else -#endif - post("unknown audio API specified"); - } - sys_save_audio_params(naudioindev, audioindev, chindev, - naudiooutdev, audiooutdev, choutdev, sys_dacsr, advance, callback); - if (sys_inchannels == 0 && sys_outchannels == 0) - enable = 0; - audio_state = enable; - sys_vgui("set pd_whichapi %d\n", (audio_isopen() ? sys_audioapi : 0)); - sched_set_using_dacs(enable); + sys_save_audio_params(nrealindev, realindev, realinchans, + nrealoutdev, realoutdev, realoutchans, sys_dacsr, advance, callback); } void sys_close_audio(void) @@ -417,6 +365,7 @@ void sys_close_audio(void) #endif post("sys_close_audio: unknown API %d", sys_audioapi); sys_inchannels = sys_outchannels = 0; + sched_set_using_audio(SCHED_AUDIO_NONE); } /* open audio using whatever parameters were last used */ @@ -424,12 +373,67 @@ void sys_reopen_audio( void) { int naudioindev, audioindev[MAXAUDIOINDEV], chindev[MAXAUDIOINDEV]; int naudiooutdev, audiooutdev[MAXAUDIOOUTDEV], choutdev[MAXAUDIOOUTDEV]; - int rate, advance, callback; + int rate, advance, callback, outcome = 0; sys_get_audio_params(&naudioindev, audioindev, chindev, &naudiooutdev, audiooutdev, choutdev, &rate, &advance, &callback); - sys_open_audio(naudioindev, audioindev, naudioindev, chindev, - naudiooutdev, audiooutdev, naudiooutdev, choutdev, rate, advance, - callback, 1); + if (!naudioindev && !naudiooutdev) + { + sched_set_using_audio(SCHED_AUDIO_NONE); + return; + } +#ifdef USEAPI_PORTAUDIO + if (sys_audioapi == API_PORTAUDIO) + { + int blksize = (sys_blocksize ? sys_blocksize : 64); + outcome = pa_open_audio((naudioindev > 0 ? chindev[0] : 0), + (naudiooutdev > 0 ? choutdev[0] : 0), rate, sys_soundin, + sys_soundout, blksize, sys_advance_samples/blksize, + (naudioindev > 0 ? audioindev[0] : 0), + (naudiooutdev > 0 ? audiooutdev[0] : 0), + (callback ? sched_audio_callbackfn : 0)); + } + else +#endif +#ifdef USEAPI_JACK + if (sys_audioapi == API_JACK) + outcome = jack_open_audio((naudioindev > 0 ? chindev[0] : 0), + (naudioindev > 0 ? choutdev[0] : 0), rate); + + else +#endif +#ifdef USEAPI_OSS + if (sys_audioapi == API_OSS) + outcome = oss_open_audio(naudioindev, audioindev, naudioindev, + chindev, naudiooutdev, audiooutdev, naudiooutdev, choutdev, rate); + else +#endif +#ifdef USEAPI_ALSA + /* for alsa, only one device is supported; it may + be open for both input and output. */ + if (sys_audioapi == API_ALSA) + outcome = alsa_open_audio(naudioindev, audioindev, naudioindev, + chindev, naudiooutdev, audiooutdev, naudiooutdev, choutdev, rate); + else +#endif +#ifdef USEAPI_MMIO + if (sys_audioapi == API_MMIO) + outcome = mmio_open_audio(naudioindev, audioindev, naudioindev, + chindev, naudiooutdev, audiooutdev, naudiooutdev, choutdev, rate); + else +#endif + post("unknown audio API specified"); + if (outcome) /* failed */ + { + audio_state = 0; + sched_set_using_audio(SCHED_AUDIO_NONE); + } + else + { + audio_state = 1; + sched_set_using_audio( + (callback ? SCHED_AUDIO_CALLBACK : SCHED_AUDIO_POLL)); + } + sys_vgui("set pd_whichapi %d\n", (outcome == 0 ? sys_audioapi : 0)); } int sys_send_dacs(void) @@ -727,9 +731,10 @@ void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) } sys_close_audio(); - sys_open_audio(nindev, newaudioindev, nindev, newaudioinchan, + sys_set_audio_settings(nindev, newaudioindev, nindev, newaudioinchan, noutdev, newaudiooutdev, noutdev, newaudiooutchan, - newrate, newadvance, newcallback, 1); + newrate, newadvance, newcallback); + sys_reopen_audio(); } void sys_listdevs(void ) @@ -807,7 +812,6 @@ void glob_audio_setapi(void *dummy, t_floatarg f) { sys_close_audio(); audio_state = 0; - sched_set_using_dacs(0); } } @@ -822,10 +826,7 @@ void sys_set_audio_state(int onoff) else { if (audio_isopen()) - { sys_close_audio(); - sched_set_using_dacs(0); - } } audio_state = onoff; } diff --git a/src/s_audio_pa.c b/src/s_audio_pa.c index 5f2af7f39..c89d463e6 100644 --- a/src/s_audio_pa.c +++ b/src/s_audio_pa.c @@ -23,19 +23,133 @@ static PABLIO_Stream *pa_stream; static int pa_inchans, pa_outchans; static float *pa_soundin, *pa_soundout; +static t_audiocallback pa_callback; #define MAX_PA_CHANS 32 #define MAX_SAMPLES_PER_FRAME MAX_PA_CHANS * DEFDACBLKSIZE +static int pa_lowlevel_callback(const void *inputBuffer, + void *outputBuffer, unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *outTime, PaStreamCallbackFlags myflags, + void *userData) +{ + int i; + unsigned int j; + float *fbuf, *fp2, *fp3, *soundiop; + + if (inputBuffer != NULL) + { + fbuf = (float *)inputBuffer; + soundiop = pa_soundin; + for (i = 0, fp2 = fbuf; i < pa_inchans; i++, fp2++) + for (j = 0, fp3 = fp2; j < framesPerBuffer; j++, fp3 += pa_inchans) + *soundiop++ = *fp3; + } + else memset((void *)pa_soundin, 0, + framesPerBuffer * pa_inchans * sizeof(float)); + (*pa_callback)(); + if (outputBuffer != NULL) + { + fbuf = (float *)outputBuffer; + soundiop = pa_soundout; + for (i = 0, fp2 = fbuf; i < pa_inchans; i++, fp2++) + for (j = 0, fp3 = fp2; j < framesPerBuffer; j++, fp3 += pa_inchans) + *fp3 = *soundiop; + } + + return 0; +} + +PaError pa_open_callback(double sampleRate, int inchannels, int outchannels, + int framesperbuf, int nbuffers, int indeviceno, int outdeviceno) +{ + long bytesPerSample; + PaError err; + PABLIO_Stream *pastream; + long numFrames; + PaStreamParameters instreamparams, outstreamparams; + + if (indeviceno < 0) + { + indeviceno = Pa_GetDefaultInputDevice(); + fprintf(stderr, "using default input device number: %d\n", indeviceno); + } + if (outdeviceno < 0) + { + outdeviceno = Pa_GetDefaultOutputDevice(); + fprintf(stderr, "using default output device number: %d\n", outdeviceno); + } + /* fprintf(stderr, "nchan %d, flags %d, bufs %d, framesperbuf %d\n", + nchannels, flags, nbuffers, framesperbuf); */ + + /* Allocate PABLIO_Stream structure for caller. */ + pastream = (PABLIO_Stream *)malloc( sizeof(PABLIO_Stream)); + if (pastream == NULL) + return (1); + memset(pastream, 0, sizeof(PABLIO_Stream)); + + /* Determine size of a sample. */ + bytesPerSample = Pa_GetSampleSize(paFloat32); + if (bytesPerSample < 0) + { + err = (PaError) bytesPerSample; + goto error; + } + pastream->insamplesPerFrame = inchannels; + pastream->inbytesPerFrame = bytesPerSample * pastream->insamplesPerFrame; + pastream->outsamplesPerFrame = outchannels; + pastream->outbytesPerFrame = bytesPerSample * pastream->outsamplesPerFrame; + + numFrames = nbuffers * framesperbuf; + + instreamparams.device = indeviceno; + instreamparams.channelCount = inchannels; + instreamparams.sampleFormat = paFloat32; + instreamparams.suggestedLatency = nbuffers*framesperbuf/sampleRate; + instreamparams.hostApiSpecificStreamInfo = 0; + + outstreamparams.device = outdeviceno; + outstreamparams.channelCount = outchannels; + outstreamparams.sampleFormat = paFloat32; + outstreamparams.suggestedLatency = nbuffers*framesperbuf/sampleRate; + outstreamparams.hostApiSpecificStreamInfo = 0; + + err = Pa_OpenStream( + &pastream->stream, + (inchannels ? &instreamparams : 0), + (outchannels ? &outstreamparams : 0), + sampleRate, + framesperbuf, + paNoFlag, /* portaudio will clip for us */ + pa_lowlevel_callback, + pastream); + if (err != paNoError) + goto error; + + err = Pa_StartStream(pastream->stream); + if (err != paNoError) + { + fprintf(stderr, "Pa_StartStream failed; closing audio stream...\n"); + CloseAudioStream( pastream ); + goto error; + } + pa_stream = pastream; + return paNoError; +error: + pa_stream = NULL; + return err; +} + int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin, - t_sample *soundout, int framesperbuf, int nbuffers, int callback, - int indeviceno, int outdeviceno) + t_sample *soundout, int framesperbuf, int nbuffers, + int indeviceno, int outdeviceno, t_audiocallback callbackfn) { PaError err; static int initialized; int j, devno, pa_indev = 0, pa_outdev = 0; - - if (callback) + + pa_callback = callbackfn; + if (callbackfn) fprintf(stderr, "callback enabled\n"); if (!initialized) { @@ -103,26 +217,34 @@ int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin, post("output device %d, channels %d", pa_outdev, outchans); post("framesperbuf %d, nbufs %d", framesperbuf, nbuffers); } - if (inchans || outchans) + pa_inchans = inchans; + pa_outchans = outchans; + pa_soundin = soundin; + pa_soundout = soundout; + if (! inchans && !outchans) + return(0); + if (callbackfn) + { + pa_callback = callbackfn; + err = pa_open_callback(rate, inchans, outchans, + framesperbuf, nbuffers, pa_indev, pa_outdev); + } + else + { err = OpenAudioStream( &pa_stream, rate, paFloat32, inchans, outchans, framesperbuf, nbuffers, pa_indev, pa_outdev); - else err = 0; + } if ( err != paNoError ) { - fprintf( stderr, "Error number %d occured opening portaudio stream\n", + fprintf(stderr, "Error number %d opening portaudio stream\n", err); fprintf( stderr, "Error message: %s\n", 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; - pa_soundin = soundin; - pa_soundout = soundout; return (0); } diff --git a/src/s_file.c b/src/s_file.c index dcf54b49f..c81d423b8 100644 --- a/src/s_file.c +++ b/src/s_file.c @@ -339,9 +339,9 @@ void sys_loadpreferences( void) sscanf(prefbuf, "%d", &advance); if (sys_getpreference("callback", prefbuf, MAXPDSTRING)) sscanf(prefbuf, "%d", &callback); - sys_open_audio(naudioindev, audioindev, naudioindev, chindev, + sys_set_audio_settings(naudioindev, audioindev, naudioindev, chindev, naudiooutdev, audiooutdev, naudiooutdev, choutdev, rate, advance, - callback, 0); + callback); /* load MIDI preferences */ /* JMZ/MB: brackets for initializing */ diff --git a/src/s_main.c b/src/s_main.c index 0224923e8..ded67c886 100644 --- a/src/s_main.c +++ b/src/s_main.c @@ -31,7 +31,7 @@ int sys_argparse(int argc, char **argv); void sys_findprogdir(char *progname); int sys_startgui(const char *guipath); int sys_rcfile(void); -int m_scheduler(void); +int m_mainloop(void); void sys_addhelppath(char *p); #ifdef USEAPI_ALSA void alsa_adddev(char *name); @@ -71,7 +71,7 @@ char sys_extraflagsstring[MAXPDSTRING]; /* here the "-1" counts signify that the corresponding vector hasn't been - specified in command line arguments; sys_open_audio will detect this + specified in command line arguments; sys_set_audio_settings will detect it and fill things in. */ static int sys_nsoundin = -1; static int sys_nsoundout = -1; @@ -314,7 +314,7 @@ int sys_main(int argc, char **argv) sys_reopen_midi(); sys_reopen_audio(); /* run scheduler until it quits */ - return (m_scheduler()); + return (m_mainloop()); } } @@ -989,9 +989,9 @@ static void sys_afterargparse(void) rate = sys_main_srate; if (sys_main_callback) callback = sys_main_callback; - sys_open_audio(naudioindev, audioindev, nchindev, chindev, + sys_set_audio_settings(naudioindev, audioindev, nchindev, chindev, naudiooutdev, audiooutdev, nchoutdev, choutdev, rate, advance, - callback, 0); + callback); sys_open_midi(nmidiindev, midiindev, nmidioutdev, midioutdev, 0); } diff --git a/src/s_stuff.h b/src/s_stuff.h index 8320558da..c4c26928e 100644 --- a/src/s_stuff.h +++ b/src/s_stuff.h @@ -71,10 +71,10 @@ extern int sys_blocksize; /* audio I/O block size in sample frames */ extern float sys_dacsr; extern int sys_schedadvance; extern int sys_sleepgrain; -void sys_open_audio(int naudioindev, int *audioindev, +void sys_set_audio_settings(int naudioindev, int *audioindev, int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, - int srate, int advance, int callback, int enable); + int srate, int advance, int callback); void sys_reopen_audio( void); void sys_close_audio(void); @@ -139,7 +139,11 @@ EXTERN void sys_log_error(int type); #define ERR_DACSLEPT 2 #define ERR_RESYNC 3 #define ERR_DATALATE 4 -void sched_set_using_dacs(int flag); + +#define SCHED_AUDIO_NONE 0 +#define SCHED_AUDIO_POLL 1 +#define SCHED_AUDIO_CALLBACK 2 +void sched_set_using_audio(int flag); /* s_inter.c */ @@ -205,9 +209,11 @@ void sys_setvirtualalarm( void); #define DEFAULTADVANCE 50 #endif +typedef void (*t_audiocallback)(vid); + int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin, - t_sample *soundout, int framesperbuf, int nbuffers, int callback, - int indeviceno, int outdeviceno); + t_sample *soundout, int framesperbuf, int nbuffers, + int indeviceno, int outdeviceno, t_audiocallback callback); void pa_close_audio(void); int pa_send_dacs(void); void sys_reportidle(void); -- GitLab