Commit c8470102 authored by Miller Puckette's avatar Miller Puckette
Browse files

first cut at callback scheduler

parent 85a56dd9
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
#define THREAD_LOCKING #define THREAD_LOCKING
#include "pthread.h" #include "pthread.h"
#define SYS_QUIT_QUIT 1
#define SYS_QUIT_RESTART 2
static int sys_quit; static int sys_quit;
double sys_time; double sys_time;
static double sys_time_per_msec = TIMEUNITPERSEC / 1000.; 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) ...@@ -326,14 +327,14 @@ void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv)
void dsp_tick(void); void dsp_tick(void);
static int sched_usedacs = 1; static int sched_useaudio = SCHED_AUDIO_POLL;
static double sched_referencerealtime, sched_referencelogicaltime; static double sched_referencerealtime, sched_referencelogicaltime;
double sys_time_per_dsp_tick; double sys_time_per_dsp_tick;
void sched_set_using_dacs(int flag) void sched_set_using_audio(int flag)
{ {
sched_usedacs = flag; sched_useaudio = flag;
if (!flag) if (flag == SCHED_AUDIO_NONE)
{ {
sched_referencerealtime = sys_getrealtime(); sched_referencerealtime = sys_getrealtime();
sched_referencelogicaltime = clock_getlogicaltime(); sched_referencelogicaltime = clock_getlogicaltime();
...@@ -385,7 +386,7 @@ nonzero if you actually used the time; otherwise we're really really idle and ...@@ -385,7 +386,7 @@ nonzero if you actually used the time; otherwise we're really really idle and
will now sleep. */ will now sleep. */
int (*sys_idlehook)(void); int (*sys_idlehook)(void);
int m_scheduler( void) static void m_pollingscheduler( void)
{ {
int idlecount = 0; int idlecount = 0;
sys_time_per_dsp_tick = (TIMEUNITPERSEC) * sys_time_per_dsp_tick = (TIMEUNITPERSEC) *
...@@ -410,7 +411,7 @@ int m_scheduler( void) ...@@ -410,7 +411,7 @@ int m_scheduler( void)
sys_addhist(0); sys_addhist(0);
waitfortick: waitfortick:
if (sched_usedacs) if (sched_useaudio != SCHED_AUDIO_NONE)
{ {
#ifdef THREAD_LOCKING #ifdef THREAD_LOCKING
/* T.Grill - send_dacs may sleep -> /* T.Grill - send_dacs may sleep ->
...@@ -441,7 +442,7 @@ int m_scheduler( void) ...@@ -441,7 +442,7 @@ int m_scheduler( void)
{ {
post("audio I/O stuck... closing audio\n"); post("audio I/O stuck... closing audio\n");
sys_close_audio(); sys_close_audio();
sched_set_using_dacs(0); sched_set_using_audio(SCHED_AUDIO_NONE);
goto waitfortick; goto waitfortick;
} }
} }
...@@ -475,7 +476,6 @@ int m_scheduler( void) ...@@ -475,7 +476,6 @@ int m_scheduler( void)
{ {
sched_pollformeters(); sched_pollformeters();
sys_reportidle(); sys_reportidle();
#ifdef THREAD_LOCKING #ifdef THREAD_LOCKING
sys_unlock(); /* unlock while we idle */ sys_unlock(); /* unlock while we idle */
#endif #endif
...@@ -489,20 +489,52 @@ int m_scheduler( void) ...@@ -489,20 +489,52 @@ int m_scheduler( void)
#ifdef THREAD_LOCKING #ifdef THREAD_LOCKING
sys_lock(); sys_lock();
#endif #endif
sys_addhist(5); sys_addhist(5);
sched_didnothing++; sched_didnothing++;
} }
} }
#ifdef THREAD_LOCKING #ifdef THREAD_LOCKING
sys_unlock(); sys_unlock();
#endif #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 ------------------- */ /* ------------ thread locking ------------------- */
...@@ -534,5 +566,5 @@ int sys_trylock(void) {} ...@@ -534,5 +566,5 @@ int sys_trylock(void) {}
void sys_exit(void) void sys_exit(void)
{ {
sys_quit = 1; sys_quit = SYS_QUIT_QUIT;
} }
...@@ -67,7 +67,7 @@ static int audio_rate; ...@@ -67,7 +67,7 @@ static int audio_rate;
static int audio_advance; static int audio_advance;
static int audio_callback; static int audio_callback;
void sched_set_callback(int flag); void sched_audio_callbackfn(void);
static int audio_isopen(void) static int audio_isopen(void)
{ {
...@@ -170,14 +170,14 @@ void sys_setchsr(int chin, int chout, int sr) ...@@ -170,14 +170,14 @@ void sys_setchsr(int chin, int chout, int sr)
/* ----------------------- public routines ----------------------- */ /* ----------------------- public routines ----------------------- */
/* open audio devices (after cleaning up the specified device and channel /* set audio device settings (after cleaning up the specified device and
vectors). The audio devices are "zero based" (i.e. "0" means the first channel vectors). The audio devices are "zero based" (i.e. "0" means the
one.) We also save the cleaned-up device specification so that we first one.) We can later re-open audio and/or show the settings on a\
can later re-open audio and/or show the settings on a dialog window. */ 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 *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 i, *ip;
int defaultchannels = SYS_DEFAULTCH; int defaultchannels = SYS_DEFAULTCH;
...@@ -189,7 +189,6 @@ void sys_open_audio(int naudioindev, int *audioindev, int nchindev, ...@@ -189,7 +189,6 @@ void sys_open_audio(int naudioindev, int *audioindev, int nchindev,
int indevs = 0, outdevs = 0, canmulti = 0, cancallback = 0; int indevs = 0, outdevs = 0, canmulti = 0, cancallback = 0;
audio_getdevs(indevlist, &indevs, outdevlist, &outdevs, &canmulti, audio_getdevs(indevlist, &indevs, outdevlist, &outdevs, &canmulti,
&cancallback, MAXNDEV, DEVDESCSIZE); &cancallback, MAXNDEV, DEVDESCSIZE);
if (sys_externalschedlib) if (sys_externalschedlib)
{ {
return; return;
...@@ -324,62 +323,11 @@ void sys_open_audio(int naudioindev, int *audioindev, int nchindev, ...@@ -324,62 +323,11 @@ void sys_open_audio(int naudioindev, int *audioindev, int nchindev,
outchans += choutdev[i]; outchans += choutdev[i];
nrealoutdev++; 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_schedadvance = advance * 1000;
sys_setchsr(inchans, outchans, rate); sys_setchsr(inchans, outchans, rate);
sys_log_error(ERR_NOTHING); sys_log_error(ERR_NOTHING);
if (enable) sys_save_audio_params(nrealindev, realindev, realinchans,
{ nrealoutdev, realoutdev, realoutchans, sys_dacsr, advance, callback);
#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);
} }
void sys_close_audio(void) void sys_close_audio(void)
...@@ -417,6 +365,7 @@ void sys_close_audio(void) ...@@ -417,6 +365,7 @@ void sys_close_audio(void)
#endif #endif
post("sys_close_audio: unknown API %d", sys_audioapi); post("sys_close_audio: unknown API %d", sys_audioapi);
sys_inchannels = sys_outchannels = 0; sys_inchannels = sys_outchannels = 0;
sched_set_using_audio(SCHED_AUDIO_NONE);
} }
/* open audio using whatever parameters were last used */ /* open audio using whatever parameters were last used */
...@@ -424,12 +373,67 @@ void sys_reopen_audio( void) ...@@ -424,12 +373,67 @@ void sys_reopen_audio( void)
{ {
int naudioindev, audioindev[MAXAUDIOINDEV], chindev[MAXAUDIOINDEV]; int naudioindev, audioindev[MAXAUDIOINDEV], chindev[MAXAUDIOINDEV];
int naudiooutdev, audiooutdev[MAXAUDIOOUTDEV], choutdev[MAXAUDIOOUTDEV]; int naudiooutdev, audiooutdev[MAXAUDIOOUTDEV], choutdev[MAXAUDIOOUTDEV];
int rate, advance, callback; int rate, advance, callback, outcome = 0;
sys_get_audio_params(&naudioindev, audioindev, chindev, sys_get_audio_params(&naudioindev, audioindev, chindev,
&naudiooutdev, audiooutdev, choutdev, &rate, &advance, &callback); &naudiooutdev, audiooutdev, choutdev, &rate, &advance, &callback);
sys_open_audio(naudioindev, audioindev, naudioindev, chindev, if (!naudioindev && !naudiooutdev)
naudiooutdev, audiooutdev, naudiooutdev, choutdev, rate, advance, {
callback, 1); 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) int sys_send_dacs(void)
...@@ -727,9 +731,10 @@ void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) ...@@ -727,9 +731,10 @@ void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv)
} }
sys_close_audio(); sys_close_audio();
sys_open_audio(nindev, newaudioindev, nindev, newaudioinchan, sys_set_audio_settings(nindev, newaudioindev, nindev, newaudioinchan,
noutdev, newaudiooutdev, noutdev, newaudiooutchan, noutdev, newaudiooutdev, noutdev, newaudiooutchan,
newrate, newadvance, newcallback, 1); newrate, newadvance, newcallback);
sys_reopen_audio();
} }
void sys_listdevs(void ) void sys_listdevs(void )
...@@ -807,7 +812,6 @@ void glob_audio_setapi(void *dummy, t_floatarg f) ...@@ -807,7 +812,6 @@ void glob_audio_setapi(void *dummy, t_floatarg f)
{ {
sys_close_audio(); sys_close_audio();
audio_state = 0; audio_state = 0;
sched_set_using_dacs(0);
} }
} }
...@@ -822,10 +826,7 @@ void sys_set_audio_state(int onoff) ...@@ -822,10 +826,7 @@ void sys_set_audio_state(int onoff)
else else
{ {
if (audio_isopen()) if (audio_isopen())
{
sys_close_audio(); sys_close_audio();
sched_set_using_dacs(0);
}
} }
audio_state = onoff; audio_state = onoff;
} }
......
...@@ -23,19 +23,133 @@ ...@@ -23,19 +23,133 @@
static PABLIO_Stream *pa_stream; static PABLIO_Stream *pa_stream;
static int pa_inchans, pa_outchans; static int pa_inchans, pa_outchans;
static float *pa_soundin, *pa_soundout; static float *pa_soundin, *pa_soundout;
static t_audiocallback pa_callback;
#define MAX_PA_CHANS 32 #define MAX_PA_CHANS 32
#define MAX_SAMPLES_PER_FRAME MAX_PA_CHANS * DEFDACBLKSIZE #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, int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin,
t_sample *soundout, int framesperbuf, int nbuffers, int callback, t_sample *soundout, int framesperbuf, int nbuffers,
int indeviceno, int outdeviceno) int indeviceno, int outdeviceno, t_audiocallback callbackfn)
{ {
PaError err; PaError err;
static int initialized; static int initialized;
int j, devno, pa_indev = 0, pa_outdev = 0; int j, devno, pa_indev = 0, pa_outdev = 0;
if (callback) pa_callback = callbackfn;
if (callbackfn)
fprintf(stderr, "callback enabled\n"); fprintf(stderr, "callback enabled\n");
if (!initialized) if (!initialized)
{ {
...@@ -103,26 +217,34 @@ int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin, ...@@ -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("output device %d, channels %d", pa_outdev, outchans);
post("framesperbuf %d, nbufs %d", framesperbuf, nbuffers); 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, err = OpenAudioStream( &pa_stream, rate, paFloat32,
inchans, outchans, framesperbuf, nbuffers, inchans, outchans, framesperbuf, nbuffers,
pa_indev, pa_outdev); pa_indev, pa_outdev);
else err = 0; }
if ( err != paNoError ) if ( err != paNoError )
{ {
fprintf( stderr, "Error number %d occured opening portaudio stream\n", fprintf(stderr, "Error number %d opening portaudio stream\n",
err); err);
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
Pa_Terminate(); Pa_Terminate();
sys_inchannels = sys_outchannels = 0;
return (1); return (1);
} }
else if (sys_verbose) else if (sys_verbose)
post("... opened OK."); post("... opened OK.");
pa_inchans = inchans;
pa_outchans = outchans;
pa_soundin = soundin;
pa_soundout = soundout;
return (0);