1 /* Copyright (c) 1997-1999 Miller Puckette.
2 * For information on usage and redistribution, and for a DISCLAIMER OF ALL
3 * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
5 /* modified 2/98 by Winfried Ritsch to deal with up to 4 synchronized
6 "wave" devices, which is how ADAT boards appear to the WAVE API. */
16 /* ------------------------- audio -------------------------- */
18 static void nt_close_midiin(void);
19 static void nt_noresync( void);
21 static void postflags(void);
23 #define NAPORTS 16 /* wini hack for multiple ADDA devices */
24 #define CHANNELS_PER_DEVICE 2
25 #define DEFAULTCHANS 2
26 #define DEFAULTSRATE 44100
29 int nt_realdacblksize
;
30 #define DEFREALDACBLKSIZE (4 * DEFDACBLKSIZE) /* larger underlying bufsize */
32 #define MAXBUFFER 100 /* number of buffers in use at maximum advance */
33 #define DEFBUFFER 30 /* default is about 30x6 = 180 msec! */
34 static int nt_naudiobuffer
= DEFBUFFER
;
35 float sys_dacsr
= DEFAULTSRATE
;
37 static int nt_whichapi
= API_MMIO
;
38 static int nt_meters
; /* true if we're metering */
39 static float nt_inmax
; /* max input amplitude */
40 static float nt_outmax
; /* max output amplitude */
41 static int nt_nwavein
, nt_nwaveout
; /* number of WAVE devices in and out */
46 HPSTR lpData
; // pointer to waveform data memory
48 WAVEHDR
*lpWaveHdr
; // pointer to header structure
51 t_sbuf ntsnd_outvec
[NAPORTS
][MAXBUFFER
]; /* circular buffer array */
52 HWAVEOUT ntsnd_outdev
[NAPORTS
]; /* output device */
53 static int ntsnd_outphase
[NAPORTS
]; /* index of next buffer to send */
55 t_sbuf ntsnd_invec
[NAPORTS
][MAXBUFFER
]; /* circular buffer array */
56 HWAVEIN ntsnd_indev
[NAPORTS
]; /* input device */
57 static int ntsnd_inphase
[NAPORTS
]; /* index of next buffer to read */
59 static void nt_waveinerror(char *s
, int err
)
62 waveInGetErrorText(err
, t
, 256);
63 fprintf(stderr
, s
, t
);
66 static void nt_waveouterror(char *s
, int err
)
69 waveOutGetErrorText(err
, t
, 256);
70 fprintf(stderr
, s
, t
);
73 static void wave_prep(t_sbuf
*bp
, int setdone
)
79 * Allocate and lock memory for the waveform data. The memory
80 * for waveform data must be globally allocated with
81 * GMEM_MOVEABLE and GMEM_SHARE flags.
85 GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
,
86 (DWORD
) (CHANNELS_PER_DEVICE
* SAMPSIZE
* nt_realdacblksize
))))
87 printf("alloc 1 failed\n");
90 (HPSTR
) GlobalLock(bp
->hData
)))
91 printf("lock 1 failed\n");
93 /* Allocate and lock memory for the header. */
96 GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, (DWORD
) sizeof(WAVEHDR
))))
97 printf("alloc 2 failed\n");
99 if (!(wh
= bp
->lpWaveHdr
=
100 (WAVEHDR
*) GlobalLock(bp
->hWaveHdr
)))
101 printf("lock 2 failed\n");
103 for (i
= CHANNELS_PER_DEVICE
* nt_realdacblksize
,
104 sp
= (short *)bp
->lpData
; i
--; )
107 wh
->lpData
= bp
->lpData
;
108 wh
->dwBufferLength
= (CHANNELS_PER_DEVICE
* SAMPSIZE
* nt_realdacblksize
);
113 /* optionally (for writing) set DONE flag as if we had queued them */
115 wh
->dwFlags
= WHDR_DONE
;
118 static UINT nt_whichdac
= WAVE_MAPPER
, nt_whichadc
= WAVE_MAPPER
;
120 int mmio_do_open_audio(void)
126 static int naudioprepped
= 0, nindevsprepped
= 0, noutdevsprepped
= 0;
128 post("%d devices in, %d devices out",
129 nt_nwavein
, nt_nwaveout
);
131 form
.wf
.wFormatTag
= WAVE_FORMAT_PCM
;
132 form
.wf
.nChannels
= CHANNELS_PER_DEVICE
;
133 form
.wf
.nSamplesPerSec
= sys_dacsr
;
134 form
.wf
.nAvgBytesPerSec
= sys_dacsr
* (CHANNELS_PER_DEVICE
* SAMPSIZE
);
135 form
.wf
.nBlockAlign
= CHANNELS_PER_DEVICE
* SAMPSIZE
;
136 form
.wBitsPerSample
= 8 * SAMPSIZE
;
138 if (nt_nwavein
<= 1 && nt_nwaveout
<= 1)
141 if (nindevsprepped
< nt_nwavein
)
143 for (i
= nindevsprepped
; i
< nt_nwavein
; i
++)
144 for (j
= 0; j
< naudioprepped
; j
++)
145 wave_prep(&ntsnd_invec
[i
][j
], 0);
146 nindevsprepped
= nt_nwavein
;
148 if (noutdevsprepped
< nt_nwaveout
)
150 for (i
= noutdevsprepped
; i
< nt_nwaveout
; i
++)
151 for (j
= 0; j
< naudioprepped
; j
++)
152 wave_prep(&ntsnd_outvec
[i
][j
], 1);
153 noutdevsprepped
= nt_nwaveout
;
155 if (naudioprepped
< nt_naudiobuffer
)
157 for (j
= naudioprepped
; j
< nt_naudiobuffer
; j
++)
159 for (i
= 0; i
< nt_nwavein
; i
++)
160 wave_prep(&ntsnd_invec
[i
][j
], 0);
161 for (i
= 0; i
< nt_nwaveout
; i
++)
162 wave_prep(&ntsnd_outvec
[i
][j
], 1);
164 naudioprepped
= nt_naudiobuffer
;
166 for (nad
=0; nad
< nt_nwavein
; nad
++)
168 /* Open waveform device(s), sucessively numbered, for input */
170 mmresult
= waveInOpen(&ntsnd_indev
[nad
], nt_whichadc
+nad
,
171 (WAVEFORMATEX
*)(&form
), 0L, 0L, CALLBACK_NULL
);
174 printf("opened adc device %d with return %d\n",
175 nt_whichadc
+nad
,mmresult
);
177 if (mmresult
!= MMSYSERR_NOERROR
)
179 nt_waveinerror("waveInOpen: %s\n", mmresult
);
180 nt_nwavein
= nad
; /* nt_nwavein = 0 wini */
184 for (i
= 0; i
< nt_naudiobuffer
; i
++)
186 mmresult
= waveInPrepareHeader(ntsnd_indev
[nad
],
187 ntsnd_invec
[nad
][i
].lpWaveHdr
, sizeof(WAVEHDR
));
188 if (mmresult
!= MMSYSERR_NOERROR
)
189 nt_waveinerror("waveinprepareheader: %s\n", mmresult
);
190 mmresult
= waveInAddBuffer(ntsnd_indev
[nad
],
191 ntsnd_invec
[nad
][i
].lpWaveHdr
, sizeof(WAVEHDR
));
192 if (mmresult
!= MMSYSERR_NOERROR
)
193 nt_waveinerror("waveInAddBuffer: %s\n", mmresult
);
197 /* quickly start them all together */
198 for (nad
= 0; nad
< nt_nwavein
; nad
++)
199 waveInStart(ntsnd_indev
[nad
]);
201 for (nda
= 0; nda
< nt_nwaveout
; nda
++)
203 /* Open a waveform device for output in sucessiv device numbering*/
204 mmresult
= waveOutOpen(&ntsnd_outdev
[nda
], nt_whichdac
+ nda
,
205 (WAVEFORMATEX
*)(&form
), 0L, 0L, CALLBACK_NULL
);
208 fprintf(stderr
,"opened dac device %d, with return %d\n",
209 nt_whichdac
+nda
, mmresult
);
211 if (mmresult
!= MMSYSERR_NOERROR
)
213 fprintf(stderr
,"Wave out open device %d + %d\n",nt_whichdac
,nda
);
214 nt_waveouterror("waveOutOpen device: %s\n", mmresult
);
222 void mmio_close_audio( void)
227 post("closing audio...");
229 for (nda
=0; nda
< nt_nwaveout
; nda
++) /*if (nt_nwaveout) wini */
231 errcode
= waveOutReset(ntsnd_outdev
[nda
]);
232 if (errcode
!= MMSYSERR_NOERROR
)
233 printf("error resetting output %d: %d\n", nda
, errcode
);
234 errcode
= waveOutClose(ntsnd_outdev
[nda
]);
235 if (errcode
!= MMSYSERR_NOERROR
)
236 printf("error closing output %d: %d\n",nda
, errcode
);
240 for(nad
=0; nad
< nt_nwavein
;nad
++) /* if (nt_nwavein) wini */
242 errcode
= waveInReset(ntsnd_indev
[nad
]);
243 if (errcode
!= MMSYSERR_NOERROR
)
244 printf("error resetting input: %d\n", errcode
);
245 errcode
= waveInClose(ntsnd_indev
[nad
]);
246 if (errcode
!= MMSYSERR_NOERROR
)
247 printf("error closing input: %d\n", errcode
);
253 #define ADCJITTER 10 /* We tolerate X buffers of jitter by default */
256 static int nt_adcjitterbufsallowed
= ADCJITTER
;
257 static int nt_dacjitterbufsallowed
= DACJITTER
;
259 /* ------------- MIDI time stamping from audio clock ------------ */
261 #ifdef MIDI_TIMESTAMP
263 static double nt_hibuftime
;
264 static double initsystime
= -1;
266 /* call this whenever we reset audio */
267 static void nt_resetmidisync(void)
269 initsystime
= clock_getsystime();
270 nt_hibuftime
= sys_getrealtime();
273 /* call this whenever we're idled waiting for audio to be ready.
274 The routine maintains a high and low water point for the difference
275 between real and DAC time. */
277 static void nt_midisync(void)
279 double jittersec
, diff
;
281 if (initsystime
== -1) nt_resetmidisync();
282 jittersec
= (nt_dacjitterbufsallowed
> nt_adcjitterbufsallowed
?
283 nt_dacjitterbufsallowed
: nt_adcjitterbufsallowed
)
284 * nt_realdacblksize
/ sys_getsr();
285 diff
= sys_getrealtime() - 0.001 * clock_gettimesince(initsystime
);
286 if (diff
> nt_hibuftime
) nt_hibuftime
= diff
;
287 if (diff
< nt_hibuftime
- jittersec
)
289 post("jitter excess %d %f", dac
, diff
);
294 static double nt_midigettimefor(LARGE_INTEGER timestamp
)
296 /* this is broken now... used to work when "timestamp" was derived from
297 QueryPerformanceCounter() instead of the gates approved
298 timeGetSystemTime() call in the MIDI callback routine below. */
299 return (nt_tixtotime(timestamp
) - nt_hibuftime
);
301 #endif /* MIDI_TIMESTAMP */
304 static int nt_fill
= 0;
305 #define WRAPFWD(x) ((x) >= nt_naudiobuffer ? (x) - nt_naudiobuffer: (x))
306 #define WRAPBACK(x) ((x) < 0 ? (x) + nt_naudiobuffer: (x))
307 #define MAXRESYNC 500
309 #if 0 /* this is used for debugging */
310 static void nt_printaudiostatus(void)
313 for (nad
= 0; nad
< nt_nwavein
; nad
++)
315 int phase
= ntsnd_inphase
[nad
];
316 int phase2
= phase
, phase3
= WRAPFWD(phase2
), count
, ntrans
= 0;
317 int firstphasedone
= -1, firstphasebusy
= -1;
318 for (count
= 0; count
< nt_naudiobuffer
; count
++)
321 (ntsnd_invec
[nad
][phase2
].lpWaveHdr
->dwFlags
& WHDR_DONE
);
323 (ntsnd_invec
[nad
][phase3
].lpWaveHdr
->dwFlags
& WHDR_DONE
);
324 if (donethis
&& !donenext
)
326 if (firstphasebusy
>= 0) goto multipleadc
;
327 firstphasebusy
= count
;
329 if (!donethis
&& donenext
)
331 if (firstphasedone
>= 0) goto multipleadc
;
332 firstphasedone
= count
;
335 phase3
= WRAPFWD(phase2
+ 1);
337 post("nad %d phase %d busy %d done %d", nad
, phase
, firstphasebusy
,
341 startpost("nad %d phase %d: oops:", nad
, phase
);
342 for (count
= 0; count
< nt_naudiobuffer
; count
++)
346 (ntsnd_invec
[nad
][count
].lpWaveHdr
->dwFlags
& WHDR_DONE
));
351 for (nda
= 0; nda
< nt_nwaveout
; nda
++)
353 int phase
= ntsnd_outphase
[nad
];
354 int phase2
= phase
, phase3
= WRAPFWD(phase2
), count
, ntrans
= 0;
355 int firstphasedone
= -1, firstphasebusy
= -1;
356 for (count
= 0; count
< nt_naudiobuffer
; count
++)
359 (ntsnd_outvec
[nda
][phase2
].lpWaveHdr
->dwFlags
& WHDR_DONE
);
361 (ntsnd_outvec
[nda
][phase3
].lpWaveHdr
->dwFlags
& WHDR_DONE
);
362 if (donethis
&& !donenext
)
364 if (firstphasebusy
>= 0) goto multipledac
;
365 firstphasebusy
= count
;
367 if (!donethis
&& donenext
)
369 if (firstphasedone
>= 0) goto multipledac
;
370 firstphasedone
= count
;
373 phase3
= WRAPFWD(phase2
+ 1);
375 if (firstphasebusy
< 0) post("nda %d phase %d all %d",
376 nda
, phase
, (ntsnd_outvec
[nad
][0].lpWaveHdr
->dwFlags
& WHDR_DONE
));
377 else post("nda %d phase %d busy %d done %d", nda
, phase
, firstphasebusy
,
381 startpost("nda %d phase %d: oops:", nda
, phase
);
382 for (count
= 0; count
< nt_naudiobuffer
; count
++)
386 (ntsnd_outvec
[nad
][count
].lpWaveHdr
->dwFlags
& WHDR_DONE
));
394 /* this is a hack to avoid ever resyncing audio pointers in case for whatever
395 reason the sync testing below gives false positives. */
397 static int nt_resync_cancelled
;
399 static void nt_noresync( void)
401 nt_resync_cancelled
= 1;
404 static void nt_resyncaudio(void)
408 if (nt_resync_cancelled
)
410 /* for each open input device, eat all buffers which are marked
411 ready. The next one will thus be "busy". */
412 post("resyncing audio");
413 for (nad
= 0; nad
< nt_nwavein
; nad
++)
415 int phase
= ntsnd_inphase
[nad
];
416 for (count
= 0; count
< MAXRESYNC
; count
++)
418 WAVEHDR
*inwavehdr
= ntsnd_invec
[nad
][phase
].lpWaveHdr
;
419 if (!(inwavehdr
->dwFlags
& WHDR_DONE
)) break;
420 if (inwavehdr
->dwFlags
& WHDR_PREPARED
)
421 waveInUnprepareHeader(ntsnd_indev
[nad
],
422 inwavehdr
, sizeof(WAVEHDR
));
423 inwavehdr
->dwFlags
= 0L;
424 waveInPrepareHeader(ntsnd_indev
[nad
], inwavehdr
, sizeof(WAVEHDR
));
425 mmresult
= waveInAddBuffer(ntsnd_indev
[nad
], inwavehdr
,
427 if (mmresult
!= MMSYSERR_NOERROR
)
428 nt_waveinerror("waveInAddBuffer: %s\n", mmresult
);
429 ntsnd_inphase
[nad
] = phase
= WRAPFWD(phase
+ 1);
431 if (count
== MAXRESYNC
) post("resync error 1");
433 /* Each output buffer which is "ready" is filled with zeros and
435 for (nda
= 0; nda
< nt_nwaveout
; nda
++)
437 int phase
= ntsnd_outphase
[nda
];
438 for (count
= 0; count
< MAXRESYNC
; count
++)
440 WAVEHDR
*outwavehdr
= ntsnd_outvec
[nda
][phase
].lpWaveHdr
;
441 if (!(outwavehdr
->dwFlags
& WHDR_DONE
)) break;
442 if (outwavehdr
->dwFlags
& WHDR_PREPARED
)
443 waveOutUnprepareHeader(ntsnd_outdev
[nda
],
444 outwavehdr
, sizeof(WAVEHDR
));
445 outwavehdr
->dwFlags
= 0L;
446 memset((char *)(ntsnd_outvec
[nda
][phase
].lpData
),
447 0, (CHANNELS_PER_DEVICE
* SAMPSIZE
* nt_realdacblksize
));
448 waveOutPrepareHeader(ntsnd_outdev
[nda
], outwavehdr
,
450 mmresult
= waveOutWrite(ntsnd_outdev
[nda
], outwavehdr
,
452 if (mmresult
!= MMSYSERR_NOERROR
)
453 nt_waveouterror("waveOutAddBuffer: %s\n", mmresult
);
454 ntsnd_outphase
[nda
] = phase
= WRAPFWD(phase
+ 1);
456 if (count
== MAXRESYNC
) post("resync error 2");
459 #ifdef MIDI_TIMESTAMP
468 static int nt_errorcount
;
469 static int nt_resynccount
;
470 static double nt_nextreporttime
= -1;
472 void nt_logerror(int which
)
475 post("error %d %d", count
, which
);
476 if (which
< NOTHING
) nt_errorcount
++;
477 if (which
== RESYNC
) nt_resynccount
++;
478 if (sys_getrealtime() > nt_nextreporttime
)
480 post("%d audio I/O error%s", nt_errorcount
,
481 (nt_errorcount
> 1 ? "s" : ""));
482 if (nt_resynccount
) post("DAC/ADC sync error");
483 nt_errorcount
= nt_resynccount
= 0;
484 nt_nextreporttime
= sys_getrealtime() - 5;
489 /* system buffer with t_sample types for one tick */
490 t_sample
*sys_soundout
;
491 t_sample
*sys_soundin
;
494 int mmio_send_dacs(void)
502 int nextfill
, doxfer
= 0;
504 if (!nt_nwavein
&& !nt_nwaveout
) return (0);
511 for (i
= 0, n
= 2 * nt_nwavein
* DEFDACBLKSIZE
, maxsamp
= nt_inmax
;
514 float f
= sys_soundin
[i
];
515 if (f
> maxsamp
) maxsamp
= f
;
516 else if (-f
> maxsamp
) maxsamp
= -f
;
519 for (i
= 0, n
= 2 * nt_nwaveout
* DEFDACBLKSIZE
, maxsamp
= nt_outmax
;
522 float f
= sys_soundout
[i
];
523 if (f
> maxsamp
) maxsamp
= f
;
524 else if (-f
> maxsamp
) maxsamp
= -f
;
529 /* the "fill pointer" nt_fill controls where in the next
530 I/O buffers we will write and/or read. If it's zero, we
531 first check whether the buffers are marked "done". */
535 for (nad
= 0; nad
< nt_nwavein
; nad
++)
537 int phase
= ntsnd_inphase
[nad
];
538 WAVEHDR
*inwavehdr
= ntsnd_invec
[nad
][phase
].lpWaveHdr
;
539 if (!(inwavehdr
->dwFlags
& WHDR_DONE
)) goto idle
;
541 for (nda
= 0; nda
< nt_nwaveout
; nda
++)
543 int phase
= ntsnd_outphase
[nda
];
544 WAVEHDR
*outwavehdr
=
545 ntsnd_outvec
[nda
][phase
].lpWaveHdr
;
546 if (!(outwavehdr
->dwFlags
& WHDR_DONE
)) goto idle
;
548 for (nad
= 0; nad
< nt_nwavein
; nad
++)
550 int phase
= ntsnd_inphase
[nad
];
552 ntsnd_invec
[nad
][phase
].lpWaveHdr
;
553 if (inwavehdr
->dwFlags
& WHDR_PREPARED
)
554 waveInUnprepareHeader(ntsnd_indev
[nad
],
555 inwavehdr
, sizeof(WAVEHDR
));
557 for (nda
= 0; nda
< nt_nwaveout
; nda
++)
559 int phase
= ntsnd_outphase
[nda
];
560 WAVEHDR
*outwavehdr
= ntsnd_outvec
[nda
][phase
].lpWaveHdr
;
561 if (outwavehdr
->dwFlags
& WHDR_PREPARED
)
562 waveOutUnprepareHeader(ntsnd_outdev
[nda
],
563 outwavehdr
, sizeof(WAVEHDR
));
567 /* Convert audio output to fixed-point and put it in the output
569 for (nda
= 0, fp1
= sys_soundout
; nda
< nt_nwaveout
; nda
++)
571 int phase
= ntsnd_outphase
[nda
];
573 for (i
= 0, sp1
= (short *)(ntsnd_outvec
[nda
][phase
].lpData
) +
574 CHANNELS_PER_DEVICE
* nt_fill
;
575 i
< 2; i
++, fp1
+= DEFDACBLKSIZE
, sp1
++)
577 for (j
= 0, fp2
= fp1
, sp2
= sp1
; j
< DEFDACBLKSIZE
;
578 j
++, fp2
++, sp2
+= CHANNELS_PER_DEVICE
)
580 int x1
= 32767.f
* *fp2
;
581 if (x1
> 32767) x1
= 32767;
582 else if (x1
< -32767) x1
= -32767;
587 memset(sys_soundout
, 0,
588 (DEFDACBLKSIZE
*sizeof(t_sample
)*CHANNELS_PER_DEVICE
)*nt_nwaveout
);
590 /* vice versa for the input buffer */
592 for (nad
= 0, fp1
= sys_soundin
; nad
< nt_nwavein
; nad
++)
594 int phase
= ntsnd_inphase
[nad
];
596 for (i
= 0, sp1
= (short *)(ntsnd_invec
[nad
][phase
].lpData
) +
597 CHANNELS_PER_DEVICE
* nt_fill
;
598 i
< 2; i
++, fp1
+= DEFDACBLKSIZE
, sp1
++)
600 for (j
= 0, fp2
= fp1
, sp2
= sp1
; j
< DEFDACBLKSIZE
;
601 j
++, fp2
++, sp2
+= CHANNELS_PER_DEVICE
)
603 *fp2
= ((float)(1./32767.)) * (float)(*sp2
);
608 nt_fill
= nt_fill
+ DEFDACBLKSIZE
;
609 if (nt_fill
== nt_realdacblksize
)
613 for (nad
= 0; nad
< nt_nwavein
; nad
++)
615 int phase
= ntsnd_inphase
[nad
];
616 HWAVEIN device
= ntsnd_indev
[nad
];
617 WAVEHDR
*inwavehdr
= ntsnd_invec
[nad
][phase
].lpWaveHdr
;
618 waveInPrepareHeader(device
, inwavehdr
, sizeof(WAVEHDR
));
619 mmresult
= waveInAddBuffer(device
, inwavehdr
, sizeof(WAVEHDR
));
620 if (mmresult
!= MMSYSERR_NOERROR
)
621 nt_waveinerror("waveInAddBuffer: %s\n", mmresult
);
622 ntsnd_inphase
[nad
] = WRAPFWD(phase
+ 1);
624 for (nda
= 0; nda
< nt_nwaveout
; nda
++)
626 int phase
= ntsnd_outphase
[nda
];
627 HWAVEOUT device
= ntsnd_outdev
[nda
];
628 WAVEHDR
*outwavehdr
= ntsnd_outvec
[nda
][phase
].lpWaveHdr
;
629 waveOutPrepareHeader(device
, outwavehdr
, sizeof(WAVEHDR
));
630 mmresult
= waveOutWrite(device
, outwavehdr
, sizeof(WAVEHDR
));
631 if (mmresult
!= MMSYSERR_NOERROR
)
632 nt_waveouterror("waveOutWrite: %s\n", mmresult
);
633 ntsnd_outphase
[nda
] = WRAPFWD(phase
+ 1);
636 /* check for DAC underflow or ADC overflow. */
637 for (nad
= 0; nad
< nt_nwavein
; nad
++)
639 int phase
= WRAPBACK(ntsnd_inphase
[nad
] - 2);
640 WAVEHDR
*inwavehdr
= ntsnd_invec
[nad
][phase
].lpWaveHdr
;
641 if (inwavehdr
->dwFlags
& WHDR_DONE
) goto late
;
643 for (nda
= 0; nda
< nt_nwaveout
; nda
++)
645 int phase
= WRAPBACK(ntsnd_outphase
[nda
] - 2);
646 WAVEHDR
*outwavehdr
= ntsnd_outvec
[nda
][phase
].lpWaveHdr
;
647 if (outwavehdr
->dwFlags
& WHDR_DONE
) goto late
;
660 /* If more than nt_adcjitterbufsallowed ADC buffers are ready
661 on any input device, resynchronize */
663 for (nad
= 0; nad
< nt_nwavein
; nad
++)
665 int phase
= ntsnd_inphase
[nad
];
668 [WRAPFWD(phase
+ nt_adcjitterbufsallowed
)].lpWaveHdr
;
669 if (inwavehdr
->dwFlags
& WHDR_DONE
)
676 /* test dac sync the same way */
677 for (nda
= 0; nda
< nt_nwaveout
; nda
++)
679 int phase
= ntsnd_outphase
[nda
];
680 WAVEHDR
*outwavehdr
=
682 [WRAPFWD(phase
+ nt_dacjitterbufsallowed
)].lpWaveHdr
;
683 if (outwavehdr
->dwFlags
& WHDR_DONE
)
689 #ifdef MIDI_TIMESTAMP
695 /* ------------------- public routines -------------------------- */
697 void mmio_open_audio(int naudioindev
, int *audioindev
,
698 int nchindev
, int *chindev
, int naudiooutdev
, int *audiooutdev
,
699 int nchoutdev
, int *choutdev
, int rate
) /* IOhannes */
703 nt_realdacblksize
= (sys_blocksize
? sys_blocksize
: DEFREALDACBLKSIZE
);
704 nbuf
= sys_advance_samples
/nt_realdacblksize
;
705 if (nbuf
>= MAXBUFFER
)
707 fprintf(stderr
, "pd: audio buffering maxed out to %d\n",
708 (int)(MAXBUFFER
* ((nt_realdacblksize
* 1000.)/44100.)));
711 else if (nbuf
< 4) nbuf
= 4;
712 fprintf(stderr
, "%d audio buffers\n", nbuf
);
713 nt_naudiobuffer
= nbuf
;
714 if (nt_adcjitterbufsallowed
> nbuf
- 2)
715 nt_adcjitterbufsallowed
= nbuf
- 2;
716 if (nt_dacjitterbufsallowed
> nbuf
- 2)
717 nt_dacjitterbufsallowed
= nbuf
- 2;
719 nt_nwavein
= sys_inchannels
/ 2;
720 nt_nwaveout
= sys_outchannels
/ 2;
721 nt_whichadc
= (naudioindev
< 1 ?
722 (nt_nwavein
> 1 ? WAVE_MAPPER
: -1) : audioindev
[0]);
723 nt_whichdac
= (naudiooutdev
< 1 ?
724 (nt_nwaveout
> 1 ? WAVE_MAPPER
: -1) : audiooutdev
[0]);
725 if (naudiooutdev
> 1 || naudioindev
> 1)
726 post("separate audio device choice not supported; using sequential devices.");
727 mmio_do_open_audio();
731 void mmio_reportidle(void)
736 /* list the audio and MIDI device names */
737 void mmio_listdevs(void)
742 ndevices
= waveInGetNumDevs();
743 for (i
= 0; i
< ndevices
; i
++)
746 wRtn
= waveInGetDevCaps(i
, (LPWAVEINCAPS
) &wicap
,
748 if (wRtn
) nt_waveinerror("waveInGetDevCaps: %s\n", wRtn
);
750 "audio input device #%d: %s\n", i
+1, wicap
.szPname
);
753 ndevices
= waveOutGetNumDevs();
754 for (i
= 0; i
< ndevices
; i
++)
757 wRtn
= waveOutGetDevCaps(i
, (LPWAVEOUTCAPS
) &wocap
,
759 if (wRtn
) nt_waveouterror("waveOutGetDevCaps: %s\n", wRtn
);
761 "audio output device #%d: %s\n", i
+1, wocap
.szPname
);
766 void mmio_getdevs(char *indevlist
, int *nindevs
,
767 char *outdevlist
, int *noutdevs
, int *canmulti
,
768 int maxndev
, int devdescsize
)
772 *canmulti
= 2; /* supports multiple devices */
773 ndev
= waveInGetNumDevs();
777 for (i
= 0; i
< ndev
; i
++)
780 wRtn
= waveInGetDevCaps(i
, (LPWAVEINCAPS
) &wicap
, sizeof(wicap
));
781 sprintf(indevlist
+ i
* devdescsize
, (wRtn
? "???" : wicap
.szPname
));
784 ndev
= waveOutGetNumDevs();
788 for (i
= 0; i
< ndev
; i
++)
791 wRtn
= waveOutGetDevCaps(i
, (LPWAVEOUTCAPS
) &wocap
, sizeof(wocap
));
792 sprintf(outdevlist
+ i
* devdescsize
, (wRtn
? "???" : wocap
.szPname
));