From f23e9df084d795daaab2460ab915665089b4511b Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 30 Mar 2008 02:51:34 +0000 Subject: [PATCH] - Fixed: OPLMIDIDevice sent the wrong pitch wheel value to the player code. Missimp.mid sounds a lot better now, though still a little off. - Fixed: MIDI files that had ticks with nothing but meta-events did not play properly. (fixes sonic3_finalboss.mid) SVN r868 (trunk) --- docs/rh-log.txt | 4 ++ src/oplsynth/mlkernel.cpp | 3 +- src/oplsynth/mlopl.cpp | 31 ++++++------- src/oplsynth/music_opl_mididevice.cpp | 19 ++++---- src/sound/i_musicinterns.h | 1 + src/sound/music_midi_midiout.cpp | 63 +++++++++++++++++++-------- 6 files changed, 75 insertions(+), 46 deletions(-) diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 34e0293e1e..14e1a6dbc8 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,8 @@ March 29, 2008 +- Fixed: OPLMIDIDevice sent the wrong pitch wheel value to the player code. + Missimp.mid sounds a lot better now, though still a little off. +- Fixed: MIDI files that had ticks with nothing but meta-events did not play + properly. (fixes sonic3_finalboss.mid) - Applied Const's Makefile.linux changes. - Made the OPL MIDI synth available from Linux. diff --git a/src/oplsynth/mlkernel.cpp b/src/oplsynth/mlkernel.cpp index 80fb74a3f1..d553e7b85a 100644 --- a/src/oplsynth/mlkernel.cpp +++ b/src/oplsynth/mlkernel.cpp @@ -92,7 +92,8 @@ int musicBlock::playTick() OPLplayNote(channel, note, -1); } break; case 2: // pitch wheel - OPLpitchWheel(channel, *score++); + // MUS pitch wheel is 8 bits, but MIDI is 14 + OPLpitchWheel(channel, *score++ << (14 - 8)); break; case 3: // system event (valueless controller) OPLchangeControl(channel, *score++, 0); diff --git a/src/oplsynth/mlopl.cpp b/src/oplsynth/mlopl.cpp index 0622962e92..285d4a6be9 100644 --- a/src/oplsynth/mlopl.cpp +++ b/src/oplsynth/mlopl.cpp @@ -95,20 +95,19 @@ int musicBlock::occupyChannel(uint slot, uint channel, int note, int volume, struct OP2instrEntry *instrument, uchar secondary) { struct OPL2instrument *instr; - struct OPLdata *data = &driverdata; struct channelEntry *ch = &channels[slot]; ch->channel = channel; ch->note = note; ch->flags = secondary ? CH_SECONDARY : 0; - if (data->channelModulation[channel] >= MOD_MIN) + if (driverdata.channelModulation[channel] >= MOD_MIN) ch->flags |= CH_VIBRATO; ch->time = MLtime; if (volume == -1) - volume = data->channelLastVolume[channel]; + volume = driverdata.channelLastVolume[channel]; else - data->channelLastVolume[channel] = volume; - ch->realvolume = calcVolume(data->channelVolume[channel], 256, ch->volume = volume); + driverdata.channelLastVolume[channel] = volume; + ch->realvolume = calcVolume(driverdata.channelVolume[channel], 256, ch->volume = volume); if (instrument->flags & FL_FIXED_PITCH) note = instrument->note; else if (channel == PERCUSSION) @@ -117,7 +116,7 @@ int musicBlock::occupyChannel(uint slot, uint channel, ch->finetune = (instrument->finetune - 0x80) >> 1; else ch->finetune = 0; - ch->pitch = ch->finetune + data->channelPitch[channel]; + ch->pitch = ch->finetune + driverdata.channelPitch[channel]; if (secondary) instr = &instrument->instr[1]; else @@ -135,7 +134,7 @@ int musicBlock::occupyChannel(uint slot, uint channel, io->OPLwriteInstrument(slot, instr); if (ch->flags & CH_VIBRATO) writeModulation(slot, instr, 1); - io->OPLwritePan(slot, instr, data->channelPan[channel]); + io->OPLwritePan(slot, instr, driverdata.channelPan[channel]); io->OPLwriteVolume(slot, instr, ch->realvolume); writeFrequency(slot, note, ch->pitch, 1); return slot; @@ -254,8 +253,7 @@ void musicBlock::OPLreleaseNote(uint channel, uchar note) { uint i; uint id = channel; - struct OPLdata *data = &driverdata; - uint sustain = data->channelSustain[channel]; + uint sustain = driverdata.channelSustain[channel]; for(i = 0; i < io->OPLchannels; i++) { @@ -274,11 +272,9 @@ void musicBlock::OPLpitchWheel(uint channel, int pitch) { uint i; uint id = channel; - struct OPLdata *data = &driverdata; - //pitch -= 0x80; - pitch >>= 1; - data->channelPitch[channel] = pitch; + pitch >>= 7; + driverdata.channelPitch[channel] = pitch; for(i = 0; i < io->OPLchannels; i++) { struct channelEntry *ch = &channels[i]; @@ -365,14 +361,13 @@ void musicBlock::OPLprogramChange(uint channel, int value) void musicBlock::OPLplayMusic(int vol) { uint i; - struct OPLdata *data = &driverdata; for (i = 0; i < CHANNELS; i++) { - data->channelVolume[i] = vol; /* default volume 127 for MUS (full volume) */ - data->channelSustain[i] = 0; - data->channelLastVolume[i] = 64; - data->channelPitch[i] = 64; + driverdata.channelVolume[i] = vol; /* default volume 127 for MUS (full volume) */ + driverdata.channelSustain[i] = 0; + driverdata.channelLastVolume[i] = 64; + driverdata.channelPitch[i] = 64; } } diff --git a/src/oplsynth/music_opl_mididevice.cpp b/src/oplsynth/music_opl_mididevice.cpp index 6921b9414b..0064d0a74f 100644 --- a/src/oplsynth/music_opl_mididevice.cpp +++ b/src/oplsynth/music_opl_mididevice.cpp @@ -44,10 +44,10 @@ // MACROS ------------------------------------------------------------------ #if defined(_DEBUG) && defined(_WIN32) -#define UNIMPL(m,c,s,t) \ +#define DEBUGOUT(m,c,s,t) \ { char foo[128]; sprintf(foo, m, c, s, t); OutputDebugString(foo); } #else -#define UNIMPL(m,c,s,t) +#define DEBUGOUT(m,c,s,t) #endif // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -125,6 +125,7 @@ int OPLMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), vo OPLstopMusic(); OPLplayMusic(100); + DEBUGOUT("========= New song started ==========\n", 0, 0, 0); return 0; } @@ -178,6 +179,7 @@ int OPLMIDIDevice::SetTempo(int tempo) { Tempo = tempo; CalcTickRate(); + DEBUGOUT("Tempo changed to %.0f, %.2f samples/tick\n", Tempo, SamplesPerTick, 0); return 0; } @@ -191,6 +193,7 @@ int OPLMIDIDevice::SetTimeDiv(int timediv) { Division = timediv; CalcTickRate(); + DEBUGOUT("Division changed to %.0f, %.2f samples/tick\n", Division, SamplesPerTick, 0); return 0; } @@ -375,8 +378,7 @@ int OPLMIDIDevice::PlayTick() DWORD *event = (DWORD *)(Events->lpData + Position); if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO) { - Tempo = MEVT_EVENTPARM(event[2]); - CalcTickRate(); + SetTempo(MEVT_EVENTPARM(event[2])); } else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG) { // Should I handle master volume changes? @@ -459,7 +461,7 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2) break; case MIDI_POLYPRESS: - UNIMPL("Unhandled note aftertouch: Channel %d, note %d, value %d\n", channel, parm1, parm2); + DEBUGOUT("Unhandled note aftertouch: Channel %d, note %d, value %d\n", channel, parm1, parm2); break; case MIDI_CTRLCHANGE: @@ -480,7 +482,7 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2) case 126: OPLchangeControl(channel, ctrlMono, parm2); break; case 127: OPLchangeControl(channel, ctrlPoly, parm2); break; default: - UNIMPL("Unhandled controller: Channel %d, controller %d, value %d\n", channel, parm1, parm2); + DEBUGOUT("Unhandled controller: Channel %d, controller %d, value %d\n", channel, parm1, parm2); break; } break; @@ -490,12 +492,11 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2) break; case MIDI_CHANPRESS: - UNIMPL("Unhandled channel aftertouch: Channel %d, value %d\n", channel, parm1, 0); + DEBUGOUT("Unhandled channel aftertouch: Channel %d, value %d\n", channel, parm1, 0); break; case MIDI_PITCHBEND: - // MUS pitch is 8 bit, but MIDI pitch is 14-bit - OPLpitchWheel(channel, (parm1 | (parm2 >> 1)) >> (14 - 8)); + OPLpitchWheel(channel, parm1 | (parm2 << 7)); break; } } diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index c94324138b..646e20cd02 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -297,6 +297,7 @@ protected: void DoRestart(); bool CheckDone(); DWORD *MakeEvents(DWORD *events, DWORD *max_events_p, DWORD max_time); + void AdvanceTracks(DWORD time); struct TrackInfo; diff --git a/src/sound/music_midi_midiout.cpp b/src/sound/music_midi_midiout.cpp index 6df981c944..a25ff29516 100644 --- a/src/sound/music_midi_midiout.cpp +++ b/src/sound/music_midi_midiout.cpp @@ -307,36 +307,63 @@ bool MIDISong2::CheckDone() DWORD *MIDISong2::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) { + DWORD *start_events; DWORD tot_time = 0; DWORD time = 0; + DWORD delay; + start_events = events; while (TrackDue && events < max_event_p && tot_time <= max_time) { - time = TrackDue->Delay; - // Advance time for all tracks by the amount needed for the one up next. - if (time != 0) - { - tot_time += time * Tempo / Division; - for (int i = 0; i < NumTracks; ++i) - { - if (!Tracks[i].Finished) - { - Tracks[i].Delay -= time; - } - } - } - // Play all events for this tic. + // It's possible that this tick may be nothing meta-events and + // not generate any real events. Repeat this until we actually + // get some output so we don't send an empty buffer to the MIDI + // device. do { - events = SendCommand(events, TrackDue, time); - TrackDue = FindNextDue(); - time = 0; + delay = TrackDue->Delay; + time += delay; + // Advance time for all tracks by the amount needed for the one up next. + tot_time += delay * Tempo / Division; + AdvanceTracks(delay); + // Play all events for this tick. + do + { + DWORD *new_events = SendCommand(events, TrackDue, time); + TrackDue = FindNextDue(); + if (new_events != events) + { + time = 0; + } + events = new_events; + } + while (TrackDue && TrackDue->Delay == 0 && events < max_event_p); } - while (TrackDue && TrackDue->Delay == 0 && events < max_event_p); + while (start_events == events && TrackDue); + time = 0; } return events; } +//========================================================================== +// +// MIDISong2 :: AdvanceTracks +// +// Advaces time for all tracks by the specified amount. +// +//========================================================================== + +void MIDISong2::AdvanceTracks(DWORD time) +{ + for (int i = 0; i < NumTracks; ++i) + { + if (!Tracks[i].Finished) + { + Tracks[i].Delay -= time; + } + } +} + //========================================================================== // // MIDISong2 :: SendCommand