Added VBL, fixed sound-on-write, added .bin disk support.
This commit is contained in:
parent
c0001155bc
commit
aec4f863c3
25
apple2.cfg
25
apple2.cfg
|
@ -21,15 +21,23 @@ autoSaveState = 1
|
|||
#floppyImage1 = ./disks/temp.nib
|
||||
#floppyImage1 = ./disks/temp.dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/Gumball (Mr. Krac-Man and The Disk Jockey crack).dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/prince_of_persia_boot.dsk
|
||||
#floppyImage2 = ./disks/prince_of_persia_a.dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/Oregon Trail (Disk 1 of 2).dsk
|
||||
#floppyImage2 = ./disks/Oregon Trail (Disk 2 of 2).dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/bt1_boot.dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/bt2_boot.dsk
|
||||
# Yes (but segfaults in the timer routine in the title screen--NB: Not anymore...)
|
||||
floppyImage1 = ./disks/bt3_boot_fixed.dsk
|
||||
floppyImage2 = ./disks/bt3_character_fixed.dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/bt3_boot_fixed.dsk
|
||||
#floppyImage2 = ./disks/bt3_character_fixed.dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/Sabotage.dsk
|
||||
# ??? (//c or //e w/128K required) (dumps to monitor)
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/airheart.dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/drol.dsk
|
||||
|
@ -37,10 +45,11 @@ floppyImage2 = ./disks/bt3_character_fixed.dsk
|
|||
#floppyImage1 = ./disks/karateka.dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/wolfenstein_dos32.nib
|
||||
# Yes, keys???
|
||||
#floppyImage1 = ./disks/MidnightMagic_etc.dsk
|
||||
# ??? Loads, then dumps to monitor (This is IIe or IIc only)
|
||||
# Yes, keys??? (joystick only)
|
||||
floppyImage1 = ./disks/MidnightMagic_etc.dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/battle_chess_1.dsk
|
||||
#floppyImage2 = ./disks/battle_chess_2.dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/MoebiusI-1.dsk
|
||||
#floppyImage2 = ./disks/MoebiusI-2.dsk
|
||||
|
@ -76,6 +85,8 @@ floppyImage2 = ./disks/bt3_character_fixed.dsk
|
|||
#floppyImage1 = ./disks/lode_runner.dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/championship_lode_runner.dsk
|
||||
# Yes
|
||||
#floppyImage1 = ./disks/championship_lode_runner.bin
|
||||
|
||||
|
||||
# OpenGL filtering type: 1 - blurry, 0 - sharp
|
||||
|
|
|
@ -198,6 +198,9 @@ WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
|
|||
|
||||
Execute65C02(&mainCPU, cycles);
|
||||
WriteSampleToBuffer();
|
||||
|
||||
// Dunno if this is correct (seems to be close enough)...
|
||||
vbl = (i < 670 ? true : false);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -421,24 +424,7 @@ WriteLog("80COL (read)\n");
|
|||
}
|
||||
else if ((addr & 0xFFF0) == 0xC030) // Read $C030-$C03F
|
||||
{
|
||||
/*
|
||||
This is problematic, mainly because the v65C02 removes actual cycles run after each call.
|
||||
Therefore, we don't really have a reliable method of sending a timestamp to the sound routine.
|
||||
How to fix?
|
||||
|
||||
What we need to send is a delta T value but related to the IRQ buffer routine. E.g., if the buffer
|
||||
hasn't had any changes in it then we just fill it with the last sample value and are done. Then
|
||||
we need to adjust our delta T accordingly. What we could do is keep a running total of time since the
|
||||
last change and adjust it accordingly, i.e., whenever a sound IRQ happens.
|
||||
How to keep track?
|
||||
|
||||
Have deltaT somewhere. Then, whenever there's a toggle, backfill buffer with last spkr state and reset
|
||||
deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer size from deltaT. (?)
|
||||
|
||||
|
||||
|
||||
*/
|
||||
ToggleSpeaker(GetCurrentV65C02Clock());
|
||||
ToggleSpeaker();
|
||||
//should it return something else here???
|
||||
return 0x00;
|
||||
}
|
||||
|
@ -1040,6 +1026,13 @@ WriteLog("ALTCHARSET on (write)\n");
|
|||
//But leaving this out seems to fuck up the key handling of some games...
|
||||
keyDown = false;
|
||||
}
|
||||
else if ((addr & 0xFFF0) == 0xC030) // Read $C030-$C03F
|
||||
{
|
||||
//Likewise, the speaker is supposed to do nothing if you write to it, and
|
||||
//for the same reason. But without this, you get no sound in David's
|
||||
//Midnight Magic...
|
||||
ToggleSpeaker();
|
||||
}
|
||||
else if (addr == 0xC050)
|
||||
{
|
||||
#ifdef SOFT_SWITCH_DEBUGGING
|
||||
|
|
|
@ -39,6 +39,7 @@ uint8_t FloppyDrive::poSector[16] = {
|
|||
0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
|
||||
char FloppyDrive::nameBuf[MAX_PATH];
|
||||
|
||||
|
||||
// FloppyDrive class implementation...
|
||||
|
||||
FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), phase(0), track(0)
|
||||
|
@ -51,6 +52,7 @@ FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), ph
|
|||
imageName[0][0] = imageName[1][0] = 0; // Zero out filenames
|
||||
}
|
||||
|
||||
|
||||
FloppyDrive::~FloppyDrive()
|
||||
{
|
||||
if (disk[0])
|
||||
|
@ -60,6 +62,7 @@ FloppyDrive::~FloppyDrive()
|
|||
delete[] disk[1];
|
||||
}
|
||||
|
||||
|
||||
bool FloppyDrive::LoadImage(const char * filename, uint8_t driveNum/*= 0*/)
|
||||
{
|
||||
WriteLog("FLOPPY: Attempting to load image '%s' in drive #%u.\n", filename, driveNum);
|
||||
|
@ -112,6 +115,7 @@ bool FloppyDrive::LoadImage(const char * filename, uint8_t driveNum/*= 0*/)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/)
|
||||
{
|
||||
if (driveNum > 1)
|
||||
|
@ -153,6 +157,7 @@ bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/)
|
||||
{
|
||||
//WARNING: Buffer overflow possibility
|
||||
|
@ -161,6 +166,7 @@ bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/)
|
|||
return SaveImage(driveNum);
|
||||
}
|
||||
|
||||
|
||||
void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/)
|
||||
{
|
||||
if (disk[driveNum] != NULL)
|
||||
|
@ -176,6 +182,7 @@ void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/)
|
|||
SpawnMessage("New blank image inserted in drive %u...", driveNum);
|
||||
}
|
||||
|
||||
|
||||
void FloppyDrive::SwapImages(void)
|
||||
{
|
||||
uint8_t nybblizedImageTmp[232960];
|
||||
|
@ -211,6 +218,7 @@ void FloppyDrive::SwapImages(void)
|
|||
SpawnMessage("Drive 0: %s...", imageName[0]);
|
||||
}
|
||||
|
||||
|
||||
void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum)
|
||||
{
|
||||
diskType[driveNum] = DT_UNKNOWN;
|
||||
|
@ -254,10 +262,11 @@ WRT to the disk image itself.
|
|||
}
|
||||
|
||||
// Actually, it just might matter WRT to nybblyzing/denybblyzing
|
||||
// Here, we check for BT3
|
||||
//Nope, no change...
|
||||
//diskType[driveNum] = DT_PRODOS;
|
||||
|
||||
NybblizeImage(driveNum);
|
||||
}
|
||||
else if (diskSize[driveNum] == 143488)
|
||||
{
|
||||
diskType[driveNum] = DT_DOS33_HDR;
|
||||
NybblizeImage(driveNum);
|
||||
}
|
||||
|
||||
|
@ -265,9 +274,11 @@ WRT to the disk image itself.
|
|||
|
||||
WriteLog("FLOPPY: Detected image type %s...\n", (diskType[driveNum] == DT_NYBBLE ?
|
||||
"Nybble image" : (diskType[driveNum] == DT_DOS33 ?
|
||||
"DOS 3.3 image" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : "unknown"))));
|
||||
"DOS 3.3 image" : (diskType[driveNum] == DT_DOS33_HDR ?
|
||||
"DOS 3.3 image (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : "unknown")))));
|
||||
}
|
||||
|
||||
|
||||
void FloppyDrive::NybblizeImage(uint8_t driveNum)
|
||||
{
|
||||
// Format of a sector is header (23) + nybbles (343) + footer (30) = 396
|
||||
|
@ -319,6 +330,8 @@ void FloppyDrive::NybblizeImage(uint8_t driveNum)
|
|||
|
||||
if (diskType[driveNum] == DT_DOS33)
|
||||
bytes += (doSector[sector] * 256) + (trk * 256 * 16);
|
||||
else if (diskType[driveNum] == DT_DOS33_HDR)
|
||||
bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
|
||||
else if (diskType[driveNum] == DT_PRODOS)
|
||||
bytes += (poSector[sector] * 256) + (trk * 256 * 16);
|
||||
else
|
||||
|
@ -371,6 +384,7 @@ WriteLog("FL: i = %u, img[i] = %02X, diskbyte = %02X\n", i, img[i], diskbyte[img
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void FloppyDrive::DenybblizeImage(uint8_t driveNum)
|
||||
{
|
||||
uint8_t decodeNybble[0x80] = {
|
||||
|
@ -462,6 +476,8 @@ void FloppyDrive::DenybblizeImage(uint8_t driveNum)
|
|||
|
||||
if (diskType[driveNum] == DT_DOS33)
|
||||
bytes += (doSector[sector] * 256) + (trk * 256 * 16);
|
||||
else if (diskType[driveNum] == DT_DOS33_HDR)
|
||||
bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
|
||||
else if (diskType[driveNum] == DT_PRODOS)
|
||||
bytes += (poSector[sector] * 256) + (trk * 256 * 16);
|
||||
else
|
||||
|
@ -472,6 +488,7 @@ void FloppyDrive::DenybblizeImage(uint8_t driveNum)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
const char * FloppyDrive::GetImageName(uint8_t driveNum/*= 0*/)
|
||||
{
|
||||
// Set up a zero-length string for return value
|
||||
|
@ -508,6 +525,7 @@ const char * FloppyDrive::GetImageName(uint8_t driveNum/*= 0*/)
|
|||
return nameBuf;
|
||||
}
|
||||
|
||||
|
||||
void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
|
||||
{
|
||||
// Probably want to save a dirty image... ;-)
|
||||
|
@ -527,6 +545,7 @@ void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
|
|||
memset(nybblizedImage[driveNum], 0xFF, 232960); // Doesn't matter if 00s or FFs...
|
||||
}
|
||||
|
||||
|
||||
bool FloppyDrive::DriveIsEmpty(uint8_t driveNum/*= 0*/)
|
||||
{
|
||||
if (driveNum > 1)
|
||||
|
@ -539,6 +558,7 @@ bool FloppyDrive::DriveIsEmpty(uint8_t driveNum/*= 0*/)
|
|||
return (imageName[driveNum][0] == 0 ? true : false);
|
||||
}
|
||||
|
||||
|
||||
bool FloppyDrive::DiskIsWriteProtected(uint8_t driveNum/*= 0*/)
|
||||
{
|
||||
if (driveNum > 1)
|
||||
|
@ -550,6 +570,7 @@ bool FloppyDrive::DiskIsWriteProtected(uint8_t driveNum/*= 0*/)
|
|||
return writeProtected[driveNum];
|
||||
}
|
||||
|
||||
|
||||
void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/)
|
||||
{
|
||||
if (driveNum > 1)
|
||||
|
@ -606,18 +627,21 @@ SpawnMessage("Stepping to track %u...", track);
|
|||
// return something if read mode...
|
||||
}
|
||||
|
||||
|
||||
void FloppyDrive::ControlMotor(uint8_t addr)
|
||||
{
|
||||
// $C0E8 - 9
|
||||
motorOn = addr;
|
||||
}
|
||||
|
||||
|
||||
void FloppyDrive::DriveEnable(uint8_t addr)
|
||||
{
|
||||
// $C0EA - B
|
||||
activeDrive = addr;
|
||||
}
|
||||
|
||||
|
||||
uint8_t FloppyDrive::ReadWrite(void)
|
||||
{
|
||||
SpawnMessage("%u:%sing %s track %u, sector %u...", activeDrive,
|
||||
|
@ -651,24 +675,28 @@ Which we now do. :-)
|
|||
return diskByte;
|
||||
}
|
||||
|
||||
|
||||
uint8_t FloppyDrive::GetLatchValue(void)
|
||||
{
|
||||
// $C0ED
|
||||
return latchValue;
|
||||
}
|
||||
|
||||
|
||||
void FloppyDrive::SetLatchValue(uint8_t value)
|
||||
{
|
||||
// $C0ED
|
||||
latchValue = value;
|
||||
}
|
||||
|
||||
|
||||
void FloppyDrive::SetReadMode(void)
|
||||
{
|
||||
// $C0EE
|
||||
ioMode = IO_MODE_READ;
|
||||
}
|
||||
|
||||
|
||||
void FloppyDrive::SetWriteMode(void)
|
||||
{
|
||||
// $C0EF
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#endif
|
||||
#include <stdint.h>
|
||||
|
||||
enum { DT_UNKNOWN, DT_DOS33, DT_PRODOS, DT_NYBBLE };
|
||||
enum { DT_UNKNOWN, DT_DOS33, DT_DOS33_HDR, DT_PRODOS, DT_NYBBLE };
|
||||
|
||||
class FloppyDrive
|
||||
{
|
||||
|
|
187
src/sound.cpp
187
src/sound.cpp
|
@ -17,7 +17,7 @@
|
|||
// STILL TO DO:
|
||||
//
|
||||
// - Figure out why it's losing samples (Bard's Tale) [DONE]
|
||||
// - Figure out why it's playing too fast
|
||||
// - Figure out why it's playing too fast [DONE]
|
||||
//
|
||||
|
||||
#include "sound.h"
|
||||
|
@ -31,27 +31,10 @@
|
|||
//#define DEBUG
|
||||
//#define WRITE_OUT_WAVE
|
||||
|
||||
// This is odd--seems to be working properly now! Maybe a bug in the SDL sound code?
|
||||
// Actually, it still doesn't sound right... Sounds too slow now. :-/
|
||||
// But then again, it's difficult to tell. Sometimes it slows waaaaaay down, but generally
|
||||
// seems to be OK other than that
|
||||
// Also, it could be that the discrepancy in pitch is due to the V65C02 and it's lack of
|
||||
// cycle accuracy...
|
||||
|
||||
//#define SAMPLE_RATE (44100.0)
|
||||
#define SAMPLE_RATE (48000.0)
|
||||
#define SAMPLES_PER_FRAME (SAMPLE_RATE / 60.0)
|
||||
// This works for AppleWin but not here... ??? WHY ???
|
||||
// ~ 21
|
||||
#define CYCLES_PER_SAMPLE (1024000.0 / SAMPLE_RATE)
|
||||
// ~ 17 (lower pitched than above...!)
|
||||
// Makes sense, as this is the divisor for # of cycles passed
|
||||
//#define CYCLES_PER_SAMPLE (800000.0 / SAMPLE_RATE)
|
||||
// This seems about right, compared to AppleWin--but AW runs @ 1.024 MHz
|
||||
// 23 (1.024) vs. 20 (0.900)
|
||||
//#define CYCLES_PER_SAMPLE (900000.0 / SAMPLE_RATE)
|
||||
//nope, too high #define CYCLES_PER_SAMPLE (960000.0 / SAMPLE_RATE)
|
||||
//#define CYCLES_PER_SAMPLE 21
|
||||
//#define SOUND_BUFFER_SIZE (8192)
|
||||
#define SOUND_BUFFER_SIZE (32768)
|
||||
|
||||
|
@ -131,9 +114,7 @@ void SoundDone(void)
|
|||
{
|
||||
if (soundInitialized)
|
||||
{
|
||||
// SDL_PauseAudio(true);
|
||||
SDL_PauseAudioDevice(device, 1);
|
||||
// SDL_CloseAudio();
|
||||
SDL_CloseAudioDevice(device);
|
||||
SDL_DestroyCond(conditional);
|
||||
SDL_DestroyMutex(mutex);
|
||||
|
@ -184,22 +165,22 @@ static void SDLSoundCallback(void * /*userdata*/, Uint8 * buffer8, int length8)
|
|||
uint32_t length = (uint32_t)length8 / 2;
|
||||
|
||||
//WriteLog("SDLSoundCallback(): filling buffer...\n");
|
||||
if (soundBufferPos < length) // The sound buffer is starved...
|
||||
if (soundBufferPos < length)
|
||||
{
|
||||
// The sound buffer is starved...
|
||||
for(uint32_t i=0; i<soundBufferPos; i++)
|
||||
buffer[i] = soundBuffer[i];
|
||||
|
||||
// Fill buffer with last value
|
||||
// memset(buffer + soundBufferPos, (uint8_t)sample, length - soundBufferPos);
|
||||
for(uint32_t i=soundBufferPos; i<length; i++)
|
||||
buffer[i] = sample;
|
||||
|
||||
soundBufferPos = 0; // Reset soundBufferPos to start of buffer...
|
||||
// Reset soundBufferPos to start of buffer...
|
||||
soundBufferPos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fill sound buffer with frame buffered sound
|
||||
// memcpy(buffer, soundBuffer, length);
|
||||
for(uint32_t i=0; i<length; i++)
|
||||
buffer[i] = soundBuffer[i];
|
||||
|
||||
|
@ -225,7 +206,7 @@ void WriteSampleToBuffer(void)
|
|||
//WriteLog("WriteSampleToBuffer(): SDL_mutexP(mutex2)\n");
|
||||
SDL_mutexP(mutex2);
|
||||
|
||||
// This should almost never happen, but...
|
||||
// This should almost never happen, but, if it does...
|
||||
while (soundBufferPos >= (SOUND_BUFFER_SIZE - 1))
|
||||
{
|
||||
//WriteLog("WriteSampleToBuffer(): Waiting for sound thread. soundBufferPos=%i, SOUNDBUFFERSIZE-1=%i\n", soundBufferPos, SOUND_BUFFER_SIZE-1);
|
||||
|
@ -242,107 +223,16 @@ void WriteSampleToBuffer(void)
|
|||
}
|
||||
|
||||
|
||||
// Need some interface functions here to take care of flipping the
|
||||
// waveform at the correct time in the sound stream...
|
||||
|
||||
/*
|
||||
Maybe set up a buffer 1 frame long (44100 / 60 = 735 bytes per frame)
|
||||
|
||||
Hmm. That's smaller than the sound buffer 2048 bytes... (About 2.75 frames needed to fill)
|
||||
|
||||
So... I guess what we could do is this:
|
||||
|
||||
-- Execute V65C02 for one frame. The read/writes at I/O address $C030 fill up the buffer
|
||||
to the current time position.
|
||||
-- The sound callback function copies the pertinent area out of the buffer, resets
|
||||
the time position back (or copies data down from what it took out)
|
||||
*/
|
||||
|
||||
void HandleBuffer(uint64_t elapsedCycles)
|
||||
{
|
||||
// Step 1: Calculate delta time
|
||||
uint64_t deltaCycles = elapsedCycles - lastToggleCycles;
|
||||
|
||||
// Step 2: Calculate new buffer position
|
||||
uint32_t currentPos = (uint32_t)((double)deltaCycles / CYCLES_PER_SAMPLE);
|
||||
|
||||
// Step 3: Make sure there's room for it
|
||||
// We need to lock since we touch both soundBuffer and soundBufferPos
|
||||
SDL_mutexP(mutex2);
|
||||
|
||||
while ((soundBufferPos + currentPos) > (SOUND_BUFFER_SIZE - 1))
|
||||
{
|
||||
SDL_mutexV(mutex2); // Release it so sound thread can get it,
|
||||
SDL_mutexP(mutex); // Must lock the mutex for the cond to work properly...
|
||||
SDL_CondWait(conditional, mutex); // Sleep/wait for the sound thread
|
||||
SDL_mutexV(mutex); // Must unlock the mutex for the cond to work properly...
|
||||
SDL_mutexP(mutex2); // Re-lock it until we're done with it...
|
||||
}
|
||||
|
||||
// Step 4: Backfill and adjust lastToggleCycles
|
||||
// currentPos is position from "zero" or soundBufferPos...
|
||||
currentPos += soundBufferPos;
|
||||
|
||||
#ifdef WRITE_OUT_WAVE
|
||||
uint32_t sbpSave = soundBufferPos;
|
||||
#endif
|
||||
// Backfill with current toggle state
|
||||
while (soundBufferPos < currentPos)
|
||||
soundBuffer[soundBufferPos++] = sample;
|
||||
|
||||
#ifdef WRITE_OUT_WAVE
|
||||
fwrite(&soundBuffer[sbpSave], sizeof(int16_t), currentPos - sbpSave, fp);
|
||||
#endif
|
||||
|
||||
SDL_mutexV(mutex2);
|
||||
lastToggleCycles = elapsedCycles;
|
||||
}
|
||||
|
||||
|
||||
void ToggleSpeaker(uint64_t elapsedCycles)
|
||||
void ToggleSpeaker(void)
|
||||
{
|
||||
if (!soundInitialized)
|
||||
return;
|
||||
|
||||
// HandleBuffer(elapsedCycles);
|
||||
speakerState = !speakerState;
|
||||
sample = (speakerState ? amplitude[ampPtr] : -amplitude[ampPtr]);
|
||||
}
|
||||
|
||||
|
||||
void AdjustLastToggleCycles(uint64_t elapsedCycles)
|
||||
{
|
||||
if (!soundInitialized)
|
||||
return;
|
||||
/*
|
||||
BOOKKEEPING
|
||||
|
||||
We need to know the following:
|
||||
|
||||
o Where in the sound buffer the base or "zero" time is
|
||||
o At what CPU timestamp the speaker was last toggled
|
||||
NOTE: we keep things "right" by advancing this number every frame, even
|
||||
if nothing happened! That way, we can keep track without having
|
||||
to detect whether or not several frames have gone by without any
|
||||
activity.
|
||||
|
||||
How to do it:
|
||||
|
||||
Every time the speaker is toggled, we move the base or "zero" time to the
|
||||
current spot in the buffer. We also backfill the buffer up to that point with
|
||||
the old toggle value. The next time the speaker is toggled, we measure the
|
||||
difference in time between the last time it was toggled (the "zero") and now,
|
||||
and repeat the cycle.
|
||||
|
||||
We handle dead spots by backfilling the buffer with the current toggle value
|
||||
every frame--this way we don't have to worry about keeping current time and
|
||||
crap like that. So, we have to move the "zero" the right amount, just like
|
||||
in ToggleSpeaker(), and backfill only without toggling.
|
||||
*/
|
||||
HandleBuffer(elapsedCycles);
|
||||
}
|
||||
|
||||
|
||||
void VolumeUp(void)
|
||||
{
|
||||
// Currently set for 16-bit samples
|
||||
|
@ -363,66 +253,3 @@ uint8_t GetVolume(void)
|
|||
return ampPtr;
|
||||
}
|
||||
|
||||
/*
|
||||
HOW IT WORKS
|
||||
|
||||
the main thread adds the amount of cpu time elapsed to samplebase. togglespeaker uses
|
||||
samplebase + current cpu time to find appropriate spot in buffer. it then fills the
|
||||
buffer up to the current time with the old toggle value before flipping it. the sound
|
||||
irq takes what it needs from the sound buffer and then adjusts both the buffer and
|
||||
samplebase back the appropriate amount.
|
||||
|
||||
|
||||
A better way might be as follows:
|
||||
|
||||
Keep timestamp array of speaker toggle times. In the sound routine, unpack as many as will
|
||||
fit into the given buffer and keep going. Have the toggle function check to see if the
|
||||
buffer is full, and if it is, way for a signal from the interrupt that there's room for
|
||||
more. Can keep a circular buffer. Also, would need a timestamp buffer on the order of 2096
|
||||
samples *in theory* could toggle each sample
|
||||
|
||||
Instead of a timestamp, just keep a delta. That way, don't need to deal with wrapping and
|
||||
all that (though the timestamp could wrap--need to check into that)
|
||||
|
||||
Need to consider corner cases where a sound IRQ happens but no speaker toggle happened.
|
||||
|
||||
If (delta > SAMPLES_PER_FRAME) then
|
||||
|
||||
Here's the relevant cases:
|
||||
|
||||
delta < SAMPLES_PER_FRAME -> Change happened within this time frame, so change buffer
|
||||
frame came and went, no change -> fill buffer with last value
|
||||
How to detect: Have bool bufferWasTouched = true when ToggleSpeaker() is called.
|
||||
Clear bufferWasTouched each frame.
|
||||
|
||||
Two major cases here:
|
||||
|
||||
o Buffer is touched on current frame
|
||||
o Buffer is untouched on current frame
|
||||
|
||||
In the first case, it doesn't matter too much if the previous frame was touched or not,
|
||||
we don't really care except in finding the correct spot in the buffer to put our change
|
||||
in. In the second case, we need to tell the IRQ that nothing happened and to continue
|
||||
to output the same value.
|
||||
|
||||
SO: How to synchronize the regular frame buffer with the IRQ buffer?
|
||||
|
||||
What happens:
|
||||
Sound IRQ --> Every 1024 sample period (@ 44.1 KHz = 0.0232s)
|
||||
Emulation --> Render a frame --> 1/60 sec --> 735 samples
|
||||
--> sound buffer is filled
|
||||
|
||||
Since the emulation is faster than the SIRQ the sound buffer should fill up
|
||||
prior to dumping it to the sound card.
|
||||
|
||||
Problem is this: If silence happens for a long time then ToggleSpeaker is never
|
||||
called and the sound buffer has stale data; at least until soundBufferPos goes to
|
||||
zero and stays there...
|
||||
|
||||
BUT this should be handled correctly by toggling the speaker value *after* filling
|
||||
the sound buffer...
|
||||
|
||||
Still getting random clicks when running...
|
||||
(This may be due to the lock/unlock sound happening in ToggleSpeaker()...)
|
||||
*/
|
||||
|
||||
|
|
|
@ -19,10 +19,8 @@ void SoundInit(void);
|
|||
void SoundDone(void);
|
||||
void SoundPause(void);
|
||||
void SoundResume(void);
|
||||
void ToggleSpeaker(uint64_t elapsedCycles);
|
||||
void ToggleSpeaker(void);
|
||||
void WriteSampleToBuffer(void);
|
||||
//void AddToSoundTimeBase(uint64_t cycles);
|
||||
void AdjustLastToggleCycles(uint64_t elapsedCycles);
|
||||
void VolumeUp(void);
|
||||
void VolumeDown(void);
|
||||
uint8_t GetVolume(void);
|
||||
|
|
Loading…
Reference in New Issue