From df6d86563085e467e3b326a4c9a0e18884343221 Mon Sep 17 00:00:00 2001 From: Robert Hargreaves Date: Thu, 25 Jul 2024 23:50:59 +0100 Subject: [PATCH] Move pitch bend logic into main MIDI module --- src/midi.c | 25 +++++++++++------- src/midi.h | 9 +++---- src/midi_dac.c | 8 ++---- src/midi_dac.h | 5 ++-- src/midi_fm.c | 25 +++++------------- src/midi_fm.h | 7 +++-- src/midi_fm_sm.c | 26 ++++++++---------- src/midi_fm_sm.h | 5 ++-- src/midi_psg.c | 54 +++++++++++++++----------------------- src/midi_psg.h | 5 ++-- tests/unit/test_midi_psg.c | 3 ++- 11 files changed, 72 insertions(+), 100 deletions(-) diff --git a/src/midi.c b/src/midi.c index 4762cf8..0e89fab 100644 --- a/src/midi.c +++ b/src/midi.c @@ -38,17 +38,17 @@ typedef struct MidiChannel { typedef enum MappingMode { MappingMode_Static, MappingMode_Dynamic, MappingMode_Auto } MappingMode; static const VTable PSG_VTable = { midi_psg_note_on, midi_psg_note_off, midi_psg_channel_volume, - midi_psg_pitch_bend, midi_psg_program, midi_psg_all_notes_off, midi_psg_pan, midi_psg_pitch }; + midi_psg_program, midi_psg_all_notes_off, midi_psg_pan, midi_psg_pitch }; static const VTable FM_VTable = { midi_fm_note_on, midi_fm_note_off, midi_fm_channel_volume, - midi_fm_pitch_bend, midi_fm_program, midi_fm_all_notes_off, midi_fm_pan, midi_fm_pitch }; + midi_fm_program, midi_fm_all_notes_off, midi_fm_pan, midi_fm_pitch }; static const VTable SpecialMode_VTable - = { midi_fm_sm_note_on, midi_fm_sm_note_off, midi_fm_sm_channel_volume, midi_fm_sm_pitch_bend, - midi_fm_sm_program, midi_fm_sm_all_notes_off, midi_fm_sm_pan, midi_fm_sm_pitch }; + = { midi_fm_sm_note_on, midi_fm_sm_note_off, midi_fm_sm_channel_volume, midi_fm_sm_program, + midi_fm_sm_all_notes_off, midi_fm_sm_pan, midi_fm_sm_pitch }; static const VTable DAC_VTable = { midi_dac_note_on, midi_dac_note_off, midi_dac_channel_volume, - midi_dac_pitch_bend, midi_dac_program, midi_dac_all_notes_off, midi_dac_pan, midi_dac_pitch }; + midi_dac_program, midi_dac_all_notes_off, midi_dac_pan, midi_dac_pitch }; static const u8** defaultEnvelopes; static const FmChannel** defaultPresets; @@ -333,7 +333,9 @@ static void updatePan(MidiChannel* midiChannel, DeviceChannel* devChan) static void updatePitchBend(MidiChannel* midiChannel, DeviceChannel* devChan) { if (devChan->pitchBend != midiChannel->pitchBend) { - devChan->ops->pitchBend(devChan->number, midiChannel->pitchBend); + PitchCents pc + = midi_effectivePitchCents(devChan->pitch, devChan->cents, midiChannel->pitchBend); + devChan->ops->pitch(devChan->number, pc.pitch, pc.cents); devChan->pitchBend = midiChannel->pitchBend; } } @@ -407,8 +409,12 @@ void midi_note_on(u8 chan, u8 pitch, u8 velocity) } devChan->midiChannel = chan; + devChan->noteOn = true; + devChan->cents = 0; + devChan->pitch = pitch; updateDeviceChannelFromAssociatedMidiChannel(devChan); - devChanNoteOn(devChan, pitch, velocity); + PitchCents pc = midi_effectivePitchCents(devChan->pitch, devChan->cents, devChan->pitchBend); + devChan->ops->noteOn(devChan->number, pc.pitch, pc.cents, velocity); } void midi_note_off(u8 chan, u8 pitch) @@ -438,7 +444,8 @@ static void devChanNoteOn(DeviceChannel* devChan, u8 pitch, u8 velocity) devChan->noteOn = true; devChan->cents = 0; devChan->pitch = pitch; - devChan->ops->noteOn(devChan->number, pitch, velocity); + PitchCents pc = midi_effectivePitchCents(devChan->pitch, devChan->cents, devChan->pitchBend); + devChan->ops->noteOn(devChan->number, pc.pitch, pc.cents, velocity); } static void devChanNoteOff(DeviceChannel* devChan, u8 pitch) @@ -959,7 +966,7 @@ void midi_tick(void) processPortamento(); } -PitchCents midi_effectivePitchCents(u8 pitch, u8 cents, u16 pitchBend) +PitchCents midi_effectivePitchCents(u8 pitch, s8 cents, u16 pitchBend) { s16 newPitch = pitch + ((pitchBend - MIDI_PITCH_BEND_CENTRE) / 0x1000); s16 newCents = cents + (((pitchBend - MIDI_PITCH_BEND_CENTRE) % 0x1000) / 41); diff --git a/src/midi.h b/src/midi.h index 20e36fc..3e46e9c 100644 --- a/src/midi.h +++ b/src/midi.h @@ -120,14 +120,13 @@ #define SYSEX_COMMAND_WRITE_YM2612_REG_PART_1 0x09 typedef struct VTable { - void (*noteOn)(u8 chan, u8 pitch, u8 velocity); + void (*noteOn)(u8 chan, u8 pitch, s8 cents, u8 velocity); void (*noteOff)(u8 chan, u8 pitch); void (*channelVolume)(u8 chan, u8 volume); - void (*pitchBend)(u8 chan, u16 bend); void (*program)(u8 chan, u8 program); void (*allNotesOff)(u8 chan); void (*pan)(u8 chan, u8 pan); - void (*pitch)(u8 chan, u8 pitch, u8 cents); + void (*pitch)(u8 chan, u8 pitch, s8 cents); } VTable; typedef struct DeviceChannel { @@ -146,7 +145,7 @@ typedef struct DeviceChannel { typedef struct PitchCents { u8 pitch; - u8 cents; + s8 cents; } PitchCents; void midi_init(const FmChannel** defaultPresets, const PercussionPreset** defaultPercussionPresets, @@ -162,4 +161,4 @@ DeviceChannel* midi_channel_mappings(void); void midi_remap_channel(u8 midiChannel, u8 deviceChannel); void midi_reset(void); void midi_tick(void); -PitchCents midi_effectivePitchCents(u8 pitch, u8 cents, u16 pitchBend); +PitchCents midi_effectivePitchCents(u8 pitch, s8 cents, u16 pitchBend); diff --git a/src/midi_dac.c b/src/midi_dac.c index 4d8a1d0..50b3645 100644 --- a/src/midi_dac.c +++ b/src/midi_dac.c @@ -3,16 +3,12 @@ #include "snd/sound.h" #include "snd/pcm/snd_pcm.h" -void midi_dac_note_on(u8 chan, u8 pitch, u8 velocity) +void midi_dac_note_on(u8 chan, u8 pitch, s8 cents, u8 velocity) { const PcmSample* sample = percussionPcmSample[0]; SND_PCM_startPlay(sample->data, sample->length, sample->rate, SOUND_PAN_CENTER, 0); } -void midi_dac_pitch_bend(u8 chan, u16 bend) -{ -} - void midi_dac_reset(void) { } @@ -37,6 +33,6 @@ void midi_dac_all_notes_off(u8 chan) { } -void midi_dac_pitch(u8 chan, u8 pitch, u8 cents) +void midi_dac_pitch(u8 chan, u8 pitch, s8 cents) { } diff --git a/src/midi_dac.h b/src/midi_dac.h index 0b25d47..16d1c66 100644 --- a/src/midi_dac.h +++ b/src/midi_dac.h @@ -1,12 +1,11 @@ #pragma once #include "types.h" -void midi_dac_note_on(u8 chan, u8 pitch, u8 velocity); -void midi_dac_pitch_bend(u8 chan, u16 bend); +void midi_dac_note_on(u8 chan, u8 pitch, s8 cents, u8 velocity); void midi_dac_reset(void); void midi_dac_note_off(u8 chan, u8 pitch); void midi_dac_channel_volume(u8 chan, u8 volume); void midi_dac_pan(u8 chan, u8 pan); void midi_dac_program(u8 chan, u8 program); void midi_dac_all_notes_off(u8 chan); -void midi_dac_pitch(u8 chan, u8 pitch, u8 cents); +void midi_dac_pitch(u8 chan, u8 pitch, s8 cents); diff --git a/src/midi_fm.c b/src/midi_fm.c index 782e0b6..0d831ea 100644 --- a/src/midi_fm.c +++ b/src/midi_fm.c @@ -11,8 +11,7 @@ static const u16 FREQS[] = { 607, // B typedef struct MidiFmChannel { u8 pitch; - u8 cents; - u16 pitchBend; + s8 cents; u8 volume; u8 velocity; u8 pan; @@ -46,13 +45,12 @@ void midi_fm_reset(void) fmChan->velocity = MAX_MIDI_VOLUME; fmChan->pan = 0; fmChan->percussive = false; - fmChan->pitchBend = DEFAULT_MIDI_PITCH_BEND; fmChan->cents = 0; } synth_init(presets[0]); } -u16 midi_fm_pitchCentsToFreqNum(u8 pitch, u8 cents) +u16 midi_fm_pitchCentsToFreqNum(u8 pitch, s8 cents) { u16 freq = lookupFreqNum(pitch, 0); u16 nextFreq = lookupFreqNum(pitch, 1); @@ -62,12 +60,11 @@ u16 midi_fm_pitchCentsToFreqNum(u8 pitch, u8 cents) static void setSynthPitch(u8 chan) { MidiFmChannel* fmChan = &fmChannels[chan]; - PitchCents pc = midi_effectivePitchCents(fmChan->pitch, fmChan->cents, fmChan->pitchBend); - synth_pitch( - chan, midi_fm_pitchToOctave(pc.pitch), midi_fm_pitchCentsToFreqNum(pc.pitch, pc.cents)); + synth_pitch(chan, midi_fm_pitchToOctave(fmChan->pitch), + midi_fm_pitchCentsToFreqNum(fmChan->pitch, fmChan->cents)); } -void midi_fm_note_on(u8 chan, u8 pitch, u8 velocity) +void midi_fm_note_on(u8 chan, u8 pitch, s8 cents, u8 velocity) { if (pitchIsOutOfRange(pitch)) { return; @@ -81,7 +78,7 @@ void midi_fm_note_on(u8 chan, u8 pitch, u8 velocity) fmChan->velocity = velocity; synth_volume(chan, effectiveVolume(fmChan)); fmChan->pitch = pitch; - fmChan->cents = 0; + fmChan->cents = cents; setSynthPitch(chan); synth_noteOn(chan); @@ -100,14 +97,6 @@ void midi_fm_channel_volume(u8 chan, u8 volume) synth_volume(chan, effectiveVolume(fmChan)); } -void midi_fm_pitch_bend(u8 chan, u16 bend) -{ - MidiFmChannel* fmChan = &fmChannels[chan]; - fmChan->pitchBend = bend; - - setSynthPitch(chan); -} - void midi_fm_program(u8 chan, u8 program) { const FmChannel* data = presets[program]; @@ -164,7 +153,7 @@ static u8 effectiveVolume(MidiFmChannel* fmChan) return (fmChan->volume * fmChan->velocity) / 0x7F; } -void midi_fm_pitch(u8 chan, u8 pitch, u8 cents) +void midi_fm_pitch(u8 chan, u8 pitch, s8 cents) { MidiFmChannel* fmChan = &fmChannels[chan]; fmChan->pitch = pitch; diff --git a/src/midi_fm.h b/src/midi_fm.h index ba11586..d7aba93 100644 --- a/src/midi_fm.h +++ b/src/midi_fm.h @@ -14,14 +14,13 @@ typedef struct PercussionPreset { void midi_fm_init( const FmChannel** defaultPresets, const PercussionPreset** defaultPercussionPresets); void midi_fm_reset(void); -void midi_fm_note_on(u8 chan, u8 pitch, u8 velocity); +void midi_fm_note_on(u8 chan, u8 pitch, s8 cents, u8 velocity); void midi_fm_note_off(u8 chan, u8 pitch); void midi_fm_channel_volume(u8 chan, u8 volume); void midi_fm_pan(u8 chan, u8 pan); -void midi_fm_pitch_bend(u8 chan, u16 bend); void midi_fm_program(u8 chan, u8 program); void midi_fm_all_notes_off(u8 chan); void midi_fm_percussive(u8 chan, bool enabled); u8 midi_fm_pitchToOctave(u8 pitch); -void midi_fm_pitch(u8 chan, u8 pitch, u8 cents); -u16 midi_fm_pitchCentsToFreqNum(u8 pitch, u8 cents); +void midi_fm_pitch(u8 chan, u8 pitch, s8 cents); +u16 midi_fm_pitchCentsToFreqNum(u8 pitch, s8 cents); diff --git a/src/midi_fm_sm.c b/src/midi_fm_sm.c index 682ad35..a06491f 100644 --- a/src/midi_fm_sm.c +++ b/src/midi_fm_sm.c @@ -6,30 +6,30 @@ typedef struct SpecialModeOperator { u8 pitch; - u16 pitchBend; + s8 cents; } SpecialModeOperator; static SpecialModeOperator smOperators[SM_OP_LEN]; -void midi_fm_sm_note_on(u8 op, u8 pitch, u8 velocity) +void midi_fm_sm_note_on(u8 op, u8 pitch, s8 cents, u8 velocity) { SpecialModeOperator* smOp = &smOperators[op]; smOp->pitch = pitch; - PitchCents pc = midi_effectivePitchCents(smOp->pitch, 0, smOp->pitchBend); + smOp->cents = cents; - synth_specialModePitch( - op, midi_fm_pitchToOctave(pc.pitch), midi_fm_pitchCentsToFreqNum(pc.pitch, pc.cents)); + synth_specialModePitch(op, midi_fm_pitchToOctave(smOp->pitch), + midi_fm_pitchCentsToFreqNum(smOp->pitch, smOp->cents)); synth_specialModeVolume(op, velocity); } -void midi_fm_sm_pitch_bend(u8 op, u16 bend) +void midi_fm_sm_pitch(u8 op, u8 pitch, s8 cents) { SpecialModeOperator* smOp = &smOperators[op]; - smOp->pitchBend = bend; - PitchCents pc = midi_effectivePitchCents(smOp->pitch, 0, smOp->pitchBend); + smOp->pitch = pitch; + smOp->cents = cents; - synth_specialModePitch( - op, midi_fm_pitchToOctave(pc.pitch), midi_fm_pitchCentsToFreqNum(pc.pitch, pc.cents)); + synth_specialModePitch(op, midi_fm_pitchToOctave(smOp->pitch), + midi_fm_pitchCentsToFreqNum(smOp->pitch, smOp->cents)); } void midi_fm_sm_reset(void) @@ -37,7 +37,7 @@ void midi_fm_sm_reset(void) for (u8 op = 0; op < SM_OP_LEN; op++) { SpecialModeOperator* smOp = &smOperators[op]; smOp->pitch = 0; - smOp->pitchBend = DEFAULT_MIDI_PITCH_BEND; + smOp->cents = 0; } } @@ -61,7 +61,3 @@ void midi_fm_sm_program(u8 chan, u8 program) void midi_fm_sm_all_notes_off(u8 chan) { } - -void midi_fm_sm_pitch(u8 chan, u8 pitch, u8 cents) -{ -} diff --git a/src/midi_fm_sm.h b/src/midi_fm_sm.h index 913c34d..ecf9cf7 100644 --- a/src/midi_fm_sm.h +++ b/src/midi_fm_sm.h @@ -2,10 +2,9 @@ #include "synth.h" #include "types.h" -void midi_fm_sm_note_on(u8 chan, u8 pitch, u8 velocity); -void midi_fm_sm_pitch_bend(u8 chan, u16 bend); +void midi_fm_sm_note_on(u8 chan, u8 pitch, s8 cents, u8 velocity); void midi_fm_sm_reset(void); -void midi_fm_sm_pitch(u8 chan, u8 pitch, u8 cents); +void midi_fm_sm_pitch(u8 chan, u8 pitch, s8 cents); // no-ops void midi_fm_sm_note_off(u8 chan, u8 pitch); diff --git a/src/midi_psg.c b/src/midi_psg.c index b4f81f7..437136a 100644 --- a/src/midi_psg.c +++ b/src/midi_psg.c @@ -36,7 +36,8 @@ static const u8 ATTENUATIONS[] = { 15, 14, 14, 14, 13, 13, 13, 13, 12, 12, 12, 1 typedef struct MidiPsgChannel { u8 chanNum; - u8 key; + u8 pitch; + s8 cents; u8 attenuation; u16 freq; bool noteOn; @@ -46,7 +47,6 @@ typedef struct MidiPsgChannel { const u8* envelopeStep; const u8* envelopeLoopStart; bool noteReleased; - u16 pitchBend; } MidiPsgChannel; static u8 userDefinedEnvelope[256]; @@ -83,7 +83,8 @@ void midi_psg_reset(void) psgChan->envelope = 0; psgChan->noteReleased = false; psgChan->freq = 0; - psgChan->pitchBend = DEFAULT_MIDI_PITCH_BEND; + psgChan->pitch = 0; + psgChan->cents = 0; initEnvelope(psgChan); } } @@ -120,42 +121,33 @@ static u16 envelopeTone(MidiPsgChannel* psgChan) { const u8 DIVISOR = 20; u8 shift = *psgChan->envelopeStep >> 4; - u16 freq = toneForMidiKey(psgChan->key); + u16 freq = toneForMidiKey(psgChan->pitch); if (shift == 0) { return freq; } u8 shiftIndex = shift - 1; if (shift < 0x5) { - u16 nextTone = toneForMidiKey(psgChan->key + 1); + u16 nextTone = toneForMidiKey(psgChan->pitch + 1); return freq - ((freq - nextTone) * PITCH_SHIFT_TABLE[shiftIndex]) / DIVISOR; } else if (shift < 0x8) { - return toneForMidiKey(psgChan->key + KEY_SHIFT_TABLE[shiftIndex]); + return toneForMidiKey(psgChan->pitch + KEY_SHIFT_TABLE[shiftIndex]); } else if (shift < 0xC) { - u16 prevFreq = toneForMidiKey(psgChan->key - 1); + u16 prevFreq = toneForMidiKey(psgChan->pitch - 1); return freq + ((prevFreq - freq) * PITCH_SHIFT_TABLE[shiftIndex - 0x07]) / DIVISOR; } else { - return toneForMidiKey(psgChan->key - KEY_SHIFT_TABLE[shiftIndex - 0x07]); + return toneForMidiKey(psgChan->pitch - KEY_SHIFT_TABLE[shiftIndex - 0x07]); } } static u16 pitchBentTone(MidiPsgChannel* psgChan, u16 baseTone) { - if (psgChan->pitchBend == DEFAULT_MIDI_PITCH_BEND) { + if (psgChan->cents == 0) { return baseTone; - } else if (psgChan->pitchBend < MIDI_PITCH_BEND_CENTRE) { - // bending down - u16 prevTone = toneForMidiKey(psgChan->key - GENERAL_MIDI_PITCH_BEND_SEMITONE_RANGE); - // prev tone is higher - u16 diff = prevTone - baseTone; - u16 bend = MIDI_PITCH_BEND_CENTRE - psgChan->pitchBend; - return baseTone + (u32)((diff * bend) / MIDI_PITCH_BEND_CENTRE); } else { // bending up - u16 nextTone = toneForMidiKey(psgChan->key + GENERAL_MIDI_PITCH_BEND_SEMITONE_RANGE); + u16 nextTone = toneForMidiKey(psgChan->pitch + 1); // next tone is lower - u16 diff = baseTone - nextTone; - u16 bend = psgChan->pitchBend - MIDI_PITCH_BEND_CENTRE; - return baseTone - (u32)((diff * bend) / MIDI_PITCH_BEND_CENTRE); + return baseTone - (u32)(((baseTone - nextTone) * psgChan->cents) / 100); } } @@ -210,14 +202,15 @@ static void applyEnvelopeStep(MidiPsgChannel* psgChan) applyAttenuation(psgChan, effectiveAttenuation(psgChan)); } -void midi_psg_note_on(u8 chan, u8 key, u8 velocity) +void midi_psg_note_on(u8 chan, u8 pitch, s8 cents, u8 velocity) { - if (key < MIN_MIDI_KEY) { + if (pitch < MIN_MIDI_KEY) { return; } MidiPsgChannel* psgChan = psgChannel(chan); psgChan->noteReleased = false; - psgChan->key = key; + psgChan->pitch = pitch; + psgChan->cents = cents; psgChan->velocity = velocity; psgChan->noteOn = true; initEnvelope(psgChan); @@ -227,7 +220,7 @@ void midi_psg_note_on(u8 chan, u8 key, u8 velocity) void midi_psg_note_off(u8 chan, u8 pitch) { MidiPsgChannel* psgChan = psgChannel(chan); - if (psgChan->noteOn && psgChan->key == pitch) { + if (psgChan->noteOn && psgChan->pitch == pitch) { if (psgChan->envelopeLoopStart != NULL) { psgChan->noteReleased = true; } else { @@ -238,7 +231,7 @@ void midi_psg_note_off(u8 chan, u8 pitch) void midi_psg_all_notes_off(u8 chan) { - midi_psg_note_off(chan, psgChannel(chan)->key); + midi_psg_note_off(chan, psgChannel(chan)->pitch); } void midi_psg_channel_volume(u8 chan, u8 volume) @@ -250,13 +243,6 @@ void midi_psg_channel_volume(u8 chan, u8 volume) } } -void midi_psg_pitch_bend(u8 chan, u16 bend) -{ - MidiPsgChannel* psgChan = psgChannel(chan); - psgChan->pitchBend = bend; - applyTone(psgChan, effectiveTone(psgChan)); -} - void midi_psg_program(u8 chan, u8 program) { MidiPsgChannel* psgChan = psgChannel(chan); @@ -322,12 +308,14 @@ u8 midi_psg_busy(void) return audible; } -void midi_psg_pitch(u8 chan, u8 pitch, u8 cents) +void midi_psg_pitch(u8 chan, u8 pitch, s8 cents) { u16 tone = toneForMidiKey(pitch); u16 nextTone = toneForMidiKey(pitch + 1); u16 newTone = tone + (((nextTone - tone) * cents) / 100); MidiPsgChannel* psgChan = psgChannel(chan); + psgChan->pitch = pitch; + psgChan->cents = cents; applyTone(psgChan, newTone); } diff --git a/src/midi_psg.h b/src/midi_psg.h index c12e2e2..2d5a69b 100644 --- a/src/midi_psg.h +++ b/src/midi_psg.h @@ -16,14 +16,13 @@ void midi_psg_init(const u8** defaultEnvelopes); void midi_psg_reset(void); -void midi_psg_note_on(u8 chan, u8 pitch, u8 velocity); +void midi_psg_note_on(u8 chan, u8 pitch, s8 cents, u8 velocity); void midi_psg_note_off(u8 chan, u8 pitch); void midi_psg_all_notes_off(u8 chan); void midi_psg_channel_volume(u8 chan, u8 volume); -void midi_psg_pitch_bend(u8 chan, u16 bend); void midi_psg_program(u8 chan, u8 program); void midi_psg_pan(u8 chan, u8 pan); void midi_psg_tick(void); void midi_psg_load_envelope(const u8* eef); u8 midi_psg_busy(void); -void midi_psg_pitch(u8 chan, u8 pitch, u8 cents); +void midi_psg_pitch(u8 chan, u8 pitch, s8 cents); diff --git a/tests/unit/test_midi_psg.c b/tests/unit/test_midi_psg.c index 1693a55..ffcbebc 100644 --- a/tests/unit/test_midi_psg.c +++ b/tests/unit/test_midi_psg.c @@ -183,12 +183,13 @@ static void test_midi_sets_psg_pitch_bend_up(UNUSED void** state) static void test_midi_psg_pitch_bend_persists_after_tick(UNUSED void** state) { for (int chan = MIN_PSG_CHAN; chan <= MAX_PSG_CHAN; chan++) { + debug_message("channel %d\n", chan); u8 expectedPsgChan = chan - MIN_PSG_CHAN; expect_psg_tone(expectedPsgChan, TONE_NTSC_C4); expect_psg_attenuation(expectedPsgChan, PSG_ATTENUATION_LOUDEST); __real_midi_note_on(chan, MIDI_PITCH_C4, MAX_MIDI_VOLUME); - expect_psg_tone(expectedPsgChan, 0x1d8); + expect_psg_tone(expectedPsgChan, 0x1d9); __real_midi_pitch_bend(chan, 1000); __real_midi_psg_tick();