Skip to content

Commit

Permalink
Added sync-tone function
Browse files Browse the repository at this point in the history
  • Loading branch information
AidanHockey5 committed Jan 20, 2022
1 parent 84429d2 commit 1eeb521
Show file tree
Hide file tree
Showing 10 changed files with 424 additions and 8 deletions.
147 changes: 145 additions & 2 deletions lib/VGMEngine/SN76489.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ void SN76489::write(uint8_t data)
writeRaw(data);
}



void SN76489::writeRaw(uint8_t data)
{
//32 clock cycles is 280.11nS
Expand All @@ -53,4 +51,149 @@ void SN76489::writeRaw(uint8_t data)
sleepClocks(NTSC_COLORBURST, 1); //Safety delay so we don't slam into the second write cycle before the PSG is finished with the first
}

bool SN76489::UpdateNoiseControl()
{
byte noiseControlData;

switch (currentNote[noiseCh])
{
case 60:
// Note: C4, Periodic noise, shift rate = clock speed (Hz) / 512
noiseControlData = 0x00;
break;

case 62:
// Note: D4, Periodic noise, shift rate = clock speed (Hz) / 1024
noiseControlData = 0x01;
break;

case 64:
// Note: E4, Periodic noise, shift rate = clock speed (Hz) / 2048
noiseControlData = 0x02;
break;

case 65:
// Note: F4, Perioic noise, shift rate = Square voice 3 frequency
noiseControlData = 0x03;
break;

case 67:
// Note: G4, White noise, shift rate = clock speed (Hz) / 512
noiseControlData = 0x04;
break;

case 69:
// Note: A4, White noise, shift rate = clock speed (Hz) / 1024
noiseControlData = 0x05;
break;

case 71:
// Note: B4, White noise, shift rate = clock speed (Hz) / 2048
noiseControlData = 0x06;
break;

case 72:
// Note: C5, White noise, shift rate = Square voice 3 frequency
noiseControlData = 0x07;
break;

default:
return false;
}

// Send the noise control byte to the SN76489 and return true
writeRaw(0x80 | 0x60 | noiseControlData);
return true;
}

void SN76489::SetNoiseOn(uint8_t key)
{
currentNote[noiseCh] = key;
currentVelocity[noiseCh] = 127;
bool updateAttenuationFlag = UpdateNoiseControl();
if(updateAttenuationFlag)
UpdateAttenuation(noiseCh);
}

void SN76489::SetNoiseOff(uint8_t key)
{
currentNote[noiseCh] = key;
currentVelocity[noiseCh] = 0;
bool updateAttenuationFlag = UpdateNoiseControl();
if(updateAttenuationFlag)
UpdateAttenuation(noiseCh);
}

void SN76489::SetChannelOn(uint8_t key, uint8_t voice)
{
bool updateAttenuationFlag;
if(voice < MAX_CHANNELS_PSG)
{
currentNote[voice] = key;
currentVelocity[voice] = 127;
updateAttenuationFlag = UpdateSquarePitch(voice);
if (updateAttenuationFlag)
UpdateAttenuation(voice);
}
}

void SN76489::SetChannelOff(uint8_t key, uint8_t voice)
{
if (key != currentNote[voice])
return;
currentVelocity[voice] = 0;
UpdateAttenuation(voice);
}

bool SN76489::UpdateSquarePitch(uint8_t voice)
{
float pitchInHz;
unsigned int frequencyData;
if (voice < 0 || voice > 2)
return false;
pitchInHz = 440 * pow(2, (float(currentNote[voice] - 69) / 12)); //removed pitch bend
frequencyData = clkfrq / float(32 * pitchInHz);
if (frequencyData > 1023)
return false;
SetSquareFrequency(voice, frequencyData);
return true;
}

void SN76489::SetSquareFrequency(uint8_t voice, int frequencyData)
{
if (voice < 0 || voice > 2)
return;
writeRaw(0x80 | frequencyRegister[voice] | (frequencyData & 0x0f));
writeRaw(frequencyData >> 4);
}

void SN76489::UpdateAttenuation(uint8_t voice)
{
uint8_t attenuationValue;
if (voice < 0 || voice > 3)
return;
attenuationValue = (127 - currentVelocity[voice]) >> 3;
writeRaw(0x80 | attenuationRegister[voice] | attenuationValue);
}

void SN76489::TestToneOn(bool channelControl[4])
{
for(uint8_t i = 0; i < 3; i++)
{
if(channelControl[i])
SetChannelOn(60, i);
}
if(channelControl[3]) //noise channel
SetNoiseOn(60);
}

void SN76489::TestToneOff(bool channelControl[4])
{
for(uint8_t i = 0; i < 3; i++)
{
SetChannelOff(60, i);
}
SetNoiseOff(60);
}

SN76489::~SN76489(){}
16 changes: 16 additions & 0 deletions lib/VGMEngine/SN76489.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,27 @@ class SN76489
void reset();
void write(uint8_t data);
void setClock(uint32_t frq);
void SetChannelOn(uint8_t key, uint8_t voice);
void SetChannelOff(uint8_t key, uint8_t voice);
void SetNoiseOn(uint8_t key);
void SetNoiseOff(uint8_t key);
bool UpdateNoiseControl();
void UpdateAttenuation(uint8_t voice);
void SetSquareFrequency(uint8_t voice, int frequencyData);
void TestToneOn(bool channelControl[4]);
void TestToneOff(bool channelControl[4]);
bool UpdateSquarePitch(uint8_t voice);
private:
//ChipClock* clk;
uint32_t clkfrq = NTSC_COLORBURST;
Bus* bus;
uint8_t WE;
const uint8_t attenuationRegister[4] = {0x10, 0x30, 0x50, 0x70};
const uint8_t frequencyRegister[3] = {0x00, 0x20, 0x40};
uint8_t currentNote[4] = {0, 0, 0, 0};
uint8_t currentVelocity[4] = {0, 0, 0, 0};
const int noiseCh = 3;
const int MAX_CHANNELS_PSG = 3;
unsigned char psgFrqLowByte = 0;
void writeRaw(uint8_t data);
};
Expand Down
29 changes: 27 additions & 2 deletions lib/VGMEngine/VGMEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,36 @@ bool VGMEngineClass::begin(File *f)
dacSampleCountDown = 0;
MegaStream_Reset(&stream);
load();
if(isSyncToneEnabled)
{
playTestTones();
}
state = PLAYING;
ready = true;
return true;
}

void VGMEngineClass::playTestTones()
{
ym2612->SetVoice(testVoice);
for(int i = 0; i < 3; i++)
{
//TODO: SN76489
ym2612->TestToneOn(ym2612CHControl);
sn76489->TestToneOn(sn76489CHControl);
delay(250);
ym2612->TestToneOff();
sn76489->TestToneOff(sn76489CHControl);
delay(250);
}

delay(250);

sn76489->reset();
ym2612->reset();
}


void VGMEngineClass::resetDataBlocks()
{
memset(dataBlocks, 0, sizeof(dataBlocks));
Expand Down Expand Up @@ -297,7 +322,7 @@ VGMEngineState VGMEngineClass::play()
{
uint8_t data = ram.ReadByte(dacStreamBufPos++);
// check if channel 6 is enabled, since this is a DAC write
if (ym2612CHControl[0x06])
if (ym2612CHControl[0x05])
{
ym2612->write(0x2A, data, 0);
}
Expand Down Expand Up @@ -466,7 +491,7 @@ uint16_t VGMEngineClass::parseVGM()
{
uint8_t data = ram.ReadByte(pcmBufferPosition++);
// check if channel 6 is enabled, since this is a DAC write
if (ym2612CHControl[0x06])
if (ym2612CHControl[0x05])
{
ym2612->write(0x2A, data, 0);
}
Expand Down
2 changes: 2 additions & 0 deletions lib/VGMEngine/VGMEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class VGMEngineClass
bool sn76489CHControl[4] = {true, true, true, true};
//Keeps track of the currently latched channel so we can ignore writes if disabled
uint8_t sn76489Latched = 0;
bool isSyncToneEnabled = false; //Should a test tone at the start of each VGM be played? This is to help people sync individual audio tracks for things like oscilloscope views.
VGMEngineState state = IDLE;
bool resetISR = false;
bool isBusy = false;
Expand Down Expand Up @@ -111,6 +112,7 @@ class VGMEngineClass
void chipSetup();
bool storePCM(bool skip = false);
bool topUp();
void playTestTones();
uint16_t parseVGM();
};

Expand Down
25 changes: 25 additions & 0 deletions lib/VGMEngine/Voice.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef VOICE_H_
#define VOICE_H_

//OPM File Format https://vgmrips.net/wiki/OPM_File_Format
typedef struct
{
unsigned char LFO[5];
unsigned char CH[7];
unsigned char M1[11];
unsigned char C1[11];
unsigned char M2[11];
unsigned char C2[11];
} Voice;

static const Voice testVoice = (Voice)
{
{0, 0, 0, 0, 0}, //LFO
{64, 7, 2, 0, 0, 120, 0}, //CH
{26, 8, 5, 2, 2, 32, 3, 3, 3, 0, 0}, //M1
{29, 5, 4, 2, 1, 32, 3, 4, 3, 0, 0}, //C1
{28, 4, 2, 2, 3, 31, 3, 1, 3, 0, 0}, //M2
{31, 10, 3, 1, 1, 12, 3, 1, 3, 0, 0} //C2
};

#endif
Loading

0 comments on commit 1eeb521

Please sign in to comment.