git @ Cat's Eye Technologies linapple / 0fd9646
Added modifier keys to F2 reset to prevent accidental reboots when switching disks (F3); Improved audio significantly: eliminated DC output of speaker data (no click when hitting F1 or F3/F4 anymore!); significantly improved Mockingboard sound quality in games that use 6522 IRQs; improved buffer streaming. greg 7 years ago
8 changed file(s) with 126 addition(s) and 66 deletion(s). Raw diff Collapse all Expand all
Binary diff not shown
6262 //
6363 ///////////////////////////////////////////////////////////
6464
65 #define MAX_OUTPUT 0x7fff
65 // GPH Changed from 0x7fff: we want some headroom to add in the Apple speaker
66 // in the mixing phase and can avoid an unnecessary divide with the
67 // associated problems by simply picking smaller values.
68 // Note also this number can theoretically go as high as 0x3fff without
69 // causing post-mix clipping, but even that sounds too loud...
70 #define MAX_OUTPUT 0x0fff
6671
6772 // See AY8910_set_clock() for definition of STEP
6873 #define STEP 0x8000
123123 //static signed long g_uInternalExecutedCycles;
124124 // TODO: Use IRQ_CHECK_TIMEOUT=128 when running at full-speed else with IRQ_CHECK_TIMEOUT=1
125125 // - What about when running benchmark?
126 static const int IRQ_CHECK_TIMEOUT = 128;
126 // GPH Dropped to IRQ_CHECK_TIMOUT=16 - Mockingboard-intensive applications sound
127 // "jerky" at 128. Does not show appreciable CPU impact in top CPU profiler.
128 static const int IRQ_CHECK_TIMEOUT = 16;
127129 static signed int g_nIrqCheckTimeout = IRQ_CHECK_TIMEOUT;
128130
129131 //
550550 char *
551551 md5str (const char *input)
552552 {
553 static char result[16 * 3 +1];
553 char result[16 * 3 +1];
554554 unsigned char* digest = (unsigned char*)md5 (input);
555555 int i;
556556
391391 0, 256);
392392 // Uint32 myblack = SDL_MapRGB(screen->format, 0, 0, 0); // black color
393393 // SDL_SetColorKey(g_hStatusSurface,SDL_SRCCOLORKEY/* | SDL_RLEACCEL*/, myblack);
394
394
395395 if (drawflags & DRAW_BACKGROUND)
396396 {
397397 /* Code moved to Video.cpp in CreateDIBSections()
493493 "Conf file is linapple.conf in current directory by default",
494494 "Hugest archive of Apple][ stuff you can find at ftp.apple.asimov.net",
495495 " F1 - This help",
496 " F2 - Cold reset, Shift+F2 - Reload conf file and restart",
496 " Ctrl+Shift+F2 - Cold reset",
497 " Shift+F2 - Reload conf file and restart",
497498 " F3, F4 - Choose an image file name for floppy disk",
498499 " in Slot 6 drive 1 or 2 respectively",
499500 " Shift+F3, Shift+F4 - The same thing for Apple hard disks",
942943 break;
943944
944945 case BTN_RUN: // F2 - Run that thing! Or Shift+2 ReloadConfig and run it anyway!
945 if(mod & KMOD_SHIFT) {
946 if((mod & (KMOD_LCTRL|KMOD_LSHIFT)) == (KMOD_LCTRL|KMOD_LSHIFT) ||
947 (mod & (KMOD_RCTRL|KMOD_RSHIFT)) == (KMOD_RCTRL|KMOD_RSHIFT)) {
948 if (g_nAppMode == MODE_LOGO)
949 DiskBoot();
950 else if (g_nAppMode == MODE_RUNNING)
951 ResetMachineState();
952 if ((g_nAppMode == MODE_DEBUG) || (g_nAppMode == MODE_STEPPING))
953 DebugEnd();
954 g_nAppMode = MODE_RUNNING;
955 DrawStatusArea(/*(HDC)0,*/DRAW_TITLE);
956 VideoRedrawScreen();
957 g_bResetTiming = true;
958 }
959 else if(mod & KMOD_SHIFT) {
946960 restart = 1; // keep up flag of restarting
947961 qe.type = SDL_QUIT;
948962 SDL_PushEvent(&qe);// push quit event
949 }
950 else {
951 if (g_nAppMode == MODE_LOGO)
952 DiskBoot();
953 else if (g_nAppMode == MODE_RUNNING)
954 ResetMachineState();
955 if ((g_nAppMode == MODE_DEBUG) || (g_nAppMode == MODE_STEPPING))
956 DebugEnd();
957 g_nAppMode = MODE_RUNNING;
958 DrawStatusArea(/*(HDC)0,*/DRAW_TITLE);
959 VideoRedrawScreen();
960 g_bResetTiming = true;
961 }
963 }
962964 break;
963965
964966 case BTN_DRIVE1:
182182
183183 static const DWORD g_dwDSBufferSize = 16 * 1024 * sizeof(short) * g_nMB_NumChannels;
184184
185 static const SHORT nWaveDataMin = (SHORT)0x8000;
186 static const SHORT nWaveDataMax = (SHORT)0x7FFF;
185 static const SHORT nWaveDataMin = (SHORT)0xF000;
186 static const SHORT nWaveDataMax = (SHORT)0x0FFF;
187187
188188 static short g_nMixBuffer[g_dwDSBufferSize / sizeof(short)];
189189
130130 //std::cerr << "DEBUG wanted: " << wantedSamples
131131 // << " actual: " << audioSpec.size / 4 << std::endl;
132132 frequency = audioSpec.freq;
133 bufferSize = 4 * (audioSpec.size / sizeof(short));
133 bufferSize = 8 * (audioSpec.size / sizeof(short));
134134 // bufferSize = SPKR_SAMPLE_RATE * 2 * sizeof(short); // 1 second of stereo short data
135135
136136 /*
249249 //////////////////////////////////////////////////////////////////////
250250 //////////////////////////////////////////////////////////////////////
251251
252
253 // GPH this is called on IRQ to refresh the audio at regular intervals.
254 // We'll mix the buffers here keeping in mind the need for speed.
255 //
252256 void audioCallback(short* stream, unsigned len)
253257 {
254258 int i;
255259 static short lastvalue = 0;
256
257260 assert((len & 1) == 0); // stereo
261
258262 unsigned len1, len2;
259263 unsigned available = getBufferFilled();
260
261 //std::cerr << "DEBUG callback: " << available << std::endl;
262264 unsigned num = std::min(len, available);
263265 if ((readIdx + num) < bufferSize) {
266 // No split in source (mixBuffer); perform straight-up copy.
267 //
268 // |--------------------|
269 // ^ (mixBuffer) $
270 //
271 // |--------------------|
272 // ^ (stream)
264273 memcpy(stream, &mixBuffer[readIdx], num * sizeof(short));
265274 readIdx += num;
266275 } else {
276 // Handle split in source
277 //
278 // |--------------------|
279 // (mixBuffer) ^
280 //
281 // |--------------------|
282 // ^ (stream)
267283 len1 = bufferSize - readIdx;
268284 memcpy(stream, &mixBuffer[readIdx], len1 * sizeof(short));
269285 len2 = num - len1;
271287 readIdx = len2;
272288 }
273289
290 // Fill the remainer of the buffer with last value to prevent potential
291 // clicks and pops.
274292 if (available != 0) {
275293 if (readIdx != 0)
276294 lastvalue = mixBuffer[readIdx-1];
279297 }
280298
281299 for (i = num; i < len; i++) {
282 stream[i] = lastvalue;
300 stream[i] = lastvalue;
283301 }
284
285 /* Note: cleared at the begininng of the func
286
287 int missing = len - available;
288 if (missing > 0) {
289 // buffer underrun
290 //std::cerr << "DEBUG underrun: " << missing << std::endl;
291 memset(&stream[available], 0, missing * sizeof(short));
292 }
293 */
302 /* GPH please don't do this in an IRQ handler!
294303 unsigned target = (5 * bufferSize) / 8;
295304 double factor = double(available) / target;
296305 filledStat = (63 * filledStat + factor) / 64;
297 //std::cerr << "DEBUG filledStat: " << filledStat << std::endl;
306 */
307
298308 #ifdef MOCKINGBOARD
299 // And add Mockingboard sound data to the stream
309 // And add Mockingboard sound data to the stream
310 // GPH: We are going to add the Mockingboard and speaker samples.
311 // Their independent maximum amplitudes have been selected to eliminate
312 // any possibility of wave peak clipping. This speeds up the timing-sensitive
313 // operation here (since we're in an IRQ handler) and eliminates the
314 // need for a potentially expensive divide.
300315 available = getBuffer2Filled();
301316 //std::cerr << "DEBUG callback: " << available << std::endl;
302317 num = std::min(len, available);
318 const short *pSrc;
319 short *pDest = stream;
303320 if ((readIdx2 + num) < bufferSize) {
304 // memcpy(stream, &mixBuffer[readIdx], num * sizeof(short));
305 for(i = 0; i < num; i++)
306 stream[i] |= mockBuffer[readIdx2 + i];
307 readIdx2 += num;
321 if( num ) {
322 pSrc = &mockBuffer[readIdx2];
323 readIdx2 += num;
324 while(num--) {
325 *pDest += *pSrc;
326 pDest++; pSrc++;
327 }
328 }
308329 } else {
309 len1 = bufferSize - readIdx2;
310 // memcpy(stream, &mixBuffer[readIdx], len1 * sizeof(short));
311 for(i = 0; i < len1; i++)
312 stream[i] |= mockBuffer[readIdx2 + i];
330 // We crossed the "seam" on the circular mockBuffer.
331 // We will therefore perform two copies, the segmentation being determined
332 // by the split in the source buffer (mockBuffer).
333 //
334 // |--------------------|
335 // (mockBuffer) ^
336 //
337 //
338 // |--------------------|
339 // ^ (stream)
340 len1 = bufferSize - readIdx2;
313341 len2 = num - len1;
314 // memcpy(&stream[len1], mixBuffer, len2 * sizeof(short));
315 for(i = 0; i < len2; i++)
316 stream[len1 + i] |= mockBuffer[i];
342 if(len1) {
343 pSrc = &mockBuffer[readIdx2];
344 while(len1--) {
345 *pDest += *pSrc;
346 pDest++; pSrc++;
347 }
348 }
317349 readIdx2 = len2;
318 }
319 #endif
350 if(len2) {
351 pSrc = mockBuffer;
352 while(len2--) {
353 *pDest += *pSrc;
354 pDest++; pSrc++;
355 }
356 }
357
358 }
359 #endif // #ifdef MOCKINGBOARD
320360 // normalization
361 // GPH TODO: Rather than do this in this handler, we should
362 // perform it efficiently by changing the values of
363 // MAX_OUTPUT (AY8910.cpp) and SPKR_DATA_INIT (Speaker.cpp),
364 // making them variables and making the appropriate access functions
365 // to call from Frame.cpp
321366 /* const short MY_MAX_VOLUME = (short)SDL_MIX_MAXVOLUME / 2;
322367 for(unsigned k=0;k<len;k+=2)
323368 if((short)stream[k] > MY_MAX_VOLUME)
4949 //-------------------------------------
5050
5151 // Globals (SOUND_WAVE)
52 const short SPKR_DATA_INIT = (short)0x8000; // data written to speakers buffer
53
54 static short g_nSpeakerData = SPKR_DATA_INIT;
52 // GPH This "INIT" value is the actual sample data written to the speaker when
53 // it gets whapped with a BIT $C030. Note the output is always digital.
54 // The Apple II had no notion of volume or of looping waves, only the single
55 // "click" of the speaker. We set this to a value well under the maximum to
56 // provide headroom for mixing Mockingboard sound without causing peak clipping.
57 const short SPKR_DATA_INIT = (short)0x2000; // data written to speakers buffer
58
59 static short g_nSpeakerData = 0x0000; //SPKR_DATA_INIT;
5560 static short *g_pSpeakerBuffer = NULL;
56 static UINT g_nBufferIdx = 0;
61 static UINT g_nBufferIdx = 0;
5762
5863 static short *g_pStereoBuffer = NULL; // buffer for stereo samples
5964
60
6165 static short *g_pRemainderBuffer = NULL; // Remainder buffer
62 static UINT g_nRemainderBufferSize; // Setup in SpkrInitialize()
63 static UINT g_nRemainderBufferIdx; // Setup in SpkrInitialize()
64
66 static UINT g_nRemainderBufferSize; // Setup in SpkrInitialize()
67 static UINT g_nRemainderBufferIdx; // Setup in SpkrInitialize()
6568
6669 // Application-wide globals:
67 DWORD soundtype = SOUND_WAVE; //default
70 DWORD soundtype = SOUND_WAVE; //default
6871 double g_fClksPerSpkrSample; // Setup in SetClksPerSpkrSample()
6972
7073 // Globals
300303 }
301304
302305 g_nSpkrLastCycle = g_nCumulativeCycles;
303 }
304
305 //=============================================================================
306
307 // Called by emulation code when Speaker I/O reg is accessed
306
307 // GPH Added - simulate decoupling capacitor - use approximate value
308 g_nSpeakerData = (short)((double) g_nSpeakerData * 0.995);
309 }
310
311 //=============================================================================
312
313 // Called by emulation code when Speaker I/O reg (0xC030) is accessed
308314 BYTE SpkrToggle (WORD, WORD, BYTE, BYTE, ULONG nCyclesLeft)
309315 {
310316 g_bSpkrToggleFlag = true;
326332
327333 UpdateSpkr();
328334
329 g_nSpeakerData = ~g_nSpeakerData;
335 g_nSpeakerData = g_nSpeakerData ^ SPKR_DATA_INIT;
330336 }
331337
332338 return MemReadFloatingBus(nCyclesLeft); // reading from $C030..$C03F retrurns unpredictable value?