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