]> Shamusworld >> Repos - legend/blob - src/sound.cpp
Initial commit for the Legend of A... project!
[legend] / src / sound.cpp
1 //
2 // Sound Interface
3 //
4 // by James Hammons
5 // (C) 2014 Underground Software
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  12/02/2005  Fixed a problem with sound callback thread signaling the
12 //                  main thread
13 // JLH  12/03/2005  Fixed sound callback dropping samples when the sample buffer
14 //                  is shorter than the callback sample buffer
15 //
16
17 // STILL TO DO:
18 //
19 //
20
21 #include "sound.h"
22
23 #include <string.h>                                                             // For memset, memcpy
24 #include <SDL2/SDL.h>
25 #include "log.h"
26
27 // Useful defines
28
29 //#define DEBUG
30 //#define WRITE_OUT_WAVE
31
32 //#define SAMPLE_RATE                   (44100.0)
33 #define SAMPLE_RATE                     (48000.0)
34 #define SAMPLES_PER_FRAME       (SAMPLE_RATE / 60.0)
35 #define CYCLES_PER_SAMPLE       (1024000.0 / SAMPLE_RATE)
36 //#define SOUND_BUFFER_SIZE     (8192)
37 #define SOUND_BUFFER_SIZE       (32768)
38
39 // Global variables
40
41
42 // Local variables
43
44 static SDL_AudioSpec desired, obtained;
45 static SDL_AudioDeviceID device;
46 static bool soundInitialized = false;
47 static bool speakerState = false;
48 static int16_t soundBuffer[SOUND_BUFFER_SIZE];
49 static uint32_t soundBufferPos;
50 static uint64_t lastToggleCycles;
51 static SDL_cond * conditional = NULL;
52 static SDL_mutex * mutex = NULL;
53 static SDL_mutex * mutex2 = NULL;
54 static int16_t sample;
55 static uint8_t ampPtr = 12;                                             // Start with -2047 - +2047
56 static int16_t amplitude[17] = { 0, 1, 2, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047,
57         4095, 8191, 16383, 32767 };
58 #ifdef WRITE_OUT_WAVE
59 static FILE * fp = NULL;
60 #endif
61
62 // Private function prototypes
63
64 static void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
65
66
67 //
68 // Initialize the SDL sound system
69 //
70 void SoundInit(void)
71 {
72 #if 0
73 // To weed out problems for now...
74 return;
75 #endif
76         SDL_zero(desired);
77         desired.freq = SAMPLE_RATE;                                     // SDL will do conversion on the fly, if it can't get the exact rate. Nice!
78         desired.format = AUDIO_S16SYS;                          // This uses the native endian (for portability)...
79         desired.channels = 1;
80         desired.samples = 512;                                          // Let's try a 1/2K buffer (can always go lower)
81         desired.callback = SDLSoundCallback;
82
83         device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0);
84
85         if (device == 0)
86         {
87                 WriteLog("Sound: Failed to initialize SDL sound.\n");
88                 return;
89         }
90
91         conditional = SDL_CreateCond();
92         mutex = SDL_CreateMutex();
93         mutex2 = SDL_CreateMutex();// Let's try real signalling...
94         soundBufferPos = 0;
95         lastToggleCycles = 0;
96         sample = desired.silence;       // ? wilwok ? yes
97
98         SDL_PauseAudioDevice(device, 0);                        // Start playback!
99         soundInitialized = true;
100         WriteLog("Sound: Successfully initialized.\n");
101
102 #ifdef WRITE_OUT_WAVE
103         fp = fopen("./apple2.wav", "wb");
104 #endif
105 }
106
107
108 //
109 // Close down the SDL sound subsystem
110 //
111 void SoundDone(void)
112 {
113         if (soundInitialized)
114         {
115                 SDL_PauseAudioDevice(device, 1);
116                 SDL_CloseAudioDevice(device);
117                 SDL_DestroyCond(conditional);
118                 SDL_DestroyMutex(mutex);
119                 SDL_DestroyMutex(mutex2);
120                 WriteLog("Sound: Done.\n");
121
122 #ifdef WRITE_OUT_WAVE
123                 fclose(fp);
124 #endif
125         }
126 }
127
128
129 void SoundPause(void)
130 {
131         if (soundInitialized)
132                 SDL_PauseAudioDevice(device, 1);
133 }
134
135
136 void SoundResume(void)
137 {
138         if (soundInitialized)
139                 SDL_PauseAudioDevice(device, 0);
140 }
141
142
143 //
144 // Sound card callback handler
145 //
146 static void SDLSoundCallback(void * /*userdata*/, Uint8 * buffer8, int length8)
147 {
148 //WriteLog("SDLSoundCallback(): begin (soundBufferPos=%i)\n", soundBufferPos);
149         // The sound buffer should only starve when starting which will cause it to
150         // lag behind the emulation at most by around 1 frame...
151         // (Actually, this should never happen since we fill the buffer beforehand.)
152         // (But, then again, if the sound hasn't been toggled for a while, then this
153         //  makes perfect sense as the buffer won't have been filled at all!)
154         // (Should NOT starve now, now that we properly handle frame edges...)
155
156         // Let's try using a mutex for shared resource consumption...
157 //Actually, I think Lock/UnlockAudio() does this already...
158 //WriteLog("SDLSoundCallback: soundBufferPos = %i\n", soundBufferPos);
159         SDL_mutexP(mutex2);
160
161         // Recast this as a 16-bit type...
162         int16_t * buffer = (int16_t *)buffer8;
163         uint32_t length = (uint32_t)length8 / 2;
164
165 //WriteLog("SDLSoundCallback(): filling buffer...\n");
166         if (soundBufferPos < length)
167         {
168                 // The sound buffer is starved...
169                 for(uint32_t i=0; i<soundBufferPos; i++)
170                         buffer[i] = soundBuffer[i];
171
172                 // Fill buffer with last value
173                 for(uint32_t i=soundBufferPos; i<length; i++)
174                         buffer[i] = sample;
175
176                 // Reset soundBufferPos to start of buffer...
177                 soundBufferPos = 0;
178         }
179         else
180         {
181                 // Fill sound buffer with frame buffered sound
182                 for(uint32_t i=0; i<length; i++)
183                         buffer[i] = soundBuffer[i];
184
185                 soundBufferPos -= length;
186
187                 // Move current buffer down to start
188                 for(uint32_t i=0; i<soundBufferPos; i++)
189                         soundBuffer[i] = soundBuffer[length + i];
190         }
191
192         // Free the mutex...
193 //WriteLog("SDLSoundCallback(): SDL_mutexV(mutex2)\n");
194         SDL_mutexV(mutex2);
195         // Wake up any threads waiting for the buffer to drain...
196         SDL_CondSignal(conditional);
197 //WriteLog("SDLSoundCallback(): end\n");
198 }
199
200
201 // This is called by the main CPU thread every ~21.333 cycles.
202 void WriteSampleToBuffer(void)
203 {
204 //WriteLog("WriteSampleToBuffer(): SDL_mutexP(mutex2)\n");
205         SDL_mutexP(mutex2);
206
207         // This should almost never happen, but, if it does...
208         while (soundBufferPos >= (SOUND_BUFFER_SIZE - 1))
209         {
210 //WriteLog("WriteSampleToBuffer(): Waiting for sound thread. soundBufferPos=%i, SOUNDBUFFERSIZE-1=%i\n", soundBufferPos, SOUND_BUFFER_SIZE-1);
211                 SDL_mutexV(mutex2);     // Release it so sound thread can get it,
212                 SDL_mutexP(mutex);      // Must lock the mutex for the cond to work properly...
213                 SDL_CondWait(conditional, mutex);       // Sleep/wait for the sound thread
214                 SDL_mutexV(mutex);      // Must unlock the mutex for the cond to work properly...
215                 SDL_mutexP(mutex2);     // Re-lock it until we're done with it...
216         }
217
218         soundBuffer[soundBufferPos++] = sample;
219 //WriteLog("WriteSampleToBuffer(): SDL_mutexV(mutex2)\n");
220         SDL_mutexV(mutex2);
221 }
222
223
224 void ToggleSpeaker(void)
225 {
226         if (!soundInitialized)
227                 return;
228
229         speakerState = !speakerState;
230         sample = (speakerState ? amplitude[ampPtr] : -amplitude[ampPtr]);
231 }
232
233
234 void VolumeUp(void)
235 {
236         // Currently set for 16-bit samples
237         if (ampPtr < 16)
238                 ampPtr++;
239 }
240
241
242 void VolumeDown(void)
243 {
244         if (ampPtr > 0)
245                 ampPtr--;
246 }
247
248
249 uint8_t GetVolume(void)
250 {
251         return ampPtr;
252 }
253