5 // (C) 2005 Underground Software
7 // JLH = James L. Hammons <jlhamm@acm.org>
10 // --- ---------- ------------------------------------------------------------
11 // JLH 12/02/2005 Fixed a problem with sound callback thread signaling the
13 // JLH 12/03/2005 Fixed sound callback dropping samples when the sample buffer
14 // is shorter than the callback sample buffer
19 // - Figure out why it's losing samples (Bard's Tale) [DONE]
24 #include <string.h> // For memset, memcpy
29 #define AMPLITUDE (32) // -32 - +32 seems to be plenty loud!
36 static SDL_AudioSpec desired;
37 static bool soundInitialized = false;
38 static bool speakerState;
39 static uint8 soundBuffer[4096];
40 static uint32 soundBufferPos;
41 static uint32 sampleBase;
42 static SDL_cond * conditional = NULL;
43 static SDL_mutex * mutex = NULL;
45 // Private function prototypes
47 static void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
50 // Initialize the SDL sound system
54 // To weed out problems for now...
59 desired.freq = 44100; // SDL will do conversion on the fly, if it can't get the exact rate. Nice!
60 desired.format = AUDIO_S8; // This uses the native endian (for portability)...
61 // desired.format = AUDIO_S16SYS; // This uses the native endian (for portability)...
63 // desired.samples = 4096; // Let's try a 4K buffer (can always go lower)
64 // desired.samples = 2048; // Let's try a 2K buffer (can always go lower)
65 desired.samples = 1024; // Let's try a 1K buffer (can always go lower)
66 desired.callback = SDLSoundCallback;
68 if (SDL_OpenAudio(&desired, NULL) < 0) // NULL means SDL guarantees what we want
70 WriteLog("Sound: Failed to initialize SDL sound.\n");
74 conditional = SDL_CreateCond();
75 mutex = SDL_CreateMutex();
76 SDL_mutexP(mutex); // Must lock the mutex for the cond to work properly...
80 SDL_PauseAudio(false); // Start playback!
81 soundInitialized = true;
82 WriteLog("Sound: Successfully initialized.\n");
86 // Close down the SDL sound subsystem
94 SDL_DestroyCond(conditional);
95 SDL_DestroyMutex(mutex);
96 WriteLog("Sound: Done.\n");
101 // Sound card callback handler
103 static void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
105 // The sound buffer should only starve when starting which will cause it to
106 // lag behind the emulation at most by around 1 frame...
107 // (Actually, this should never happen since we fill the buffer beforehand.)
108 // (But, then again, if the sound hasn't been toggled for a while, then this
109 // makes perfect sense as the buffer won't have been filled at all!)
111 if (soundBufferPos < (uint32)length) // The sound buffer is starved...
113 //printf("Sound buffer starved!\n");
115 for(uint32 i=0; i<soundBufferPos; i++)
116 buffer[i] = soundBuffer[i];
118 // Fill buffer with last value
119 memset(buffer + soundBufferPos, (uint8)(speakerState ? AMPLITUDE : -AMPLITUDE), length - soundBufferPos);
120 soundBufferPos = 0; // Reset soundBufferPos to start of buffer...
121 sampleBase = 0; // & sampleBase...
122 //Ick. This should never happen!
123 SDL_CondSignal(conditional); // Wake up any threads waiting for the buffer to drain...
127 memcpy(buffer, soundBuffer, length); // Fill sound buffer with frame buffered sound
128 soundBufferPos -= length;
129 sampleBase -= length;
131 // if (soundBufferPos > 0)
132 // memcpy(soundBuffer, soundBuffer + length, soundBufferPos); // Move current buffer down to start
133 // memcpy(soundBuffer, soundBuffer + length, length);
134 // Move current buffer down to start
135 for(uint32 i=0; i<soundBufferPos; i++)
136 soundBuffer[i] = soundBuffer[length + i];
138 // lastValue = buffer[length - 1];
139 SDL_CondSignal(conditional); // Wake up any threads waiting for the buffer to drain...
142 // Need some interface functions here to take care of flipping the
143 // waveform at the correct time in the sound stream...
146 Maybe set up a buffer 1 frame long (44100 / 60 = 735 bytes per frame)
148 Hmm. That's smaller than the sound buffer 2048 bytes... (About 2.75 frames needed to fill)
150 So... I guess what we could do is this:
152 -- Execute V65C02 for one frame. The read/writes at I/O address $C030 fill up the buffer
153 to the current time position.
154 -- The sound callback function copies the pertinent area out of the buffer, resets
155 the time position back (or copies data down from what it took out)
158 void ToggleSpeaker(uint32 time)
160 if (!soundInitialized)
164 if (time > 95085)//(time & 0x80000000)
166 WriteLog("ToggleSpeaker() given bad time value: %08X (%u)!\n", time, time);
171 // 1.024 MHz / 60 = 17066.6... cycles (23.2199 cycles per sample)
172 // Need the last frame position in order to calculate correctly...
176 uint32 currentPos = sampleBase + (uint32)((double)time / 23.2199);
178 if (currentPos > 4095)
181 WriteLog("ToggleSpeaker() about to go into spinlock at time: %08X (%u) (sampleBase=%u)!\n", time, time, sampleBase);
183 // Still hanging on this spinlock...
184 // That could be because the "time" value is too high and so the buffer will NEVER be
186 // Now that we're using a conditional, it seems to be working OK--though not perfectly...
188 ToggleSpeaker() about to go into spinlock at time: 00004011 (16401) (sampleBase=3504)!
189 16401 -> 706 samples, 3504 + 706 = 4210
191 And it still thrashed the sound even though it didn't run into a spinlock...
193 Seems like it's OK now that I've fixed the buffer-less-than-length bug...
196 SDL_CondWait(conditional, mutex);
198 currentPos = sampleBase + (uint32)((double)time / 23.2199);
200 WriteLog("--> after spinlock (sampleBase=%u)...\n", sampleBase);
204 int8 sample = (speakerState ? AMPLITUDE : -AMPLITUDE);
206 while (soundBufferPos < currentPos)
207 soundBuffer[soundBufferPos++] = (uint8)sample;
209 speakerState = !speakerState;
213 void HandleSoundAtFrameEdge(void)
215 if (!soundInitialized)