]> Shamusworld >> Repos - stargem2/blob - src/sound.cpp
Converted to SDL 2, added fullscreen support (F12 to toggle).
[stargem2] / src / sound.cpp
1 //
2 // Sound Interface v2.0
3 //
4 // by James L. Hammons
5 // (c) 2006 Underground Software
6 //
7 // JLH = James L. Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  06/15/2006  Added changelog ;-)
12 // JLH  06/15/2006  Scrubbed all BYTE, WORD & DWORD references from the code
13 // JLH  02/10/2009  Fixed sound IRQ callback so that CPU samples are taken at
14 //                  cycle exact boundaries
15 // JLH  07/21/2009  Moved numeric constants into macros for sanity's sake
16 //
17 // Notes:
18 //   The sound CPU (6808) runs at 894,750 (3,579,000 / 4) Hz.
19 //   At 44.1 KHz, this works out to 894750 / 44100 = 20.289115646... cycles per sample.
20 //
21
22 // Still need to add volume control...
23
24 #include "sound.h"
25
26 #include <SDL2/SDL.h>
27 #include <stdint.h>
28 #include "log.h"
29 #include "v6808.h"
30 #include "timing.h"
31
32 //using namespace std;
33
34 //#define AUDIO_SAMPLE_RATE                             44100.0
35 #define AUDIO_SAMPLE_RATE                               48000.0
36 #define CYCLES_TO_EXECUTE                               (M6808_CLOCK_SPEED_IN_HZ / AUDIO_SAMPLE_RATE)
37
38 // Local variables
39
40 static SDL_AudioSpec desired;
41 static bool soundInitialized = false;
42 static uint32_t cyclesToExecuteWholePart;
43 static double cyclesToExecuteFractionalPart;
44
45 // Private function prototypes
46
47 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
48
49
50 //
51 // Initialize the SDL sound system
52 //
53 void SoundInit(void)
54 {
55 //      memory_malloc_secure((void **)&DACBuffer, BUFFER_SIZE * sizeof(uint16), "DAC buffer");
56
57         desired.freq = AUDIO_SAMPLE_RATE;                               // SDL will do conversion on the fly, if it can't get the exact rate. Nice!
58         desired.format = AUDIO_U8;                                              // This uses the native endian (for portability)...
59         desired.channels = 1;
60 //      desired.samples = 4096;                                                 // Let's try a 4K buffer (can always go lower)
61         desired.samples = 2048;                                                 // Let's try a 2K buffer (can always go lower)
62         desired.callback = SDLSoundCallback;
63
64         if (SDL_OpenAudio(&desired, NULL) < 0)                  // NULL means SDL guarantees what we want
65         {
66                 WriteLog("Sound: Failed to initialize SDL sound.\n");
67                 return;
68         }
69
70         // Setup clock cycles & etc.
71         cyclesToExecuteWholePart = (uint32_t)CYCLES_TO_EXECUTE;
72         cyclesToExecuteFractionalPart = CYCLES_TO_EXECUTE - (double)cyclesToExecuteWholePart;
73 #if 0
74 printf("Cycles to execute: %lf; cycles W: %u; cycles F: %lf\n", CYCLES_TO_EXECUTE, cyclesToExecuteWholePart, cyclesToExecuteFractionalPart);
75 #endif
76
77         SDL_PauseAudio(false);                                                  // Start playback!
78         soundInitialized = true;
79         WriteLog("Sound: Successfully initialized.\n");
80 }
81
82
83 //
84 // Close down the SDL sound subsystem
85 //
86 void SoundDone(void)
87 {
88         if (soundInitialized)
89         {
90                 SDL_PauseAudio(true);
91                 SDL_CloseAudio();
92                 WriteLog("Sound: Done.\n");
93         }
94 }
95
96
97 //
98 // Sound card callback handler
99 //
100 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
101 {
102         extern V6808REGS soundCPU;
103         extern uint8_t sram[];
104         int cnt = 0;
105
106         static float overflow = 0.0;
107         static uint32_t time = cyclesToExecuteWholePart;
108
109         while (cnt != length)
110         {
111                 Execute6808(&soundCPU, time);
112                 soundCPU.clock -= time;
113                 buffer[cnt++] = sram[0x0400];                           // Fill the buffer with the PIA output value
114                 time = cyclesToExecuteWholePart;
115                 overflow += cyclesToExecuteFractionalPart;
116
117                 if (overflow > 1.0)
118                 {
119                         overflow -= 1.0;
120                         time++;
121                 }
122         }
123 }
124