REminiscence/prf_player.cpp

415 lines
12 KiB
C++

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "file.h"
#ifdef _WIN32
#define MIDI_DRIVER_SYMBOL __declspec(dllimport)
#else
#define MIDI_DRIVER_SYMBOL
#endif
#include "midi_driver.h"
#include "midi_parser.h"
#include "mixer.h"
#include "prf_player.h"
#include "util.h"
static const struct {
int mode;
int hz;
const MidiDriverInfo *info;
} _midiDrivers[] = {
#ifdef USE_MIDI_DRIVER
{ MODE_ADLIB, TIMER_ADLIB_HZ, &midi_driver_adlib },
{ MODE_MT32, TIMER_MT32_HZ, &midi_driver_mt32 },
#endif
{ -1, 0, 0 }
};
static const int kMusicVolume = 63;
PrfPlayer::PrfPlayer(Mixer *mix, FileSystem *fs, int mode)
: _playing(false), _mixer(mix), _fs(fs), _mode(mode), _driver(0) {
for (int i = 0; _midiDrivers[i].info; ++i) {
if (_midiDrivers[i].mode == mode) {
_timerHz = _midiDrivers[i].hz;
_driver = _midiDrivers[i].info->create();
assert(_driver);
if (_driver->init() < 0) {
warning("Failed to initialize MIDI driver %s", _midiDrivers[i].info->name);
_driver = 0;
}
return;
}
}
fprintf(stdout, "WARNING: no midi driver for mode %d", mode);
}
PrfPlayer::~PrfPlayer() {
if (_driver) {
_driver->fini();
_driver = 0;
}
}
void PrfPlayer::play(int num) {
_playing = false;
memset(&_prfData, 0, sizeof(_prfData));
memset(&_tracks, 0, sizeof(_tracks));
if (num < _namesCount) {
char name[64];
snprintf(name, sizeof(name), "%s.prf", (num == 1 && _mode == MODE_MT32) ? "opt" : _names[num]);
File f;
if (f.open(name, "rb", _fs)) {
loadPrf(&f);
if (_mode == MODE_ADLIB) {
for (int i = 0; i < 16; ++i) {
memset(_adlibInstrumentData[i], 0, ADLIB_INSTRUMENT_DATA_LEN);
if (_prfData.instruments[i][0]) {
snprintf(name, sizeof(name), "%s.INS", _prfData.instruments[i]);
if (f.open(name, "rb", _fs)) {
loadIns(&f, i);
} else {
warning("Unable to open '%s'", name);
}
}
}
}
if (f.open(_prfData.midi, "rb", _fs)) {
_parser.loadMid(&f);
play();
}
}
}
}
void PrfPlayer::loadPrf(File *f) {
for (int i = 0; i < 16; ++i) {
f->read(_prfData.instruments[i], INSTRUMENT_NAME_LEN);
const int len = strlen(_prfData.instruments[i]);
if (len > 8) {
_prfData.instruments[i][8] = 0;
debug(DBG_PRF, "Truncating instrument name to '%s'", _prfData.instruments[i]);
}
}
for (int i = 0; i < 16; ++i) {
_prfData.adlibNotes[i] = f->readUint16LE();
}
for (int i = 0; i < 16; ++i) {
_prfData.adlibVelocities[i] = f->readUint16LE();
}
_prfData.timerTicks = f->readUint32LE();
_prfData.timerMod = f->readUint16LE();
f->read(_prfData.midi, MIDI_FILENAME_LEN);
_prfData.adlibDoNotesLookup = f->readUint16LE();
for (int i = 0; i < 16; ++i) {
_prfData.adlibPrograms[i] = f->readUint16LE();
}
for (int i = 0; i < 16; ++i) {
_prfData.mt32Programs[i] = f->readUint16LE();
}
for (int i = 0; i < 16; ++i) {
_prfData.mt32Velocities[i] = f->readUint16LE();
}
for (int i = 0; i < 16; ++i) {
_prfData.mt32Notes[i] = f->readUint16LE();
}
for (int i = 0; i < 16; ++i) {
_tracks[i].hw_channel_num = f->readByte();
}
for (int i = 0; i < 16; ++i) {
_tracks[i].mt32_program_num = f->readByte();
}
for (int i = 0; i < 16; ++i) {
_tracks[i].loop_flag = f->readByte();
}
_prfData.totalDurationTicks = f->readUint32LE();
_prfData.mt32DoChannelsLookup = f->readByte();
assert(f->tell() == 0x2F1);
debug(DBG_PRF, "duration %d timer %d", _prfData.totalDurationTicks, _prfData.timerTicks);
}
void PrfPlayer::loadIns(File *f, int i) {
uint8_t *p = _adlibInstrumentData[i];
p[0] = f->readByte();
p[1] = f->readByte();
f->read(&p[6], 26 * 2);
f->seek(54 + 20);
p[2] = f->readByte();
f->readByte();
p[3] = f->readByte();
f->readByte();
if (p[2] != 0 || p[3] != 0) {
debug(DBG_PRF, "Wave Select Register 0x%02x 0x%02x was ignored", p[2], p[3]);
}
p[4] = 0;
p[5] = 0;
const int unk = f->readUint16LE();
assert(unk == 1);
assert(f->tell() == 80);
}
void PrfPlayer::play() {
if (!_driver) {
return;
}
_driver->reset(_mixer->getSampleRate());
for (int i = 0; i < _parser._tracksCount; ++i) {
_tracks[i].instrument_num = i;
if (_mode == MODE_MT32) {
mt32ProgramChange(i, _tracks[i].mt32_program_num);
mt32ControlChangeResetRPN(i);
}
}
for (int i = 0; i < 16; ++i) {
_tracks[i].instrument_num = i;
}
for (int i = 0; i < _parser._tracksCount; ++i) {
PrfTrack *current_track = &_tracks[i];
MidiTrack *track = &_parser._tracks[i];
track->rewind();
const MidiEvent *ev = track->nextEvent();
current_track->counter = ev ? ev->timestamp : 0;
current_track->counter2 = current_track->counter % _prfData.timerMod;
}
_timerTick = _musicTick = 0;
_samplesLeft = 0;
_samplesPerTick = _mixer->getSampleRate() / _timerHz;
_playing = true;
_mixer->setPremixHook(mixCallback, this);
}
void PrfPlayer::stop() {
if (_playing) {
_mixer->setPremixHook(0, 0);
_playing = false;
}
}
void PrfPlayer::mt32NoteOn(int track, int note, int velocity) {
if (_prfData.mt32DoChannelsLookup != 0 && _tracks[track].hw_channel_num == 9) {
note = _prfData.mt32Notes[_tracks[track].instrument_num];
}
_driver->noteOn(_tracks[track].hw_channel_num, note, (velocity * kMusicVolume) >> 6);
}
void PrfPlayer::mt32NoteOff(int track, int note, int velocity) {
if (_prfData.mt32DoChannelsLookup != 0 && _tracks[track].hw_channel_num == 9) {
note = _prfData.mt32Notes[_tracks[track].instrument_num];
}
_driver->noteOff(_tracks[track].hw_channel_num, note, (velocity * kMusicVolume) >> 6);
}
void PrfPlayer::mt32ProgramChange(int track, int num) {
_driver->programChange(_tracks[track].hw_channel_num, num);
}
void PrfPlayer::mt32PitchBend(int track, int lsb, int msb) {
_driver->pitchBend(_tracks[track].hw_channel_num, lsb, msb);
}
static const uint8_t MIDI_CONTROLLER_RPN_LSB = 0x64;
static const uint8_t MIDI_CONTROLLER_RPN_MSB = 0x65;
static const uint8_t MIDI_CONTROLLER_DATA_ENTRY_LSB = 0x26;
static const uint8_t MIDI_CONTROLLER_DATA_ENTRY_MSB = 0x06;
void PrfPlayer::mt32ControlChangeResetRPN(int track) {
_driver->controlChange(_tracks[track].hw_channel_num, MIDI_CONTROLLER_RPN_MSB, 0);
_driver->controlChange(_tracks[track].hw_channel_num, MIDI_CONTROLLER_RPN_LSB, 0);
_driver->controlChange(_tracks[track].hw_channel_num, MIDI_CONTROLLER_DATA_ENTRY_MSB, 1);
_driver->controlChange(_tracks[track].hw_channel_num, MIDI_CONTROLLER_DATA_ENTRY_LSB, 0);
}
void PrfPlayer::adlibNoteOn(int track, int note, int velocity) {
const int instrument_num = _tracks[track].instrument_num;
_driver->setInstrumentData(track, instrument_num, _adlibInstrumentData[instrument_num]);
_driver->noteOn(track, note, velocity);
}
void PrfPlayer::adlibNoteOff(int track, int note, int velocity) {
const int instrument_num = _tracks[track].instrument_num;
_driver->setInstrumentData(track, instrument_num, _adlibInstrumentData[instrument_num]);
_driver->noteOff(track, note, velocity);
}
void PrfPlayer::handleTick() {
for (int i = 0; i < _parser._tracksCount; ++i) {
MidiTrack *track = &_parser._tracks[i];
if (track->endOfTrack) {
continue;
}
const int track_index = i;
PrfTrack *current_track = &_tracks[i];
if (current_track->counter != 0) {
--current_track->counter;
continue;
}
while (1) {
const MidiEvent &ev = track->event;
switch (ev.command & 0xF0) {
case MIDI_COMMAND_NOTE_OFF: {
int note = ev.param1;
int velocity = ev.param2;
if (_mode == MODE_MT32) {
note += _prfData.mt32Notes[current_track->instrument_num];
velocity += _prfData.mt32Velocities[current_track->instrument_num];
if (velocity < 0) {
velocity = 0;
} else if (velocity > 127) {
velocity = 127;
}
mt32NoteOff(track_index, note, velocity);
} else {
if (_prfData.adlibDoNotesLookup) {
note += _prfData.adlibNotes[current_track->instrument_num];
} else {
note += _prfData.adlibNotes[track_index];
}
if (_prfData.adlibDoNotesLookup) {
velocity += _prfData.adlibVelocities[current_track->instrument_num];
} else {
velocity += _prfData.adlibVelocities[track_index];
}
if (velocity < 0) {
velocity = 0;
} else if (velocity > 127) {
velocity = 127;
}
adlibNoteOff(track_index, note, velocity);
}
}
break;
case MIDI_COMMAND_NOTE_ON: {
int note = ev.param1;
int velocity = ev.param2;
if (_mode == MODE_MT32) {
note += _prfData.mt32Notes[current_track->instrument_num];
if (velocity == 0) {
mt32NoteOff(track_index, note, velocity);
break;
}
velocity += _prfData.mt32Velocities[current_track->instrument_num];
if (velocity < 0) {
velocity = 0;
} else if (velocity > 127) {
velocity = 127;
}
mt32NoteOn(track_index, note, velocity);
} else {
assert(_mode == MODE_ADLIB);
if (_prfData.adlibDoNotesLookup) {
note += _prfData.adlibNotes[current_track->instrument_num];
} else {
note += _prfData.adlibNotes[track_index];
}
if (velocity == 0) {
adlibNoteOff(track_index, note, velocity);
break;
}
if (_prfData.adlibDoNotesLookup) {
velocity += _prfData.adlibVelocities[current_track->instrument_num];
} else {
velocity += _prfData.adlibVelocities[track_index];
}
if (velocity < 0) {
velocity = 0;
} else if (velocity > 127) {
velocity = 127;
}
adlibNoteOn(track_index, note, velocity);
}
}
break;
case MIDI_COMMAND_PROGRAM_CHANGE: {
if (_mode == MODE_MT32) {
for (int num = 0; num < 16; ++num) {
if (_prfData.mt32Programs[num] == ev.param1) {
mt32ProgramChange(track_index, _tracks[num].mt32_program_num);
break;
}
}
} else {
assert(_mode == MODE_ADLIB);
if (_prfData.adlibDoNotesLookup) {
for (int num = 0; num < 16; ++num) {
if (_prfData.adlibPrograms[num] == ev.param1) {
current_track->instrument_num = num;
break;
}
}
}
}
}
break;
case MIDI_COMMAND_CHANNEL_PRESSURE:
break;
case MIDI_COMMAND_PITCH_BEND: {
if (_mode == MODE_MT32) {
mt32PitchBend(track_index, ev.param1, ev.param2);
} else {
assert(_mode == MODE_ADLIB);
_driver->pitchBend(track_index, ev.param1, ev.param2);
}
}
break;
default:
warning("Unhandled MIDI event command 0x%x", ev.command);
break;
}
const MidiEvent *next = track->nextEvent();
if (!next) {
if (current_track->loop_flag == 0) {
track->rewind();
const MidiEvent *next = track->nextEvent();
if (next) {
current_track->counter = _prfData.timerMod - current_track->counter2 + next->timestamp - 1;
current_track->counter2 = next->timestamp % _prfData.timerMod;
}
}
break;
}
current_track->counter = next->timestamp;
current_track->counter2 = (current_track->counter + current_track->counter2) % _prfData.timerMod;
if (current_track->counter != 0) {
--current_track->counter;
break;
}
}
}
}
bool PrfPlayer::end() const {
return (_prfData.totalDurationTicks != 0) && _musicTick > _prfData.totalDurationTicks;
}
int PrfPlayer::readSamples(int16_t *samples, int count) {
const int total = count;
while (count != 0) {
if (_samplesLeft == 0) {
handleTick();
if (_prfData.totalDurationTicks != 0) {
++_musicTick;
if (_musicTick == _prfData.totalDurationTicks + 1) {
debug(DBG_PRF, "End of music");
}
}
_samplesLeft = _samplesPerTick * _prfData.timerTicks;
}
const int len = (count < _samplesLeft) ? count : _samplesLeft;
_driver->readSamples(samples, len);
_samplesLeft -= len;
count -= len;
samples += len * 2;
}
return total - count;
}
bool PrfPlayer::mixCallback(void *param, int16_t *buf, int len) {
return ((PrfPlayer *)param)->mix(buf, len);
}
bool PrfPlayer::mix(int16_t *buf, int len) {
const int count = readSamples(buf, len);
return count != 0;
}