diff options
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/s_audio_mmio.c')
-rw-r--r-- | apps/plugins/pdbox/PDa/src/s_audio_mmio.c | 1588 |
1 files changed, 1588 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/s_audio_mmio.c b/apps/plugins/pdbox/PDa/src/s_audio_mmio.c new file mode 100644 index 0000000000..44bbae855b --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/s_audio_mmio.c | |||
@@ -0,0 +1,1588 @@ | |||
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. */ | ||
4 | |||
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. */ | ||
7 | |||
8 | #include "m_pd.h" | ||
9 | #include "s_stuff.h" | ||
10 | #include <stdio.h> | ||
11 | |||
12 | #include <windows.h> | ||
13 | |||
14 | #include <MMSYSTEM.H> | ||
15 | |||
16 | /* ------------------------- audio -------------------------- */ | ||
17 | |||
18 | static void nt_close_midiin(void); | ||
19 | static void nt_noresync( void); | ||
20 | |||
21 | static void postflags(void); | ||
22 | |||
23 | #define NAPORTS 16 /* wini hack for multiple ADDA devices */ | ||
24 | #define CHANNELS_PER_DEVICE 2 | ||
25 | #define DEFAULTCHANS 2 | ||
26 | #define DEFAULTSRATE 44100 | ||
27 | #define SAMPSIZE 2 | ||
28 | |||
29 | int nt_realdacblksize; | ||
30 | #define DEFREALDACBLKSIZE (4 * DEFDACBLKSIZE) /* larger underlying bufsize */ | ||
31 | |||
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; | ||
36 | |||
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 */ | ||
42 | |||
43 | typedef struct _sbuf | ||
44 | { | ||
45 | HANDLE hData; | ||
46 | HPSTR lpData; // pointer to waveform data memory | ||
47 | HANDLE hWaveHdr; | ||
48 | WAVEHDR *lpWaveHdr; // pointer to header structure | ||
49 | } t_sbuf; | ||
50 | |||
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 */ | ||
54 | |||
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 */ | ||
58 | |||
59 | static void nt_waveinerror(char *s, int err) | ||
60 | { | ||
61 | char t[256]; | ||
62 | waveInGetErrorText(err, t, 256); | ||
63 | fprintf(stderr, s, t); | ||
64 | } | ||
65 | |||
66 | static void nt_waveouterror(char *s, int err) | ||
67 | { | ||
68 | char t[256]; | ||
69 | waveOutGetErrorText(err, t, 256); | ||
70 | fprintf(stderr, s, t); | ||
71 | } | ||
72 | |||
73 | static void wave_prep(t_sbuf *bp, int setdone) | ||
74 | { | ||
75 | WAVEHDR *wh; | ||
76 | short *sp; | ||
77 | int i; | ||
78 | /* | ||
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. | ||
82 | */ | ||
83 | |||
84 | if (!(bp->hData = | ||
85 | GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, | ||
86 | (DWORD) (CHANNELS_PER_DEVICE * SAMPSIZE * nt_realdacblksize)))) | ||
87 | printf("alloc 1 failed\n"); | ||
88 | |||
89 | if (!(bp->lpData = | ||
90 | (HPSTR) GlobalLock(bp->hData))) | ||
91 | printf("lock 1 failed\n"); | ||
92 | |||
93 | /* Allocate and lock memory for the header. */ | ||
94 | |||
95 | if (!(bp->hWaveHdr = | ||
96 | GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR)))) | ||
97 | printf("alloc 2 failed\n"); | ||
98 | |||
99 | if (!(wh = bp->lpWaveHdr = | ||
100 | (WAVEHDR *) GlobalLock(bp->hWaveHdr))) | ||
101 | printf("lock 2 failed\n"); | ||
102 | |||
103 | for (i = CHANNELS_PER_DEVICE * nt_realdacblksize, | ||
104 | sp = (short *)bp->lpData; i--; ) | ||
105 | *sp++ = 0; | ||
106 | |||
107 | wh->lpData = bp->lpData; | ||
108 | wh->dwBufferLength = (CHANNELS_PER_DEVICE * SAMPSIZE * nt_realdacblksize); | ||
109 | wh->dwFlags = 0; | ||
110 | wh->dwLoops = 0L; | ||
111 | wh->lpNext = 0; | ||
112 | wh->reserved = 0; | ||
113 | /* optionally (for writing) set DONE flag as if we had queued them */ | ||
114 | if (setdone) | ||
115 | wh->dwFlags = WHDR_DONE; | ||
116 | } | ||
117 | |||
118 | static UINT nt_whichdac = WAVE_MAPPER, nt_whichadc = WAVE_MAPPER; | ||
119 | |||
120 | int mmio_do_open_audio(void) | ||
121 | { | ||
122 | PCMWAVEFORMAT form; | ||
123 | int i, j; | ||
124 | UINT mmresult; | ||
125 | int nad, nda; | ||
126 | static int naudioprepped = 0, nindevsprepped = 0, noutdevsprepped = 0; | ||
127 | if (sys_verbose) | ||
128 | post("%d devices in, %d devices out", | ||
129 | nt_nwavein, nt_nwaveout); | ||
130 | |||
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; | ||
137 | |||
138 | if (nt_nwavein <= 1 && nt_nwaveout <= 1) | ||
139 | nt_noresync(); | ||
140 | |||
141 | if (nindevsprepped < nt_nwavein) | ||
142 | { | ||
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; | ||
147 | } | ||
148 | if (noutdevsprepped < nt_nwaveout) | ||
149 | { | ||
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; | ||
154 | } | ||
155 | if (naudioprepped < nt_naudiobuffer) | ||
156 | { | ||
157 | for (j = naudioprepped; j < nt_naudiobuffer; j++) | ||
158 | { | ||
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); | ||
163 | } | ||
164 | naudioprepped = nt_naudiobuffer; | ||
165 | } | ||
166 | for (nad=0; nad < nt_nwavein; nad++) | ||
167 | { | ||
168 | /* Open waveform device(s), sucessively numbered, for input */ | ||
169 | |||
170 | mmresult = waveInOpen(&ntsnd_indev[nad], nt_whichadc+nad, | ||
171 | (WAVEFORMATEX *)(&form), 0L, 0L, CALLBACK_NULL); | ||
172 | |||
173 | if (sys_verbose) | ||
174 | printf("opened adc device %d with return %d\n", | ||
175 | nt_whichadc+nad,mmresult); | ||
176 | |||
177 | if (mmresult != MMSYSERR_NOERROR) | ||
178 | { | ||
179 | nt_waveinerror("waveInOpen: %s\n", mmresult); | ||
180 | nt_nwavein = nad; /* nt_nwavein = 0 wini */ | ||
181 | } | ||
182 | else | ||
183 | { | ||
184 | for (i = 0; i < nt_naudiobuffer; i++) | ||
185 | { | ||
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); | ||
194 | } | ||
195 | } | ||
196 | } | ||
197 | /* quickly start them all together */ | ||
198 | for (nad = 0; nad < nt_nwavein; nad++) | ||
199 | waveInStart(ntsnd_indev[nad]); | ||
200 | |||
201 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
202 | { | ||
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); | ||
206 | |||
207 | if (sys_verbose) | ||
208 | fprintf(stderr,"opened dac device %d, with return %d\n", | ||
209 | nt_whichdac +nda, mmresult); | ||
210 | |||
211 | if (mmresult != MMSYSERR_NOERROR) | ||
212 | { | ||
213 | fprintf(stderr,"Wave out open device %d + %d\n",nt_whichdac,nda); | ||
214 | nt_waveouterror("waveOutOpen device: %s\n", mmresult); | ||
215 | nt_nwaveout = nda; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | return (0); | ||
220 | } | ||
221 | |||
222 | void mmio_close_audio( void) | ||
223 | { | ||
224 | int errcode; | ||
225 | int nda, nad; | ||
226 | if (sys_verbose) | ||
227 | post("closing audio..."); | ||
228 | |||
229 | for (nda=0; nda < nt_nwaveout; nda++) /*if (nt_nwaveout) wini */ | ||
230 | { | ||
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); | ||
237 | } | ||
238 | nt_nwaveout = 0; | ||
239 | |||
240 | for(nad=0; nad < nt_nwavein;nad++) /* if (nt_nwavein) wini */ | ||
241 | { | ||
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); | ||
248 | } | ||
249 | nt_nwavein = 0; | ||
250 | } | ||
251 | |||
252 | |||
253 | #define ADCJITTER 10 /* We tolerate X buffers of jitter by default */ | ||
254 | #define DACJITTER 10 | ||
255 | |||
256 | static int nt_adcjitterbufsallowed = ADCJITTER; | ||
257 | static int nt_dacjitterbufsallowed = DACJITTER; | ||
258 | |||
259 | /* ------------- MIDI time stamping from audio clock ------------ */ | ||
260 | |||
261 | #ifdef MIDI_TIMESTAMP | ||
262 | |||
263 | static double nt_hibuftime; | ||
264 | static double initsystime = -1; | ||
265 | |||
266 | /* call this whenever we reset audio */ | ||
267 | static void nt_resetmidisync(void) | ||
268 | { | ||
269 | initsystime = clock_getsystime(); | ||
270 | nt_hibuftime = sys_getrealtime(); | ||
271 | } | ||
272 | |||
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. */ | ||
276 | |||
277 | static void nt_midisync(void) | ||
278 | { | ||
279 | double jittersec, diff; | ||
280 | |||
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) | ||
288 | { | ||
289 | post("jitter excess %d %f", dac, diff); | ||
290 | nt_resetmidisync(); | ||
291 | } | ||
292 | } | ||
293 | |||
294 | static double nt_midigettimefor(LARGE_INTEGER timestamp) | ||
295 | { | ||
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); | ||
300 | } | ||
301 | #endif /* MIDI_TIMESTAMP */ | ||
302 | |||
303 | |||
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 | ||
308 | |||
309 | #if 0 /* this is used for debugging */ | ||
310 | static void nt_printaudiostatus(void) | ||
311 | { | ||
312 | int nad, nda; | ||
313 | for (nad = 0; nad < nt_nwavein; nad++) | ||
314 | { | ||
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++) | ||
319 | { | ||
320 | int donethis = | ||
321 | (ntsnd_invec[nad][phase2].lpWaveHdr->dwFlags & WHDR_DONE); | ||
322 | int donenext = | ||
323 | (ntsnd_invec[nad][phase3].lpWaveHdr->dwFlags & WHDR_DONE); | ||
324 | if (donethis && !donenext) | ||
325 | { | ||
326 | if (firstphasebusy >= 0) goto multipleadc; | ||
327 | firstphasebusy = count; | ||
328 | } | ||
329 | if (!donethis && donenext) | ||
330 | { | ||
331 | if (firstphasedone >= 0) goto multipleadc; | ||
332 | firstphasedone = count; | ||
333 | } | ||
334 | phase2 = phase3; | ||
335 | phase3 = WRAPFWD(phase2 + 1); | ||
336 | } | ||
337 | post("nad %d phase %d busy %d done %d", nad, phase, firstphasebusy, | ||
338 | firstphasedone); | ||
339 | continue; | ||
340 | multipleadc: | ||
341 | startpost("nad %d phase %d: oops:", nad, phase); | ||
342 | for (count = 0; count < nt_naudiobuffer; count++) | ||
343 | { | ||
344 | char buf[80]; | ||
345 | sprintf(buf, " %d", | ||
346 | (ntsnd_invec[nad][count].lpWaveHdr->dwFlags & WHDR_DONE)); | ||
347 | poststring(buf); | ||
348 | } | ||
349 | endpost(); | ||
350 | } | ||
351 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
352 | { | ||
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++) | ||
357 | { | ||
358 | int donethis = | ||
359 | (ntsnd_outvec[nda][phase2].lpWaveHdr->dwFlags & WHDR_DONE); | ||
360 | int donenext = | ||
361 | (ntsnd_outvec[nda][phase3].lpWaveHdr->dwFlags & WHDR_DONE); | ||
362 | if (donethis && !donenext) | ||
363 | { | ||
364 | if (firstphasebusy >= 0) goto multipledac; | ||
365 | firstphasebusy = count; | ||
366 | } | ||
367 | if (!donethis && donenext) | ||
368 | { | ||
369 | if (firstphasedone >= 0) goto multipledac; | ||
370 | firstphasedone = count; | ||
371 | } | ||
372 | phase2 = phase3; | ||
373 | phase3 = WRAPFWD(phase2 + 1); | ||
374 | } | ||
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, | ||
378 | firstphasedone); | ||
379 | continue; | ||
380 | multipledac: | ||
381 | startpost("nda %d phase %d: oops:", nda, phase); | ||
382 | for (count = 0; count < nt_naudiobuffer; count++) | ||
383 | { | ||
384 | char buf[80]; | ||
385 | sprintf(buf, " %d", | ||
386 | (ntsnd_outvec[nad][count].lpWaveHdr->dwFlags & WHDR_DONE)); | ||
387 | poststring(buf); | ||
388 | } | ||
389 | endpost(); | ||
390 | } | ||
391 | } | ||
392 | #endif /* 0 */ | ||
393 | |||
394 | /* this is a hack to avoid ever resyncing audio pointers in case for whatever | ||
395 | reason the sync testing below gives false positives. */ | ||
396 | |||
397 | static int nt_resync_cancelled; | ||
398 | |||
399 | static void nt_noresync( void) | ||
400 | { | ||
401 | nt_resync_cancelled = 1; | ||
402 | } | ||
403 | |||
404 | static void nt_resyncaudio(void) | ||
405 | { | ||
406 | UINT mmresult; | ||
407 | int nad, nda, count; | ||
408 | if (nt_resync_cancelled) | ||
409 | return; | ||
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++) | ||
414 | { | ||
415 | int phase = ntsnd_inphase[nad]; | ||
416 | for (count = 0; count < MAXRESYNC; count++) | ||
417 | { | ||
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, | ||
426 | sizeof(WAVEHDR)); | ||
427 | if (mmresult != MMSYSERR_NOERROR) | ||
428 | nt_waveinerror("waveInAddBuffer: %s\n", mmresult); | ||
429 | ntsnd_inphase[nad] = phase = WRAPFWD(phase + 1); | ||
430 | } | ||
431 | if (count == MAXRESYNC) post("resync error 1"); | ||
432 | } | ||
433 | /* Each output buffer which is "ready" is filled with zeros and | ||
434 | queued. */ | ||
435 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
436 | { | ||
437 | int phase = ntsnd_outphase[nda]; | ||
438 | for (count = 0; count < MAXRESYNC; count++) | ||
439 | { | ||
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, | ||
449 | sizeof(WAVEHDR)); | ||
450 | mmresult = waveOutWrite(ntsnd_outdev[nda], outwavehdr, | ||
451 | sizeof(WAVEHDR)); | ||
452 | if (mmresult != MMSYSERR_NOERROR) | ||
453 | nt_waveouterror("waveOutAddBuffer: %s\n", mmresult); | ||
454 | ntsnd_outphase[nda] = phase = WRAPFWD(phase + 1); | ||
455 | } | ||
456 | if (count == MAXRESYNC) post("resync error 2"); | ||
457 | } | ||
458 | |||
459 | #ifdef MIDI_TIMESTAMP | ||
460 | nt_resetmidisync(); | ||
461 | #endif | ||
462 | |||
463 | } | ||
464 | |||
465 | #define LATE 0 | ||
466 | #define RESYNC 1 | ||
467 | #define NOTHING 2 | ||
468 | static int nt_errorcount; | ||
469 | static int nt_resynccount; | ||
470 | static double nt_nextreporttime = -1; | ||
471 | |||
472 | void nt_logerror(int which) | ||
473 | { | ||
474 | #if 0 | ||
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) | ||
479 | { | ||
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; | ||
485 | } | ||
486 | #endif | ||
487 | } | ||
488 | |||
489 | /* system buffer with t_sample types for one tick */ | ||
490 | t_sample *sys_soundout; | ||
491 | t_sample *sys_soundin; | ||
492 | float sys_dacsr; | ||
493 | |||
494 | int mmio_send_dacs(void) | ||
495 | { | ||
496 | HMMIO hmmio; | ||
497 | UINT mmresult; | ||
498 | HANDLE hFormat; | ||
499 | int i, j; | ||
500 | short *sp1, *sp2; | ||
501 | float *fp1, *fp2; | ||
502 | int nextfill, doxfer = 0; | ||
503 | int nda, nad; | ||
504 | if (!nt_nwavein && !nt_nwaveout) return (0); | ||
505 | |||
506 | |||
507 | if (nt_meters) | ||
508 | { | ||
509 | int i, n; | ||
510 | float maxsamp; | ||
511 | for (i = 0, n = 2 * nt_nwavein * DEFDACBLKSIZE, maxsamp = nt_inmax; | ||
512 | i < n; i++) | ||
513 | { | ||
514 | float f = sys_soundin[i]; | ||
515 | if (f > maxsamp) maxsamp = f; | ||
516 | else if (-f > maxsamp) maxsamp = -f; | ||
517 | } | ||
518 | nt_inmax = maxsamp; | ||
519 | for (i = 0, n = 2 * nt_nwaveout * DEFDACBLKSIZE, maxsamp = nt_outmax; | ||
520 | i < n; i++) | ||
521 | { | ||
522 | float f = sys_soundout[i]; | ||
523 | if (f > maxsamp) maxsamp = f; | ||
524 | else if (-f > maxsamp) maxsamp = -f; | ||
525 | } | ||
526 | nt_outmax = maxsamp; | ||
527 | } | ||
528 | |||
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". */ | ||
532 | |||
533 | if (!nt_fill) | ||
534 | { | ||
535 | for (nad = 0; nad < nt_nwavein; nad++) | ||
536 | { | ||
537 | int phase = ntsnd_inphase[nad]; | ||
538 | WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; | ||
539 | if (!(inwavehdr->dwFlags & WHDR_DONE)) goto idle; | ||
540 | } | ||
541 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
542 | { | ||
543 | int phase = ntsnd_outphase[nda]; | ||
544 | WAVEHDR *outwavehdr = | ||
545 | ntsnd_outvec[nda][phase].lpWaveHdr; | ||
546 | if (!(outwavehdr->dwFlags & WHDR_DONE)) goto idle; | ||
547 | } | ||
548 | for (nad = 0; nad < nt_nwavein; nad++) | ||
549 | { | ||
550 | int phase = ntsnd_inphase[nad]; | ||
551 | WAVEHDR *inwavehdr = | ||
552 | ntsnd_invec[nad][phase].lpWaveHdr; | ||
553 | if (inwavehdr->dwFlags & WHDR_PREPARED) | ||
554 | waveInUnprepareHeader(ntsnd_indev[nad], | ||
555 | inwavehdr, sizeof(WAVEHDR)); | ||
556 | } | ||
557 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
558 | { | ||
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)); | ||
564 | } | ||
565 | } | ||
566 | |||
567 | /* Convert audio output to fixed-point and put it in the output | ||
568 | buffer. */ | ||
569 | for (nda = 0, fp1 = sys_soundout; nda < nt_nwaveout; nda++) | ||
570 | { | ||
571 | int phase = ntsnd_outphase[nda]; | ||
572 | |||
573 | for (i = 0, sp1 = (short *)(ntsnd_outvec[nda][phase].lpData) + | ||
574 | CHANNELS_PER_DEVICE * nt_fill; | ||
575 | i < 2; i++, fp1 += DEFDACBLKSIZE, sp1++) | ||
576 | { | ||
577 | for (j = 0, fp2 = fp1, sp2 = sp1; j < DEFDACBLKSIZE; | ||
578 | j++, fp2++, sp2 += CHANNELS_PER_DEVICE) | ||
579 | { | ||
580 | int x1 = 32767.f * *fp2; | ||
581 | if (x1 > 32767) x1 = 32767; | ||
582 | else if (x1 < -32767) x1 = -32767; | ||
583 | *sp2 = x1; | ||
584 | } | ||
585 | } | ||
586 | } | ||
587 | memset(sys_soundout, 0, | ||
588 | (DEFDACBLKSIZE *sizeof(t_sample)*CHANNELS_PER_DEVICE)*nt_nwaveout); | ||
589 | |||
590 | /* vice versa for the input buffer */ | ||
591 | |||
592 | for (nad = 0, fp1 = sys_soundin; nad < nt_nwavein; nad++) | ||
593 | { | ||
594 | int phase = ntsnd_inphase[nad]; | ||
595 | |||
596 | for (i = 0, sp1 = (short *)(ntsnd_invec[nad][phase].lpData) + | ||
597 | CHANNELS_PER_DEVICE * nt_fill; | ||
598 | i < 2; i++, fp1 += DEFDACBLKSIZE, sp1++) | ||
599 | { | ||
600 | for (j = 0, fp2 = fp1, sp2 = sp1; j < DEFDACBLKSIZE; | ||
601 | j++, fp2++, sp2 += CHANNELS_PER_DEVICE) | ||
602 | { | ||
603 | *fp2 = ((float)(1./32767.)) * (float)(*sp2); | ||
604 | } | ||
605 | } | ||
606 | } | ||
607 | |||
608 | nt_fill = nt_fill + DEFDACBLKSIZE; | ||
609 | if (nt_fill == nt_realdacblksize) | ||
610 | { | ||
611 | nt_fill = 0; | ||
612 | |||
613 | for (nad = 0; nad < nt_nwavein; nad++) | ||
614 | { | ||
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); | ||
623 | } | ||
624 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
625 | { | ||
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); | ||
634 | } | ||
635 | |||
636 | /* check for DAC underflow or ADC overflow. */ | ||
637 | for (nad = 0; nad < nt_nwavein; nad++) | ||
638 | { | ||
639 | int phase = WRAPBACK(ntsnd_inphase[nad] - 2); | ||
640 | WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; | ||
641 | if (inwavehdr->dwFlags & WHDR_DONE) goto late; | ||
642 | } | ||
643 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
644 | { | ||
645 | int phase = WRAPBACK(ntsnd_outphase[nda] - 2); | ||
646 | WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; | ||
647 | if (outwavehdr->dwFlags & WHDR_DONE) goto late; | ||
648 | } | ||
649 | } | ||
650 | return (1); | ||
651 | |||
652 | late: | ||
653 | |||
654 | nt_logerror(LATE); | ||
655 | nt_resyncaudio(); | ||
656 | return (1); | ||
657 | |||
658 | idle: | ||
659 | |||
660 | /* If more than nt_adcjitterbufsallowed ADC buffers are ready | ||
661 | on any input device, resynchronize */ | ||
662 | |||
663 | for (nad = 0; nad < nt_nwavein; nad++) | ||
664 | { | ||
665 | int phase = ntsnd_inphase[nad]; | ||
666 | WAVEHDR *inwavehdr = | ||
667 | ntsnd_invec[nad] | ||
668 | [WRAPFWD(phase + nt_adcjitterbufsallowed)].lpWaveHdr; | ||
669 | if (inwavehdr->dwFlags & WHDR_DONE) | ||
670 | { | ||
671 | nt_resyncaudio(); | ||
672 | return (0); | ||
673 | } | ||
674 | } | ||
675 | |||
676 | /* test dac sync the same way */ | ||
677 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
678 | { | ||
679 | int phase = ntsnd_outphase[nda]; | ||
680 | WAVEHDR *outwavehdr = | ||
681 | ntsnd_outvec[nda] | ||
682 | [WRAPFWD(phase + nt_dacjitterbufsallowed)].lpWaveHdr; | ||
683 | if (outwavehdr->dwFlags & WHDR_DONE) | ||
684 | { | ||
685 | nt_resyncaudio(); | ||
686 | return (0); | ||
687 | } | ||
688 | } | ||
689 | #ifdef MIDI_TIMESTAMP | ||
690 | nt_midisync(); | ||
691 | #endif | ||
692 | return (0); | ||
693 | } | ||
694 | |||
695 | /* ------------------- public routines -------------------------- */ | ||
696 | |||
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 */ | ||
700 | { | ||
701 | int nbuf; | ||
702 | |||
703 | nt_realdacblksize = (sys_blocksize ? sys_blocksize : DEFREALDACBLKSIZE); | ||
704 | nbuf = sys_advance_samples/nt_realdacblksize; | ||
705 | if (nbuf >= MAXBUFFER) | ||
706 | { | ||
707 | fprintf(stderr, "pd: audio buffering maxed out to %d\n", | ||
708 | (int)(MAXBUFFER * ((nt_realdacblksize * 1000.)/44100.))); | ||
709 | nbuf = MAXBUFFER; | ||
710 | } | ||
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; | ||
718 | |||
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(); | ||
728 | } | ||
729 | |||
730 | |||
731 | void mmio_reportidle(void) | ||
732 | { | ||
733 | } | ||
734 | |||
735 | #if 0 | ||
736 | /* list the audio and MIDI device names */ | ||
737 | void mmio_listdevs(void) | ||
738 | { | ||
739 | UINT wRtn, ndevices; | ||
740 | unsigned int i; | ||
741 | |||
742 | ndevices = waveInGetNumDevs(); | ||
743 | for (i = 0; i < ndevices; i++) | ||
744 | { | ||
745 | WAVEINCAPS wicap; | ||
746 | wRtn = waveInGetDevCaps(i, (LPWAVEINCAPS) &wicap, | ||
747 | sizeof(wicap)); | ||
748 | if (wRtn) nt_waveinerror("waveInGetDevCaps: %s\n", wRtn); | ||
749 | else fprintf(stderr, | ||
750 | "audio input device #%d: %s\n", i+1, wicap.szPname); | ||
751 | } | ||
752 | |||
753 | ndevices = waveOutGetNumDevs(); | ||
754 | for (i = 0; i < ndevices; i++) | ||
755 | { | ||
756 | WAVEOUTCAPS wocap; | ||
757 | wRtn = waveOutGetDevCaps(i, (LPWAVEOUTCAPS) &wocap, | ||
758 | sizeof(wocap)); | ||
759 | if (wRtn) nt_waveouterror("waveOutGetDevCaps: %s\n", wRtn); | ||
760 | else fprintf(stderr, | ||
761 | "audio output device #%d: %s\n", i+1, wocap.szPname); | ||
762 | } | ||
763 | } | ||
764 | #endif | ||
765 | |||
766 | void mmio_getdevs(char *indevlist, int *nindevs, | ||
767 | char *outdevlist, int *noutdevs, int *canmulti, | ||
768 | int maxndev, int devdescsize) | ||
769 | { | ||
770 | int wRtn, ndev, i; | ||
771 | |||
772 | *canmulti = 2; /* supports multiple devices */ | ||
773 | ndev = waveInGetNumDevs(); | ||
774 | if (ndev > maxndev) | ||
775 | ndev = maxndev; | ||
776 | *nindevs = ndev; | ||
777 | for (i = 0; i < ndev; i++) | ||
778 | { | ||
779 | WAVEINCAPS wicap; | ||
780 | wRtn = waveInGetDevCaps(i, (LPWAVEINCAPS) &wicap, sizeof(wicap)); | ||
781 | sprintf(indevlist + i * devdescsize, (wRtn ? "???" : wicap.szPname)); | ||
782 | } | ||
783 | |||
784 | ndev = waveOutGetNumDevs(); | ||
785 | if (ndev > maxndev) | ||
786 | ndev = maxndev; | ||
787 | *noutdevs = ndev; | ||
788 | for (i = 0; i < ndev; i++) | ||
789 | { | ||
790 | WAVEOUTCAPS wocap; | ||
791 | wRtn = waveOutGetDevCaps(i, (LPWAVEOUTCAPS) &wocap, sizeof(wocap)); | ||
792 | sprintf(outdevlist + i * devdescsize, (wRtn ? "???" : wocap.szPname)); | ||
793 | } | ||
794 | } | ||
795 | /* Copyright (c) 1997-1999 Miller Puckette. | ||
796 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL | ||
797 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ | ||
798 | |||
799 | /* modified 2/98 by Winfried Ritsch to deal with up to 4 synchronized | ||
800 | "wave" devices, which is how ADAT boards appear to the WAVE API. */ | ||
801 | |||
802 | #include "m_pd.h" | ||
803 | #include "s_stuff.h" | ||
804 | #include <stdio.h> | ||
805 | |||
806 | #include <windows.h> | ||
807 | |||
808 | #include <MMSYSTEM.H> | ||
809 | |||
810 | /* ------------------------- audio -------------------------- */ | ||
811 | |||
812 | static void nt_close_midiin(void); | ||
813 | static void nt_noresync( void); | ||
814 | |||
815 | static void postflags(void); | ||
816 | |||
817 | #define NAPORTS 16 /* wini hack for multiple ADDA devices */ | ||
818 | #define CHANNELS_PER_DEVICE 2 | ||
819 | #define DEFAULTCHANS 2 | ||
820 | #define DEFAULTSRATE 44100 | ||
821 | #define SAMPSIZE 2 | ||
822 | |||
823 | int nt_realdacblksize; | ||
824 | #define DEFREALDACBLKSIZE (4 * DEFDACBLKSIZE) /* larger underlying bufsize */ | ||
825 | |||
826 | #define MAXBUFFER 100 /* number of buffers in use at maximum advance */ | ||
827 | #define DEFBUFFER 30 /* default is about 30x6 = 180 msec! */ | ||
828 | static int nt_naudiobuffer = DEFBUFFER; | ||
829 | float sys_dacsr = DEFAULTSRATE; | ||
830 | |||
831 | static int nt_whichapi = API_MMIO; | ||
832 | static int nt_meters; /* true if we're metering */ | ||
833 | static float nt_inmax; /* max input amplitude */ | ||
834 | static float nt_outmax; /* max output amplitude */ | ||
835 | static int nt_nwavein, nt_nwaveout; /* number of WAVE devices in and out */ | ||
836 | |||
837 | typedef struct _sbuf | ||
838 | { | ||
839 | HANDLE hData; | ||
840 | HPSTR lpData; // pointer to waveform data memory | ||
841 | HANDLE hWaveHdr; | ||
842 | WAVEHDR *lpWaveHdr; // pointer to header structure | ||
843 | } t_sbuf; | ||
844 | |||
845 | t_sbuf ntsnd_outvec[NAPORTS][MAXBUFFER]; /* circular buffer array */ | ||
846 | HWAVEOUT ntsnd_outdev[NAPORTS]; /* output device */ | ||
847 | static int ntsnd_outphase[NAPORTS]; /* index of next buffer to send */ | ||
848 | |||
849 | t_sbuf ntsnd_invec[NAPORTS][MAXBUFFER]; /* circular buffer array */ | ||
850 | HWAVEIN ntsnd_indev[NAPORTS]; /* input device */ | ||
851 | static int ntsnd_inphase[NAPORTS]; /* index of next buffer to read */ | ||
852 | |||
853 | static void nt_waveinerror(char *s, int err) | ||
854 | { | ||
855 | char t[256]; | ||
856 | waveInGetErrorText(err, t, 256); | ||
857 | fprintf(stderr, s, t); | ||
858 | } | ||
859 | |||
860 | static void nt_waveouterror(char *s, int err) | ||
861 | { | ||
862 | char t[256]; | ||
863 | waveOutGetErrorText(err, t, 256); | ||
864 | fprintf(stderr, s, t); | ||
865 | } | ||
866 | |||
867 | static void wave_prep(t_sbuf *bp, int setdone) | ||
868 | { | ||
869 | WAVEHDR *wh; | ||
870 | short *sp; | ||
871 | int i; | ||
872 | /* | ||
873 | * Allocate and lock memory for the waveform data. The memory | ||
874 | * for waveform data must be globally allocated with | ||
875 | * GMEM_MOVEABLE and GMEM_SHARE flags. | ||
876 | */ | ||
877 | |||
878 | if (!(bp->hData = | ||
879 | GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, | ||
880 | (DWORD) (CHANNELS_PER_DEVICE * SAMPSIZE * nt_realdacblksize)))) | ||
881 | printf("alloc 1 failed\n"); | ||
882 | |||
883 | if (!(bp->lpData = | ||
884 | (HPSTR) GlobalLock(bp->hData))) | ||
885 | printf("lock 1 failed\n"); | ||
886 | |||
887 | /* Allocate and lock memory for the header. */ | ||
888 | |||
889 | if (!(bp->hWaveHdr = | ||
890 | GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR)))) | ||
891 | printf("alloc 2 failed\n"); | ||
892 | |||
893 | if (!(wh = bp->lpWaveHdr = | ||
894 | (WAVEHDR *) GlobalLock(bp->hWaveHdr))) | ||
895 | printf("lock 2 failed\n"); | ||
896 | |||
897 | for (i = CHANNELS_PER_DEVICE * nt_realdacblksize, | ||
898 | sp = (short *)bp->lpData; i--; ) | ||
899 | *sp++ = 0; | ||
900 | |||
901 | wh->lpData = bp->lpData; | ||
902 | wh->dwBufferLength = (CHANNELS_PER_DEVICE * SAMPSIZE * nt_realdacblksize); | ||
903 | wh->dwFlags = 0; | ||
904 | wh->dwLoops = 0L; | ||
905 | wh->lpNext = 0; | ||
906 | wh->reserved = 0; | ||
907 | /* optionally (for writing) set DONE flag as if we had queued them */ | ||
908 | if (setdone) | ||
909 | wh->dwFlags = WHDR_DONE; | ||
910 | } | ||
911 | |||
912 | static UINT nt_whichdac = WAVE_MAPPER, nt_whichadc = WAVE_MAPPER; | ||
913 | |||
914 | int mmio_do_open_audio(void) | ||
915 | { | ||
916 | PCMWAVEFORMAT form; | ||
917 | int i, j; | ||
918 | UINT mmresult; | ||
919 | int nad, nda; | ||
920 | static int naudioprepped = 0, nindevsprepped = 0, noutdevsprepped = 0; | ||
921 | if (sys_verbose) | ||
922 | post("%d devices in, %d devices out", | ||
923 | nt_nwavein, nt_nwaveout); | ||
924 | |||
925 | form.wf.wFormatTag = WAVE_FORMAT_PCM; | ||
926 | form.wf.nChannels = CHANNELS_PER_DEVICE; | ||
927 | form.wf.nSamplesPerSec = sys_dacsr; | ||
928 | form.wf.nAvgBytesPerSec = sys_dacsr * (CHANNELS_PER_DEVICE * SAMPSIZE); | ||
929 | form.wf.nBlockAlign = CHANNELS_PER_DEVICE * SAMPSIZE; | ||
930 | form.wBitsPerSample = 8 * SAMPSIZE; | ||
931 | |||
932 | if (nt_nwavein <= 1 && nt_nwaveout <= 1) | ||
933 | nt_noresync(); | ||
934 | |||
935 | if (nindevsprepped < nt_nwavein) | ||
936 | { | ||
937 | for (i = nindevsprepped; i < nt_nwavein; i++) | ||
938 | for (j = 0; j < naudioprepped; j++) | ||
939 | wave_prep(&ntsnd_invec[i][j], 0); | ||
940 | nindevsprepped = nt_nwavein; | ||
941 | } | ||
942 | if (noutdevsprepped < nt_nwaveout) | ||
943 | { | ||
944 | for (i = noutdevsprepped; i < nt_nwaveout; i++) | ||
945 | for (j = 0; j < naudioprepped; j++) | ||
946 | wave_prep(&ntsnd_outvec[i][j], 1); | ||
947 | noutdevsprepped = nt_nwaveout; | ||
948 | } | ||
949 | if (naudioprepped < nt_naudiobuffer) | ||
950 | { | ||
951 | for (j = naudioprepped; j < nt_naudiobuffer; j++) | ||
952 | { | ||
953 | for (i = 0; i < nt_nwavein; i++) | ||
954 | wave_prep(&ntsnd_invec[i][j], 0); | ||
955 | for (i = 0; i < nt_nwaveout; i++) | ||
956 | wave_prep(&ntsnd_outvec[i][j], 1); | ||
957 | } | ||
958 | naudioprepped = nt_naudiobuffer; | ||
959 | } | ||
960 | for (nad=0; nad < nt_nwavein; nad++) | ||
961 | { | ||
962 | /* Open waveform device(s), sucessively numbered, for input */ | ||
963 | |||
964 | mmresult = waveInOpen(&ntsnd_indev[nad], nt_whichadc+nad, | ||
965 | (WAVEFORMATEX *)(&form), 0L, 0L, CALLBACK_NULL); | ||
966 | |||
967 | if (sys_verbose) | ||
968 | printf("opened adc device %d with return %d\n", | ||
969 | nt_whichadc+nad,mmresult); | ||
970 | |||
971 | if (mmresult != MMSYSERR_NOERROR) | ||
972 | { | ||
973 | nt_waveinerror("waveInOpen: %s\n", mmresult); | ||
974 | nt_nwavein = nad; /* nt_nwavein = 0 wini */ | ||
975 | } | ||
976 | else | ||
977 | { | ||
978 | for (i = 0; i < nt_naudiobuffer; i++) | ||
979 | { | ||
980 | mmresult = waveInPrepareHeader(ntsnd_indev[nad], | ||
981 | ntsnd_invec[nad][i].lpWaveHdr, sizeof(WAVEHDR)); | ||
982 | if (mmresult != MMSYSERR_NOERROR) | ||
983 | nt_waveinerror("waveinprepareheader: %s\n", mmresult); | ||
984 | mmresult = waveInAddBuffer(ntsnd_indev[nad], | ||
985 | ntsnd_invec[nad][i].lpWaveHdr, sizeof(WAVEHDR)); | ||
986 | if (mmresult != MMSYSERR_NOERROR) | ||
987 | nt_waveinerror("waveInAddBuffer: %s\n", mmresult); | ||
988 | } | ||
989 | } | ||
990 | } | ||
991 | /* quickly start them all together */ | ||
992 | for (nad = 0; nad < nt_nwavein; nad++) | ||
993 | waveInStart(ntsnd_indev[nad]); | ||
994 | |||
995 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
996 | { | ||
997 | /* Open a waveform device for output in sucessiv device numbering*/ | ||
998 | mmresult = waveOutOpen(&ntsnd_outdev[nda], nt_whichdac + nda, | ||
999 | (WAVEFORMATEX *)(&form), 0L, 0L, CALLBACK_NULL); | ||
1000 | |||
1001 | if (sys_verbose) | ||
1002 | fprintf(stderr,"opened dac device %d, with return %d\n", | ||
1003 | nt_whichdac +nda, mmresult); | ||
1004 | |||
1005 | if (mmresult != MMSYSERR_NOERROR) | ||
1006 | { | ||
1007 | fprintf(stderr,"Wave out open device %d + %d\n",nt_whichdac,nda); | ||
1008 | nt_waveouterror("waveOutOpen device: %s\n", mmresult); | ||
1009 | nt_nwaveout = nda; | ||
1010 | } | ||
1011 | } | ||
1012 | |||
1013 | return (0); | ||
1014 | } | ||
1015 | |||
1016 | void mmio_close_audio( void) | ||
1017 | { | ||
1018 | int errcode; | ||
1019 | int nda, nad; | ||
1020 | if (sys_verbose) | ||
1021 | post("closing audio..."); | ||
1022 | |||
1023 | for (nda=0; nda < nt_nwaveout; nda++) /*if (nt_nwaveout) wini */ | ||
1024 | { | ||
1025 | errcode = waveOutReset(ntsnd_outdev[nda]); | ||
1026 | if (errcode != MMSYSERR_NOERROR) | ||
1027 | printf("error resetting output %d: %d\n", nda, errcode); | ||
1028 | errcode = waveOutClose(ntsnd_outdev[nda]); | ||
1029 | if (errcode != MMSYSERR_NOERROR) | ||
1030 | printf("error closing output %d: %d\n",nda , errcode); | ||
1031 | } | ||
1032 | nt_nwaveout = 0; | ||
1033 | |||
1034 | for(nad=0; nad < nt_nwavein;nad++) /* if (nt_nwavein) wini */ | ||
1035 | { | ||
1036 | errcode = waveInReset(ntsnd_indev[nad]); | ||
1037 | if (errcode != MMSYSERR_NOERROR) | ||
1038 | printf("error resetting input: %d\n", errcode); | ||
1039 | errcode = waveInClose(ntsnd_indev[nad]); | ||
1040 | if (errcode != MMSYSERR_NOERROR) | ||
1041 | printf("error closing input: %d\n", errcode); | ||
1042 | } | ||
1043 | nt_nwavein = 0; | ||
1044 | } | ||
1045 | |||
1046 | |||
1047 | #define ADCJITTER 10 /* We tolerate X buffers of jitter by default */ | ||
1048 | #define DACJITTER 10 | ||
1049 | |||
1050 | static int nt_adcjitterbufsallowed = ADCJITTER; | ||
1051 | static int nt_dacjitterbufsallowed = DACJITTER; | ||
1052 | |||
1053 | /* ------------- MIDI time stamping from audio clock ------------ */ | ||
1054 | |||
1055 | #ifdef MIDI_TIMESTAMP | ||
1056 | |||
1057 | static double nt_hibuftime; | ||
1058 | static double initsystime = -1; | ||
1059 | |||
1060 | /* call this whenever we reset audio */ | ||
1061 | static void nt_resetmidisync(void) | ||
1062 | { | ||
1063 | initsystime = clock_getsystime(); | ||
1064 | nt_hibuftime = sys_getrealtime(); | ||
1065 | } | ||
1066 | |||
1067 | /* call this whenever we're idled waiting for audio to be ready. | ||
1068 | The routine maintains a high and low water point for the difference | ||
1069 | between real and DAC time. */ | ||
1070 | |||
1071 | static void nt_midisync(void) | ||
1072 | { | ||
1073 | double jittersec, diff; | ||
1074 | |||
1075 | if (initsystime == -1) nt_resetmidisync(); | ||
1076 | jittersec = (nt_dacjitterbufsallowed > nt_adcjitterbufsallowed ? | ||
1077 | nt_dacjitterbufsallowed : nt_adcjitterbufsallowed) | ||
1078 | * nt_realdacblksize / sys_getsr(); | ||
1079 | diff = sys_getrealtime() - 0.001 * clock_gettimesince(initsystime); | ||
1080 | if (diff > nt_hibuftime) nt_hibuftime = diff; | ||
1081 | if (diff < nt_hibuftime - jittersec) | ||
1082 | { | ||
1083 | post("jitter excess %d %f", dac, diff); | ||
1084 | nt_resetmidisync(); | ||
1085 | } | ||
1086 | } | ||
1087 | |||
1088 | static double nt_midigettimefor(LARGE_INTEGER timestamp) | ||
1089 | { | ||
1090 | /* this is broken now... used to work when "timestamp" was derived from | ||
1091 | QueryPerformanceCounter() instead of the gates approved | ||
1092 | timeGetSystemTime() call in the MIDI callback routine below. */ | ||
1093 | return (nt_tixtotime(timestamp) - nt_hibuftime); | ||
1094 | } | ||
1095 | #endif /* MIDI_TIMESTAMP */ | ||
1096 | |||
1097 | |||
1098 | static int nt_fill = 0; | ||
1099 | #define WRAPFWD(x) ((x) >= nt_naudiobuffer ? (x) - nt_naudiobuffer: (x)) | ||
1100 | #define WRAPBACK(x) ((x) < 0 ? (x) + nt_naudiobuffer: (x)) | ||
1101 | #define MAXRESYNC 500 | ||
1102 | |||
1103 | #if 0 /* this is used for debugging */ | ||
1104 | static void nt_printaudiostatus(void) | ||
1105 | { | ||
1106 | int nad, nda; | ||
1107 | for (nad = 0; nad < nt_nwavein; nad++) | ||
1108 | { | ||
1109 | int phase = ntsnd_inphase[nad]; | ||
1110 | int phase2 = phase, phase3 = WRAPFWD(phase2), count, ntrans = 0; | ||
1111 | int firstphasedone = -1, firstphasebusy = -1; | ||
1112 | for (count = 0; count < nt_naudiobuffer; count++) | ||
1113 | { | ||
1114 | int donethis = | ||
1115 | (ntsnd_invec[nad][phase2].lpWaveHdr->dwFlags & WHDR_DONE); | ||
1116 | int donenext = | ||
1117 | (ntsnd_invec[nad][phase3].lpWaveHdr->dwFlags & WHDR_DONE); | ||
1118 | if (donethis && !donenext) | ||
1119 | { | ||
1120 | if (firstphasebusy >= 0) goto multipleadc; | ||
1121 | firstphasebusy = count; | ||
1122 | } | ||
1123 | if (!donethis && donenext) | ||
1124 | { | ||
1125 | if (firstphasedone >= 0) goto multipleadc; | ||
1126 | firstphasedone = count; | ||
1127 | } | ||
1128 | phase2 = phase3; | ||
1129 | phase3 = WRAPFWD(phase2 + 1); | ||
1130 | } | ||
1131 | post("nad %d phase %d busy %d done %d", nad, phase, firstphasebusy, | ||
1132 | firstphasedone); | ||
1133 | continue; | ||
1134 | multipleadc: | ||
1135 | startpost("nad %d phase %d: oops:", nad, phase); | ||
1136 | for (count = 0; count < nt_naudiobuffer; count++) | ||
1137 | { | ||
1138 | char buf[80]; | ||
1139 | sprintf(buf, " %d", | ||
1140 | (ntsnd_invec[nad][count].lpWaveHdr->dwFlags & WHDR_DONE)); | ||
1141 | poststring(buf); | ||
1142 | } | ||
1143 | endpost(); | ||
1144 | } | ||
1145 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
1146 | { | ||
1147 | int phase = ntsnd_outphase[nad]; | ||
1148 | int phase2 = phase, phase3 = WRAPFWD(phase2), count, ntrans = 0; | ||
1149 | int firstphasedone = -1, firstphasebusy = -1; | ||
1150 | for (count = 0; count < nt_naudiobuffer; count++) | ||
1151 | { | ||
1152 | int donethis = | ||
1153 | (ntsnd_outvec[nda][phase2].lpWaveHdr->dwFlags & WHDR_DONE); | ||
1154 | int donenext = | ||
1155 | (ntsnd_outvec[nda][phase3].lpWaveHdr->dwFlags & WHDR_DONE); | ||
1156 | if (donethis && !donenext) | ||
1157 | { | ||
1158 | if (firstphasebusy >= 0) goto multipledac; | ||
1159 | firstphasebusy = count; | ||
1160 | } | ||
1161 | if (!donethis && donenext) | ||
1162 | { | ||
1163 | if (firstphasedone >= 0) goto multipledac; | ||
1164 | firstphasedone = count; | ||
1165 | } | ||
1166 | phase2 = phase3; | ||
1167 | phase3 = WRAPFWD(phase2 + 1); | ||
1168 | } | ||
1169 | if (firstphasebusy < 0) post("nda %d phase %d all %d", | ||
1170 | nda, phase, (ntsnd_outvec[nad][0].lpWaveHdr->dwFlags & WHDR_DONE)); | ||
1171 | else post("nda %d phase %d busy %d done %d", nda, phase, firstphasebusy, | ||
1172 | firstphasedone); | ||
1173 | continue; | ||
1174 | multipledac: | ||
1175 | startpost("nda %d phase %d: oops:", nda, phase); | ||
1176 | for (count = 0; count < nt_naudiobuffer; count++) | ||
1177 | { | ||
1178 | char buf[80]; | ||
1179 | sprintf(buf, " %d", | ||
1180 | (ntsnd_outvec[nad][count].lpWaveHdr->dwFlags & WHDR_DONE)); | ||
1181 | poststring(buf); | ||
1182 | } | ||
1183 | endpost(); | ||
1184 | } | ||
1185 | } | ||
1186 | #endif /* 0 */ | ||
1187 | |||
1188 | /* this is a hack to avoid ever resyncing audio pointers in case for whatever | ||
1189 | reason the sync testing below gives false positives. */ | ||
1190 | |||
1191 | static int nt_resync_cancelled; | ||
1192 | |||
1193 | static void nt_noresync( void) | ||
1194 | { | ||
1195 | nt_resync_cancelled = 1; | ||
1196 | } | ||
1197 | |||
1198 | static void nt_resyncaudio(void) | ||
1199 | { | ||
1200 | UINT mmresult; | ||
1201 | int nad, nda, count; | ||
1202 | if (nt_resync_cancelled) | ||
1203 | return; | ||
1204 | /* for each open input device, eat all buffers which are marked | ||
1205 | ready. The next one will thus be "busy". */ | ||
1206 | post("resyncing audio"); | ||
1207 | for (nad = 0; nad < nt_nwavein; nad++) | ||
1208 | { | ||
1209 | int phase = ntsnd_inphase[nad]; | ||
1210 | for (count = 0; count < MAXRESYNC; count++) | ||
1211 | { | ||
1212 | WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; | ||
1213 | if (!(inwavehdr->dwFlags & WHDR_DONE)) break; | ||
1214 | if (inwavehdr->dwFlags & WHDR_PREPARED) | ||
1215 | waveInUnprepareHeader(ntsnd_indev[nad], | ||
1216 | inwavehdr, sizeof(WAVEHDR)); | ||
1217 | inwavehdr->dwFlags = 0L; | ||
1218 | waveInPrepareHeader(ntsnd_indev[nad], inwavehdr, sizeof(WAVEHDR)); | ||
1219 | mmresult = waveInAddBuffer(ntsnd_indev[nad], inwavehdr, | ||
1220 | sizeof(WAVEHDR)); | ||
1221 | if (mmresult != MMSYSERR_NOERROR) | ||
1222 | nt_waveinerror("waveInAddBuffer: %s\n", mmresult); | ||
1223 | ntsnd_inphase[nad] = phase = WRAPFWD(phase + 1); | ||
1224 | } | ||
1225 | if (count == MAXRESYNC) post("resync error 1"); | ||
1226 | } | ||
1227 | /* Each output buffer which is "ready" is filled with zeros and | ||
1228 | queued. */ | ||
1229 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
1230 | { | ||
1231 | int phase = ntsnd_outphase[nda]; | ||
1232 | for (count = 0; count < MAXRESYNC; count++) | ||
1233 | { | ||
1234 | WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; | ||
1235 | if (!(outwavehdr->dwFlags & WHDR_DONE)) break; | ||
1236 | if (outwavehdr->dwFlags & WHDR_PREPARED) | ||
1237 | waveOutUnprepareHeader(ntsnd_outdev[nda], | ||
1238 | outwavehdr, sizeof(WAVEHDR)); | ||
1239 | outwavehdr->dwFlags = 0L; | ||
1240 | memset((char *)(ntsnd_outvec[nda][phase].lpData), | ||
1241 | 0, (CHANNELS_PER_DEVICE * SAMPSIZE * nt_realdacblksize)); | ||
1242 | waveOutPrepareHeader(ntsnd_outdev[nda], outwavehdr, | ||
1243 | sizeof(WAVEHDR)); | ||
1244 | mmresult = waveOutWrite(ntsnd_outdev[nda], outwavehdr, | ||
1245 | sizeof(WAVEHDR)); | ||
1246 | if (mmresult != MMSYSERR_NOERROR) | ||
1247 | nt_waveouterror("waveOutAddBuffer: %s\n", mmresult); | ||
1248 | ntsnd_outphase[nda] = phase = WRAPFWD(phase + 1); | ||
1249 | } | ||
1250 | if (count == MAXRESYNC) post("resync error 2"); | ||
1251 | } | ||
1252 | |||
1253 | #ifdef MIDI_TIMESTAMP | ||
1254 | nt_resetmidisync(); | ||
1255 | #endif | ||
1256 | |||
1257 | } | ||
1258 | |||
1259 | #define LATE 0 | ||
1260 | #define RESYNC 1 | ||
1261 | #define NOTHING 2 | ||
1262 | static int nt_errorcount; | ||
1263 | static int nt_resynccount; | ||
1264 | static double nt_nextreporttime = -1; | ||
1265 | |||
1266 | void nt_logerror(int which) | ||
1267 | { | ||
1268 | #if 0 | ||
1269 | post("error %d %d", count, which); | ||
1270 | if (which < NOTHING) nt_errorcount++; | ||
1271 | if (which == RESYNC) nt_resynccount++; | ||
1272 | if (sys_getrealtime() > nt_nextreporttime) | ||
1273 | { | ||
1274 | post("%d audio I/O error%s", nt_errorcount, | ||
1275 | (nt_errorcount > 1 ? "s" : "")); | ||
1276 | if (nt_resynccount) post("DAC/ADC sync error"); | ||
1277 | nt_errorcount = nt_resynccount = 0; | ||
1278 | nt_nextreporttime = sys_getrealtime() - 5; | ||
1279 | } | ||
1280 | #endif | ||
1281 | } | ||
1282 | |||
1283 | /* system buffer with t_sample types for one tick */ | ||
1284 | t_sample *sys_soundout; | ||
1285 | t_sample *sys_soundin; | ||
1286 | float sys_dacsr; | ||
1287 | |||
1288 | int mmio_send_dacs(void) | ||
1289 | { | ||
1290 | HMMIO hmmio; | ||
1291 | UINT mmresult; | ||
1292 | HANDLE hFormat; | ||
1293 | int i, j; | ||
1294 | short *sp1, *sp2; | ||
1295 | float *fp1, *fp2; | ||
1296 | int nextfill, doxfer = 0; | ||
1297 | int nda, nad; | ||
1298 | if (!nt_nwavein && !nt_nwaveout) return (0); | ||
1299 | |||
1300 | |||
1301 | if (nt_meters) | ||
1302 | { | ||
1303 | int i, n; | ||
1304 | float maxsamp; | ||
1305 | for (i = 0, n = 2 * nt_nwavein * DEFDACBLKSIZE, maxsamp = nt_inmax; | ||
1306 | i < n; i++) | ||
1307 | { | ||
1308 | float f = sys_soundin[i]; | ||
1309 | if (f > maxsamp) maxsamp = f; | ||
1310 | else if (-f > maxsamp) maxsamp = -f; | ||
1311 | } | ||
1312 | nt_inmax = maxsamp; | ||
1313 | for (i = 0, n = 2 * nt_nwaveout * DEFDACBLKSIZE, maxsamp = nt_outmax; | ||
1314 | i < n; i++) | ||
1315 | { | ||
1316 | float f = sys_soundout[i]; | ||
1317 | if (f > maxsamp) maxsamp = f; | ||
1318 | else if (-f > maxsamp) maxsamp = -f; | ||
1319 | } | ||
1320 | nt_outmax = maxsamp; | ||
1321 | } | ||
1322 | |||
1323 | /* the "fill pointer" nt_fill controls where in the next | ||
1324 | I/O buffers we will write and/or read. If it's zero, we | ||
1325 | first check whether the buffers are marked "done". */ | ||
1326 | |||
1327 | if (!nt_fill) | ||
1328 | { | ||
1329 | for (nad = 0; nad < nt_nwavein; nad++) | ||
1330 | { | ||
1331 | int phase = ntsnd_inphase[nad]; | ||
1332 | WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; | ||
1333 | if (!(inwavehdr->dwFlags & WHDR_DONE)) goto idle; | ||
1334 | } | ||
1335 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
1336 | { | ||
1337 | int phase = ntsnd_outphase[nda]; | ||
1338 | WAVEHDR *outwavehdr = | ||
1339 | ntsnd_outvec[nda][phase].lpWaveHdr; | ||
1340 | if (!(outwavehdr->dwFlags & WHDR_DONE)) goto idle; | ||
1341 | } | ||
1342 | for (nad = 0; nad < nt_nwavein; nad++) | ||
1343 | { | ||
1344 | int phase = ntsnd_inphase[nad]; | ||
1345 | WAVEHDR *inwavehdr = | ||
1346 | ntsnd_invec[nad][phase].lpWaveHdr; | ||
1347 | if (inwavehdr->dwFlags & WHDR_PREPARED) | ||
1348 | waveInUnprepareHeader(ntsnd_indev[nad], | ||
1349 | inwavehdr, sizeof(WAVEHDR)); | ||
1350 | } | ||
1351 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
1352 | { | ||
1353 | int phase = ntsnd_outphase[nda]; | ||
1354 | WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; | ||
1355 | if (outwavehdr->dwFlags & WHDR_PREPARED) | ||
1356 | waveOutUnprepareHeader(ntsnd_outdev[nda], | ||
1357 | outwavehdr, sizeof(WAVEHDR)); | ||
1358 | } | ||
1359 | } | ||
1360 | |||
1361 | /* Convert audio output to fixed-point and put it in the output | ||
1362 | buffer. */ | ||
1363 | for (nda = 0, fp1 = sys_soundout; nda < nt_nwaveout; nda++) | ||
1364 | { | ||
1365 | int phase = ntsnd_outphase[nda]; | ||
1366 | |||
1367 | for (i = 0, sp1 = (short *)(ntsnd_outvec[nda][phase].lpData) + | ||
1368 | CHANNELS_PER_DEVICE * nt_fill; | ||
1369 | i < 2; i++, fp1 += DEFDACBLKSIZE, sp1++) | ||
1370 | { | ||
1371 | for (j = 0, fp2 = fp1, sp2 = sp1; j < DEFDACBLKSIZE; | ||
1372 | j++, fp2++, sp2 += CHANNELS_PER_DEVICE) | ||
1373 | { | ||
1374 | int x1 = 32767.f * *fp2; | ||
1375 | if (x1 > 32767) x1 = 32767; | ||
1376 | else if (x1 < -32767) x1 = -32767; | ||
1377 | *sp2 = x1; | ||
1378 | } | ||
1379 | } | ||
1380 | } | ||
1381 | memset(sys_soundout, 0, | ||
1382 | (DEFDACBLKSIZE *sizeof(t_sample)*CHANNELS_PER_DEVICE)*nt_nwaveout); | ||
1383 | |||
1384 | /* vice versa for the input buffer */ | ||
1385 | |||
1386 | for (nad = 0, fp1 = sys_soundin; nad < nt_nwavein; nad++) | ||
1387 | { | ||
1388 | int phase = ntsnd_inphase[nad]; | ||
1389 | |||
1390 | for (i = 0, sp1 = (short *)(ntsnd_invec[nad][phase].lpData) + | ||
1391 | CHANNELS_PER_DEVICE * nt_fill; | ||
1392 | i < 2; i++, fp1 += DEFDACBLKSIZE, sp1++) | ||
1393 | { | ||
1394 | for (j = 0, fp2 = fp1, sp2 = sp1; j < DEFDACBLKSIZE; | ||
1395 | j++, fp2++, sp2 += CHANNELS_PER_DEVICE) | ||
1396 | { | ||
1397 | *fp2 = ((float)(1./32767.)) * (float)(*sp2); | ||
1398 | } | ||
1399 | } | ||
1400 | } | ||
1401 | |||
1402 | nt_fill = nt_fill + DEFDACBLKSIZE; | ||
1403 | if (nt_fill == nt_realdacblksize) | ||
1404 | { | ||
1405 | nt_fill = 0; | ||
1406 | |||
1407 | for (nad = 0; nad < nt_nwavein; nad++) | ||
1408 | { | ||
1409 | int phase = ntsnd_inphase[nad]; | ||
1410 | HWAVEIN device = ntsnd_indev[nad]; | ||
1411 | WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; | ||
1412 | waveInPrepareHeader(device, inwavehdr, sizeof(WAVEHDR)); | ||
1413 | mmresult = waveInAddBuffer(device, inwavehdr, sizeof(WAVEHDR)); | ||
1414 | if (mmresult != MMSYSERR_NOERROR) | ||
1415 | nt_waveinerror("waveInAddBuffer: %s\n", mmresult); | ||
1416 | ntsnd_inphase[nad] = WRAPFWD(phase + 1); | ||
1417 | } | ||
1418 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
1419 | { | ||
1420 | int phase = ntsnd_outphase[nda]; | ||
1421 | HWAVEOUT device = ntsnd_outdev[nda]; | ||
1422 | WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; | ||
1423 | waveOutPrepareHeader(device, outwavehdr, sizeof(WAVEHDR)); | ||
1424 | mmresult = waveOutWrite(device, outwavehdr, sizeof(WAVEHDR)); | ||
1425 | if (mmresult != MMSYSERR_NOERROR) | ||
1426 | nt_waveouterror("waveOutWrite: %s\n", mmresult); | ||
1427 | ntsnd_outphase[nda] = WRAPFWD(phase + 1); | ||
1428 | } | ||
1429 | |||
1430 | /* check for DAC underflow or ADC overflow. */ | ||
1431 | for (nad = 0; nad < nt_nwavein; nad++) | ||
1432 | { | ||
1433 | int phase = WRAPBACK(ntsnd_inphase[nad] - 2); | ||
1434 | WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; | ||
1435 | if (inwavehdr->dwFlags & WHDR_DONE) goto late; | ||
1436 | } | ||
1437 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
1438 | { | ||
1439 | int phase = WRAPBACK(ntsnd_outphase[nda] - 2); | ||
1440 | WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; | ||
1441 | if (outwavehdr->dwFlags & WHDR_DONE) goto late; | ||
1442 | } | ||
1443 | } | ||
1444 | return (1); | ||
1445 | |||
1446 | late: | ||
1447 | |||
1448 | nt_logerror(LATE); | ||
1449 | nt_resyncaudio(); | ||
1450 | return (1); | ||
1451 | |||
1452 | idle: | ||
1453 | |||
1454 | /* If more than nt_adcjitterbufsallowed ADC buffers are ready | ||
1455 | on any input device, resynchronize */ | ||
1456 | |||
1457 | for (nad = 0; nad < nt_nwavein; nad++) | ||
1458 | { | ||
1459 | int phase = ntsnd_inphase[nad]; | ||
1460 | WAVEHDR *inwavehdr = | ||
1461 | ntsnd_invec[nad] | ||
1462 | [WRAPFWD(phase + nt_adcjitterbufsallowed)].lpWaveHdr; | ||
1463 | if (inwavehdr->dwFlags & WHDR_DONE) | ||
1464 | { | ||
1465 | nt_resyncaudio(); | ||
1466 | return (0); | ||
1467 | } | ||
1468 | } | ||
1469 | |||
1470 | /* test dac sync the same way */ | ||
1471 | for (nda = 0; nda < nt_nwaveout; nda++) | ||
1472 | { | ||
1473 | int phase = ntsnd_outphase[nda]; | ||
1474 | WAVEHDR *outwavehdr = | ||
1475 | ntsnd_outvec[nda] | ||
1476 | [WRAPFWD(phase + nt_dacjitterbufsallowed)].lpWaveHdr; | ||
1477 | if (outwavehdr->dwFlags & WHDR_DONE) | ||
1478 | { | ||
1479 | nt_resyncaudio(); | ||
1480 | return (0); | ||
1481 | } | ||
1482 | } | ||
1483 | #ifdef MIDI_TIMESTAMP | ||
1484 | nt_midisync(); | ||
1485 | #endif | ||
1486 | return (0); | ||
1487 | } | ||
1488 | |||
1489 | /* ------------------- public routines -------------------------- */ | ||
1490 | |||
1491 | void mmio_open_audio(int naudioindev, int *audioindev, | ||
1492 | int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, | ||
1493 | int nchoutdev, int *choutdev, int rate) /* IOhannes */ | ||
1494 | { | ||
1495 | int nbuf; | ||
1496 | |||
1497 | nt_realdacblksize = (sys_blocksize ? sys_blocksize : DEFREALDACBLKSIZE); | ||
1498 | nbuf = sys_advance_samples/nt_realdacblksize; | ||
1499 | if (nbuf >= MAXBUFFER) | ||
1500 | { | ||
1501 | fprintf(stderr, "pd: audio buffering maxed out to %d\n", | ||
1502 | (int)(MAXBUFFER * ((nt_realdacblksize * 1000.)/44100.))); | ||
1503 | nbuf = MAXBUFFER; | ||
1504 | } | ||
1505 | else if (nbuf < 4) nbuf = 4; | ||
1506 | fprintf(stderr, "%d audio buffers\n", nbuf); | ||
1507 | nt_naudiobuffer = nbuf; | ||
1508 | if (nt_adcjitterbufsallowed > nbuf - 2) | ||
1509 | nt_adcjitterbufsallowed = nbuf - 2; | ||
1510 | if (nt_dacjitterbufsallowed > nbuf - 2) | ||
1511 | nt_dacjitterbufsallowed = nbuf - 2; | ||
1512 | |||
1513 | nt_nwavein = sys_inchannels / 2; | ||
1514 | nt_nwaveout = sys_outchannels / 2; | ||
1515 | nt_whichadc = (naudioindev < 1 ? | ||
1516 | (nt_nwavein > 1 ? WAVE_MAPPER : -1) : audioindev[0]); | ||
1517 | nt_whichdac = (naudiooutdev < 1 ? | ||
1518 | (nt_nwaveout > 1 ? WAVE_MAPPER : -1) : audiooutdev[0]); | ||
1519 | if (naudiooutdev > 1 || naudioindev > 1) | ||
1520 | post("separate audio device choice not supported; using sequential devices."); | ||
1521 | mmio_do_open_audio(); | ||
1522 | } | ||
1523 | |||
1524 | |||
1525 | void mmio_reportidle(void) | ||
1526 | { | ||
1527 | } | ||
1528 | |||
1529 | #if 0 | ||
1530 | /* list the audio and MIDI device names */ | ||
1531 | void mmio_listdevs(void) | ||
1532 | { | ||
1533 | UINT wRtn, ndevices; | ||
1534 | unsigned int i; | ||
1535 | |||
1536 | ndevices = waveInGetNumDevs(); | ||
1537 | for (i = 0; i < ndevices; i++) | ||
1538 | { | ||
1539 | WAVEINCAPS wicap; | ||
1540 | wRtn = waveInGetDevCaps(i, (LPWAVEINCAPS) &wicap, | ||
1541 | sizeof(wicap)); | ||
1542 | if (wRtn) nt_waveinerror("waveInGetDevCaps: %s\n", wRtn); | ||
1543 | else fprintf(stderr, | ||
1544 | "audio input device #%d: %s\n", i+1, wicap.szPname); | ||
1545 | } | ||
1546 | |||
1547 | ndevices = waveOutGetNumDevs(); | ||
1548 | for (i = 0; i < ndevices; i++) | ||
1549 | { | ||
1550 | WAVEOUTCAPS wocap; | ||
1551 | wRtn = waveOutGetDevCaps(i, (LPWAVEOUTCAPS) &wocap, | ||
1552 | sizeof(wocap)); | ||
1553 | if (wRtn) nt_waveouterror("waveOutGetDevCaps: %s\n", wRtn); | ||
1554 | else fprintf(stderr, | ||
1555 | "audio output device #%d: %s\n", i+1, wocap.szPname); | ||
1556 | } | ||
1557 | } | ||
1558 | #endif | ||
1559 | |||
1560 | void mmio_getdevs(char *indevlist, int *nindevs, | ||
1561 | char *outdevlist, int *noutdevs, int *canmulti, | ||
1562 | int maxndev, int devdescsize) | ||
1563 | { | ||
1564 | int wRtn, ndev, i; | ||
1565 | |||
1566 | *canmulti = 2; /* supports multiple devices */ | ||
1567 | ndev = waveInGetNumDevs(); | ||
1568 | if (ndev > maxndev) | ||
1569 | ndev = maxndev; | ||
1570 | *nindevs = ndev; | ||
1571 | for (i = 0; i < ndev; i++) | ||
1572 | { | ||
1573 | WAVEINCAPS wicap; | ||
1574 | wRtn = waveInGetDevCaps(i, (LPWAVEINCAPS) &wicap, sizeof(wicap)); | ||
1575 | sprintf(indevlist + i * devdescsize, (wRtn ? "???" : wicap.szPname)); | ||
1576 | } | ||
1577 | |||
1578 | ndev = waveOutGetNumDevs(); | ||
1579 | if (ndev > maxndev) | ||
1580 | ndev = maxndev; | ||
1581 | *noutdevs = ndev; | ||
1582 | for (i = 0; i < ndev; i++) | ||
1583 | { | ||
1584 | WAVEOUTCAPS wocap; | ||
1585 | wRtn = waveOutGetDevCaps(i, (LPWAVEOUTCAPS) &wocap, sizeof(wocap)); | ||
1586 | sprintf(outdevlist + i * devdescsize, (wRtn ? "???" : wocap.szPname)); | ||
1587 | } | ||
1588 | } | ||