diff options
author | Guenter Geiger <ggeiger@users.sourceforge.net> | 2002-07-29 17:06:19 +0000 |
---|---|---|
committer | Guenter Geiger <ggeiger@users.sourceforge.net> | 2002-07-29 17:06:19 +0000 |
commit | 57045df5fe3ec557e57dc7434ac1a07b5521bffc (patch) | |
tree | 7174058b41b73c808107c7090d9a4e93ee202341 /pd/src/s_nt.c | |
parent | da38b3424229e59f956252c3d89895e43e84e278 (diff) |
This commit was generated by cvs2svn to compensate for changes in r58,
which included commits to RCS files with non-trunk default branches.
svn path=/trunk/; revision=59
Diffstat (limited to 'pd/src/s_nt.c')
-rw-r--r-- | pd/src/s_nt.c | 1586 |
1 files changed, 1586 insertions, 0 deletions
diff --git a/pd/src/s_nt.c b/pd/src/s_nt.c new file mode 100644 index 00000000..99346e7c --- /dev/null +++ b/pd/src/s_nt.c @@ -0,0 +1,1586 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* modified 2/98 by Winfried Ritsch to deal with up to 4 synchronized +"wave" devices, which is how ADAT boards appear to the WAVE API. */ + +#include "m_imp.h" +#include <stdio.h> + +#include <windows.h> + +#include <MMSYSTEM.H> + +/* ------------------------- audio -------------------------- */ + +static void nt_close_midiin(void); + +static void postflags(void); + +#define NAPORTS 16 /* wini hack for multiple ADDA devices */ +#define NT_MAXCH (2 * NAPORTS) +#define CHANNELS_PER_DEVICE 2 +#define DEFAULTCHANS 2 +#define DEFAULTSRATE 44100 +#define SAMPSIZE 2 + +#define REALDACBLKSIZE (4 * DACBLKSIZE) /* larger underlying bufsize */ + +#define MAXBUFFER 100 /* number of buffers in use at maximum advance */ +#define DEFBUFFER 30 /* default is about 30x6 = 180 msec! */ +static int nt_naudiobuffer = DEFBUFFER; +static int nt_advance_samples; + +float sys_dacsr = DEFAULTSRATE; + +static int nt_whichapi = API_MMIO; +static int nt_meters; /* true if we're metering */ +static float nt_inmax; /* max input amplitude */ +static float nt_outmax; /* max output amplitude */ +static int nt_nwavein, nt_nwaveout; /* number of WAVE devices in and out */ +static int nt_blocksize = 0; /* audio I/O block size in sample frames */ +int sys_schedadvance = 20000; /* scheduler advance in microseconds */ + +typedef struct _sbuf +{ + HANDLE hData; + HPSTR lpData; // pointer to waveform data memory + HANDLE hWaveHdr; + WAVEHDR *lpWaveHdr; // pointer to header structure +} t_sbuf; + +t_sbuf ntsnd_outvec[NAPORTS][MAXBUFFER]; /* circular buffer array */ +HWAVEOUT ntsnd_outdev[NAPORTS]; /* output device */ +static int ntsnd_outphase[NAPORTS]; /* index of next buffer to send */ + +t_sbuf ntsnd_invec[NAPORTS][MAXBUFFER]; /* circular buffer array */ +HWAVEIN ntsnd_indev[NAPORTS]; /* input device */ +static int ntsnd_inphase[NAPORTS]; /* index of next buffer to read */ +int sys_hipriority = 0; + +static void nt_waveinerror(char *s, int err) +{ + char t[256]; + waveInGetErrorText(err, t, 256); + fprintf(stderr, s, t); +} + +static void nt_waveouterror(char *s, int err) +{ + char t[256]; + waveOutGetErrorText(err, t, 256); + fprintf(stderr, s, t); +} + +static void wave_prep(t_sbuf *bp) +{ + WAVEHDR *wh; + short *sp; + int i; + /* + * Allocate and lock memory for the waveform data. The memory + * for waveform data must be globally allocated with + * GMEM_MOVEABLE and GMEM_SHARE flags. + */ + + if (!(bp->hData = + GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, + (DWORD) (CHANNELS_PER_DEVICE * REALDACBLKSIZE * SAMPSIZE)))) + printf("alloc 1 failed\n"); + + if (!(bp->lpData = + (HPSTR) GlobalLock(bp->hData))) + printf("lock 1 failed\n"); + + /* Allocate and lock memory for the header. */ + + if (!(bp->hWaveHdr = + GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR)))) + printf("alloc 2 failed\n"); + + if (!(wh = bp->lpWaveHdr = + (WAVEHDR *) GlobalLock(bp->hWaveHdr))) + printf("lock 2 failed\n"); + + for (i = CHANNELS_PER_DEVICE * REALDACBLKSIZE, + sp = (short *)bp->lpData; i--; ) + *sp++ = 0; + + wh->lpData = bp->lpData; + wh->dwBufferLength = (CHANNELS_PER_DEVICE * REALDACBLKSIZE * SAMPSIZE); + wh->dwFlags = 0; + wh->dwLoops = 0L; + wh->lpNext = 0; + wh->reserved = 0; +} + +static int nt_inalloc[NAPORTS], nt_outalloc[NAPORTS]; +static UINT nt_whichdac = WAVE_MAPPER, nt_whichadc = WAVE_MAPPER; + +int mmio_open_audio(void) +{ + PCMWAVEFORMAT form; + int i; + UINT mmresult; + int nad, nda; + + if (sys_verbose) + post("%d devices in, %d devices out", + nt_nwavein, nt_nwaveout); + + form.wf.wFormatTag = WAVE_FORMAT_PCM; + form.wf.nChannels = CHANNELS_PER_DEVICE; + form.wf.nSamplesPerSec = sys_dacsr; + form.wf.nAvgBytesPerSec = sys_dacsr * (CHANNELS_PER_DEVICE * SAMPSIZE); + form.wf.nBlockAlign = CHANNELS_PER_DEVICE * SAMPSIZE; + form.wBitsPerSample = 8 * SAMPSIZE; + + for (nad=0; nad < nt_nwavein; nad++) + { + /* Open waveform device(s), sucessively numbered, for input */ + + mmresult = waveInOpen(&ntsnd_indev[nad], nt_whichadc+nad, + (WAVEFORMATEX *)(&form), 0L, 0L, CALLBACK_NULL); + + if (sys_verbose) + printf("opened adc device %d with return %d\n", + nt_whichadc+nad,mmresult); + + if (mmresult != MMSYSERR_NOERROR) + { + nt_waveinerror("waveInOpen: %s\n", mmresult); + nt_nwavein = nad; /* nt_nwavein = 0 wini */ + } + else + { + if (!nt_inalloc[nad]) + { + for (i = 0; i < nt_naudiobuffer; i++) + wave_prep(&ntsnd_invec[nad][i]); + nt_inalloc[nad] = 1; + } + for (i = 0; i < nt_naudiobuffer; i++) + { + mmresult = waveInPrepareHeader(ntsnd_indev[nad], + ntsnd_invec[nad][i].lpWaveHdr, sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) + nt_waveinerror("waveinprepareheader: %s\n", mmresult); + mmresult = waveInAddBuffer(ntsnd_indev[nad], + ntsnd_invec[nad][i].lpWaveHdr, sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) + nt_waveinerror("waveInAddBuffer: %s\n", mmresult); + } + } + } + /* quickly start them all together */ + for(nad=0; nad < nt_nwavein; nad++) + waveInStart(ntsnd_indev[nad]); + + for(nda=0; nda < nt_nwaveout; nda++) + { + + /* Open a waveform device for output in sucessiv device numbering*/ + mmresult = waveOutOpen(&ntsnd_outdev[nda], nt_whichdac + nda, + (WAVEFORMATEX *)(&form), 0L, 0L, CALLBACK_NULL); + + if (sys_verbose) + fprintf(stderr,"opened dac device %d, with return %d\n", + nt_whichdac +nda, mmresult); + + if (mmresult != MMSYSERR_NOERROR) + { + fprintf(stderr,"Wave out open device %d + %d\n",nt_whichdac,nda); + nt_waveouterror("waveOutOpen device: %s\n", mmresult); + nt_nwaveout = nda; + } + else + { + if (!(nt_outalloc[nda])) + { + for (i = 0; i < nt_naudiobuffer; i++) + { + wave_prep(&ntsnd_outvec[nda][i]); + /* set DONE flag as if we had queued them */ + ntsnd_outvec[nda][i].lpWaveHdr->dwFlags = WHDR_DONE; + } + nt_outalloc[nda] = 1; + } + } + } + + return (0); +} + +void mmio_close_audio( void) +{ + int errcode; + int nda, nad; + if (sys_verbose) + post("closing audio..."); + + for (nda=0; nda < nt_nwaveout; nda++) /*if (nt_nwaveout) wini */ + { + errcode = waveOutReset(ntsnd_outdev[nda]); + if (errcode != MMSYSERR_NOERROR) + printf("error resetting output %d: %d\n", nda, errcode); + errcode = waveOutClose(ntsnd_outdev[nda]); + if (errcode != MMSYSERR_NOERROR) + printf("error closing output %d: %d\n",nda , errcode); + } + nt_nwaveout = 0; + + for(nad=0; nad < nt_nwavein;nad++) /* if (nt_nwavein) wini */ + { + errcode = waveInReset(ntsnd_indev[nad]); + if (errcode != MMSYSERR_NOERROR) + printf("error resetting input: %d\n", errcode); + errcode = waveInClose(ntsnd_indev[nad]); + if (errcode != MMSYSERR_NOERROR) + printf("error closing input: %d\n", errcode); + } + nt_nwavein = 0; +} + + +#define ADCJITTER 10 /* We tolerate X buffers of jitter by default */ +#define DACJITTER 10 + +static int nt_adcjitterbufsallowed = ADCJITTER; +static int nt_dacjitterbufsallowed = DACJITTER; + + /* ------------- MIDI time stamping from audio clock ------------ */ + +#ifdef MIDI_TIMESTAMP + +static double nt_hibuftime; +static double initsystime = -1; + + /* call this whenever we reset audio */ +static void nt_resetmidisync(void) +{ + initsystime = clock_getsystime(); + nt_hibuftime = sys_getrealtime(); +} + + /* call this whenever we're idled waiting for audio to be ready. + The routine maintains a high and low water point for the difference + between real and DAC time. */ + +static void nt_midisync(void) +{ + double jittersec, diff; + + if (initsystime == -1) nt_resetmidisync(); + jittersec = (nt_dacjitterbufsallowed > nt_adcjitterbufsallowed ? + nt_dacjitterbufsallowed : nt_adcjitterbufsallowed) + * REALDACBLKSIZE / sys_getsr(); + diff = sys_getrealtime() - 0.001 * clock_gettimesince(initsystime); + if (diff > nt_hibuftime) nt_hibuftime = diff; + if (diff < nt_hibuftime - jittersec) + { + post("jitter excess %d %f", dac, diff); + nt_resetmidisync(); + } +} + +static double nt_midigettimefor(LARGE_INTEGER timestamp) +{ + /* this is broken now... used to work when "timestamp" was derived from + QueryPerformanceCounter() instead of the gates approved + timeGetSystemTime() call in the MIDI callback routine below. */ + return (nt_tixtotime(timestamp) - nt_hibuftime); +} +#endif /* MIDI_TIMESTAMP */ + + +static int nt_fill = 0; +#define WRAPFWD(x) ((x) >= nt_naudiobuffer ? (x) - nt_naudiobuffer: (x)) +#define WRAPBACK(x) ((x) < 0 ? (x) + nt_naudiobuffer: (x)) +#define MAXRESYNC 500 + +#if 0 /* this is used for debugging */ +static void nt_printaudiostatus(void) +{ + int nad, nda; + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + int phase2 = phase, phase3 = WRAPFWD(phase2), count, ntrans = 0; + int firstphasedone = -1, firstphasebusy = -1; + for (count = 0; count < nt_naudiobuffer; count++) + { + int donethis = + (ntsnd_invec[nad][phase2].lpWaveHdr->dwFlags & WHDR_DONE); + int donenext = + (ntsnd_invec[nad][phase3].lpWaveHdr->dwFlags & WHDR_DONE); + if (donethis && !donenext) + { + if (firstphasebusy >= 0) goto multipleadc; + firstphasebusy = count; + } + if (!donethis && donenext) + { + if (firstphasedone >= 0) goto multipleadc; + firstphasedone = count; + } + phase2 = phase3; + phase3 = WRAPFWD(phase2 + 1); + } + post("nad %d phase %d busy %d done %d", nad, phase, firstphasebusy, + firstphasedone); + continue; + multipleadc: + startpost("nad %d phase %d: oops:", nad, phase); + for (count = 0; count < nt_naudiobuffer; count++) + { + char buf[80]; + sprintf(buf, " %d", + (ntsnd_invec[nad][count].lpWaveHdr->dwFlags & WHDR_DONE)); + poststring(buf); + } + endpost(); + } + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nad]; + int phase2 = phase, phase3 = WRAPFWD(phase2), count, ntrans = 0; + int firstphasedone = -1, firstphasebusy = -1; + for (count = 0; count < nt_naudiobuffer; count++) + { + int donethis = + (ntsnd_outvec[nda][phase2].lpWaveHdr->dwFlags & WHDR_DONE); + int donenext = + (ntsnd_outvec[nda][phase3].lpWaveHdr->dwFlags & WHDR_DONE); + if (donethis && !donenext) + { + if (firstphasebusy >= 0) goto multipledac; + firstphasebusy = count; + } + if (!donethis && donenext) + { + if (firstphasedone >= 0) goto multipledac; + firstphasedone = count; + } + phase2 = phase3; + phase3 = WRAPFWD(phase2 + 1); + } + if (firstphasebusy < 0) post("nda %d phase %d all %d", + nda, phase, (ntsnd_outvec[nad][0].lpWaveHdr->dwFlags & WHDR_DONE)); + else post("nda %d phase %d busy %d done %d", nda, phase, firstphasebusy, + firstphasedone); + continue; + multipledac: + startpost("nda %d phase %d: oops:", nda, phase); + for (count = 0; count < nt_naudiobuffer; count++) + { + char buf[80]; + sprintf(buf, " %d", + (ntsnd_outvec[nad][count].lpWaveHdr->dwFlags & WHDR_DONE)); + poststring(buf); + } + endpost(); + } +} +#endif /* 0 */ + +/* this is a hack to avoid ever resyncing audio pointers in case for whatever +reason the sync testing below gives false positives. */ + +static int nt_resync_cancelled; + +void nt_noresync( void) +{ + nt_resync_cancelled = 1; +} + +static void nt_resyncaudio(void) +{ + UINT mmresult; + int nad, nda, count; + if (nt_resync_cancelled) + return; + /* for each open input device, eat all buffers which are marked + ready. The next one will thus be "busy". */ + post("resyncing audio"); + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + for (count = 0; count < MAXRESYNC; count++) + { + WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; + if (!(inwavehdr->dwFlags & WHDR_DONE)) break; + if (inwavehdr->dwFlags & WHDR_PREPARED) + waveInUnprepareHeader(ntsnd_indev[nad], + inwavehdr, sizeof(WAVEHDR)); + inwavehdr->dwFlags = 0L; + waveInPrepareHeader(ntsnd_indev[nad], inwavehdr, sizeof(WAVEHDR)); + mmresult = waveInAddBuffer(ntsnd_indev[nad], inwavehdr, + sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) + nt_waveinerror("waveInAddBuffer: %s\n", mmresult); + ntsnd_inphase[nad] = phase = WRAPFWD(phase + 1); + } + if (count == MAXRESYNC) post("resync error 1"); + } + /* Each output buffer which is "ready" is filled with zeros and + queued. */ + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nda]; + for (count = 0; count < MAXRESYNC; count++) + { + WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; + if (!(outwavehdr->dwFlags & WHDR_DONE)) break; + if (outwavehdr->dwFlags & WHDR_PREPARED) + waveOutUnprepareHeader(ntsnd_outdev[nda], + outwavehdr, sizeof(WAVEHDR)); + outwavehdr->dwFlags = 0L; + memset((char *)(ntsnd_outvec[nda][phase].lpData), + 0, (CHANNELS_PER_DEVICE * REALDACBLKSIZE * SAMPSIZE)); + waveOutPrepareHeader(ntsnd_outdev[nda], outwavehdr, + sizeof(WAVEHDR)); + mmresult = waveOutWrite(ntsnd_outdev[nda], outwavehdr, + sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) + nt_waveouterror("waveOutAddBuffer: %s\n", mmresult); + ntsnd_outphase[nda] = phase = WRAPFWD(phase + 1); + } + if (count == MAXRESYNC) post("resync error 2"); + } + +#ifdef MIDI_TIMESTAMP + nt_resetmidisync(); +#endif + +} + +#define LATE 0 +#define RESYNC 1 +#define NOTHING 2 +static int nt_errorcount; +static int nt_resynccount; +static double nt_nextreporttime = -1; + +void nt_logerror(int which) +{ +#if 0 + post("error %d %d", count, which); + if (which < NOTHING) nt_errorcount++; + if (which == RESYNC) nt_resynccount++; + if (sys_getrealtime() > nt_nextreporttime) + { + post("%d audio I/O error%s", nt_errorcount, + (nt_errorcount > 1 ? "s" : "")); + if (nt_resynccount) post("DAC/ADC sync error"); + nt_errorcount = nt_resynccount = 0; + nt_nextreporttime = sys_getrealtime() - 5; + } +#endif +} + +/* system buffer with t_sample types for one tick */ +t_sample *sys_soundout; +t_sample *sys_soundin; +float sys_dacsr; + +int mmio_send_dacs(void) +{ + HMMIO hmmio; + UINT mmresult; + HANDLE hFormat; + int i, j; + short *sp1, *sp2; + float *fp1, *fp2; + int nextfill, doxfer = 0; + int nda, nad; + if (!nt_nwavein && !nt_nwaveout) return (0); + + + if (nt_meters) + { + int i, n; + float maxsamp; + for (i = 0, n = 2 * nt_nwavein * DACBLKSIZE, maxsamp = nt_inmax; + i < n; i++) + { + float f = sys_soundin[i]; + if (f > maxsamp) maxsamp = f; + else if (-f > maxsamp) maxsamp = -f; + } + nt_inmax = maxsamp; + for (i = 0, n = 2 * nt_nwaveout * DACBLKSIZE, maxsamp = nt_outmax; + i < n; i++) + { + float f = sys_soundout[i]; + if (f > maxsamp) maxsamp = f; + else if (-f > maxsamp) maxsamp = -f; + } + nt_outmax = maxsamp; + } + + /* the "fill pointer" nt_fill controls where in the next + I/O buffers we will write and/or read. If it's zero, we + first check whether the buffers are marked "done". */ + + if (!nt_fill) + { + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; + if (!(inwavehdr->dwFlags & WHDR_DONE)) goto idle; + } + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nda]; + WAVEHDR *outwavehdr = + ntsnd_outvec[nda][phase].lpWaveHdr; + if (!(outwavehdr->dwFlags & WHDR_DONE)) goto idle; + } + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + WAVEHDR *inwavehdr = + ntsnd_invec[nad][phase].lpWaveHdr; + if (inwavehdr->dwFlags & WHDR_PREPARED) + waveInUnprepareHeader(ntsnd_indev[nad], + inwavehdr, sizeof(WAVEHDR)); + } + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nda]; + WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; + if (outwavehdr->dwFlags & WHDR_PREPARED) + waveOutUnprepareHeader(ntsnd_outdev[nda], + outwavehdr, sizeof(WAVEHDR)); + } + } + + /* Convert audio output to fixed-point and put it in the output + buffer. */ + for (nda = 0, fp1 = sys_soundout; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nda]; + + for (i = 0, sp1 = (short *)(ntsnd_outvec[nda][phase].lpData) + + CHANNELS_PER_DEVICE * nt_fill; + i < 2; i++, fp1 += DACBLKSIZE, sp1++) + { + for (j = 0, fp2 = fp1, sp2 = sp1; j < DACBLKSIZE; + j++, fp2++, sp2 += CHANNELS_PER_DEVICE) + { + int x1 = 32767.f * *fp2; + if (x1 > 32767) x1 = 32767; + else if (x1 < -32767) x1 = -32767; + *sp2 = x1; + } + } + } + memset(sys_soundout, 0, + (DACBLKSIZE*sizeof(t_sample)*CHANNELS_PER_DEVICE)*nt_nwaveout); + + /* vice versa for the input buffer */ + + for (nad = 0, fp1 = sys_soundin; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + + for (i = 0, sp1 = (short *)(ntsnd_invec[nad][phase].lpData) + + CHANNELS_PER_DEVICE * nt_fill; + i < 2; i++, fp1 += DACBLKSIZE, sp1++) + { + for (j = 0, fp2 = fp1, sp2 = sp1; j < DACBLKSIZE; + j++, fp2++, sp2 += CHANNELS_PER_DEVICE) + { + *fp2 = ((float)(1./32767.)) * (float)(*sp2); + } + } + } + + nt_fill = nt_fill + DACBLKSIZE; + if (nt_fill == REALDACBLKSIZE) + { + nt_fill = 0; + + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + HWAVEIN device = ntsnd_indev[nad]; + WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; + waveInPrepareHeader(device, inwavehdr, sizeof(WAVEHDR)); + mmresult = waveInAddBuffer(device, inwavehdr, sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) + nt_waveinerror("waveInAddBuffer: %s\n", mmresult); + ntsnd_inphase[nad] = WRAPFWD(phase + 1); + } + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nda]; + HWAVEOUT device = ntsnd_outdev[nda]; + WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; + waveOutPrepareHeader(device, outwavehdr, sizeof(WAVEHDR)); + mmresult = waveOutWrite(device, outwavehdr, sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) + nt_waveouterror("waveOutWrite: %s\n", mmresult); + ntsnd_outphase[nda] = WRAPFWD(phase + 1); + } + + /* check for DAC underflow or ADC overflow. */ + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = WRAPBACK(ntsnd_inphase[nad] - 2); + WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; + if (inwavehdr->dwFlags & WHDR_DONE) goto late; + } + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = WRAPBACK(ntsnd_outphase[nda] - 2); + WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; + if (outwavehdr->dwFlags & WHDR_DONE) goto late; + } + } + return (1); + +late: + + nt_logerror(LATE); + nt_resyncaudio(); + return (1); + +idle: + + /* If more than nt_adcjitterbufsallowed ADC buffers are ready + on any input device, resynchronize */ + + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + WAVEHDR *inwavehdr = + ntsnd_invec[nad] + [WRAPFWD(phase + nt_adcjitterbufsallowed)].lpWaveHdr; + if (inwavehdr->dwFlags & WHDR_DONE) + { + nt_resyncaudio(); + return (0); + } + } + + /* test dac sync the same way */ + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nda]; + WAVEHDR *outwavehdr = + ntsnd_outvec[nda] + [WRAPFWD(phase + nt_dacjitterbufsallowed)].lpWaveHdr; + if (outwavehdr->dwFlags & WHDR_DONE) + { + nt_resyncaudio(); + return (0); + } + } +#ifdef MIDI_TIMESTAMP + nt_midisync(); +#endif + return (0); +} + + +static void nt_setchsr(int inchannels, int outchannels, int sr) +{ + int inbytes = inchannels * (DACBLKSIZE*sizeof(float)); + int outbytes = outchannels * (DACBLKSIZE*sizeof(float)); + + if (nt_nwavein) + free(sys_soundin); + if (nt_nwaveout) + free(sys_soundout); + + nt_nwavein = inchannels/CHANNELS_PER_DEVICE; + nt_nwaveout = outchannels/CHANNELS_PER_DEVICE; + sys_dacsr = sr; + + sys_soundin = (t_float *)malloc(inbytes); + memset(sys_soundin, 0, inbytes); + + sys_soundout = (t_float *)malloc(outbytes); + memset(sys_soundout, 0, outbytes); + + nt_advance_samples = (sys_schedadvance * sys_dacsr) / (1000000.); + if (nt_advance_samples < 3 * DACBLKSIZE) + nt_advance_samples = 3 * DACBLKSIZE; +} + +/* ------------------------- MIDI output -------------------------- */ +static void nt_midiouterror(char *s, int err) +{ + char t[256]; + midiOutGetErrorText(err, t, 256); + fprintf(stderr, s, t); +} + +static HMIDIOUT hMidiOut[MAXMIDIOUTDEV]; /* output device */ +static int nt_nmidiout; /* number of devices */ + +static void nt_open_midiout(int nmidiout, int *midioutvec) +{ + UINT result, wRtn; + int i; + int dev; + MIDIOUTCAPS midioutcaps; + if (nmidiout > MAXMIDIOUTDEV) + nmidiout = MAXMIDIOUTDEV; + + dev = 0; + + for (i = 0; i < nmidiout; i++) + { + MIDIOUTCAPS mocap; + result = midiOutOpen(&hMidiOut[dev], midioutvec[i]-1, 0, 0, + CALLBACK_NULL); + wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap, + sizeof(mocap)); + if (result != MMSYSERR_NOERROR) + { + fprintf(stderr,"midiOutOpen: %s\n",midioutcaps.szPname); + nt_midiouterror("midiOutOpen: %s\n", result); + } + else + { + if (sys_verbose) + fprintf(stderr,"midiOutOpen: Open %s as Port %d\n", + midioutcaps.szPname, dev); + dev++; + } + } + nt_nmidiout = dev; +} + +void sys_putmidimess(int portno, int a, int b, int c) +{ + DWORD foo; + MMRESULT res; + if (portno >= 0 && portno < nt_nmidiout) + { + foo = (a & 0xff) | ((b & 0xff) << 8) | ((c & 0xff) << 16); + res = midiOutShortMsg(hMidiOut[portno], foo); + if (res != MMSYSERR_NOERROR) + post("MIDI out error %d", res); + } +} + +void sys_putmidibyte(int portno, int byte) +{ + MMRESULT res; + if (portno >= 0 && portno < nt_nmidiout) + { + res = midiOutShortMsg(hMidiOut[portno], byte); + if (res != MMSYSERR_NOERROR) + post("MIDI out error %d", res); + } +} + +static void nt_close_midiout(void) +{ + int i; + for (i = 0; i < nt_nmidiout; i++) + { + midiOutReset(hMidiOut[i]); + midiOutClose(hMidiOut[i]); + } + nt_nmidiout = 0; +} + +/* -------------------------- MIDI input ---------------------------- */ + +#define INPUT_BUFFER_SIZE 1000 // size of input buffer in events + +static void nt_midiinerror(char *s, int err) +{ + char t[256]; + midiInGetErrorText(err, t, 256); + fprintf(stderr, s, t); +} + + +/* Structure to represent a single MIDI event. + */ + +#define EVNT_F_ERROR 0x00000001L + +typedef struct event_tag +{ + DWORD fdwEvent; + DWORD dwDevice; + LARGE_INTEGER timestamp; + DWORD data; +} EVENT; +typedef EVENT FAR *LPEVENT; + +/* Structure to manage the circular input buffer. + */ +typedef struct circularBuffer_tag +{ + HANDLE hSelf; /* handle to this structure */ + HANDLE hBuffer; /* buffer handle */ + WORD wError; /* error flags */ + DWORD dwSize; /* buffer size (in EVENTS) */ + DWORD dwCount; /* byte count (in EVENTS) */ + LPEVENT lpStart; /* ptr to start of buffer */ + LPEVENT lpEnd; /* ptr to end of buffer (last byte + 1) */ + LPEVENT lpHead; /* ptr to head (next location to fill) */ + LPEVENT lpTail; /* ptr to tail (next location to empty) */ +} CIRCULARBUFFER; +typedef CIRCULARBUFFER FAR *LPCIRCULARBUFFER; + + +/* Structure to pass instance data from the application + to the low-level callback function. + */ +typedef struct callbackInstance_tag +{ + HANDLE hSelf; + DWORD dwDevice; + LPCIRCULARBUFFER lpBuf; +} CALLBACKINSTANCEDATA; +typedef CALLBACKINSTANCEDATA FAR *LPCALLBACKINSTANCEDATA; + +/* Function prototypes + */ +LPCALLBACKINSTANCEDATA FAR PASCAL AllocCallbackInstanceData(void); +void FAR PASCAL FreeCallbackInstanceData(LPCALLBACKINSTANCEDATA lpBuf); + +LPCIRCULARBUFFER AllocCircularBuffer(DWORD dwSize); +void FreeCircularBuffer(LPCIRCULARBUFFER lpBuf); +WORD FAR PASCAL GetEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent); + +// Callback instance data pointers +LPCALLBACKINSTANCEDATA lpCallbackInstanceData[MAXMIDIINDEV]; + +UINT wNumDevices = 0; // Number of MIDI input devices opened +BOOL bRecordingEnabled = 1; // Enable/disable recording flag +int nNumBufferLines = 0; // Number of lines in display buffer +RECT rectScrollClip; // Clipping rectangle for scrolling + +LPCIRCULARBUFFER lpInputBuffer; // Input buffer structure +EVENT incomingEvent; // Incoming MIDI event structure + +MIDIINCAPS midiInCaps[MAXMIDIINDEV]; // Device capabilities structures +HMIDIIN hMidiIn[MAXMIDIINDEV]; // MIDI input device handles + + +/* AllocCallbackInstanceData - Allocates a CALLBACKINSTANCEDATA + * structure. This structure is used to pass information to the + * low-level callback function, each time it receives a message. + * + * Because this structure is accessed by the low-level callback + * function, it must be allocated using GlobalAlloc() with the + * GMEM_SHARE and GMEM_MOVEABLE flags and page-locked with + * GlobalPageLock(). + * + * Params: void + * + * Return: A pointer to the allocated CALLBACKINSTANCE data structure. + */ +LPCALLBACKINSTANCEDATA FAR PASCAL AllocCallbackInstanceData(void) +{ + HANDLE hMem; + LPCALLBACKINSTANCEDATA lpBuf; + + /* Allocate and lock global memory. + */ + hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, + (DWORD)sizeof(CALLBACKINSTANCEDATA)); + if(hMem == NULL) + return NULL; + + lpBuf = (LPCALLBACKINSTANCEDATA)GlobalLock(hMem); + if(lpBuf == NULL){ + GlobalFree(hMem); + return NULL; + } + + /* Page lock the memory. + */ + //GlobalPageLock((HGLOBAL)HIWORD(lpBuf)); + + /* Save the handle. + */ + lpBuf->hSelf = hMem; + + return lpBuf; +} + +/* FreeCallbackInstanceData - Frees the given CALLBACKINSTANCEDATA structure. + * + * Params: lpBuf - Points to the CALLBACKINSTANCEDATA structure to be freed. + * + * Return: void + */ +void FAR PASCAL FreeCallbackInstanceData(LPCALLBACKINSTANCEDATA lpBuf) +{ + HANDLE hMem; + + /* Save the handle until we're through here. + */ + hMem = lpBuf->hSelf; + + /* Free the structure. + */ + //GlobalPageUnlock((HGLOBAL)HIWORD(lpBuf)); + GlobalUnlock(hMem); + GlobalFree(hMem); +} + + +/* + * AllocCircularBuffer - Allocates memory for a CIRCULARBUFFER structure + * and a buffer of the specified size. Each memory block is allocated + * with GlobalAlloc() using GMEM_SHARE and GMEM_MOVEABLE flags, locked + * with GlobalLock(), and page-locked with GlobalPageLock(). + * + * Params: dwSize - The size of the buffer, in events. + * + * Return: A pointer to a CIRCULARBUFFER structure identifying the + * allocated display buffer. NULL if the buffer could not be allocated. + */ + + +LPCIRCULARBUFFER AllocCircularBuffer(DWORD dwSize) +{ + HANDLE hMem; + LPCIRCULARBUFFER lpBuf; + LPEVENT lpMem; + + /* Allocate and lock a CIRCULARBUFFER structure. + */ + hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, + (DWORD)sizeof(CIRCULARBUFFER)); + if(hMem == NULL) + return NULL; + + lpBuf = (LPCIRCULARBUFFER)GlobalLock(hMem); + if(lpBuf == NULL) + { + GlobalFree(hMem); + return NULL; + } + + /* Page lock the memory. Global memory blocks accessed by + * low-level callback functions must be page locked. + */ +#ifndef _WIN32 + GlobalSmartPageLock((HGLOBAL)HIWORD(lpBuf)); +#endif + + /* Save the memory handle. + */ + lpBuf->hSelf = hMem; + + /* Allocate and lock memory for the actual buffer. + */ + hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, dwSize * sizeof(EVENT)); + if(hMem == NULL) + { +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf)); +#endif + GlobalUnlock(lpBuf->hSelf); + GlobalFree(lpBuf->hSelf); + return NULL; + } + + lpMem = (LPEVENT)GlobalLock(hMem); + if(lpMem == NULL) + { + GlobalFree(hMem); +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf)); +#endif + GlobalUnlock(lpBuf->hSelf); + GlobalFree(lpBuf->hSelf); + return NULL; + } + + /* Page lock the memory. Global memory blocks accessed by + * low-level callback functions must be page locked. + */ +#ifndef _WIN32 + GlobalSmartPageLock((HGLOBAL)HIWORD(lpMem)); +#endif + + /* Set up the CIRCULARBUFFER structure. + */ + lpBuf->hBuffer = hMem; + lpBuf->wError = 0; + lpBuf->dwSize = dwSize; + lpBuf->dwCount = 0L; + lpBuf->lpStart = lpMem; + lpBuf->lpEnd = lpMem + dwSize; + lpBuf->lpTail = lpMem; + lpBuf->lpHead = lpMem; + + return lpBuf; +} + +/* FreeCircularBuffer - Frees the memory for the given CIRCULARBUFFER + * structure and the memory for the buffer it references. + * + * Params: lpBuf - Points to the CIRCULARBUFFER to be freed. + * + * Return: void + */ +void FreeCircularBuffer(LPCIRCULARBUFFER lpBuf) +{ + HANDLE hMem; + + /* Free the buffer itself. + */ +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf->lpStart)); +#endif + GlobalUnlock(lpBuf->hBuffer); + GlobalFree(lpBuf->hBuffer); + + /* Free the CIRCULARBUFFER structure. + */ + hMem = lpBuf->hSelf; +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf)); +#endif + GlobalUnlock(hMem); + GlobalFree(hMem); +} + +/* GetEvent - Gets a MIDI event from the circular input buffer. Events + * are removed from the buffer. The corresponding PutEvent() function + * is called by the low-level callback function, so it must reside in + * the callback DLL. PutEvent() is defined in the CALLBACK.C module. + * + * Params: lpBuf - Points to the circular buffer. + * lpEvent - Points to an EVENT structure that is filled with the + * retrieved event. + * + * Return: Returns non-zero if successful, zero if there are no + * events to get. + */ +WORD FAR PASCAL GetEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent) +{ + /* If no event available, return */ + if (!wNumDevices || lpBuf->dwCount <= 0) return (0); + + /* Get the event. + */ + *lpEvent = *lpBuf->lpTail; + + /* Decrement the byte count, bump the tail pointer. + */ + --lpBuf->dwCount; + ++lpBuf->lpTail; + + /* Wrap the tail pointer, if necessary. + */ + if(lpBuf->lpTail >= lpBuf->lpEnd) + lpBuf->lpTail = lpBuf->lpStart; + + return 1; +} + +/* PutEvent - Puts an EVENT in a CIRCULARBUFFER. If the buffer is full, + * it sets the wError element of the CIRCULARBUFFER structure + * to be non-zero. + * + * Params: lpBuf - Points to the CIRCULARBUFFER. + * lpEvent - Points to the EVENT. + * + * Return: void +*/ + +void FAR PASCAL PutEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent) +{ + /* If the buffer is full, set an error and return. + */ + if(lpBuf->dwCount >= lpBuf->dwSize){ + lpBuf->wError = 1; + return; + } + + /* Put the event in the buffer, bump the head pointer and the byte count. + */ + *lpBuf->lpHead = *lpEvent; + + ++lpBuf->lpHead; + ++lpBuf->dwCount; + + /* Wrap the head pointer, if necessary. + */ + if(lpBuf->lpHead >= lpBuf->lpEnd) + lpBuf->lpHead = lpBuf->lpStart; +} + +/* midiInputHandler - Low-level callback function to handle MIDI input. + * Installed by midiInOpen(). The input handler takes incoming + * MIDI events and places them in the circular input buffer. It then + * notifies the application by posting a MM_MIDIINPUT message. + * + * This function is accessed at interrupt time, so it should be as + * fast and efficient as possible. You can't make any + * Windows calls here, except PostMessage(). The only Multimedia + * Windows call you can make are timeGetSystemTime(), midiOutShortMsg(). + * + * + * Param: hMidiIn - Handle for the associated input device. + * wMsg - One of the MIM_***** messages. + * dwInstance - Points to CALLBACKINSTANCEDATA structure. + * dwParam1 - MIDI data. + * dwParam2 - Timestamp (in milliseconds) + * + * Return: void + */ +void FAR PASCAL midiInputHandler( +HMIDIIN hMidiIn, +WORD wMsg, +DWORD dwInstance, +DWORD dwParam1, +DWORD dwParam2) +{ + EVENT event; + + switch(wMsg) + { + case MIM_OPEN: + break; + + /* The only error possible is invalid MIDI data, so just pass + * the invalid data on so we'll see it. + */ + case MIM_ERROR: + case MIM_DATA: + event.fdwEvent = (wMsg == MIM_ERROR) ? EVNT_F_ERROR : 0; + event.dwDevice = ((LPCALLBACKINSTANCEDATA)dwInstance)->dwDevice; + event.data = dwParam1; +#ifdef MIDI_TIMESTAMP + event.timestamp = timeGetSystemTime(); +#endif + /* Put the MIDI event in the circular input buffer. + */ + + PutEvent(((LPCALLBACKINSTANCEDATA)dwInstance)->lpBuf, + (LPEVENT) &event); + + break; + + default: + break; + } +} + +void nt_open_midiin(int nmidiin, int *midiinvec) +{ + UINT wRtn; + char szErrorText[256]; + unsigned int i; + unsigned int ndev = 0; + /* Allocate a circular buffer for low-level MIDI input. This buffer + * is filled by the low-level callback function and emptied by the + * application. + */ + lpInputBuffer = AllocCircularBuffer((DWORD)(INPUT_BUFFER_SIZE)); + if (lpInputBuffer == NULL) + { + printf("Not enough memory available for input buffer.\n"); + return; + } + + /* Open all MIDI input devices after allocating and setting up + * instance data for each device. The instance data is used to + * pass buffer management information between the application and + * the low-level callback function. It also includes a device ID, + * a handle to the MIDI Mapper, and a handle to the application's + * display window, so the callback can notify the window when input + * data is available. A single callback function is used to service + * all opened input devices. + */ + for (i=0; (i<(unsigned)nmidiin) && (i<MAXMIDIINDEV); i++) + { + if ((lpCallbackInstanceData[ndev] = AllocCallbackInstanceData()) == NULL) + { + printf("Not enough memory available.\n"); + FreeCircularBuffer(lpInputBuffer); + return; + } + lpCallbackInstanceData[i]->dwDevice = i; + lpCallbackInstanceData[i]->lpBuf = lpInputBuffer; + + wRtn = midiInOpen((LPHMIDIIN)&hMidiIn[ndev], + midiinvec[i] - 1, + (DWORD)midiInputHandler, + (DWORD)lpCallbackInstanceData[ndev], + CALLBACK_FUNCTION); + if (wRtn) + { + FreeCallbackInstanceData(lpCallbackInstanceData[ndev]); + nt_midiinerror("midiInOpen: %s\n", wRtn); + } + else ndev++; + } + + /* Start MIDI input. + */ + for (i=0; i<ndev; i++) + { + if (hMidiIn[i]) + midiInStart(hMidiIn[i]); + } + wNumDevices = ndev; +} + +static void nt_close_midiin(void) +{ + unsigned int i; + /* Stop, reset, close MIDI input. Free callback instance data. + */ + + for (i=0; (i<wNumDevices) && (i<MAXMIDIINDEV); i++) + { + if (hMidiIn[i]) + { + if (sys_verbose) + post("closing MIDI input %d...", i); + midiInStop(hMidiIn[i]); + midiInReset(hMidiIn[i]); + midiInClose(hMidiIn[i]); + FreeCallbackInstanceData(lpCallbackInstanceData[i]); + } + } + + /* Free input buffer. + */ + if (lpInputBuffer) + FreeCircularBuffer(lpInputBuffer); + + if (sys_verbose) + post("...done"); + wNumDevices = 0; +} + +void inmidi_noteon(int portno, int channel, int pitch, int velo); +void inmidi_controlchange(int portno, int channel, int ctlnumber, int value); +void inmidi_programchange(int portno, int channel, int value); +void inmidi_pitchbend(int portno, int channel, int value); +void inmidi_aftertouch(int portno, int channel, int value); +void inmidi_polyaftertouch(int portno, int channel, int pitch, int value); +void inmidi_realtimein(int portno, int rtmsg); + +void sys_poll_midi(void) +{ + static EVENT nt_nextevent; + static int nt_isnextevent; + static double nt_nexteventtime; + + while (1) + { + if (!nt_isnextevent) + { + if (!GetEvent(lpInputBuffer, &nt_nextevent)) break; + nt_isnextevent = 1; +#ifdef MIDI_TIMESTAMP + nt_nexteventtime = nt_midigettimefor(&foo.timestamp); +#endif + } +#ifdef MIDI_TIMESTAMP + if (0.001 * clock_gettimesince(initsystime) >= nt_nexteventtime) +#endif + { + int msgtype = ((nt_nextevent.data & 0xf0) >> 4) - 8; + int commandbyte = nt_nextevent.data & 0xff; + int byte1 = (nt_nextevent.data >> 8) & 0xff; + int byte2 = (nt_nextevent.data >> 16) & 0xff; + int portno = nt_nextevent.dwDevice; + switch (msgtype) + { + case 0: + case 1: + case 2: + case 3: + case 6: + sys_midibytein(portno, commandbyte); + sys_midibytein(portno, byte1); + sys_midibytein(portno, byte2); + break; + case 4: + case 5: + sys_midibytein(portno, commandbyte); + sys_midibytein(portno, byte1); + break; + case 7: + sys_midibytein(portno, commandbyte); + break; + } + nt_isnextevent = 0; + } + } +} + +/* ------------------- public routines -------------------------- */ + +void sys_open_audio(int naudioindev, int *audioindev, + int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, + int nchoutdev, int *choutdev, int rate) /* IOhannes */ +{ + int inchans, outchans; + if (nchindev < 0) + inchans = (nchindev < 1 ? -1 : chindev[0]); + else + { + int i = nchindev; + int *l = chindev; + inchans = 0; + while (i--) + inchans += *l++; + } + if (nchoutdev<0) + outchans = (nchoutdev < 1 ? -1 : choutdev[0]); + else + { + int i = nchoutdev; + int *l = choutdev; + outchans = 0; + while (i--) + outchans += *l++; + } + if (inchans < 0) + inchans = DEFAULTCHANS; + if (outchans < 0) + outchans = DEFAULTCHANS; + if (inchans & 1) + { + post("input channels rounded up to even number"); + inchans += 1; + } + if (outchans & 1) + { + post("output channels rounded up to even number"); + outchans += 1; + } + if (inchans > NT_MAXCH) + inchans = NT_MAXCH; + if (outchans > NT_MAXCH) + outchans = NT_MAXCH; + if (sys_verbose) + post("channels in %d, out %d", inchans, outchans); + if (rate < 1) + rate = DEFAULTSRATE; + nt_setchsr(inchans, outchans, rate); + if (nt_whichapi == API_PORTAUDIO) + { + int blocksize = (nt_blocksize ? nt_blocksize : 256); + if (blocksize != (1 << ilog2(blocksize))) + post("warning: blocksize adjusted to power of 2: %d", + (blocksize = (1 << ilog2(blocksize)))); + pa_open_audio(inchans, outchans, rate, sys_soundin, sys_soundout, + blocksize, nt_advance_samples/blocksize, + (naudioindev < 1 ? -1 : audioindev[0]), + (naudiooutdev < 1 ? -1 : audiooutdev[0])); + } + else + { + nt_nwavein = inchans / 2; + nt_nwaveout = outchans / 2; + nt_whichdac = (naudiooutdev < 1 ? (nt_nwaveout > 1 ? 0 : -1) : audiooutdev[0] - 1); + nt_whichadc = (naudioindev < 1 ? (nt_nwavein > 1 ? 0 : -1) : audioindev[0] - 1); + if (naudiooutdev > 1 || naudioindev > 1) + post("separate audio device choice not supported; using sequential devices."); + if (nt_blocksize) + post("warning: blocksize not settable for MMIO, just ASIO"); + mmio_open_audio(); + } +} + +void sys_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec) +{ + if (nmidiout) + nt_open_midiout(nmidiout, midioutvec); + if (nmidiin) + { + post( + "midi input enabled; warning, don't close the DOS window directly!"); + nt_open_midiin(nmidiin, midiinvec); + } + else post("not using MIDI input (use 'pd -midiindev 1' to override)"); +} + +float sys_getsr(void) +{ + return (sys_dacsr); +} + +int sys_get_inchannels(void) +{ + return (2 * nt_nwavein); +} + +int sys_get_outchannels(void) +{ + return (2 * nt_nwaveout); +} + +void sys_audiobuf(int n) +{ + /* set the size, in msec, of the audio FIFO. It's incorrect to + calculate this on the basis of 44100 sample rate; really, the + work should have been done in nt_setchsr(). */ + int nbuf = n * (44100./(REALDACBLKSIZE * 1000.)); + if (nbuf >= MAXBUFFER) + { + fprintf(stderr, "pd: audio buffering maxed out to %d\n", + (int)(MAXBUFFER * ((REALDACBLKSIZE * 1000.)/44100.))); + nbuf = MAXBUFFER; + } + else if (nbuf < 4) nbuf = 4; + fprintf(stderr, "%d audio buffers\n", nbuf); + nt_naudiobuffer = nbuf; + if (nt_adcjitterbufsallowed > nbuf - 2) + nt_adcjitterbufsallowed = nbuf - 2; + if (nt_dacjitterbufsallowed > nbuf - 2) + nt_dacjitterbufsallowed = nbuf - 2; + sys_schedadvance = 1000 * n; +} + +void sys_getmeters(float *inmax, float *outmax) +{ + if (inmax) + { + nt_meters = 1; + *inmax = nt_inmax; + *outmax = nt_outmax; + } + else + nt_meters = 0; + nt_inmax = nt_outmax = 0; +} + +void sys_reportidle(void) +{ +} + +int sys_send_dacs(void) +{ + if (nt_whichapi == API_PORTAUDIO) + return (pa_send_dacs()); + else return (mmio_send_dacs()); +} + +void sys_close_audio( void) +{ + if (nt_whichapi == API_PORTAUDIO) + pa_close_audio(); + else mmio_close_audio(); +} + +void sys_close_midi( void) +{ + nt_close_midiin(); + nt_close_midiout(); +} + +void sys_setblocksize(int n) +{ + if (n < 1) + n = 1; + nt_blocksize = n; +} + +/* ----------- public routines which are only defined for MSW/NT ---------- */ + +/* select between MMIO and ASIO audio APIs */ +void nt_set_sound_api(int which) +{ + nt_whichapi = which; + if (sys_verbose) + post("nt_whichapi %d", nt_whichapi); +} + +/* list the audio and MIDI device names */ +void sys_listdevs(void) +{ + UINT wRtn, ndevices; + unsigned int i; + + /* for MIDI and audio in and out, get the number of devices. + Then get the capabilities of each device and print its description. */ + + ndevices = midiInGetNumDevs(); + for (i = 0; i < ndevices; i++) + { + MIDIINCAPS micap; + wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) &micap, + sizeof(micap)); + if (wRtn) nt_midiinerror("midiInGetDevCaps: %s\n", wRtn); + else fprintf(stderr, + "MIDI input device #%d: %s\n", i+1, micap.szPname); + } + + ndevices = midiOutGetNumDevs(); + for (i = 0; i < ndevices; i++) + { + MIDIOUTCAPS mocap; + wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap, + sizeof(mocap)); + if (wRtn) nt_midiouterror("midiOutGetDevCaps: %s\n", wRtn); + else fprintf(stderr, + "MIDI output device #%d: %s\n", i+1, mocap.szPname); + } + + if (nt_whichapi == API_PORTAUDIO) + { + pa_listdevs(); + return; + } + ndevices = waveInGetNumDevs(); + for (i = 0; i < ndevices; i++) + { + WAVEINCAPS wicap; + wRtn = waveInGetDevCaps(i, (LPWAVEINCAPS) &wicap, + sizeof(wicap)); + if (wRtn) nt_waveinerror("waveInGetDevCaps: %s\n", wRtn); + else fprintf(stderr, + "audio input device #%d: %s\n", i+1, wicap.szPname); + } + + ndevices = waveOutGetNumDevs(); + for (i = 0; i < ndevices; i++) + { + WAVEOUTCAPS wocap; + wRtn = waveOutGetDevCaps(i, (LPWAVEOUTCAPS) &wocap, + sizeof(wocap)); + if (wRtn) nt_waveouterror("waveOutGetDevCaps: %s\n", wRtn); + else fprintf(stderr, + "audio output device #%d: %s\n", i+1, wocap.szPname); + } +} + +void nt_soundindev(int which) +{ + nt_whichadc = which - 1; +} + +void nt_soundoutdev(int which) +{ + nt_whichdac = which - 1; +} + +void glob_audio(void *dummy, t_floatarg fadc, t_floatarg fdac) +{ + int adc = fadc, dac = fdac; + if (!dac && !adc) + post("%d channels in, %d channels out", + 2 * nt_nwavein, 2 * nt_nwaveout); + else + { + sys_close_audio(); + sys_open_audio(1, 0, 1, 0, /* dummy parameters */ + 1, &adc, 1, &dac, sys_dacsr); + } +} + |