aboutsummaryrefslogtreecommitdiff
path: root/pd/src/s_nt.c
diff options
context:
space:
mode:
Diffstat (limited to 'pd/src/s_nt.c')
-rw-r--r--pd/src/s_nt.c1586
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);
+ }
+}
+