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
10 * SND_REGISTER -- Registers a PSG with the SND module.
11 * SND_INIT -- Initialize a SND_T
12 * ============================================================================
13 */
14
15 #include "config.h"
16 #include "periph/periph.h"
17 #include "snd.h"
18 #include "avi/avi.h"
19
20 LOCAL int32_t *mixbuf = NULL;
21 LOCAL uint32_t snd_tick(periph_t *const periph, uint32_t len);
22
23 /* ======================================================================== */
24 /* SND Private structure */
25 /* ======================================================================== */
26 typedef struct snd_pvt_t
27 {
28 avi_writer_t *avi;
29 } snd_pvt_t;
30
31 /* ======================================================================== */
32 /* WAV header. */
33 /* ======================================================================== */
34 LOCAL const uint8_t snd_wav_hdr[44] =
35 {
36 0x52, 0x49, 0x46, 0x46, /* 0 "RIFF" */
37 0x00, 0x00, 0x00, 0x00, /* 4 Total file length - 8 */
38 0x57, 0x41, 0x56, 0x45, /* 8 "WAVE" */
39 0x66, 0x6D, 0x74, 0x20, /* 12 "fmt " */
40 0x10, 0x00, 0x00, 0x00, /* 16 0x10 == PCM */
41 0x01, 0x00, /* 20 0x01 == PCM */
42 0x01, 0x00, /* 22 1 channel */
43 0x00, 0x00, 0x00, 0x00, /* 24 Sample rate */
44 0x00, 0x00, 0x00, 0x00, /* 28 Byte rate: sample rate * block align */
45 0x02, 0x00, /* 30 Block align: channels * bits/sample / 8 */
46 0x10, 0x00, /* 32 Bits/sample = 16. */
47 0x64, 0x61, 0x74, 0x61, /* 36 "data" */
48 0x00, 0x00, 0x00, 0x00 /* 40 Total sample data length */
49 };
50
51 /* ======================================================================== */
52 /* SND_UPDATE_WAV_HDR -- Keep the WAV header up to date. Stupid format. */
53 /* ======================================================================== */
snd_update_wav_hdr(FILE * f,int rate)54 LOCAL void snd_update_wav_hdr(FILE *f, int rate)
55 {
56 long filepos;
57 uint32_t tot_smp_bytes, tot_file_bytes;
58 uint8_t upd_wav_hdr[44];
59
60 /* -------------------------------------------------------------------- */
61 /* Poke the size information into the header both places it appears. */
62 /* Do this to a private copy of the header. */
63 /* -------------------------------------------------------------------- */
64 memcpy(upd_wav_hdr, snd_wav_hdr, 44);
65
66 filepos = ftell(f);
67 if (filepos < 0) /* do nothing if ftell() failed. */
68 return;
69
70 tot_smp_bytes = filepos > 44 ? filepos - 44 : 0;
71 tot_file_bytes = filepos > 8 ? filepos - 8 : 0;
72
73 /* Total file size, minus first 8 bytes */
74 upd_wav_hdr[ 4] = (tot_file_bytes >> 0) & 0xFF;
75 upd_wav_hdr[ 5] = (tot_file_bytes >> 8) & 0xFF;
76 upd_wav_hdr[ 6] = (tot_file_bytes >> 16) & 0xFF;
77 upd_wav_hdr[ 7] = (tot_file_bytes >> 24) & 0xFF;
78
79 /* Total size of sample-data payload */
80 upd_wav_hdr[40] = (tot_smp_bytes >> 0) & 0xFF;
81 upd_wav_hdr[41] = (tot_smp_bytes >> 8) & 0xFF;
82 upd_wav_hdr[42] = (tot_smp_bytes >> 16) & 0xFF;
83 upd_wav_hdr[43] = (tot_smp_bytes >> 24) & 0xFF;
84
85 /* Poke in the sample rate / byte rate too */
86 upd_wav_hdr[24] = (rate >> 0) & 0xFF;
87 upd_wav_hdr[25] = (rate >> 8) & 0xFF;
88 upd_wav_hdr[26] = (rate >> 16) & 0xFF;
89 upd_wav_hdr[27] = (rate >> 24) & 0xFF;
90 upd_wav_hdr[28] = (rate * 2 >> 0) & 0xFF;
91 upd_wav_hdr[29] = (rate * 2 >> 8) & 0xFF;
92 upd_wav_hdr[30] = (rate * 2 >> 16) & 0xFF;
93 upd_wav_hdr[31] = (rate * 2 >> 24) & 0xFF;
94
95 /* -------------------------------------------------------------------- */
96 /* Rewind to the beginning of the file to write the header. Skip */
97 /* it if the seek fails for some reason. */
98 /* -------------------------------------------------------------------- */
99 if (fseek(f, 0, SEEK_SET) == 0)
100 {
101 fwrite(upd_wav_hdr, 44, 1, f);
102 fseek(f, 0, SEEK_END);
103 }
104 }
105
106 /*
107 * ============================================================================
108 * SND_TICK -- Update state of the sound universe. Drains audio data
109 * from the PSGs
110 * ============================================================================
111 */
snd_tick(periph_t * const periph,uint32_t len)112 LOCAL uint32_t snd_tick(periph_t *const periph, uint32_t len)
113 {
114 snd_t *const snd = PERIPH_AS(snd_t, periph);
115 int min_num_dirty;
116 int i, j, k, mix;
117 int try_drop = 0, did_drop = 0, dly_drop = 0;
118 int16_t *clean;
119 uint64_t new_now;
120 int not_silent = snd->raw_start;
121 const int avi_active = avi_is_active(snd->pvt->avi);
122
123 /* -------------------------------------------------------------------- */
124 /* Check for volume up/down requests. */
125 /* -------------------------------------------------------------------- */
126 if (snd->change_vol == 1) snd->atten -= (snd->atten > 0);
127 if (snd->change_vol == 2) snd->atten += (snd->atten < 32);
128 snd->change_vol = 0;
129
130 /* -------------------------------------------------------------------- */
131 /* Trival case: No sound devices == no work for us. */
132 /* -------------------------------------------------------------------- */
133 if (snd->src_cnt == 0)
134 return len;
135
136 /* -------------------------------------------------------------------- */
137 /* If all of our buffers are dirty, we can't do anything, so return */
138 /* the fact that we've made no progress. */
139 /* -------------------------------------------------------------------- */
140 if (snd->mixbuf.num_clean == 0)
141 return 0;
142
143 /* -------------------------------------------------------------------- */
144 /* Calculate the minimum number of dirty buffers to process. We try */
145 /* to pull as many dirty buffers as we can while keeping all sources */
146 /* synchronized. */
147 /* -------------------------------------------------------------------- */
148 min_num_dirty = snd->src[0]->num_dirty;
149 for (i = 1; i < snd->src_cnt; i++)
150 {
151 if (min_num_dirty > snd->src[i]->num_dirty)
152 min_num_dirty = snd->src[i]->num_dirty;
153 }
154
155 /* -------------------------------------------------------------------- */
156 /* Try to drop everything coming to us. However, if we're dumping */
157 /* sound to a raw audio file or AVI, don't actually drop anything */
158 /* until after we've written the audio out to file. */
159 /* -------------------------------------------------------------------- */
160 try_drop = min_num_dirty;
161 if (snd->raw_file || avi_active)
162 {
163 dly_drop = try_drop;
164 try_drop = 0;
165 }
166
167 /* -------------------------------------------------------------------- */
168 /* Update the drop count by the number of buffers that we can drop */
169 /* during this update pass. */
170 /* -------------------------------------------------------------------- */
171 did_drop = try_drop;
172 //snd->mixbuf.tot_drop += did_drop; /* doesn't make sense to report it */
173
174 /* -------------------------------------------------------------------- */
175 /* Merge the dirty buffers together into mix buffers, and place the */
176 /* dirty buffers back on the clean list. This will allows the sound */
177 /* devices to continue generating sound while we wait for the mixed */
178 /* data to play. */
179 /* -------------------------------------------------------------------- */
180 assert(try_drop == 0 || dly_drop == 0);
181 for (i = try_drop; i < min_num_dirty; i++)
182 {
183 int clean_idx;
184 /* ---------------------------------------------------------------- */
185 /* Remove a buffer from the clean list for mixing. */
186 /* ---------------------------------------------------------------- */
187 clean_idx = --snd->mixbuf.num_clean;
188 clean = snd->mixbuf.clean[clean_idx];
189 snd->mixbuf.clean[clean_idx] = NULL; /* spot bugs! */
190
191 /* ---------------------------------------------------------------- */
192 /* Simple case: One source -- no mixing required. */
193 /* ---------------------------------------------------------------- */
194 if (snd->src_cnt == 1)
195 {
196 int16_t *tmp;
197
198 /*memcpy(clean, snd->src[0]->dirty[i], snd->buf_size * 2);*/
199
200 /* ------------------------------------------------------------ */
201 /* XXX: THIS IS AN EVIL HACK. I SHOULD BE DOING THE MEMCPY! */
202 /* ------------------------------------------------------------ */
203 tmp = snd->src[0]->dirty[i];
204 snd->src[0]->dirty[i] = clean;
205 clean = tmp;
206
207 /* ------------------------------------------------------------ */
208 /* Handle writing sound to raw files. */
209 /* ------------------------------------------------------------ */
210 if (snd->raw_file || !snd->raw_start)
211 for (j = 0; j < snd->buf_size && !not_silent; j++)
212 not_silent = clean[j];
213
214 goto one_source;
215 }
216
217 /* ---------------------------------------------------------------- */
218 /* Accumulate all of the source buffers at 32-bit precision. */
219 /* ---------------------------------------------------------------- */
220 memset(mixbuf, 0, snd->buf_size * sizeof(int));
221 for (j = 0; j < snd->src_cnt; j++)
222 {
223 for (k = 0; k < snd->buf_size; k++)
224 mixbuf[k] += snd->src[j]->dirty[i][k];
225 }
226
227 /* ---------------------------------------------------------------- */
228 /* Saturate the mix results to 16-bit precision and place them */
229 /* in the formerly-clean mix buffer. */
230 /* ---------------------------------------------------------------- */
231 for (j = 0; j < snd->buf_size; j++)
232 {
233 mix = mixbuf[j];
234 if (mix > 0x7FFF) mix = 0x7FFF;
235 if (mix < -0x8000) mix = -0x8000;
236 clean[j] = mix;
237 not_silent |= mix;
238 }
239
240 /* ---------------------------------------------------------------- */
241 /* "Atomically" place this in the dirty buffer list so that the */
242 /* snd_fill() routine can get to it. Handle delayed-drops due to */
243 /* file writing here. */
244 /* ---------------------------------------------------------------- */
245 one_source:
246
247 /* ---------------------------------------------------------------- */
248 /* If an AVI is open, send the audio to the AVI file. */
249 /* ---------------------------------------------------------------- */
250 if (avi_active && avi_start_time(snd->pvt->avi) < snd->periph.now)
251 avi_record_audio(snd->pvt->avi, clean, snd->buf_size,
252 !not_silent);
253
254 /* ---------------------------------------------------------------- */
255 /* If we're also writing this out to an audio file, do that last. */
256 /* ---------------------------------------------------------------- */
257 if (snd->raw_file && not_silent)
258 {
259 fwrite(clean, sizeof(int16_t), snd->buf_size,
260 snd->raw_file);
261 snd->raw_start = 1;
262 }
263
264 /* ---------------------------------------------------------------- */
265 /* If this frame wasn't actually put on the mixer's dirty list */
266 /* because we're dropping it, then put it back on the clean list. */
267 /* ---------------------------------------------------------------- */
268 assert(dly_drop > 0);
269 if (dly_drop > 0)
270 {
271 snd->mixbuf.clean[snd->mixbuf.num_clean++] = clean;
272 dly_drop--;
273 did_drop++;
274 }
275 }
276
277 /* -------------------------------------------------------------------- */
278 /* Update our sources with how much we actually _did_ drop. */
279 /* -------------------------------------------------------------------- */
280 for (i = 0; i < snd->src_cnt; i++)
281 {
282 snd->src[i]->tot_drop += did_drop;
283 if (snd->src[i]->drop >= did_drop)
284 snd->src[i]->drop -= did_drop;
285 else
286 snd->src[i]->drop = 0;
287 }
288
289 /* -------------------------------------------------------------------- */
290 /* Now fix up our sources' clean and dirty lists. */
291 /* -------------------------------------------------------------------- */
292 for (i = 0; i < snd->src_cnt; i++)
293 {
294 /* ---------------------------------------------------------------- */
295 /* Place the formerly dirty buffers on the clean list. */
296 /* ---------------------------------------------------------------- */
297 for (j = 0; j < min_num_dirty; j++)
298 snd->src[i]->clean[snd->src[i]->num_clean++] =
299 snd->src[i]->dirty[j];
300
301 /* ---------------------------------------------------------------- */
302 /* Remove the formerly dirty buffers from the dirty list. */
303 /* ---------------------------------------------------------------- */
304 snd->src[i]->num_dirty -= min_num_dirty;
305
306 if (min_num_dirty > 0)
307 {
308 for (j = 0; j < snd->src[i]->num_dirty; j++)
309 {
310 snd->src[i]->dirty[j] = snd->src[i]->dirty[min_num_dirty + j];
311 snd->src[i]->dirty[min_num_dirty + j] = NULL;
312 }
313 }
314 }
315
316 /* -------------------------------------------------------------------- */
317 /* Finally, figure out how many system ticks this accounted for. */
318 /* -------------------------------------------------------------------- */
319 snd->samples += min_num_dirty * snd->buf_size;
320 if (snd->time_scale > 0)
321 {
322 new_now = (double)snd->samples * snd->cyc_per_sec * snd->time_scale
323 / snd->rate;
324 if (new_now >= snd->periph.now) /* Uhoh... are we slipping away? */
325 len = new_now - snd->periph.now;
326 }
327
328 /* -------------------------------------------------------------------- */
329 /* If we're writing a sound sample file, update the file length. */
330 /* -------------------------------------------------------------------- */
331 if (snd->raw_file && not_silent)
332 snd_update_wav_hdr(snd->raw_file, snd->rate);
333
334 return len;
335 }
336
337 /*
338 * ============================================================================
339 * SND_REGISTER -- Registers a sound input buffer with the sound object
340 * ============================================================================
341 */
snd_register(periph_t * const per,snd_buf_t * const src)342 int snd_register
343 (
344 periph_t *const per, /* Sound object. */
345 snd_buf_t *const src /* Sound input buffer. */
346 )
347 {
348 int i;
349 snd_t *const snd = PERIPH_AS(snd_t, per);
350
351 /* -------------------------------------------------------------------- */
352 /* Initialize the sound buffer to all 0's and make ourselves parent */
353 /* -------------------------------------------------------------------- */
354 memset(src, 0, sizeof(snd_buf_t));
355 src->snd = snd;
356
357 /* -------------------------------------------------------------------- */
358 /* Set up its buffers as 'clean'. */
359 /* -------------------------------------------------------------------- */
360 src->num_clean = snd->buf_cnt;
361 src->tot_buf = snd->buf_cnt;
362 src->num_dirty = 0;
363
364 src->buf = CALLOC(int16_t, snd->buf_size * src->num_clean);
365 src->clean = CALLOC(int16_t *, src->num_clean);
366 src->dirty = CALLOC(int16_t *, src->num_clean);
367
368 if (!src->buf || !src->clean || !src->dirty)
369 {
370 fprintf(stderr, "snd_register: Out of memory allocating sndbuf.\n");
371 return -1;
372 }
373
374 for (i = 0; i < src->num_clean; i++)
375 {
376 src->clean[i] = src->buf + i * snd->buf_size;
377 src->dirty[i] = NULL;
378 }
379
380 /* -------------------------------------------------------------------- */
381 /* Add this sound source to our list of sound sources. */
382 /* -------------------------------------------------------------------- */
383 snd->src_cnt++;
384 snd->src = (snd_buf_t**) realloc(snd->src,
385 snd->src_cnt * sizeof(snd_buf_t*));
386 if (!snd->src)
387 {
388 fprintf(stderr, "Error: Out of memory in snd_register()\n");
389 return -1;
390 }
391 snd->src[snd->src_cnt - 1] = src;
392
393 return 0;
394 }
395
396 LOCAL void snd_dtor(periph_t *const p);
397
398 /*
399 * ============================================================================
400 * SND_INIT -- Initialize a SND_T
401 * ============================================================================
402 */
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)403 int snd_init(snd_t *snd, int rate, char *raw_file,
404 int user_snd_buf_size, int user_snd_buf_cnt,
405 struct avi_writer_t *const avi, int pal_mode, double time_scale)
406 {
407 int i;
408
409 /* -------------------------------------------------------------------- */
410 /* Prepare to fill up SND structure. */
411 /* -------------------------------------------------------------------- */
412 memset(snd, 0, sizeof(snd_t));
413 if (!(snd->pvt = CALLOC(snd_pvt_t, 1)))
414 {
415 fprintf(stderr, "snd_init: Out of memory allocating snd_pvt_t.\n");
416 goto fail;
417 }
418
419 snd->buf_size = user_snd_buf_size > 0 ? user_snd_buf_size
420 : SND_BUF_SIZE_DEFAULT;
421
422 snd->buf_cnt = user_snd_buf_cnt > 0 ? user_snd_buf_cnt
423 : SND_BUF_CNT_DEFAULT;
424
425
426 /* -------------------------------------------------------------------- */
427 /* Hook in AVI writer. */
428 /* -------------------------------------------------------------------- */
429 snd->pvt->avi = avi;
430
431 /* -------------------------------------------------------------------- */
432 /* Set up SND's internal varables. */
433 /* -------------------------------------------------------------------- */
434 snd->rate = rate;
435 snd->atten = 0;
436 snd->cyc_per_sec = pal_mode ? 1000000 : 894886;
437 snd->time_scale = time_scale;
438
439 /* -------------------------------------------------------------------- */
440 /* Set up SND as a peripheral. */
441 /* -------------------------------------------------------------------- */
442 snd->periph.read = NULL;
443 snd->periph.write = NULL;
444 snd->periph.peek = NULL;
445 snd->periph.poke = NULL;
446 snd->periph.tick = snd_tick;
447 snd->periph.min_tick = snd->buf_size * snd->cyc_per_sec / (2*rate);
448 snd->periph.max_tick = snd->periph.min_tick * 3;
449 snd->periph.addr_base = ~0U;
450 snd->periph.addr_mask = ~0U;
451 snd->periph.dtor = snd_dtor;
452
453 /* -------------------------------------------------------------------- */
454 /* Set up our mix buffers as 'clean'. */
455 /* -------------------------------------------------------------------- */
456 snd->mixbuf.tot_buf = snd->buf_cnt;
457 snd->mixbuf.num_clean = snd->buf_cnt;
458 snd->mixbuf.num_dirty = 0;
459 snd->mixbuf.buf = CALLOC(int16_t, snd->buf_size * snd->mixbuf.num_clean);
460 snd->mixbuf.clean = CALLOC(int16_t *, snd->mixbuf.num_clean);
461 snd->mixbuf.dirty = CALLOC(int16_t *, snd->mixbuf.num_clean);
462
463 if (mixbuf) free(mixbuf);
464 mixbuf = CALLOC(int32_t, snd->buf_size);
465
466 if (!snd->mixbuf.buf || !snd->mixbuf.clean || !snd->mixbuf.dirty || !mixbuf)
467 {
468 fprintf(stderr, "snd_init: Out of memory allocating mixbuf.\n");
469 goto fail;
470 }
471
472 for (i = 0; i < snd->mixbuf.num_clean; i++)
473 snd->mixbuf.clean[i] = snd->mixbuf.buf + i * snd->buf_size;
474
475 /* -------------------------------------------------------------------- */
476 /* If the user is dumping audio to a raw-audio file, open 'er up. */
477 /* -------------------------------------------------------------------- */
478 if (raw_file)
479 {
480 snd->raw_file = fopen(raw_file, "wb");
481 if (!snd->raw_file)
482 {
483 fprintf(stderr,"snd: Error opening '%s' for writing.\n",raw_file);
484 perror("fopen");
485 goto fail;
486 }
487 snd_update_wav_hdr(snd->raw_file, snd->rate);
488 return 0;
489 }
490
491 return 0;
492
493
494 fail:
495 CONDFREE(snd->pvt);
496 CONDFREE(snd->mixbuf.buf);
497 CONDFREE(snd->mixbuf.clean);
498 CONDFREE(snd->mixbuf.dirty);
499 CONDFREE(mixbuf);
500
501 return -1;
502 }
503
504 /* ======================================================================== */
505 /* SND_DTOR -- Tear down the sound device. */
506 /* ======================================================================== */
snd_dtor(periph_t * const p)507 LOCAL void snd_dtor(periph_t *const p)
508 {
509 snd_t *const snd = PERIPH_AS(snd_t, p);
510 int i;
511
512 CONDFREE(mixbuf);
513 CONDFREE(snd->mixbuf.buf);
514 CONDFREE(snd->mixbuf.clean);
515 CONDFREE(snd->mixbuf.dirty);
516
517 CONDFREE(snd->pvt);
518
519 for (i = 0; i < snd->src_cnt; i++)
520 if (snd->src[i])
521 {
522 CONDFREE(snd->src[i]->buf);
523 CONDFREE(snd->src[i]->clean);
524 CONDFREE(snd->src[i]->dirty);
525 }
526
527 CONDFREE(snd->src);
528 }
529
530 /*
531 * ============================================================================
532 * SND_PLAY_SILENCE -- Pump silent audio frame. (Used during reset.)
533 * SND_PLAY_STATIC -- A silly bit of fun.
534 * ============================================================================
535 */
snd_play_silence(snd_t * const snd)536 void snd_play_silence(snd_t *const snd) { UNUSED(snd); }
snd_play_static(snd_t * const snd)537 void snd_play_static (snd_t *const snd) { UNUSED(snd); }
538
539 /* ======================================================================== */
540 /* This program is free software; you can redistribute it and/or modify */
541 /* it under the terms of the GNU General Public License as published by */
542 /* the Free Software Foundation; either version 2 of the License, or */
543 /* (at your option) any later version. */
544 /* */
545 /* This program is distributed in the hope that it will be useful, */
546 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
547 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
548 /* General Public License for more details. */
549 /* */
550 /* You should have received a copy of the GNU General Public License along */
551 /* with this program; if not, write to the Free Software Foundation, Inc., */
552 /* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
553 /* ======================================================================== */
554 /* Copyright (c) 1998-1999, Joseph Zbiciak */
555 /* ======================================================================== */
556