1 /*
2 * ============================================================================
3 * Title: Sound Interface Abstraction
4 * Author: J. Zbiciak
5 * ============================================================================
6 *
7 * ============================================================================
8 * SND_TICK -- Update state of the sound universe. Drains audio data
9 * from the PSGs and prepares it for SDL's sound layer.
10 * SND_FILL -- Audio callback used by SDL for filling SDL's buffers.
11 * SND_REGISTER -- Registers a PSG with the SND module.
12 * SND_INIT -- Initialize a SND_T
13 * ============================================================================
14 */
15
16
17 #include "config.h"
18 #include "sdl_jzintv.h"
19 #include "periph/periph.h"
20 #include "snd.h"
21 #include "avi/avi.h"
22 #include "plat/plat_lib.h"
23
24 LOCAL int32_t *mixbuf = NULL;
25 LOCAL uint32_t snd_tick(periph_t *const periph, uint32_t len);
26
27 /* ======================================================================== */
28 /* SND Private structure */
29 /* All sound API specific stuff (SDL in this case) goes here. */
30 /* ======================================================================== */
31 typedef struct snd_pvt_t
32 {
33 SDL_AudioSpec *audio_fmt;
34 SDL_AudioCVT *audio_cvt;
35 avi_writer_t *avi;
36 } snd_pvt_t;
37
38
39 /* ======================================================================== */
40 /* WAV header. */
41 /* ======================================================================== */
42 LOCAL const uint8_t snd_wav_hdr[44] =
43 {
44 0x52, 0x49, 0x46, 0x46, /* 0 "RIFF" */
45 0x00, 0x00, 0x00, 0x00, /* 4 Total file length - 8 */
46 0x57, 0x41, 0x56, 0x45, /* 8 "WAVE" */
47 0x66, 0x6D, 0x74, 0x20, /* 12 "fmt " */
48 0x10, 0x00, 0x00, 0x00, /* 16 0x10 == PCM */
49 0x01, 0x00, /* 20 0x01 == PCM */
50 0x01, 0x00, /* 22 1 channel */
51 0x00, 0x00, 0x00, 0x00, /* 24 Sample rate */
52 0x00, 0x00, 0x00, 0x00, /* 28 Byte rate: sample rate * block align */
53 0x02, 0x00, /* 30 Block align: channels * bits/sample / 8 */
54 0x10, 0x00, /* 32 Bits/sample = 16. */
55 0x64, 0x61, 0x74, 0x61, /* 36 "data" */
56 0x00, 0x00, 0x00, 0x00 /* 40 Total sample data length */
57 };
58
59 /* ======================================================================== */
60 /* SND_UPDATE_WAV_HDR -- Keep the WAV header up to date. Stupid format. */
61 /* ======================================================================== */
snd_update_wav_hdr(FILE * f,int rate)62 LOCAL void snd_update_wav_hdr(FILE *f, int rate)
63 {
64 long filepos;
65 uint32_t tot_smp_bytes, tot_file_bytes;
66 uint8_t upd_wav_hdr[44];
67
68 /* -------------------------------------------------------------------- */
69 /* Poke the size information into the header both places it appears. */
70 /* Do this to a private copy of the header. */
71 /* -------------------------------------------------------------------- */
72 memcpy(upd_wav_hdr, snd_wav_hdr, 44);
73
74 filepos = ftell(f);
75 if (filepos < 0) /* do nothing if ftell() failed. */
76 return;
77
78 tot_smp_bytes = filepos > 44 ? filepos - 44 : 0;
79 tot_file_bytes = filepos > 8 ? filepos - 8 : 0;
80
81 /* Total file size, minus first 8 bytes */
82 upd_wav_hdr[ 4] = (tot_file_bytes >> 0) & 0xFF;
83 upd_wav_hdr[ 5] = (tot_file_bytes >> 8) & 0xFF;
84 upd_wav_hdr[ 6] = (tot_file_bytes >> 16) & 0xFF;
85 upd_wav_hdr[ 7] = (tot_file_bytes >> 24) & 0xFF;
86
87 /* Total size of sample-data payload */
88 upd_wav_hdr[40] = (tot_smp_bytes >> 0) & 0xFF;
89 upd_wav_hdr[41] = (tot_smp_bytes >> 8) & 0xFF;
90 upd_wav_hdr[42] = (tot_smp_bytes >> 16) & 0xFF;
91 upd_wav_hdr[43] = (tot_smp_bytes >> 24) & 0xFF;
92
93 /* Poke in the sample rate / byte rate too */
94 upd_wav_hdr[24] = (rate >> 0) & 0xFF;
95 upd_wav_hdr[25] = (rate >> 8) & 0xFF;
96 upd_wav_hdr[26] = (rate >> 16) & 0xFF;
97 upd_wav_hdr[27] = (rate >> 24) & 0xFF;
98 upd_wav_hdr[28] = (rate * 2 >> 0) & 0xFF;
99 upd_wav_hdr[29] = (rate * 2 >> 8) & 0xFF;
100 upd_wav_hdr[30] = (rate * 2 >> 16) & 0xFF;
101 upd_wav_hdr[31] = (rate * 2 >> 24) & 0xFF;
102
103 /* -------------------------------------------------------------------- */
104 /* Rewind to the beginning of the file to write the header. Skip */
105 /* it if the seek fails for some reason. */
106 /* -------------------------------------------------------------------- */
107 if (fseek(f, 0, SEEK_SET) == 0)
108 {
109 fwrite(upd_wav_hdr, 44, 1, f);
110 fseek(f, 0, SEEK_END);
111 }
112 }
113
114 /*
115 * ============================================================================
116 * SND_TICK -- Update state of the sound universe. Drains audio data
117 * from the PSGs and prepares it for SDL's sound layer.
118 * ============================================================================
119 */
snd_tick(periph_t * const periph,uint32_t len)120 LOCAL uint32_t snd_tick(periph_t *const periph, uint32_t len)
121 {
122 snd_t *const snd = PERIPH_AS(snd_t, periph);
123 int min_num_dirty;
124 int i, j, k, mix;
125 int try_drop = 0, did_drop = 0, dly_drop = 0;
126 bool drop_all = false;
127 int16_t *clean;
128 uint64_t new_now;
129 int not_silent = snd->raw_start;
130 const int avi_active = avi_is_active(snd->pvt->avi);
131
132 /* -------------------------------------------------------------------- */
133 /* Check for volume up/down requests. */
134 /* -------------------------------------------------------------------- */
135 if (snd->change_vol == 1) snd->atten -= (snd->atten > 0);
136 if (snd->change_vol == 2) snd->atten += (snd->atten < 32);
137 snd->change_vol = 0;
138
139 /* -------------------------------------------------------------------- */
140 /* Trival case: No sound devices == no work for us. */
141 /* -------------------------------------------------------------------- */
142 if (snd->src_cnt == 0)
143 return len;
144
145 /* -------------------------------------------------------------------- */
146 /* If all of our buffers are dirty, we can't do anything. */
147 /* If we're rate controlled, return the fact that we've made no */
148 /* progress. If we're uncontrolled, try to drop incoming audio. */
149 /* -------------------------------------------------------------------- */
150 SDL_LockAudio();
151 if (snd->mixbuf.num_clean == 0)
152 {
153 if (snd->time_scale > 0)
154 {
155 SDL_UnlockAudio();
156 return 0;
157 } else
158 drop_all = true;
159 }
160
161
162 /* -------------------------------------------------------------------- */
163 /* Step through all of the sound sources' dirty buffers and determine */
164 /* how many additional buffers we'll be dropping. Take the maximum */
165 /* across all audio sources. */
166 /* -------------------------------------------------------------------- */
167 if (!drop_all)
168 {
169 try_drop = 0;
170 for (i = 0; i < snd->src_cnt; i++)
171 {
172 if (try_drop < snd->src[i]->drop)
173 try_drop = snd->src[i]->drop;
174 }
175 } else
176 {
177 try_drop = snd->src[0]->num_dirty;
178 for (i = 0; i < snd->src_cnt; i++)
179 {
180 if (try_drop > snd->src[i]->num_dirty)
181 try_drop = snd->src[i]->num_dirty;
182 }
183 }
184
185 /* -------------------------------------------------------------------- */
186 /* If we're dumping sound to a raw audio file or AVI, don't actually */
187 /* drop anything until after we've written the audio out to file. */
188 /* -------------------------------------------------------------------- */
189 if (snd->raw_file || avi_active)
190 {
191 dly_drop = try_drop;
192 try_drop = 0;
193 }
194
195 /* -------------------------------------------------------------------- */
196 /* Calculate the minimum number of dirty buffers to process versus */
197 /* the number of clean buffers we have available, also taking into */
198 /* account the room we'll have since we're dropping buffers. */
199 /* -------------------------------------------------------------------- */
200 min_num_dirty = snd->mixbuf.num_clean + try_drop;
201 for (i = 0; i < snd->src_cnt; i++)
202 {
203 if (min_num_dirty > snd->src[i]->num_dirty)
204 min_num_dirty = snd->src[i]->num_dirty;
205 }
206
207 /* -------------------------------------------------------------------- */
208 /* Update the drop count by the number of buffers that we can drop */
209 /* during this update pass. */
210 /* -------------------------------------------------------------------- */
211 if (try_drop > min_num_dirty)
212 did_drop = min_num_dirty;
213 else
214 did_drop = try_drop;
215 if (!drop_all) /* lie about the drops due to uncontrolled speed */
216 snd->mixbuf.tot_drop += did_drop;
217 SDL_UnlockAudio();
218
219 /* -------------------------------------------------------------------- */
220 /* Merge the dirty buffers together into mix buffers, and place the */
221 /* dirty buffers back on the clean list. This will allows the sound */
222 /* devices to continue generating sound while we wait for the mixed */
223 /* data to play. */
224 /* -------------------------------------------------------------------- */
225 assert(try_drop == 0 || dly_drop == 0);
226 for (i = try_drop; i < min_num_dirty; i++)
227 {
228 int clean_idx;
229 /* ---------------------------------------------------------------- */
230 /* Remove a buffer from the clean list for mixing. */
231 /* ---------------------------------------------------------------- */
232 SDL_LockAudio();
233 clean_idx = --snd->mixbuf.num_clean;
234 clean = snd->mixbuf.clean[clean_idx];
235 snd->mixbuf.clean[clean_idx] = NULL; /* spot bugs! */
236 SDL_UnlockAudio();
237
238 /* ---------------------------------------------------------------- */
239 /* Simple case: One source -- no mixing required. */
240 /* ---------------------------------------------------------------- */
241 if (snd->src_cnt == 1)
242 {
243 int16_t *tmp;
244
245 /*memcpy(clean, snd->src[0]->dirty[i], snd->buf_size * 2);*/
246
247 /* ------------------------------------------------------------ */
248 /* XXX: THIS IS AN EVIL HACK. I SHOULD BE DOING THE MEMCPY! */
249 /* ------------------------------------------------------------ */
250 tmp = snd->src[0]->dirty[i];
251 snd->src[0]->dirty[i] = clean;
252 clean = tmp;
253
254 /* ------------------------------------------------------------ */
255 /* Handle writing sound to raw files. */
256 /* ------------------------------------------------------------ */
257 if (snd->raw_file || !snd->raw_start)
258 for (j = 0; j < snd->buf_size && !not_silent; j++)
259 not_silent = clean[j];
260
261 goto one_source;
262 }
263
264 /* ---------------------------------------------------------------- */
265 /* Accumulate all of the source buffers at 32-bit precision. */
266 /* ---------------------------------------------------------------- */
267 memset(mixbuf, 0, snd->buf_size * sizeof(int));
268 for (j = 0; j < snd->src_cnt; j++)
269 {
270 for (k = 0; k < snd->buf_size; k++)
271 mixbuf[k] += snd->src[j]->dirty[i][k];
272 }
273
274 /* ---------------------------------------------------------------- */
275 /* Saturate the mix results to 16-bit precision and place them */
276 /* in the formerly-clean mix buffer. */
277 /* ---------------------------------------------------------------- */
278 for (j = 0; j < snd->buf_size; j++)
279 {
280 mix = mixbuf[j];
281 if (mix > 0x7FFF) mix = 0x7FFF;
282 if (mix < -0x8000) mix = -0x8000;
283 clean[j] = mix;
284 not_silent |= mix;
285 }
286
287 /* ---------------------------------------------------------------- */
288 /* "Atomically" place this in the dirty buffer list so that the */
289 /* snd_fill() routine can get to it. Handle delayed-drops due to */
290 /* file writing here. */
291 /* ---------------------------------------------------------------- */
292 one_source:
293
294 if (dly_drop == 0)
295 {
296 SDL_LockAudio();
297 if (snd->mixbuf.num_dirty == 0)
298 snd->mixbuf.top_dirty_ptr = 0;
299
300 snd->mixbuf.dirty[snd->mixbuf.num_dirty++] = clean;
301 SDL_UnlockAudio();
302 }
303
304 /* ---------------------------------------------------------------- */
305 /* If an AVI is open, send the audio to the AVI file. */
306 /* ---------------------------------------------------------------- */
307 if (avi_active && avi_start_time(snd->pvt->avi) < snd->periph.now)
308 avi_record_audio(snd->pvt->avi, clean, snd->buf_size,
309 !not_silent);
310
311 /* ---------------------------------------------------------------- */
312 /* If we're also writing this out to an audio file, do that last. */
313 /* ---------------------------------------------------------------- */
314 if (snd->raw_file && not_silent)
315 {
316 fwrite(clean, sizeof(int16_t), snd->buf_size,
317 snd->raw_file);
318 snd->raw_start = 1;
319 }
320
321 /* ---------------------------------------------------------------- */
322 /* If this frame wasn't actually put on the mixer's dirty list */
323 /* because we're dropping it, then put it back on the clean list. */
324 /* ---------------------------------------------------------------- */
325 if (dly_drop > 0)
326 {
327 SDL_LockAudio();
328 snd->mixbuf.clean[snd->mixbuf.num_clean++] = clean;
329 SDL_UnlockAudio();
330 dly_drop--;
331 did_drop++;
332 }
333 }
334
335 /* -------------------------------------------------------------------- */
336 /* Update our sources with how much we actually _did_ drop. */
337 /* -------------------------------------------------------------------- */
338 for (i = 0; i < snd->src_cnt; i++)
339 {
340 snd->src[i]->tot_drop += did_drop;
341 if (snd->src[i]->drop >= did_drop)
342 snd->src[i]->drop -= did_drop;
343 else
344 snd->src[i]->drop = 0;
345 }
346
347 /* -------------------------------------------------------------------- */
348 /* Now fix up our sources' clean and dirty lists. */
349 /* -------------------------------------------------------------------- */
350 for (i = 0; i < snd->src_cnt; i++)
351 {
352 /* ---------------------------------------------------------------- */
353 /* Place the formerly dirty buffers on the clean list. */
354 /* ---------------------------------------------------------------- */
355 for (j = 0; j < min_num_dirty; j++)
356 snd->src[i]->clean[snd->src[i]->num_clean++] =
357 snd->src[i]->dirty[j];
358
359 /* ---------------------------------------------------------------- */
360 /* Remove the formerly dirty buffers from the dirty list. */
361 /* ---------------------------------------------------------------- */
362 snd->src[i]->num_dirty -= min_num_dirty;
363
364 if (min_num_dirty > 0)
365 {
366 for (j = 0; j < snd->src[i]->num_dirty; j++)
367 {
368 snd->src[i]->dirty[j] = snd->src[i]->dirty[min_num_dirty + j];
369 snd->src[i]->dirty[min_num_dirty + j] = NULL;
370 }
371 }
372 }
373
374 /* -------------------------------------------------------------------- */
375 /* Unpause the audio driver if we're sufficiently piped up. */
376 /* -------------------------------------------------------------------- */
377 SDL_LockAudio();
378 if (snd->mixbuf.num_dirty > snd->mixbuf.num_clean)
379 SDL_PauseAudio(0);
380 SDL_UnlockAudio();
381
382 /* -------------------------------------------------------------------- */
383 /* Finally, figure out how many system ticks this accounted for. */
384 /* -------------------------------------------------------------------- */
385 snd->samples += min_num_dirty * snd->buf_size;
386 if (snd->time_scale > 0)
387 {
388 new_now = (double)snd->samples * snd->cyc_per_sec * snd->time_scale
389 / snd->rate;
390 if (new_now >= snd->periph.now) /* Uhoh... are we slipping away? */
391 len = new_now - snd->periph.now;
392 }
393
394 /* -------------------------------------------------------------------- */
395 /* If we're writing a sound sample file, update the file length. */
396 /* -------------------------------------------------------------------- */
397 if (snd->raw_file && not_silent)
398 snd_update_wav_hdr(snd->raw_file, snd->rate);
399
400 return len;
401 }
402
403 /*
404 * ============================================================================
405 * SND_FILL -- Audio callback used by SDL for filling SDL's buffers.
406 * ============================================================================
407 */
snd_fill(void * udata,uint8_t * stream,int len)408 LOCAL void snd_fill(void *udata, uint8_t *stream, int len)
409 {
410 snd_t *snd = (snd_t*)udata;
411
412 snd->tot_dirty += snd->mixbuf.num_dirty;
413 snd->tot_frame++;
414
415 /* -------------------------------------------------------------------- */
416 /* Sad case: We're slipping behind. */
417 /* -------------------------------------------------------------------- */
418 if (snd->mixbuf.num_dirty == 0)
419 {
420 /* Grab a clean buffer and zero it. Ugly. */
421 snd->mixbuf.dirty[snd->mixbuf.num_dirty++] =
422 snd->mixbuf.clean[--snd->mixbuf.num_clean];
423 memset(snd->mixbuf.dirty[0], 0,
424 snd->buf_size * sizeof(snd->mixbuf.dirty[0][0]));
425 }
426
427 /* -------------------------------------------------------------------- */
428 /* Do it if we can. */
429 /* -------------------------------------------------------------------- */
430 if (len > 0)
431 {
432 int i;
433
434 /* ---------------------------------------------------------------- */
435 /* Write out the audio stream PRONTO! */
436 /* ---------------------------------------------------------------- */
437 if (snd->atten > 0)
438 {
439 int a = (snd->atten + 1) >> 1;
440
441 for (i = 0; i < snd->buf_size; i++)
442 snd->mixbuf.dirty[0][i] >>= a;
443 }
444
445 if (snd->atten & 1)
446 for (i = 0; i < snd->buf_size; i++)
447 snd->mixbuf.dirty[0][i] += snd->mixbuf.dirty[0][i] >> 1;
448
449 /* ---------------------------------------------------------------- */
450 /* Direct write if no AudioCVT, else convert it on the fly. */
451 /* ---------------------------------------------------------------- */
452 if (!snd->pvt->audio_cvt)
453 {
454 memcpy(stream, snd->mixbuf.dirty[0], len);
455 } else
456 {
457 #if !defined(__EMSCRIPTEN__)
458 SDL_AudioCVT *cvt = snd->pvt->audio_cvt;
459
460 cvt->len = len / cvt->len_ratio;
461 memcpy(cvt->buf, snd->mixbuf.dirty[0], cvt->len);
462
463 SDL_ConvertAudio(cvt);
464
465 memcpy(stream, cvt->buf, len);
466 #endif
467 }
468
469 /* ---------------------------------------------------------------- */
470 /* Put our buffer back on the clean buffer list. */
471 /* ---------------------------------------------------------------- */
472 snd->mixbuf.clean[snd->mixbuf.num_clean++] = snd->mixbuf.dirty[0];
473
474 /* ---------------------------------------------------------------- */
475 /* Update the dirty buffer list. */
476 /* ---------------------------------------------------------------- */
477 snd->mixbuf.num_dirty--;
478 for (i = 0; i < snd->mixbuf.num_dirty; i++)
479 snd->mixbuf.dirty[i] = snd->mixbuf.dirty[i + 1];
480
481 return;
482 }
483 }
484
485 /*
486 * ============================================================================
487 * SND_REGISTER -- Registers a sound input buffer with the sound object
488 * ============================================================================
489 */
snd_register(periph_t * const per,snd_buf_t * const src)490 int snd_register
491 (
492 periph_t *const per, /* Sound object. */
493 snd_buf_t *const src /* Sound input buffer. */
494 )
495 {
496 int i;
497 snd_t *const snd = PERIPH_AS(snd_t, per);
498
499 /* -------------------------------------------------------------------- */
500 /* Initialize the sound buffer to all 0's and make ourselves parent */
501 /* -------------------------------------------------------------------- */
502 memset(src, 0, sizeof(snd_buf_t));
503 src->snd = snd;
504
505 /* -------------------------------------------------------------------- */
506 /* Set up its buffers as 'clean'. */
507 /* -------------------------------------------------------------------- */
508 src->num_clean = snd->buf_cnt;
509 src->tot_buf = snd->buf_cnt;
510 src->num_dirty = 0;
511
512 src->buf = CALLOC(int16_t, snd->buf_size * src->num_clean);
513 src->clean = CALLOC(int16_t *, src->num_clean);
514 src->dirty = CALLOC(int16_t *, src->num_clean);
515
516 if (!src->buf || !src->clean || !src->dirty)
517 {
518 fprintf(stderr, "snd_register: Out of memory allocating sndbuf.\n");
519 return -1;
520 }
521
522 for (i = 0; i < src->num_clean; i++)
523 {
524 src->clean[i] = src->buf + i * snd->buf_size;
525 src->dirty[i] = NULL;
526 }
527
528
529 /* -------------------------------------------------------------------- */
530 /* Add this sound source to our list of sound sources. */
531 /* -------------------------------------------------------------------- */
532 snd->src_cnt++;
533 snd->src = (snd_buf_t**) realloc(snd->src,
534 snd->src_cnt * sizeof(snd_buf_t*));
535 if (!snd->src)
536 {
537 fprintf(stderr, "Error: Out of memory in snd_register()\n");
538 return -1;
539 }
540 snd->src[snd->src_cnt - 1] = src;
541
542 return 0;
543 }
544
545 /*
546 * ============================================================================
547 * SND_FMT_NAME -- Return human-readable sound format name
548 * ============================================================================
549 */
snd_fmt_name(uint16_t fmt)550 LOCAL const char *snd_fmt_name(uint16_t fmt)
551 {
552 return fmt == AUDIO_U8 ? "Unsigned 8-bit"
553 : fmt == AUDIO_S8 ? "Signed 8-bit"
554 : fmt == AUDIO_U16SYS ? "Unsigned 16-bit (native)"
555 : fmt == AUDIO_U16LSB ? "Unsigned 16-bit (LSB first)"
556 : fmt == AUDIO_U16MSB ? "Unsigned 16-bit (MSB first)"
557 : fmt == AUDIO_U16 ? "Unsigned 16-bit (LSB first?)"
558 : fmt == AUDIO_S16SYS ? "Signed 16-bit (native)"
559 : fmt == AUDIO_S16LSB ? "Signed 16-bit (LSB first)"
560 : fmt == AUDIO_S16MSB ? "Signed 16-bit (MSB first)"
561 : fmt == AUDIO_S16 ? "Signed 16-bit (LSB first?)"
562 : "Unknown";
563 }
564
565 LOCAL void snd_dtor(periph_t *const p);
566
567 /*
568 * ============================================================================
569 * SND_INIT -- Initialize a SND_T
570 * ============================================================================
571 */
snd_init(snd_t * snd,int rate,char * raw_file,int user_snd_buf_size,int user_snd_buf_cnt,struct avi_writer_t * const avi,int pal_mode,double time_scale)572 int snd_init(snd_t *snd, int rate, char *raw_file,
573 int user_snd_buf_size, int user_snd_buf_cnt,
574 struct avi_writer_t *const avi, int pal_mode, double time_scale)
575 {
576 int i;
577 SDL_AudioSpec *wanted = NULL, *actual = NULL;
578 SDL_AudioCVT *audio_cvt = NULL;
579 uint8_t *cvt_buf = NULL;
580
581 /* -------------------------------------------------------------------- */
582 /* Prepare to fill up SND structure. */
583 /* -------------------------------------------------------------------- */
584 memset(snd, 0, sizeof(snd_t));
585 if (!(snd->pvt = CALLOC(snd_pvt_t, 1)))
586 {
587 fprintf(stderr, "snd_init: Out of memory allocating snd_pvt_t.\n");
588 goto fail;
589 }
590
591 snd->buf_size = user_snd_buf_size > 0 ? user_snd_buf_size
592 : SND_BUF_SIZE_DEFAULT;
593
594 snd->buf_cnt = user_snd_buf_cnt > 0 ? user_snd_buf_cnt
595 : SND_BUF_CNT_DEFAULT;
596
597 /* -------------------------------------------------------------------- */
598 /* Open the audio device asking for our preferred format, but be */
599 /* prepared for it to be different than what we asked for. */
600 /* -------------------------------------------------------------------- */
601 if (!(wanted = CALLOC(SDL_AudioSpec, 1)) ||
602 !(actual = CALLOC(SDL_AudioSpec, 1)))
603 {
604 fprintf(stderr, "snd_init: Out of memory allocating AudioSpec.\n");
605 goto fail;
606 }
607
608 wanted->freq = rate;
609 wanted->format = AUDIO_S16SYS;
610 wanted->channels = 1;
611 wanted->samples = snd->buf_size;
612 wanted->callback = snd_fill;
613 wanted->userdata = (void*)snd;
614
615 if ( SDL_OpenAudio(wanted, actual) < 0 )
616 {
617 fprintf(stderr, "snd: Couldn't open audio: %s\n", SDL_GetError());
618 goto fail;
619 }
620
621 /* -------------------------------------------------------------------- */
622 /* If we get a different format, complain and set up an AudioCVT. */
623 /* (Previously, we forced SDL to convert for us by passing NULL for */
624 /* "actual", but it appears some SDL ports don't do this?) */
625 /* -------------------------------------------------------------------- */
626 if (wanted->freq != actual->freq ||
627 wanted->format != actual->format ||
628 wanted->channels != actual->channels ||
629 snd->buf_size != actual->samples)
630 {
631 jzp_printf
632 (
633 "snd: Requested %d chan @ %5dHz, %s, bufsize %5d\n"
634 "snd: Got %d chan @ %5dHz, %s, bufsize %5d\n",
635 wanted->channels, wanted->freq, snd_fmt_name(wanted->format),
636 snd->buf_size,
637
638 actual->channels, actual->freq, snd_fmt_name(actual->format),
639 actual->samples
640 );
641
642 /* ---------------------------------------------------------------- */
643 /* If we got a smaller buffer size than we wanted, and the user */
644 /* didn't explicitly specify a number of buffers, increase our */
645 /* total buffer pool to compensate. */
646 /* ---------------------------------------------------------------- */
647 int adjusted_buf_cnt = snd->buf_cnt;
648 if (user_snd_buf_cnt <= 0 && actual->samples < snd->buf_size)
649 {
650 adjusted_buf_cnt =
651 (snd->buf_cnt * snd->buf_size + actual->samples / 2)
652 / actual->samples;
653 }
654
655 if (adjusted_buf_cnt > snd->buf_cnt)
656 {
657 jzp_printf(
658 "snd: Increasing bufcnt from %d to %d based on bufsize %d\n",
659 snd->buf_cnt, adjusted_buf_cnt, actual->samples);
660 snd->buf_cnt = adjusted_buf_cnt;
661 }
662
663 /* ---------------------------------------------------------------- */
664 /* Build a conversion structure only if format/channels differ. */
665 /* ---------------------------------------------------------------- */
666 if (wanted->format != actual->format ||
667 wanted->channels != actual->channels)
668 {
669 jzp_printf("snd: Using SDL_AudioCVT\n");
670
671 if (!(audio_cvt = CALLOC(SDL_AudioCVT, 1)))
672 {
673 fprintf(stderr, "snd_init: Out of memory allocating "
674 "AudioCVT structures.\n");
675 goto fail;
676 }
677
678 #if !defined(__EMSCRIPTEN__)
679 if (SDL_BuildAudioCVT
680 (
681 audio_cvt,
682 /* source format */
683 wanted->format,
684 wanted->channels,
685 actual->freq, /* jzintv does rate conversion */
686 /* dest format */
687 actual->format,
688 actual->channels,
689 actual->freq
690 ) <= 0)
691 #endif
692 {
693 fprintf(stderr, "snd_init: Could not set up SDL_AudioCVT\n");
694 goto fail;
695 }
696
697 audio_cvt->len = actual->size;
698
699 if (!(cvt_buf = CALLOC(uint8_t,
700 audio_cvt->len*audio_cvt->len_mult)))
701 {
702 fprintf(stderr, "snd_init: Out of memory allocating "
703 "AudioCVT structures.\n");
704 goto fail;
705 }
706
707 audio_cvt->buf = cvt_buf;
708 }
709 }
710
711 free(wanted);
712 wanted = NULL;
713
714 /* -------------------------------------------------------------------- */
715 /* Hook in SDL-specific fields in SND. */
716 /* -------------------------------------------------------------------- */
717 snd->pvt->audio_fmt = actual;
718 snd->pvt->audio_cvt = audio_cvt;
719
720 /* -------------------------------------------------------------------- */
721 /* Hook in AVI writer. */
722 /* -------------------------------------------------------------------- */
723 snd->pvt->avi = avi;
724
725 /* -------------------------------------------------------------------- */
726 /* Set up SND's internal varables. */
727 /* -------------------------------------------------------------------- */
728 snd->buf_size = actual->samples;
729 snd->rate = actual->freq;
730 snd->atten = 0;
731 snd->cyc_per_sec = pal_mode ? 1000000 : 894886;
732 snd->time_scale = time_scale;
733
734 /* -------------------------------------------------------------------- */
735 /* Set up SND as a peripheral. */
736 /* -------------------------------------------------------------------- */
737 snd->periph.read = NULL;
738 snd->periph.write = NULL;
739 snd->periph.peek = NULL;
740 snd->periph.poke = NULL;
741 snd->periph.tick = snd_tick;
742 snd->periph.min_tick = snd->buf_size * snd->cyc_per_sec / (2*rate);
743 snd->periph.max_tick = ~0U;
744 snd->periph.addr_base = ~0U;
745 snd->periph.addr_mask = ~0U;
746 snd->periph.dtor = snd_dtor;
747
748 /* -------------------------------------------------------------------- */
749 /* Set up our mix buffers as 'clean'. */
750 /* -------------------------------------------------------------------- */
751 snd->mixbuf.tot_buf = snd->buf_cnt;
752 snd->mixbuf.num_clean = snd->buf_cnt;
753 snd->mixbuf.num_dirty = 0;
754 snd->mixbuf.buf = CALLOC(int16_t, snd->buf_size * snd->mixbuf.num_clean);
755 snd->mixbuf.clean = CALLOC(int16_t *, snd->mixbuf.num_clean);
756 snd->mixbuf.dirty = CALLOC(int16_t *, snd->mixbuf.num_clean);
757
758 if (mixbuf) free(mixbuf);
759 mixbuf = CALLOC(int32_t, snd->buf_size);
760
761 if (!snd->mixbuf.buf || !snd->mixbuf.clean || !snd->mixbuf.dirty || !mixbuf)
762 {
763 fprintf(stderr, "snd_init: Out of memory allocating mixbuf.\n");
764 goto fail;
765 }
766
767 for (i = 0; i < snd->mixbuf.num_clean; i++)
768 snd->mixbuf.clean[i] = snd->mixbuf.buf + i * snd->buf_size;
769
770 /* -------------------------------------------------------------------- */
771 /* If the user is dumping audio to a raw-audio file, open 'er up. */
772 /* -------------------------------------------------------------------- */
773 if (raw_file)
774 {
775 snd->raw_file = fopen(raw_file, "wb");
776 if (!snd->raw_file)
777 {
778 fprintf(stderr,"snd: Error opening '%s' for writing.\n",raw_file);
779 perror("fopen");
780 goto fail;
781 }
782 snd_update_wav_hdr(snd->raw_file, snd->rate);
783 return 0;
784 }
785
786 /* -------------------------------------------------------------------- */
787 /* Make sure the audio is paused. */
788 /* -------------------------------------------------------------------- */
789 SDL_PauseAudio(1);
790
791 return 0;
792
793 fail:
794 CONDFREE(wanted);
795 CONDFREE(actual);
796 CONDFREE(audio_cvt);
797 CONDFREE(cvt_buf);
798 CONDFREE(snd->pvt);
799 CONDFREE(snd->mixbuf.buf);
800 CONDFREE(snd->mixbuf.clean);
801 CONDFREE(snd->mixbuf.dirty);
802 CONDFREE(mixbuf);
803
804 return -1;
805 }
806
807 /* ======================================================================== */
808 /* SND_DTOR -- Tear down the sound device. */
809 /* ======================================================================== */
snd_dtor(periph_t * const p)810 LOCAL void snd_dtor(periph_t *const p)
811 {
812 snd_t *const snd = PERIPH_AS(snd_t, p);
813 snd_pvt_t *const pvt = (snd_pvt_t *)snd->pvt;
814 int i;
815
816 SDL_CloseAudio();
817 CONDFREE(mixbuf);
818 CONDFREE(snd->mixbuf.buf);
819 CONDFREE(snd->mixbuf.clean);
820 CONDFREE(snd->mixbuf.dirty);
821
822 if (pvt)
823 {
824 if (pvt->audio_cvt)
825 {
826 CONDFREE(pvt->audio_cvt->buf);
827 CONDFREE(pvt->audio_cvt);
828 }
829 CONDFREE(pvt->audio_fmt);
830 }
831
832 CONDFREE(snd->pvt);
833
834 for (i = 0; i < snd->src_cnt; i++)
835 if (snd->src[i])
836 {
837 CONDFREE(snd->src[i]->buf);
838 CONDFREE(snd->src[i]->clean);
839 CONDFREE(snd->src[i]->dirty);
840 }
841
842 CONDFREE(snd->src);
843 }
844
845 /* ======================================================================== */
846 /* SND_PLAY_SILENCE -- Pump silent audio frames. (Used during reset.) */
847 /* ======================================================================== */
snd_play_silence(snd_t * const snd)848 void snd_play_silence(snd_t *const snd)
849 {
850 SDL_LockAudio();
851
852 while (snd->mixbuf.num_clean > 0)
853 {
854 const int clean_idx = --snd->mixbuf.num_clean;
855 int16_t *const clean = snd->mixbuf.clean[clean_idx];
856 snd->mixbuf.clean[clean_idx] = NULL; /* spot bugs! */
857
858 memset(clean, 0, snd->buf_size * sizeof(*clean));
859
860 if (snd->mixbuf.num_dirty == 0)
861 snd->mixbuf.top_dirty_ptr = 0;
862
863 snd->mixbuf.dirty[snd->mixbuf.num_dirty++] = clean;
864 }
865
866 SDL_PauseAudio(0);
867 SDL_UnlockAudio();
868
869 /* -------------------------------------------------------------------- */
870 /* Move all of our sources' dirty buffers to the clean list. */
871 /* -------------------------------------------------------------------- */
872 for (int i = 0; i < snd->src_cnt; i++)
873 {
874 for (int j = 0; j < snd->src[i]->num_dirty; j++)
875 snd->src[i]->clean[snd->src[i]->num_clean++] =
876 snd->src[i]->dirty[j];
877
878 snd->src[i]->num_dirty = 0;
879 }
880 }
881
882 /* ======================================================================== */
883 /* SND_PLAY_STATIC -- A silly bit of fun. */
884 /* ======================================================================== */
snd_play_static(snd_t * const snd)885 void snd_play_static(snd_t *const snd)
886 {
887 SDL_LockAudio();
888
889 while (snd->mixbuf.num_clean > 0)
890 {
891 const int clean_idx = --snd->mixbuf.num_clean;
892 int16_t *const clean = snd->mixbuf.clean[clean_idx];
893 snd->mixbuf.clean[clean_idx] = NULL; /* spot bugs! */
894
895 for (int i = 0; i < snd->buf_size; ++i)
896 clean[i] = (rand_jz() & 0x3FFF) - 0x2000;
897
898 if (snd->mixbuf.num_dirty == 0)
899 snd->mixbuf.top_dirty_ptr = 0;
900
901 snd->mixbuf.dirty[snd->mixbuf.num_dirty++] = clean;
902 }
903
904 SDL_PauseAudio(0);
905 SDL_UnlockAudio();
906 }
907
908 /* ======================================================================== */
909 /* This program is free software; you can redistribute it and/or modify */
910 /* it under the terms of the GNU General Public License as published by */
911 /* the Free Software Foundation; either version 2 of the License, or */
912 /* (at your option) any later version. */
913 /* */
914 /* This program is distributed in the hope that it will be useful, */
915 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
916 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
917 /* General Public License for more details. */
918 /* */
919 /* You should have received a copy of the GNU General Public License along */
920 /* with this program; if not, write to the Free Software Foundation, Inc., */
921 /* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
922 /* ======================================================================== */
923 /* Copyright (c) 1998-1999, Joseph Zbiciak */
924 /* ======================================================================== */
925