/* Copyright (c) 2003, 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. */ /* machine-independent (well, mostly!) audio layer. Stores and recalls audio settings from argparse routine and from dialog window. */ #define PD_PLUSPLUS_FACE #include "m_pd.h" #include "s_stuff.h" #include "m_simd.h" #include #ifdef UNISTD #include #include #include #endif #include #include #include #include #define SYS_DEFAULTCH 2 #define SYS_MAXCH 100 typedef long t_pa_sample; #define SYS_SAMPLEWIDTH sizeof(t_pa_sample) #define SYS_BYTESPERCHAN (sys_dacblocksize * SYS_SAMPLEWIDTH) #define SYS_XFERSAMPS (SYS_DEFAULTCH*sys_dacblocksize) #define SYS_XFERSIZE (SYS_SAMPLEWIDTH * SYS_XFERSAMPS) #define MAXNDEV 100 #define DEVDESCSIZE 80 extern t_audioapi pa_api, jack_api, oss_api, alsa_api, sgi_api, mmio_api, asio_api; t_sample *sys_soundin; t_sample *sys_soundout; float sys_dacsr; using namespace std; static t_audioapi *sys_audio() { #ifdef USEAPI_PORTAUDIO if (sys_audioapi == API_PORTAUDIO) return &pa_api; #endif #ifdef USEAPI_JACK if (sys_audioapi == API_JACK) return &jack_api; #endif #ifdef USEAPI_OSS if (sys_audioapi == API_OSS) return &oss_api; #endif #ifdef USEAPI_ALSA if (sys_audioapi == API_ALSA) return &alsa_api; #endif #ifdef USEAPI_SGI if (sys_audioapi == API_SGI) return &sgi_api; #endif #ifdef USEAPI_MMIO if (sys_audioapi == API_MMIO) return &mmio_api; #endif #ifdef USEAPI_ASIO if (sys_audioapi == API_ASIO) return &asio_api; #endif post("sys_close_audio: unknown API %d", sys_audioapi); sys_inchannels = sys_outchannels = 0; sched_set_using_dacs(0); /* tb: dsp is switched off */ return 0; } static void audio_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize); /* these are set in this file when opening audio, but then may be reduced, even to zero, in the system dependent open_audio routines. */ int sys_inchannels; int sys_outchannels; int sys_advance_samples; /* scheduler advance in samples */ int sys_blocksize = 0; /* audio I/O block size in sample frames */ #ifndef API_DEFAULT #define API_DEFAULT 0 #endif int sys_audioapi = API_DEFAULT; int sys_dacblocksize; int sys_schedblocksize; int sys_meters; /* true if we're metering */ static float sys_inmax; /* max input amplitude */ static float sys_outmax; /* max output amplitude */ int sys_schedadvance; /* scheduler advance in microseconds */ /* the "state" is normally one if we're open and zero otherwise; but if the state is one, we still haven't necessarily opened the audio hardware; see audio_isopen() below. */ static int audio_state; /* last requested parameters */ static t_audiodevs audio_in; static t_audiodevs audio_out; static int audio_rate; static int audio_dacblocksize; static int audio_advance; static int audio_scheduler; extern int sys_callbackscheduler; float peakvec(t_float* vec, t_int n, t_float cur_max); static float (*peak_fp)(t_float*, t_int, t_float) = peakvec; static int audio_isopen() { return audio_state && ((audio_in.ndev > 0 && audio_in.chdev[0] > 0) || (audio_out.ndev > 0 && audio_out.chdev[0] > 0)); } extern "C" void sys_get_audio_params(t_audiodevs *in, t_audiodevs *out, int *prate, int *pdacblocksize, int *padvance, int *pscheduler) { in->ndev = audio_in.ndev; out->ndev = audio_out.ndev; for (int i=0; idev[i] = audio_in.dev[i]; in->chdev[i] = audio_in.chdev[i];} for (int i=0; idev[i] = audio_out.dev[i]; out->chdev[i] = audio_out.chdev[i];} *prate = audio_rate; *pdacblocksize = audio_dacblocksize; *padvance = audio_advance; *pscheduler = audio_scheduler; } void sys_save_audio_params( int nindev, int *indev, int *chindev, int noutdev, int *outdev, int *choutdev, int rate, int dacblocksize, int advance, int scheduler) { audio_in.ndev = nindev; audio_out.ndev = noutdev; for (int i=0; indev, in->dev, in->ndev, in->chdev, out->ndev, out->dev, out->ndev, out->chdev, rate, dacblocksize, advance, scheduler, 1); } /* init routines for any API which needs to set stuff up before any other API gets used. This is only true of OSS so far. */ #ifdef USEAPI_OSS void oss_init(); #endif static void audio_init() { static int initted = 0; if (initted) return; initted = 1; #ifdef USEAPI_OSS oss_init(); #endif } /* set channels and sample rate. */ void sys_setchsr(int chin, int chout, int sr, int dacblocksize) { int inbytes = (chin ? chin : 2) * (sys_dacblocksize*sizeof(float)); int outbytes = (chout ? chout : 2) * (sys_dacblocksize*sizeof(float)); if (dacblocksize != (1<= 1) { nchindev=1; chindev[0] = defaultchannels; nindev = 1; indev[0] = DEFAULTAUDIODEV; } else nindev = nchindev = 0; } else { for (int i=0; i nindev) { for (int i=nindev; i= 1) { nchoutdev=1; choutdev[0]=defaultchannels; noutdev=1; outdev[0] = DEFAULTAUDIODEV; } else nchoutdev = noutdev = 0; } else { for (int i=0; i noutdev) { for (int i=noutdev; i 0 ? chindev[i] : 0)); for (int i=0; i < noutdev; i++) outchans += (realoutchans[i] = (choutdev[i] > 0 ? choutdev[i] : 0)); /* 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, dacblocksize); sys_log_error(ERR_NOTHING); if (enable) { /* for alsa, only one device is supported; it may be open for both input and output. */ #ifdef USEAPI_PORTAUDIO if (sys_audioapi == API_PORTAUDIO) pa_open_audio(inchans, outchans, rate, advance, (noutdev > 0 ? indev[0] : 0), (noutdev > 0 ? outdev[0] : 0), schedmode); else #endif #ifdef USEAPI_JACK if (sys_audioapi == API_JACK) jack_open_audio((nindev > 0 ? realinchans[0] : 0), (noutdev > 0 ? realoutchans[0] : 0), rate, schedmode); else #endif if (sys_audioapi == API_OSS || sys_audioapi == API_ALSA || sys_audioapi == API_MMIO) sys_audio()->open_audio(nindev, indev, nchindev, realinchans, noutdev, outdev, nchoutdev, realoutchans, rate, -42); else if (sys_audioapi == API_SGI) sys_audio()->open_audio(nindev, indev, nchindev, chindev, noutdev, outdev, nchoutdev, choutdev, rate, -42); else if (sys_audioapi == API_ASIO) sys_audio()->open_audio(nindev, indev, nchindev, chindev, noutdev, outdev, nchoutdev, choutdev, rate, schedmode); else post("unknown audio API specified"); } sys_save_audio_params(nindev, indev, chindev, noutdev, outdev, choutdev, int(sys_dacsr), sys_dacblocksize, advance, schedmode); 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_update_sleepgrain(); if (enable) { t_atom argv[1]; t_symbol *selector = gensym("audio_started"); t_symbol *pd = gensym("pd"); SETFLOAT(argv, 1.); typedmess(pd->s_thing, selector, 1, argv); } } void sys_close_audio() { /* jsarlo { (*/ if (sys_externalschedlib) return; /* } jsarlo */ if (!audio_isopen()) return; if (sys_audio()) sys_audio()->close_audio(); else post("sys_close_audio: unknown API %d", sys_audioapi); sys_inchannels = sys_outchannels = 0; sched_set_using_dacs(0); /* tb: dsp is switched off */ } /* open audio using whatever parameters were last used */ void sys_reopen_audio() { t_audiodevs in, out; int rate, dacblocksize, advance, scheduler; sys_close_audio(); sys_get_audio_params(&in,&out,&rate, &dacblocksize, &advance, &scheduler); sys_open_audio2(&in,&out, rate, dacblocksize, advance, scheduler); } /* tb: default value of peak_fp {*/ float peakvec(t_float* vec, t_int n, t_float cur_max) { for (int i=0; i cur_max) cur_max = f; else if (-f > cur_max) cur_max = -f; } return cur_max; } /* } */ void sys_peakmeters() { if (sys_inchannels) sys_inmax = peak_fp(sys_soundin, sys_inchannels * sys_dacblocksize, sys_inmax); if (sys_outchannels) sys_outmax = peak_fp(sys_soundout,sys_outchannels * sys_dacblocksize, sys_outmax); } int sys_send_dacs() { if (sys_meters) sys_peakmeters(); if (sys_audio()) return sys_audio()->send_dacs(); post("unknown API"); return 0; } float sys_getsr() {return sys_dacsr;} int sys_get_outchannels() {return sys_outchannels;} int sys_get_inchannels() {return sys_inchannels;} void sys_getmeters(float *inmax, float *outmax) { if (inmax) { sys_meters = 1; *inmax = sys_inmax; *outmax = sys_outmax; } else sys_meters = 0; sys_inmax = sys_outmax = 0; } static void audio_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { audio_init(); if (sys_audio()) sys_audio()->getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti, maxndev, devdescsize); else {*nindevs = *noutdevs = 0;} } static void sys_listaudiodevs() { char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; int nindevs = 0, noutdevs = 0, canmulti = 0; audio_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, MAXNDEV, DEVDESCSIZE); /* To agree with command line flags, normally start at 1; but microsoft "MMIO" device list starts at 0 (the "mapper"). */ /* (see also sys_mmio variable in s_main.c) */ if (!nindevs) post("no audio input devices found"); else { post("audio input devices:"); for (int i=0; i 1 || out.ndev > 1) flongform = 1; ostringstream indevs; for (int i=0; i< in.ndev; i++) indevs << " " << in.dev [i]; ostringstream outdevs; for (int i=0; i 5000) sys_sleepgrain = 5000; } /* t_audiodevs are the ones you're using; char[] are all the devices available. */ void glob_audio_getaudioindevices(t_pd * dummy, t_symbol *s, int ac, t_atom *av) { t_audiodevs in,out; int rate, dacblocksize, advance, scheduler; int nindevs = 0, noutdevs = 0, canmulti = 0; t_atom argv[MAXNDEV]; int f = ac ? (int)atom_getfloatarg(0,ac,av) : -1; t_symbol *selector = gensym("audioindev"); t_symbol *pd = gensym("pd"); char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; audio_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, MAXNDEV, DEVDESCSIZE); sys_get_audio_params(&in,&out, &rate, &dacblocksize, &advance, &scheduler); if (f < 0) { for (int i=0; is_thing, selector, nindevs, argv); } else if (f < nindevs) { SETSTRING(argv, indevlist + f * DEVDESCSIZE); typedmess(pd->s_thing, selector, 1, argv); } } void glob_audio_getaudiooutdevices(t_pd * dummy, t_symbol *s, int ac, t_atom *av) { t_audiodevs in,out; int rate, dacblocksize, advance, scheduler; int nindevs = 0, noutdevs = 0, canmulti = 0; t_atom argv[MAXNDEV]; int f = ac ? (int)atom_getfloatarg(0,ac,av) : -1; t_symbol *selector = gensym("audiooutdev"); t_symbol *pd = gensym("pd"); char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; audio_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, MAXNDEV, DEVDESCSIZE); sys_get_audio_params(&in,&out, &rate, &dacblocksize, &advance, &scheduler); if (f < 0) { for (int i=0; is_thing, selector, noutdevs, argv); } else if (f < noutdevs) { SETSTRING(argv, outdevlist + f * DEVDESCSIZE); typedmess(pd->s_thing, selector, 1, argv); } } /* some prototypes from s_audio_portaudio.c */ extern void pa_getcurrent_devices(); extern void pa_getaudioininfo(t_float f); extern void pa_getaudiooutinfo(t_float f); extern void pa_test_setting (int ac, t_atom *av); extern void pa_get_asio_latencies(t_float f); void glob_audio_getaudioininfo(t_pd * dummy, t_float f) { #if defined(USEAPI_PORTAUDIO) && !defined(PABLIO) if (sys_audioapi == API_PORTAUDIO) pa_getaudioininfo(f); #endif } void glob_audio_getaudiooutinfo(t_pd * dummy, t_float f) { #if defined(USEAPI_PORTAUDIO) && !defined(PABLIO) if (sys_audioapi == API_PORTAUDIO) pa_getaudiooutinfo(f); #endif } void glob_audio_testaudiosetting(t_pd * dummy, t_symbol *s, int ac, t_atom *av) { #if defined(USEAPI_PORTAUDIO) && !defined(PABLIO) if (sys_audioapi == API_PORTAUDIO) pa_test_setting (ac, av); #endif } void glob_audio_getcurrent_devices() { #if defined(USEAPI_PORTAUDIO) && !defined(PABLIO) if (sys_audioapi == API_PORTAUDIO) pa_getcurrent_devices(); #endif } void glob_audio_asio_latencies(t_pd * dummy, t_float f) { #if defined(USEAPI_PORTAUDIO) && !defined(PABLIO) if (sys_audioapi == API_PORTAUDIO) pa_get_asio_latencies(f); #endif } /* tb } */