- 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)
This commit is contained in:
Randy Heit 2008-03-30 02:51:34 +00:00
parent f72635ac69
commit f23e9df084
6 changed files with 75 additions and 46 deletions

View file

@ -1,4 +1,8 @@
March 29, 2008 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. - Applied Const's Makefile.linux changes.
- Made the OPL MIDI synth available from Linux. - Made the OPL MIDI synth available from Linux.

View file

@ -92,7 +92,8 @@ int musicBlock::playTick()
OPLplayNote(channel, note, -1); OPLplayNote(channel, note, -1);
} break; } break;
case 2: // pitch wheel case 2: // pitch wheel
OPLpitchWheel(channel, *score++); // MUS pitch wheel is 8 bits, but MIDI is 14
OPLpitchWheel(channel, *score++ << (14 - 8));
break; break;
case 3: // system event (valueless controller) case 3: // system event (valueless controller)
OPLchangeControl(channel, *score++, 0); OPLchangeControl(channel, *score++, 0);

View file

@ -95,20 +95,19 @@ int musicBlock::occupyChannel(uint slot, uint channel,
int note, int volume, struct OP2instrEntry *instrument, uchar secondary) int note, int volume, struct OP2instrEntry *instrument, uchar secondary)
{ {
struct OPL2instrument *instr; struct OPL2instrument *instr;
struct OPLdata *data = &driverdata;
struct channelEntry *ch = &channels[slot]; struct channelEntry *ch = &channels[slot];
ch->channel = channel; ch->channel = channel;
ch->note = note; ch->note = note;
ch->flags = secondary ? CH_SECONDARY : 0; ch->flags = secondary ? CH_SECONDARY : 0;
if (data->channelModulation[channel] >= MOD_MIN) if (driverdata.channelModulation[channel] >= MOD_MIN)
ch->flags |= CH_VIBRATO; ch->flags |= CH_VIBRATO;
ch->time = MLtime; ch->time = MLtime;
if (volume == -1) if (volume == -1)
volume = data->channelLastVolume[channel]; volume = driverdata.channelLastVolume[channel];
else else
data->channelLastVolume[channel] = volume; driverdata.channelLastVolume[channel] = volume;
ch->realvolume = calcVolume(data->channelVolume[channel], 256, ch->volume = volume); ch->realvolume = calcVolume(driverdata.channelVolume[channel], 256, ch->volume = volume);
if (instrument->flags & FL_FIXED_PITCH) if (instrument->flags & FL_FIXED_PITCH)
note = instrument->note; note = instrument->note;
else if (channel == PERCUSSION) else if (channel == PERCUSSION)
@ -117,7 +116,7 @@ int musicBlock::occupyChannel(uint slot, uint channel,
ch->finetune = (instrument->finetune - 0x80) >> 1; ch->finetune = (instrument->finetune - 0x80) >> 1;
else else
ch->finetune = 0; ch->finetune = 0;
ch->pitch = ch->finetune + data->channelPitch[channel]; ch->pitch = ch->finetune + driverdata.channelPitch[channel];
if (secondary) if (secondary)
instr = &instrument->instr[1]; instr = &instrument->instr[1];
else else
@ -135,7 +134,7 @@ int musicBlock::occupyChannel(uint slot, uint channel,
io->OPLwriteInstrument(slot, instr); io->OPLwriteInstrument(slot, instr);
if (ch->flags & CH_VIBRATO) if (ch->flags & CH_VIBRATO)
writeModulation(slot, instr, 1); writeModulation(slot, instr, 1);
io->OPLwritePan(slot, instr, data->channelPan[channel]); io->OPLwritePan(slot, instr, driverdata.channelPan[channel]);
io->OPLwriteVolume(slot, instr, ch->realvolume); io->OPLwriteVolume(slot, instr, ch->realvolume);
writeFrequency(slot, note, ch->pitch, 1); writeFrequency(slot, note, ch->pitch, 1);
return slot; return slot;
@ -254,8 +253,7 @@ void musicBlock::OPLreleaseNote(uint channel, uchar note)
{ {
uint i; uint i;
uint id = channel; uint id = channel;
struct OPLdata *data = &driverdata; uint sustain = driverdata.channelSustain[channel];
uint sustain = data->channelSustain[channel];
for(i = 0; i < io->OPLchannels; i++) for(i = 0; i < io->OPLchannels; i++)
{ {
@ -274,11 +272,9 @@ void musicBlock::OPLpitchWheel(uint channel, int pitch)
{ {
uint i; uint i;
uint id = channel; uint id = channel;
struct OPLdata *data = &driverdata;
//pitch -= 0x80; pitch >>= 7;
pitch >>= 1; driverdata.channelPitch[channel] = pitch;
data->channelPitch[channel] = pitch;
for(i = 0; i < io->OPLchannels; i++) for(i = 0; i < io->OPLchannels; i++)
{ {
struct channelEntry *ch = &channels[i]; struct channelEntry *ch = &channels[i];
@ -365,14 +361,13 @@ void musicBlock::OPLprogramChange(uint channel, int value)
void musicBlock::OPLplayMusic(int vol) void musicBlock::OPLplayMusic(int vol)
{ {
uint i; uint i;
struct OPLdata *data = &driverdata;
for (i = 0; i < CHANNELS; i++) for (i = 0; i < CHANNELS; i++)
{ {
data->channelVolume[i] = vol; /* default volume 127 for MUS (full volume) */ driverdata.channelVolume[i] = vol; /* default volume 127 for MUS (full volume) */
data->channelSustain[i] = 0; driverdata.channelSustain[i] = 0;
data->channelLastVolume[i] = 64; driverdata.channelLastVolume[i] = 64;
data->channelPitch[i] = 64; driverdata.channelPitch[i] = 64;
} }
} }

View file

@ -44,10 +44,10 @@
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
#if defined(_DEBUG) && defined(_WIN32) #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); } { char foo[128]; sprintf(foo, m, c, s, t); OutputDebugString(foo); }
#else #else
#define UNIMPL(m,c,s,t) #define DEBUGOUT(m,c,s,t)
#endif #endif
// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@ -125,6 +125,7 @@ int OPLMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), vo
OPLstopMusic(); OPLstopMusic();
OPLplayMusic(100); OPLplayMusic(100);
DEBUGOUT("========= New song started ==========\n", 0, 0, 0);
return 0; return 0;
} }
@ -178,6 +179,7 @@ int OPLMIDIDevice::SetTempo(int tempo)
{ {
Tempo = tempo; Tempo = tempo;
CalcTickRate(); CalcTickRate();
DEBUGOUT("Tempo changed to %.0f, %.2f samples/tick\n", Tempo, SamplesPerTick, 0);
return 0; return 0;
} }
@ -191,6 +193,7 @@ int OPLMIDIDevice::SetTimeDiv(int timediv)
{ {
Division = timediv; Division = timediv;
CalcTickRate(); CalcTickRate();
DEBUGOUT("Division changed to %.0f, %.2f samples/tick\n", Division, SamplesPerTick, 0);
return 0; return 0;
} }
@ -375,8 +378,7 @@ int OPLMIDIDevice::PlayTick()
DWORD *event = (DWORD *)(Events->lpData + Position); DWORD *event = (DWORD *)(Events->lpData + Position);
if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO) if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO)
{ {
Tempo = MEVT_EVENTPARM(event[2]); SetTempo(MEVT_EVENTPARM(event[2]));
CalcTickRate();
} }
else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG) else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG)
{ // Should I handle master volume changes? { // Should I handle master volume changes?
@ -459,7 +461,7 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2)
break; break;
case MIDI_POLYPRESS: 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; break;
case MIDI_CTRLCHANGE: case MIDI_CTRLCHANGE:
@ -480,7 +482,7 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2)
case 126: OPLchangeControl(channel, ctrlMono, parm2); break; case 126: OPLchangeControl(channel, ctrlMono, parm2); break;
case 127: OPLchangeControl(channel, ctrlPoly, parm2); break; case 127: OPLchangeControl(channel, ctrlPoly, parm2); break;
default: 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;
} }
break; break;
@ -490,12 +492,11 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2)
break; break;
case MIDI_CHANPRESS: 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; break;
case MIDI_PITCHBEND: case MIDI_PITCHBEND:
// MUS pitch is 8 bit, but MIDI pitch is 14-bit OPLpitchWheel(channel, parm1 | (parm2 << 7));
OPLpitchWheel(channel, (parm1 | (parm2 >> 1)) >> (14 - 8));
break; break;
} }
} }

View file

@ -297,6 +297,7 @@ protected:
void DoRestart(); void DoRestart();
bool CheckDone(); bool CheckDone();
DWORD *MakeEvents(DWORD *events, DWORD *max_events_p, DWORD max_time); DWORD *MakeEvents(DWORD *events, DWORD *max_events_p, DWORD max_time);
void AdvanceTracks(DWORD time);
struct TrackInfo; struct TrackInfo;

View file

@ -307,36 +307,63 @@ bool MIDISong2::CheckDone()
DWORD *MIDISong2::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) DWORD *MIDISong2::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time)
{ {
DWORD *start_events;
DWORD tot_time = 0; DWORD tot_time = 0;
DWORD time = 0; DWORD time = 0;
DWORD delay;
start_events = events;
while (TrackDue && events < max_event_p && tot_time <= max_time) while (TrackDue && events < max_event_p && tot_time <= max_time)
{ {
time = TrackDue->Delay; // It's possible that this tick may be nothing meta-events and
// Advance time for all tracks by the amount needed for the one up next. // not generate any real events. Repeat this until we actually
if (time != 0) // get some output so we don't send an empty buffer to the MIDI
{ // device.
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.
do do
{ {
events = SendCommand(events, TrackDue, time); delay = TrackDue->Delay;
TrackDue = FindNextDue(); time += delay;
time = 0; // 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; 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 // MIDISong2 :: SendCommand