]> Shamusworld >> Repos - virtualjaguar/blob - src/dac.cpp
Fixes to handle mono sound properly
[virtualjaguar] / src / dac.cpp
1 //
2 // DAC (really, Synchronous Serial Interface) Handler
3 //
4 // Originally by David Raingeard
5 // GCC/SDL port by Niels Wagenaar (Linux/WIN32) and Caz (BeOS)
6 // Rewritten by James L. Hammons
7 //
8
9 // Need to set up defaults that the BIOS sets for the SSI here in DACInit()... !!! FIX !!!
10 // or something like that... Seems like it already does, but it doesn't seem to
11 // work correctly...! Perhaps just need to set up SSI stuff so BUTCH doesn't get
12 // confused...
13
14 #include "SDL.h"
15 #include "m68k.h"
16 #include "jaguar.h"
17 #include "settings.h"
18 #include "dac.h"
19
20 //#define DEBUG_DAC
21
22 #define BUFFER_SIZE             0x10000                                         // Make the DAC buffers 64K x 16 bits
23
24 // Jaguar memory locations
25
26 #define LTXD                    0xF1A148
27 #define RTXD                    0xF1A14C
28 #define LRXD                    0xF1A148
29 #define RRXD                    0xF1A14C
30 #define SCLK                    0xF1A150
31 #define SMODE                   0xF1A154
32
33 // Global variables
34
35 uint16 lrxd, rrxd;                                                                      // I2S ports (into Jaguar)
36
37 // Local variables
38
39 static uint32 LeftFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOHeadPtr, RightFIFOTailPtr;
40 static SDL_AudioSpec desired;
41 static bool SDLSoundInitialized = false;
42
43 // We can get away with using native endian here because we can tell SDL to use the native
44 // endian when looking at the sample buffer, i.e., no need to worry about it.
45
46 static uint16 * DACBuffer;
47 static uint8 SCLKFrequencyDivider = 19;                         // Default is roughly 22 KHz (20774 Hz in NTSC mode)
48 /*static*/ uint16 serialMode = 0;
49
50 // Private function prototypes
51
52 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length);
53 int GetCalculatedFrequency(void);
54
55 //
56 // Initialize the SDL sound system
57 //
58 void DACInit(void)
59 {
60         memory_malloc_secure((void **)&DACBuffer, BUFFER_SIZE * sizeof(uint16), "DAC buffer");
61
62         desired.freq = GetCalculatedFrequency();                // SDL will do conversion on the fly, if it can't get the exact rate. Nice!
63         desired.format = AUDIO_S16SYS;                                  // This uses the native endian (for portability)...
64         desired.channels = 2;
65 //      desired.samples = 4096;                                                 // Let's try a 4K buffer (can always go lower)
66         desired.samples = 2048;                                                 // Let's try a 2K buffer (can always go lower)
67         desired.callback = SDLSoundCallback;
68
69         if (SDL_OpenAudio(&desired, NULL) < 0)                  // NULL means SDL guarantees what we want
70                 WriteLog("DAC: Failed to initialize SDL sound...\n");
71         else
72         {
73                 SDLSoundInitialized = true;
74                 DACReset();
75                 SDL_PauseAudio(false);                                                  // Start playback!
76                 WriteLog("DAC: Successfully initialized.\n");
77         }
78 }
79
80 //
81 // Reset the sound buffer FIFOs
82 //
83 void DACReset(void)
84 {
85         LeftFIFOHeadPtr = LeftFIFOTailPtr = 0, RightFIFOHeadPtr = RightFIFOTailPtr = 1;
86 }
87
88 //
89 // Close down the SDL sound subsystem
90 //
91 void DACDone(void)
92 {
93         if (SDLSoundInitialized)
94         {
95                 SDL_PauseAudio(true);
96                 SDL_CloseAudio();
97         }
98
99         memory_free(DACBuffer);
100         WriteLog("DAC: Done.\n");
101 }
102
103 //
104 // SDL callback routine to fill audio buffer
105 //
106 // Note: The samples are packed in the buffer in 16 bit left/16 bit right pairs.
107 //
108 void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
109 {
110         // Clear the buffer to silence, in case the DAC buffer is empty (or short)
111 //This causes choppy sound... Ick.
112         memset(buffer, desired.silence, length);
113 //WriteLog("DAC: Inside callback...\n");
114         if (LeftFIFOHeadPtr != LeftFIFOTailPtr)
115         {
116 //WriteLog("DAC: About to write some data!\n");
117                 int numLeftSamplesReady
118                         = (LeftFIFOTailPtr + (LeftFIFOTailPtr < LeftFIFOHeadPtr ? BUFFER_SIZE : 0))
119                                 - LeftFIFOHeadPtr;
120                 int numRightSamplesReady
121                         = (RightFIFOTailPtr + (RightFIFOTailPtr < RightFIFOHeadPtr ? BUFFER_SIZE : 0))
122                                 - RightFIFOHeadPtr;
123 //This waits for the slower side to catch up. If writing only one side, then this
124 //causes the buffer not to drain...
125                 int numSamplesReady
126                         = (numLeftSamplesReady < numRightSamplesReady
127                                 ? numLeftSamplesReady : numRightSamplesReady);//Hmm. * 2;
128
129 //Kludge, until I can figure out WTF is going on WRT Super Burnout.
130 if (numLeftSamplesReady == 0 || numRightSamplesReady == 0)
131         numSamplesReady = numLeftSamplesReady + numRightSamplesReady;
132
133 //The numbers look good--it's just that the DSP can't get enough samples in the DAC buffer!
134 //WriteLog("DAC: Left/RightFIFOHeadPtr: %u/%u, Left/RightFIFOTailPtr: %u/%u\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
135 //WriteLog("     numLeft/RightSamplesReady: %i/%i, numSamplesReady: %i, length of buffer: %i\n", numLeftSamplesReady, numRightSamplesReady, numSamplesReady, length);
136
137 /*              if (numSamplesReady > length)
138                         numSamplesReady = length;//*/
139                 if (numSamplesReady > length / 2)       // length / 2 because we're comparing 16-bit lengths
140                         numSamplesReady = length / 2;
141 //else
142 //      WriteLog("     Not enough samples to fill the buffer (short by %u L/R samples)...\n", (length / 2) - numSamplesReady);
143 //WriteLog("DAC: %u samples ready.\n", numSamplesReady);
144
145                 // Actually, it's a bit more involved than this, but this is the general idea:
146 //              memcpy(buffer, DACBuffer, length);
147                 for(int i=0; i<numSamplesReady; i++)
148                         ((uint16 *)buffer)[i] = DACBuffer[(LeftFIFOHeadPtr + i) % BUFFER_SIZE];
149                         // Could also use (as long as BUFFER_SIZE is a multiple of 2):
150 //                      buffer[i] = DACBuffer[(LeftFIFOHeadPtr + i) & (BUFFER_SIZE - 1)];
151
152                 LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
153                 RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) % BUFFER_SIZE;
154                 // Could also use (as long as BUFFER_SIZE is a multiple of 2):
155 //              LeftFIFOHeadPtr = (LeftFIFOHeadPtr + numSamplesReady) & (BUFFER_SIZE - 1);
156 //              RightFIFOHeadPtr = (RightFIFOHeadPtr + numSamplesReady) & (BUFFER_SIZE - 1);
157 //WriteLog("  -> Left/RightFIFOHeadPtr: %04X/%04X, Left/RightFIFOTailPtr: %04X/%04X\n", LeftFIFOHeadPtr, RightFIFOHeadPtr, LeftFIFOTailPtr, RightFIFOTailPtr);
158         }
159 //Hmm. Seems that the SDL buffer isn't being starved by the DAC buffer...
160 //      else
161 //              WriteLog("DAC: Silence...!\n");
162 }
163
164 //
165 // Calculate the frequency of SCLK * 32 using the divider
166 //
167 int GetCalculatedFrequency(void)
168 {
169         int systemClockFrequency = (vjs.hardwareTypeNTSC ? RISC_CLOCK_RATE_NTSC : RISC_CLOCK_RATE_PAL);
170
171         // We divide by 32 here in order to find the frequency of 32 SCLKs in a row (transferring
172         // 16 bits of left data + 16 bits of right data = 32 bits, 1 SCLK = 1 bit transferred).
173         return systemClockFrequency / (32 * (2 * (SCLKFrequencyDivider + 1)));
174 }
175
176 //
177 // LTXD/RTXD/SCLK/SMODE ($F1A148/4C/50/54)
178 //
179 void DACWriteByte(uint32 offset, uint8 data, uint32 who/*= UNKNOWN*/)
180 {
181         WriteLog("DAC: %s writing BYTE %02X at %08X\n", whoName[who], data, offset);
182         if (offset == SCLK + 3)
183                 DACWriteWord(offset - 3, (uint16)data);
184 }
185
186 void DACWriteWord(uint32 offset, uint16 data, uint32 who/*= UNKNOWN*/)
187 {
188         if (offset == LTXD + 2)
189         {
190                 // Spin until buffer has been drained (for too fast processors!)...
191 //Small problem--if Head == 0 and Tail == buffer end, then this will fail... !!! FIX !!!
192 //[DONE]
193                 // Also, we're taking advantage of the fact that the buffer is a multiple of two
194                 // in this check...
195 uint32 spin = 0;
196                 while (((LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == LeftFIFOHeadPtr)//;
197                 {
198 spin++;
199 //if ((spin & 0x0FFFFFFF) == 0)
200 //      WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
201
202 if (spin == 0xFFFF0000)
203 {
204 uint32 ltail = LeftFIFOTailPtr, lhead = LeftFIFOHeadPtr;
205 WriteLog("Tail=%X, Head=%X", ltail, lhead);
206
207         WriteLog("\nStuck in left DAC spinlock! Aborting!\n");
208         WriteLog("LTail=%X, LHead=%X, BUFFER_SIZE-1=%X\n", LeftFIFOTailPtr, LeftFIFOHeadPtr, BUFFER_SIZE - 1);
209         WriteLog("RTail=%X, RHead=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
210         WriteLog("From while: Tail=%X, Head=%X", (LeftFIFOTailPtr + 2) & (BUFFER_SIZE - 1), LeftFIFOHeadPtr);
211         log_done();
212         exit(0);
213 }
214                 }//*/
215
216                 SDL_LockAudio();                                                        // Is it necessary to do this? Mebbe.
217                 // We use a circular buffer 'cause it's easy. Note that the callback function
218                 // takes care of dumping audio to the soundcard...! Also note that we're writing
219                 // the samples in the buffer in an interleaved L/R format.
220                 LeftFIFOTailPtr = (LeftFIFOTailPtr + 2) % BUFFER_SIZE;
221                 DACBuffer[LeftFIFOTailPtr] = data;
222                 SDL_UnlockAudio();
223         }
224         else if (offset == RTXD + 2)
225         {
226 /*
227 Here's what's happening now:
228
229 Stuck in right DAC spinlock!
230 Aborting!
231
232 Tail=681, Head=681, BUFFER_SIZE-1=FFFF
233 From while: Tail=683, Head=681
234
235 ????? What the FUCK ?????
236
237 & when I uncomment the lines below spin++; it *doesn't* lock here... WTF?????
238
239 I think it was missing parentheses causing the fuckup... Seems to work now...
240
241 Except for Super Burnout now...! Aarrrgggghhhhh!
242
243 Tail=AC, Head=AE
244 Stuck in left DAC spinlock! Aborting!
245 Tail=AC, Head=AE, BUFFER_SIZE-1=FFFF
246 From while: Tail=AE, Head=AE
247
248 So it's *really* stuck here in the left FIFO. Figure out why!!!
249
250 Prolly 'cause it doesn't set the sample rate right away--betcha it works with the BIOS...
251 It gets farther, but then locks here (weird!):
252
253 Tail=2564, Head=2566
254 Stuck in left DAC spinlock! Aborting!
255 Tail=2564, Head=2566, BUFFER_SIZE-1=FFFF
256 From while: Tail=2566, Head=2566
257
258 Weird--recompile with more WriteLog() entries and it *doesn't* lock...
259 Yeah, because there was no DSP running. Duh!
260
261 Tail=AC, Head=AE
262 Stuck in left DAC spinlock! Aborting!
263 LTail=AC, LHead=AE, BUFFER_SIZE-1=FFFF
264 RTail=AF, RHead=AF, BUFFER_SIZE-1=FFFF
265 From while: Tail=AE, Head=AE
266
267 Odd: The right FIFO is empty, but the left FIFO is full!
268 And this is what is causing the lockup--the DAC callback waits for the side with
269 less samples ready and in this case it's the right channel (that never fills up)
270 that it's waiting for...!
271
272 Okay, with the kludge in place for the right channel not being filled, we select
273 a track and then it locks here:
274
275 Tail=60D8, Head=60DA
276 Stuck in left DAC spinlock! Aborting!
277 LTail=60D8, LHead=60D8, BUFFER_SIZE-1=FFFF
278 RTail=DB, RHead=60D9, BUFFER_SIZE-1=FFFF
279 From while: Tail=60DA, Head=60D8
280 */
281                 // Spin until buffer has been drained (for too fast processors!)...
282 uint32 spin = 0;
283                 while (((RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1)) == RightFIFOHeadPtr)//;
284                 {
285 spin++;
286 //if ((spin & 0x0FFFFFFF) == 0)
287 //      WriteLog("Tail=%X, Head=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
288
289 if (spin == 0xFFFF0000)
290 {
291 uint32 rtail = RightFIFOTailPtr, rhead = RightFIFOHeadPtr;
292 WriteLog("Tail=%X, Head=%X", rtail, rhead);
293
294         WriteLog("\nStuck in right DAC spinlock! Aborting!\n");
295         WriteLog("LTail=%X, LHead=%X, BUFFER_SIZE-1=%X\n", LeftFIFOTailPtr, LeftFIFOHeadPtr, BUFFER_SIZE - 1);
296         WriteLog("RTail=%X, RHead=%X, BUFFER_SIZE-1=%X\n", RightFIFOTailPtr, RightFIFOHeadPtr, BUFFER_SIZE - 1);
297         WriteLog("From while: Tail=%X, Head=%X", (RightFIFOTailPtr + 2) & (BUFFER_SIZE - 1), RightFIFOHeadPtr);
298         log_done();
299         exit(0);
300 }
301                 }//*/
302
303                 SDL_LockAudio();
304                 RightFIFOTailPtr = (RightFIFOTailPtr + 2) % BUFFER_SIZE;
305                 DACBuffer[RightFIFOTailPtr] = data;
306                 SDL_UnlockAudio();
307 /*#ifdef DEBUG_DAC
308                 else
309                         WriteLog("DAC: Ran into FIFO's right tail pointer!\n");
310 #endif*/
311         }
312         else if (offset == SCLK + 2)                                    // Sample rate
313         {
314                 WriteLog("DAC: Writing %u to SCLK...\n", data);
315                 if ((uint8)data != SCLKFrequencyDivider)
316                 {
317                         SCLKFrequencyDivider = (uint8)data;
318 //Of course a better way would be to query the hardware to find the upper limit...
319                         if (data > 7)   // Anything less than 8 is too high!
320                         {
321                                 if (SDLSoundInitialized)
322                                         SDL_CloseAudio();
323
324                                 desired.freq = GetCalculatedFrequency();// SDL will do conversion on the fly, if it can't get the exact rate. Nice!
325                                 WriteLog("DAC: Changing sample rate to %u Hz!\n", desired.freq);
326
327                                 if (SDLSoundInitialized)
328                                 {
329                                         if (SDL_OpenAudio(&desired, NULL) < 0)  // NULL means SDL guarantees what we want
330                                         {
331                                                 WriteLog("DAC: Failed to initialize SDL sound: %s.\nDesired freq: %u\nShutting down!\n", SDL_GetError(), desired.freq);
332                                                 log_done();
333                                                 exit(1);
334                                         }
335                                 }
336
337                                 DACReset();
338
339                                 if (SDLSoundInitialized)
340                                         SDL_PauseAudio(false);                  // Start playback!
341                         }
342                 }
343         }
344         else if (offset == SMODE + 2)
345         {
346                 serialMode = data;
347                 WriteLog("DAC: %s writing to SMODE. Bits: %s%s%s%s%s%s [68K PC=%08X]\n", whoName[who],
348                         (data & 0x01 ? "INTERNAL " : ""), (data & 0x02 ? "MODE " : ""),
349                         (data & 0x04 ? "WSEN " : ""), (data & 0x08 ? "RISING " : ""),
350                         (data & 0x10 ? "FALLING " : ""), (data & 0x20 ? "EVERYWORD" : ""),
351                         m68k_get_reg(NULL, M68K_REG_PC));
352         }
353 }
354
355 //
356 // LRXD/RRXD/SSTAT ($F1A148/4C/50)
357 //
358 uint8 DACReadByte(uint32 offset, uint32 who/*= UNKNOWN*/)
359 {
360 //      WriteLog("DAC: %s reading byte from %08X\n", whoName[who], offset);
361         return 0xFF;
362 }
363
364 //static uint16 fakeWord = 0;
365 uint16 DACReadWord(uint32 offset, uint32 who/*= UNKNOWN*/)
366 {
367 //      WriteLog("DAC: %s reading word from %08X\n", whoName[who], offset);
368 //      return 0xFFFF;
369 //      WriteLog("DAC: %s reading WORD %04X from %08X\n", whoName[who], fakeWord, offset);
370 //      return fakeWord++;
371 //NOTE: This only works if a bunch of things are set in BUTCH which we currently don't
372 //      check for. !!! FIX !!!
373 // Partially fixed: We check for I2SCNTRL in the JERRY I2S routine...
374 //      return GetWordFromButchSSI(offset, who);
375         if (offset == LRXD || offset == RRXD)
376                 return 0x0000;
377         else if (offset == LRXD + 2)
378                 return lrxd;
379         else if (offset == RRXD + 2)
380                 return rrxd;
381
382         return 0xFFFF;  // May need SSTAT as well... (but may be a Jaguar II only feature)              
383 }