While I was recently tinkering on plugout_midi.c, I realised that the MIDI output does not take the length registers into account and short notes will not be ended at the proper time. I tried to take the length into account but realised that it will only work properly if the MIDI export does cycle-accurate processing of the length register – but that's impossible with the IO-based callbacks.
Fortunately we already have something that does cycle-accurate bookkeeping of the length register (as well as all other registers): gbsplay itself :-) This realization lead to the idea for another MIDI output plugin.
plugout_midi.c registers as an IO callback and basically duplicates part of gbhw.c to keep track of what is going on. Some bookkeeping is needed and that state is basically a more or less accurate duplication of the state in gbhw.c. Things that happen between two IO calls (eg. a length register running out, ending a note) are not seen by plugout_midi.c.
plugout_altmidi.c instead registers a new callback into every Gameboy cpu cycle and directly parses the gbhw_channel state from gbhw.c. It is nearly cycle accurate (and thus more CPU intensive) and can take the length registers into account. Duplicate bookkeeping of the sound hardware state and duplicate code to handle the IO calls are greatly reduced. Currently only two IOs are registerd in the IO callback:
Channel triggers are hard to deduce from the sound hardware state, so the IO calls are used to stop the current note. Restarting the note happens in the default code path in the new per-step callback.
Pan effects (left/right channel enable IOs) can be mapped straight to MIDI, so it is done via IO callback. This could also be done in the per-step callback, but this would mean a (mostly unneded) comparison operation every step with no benefits.
current status
I did listening tests via timidity -E i80 -int gbsfile-%d.mid and it seems to work quite good. Even drymouth sounds kind of like it should ;-)
todo
I don't know if the new step-callback is the way to go. That's quite a lot of internal state that is opened up to plugins. Any ideas?
While I was recently tinkering on
plugout_midi.c
, I realised that the MIDI output does not take the length registers into account and short notes will not be ended at the proper time. I tried to take the length into account but realised that it will only work properly if the MIDI export does cycle-accurate processing of the length register – but that's impossible with the IO-based callbacks.Fortunately we already have something that does cycle-accurate bookkeeping of the length register (as well as all other registers): gbsplay itself :-) This realization lead to the idea for another MIDI output plugin.
plugout_midi.c
registers as an IO callback and basically duplicates part ofgbhw.c
to keep track of what is going on. Some bookkeeping is needed and that state is basically a more or less accurate duplication of the state ingbhw.c
. Things that happen between two IO calls (eg. a length register running out, ending a note) are not seen byplugout_midi.c
.plugout_altmidi.c
instead registers a new callback into every Gameboy cpu cycle and directly parses thegbhw_channel
state fromgbhw.c
. It is nearly cycle accurate (and thus more CPU intensive) and can take the length registers into account. Duplicate bookkeeping of the sound hardware state and duplicate code to handle the IO calls are greatly reduced. Currently only two IOs are registerd in the IO callback:current status
I did listening tests via
timidity -E i80 -int gbsfile-%d.mid
and it seems to work quite good. Even drymouth sounds kind of like it should ;-)todo