From 100218a3c25836f314eb21a7260ab805cc51b8a8 Mon Sep 17 00:00:00 2001 From: Gregory Montoir Date: Sat, 15 Apr 2023 08:44:11 +0800 Subject: [PATCH] Import 0.5.1 --- CHANGES.txt | 5 +++ README.txt | 2 +- cpc_player.cpp | 3 +- cutscene.cpp | 15 +++++---- cutscene.h | 8 ++--- game.cpp | 27 +++++++++++++-- intern.h | 3 +- main.cpp | 12 +++++-- midi_parser.cpp | 84 +++++++++++++++++++++++++++++----------------- midi_parser.h | 14 ++++++-- mixer.cpp | 19 ++++++----- mixer.h | 2 +- mod_player.cpp | 46 +++++++++++++++---------- mod_player.h | 6 ++-- ogg_player.cpp | 24 ++++++++----- piege.cpp | 31 +++++++++++++---- prf_player.cpp | 71 ++++++++++++++++++--------------------- prf_player.h | 1 + seq_player.cpp | 8 +++-- sfx_player.cpp | 15 +++++---- staticres.cpp | 66 ++++++++++++++++++++---------------- systemstub_sdl.cpp | 5 +-- util.h | 3 +- 23 files changed, 295 insertions(+), 175 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3b083a5..a45b4e7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,8 @@ +* release 0.5.1 + - added looping for DOS .prf music + - changed audio mixer to stereo + - updated timings for cutscenes + * release 0.5.0 - added CD-i widescreen mode (flashp*bob) - added support for DOS .prf music (Adlib, MT32) diff --git a/README.txt b/README.txt index b430d7a..dfa7bd9 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ REminiscence README -Release version: 0.5.0 +Release version: 0.5.1 ------------------------------------------------------------------------------- diff --git a/cpc_player.cpp b/cpc_player.cpp index fd54c5a..17a5d5e 100644 --- a/cpc_player.cpp +++ b/cpc_player.cpp @@ -120,7 +120,8 @@ bool CpcPlayer::mix(int16_t *buf, int len) { for (int i = 0; i < len; ++i) { _sampleL = decodeSDX2(_sampleL, readSampleData()); _sampleR = decodeSDX2(_sampleR, readSampleData()); - *buf++ = (_sampleL + _sampleR) / 2; + *buf++ = _sampleL; + *buf++ = _sampleR; } return true; } diff --git a/cutscene.cpp b/cutscene.cpp index 8aeeda8..78ad469 100644 --- a/cutscene.cpp +++ b/cutscene.cpp @@ -35,15 +35,16 @@ const uint8_t *Cutscene::getPolygonData() const { return _res->_pol; } -void Cutscene::sync() { +void Cutscene::sync(int frameDelay) { if (_stub->_pi.quit) { return; } if (_stub->_pi.dbgMask & PlayerInput::DF_FASTMODE) { return; } + static const int frameHz = 60; const int32_t delay = _stub->getTimeStamp() - _tstamp; - const int32_t pause = _frameDelay * TIMER_SLICE - delay; + const int32_t pause = frameDelay * (1000 / frameHz) - delay; if (pause > 0) { _stub->sleep(pause); } @@ -72,7 +73,7 @@ void Cutscene::updatePalette() { } void Cutscene::updateScreen() { - sync(); + sync(_frameDelay - 1); updatePalette(); SWAP(_frontPage, _backPage); _stub->copyRect(0, 0, _vid->_w, _vid->_h, _frontPage, _vid->_w); @@ -343,7 +344,7 @@ void Cutscene::op_waitForSync() { _creditsSlowText = false; } else { _frameDelay = fetchNextCmdByte() * 4; - sync(); // XXX handle input + sync(_frameDelay); } } @@ -470,7 +471,7 @@ void Cutscene::op_drawCaptionText() { } else if (_id == kCineEspions) { // cutscene relies on drawCaptionText opcodes for timing _frameDelay = 100; - sync(); + sync(_frameDelay); } } } @@ -1257,7 +1258,7 @@ void Cutscene::playText(const char *str) { _stub->_pi.backspace = false; break; } - _stub->sleep(TIMER_SLICE); + _stub->sleep(30); } } @@ -1467,7 +1468,7 @@ void Cutscene::playSet(const uint8_t *p, int offset) { _stub->copyRect(0, 0, _vid->_w, _vid->_h, _backPage, _vid->_w); _stub->updateScreen(0); - const int diff = 6 * TIMER_SLICE - (_stub->getTimeStamp() - timestamp); + const int diff = 90 - (_stub->getTimeStamp() - timestamp); _stub->sleep((diff < 16) ? 16 : diff); _stub->processEvents(); if (_stub->_pi.backspace) { diff --git a/cutscene.h b/cutscene.h index a9fab45..8a66bce 100644 --- a/cutscene.h +++ b/cutscene.h @@ -19,8 +19,7 @@ struct Cutscene { enum { MAX_VERTICES = 128, - NUM_OPCODES = 15, - TIMER_SLICE = 15 + NUM_OPCODES = 15 }; enum { @@ -58,7 +57,8 @@ struct Cutscene { static const uint8_t _creditsDataDOS[]; static const uint8_t _creditsDataAmiga[]; static const uint16_t _creditsCutSeq[]; - static const uint8_t _musicTable[]; + static const uint8_t _musicTableDOS[]; + static const uint8_t _musicTableAmiga[]; static const uint8_t _protectionShapeData[]; static const Text _frTextsTable[]; static const Text _enTextsTable[]; @@ -122,7 +122,7 @@ struct Cutscene { const uint8_t *getCommandData() const; const uint8_t *getPolygonData() const; - void sync(); + void sync(int delay); void copyPalette(const uint8_t *pal, uint16_t num); void updatePalette(); void updateScreen(); diff --git a/game.cpp b/game.cpp index 4ea05e8..27f5684 100644 --- a/game.cpp +++ b/game.cpp @@ -133,7 +133,6 @@ void Game::run() { _skillLevel = _menu._skill; _currentLevel = _menu._level; } - _mix.stopMusic(); break; case kResourceTypeAmiga: displayTitleScreenAmiga(); @@ -143,6 +142,7 @@ void Game::run() { displayTitleScreenMac(Menu::kMacTitleScreen_Flashback); break; } + _mix.stopMusic(); } if (_stub->_pi.quit) { break; @@ -544,7 +544,18 @@ void Game::playCutscene(int id) { } } } - _mix.playMusic(Cutscene::_musicTable[_cut._id]); + if (_res.isAmiga()) { + const int num = Cutscene::_musicTableAmiga[_cut._id * 2]; + if (num != 0xFF) { + const int bpm = Cutscene::_musicTableAmiga[_cut._id * 2 + 1]; + _mix.playMusic(num, bpm); + } + } else { + const int num = Cutscene::_musicTableDOS[_cut._id]; + if (num != 0xFF) { + _mix.playMusic(num); + } + } _cut.play(); if (id == 0xD && !_cut._interrupted) { if (!_res.isAmiga()) { @@ -1713,6 +1724,18 @@ void Game::loadLevelData() { char name[64]; snprintf(name, sizeof(name), "DUMP/level%d_room%02d.bmp", _currentLevel, i); saveBMP(name, _vid._backLayer, palette, _vid._w, _vid._h); + + memcpy(_vid._frontLayer, _vid._backLayer, _vid._layerSize); + for (int y = 0; y < 7; ++y) { + for (int x = 0; x < 16; ++x) { + const uint8_t num = _res._ctData[0x100 + _currentRoom * 0x70 + y * 16 + x]; + char buf[16]; + snprintf(buf, sizeof(buf), "%d", num); + _vid.drawString(buf, x * 16, y * 36 + 8, 0xE7); + } + } + snprintf(name, sizeof(name), "DUMP/level%d_room%02d_grid.bmp", _currentLevel, i); + saveBMP(name, _vid._frontLayer, palette, _vid._w, _vid._h); } } } diff --git a/intern.h b/intern.h index a0fd2c0..add5d3b 100644 --- a/intern.h +++ b/intern.h @@ -52,8 +52,7 @@ inline int16_t S8_to_S16(int a) { } else if (a > 127) { return 32767; } else { - const uint8_t u8 = (a ^ 0x80); - return ((u8 << 8) | u8) - 32768; + return ((uint8_t)a) * 257; } } diff --git a/main.cpp b/main.cpp index e54295d..d7fb2d2 100644 --- a/main.cpp +++ b/main.cpp @@ -260,12 +260,16 @@ int main(int argc, char *argv[]) { { LANG_JP, "JP" }, { -1, 0 } }; - for (int i = 0; languages[i].str; ++i) { + int i = 0; + for (; languages[i].str; ++i) { if (strcasecmp(languages[i].str, optarg) == 0) { forcedLanguage = languages[i].lang; break; } } + if (!languages[i].str) { + warning("Invalid language '%s'", optarg); + } } break; case 7: @@ -286,12 +290,16 @@ int main(int argc, char *argv[]) { { MODE_MT32, "mt32" }, { -1, 0 } }; - for (int i = 0; drivers[i].str; ++i) { + int i = 0; + for (; drivers[i].str; ++i) { if (strcasecmp(drivers[i].str, optarg) == 0) { midiDriver = drivers[i].mode; break; } } + if (!drivers[i].str) { + warning("Invalid MIDI driver '%s'", optarg); + } } break; default: diff --git a/midi_parser.cpp b/midi_parser.cpp index 272cbef..8dcfc4f 100644 --- a/midi_parser.cpp +++ b/midi_parser.cpp @@ -6,10 +6,15 @@ #include "midi_parser.h" #include "util.h" -static uint32_t readVLQ(File *f) { +void MidiTrack::rewind() { + endOfTrack = false; + offset = 0; +} + +static uint32_t readVLQ(const uint8_t *data, uint32_t& offset) { uint32_t value = 0; for (int i = 0; i < 4; ++i) { - const uint8_t b = f->readByte(); + const uint8_t b = data[offset++]; value = (value << 7) | (b & 0x7F); if ((b & 0x80) == 0) { break; @@ -18,39 +23,42 @@ static uint32_t readVLQ(File *f) { return value; } -static void loadTrk(File *f, int len, MidiTrack &track) { - const uint32_t end = f->tell() + len; - while (f->tell() < end) { - MidiEvent ev; - ev.timestamp = readVLQ(f); - ev.command = f->readByte(); +const MidiEvent *MidiTrack::nextEvent() { + while (offset < size) { + MidiEvent &ev = event; + ev.timestamp = readVLQ(data, offset); + ev.command = data[offset++]; switch (ev.command & 0xF0) { case MIDI_COMMAND_NOTE_OFF: case MIDI_COMMAND_NOTE_ON: - ev.param1 = f->readByte(); - ev.param2 = f->readByte(); - //fprintf(stdout, "\t Note %s: note %d vel %d timestamp %d\n", (((ev.command & 0xF0) == MIDI_COMMAND_NOTE_OFF) ? "Off" : "On"), ev.param1, ev.param2, ev.timestamp); + ev.param1 = data[offset++]; + ev.param2 = data[offset++]; + debug(DBG_MIDI, "Note %s: note %d vel %d timestamp %d", (((ev.command & 0xF0) == MIDI_COMMAND_NOTE_OFF) ? "Off" : "On"), ev.param1, ev.param2, ev.timestamp); break; case MIDI_COMMAND_PROGRAM_CHANGE: - ev.param1 = f->readByte(); - //fprintf(stdout, "\t Program %d timestamp %d\n", ev.param1, ev.timestamp); + ev.param1 = data[offset++]; + debug(DBG_MIDI, "Program %d timestamp %d", ev.param1, ev.timestamp); break; case MIDI_COMMAND_CHANNEL_PRESSURE: - ev.param1 = f->readByte(); - //fprintf(stdout, "\t Channel pressure %d timestamp %d\n", ev.param1, ev.timestamp); + ev.param1 = data[offset++]; + debug(DBG_MIDI, "Channel pressure %d timestamp %d", ev.param1, ev.timestamp); break; case MIDI_COMMAND_PITCH_BEND: - ev.param1 = f->readByte(); - ev.param2 = f->readByte(); - //fprintf(stdout, "\t Pitch Bend %d,%d\n", ev.param1, ev.param2); + ev.param1 = data[offset++]; + ev.param2 = data[offset++]; + debug(DBG_MIDI, "Pitch Bend %d,%d", ev.param1, ev.param2); break; case MIDI_COMMAND_SYSEX: if (ev.command == 0xFF) { - f->readByte(); /* type */ - const int len = readVLQ(f); - f->seek(f->tell() + len); - //fprintf(stdout, "\t SysEx timestamp %d\n", ev.timestamp); + const int num = data[offset++]; + const int len = readVLQ(data, offset); + offset += len; + debug(DBG_MIDI, "SysEx timestamp %d", ev.timestamp); assert(ev.timestamp == 0); + if (num == MIDI_SYSEX_END_OF_TRACK) { + endOfTrack = true; + return 0; + } continue; } /* fall-through */ @@ -58,14 +66,22 @@ static void loadTrk(File *f, int len, MidiTrack &track) { warning("Unhandled MIDI command 0x%x", ev.command); break; } - track.events.push(ev); + return &event; } + return 0; +} + +MidiParser::MidiParser() { + memset(_tracks, 0, sizeof(_tracks)); + _tracksCount = 0; } void MidiParser::loadMid(File *f) { for (int i = 0; i < MAX_TRACKS; ++i) { - _tracks[i].events = std::queue(); + free(_tracks[i].data); + _tracks[i].data = 0; } + memset(_tracks, 0, sizeof(_tracks)); _tracksCount = 0; char tag[4]; f->read(tag, 4); @@ -73,18 +89,26 @@ void MidiParser::loadMid(File *f) { const uint32_t len = f->readUint32BE(); assert(len == 6); f->readByte(); - /* const int type = */ f->readByte(); + const int type = f->readByte(); const int tracksCount = f->readUint16BE(); assert(tracksCount <= MAX_TRACKS); - /* const int ppqn = */ f->readUint16BE(); - //fprintf(stdout, "MThd type %d tracks %d ppqn %d len %d\n", type, tracksCount, ppqn, len); + const int ppqn = f->readUint16BE(); + debug(DBG_MIDI, "MThd type %d tracks %d ppqn %d len %d", type, tracksCount, ppqn, len); for (int i = 0; i < tracksCount; ++i) { f->read(tag, 4); assert(memcmp(tag, "MTrk", 4) == 0); const uint32_t len = f->readUint32BE(); - //fprintf(stdout, "Track #%d len %d\n", i, len); - loadTrk(f, len, _tracks[i]); - //fprintf(stdout, "Track #%d events %ld\n", i, track.events.size()); + debug(DBG_MIDI, "Track #%d len %d", i, len); + MidiTrack *track = &_tracks[i]; + track->data = (uint8_t *)malloc(len); + if (!track->data) { + warning("Failed to allocate %d bytes for MIDI track %d", len, i); + } else { + track->endOfTrack = false; + track->offset = 0; + track->size = len; + f->read(track->data, len); + } } _tracksCount = tracksCount; } diff --git a/midi_parser.h b/midi_parser.h index f3f1690..448ab01 100644 --- a/midi_parser.h +++ b/midi_parser.h @@ -2,7 +2,6 @@ #ifndef MIDI_PARSER_H__ #define MIDI_PARSER_H__ -#include #include #define MIDI_COMMAND_NOTE_OFF 0x80 @@ -12,6 +11,8 @@ #define MIDI_COMMAND_PITCH_BEND 0xE0 #define MIDI_COMMAND_SYSEX 0xF0 /* unused */ +#define MIDI_SYSEX_END_OF_TRACK 0x2F + struct MidiEvent { uint32_t timestamp; uint8_t command; @@ -20,7 +21,13 @@ struct MidiEvent { }; struct MidiTrack { - std::queue events; + bool endOfTrack; + uint8_t *data; + uint32_t offset, size; + MidiEvent event; + + void rewind(); + const MidiEvent *nextEvent(); }; struct File; @@ -28,6 +35,9 @@ struct File; #define MAX_TRACKS 16 struct MidiParser { + + MidiParser(); + void loadMid(File *f); int _tracksCount; diff --git a/mixer.cpp b/mixer.cpp index bd4f846..36dfed6 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -90,8 +90,8 @@ static bool isMusicSfx(int num) { return (num >= 68 && num <= 75); } -void Mixer::playMusic(int num) { - debug(DBG_SND, "Mixer::playMusic(%d)", num); +void Mixer::playMusic(int num, int tempo) { + debug(DBG_SND, "Mixer::playMusic(%d, %d)", num, tempo); int trackNum = -1; if (num == 1) { // menu screen trackNum = 2; @@ -119,7 +119,7 @@ void Mixer::playMusic(int num) { _musicType = MT_SFX; } } else { // cutscene - _mod.play(num); + _mod.play(num, tempo); if (_mod._playing) { _musicType = MT_MOD; return; @@ -156,7 +156,7 @@ void Mixer::stopMusic() { break; } _musicType = MT_NONE; - if (_musicTrack != -1) { + if (_musicTrack > 2) { // do not resume menu music switch (_backgroundMusicType) { case MT_OGG: _ogg.resumeTrack(); @@ -194,18 +194,21 @@ void Mixer::mix(int16_t *out, int len) { MixerChannel *ch = &_channels[i]; if (ch->active) { for (int pos = 0; pos < len; ++pos) { - if ((ch->chunkPos >> FRAC_BITS) >= (ch->chunk.len - 1)) { + const uint32_t cpos = ch->chunkPos >> FRAC_BITS; + if (cpos >= ch->chunk.len) { ch->active = false; break; } - const int sample = ch->chunk.getPCM(ch->chunkPos >> FRAC_BITS) * ch->volume / Mixer::MAX_VOLUME; - out[pos] = ADDC_S16(out[pos], S8_to_S16(sample)); + const int sample8 = ch->chunk.getPCM(cpos) * ch->volume / Mixer::MAX_VOLUME; + const int sample16 = S8_to_S16(sample8); + out[2 * pos] = ADDC_S16(out[2 * pos], sample16); + out[2 * pos + 1] = ADDC_S16(out[2 * pos + 1], sample16); ch->chunkPos += ch->chunkInc; } } } if (kUseNr) { - nr(out, len); + nr(out, len * 2); // stereo } } diff --git a/mixer.h b/mixer.h index 664a7eb..b9cb57d 100644 --- a/mixer.h +++ b/mixer.h @@ -84,7 +84,7 @@ struct Mixer { bool isPlaying(const uint8_t *data) const; uint32_t getSampleRate() const; void stopAll(); - void playMusic(int num); + void playMusic(int num, int tempo = 0); void stopMusic(); void mix(int16_t *buf, int len); diff --git a/mod_player.cpp b/mod_player.cpp index a26b72d..14d40ab 100644 --- a/mod_player.cpp +++ b/mod_player.cpp @@ -16,6 +16,7 @@ struct ModPlayer_impl { ModPlugFile *_mf; ModPlug_Settings _settings; + int _songTempo; bool _repeatIntro; ModPlayer_impl() @@ -26,7 +27,7 @@ struct ModPlayer_impl { memset(&_settings, 0, sizeof(_settings)); ModPlug_GetSettings(&_settings); _settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION; - _settings.mChannels = 1; + _settings.mChannels = 2; _settings.mBits = 16; _settings.mFrequency = rate; _settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; @@ -59,7 +60,7 @@ struct ModPlayer_impl { ModPlug_SeekOrder(_mf, 1); _repeatIntro = false; } - const int count = ModPlug_Read(_mf, buf, len * sizeof(int16_t)); + const int count = ModPlug_Read(_mf, buf, len * sizeof(int16_t) * 2); // setting mLoopCount to non-zero does not trigger any looping in // my test and ModPlug_Read returns 0. // looking at the libmodplug-0.8.8 tarball, it seems the variable @@ -82,6 +83,7 @@ struct ModPlayer_impl { NUM_TRACKS = 4, NUM_PATTERNS = 128, FRAC_BITS = 12, + BASE_TEMPO = 125, PAULA_FREQ = 3546897 }; @@ -234,7 +236,7 @@ bool ModPlayer_impl::load(File *f) { _currentTick = 0; _patternDelay = 0; _songSpeed = 6; - _songTempo = 125; + _songTempo = BASE_TEMPO; _patternLoopPos = 0; _patternLoopCount = -1; _samplesLeft = 0; @@ -506,6 +508,7 @@ void ModPlayer_impl::handleEffect(int trackNum, bool tick) { tk->volume = 0; } } + break; case 0xD: // delay sample if (!tick) { tk->delayCounter = effectY; @@ -616,8 +619,11 @@ void ModPlayer_impl::mixSamples(int16_t *buf, int samplesLen) { curLen = 0; } while (count--) { - const int out = si->getPCM(pos >> FRAC_BITS) * tk->volume / 64; - *mixbuf = ADDC_S16(*mixbuf, S8_to_S16(out)); + const int sample8 = si->getPCM(pos >> FRAC_BITS) * tk->volume / 64; + const int sample16 = S8_to_S16(sample8); + *mixbuf = ADDC_S16(*mixbuf, sample16); + ++mixbuf; + *mixbuf = ADDC_S16(*mixbuf, sample16); ++mixbuf; pos += deltaPos; } @@ -628,9 +634,9 @@ void ModPlayer_impl::mixSamples(int16_t *buf, int samplesLen) { } bool ModPlayer_impl::mix(int16_t *buf, int len) { - memset(buf, 0, sizeof(int16_t) * len); + memset(buf, 0, sizeof(int16_t) * len * 2); // stereo if (_playing) { - const int samplesPerTick = _mixingRate / (50 * _songTempo / 125); + const int samplesPerTick = _mixingRate / (50 * _songTempo / BASE_TEMPO); while (len != 0) { if (_samplesLeft == 0) { handleTick(); @@ -643,7 +649,7 @@ bool ModPlayer_impl::mix(int16_t *buf, int len) { _samplesLeft -= count; len -= count; mixSamples(buf, count); - buf += count; + buf += count * 2; // stereo } } return _playing; @@ -659,20 +665,24 @@ ModPlayer::~ModPlayer() { delete _impl; } -void ModPlayer::play(int num) { - if (num < _modulesFilesCount) { +void ModPlayer::play(int num, int tempo) { + if (num * 2 < _namesCount) { File f; - for (uint8_t i = 0; i < ARRAYSIZE(_modulesFiles[num]); ++i) { - if (f.open(_modulesFiles[num][i], "rb", _fs)) { - _impl->init(_mix->getSampleRate()); - if (_impl->load(&f)) { - _impl->_repeatIntro = (num == 0) && !_isAmiga; - _mix->setPremixHook(mixCallback, _impl); - _playing = true; - } + if (!f.open(_names[num * 2], "rb", _fs)) { + const char *p = _names[num * 2 + 1]; + char name[32]; + snprintf(name, sizeof(name), "mod.flashback-%s", p ? p : _names[num * 2]); + if (!f.open(name, "rb", _fs)) { return; } } + _impl->init(_mix->getSampleRate()); + if (_impl->load(&f)) { + _impl->_songTempo = tempo; + _impl->_repeatIntro = (num == 0) && !_isAmiga; + _mix->setPremixHook(mixCallback, _impl); + _playing = true; + } } } diff --git a/mod_player.h b/mod_player.h index 19b0a15..a24f254 100644 --- a/mod_player.h +++ b/mod_player.h @@ -16,8 +16,8 @@ struct ModPlayer_impl; struct ModPlayer { static const uint16_t _periodTable[]; - static const char *_modulesFiles[][2]; - static const int _modulesFilesCount; + static const char *_names[]; + static const int _namesCount; bool _isAmiga; bool _playing; @@ -28,7 +28,7 @@ struct ModPlayer { ModPlayer(Mixer *mixer, FileSystem *fs); ~ModPlayer(); - void play(int num); + void play(int num, int tempo); void stop(); static bool mixCallback(void *param, int16_t *buf, int len); diff --git a/ogg_player.cpp b/ogg_player.cpp index 7c1ebe9..8b2f4b7 100644 --- a/ogg_player.cpp +++ b/ogg_player.cpp @@ -119,8 +119,9 @@ struct OggDecoder_impl { case 2: assert((len & 3) == 0); for (int i = 0; i < len / 2; i += 2) { - const int16_t s16 = (_readBuf[i] + _readBuf[i + 1]) / 2; - *dst = ADDC_S16(*dst, s16); + *dst = ADDC_S16(*dst, _readBuf[i]); + ++dst; + *dst = ADDC_S16(*dst, _readBuf[i + 1]); ++dst; } break; @@ -128,6 +129,8 @@ struct OggDecoder_impl { for (int i = 0; i < len / 2; ++i) { *dst = ADDC_S16(*dst, _readBuf[i]); ++dst; + *dst = ADDC_S16(*dst, _readBuf[i]); + ++dst; } break; } @@ -187,8 +190,9 @@ struct OggDecoder_impl { if (_decodedSamplesLen != 0) { const int len = MIN(_decodedSamplesLen, samples); for (int i = 0; i < len; ++i) { - const int sample = (_decodedSamples[0][i] + _decodedSamples[1][i]) / 2; - *dst = ADDC_S16(*dst, ((sample * kMusicVolume) >> 8)); + *dst = ADDC_S16(*dst, ((_decodedSamples[0][i] * kMusicVolume) >> 8)); + ++dst; + *dst = ADDC_S16(*dst, ((_decodedSamples[1][i] * kMusicVolume) >> 8)); ++dst; } total += len; @@ -229,8 +233,9 @@ struct OggDecoder_impl { for (int i = 0; i < len; ++i) { const int l = int(outputs[0][i] * 32768 + .5); const int r = int(outputs[1][i] * 32768 + .5); - const int sample = (l + r) / 2; - *dst = ADDC_S16(*dst, ((sample * kMusicVolume) >> 8)); + *dst = ADDC_S16(*dst, ((l * kMusicVolume) >> 8)); + ++dst; + *dst = ADDC_S16(*dst, ((r * kMusicVolume) >> 8)); ++dst; } if (count > remain) { @@ -270,12 +275,15 @@ OggPlayer::~OggPlayer() { _impl = 0; } +// https://www.amigaremix.com/remix/191 +static const char *kMenuThemeRemix = "deadly_cookie_-_flashback.ogg"; + bool OggPlayer::playTrack(int num) { stopTrack(); char buf[16]; snprintf(buf, sizeof(buf), "track%02d.ogg", num); - if (_impl->load(buf, _fs, _mix->getSampleRate())) { - debug(DBG_INFO, "Playing '%s'", buf); + if (_impl->load(buf, _fs, _mix->getSampleRate()) + || (num == 2 && _impl->load(kMenuThemeRemix, _fs, _mix->getSampleRate()))) { _mix->setPremixHook(mixCallback, this); return true; } diff --git a/piege.cpp b/piege.cpp index a353880..c6289ce 100644 --- a/piege.cpp +++ b/piege.cpp @@ -1641,21 +1641,24 @@ int Game::pge_o_unk0x71(ObjectOpcodeArgs *args) { } int Game::pge_o_unk0x72(ObjectOpcodeArgs *args) { - int8_t *var4 = &_res._ctData[0x100] + args->pge->room_location * 0x70; - var4 += (((args->pge->pos_y / 36) & ~1) + args->a) * 16 + (args->pge->pos_x + 8) / 16; + int8_t *grid_data = &_res._ctData[0x100] + args->pge->room_location * 0x70; + int16_t pge_pos_y = ((args->pge->pos_y / 36) & ~1) + args->a; + int16_t pge_pos_x = (args->pge->pos_x + 8) >> 4; + grid_data += pge_pos_y * 16 + pge_pos_x; CollisionSlot2 *_di = _col_slots2Next; - int _cx = 0x100; - while (_di && _cx != 0) { - if (_di->unk2 != var4) { + int count = 256; // ARRAYSIZE(_col_slots2) + while (_di && count != 0) { + if (_di->unk2 != grid_data) { _di = _di->next_slot; - --_cx; + --count; } else { memcpy(_di->unk2, _di->data_buf, _di->data_size + 1); break; } } - return 0xFFFF; // XXX var4; + // original returns the pointer to ctData + return 0xFFFF; } int Game::pge_o_unk0x73(ObjectOpcodeArgs *args) { @@ -2079,7 +2082,21 @@ int Game::pge_updateCollisionState(LivePGE *pge, int16_t pge_dy, uint8_t value) memset(grid_data, value, pge_collision_data_len); return 1; } else { + // the increment looks wrong but matches the DOS disassembly + // + // seg000:667B inc cx + // seg000:667C mov si, bx + // seg000:667E mov bx, [bx+t_collision_slot2.next_slot] + // seg000:6680 loop loc_0_1665B + // + // interestingly Amiga does not have it + // + // CODE:000042BA movea.l a4,a5 + // CODE:000042BC movea.l 0(a4),a4 + // CODE:000042C0 dbf d0,loc_4290 + ++i; + slot1 = slot1->next_slot; if (--i == 0) { break; diff --git a/prf_player.cpp b/prf_player.cpp index a117a34..e11ebd3 100644 --- a/prf_player.cpp +++ b/prf_player.cpp @@ -167,8 +167,10 @@ void PrfPlayer::play() { for (int i = 0; i < _parser._tracksCount; ++i) { PrfTrack *current_track = &_tracks[i]; MidiTrack *track = &_parser._tracks[i]; - MidiEvent ev = track->events.front(); - current_track->counter = ev.timestamp; + 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; @@ -233,7 +235,7 @@ void PrfPlayer::adlibNoteOff(int track, int note, int velocity) { void PrfPlayer::handleTick() { for (int i = 0; i < _parser._tracksCount; ++i) { MidiTrack *track = &_parser._tracks[i]; - if (track->events.size() == 0) { + if (track->endOfTrack) { continue; } const int track_index = i; @@ -242,12 +244,8 @@ void PrfPlayer::handleTick() { --current_track->counter; continue; } -next_event: - MidiEvent ev = track->events.front(); - track->events.pop(); - if (current_track->loop_flag) { - track->events.push(ev); - } + while (1) { + const MidiEvent &ev = track->event; switch (ev.command & 0xF0) { case MIDI_COMMAND_NOTE_OFF: { int note = ev.param1; @@ -358,14 +356,25 @@ next_event: warning("Unhandled MIDI event command 0x%x", ev.command); break; } - if (track->events.size() != 0) { - ev = track->events.front(); - current_track->counter = ev.timestamp; - if (current_track->counter == 0) { - goto next_event; + 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; + } } - --current_track->counter; + 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; + } + } } } @@ -377,19 +386,13 @@ int PrfPlayer::readSamples(int16_t *samples, int count) { const int total = count; while (count != 0) { if (_samplesLeft == 0) { - //++_timerTick; - //if (_timerTick == _prfData.timerTicks) { - //fprintf(stdout, "musicTick #%d of %d\n", _musicTick, _prfData.totalDurationTicks); - handleTick(); - //_timerTick = 0; - if (_prfData.totalDurationTicks != 0) { - ++_musicTick; - if (_musicTick == _prfData.totalDurationTicks + 1) { - debug(DBG_PRF, "End of music"); - //break; - } + 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; @@ -406,16 +409,6 @@ bool PrfPlayer::mixCallback(void *param, int16_t *buf, int len) { } bool PrfPlayer::mix(int16_t *buf, int len) { - int16_t *p = (int16_t *)alloca(len * sizeof(int16_t) * 2); - if (p) { - const int count = readSamples(p, len); - /* stereo to mono */ - for (int i = 0; i < count; ++i) { - const int16_t l = *p++; - const int16_t r = *p++; - buf[i] = CLIP((l + r) / 2, -32768, 32767); - } - return count != 0; - } - return false; + const int count = readSamples(buf, len); + return count != 0; } diff --git a/prf_player.h b/prf_player.h index a6a4333..5317590 100644 --- a/prf_player.h +++ b/prf_player.h @@ -32,6 +32,7 @@ struct PrfData { struct PrfTrack { uint8_t instrument_num; uint32_t counter; + uint32_t counter2; uint8_t hw_channel_num; uint8_t mt32_program_num; uint8_t loop_flag; diff --git a/seq_player.cpp b/seq_player.cpp index be45185..9a00da8 100644 --- a/seq_player.cpp +++ b/seq_player.cpp @@ -334,8 +334,12 @@ bool SeqPlayer::mix(int16_t *buf, int samples) { } while (_soundQueue && samples > 0) { const int count = MIN(samples, _soundQueue->size - _soundQueue->read); - memcpy(buf, _soundQueue->data + _soundQueue->read, count * sizeof(int16_t)); - buf += count; + const int16_t *src = (const int16_t *)(_soundQueue->data + _soundQueue->read); + for (int i = 0; i < count; ++i) { + const int16_t sample = *src++; + *buf++ = sample; + *buf++ = sample; + } _soundQueue->read += count; if (_soundQueue->read == _soundQueue->size) { SoundBufferQueue *next = _soundQueue->next; diff --git a/sfx_player.cpp b/sfx_player.cpp index 34c7312..dcc6070 100644 --- a/sfx_player.cpp +++ b/sfx_player.cpp @@ -13,7 +13,7 @@ static const int kMasterVolume = 64 * 3; // 12 dB/oct Butterworth low-pass filter at 3.3 kHz -static const bool kLowPassFilter = true; +static const bool kLowPassFilter = false; #define NZEROS 2 #define NPOLES 2 @@ -151,8 +151,11 @@ void SfxPlayer::mixSamples(int16_t *buf, int samplesLen) { curLen = 0; } while (count--) { - const int out = si->getPCM(pos >> FRAC_BITS) * si->vol / kMasterVolume; - *mixbuf = ADDC_S16(*mixbuf, S8_to_S16(out)); + const int sample8 = si->getPCM(pos >> FRAC_BITS) * si->vol / kMasterVolume; + const int sample16 = S8_to_S16(sample8); + *mixbuf = ADDC_S16(*mixbuf, sample16); + ++mixbuf; + *mixbuf = ADDC_S16(*mixbuf, sample16); ++mixbuf; pos += deltaPos; } @@ -163,7 +166,7 @@ void SfxPlayer::mixSamples(int16_t *buf, int samplesLen) { } bool SfxPlayer::mix(int16_t *buf, int len) { - memset(buf, 0, sizeof(int16_t) * len); + memset(buf, 0, sizeof(int16_t) * len * 2); // stereo if (_playing) { const int samplesPerTick = _mix->getSampleRate() / 50; while (len != 0) { @@ -179,9 +182,9 @@ bool SfxPlayer::mix(int16_t *buf, int len) { len -= count; mixSamples(buf, count); if (kLowPassFilter) { - butterworth(buf, count); + butterworth(buf, count * 2); // stereo } - buf += count; + buf += count * 2; // stereo } } return _playing; diff --git a/staticres.cpp b/staticres.cpp index b797ea0..3b65525 100644 --- a/staticres.cpp +++ b/staticres.cpp @@ -537,7 +537,7 @@ const uint16_t Cutscene::_creditsCutSeq[] = { 0x00, 0x05, 0x2F, 0x32, 0x36, 0x3E, 0x30, 0x39, 0x3F, 0x14, 0x34, 0xFFFF }; -const uint8_t Cutscene::_musicTable[] = { +const uint8_t Cutscene::_musicTableDOS[] = { 0x10, 0x15, 0x15, 0xFF, 0x15, 0x19, 0x0F, 0xFF, 0x15, 0x04, 0x15, 0xFF, 0xFF, 0x00, 0x19, 0x15, 0x15, 0x0D, 0x15, 0x0D, 0x18, 0x13, 0xFF, 0xFF, 0xFF, 0x14, 0x14, 0x14, 0x14, 0x14, 0xFF, 0xFF, 0x13, 0x13, 0x13, 0x13, 0x15, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x13, 0x13, 0x11, 0xFF, 0x03, @@ -545,6 +545,19 @@ const uint8_t Cutscene::_musicTable[] = { 0x0B, 0x0C, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xFF, 0xFF, 0xFF }; +const uint8_t Cutscene::_musicTableAmiga[] = { + 0x10, 0x7D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x7D, 0xFF, 0xFF, + 0xFF, 0xFF, 0x04, 0x73, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x6B, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x0D, 0x7D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x13, 0x7D, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x13, 0x7D, 0x13, 0x7D, 0x13, 0x7D, 0x13, 0x7D, 0xFF, 0xFF, 0x14, 0x7D, 0x14, 0x7D, 0x14, 0x7D, + 0x14, 0x7D, 0x14, 0x7D, 0x14, 0x7D, 0x13, 0x9B, 0x13, 0x9B, 0x11, 0x7D, 0xFF, 0xFF, 0x03, 0x78, + 0x0E, 0x7D, 0x13, 0x7D, 0x12, 0x6E, 0xFF, 0xFF, 0x06, 0x87, 0x07, 0x7D, 0x0A, 0x7D, 0x0A, 0x7D, + 0xFF, 0xFF, 0x05, 0x7D, 0x13, 0x7D, 0x02, 0x7D, 0xFF, 0xFF, 0x09, 0x7D, 0xFF, 0xFF, 0x08, 0x7D, + 0x0B, 0x7D, 0x0C, 0x7D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x3D, 0x25, 0x27, 0x28 +}; + const uint8_t Cutscene::_protectionShapeData[] = { 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x06, 0xD4, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x08, 0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x28, 0x00, 0x54, 0x00, 0x8C, 0x00, 0xB2, @@ -4419,36 +4432,31 @@ const uint16_t ModPlayer::_periodTable[] = { 216, 203, 192, 181, 171, 161, 152, 144, 136, 128, 121, 114 // C-3 to B-3 Finetune -1 }; -const char *ModPlayer::_modulesFiles[][2] = { - { "intro", "mod.flashback-introb" }, // introl3 - { "options", "mod.flashback-options2" }, // option3 - { "journal", "mod.flashback-options1" }, // journal3 - { "ceinture", "mod.flashback-ceinturea" }, // chute3 - { "desinteg", "mod.flashback-desintegr" }, // desinte3 - { "reunion", "mod.flashback-reunion" }, // capture3 - { "voyage", "mod.flashback-voyage" }, // voyage3 - { "level4", "mod.flashback-teleporta" }, // telepor3 - { "planetexplo", "mod.flashback-teleport2" }, // planexp3 - { "fin", "mod.flashback-fin" }, // end31 - { "ascenseur", "mod.flashback-ascenseur" }, // lift3 - { "logo", "mod.flashback-logo" }, // present3 - { "game_over", "mod.flashback-game_over" }, // gameove3 - { "holocube", "mod.flashback-holocube" }, // holo3 - { "memoire", "mod.flashback-memoire" }, // memory3 - { "chute", "mod.flashback-chute" }, // chutevi3 - { "debut", "mod.flashback-jungle" }, // reveil3 - { "missions", "mod.flashback-missionca" }, // misvali3 - { "taxi", "mod.flashback-taxi" }, // taxi3 - { "donneobj", "mod.flashback-donneobjt" }, // donner3 - { "missions2", "mod.flashback-fin2" } // mission3 -// { 0, 0, }, // objet3 -// { 0, 0, }, // recharg3 -// { 0, 0, }, // generat3 -// { 0, 0, }, // pont3 -// { 0, 0, } // rechage3 +const char *ModPlayer::_names[] = { + "intro", "introb", + "options", "options2", + "journal", "options1", + "ceinture", "ceinturea", + "desinteg", "desintegr", + "reunion", 0, + "voyage", 0, + "level4", "teleporta", + "planetexplo", "teleport2", + "fin", 0, + "ascenseur", 0, + "logo", 0, + "game_over", 0, + "holocube", 0, + "memoire", 0, + "chute", 0, + "debut", "jungle", + "missions", "missionca", + "taxi", 0, + "donneobj", "donneobjt", + "missions2", "fin2", }; -const int ModPlayer::_modulesFilesCount = ARRAYSIZE(_modulesFiles); +const int ModPlayer::_namesCount = ARRAYSIZE(_names); const char *SeqPlayer::_namesTable[] = { /* 0x00 */ diff --git a/systemstub_sdl.cpp b/systemstub_sdl.cpp index b60f8d3..a41e92f 100644 --- a/systemstub_sdl.cpp +++ b/systemstub_sdl.cpp @@ -928,7 +928,8 @@ uint32_t SystemStub_SDL::getTimeStamp() { static void mixAudioS16(void *param, uint8_t *buf, int len) { SystemStub_SDL *stub = (SystemStub_SDL *)param; memset(buf, 0, len); - stub->_audioCbProc(stub->_audioCbData, (int16_t *)buf, len / 2); + assert((len & 3) == 0); + stub->_audioCbProc(stub->_audioCbData, (int16_t *)buf, len / (sizeof(int16_t) * 2)); } void SystemStub_SDL::startAudio(AudioCallback callback, void *param) { @@ -936,7 +937,7 @@ void SystemStub_SDL::startAudio(AudioCallback callback, void *param) { memset(&desired, 0, sizeof(desired)); desired.freq = kAudioHz; desired.format = AUDIO_S16SYS; - desired.channels = 1; + desired.channels = 2; desired.samples = 2048; desired.callback = mixAudioS16; desired.userdata = this; diff --git a/util.h b/util.h index ccca611..c9b14dc 100644 --- a/util.h +++ b/util.h @@ -24,7 +24,8 @@ enum { DBG_SFX = 1 << 11, DBG_FILE = 1 << 12, DBG_DEMO = 1 << 13, - DBG_PRF = 1 << 14 + DBG_PRF = 1 << 14, + DBG_MIDI = 1 << 15 }; extern uint16_t g_debugMask;