Import 0.5.1

This commit is contained in:
Gregory Montoir 2023-04-15 08:44:11 +08:00
parent 1f86fdea2d
commit 100218a3c2
23 changed files with 295 additions and 175 deletions

View File

@ -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 * release 0.5.0
- added CD-i widescreen mode (flashp*bob) - added CD-i widescreen mode (flashp*bob)
- added support for DOS .prf music (Adlib, MT32) - added support for DOS .prf music (Adlib, MT32)

View File

@ -1,6 +1,6 @@
REminiscence README REminiscence README
Release version: 0.5.0 Release version: 0.5.1
------------------------------------------------------------------------------- -------------------------------------------------------------------------------

View File

@ -120,7 +120,8 @@ bool CpcPlayer::mix(int16_t *buf, int len) {
for (int i = 0; i < len; ++i) { for (int i = 0; i < len; ++i) {
_sampleL = decodeSDX2(_sampleL, readSampleData()); _sampleL = decodeSDX2(_sampleL, readSampleData());
_sampleR = decodeSDX2(_sampleR, readSampleData()); _sampleR = decodeSDX2(_sampleR, readSampleData());
*buf++ = (_sampleL + _sampleR) / 2; *buf++ = _sampleL;
*buf++ = _sampleR;
} }
return true; return true;
} }

View File

@ -35,15 +35,16 @@ const uint8_t *Cutscene::getPolygonData() const {
return _res->_pol; return _res->_pol;
} }
void Cutscene::sync() { void Cutscene::sync(int frameDelay) {
if (_stub->_pi.quit) { if (_stub->_pi.quit) {
return; return;
} }
if (_stub->_pi.dbgMask & PlayerInput::DF_FASTMODE) { if (_stub->_pi.dbgMask & PlayerInput::DF_FASTMODE) {
return; return;
} }
static const int frameHz = 60;
const int32_t delay = _stub->getTimeStamp() - _tstamp; 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) { if (pause > 0) {
_stub->sleep(pause); _stub->sleep(pause);
} }
@ -72,7 +73,7 @@ void Cutscene::updatePalette() {
} }
void Cutscene::updateScreen() { void Cutscene::updateScreen() {
sync(); sync(_frameDelay - 1);
updatePalette(); updatePalette();
SWAP(_frontPage, _backPage); SWAP(_frontPage, _backPage);
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _frontPage, _vid->_w); _stub->copyRect(0, 0, _vid->_w, _vid->_h, _frontPage, _vid->_w);
@ -343,7 +344,7 @@ void Cutscene::op_waitForSync() {
_creditsSlowText = false; _creditsSlowText = false;
} else { } else {
_frameDelay = fetchNextCmdByte() * 4; _frameDelay = fetchNextCmdByte() * 4;
sync(); // XXX handle input sync(_frameDelay);
} }
} }
@ -470,7 +471,7 @@ void Cutscene::op_drawCaptionText() {
} else if (_id == kCineEspions) { } else if (_id == kCineEspions) {
// cutscene relies on drawCaptionText opcodes for timing // cutscene relies on drawCaptionText opcodes for timing
_frameDelay = 100; _frameDelay = 100;
sync(); sync(_frameDelay);
} }
} }
} }
@ -1257,7 +1258,7 @@ void Cutscene::playText(const char *str) {
_stub->_pi.backspace = false; _stub->_pi.backspace = false;
break; 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->copyRect(0, 0, _vid->_w, _vid->_h, _backPage, _vid->_w);
_stub->updateScreen(0); _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->sleep((diff < 16) ? 16 : diff);
_stub->processEvents(); _stub->processEvents();
if (_stub->_pi.backspace) { if (_stub->_pi.backspace) {

View File

@ -19,8 +19,7 @@ struct Cutscene {
enum { enum {
MAX_VERTICES = 128, MAX_VERTICES = 128,
NUM_OPCODES = 15, NUM_OPCODES = 15
TIMER_SLICE = 15
}; };
enum { enum {
@ -58,7 +57,8 @@ struct Cutscene {
static const uint8_t _creditsDataDOS[]; static const uint8_t _creditsDataDOS[];
static const uint8_t _creditsDataAmiga[]; static const uint8_t _creditsDataAmiga[];
static const uint16_t _creditsCutSeq[]; 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 uint8_t _protectionShapeData[];
static const Text _frTextsTable[]; static const Text _frTextsTable[];
static const Text _enTextsTable[]; static const Text _enTextsTable[];
@ -122,7 +122,7 @@ struct Cutscene {
const uint8_t *getCommandData() const; const uint8_t *getCommandData() const;
const uint8_t *getPolygonData() const; const uint8_t *getPolygonData() const;
void sync(); void sync(int delay);
void copyPalette(const uint8_t *pal, uint16_t num); void copyPalette(const uint8_t *pal, uint16_t num);
void updatePalette(); void updatePalette();
void updateScreen(); void updateScreen();

View File

@ -133,7 +133,6 @@ void Game::run() {
_skillLevel = _menu._skill; _skillLevel = _menu._skill;
_currentLevel = _menu._level; _currentLevel = _menu._level;
} }
_mix.stopMusic();
break; break;
case kResourceTypeAmiga: case kResourceTypeAmiga:
displayTitleScreenAmiga(); displayTitleScreenAmiga();
@ -143,6 +142,7 @@ void Game::run() {
displayTitleScreenMac(Menu::kMacTitleScreen_Flashback); displayTitleScreenMac(Menu::kMacTitleScreen_Flashback);
break; break;
} }
_mix.stopMusic();
} }
if (_stub->_pi.quit) { if (_stub->_pi.quit) {
break; 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(); _cut.play();
if (id == 0xD && !_cut._interrupted) { if (id == 0xD && !_cut._interrupted) {
if (!_res.isAmiga()) { if (!_res.isAmiga()) {
@ -1713,6 +1724,18 @@ void Game::loadLevelData() {
char name[64]; char name[64];
snprintf(name, sizeof(name), "DUMP/level%d_room%02d.bmp", _currentLevel, i); snprintf(name, sizeof(name), "DUMP/level%d_room%02d.bmp", _currentLevel, i);
saveBMP(name, _vid._backLayer, palette, _vid._w, _vid._h); 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);
} }
} }
} }

View File

@ -52,8 +52,7 @@ inline int16_t S8_to_S16(int a) {
} else if (a > 127) { } else if (a > 127) {
return 32767; return 32767;
} else { } else {
const uint8_t u8 = (a ^ 0x80); return ((uint8_t)a) * 257;
return ((u8 << 8) | u8) - 32768;
} }
} }

View File

@ -260,12 +260,16 @@ int main(int argc, char *argv[]) {
{ LANG_JP, "JP" }, { LANG_JP, "JP" },
{ -1, 0 } { -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) { if (strcasecmp(languages[i].str, optarg) == 0) {
forcedLanguage = languages[i].lang; forcedLanguage = languages[i].lang;
break; break;
} }
} }
if (!languages[i].str) {
warning("Invalid language '%s'", optarg);
}
} }
break; break;
case 7: case 7:
@ -286,12 +290,16 @@ int main(int argc, char *argv[]) {
{ MODE_MT32, "mt32" }, { MODE_MT32, "mt32" },
{ -1, 0 } { -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) { if (strcasecmp(drivers[i].str, optarg) == 0) {
midiDriver = drivers[i].mode; midiDriver = drivers[i].mode;
break; break;
} }
} }
if (!drivers[i].str) {
warning("Invalid MIDI driver '%s'", optarg);
}
} }
break; break;
default: default:

View File

@ -6,10 +6,15 @@
#include "midi_parser.h" #include "midi_parser.h"
#include "util.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; uint32_t value = 0;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
const uint8_t b = f->readByte(); const uint8_t b = data[offset++];
value = (value << 7) | (b & 0x7F); value = (value << 7) | (b & 0x7F);
if ((b & 0x80) == 0) { if ((b & 0x80) == 0) {
break; break;
@ -18,39 +23,42 @@ static uint32_t readVLQ(File *f) {
return value; return value;
} }
static void loadTrk(File *f, int len, MidiTrack &track) { const MidiEvent *MidiTrack::nextEvent() {
const uint32_t end = f->tell() + len; while (offset < size) {
while (f->tell() < end) { MidiEvent &ev = event;
MidiEvent ev; ev.timestamp = readVLQ(data, offset);
ev.timestamp = readVLQ(f); ev.command = data[offset++];
ev.command = f->readByte();
switch (ev.command & 0xF0) { switch (ev.command & 0xF0) {
case MIDI_COMMAND_NOTE_OFF: case MIDI_COMMAND_NOTE_OFF:
case MIDI_COMMAND_NOTE_ON: case MIDI_COMMAND_NOTE_ON:
ev.param1 = f->readByte(); ev.param1 = data[offset++];
ev.param2 = f->readByte(); ev.param2 = data[offset++];
//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); 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; break;
case MIDI_COMMAND_PROGRAM_CHANGE: case MIDI_COMMAND_PROGRAM_CHANGE:
ev.param1 = f->readByte(); ev.param1 = data[offset++];
//fprintf(stdout, "\t Program %d timestamp %d\n", ev.param1, ev.timestamp); debug(DBG_MIDI, "Program %d timestamp %d", ev.param1, ev.timestamp);
break; break;
case MIDI_COMMAND_CHANNEL_PRESSURE: case MIDI_COMMAND_CHANNEL_PRESSURE:
ev.param1 = f->readByte(); ev.param1 = data[offset++];
//fprintf(stdout, "\t Channel pressure %d timestamp %d\n", ev.param1, ev.timestamp); debug(DBG_MIDI, "Channel pressure %d timestamp %d", ev.param1, ev.timestamp);
break; break;
case MIDI_COMMAND_PITCH_BEND: case MIDI_COMMAND_PITCH_BEND:
ev.param1 = f->readByte(); ev.param1 = data[offset++];
ev.param2 = f->readByte(); ev.param2 = data[offset++];
//fprintf(stdout, "\t Pitch Bend %d,%d\n", ev.param1, ev.param2); debug(DBG_MIDI, "Pitch Bend %d,%d", ev.param1, ev.param2);
break; break;
case MIDI_COMMAND_SYSEX: case MIDI_COMMAND_SYSEX:
if (ev.command == 0xFF) { if (ev.command == 0xFF) {
f->readByte(); /* type */ const int num = data[offset++];
const int len = readVLQ(f); const int len = readVLQ(data, offset);
f->seek(f->tell() + len); offset += len;
//fprintf(stdout, "\t SysEx timestamp %d\n", ev.timestamp); debug(DBG_MIDI, "SysEx timestamp %d", ev.timestamp);
assert(ev.timestamp == 0); assert(ev.timestamp == 0);
if (num == MIDI_SYSEX_END_OF_TRACK) {
endOfTrack = true;
return 0;
}
continue; continue;
} }
/* fall-through */ /* fall-through */
@ -58,14 +66,22 @@ static void loadTrk(File *f, int len, MidiTrack &track) {
warning("Unhandled MIDI command 0x%x", ev.command); warning("Unhandled MIDI command 0x%x", ev.command);
break; break;
} }
track.events.push(ev); return &event;
} }
return 0;
}
MidiParser::MidiParser() {
memset(_tracks, 0, sizeof(_tracks));
_tracksCount = 0;
} }
void MidiParser::loadMid(File *f) { void MidiParser::loadMid(File *f) {
for (int i = 0; i < MAX_TRACKS; ++i) { for (int i = 0; i < MAX_TRACKS; ++i) {
_tracks[i].events = std::queue<MidiEvent>(); free(_tracks[i].data);
_tracks[i].data = 0;
} }
memset(_tracks, 0, sizeof(_tracks));
_tracksCount = 0; _tracksCount = 0;
char tag[4]; char tag[4];
f->read(tag, 4); f->read(tag, 4);
@ -73,18 +89,26 @@ void MidiParser::loadMid(File *f) {
const uint32_t len = f->readUint32BE(); const uint32_t len = f->readUint32BE();
assert(len == 6); assert(len == 6);
f->readByte(); f->readByte();
/* const int type = */ f->readByte(); const int type = f->readByte();
const int tracksCount = f->readUint16BE(); const int tracksCount = f->readUint16BE();
assert(tracksCount <= MAX_TRACKS); assert(tracksCount <= MAX_TRACKS);
/* const int ppqn = */ f->readUint16BE(); const int ppqn = f->readUint16BE();
//fprintf(stdout, "MThd type %d tracks %d ppqn %d len %d\n", type, tracksCount, ppqn, len); debug(DBG_MIDI, "MThd type %d tracks %d ppqn %d len %d", type, tracksCount, ppqn, len);
for (int i = 0; i < tracksCount; ++i) { for (int i = 0; i < tracksCount; ++i) {
f->read(tag, 4); f->read(tag, 4);
assert(memcmp(tag, "MTrk", 4) == 0); assert(memcmp(tag, "MTrk", 4) == 0);
const uint32_t len = f->readUint32BE(); const uint32_t len = f->readUint32BE();
//fprintf(stdout, "Track #%d len %d\n", i, len); debug(DBG_MIDI, "Track #%d len %d", i, len);
loadTrk(f, len, _tracks[i]); MidiTrack *track = &_tracks[i];
//fprintf(stdout, "Track #%d events %ld\n", i, track.events.size()); 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; _tracksCount = tracksCount;
} }

View File

@ -2,7 +2,6 @@
#ifndef MIDI_PARSER_H__ #ifndef MIDI_PARSER_H__
#define MIDI_PARSER_H__ #define MIDI_PARSER_H__
#include <queue>
#include <stdint.h> #include <stdint.h>
#define MIDI_COMMAND_NOTE_OFF 0x80 #define MIDI_COMMAND_NOTE_OFF 0x80
@ -12,6 +11,8 @@
#define MIDI_COMMAND_PITCH_BEND 0xE0 #define MIDI_COMMAND_PITCH_BEND 0xE0
#define MIDI_COMMAND_SYSEX 0xF0 /* unused */ #define MIDI_COMMAND_SYSEX 0xF0 /* unused */
#define MIDI_SYSEX_END_OF_TRACK 0x2F
struct MidiEvent { struct MidiEvent {
uint32_t timestamp; uint32_t timestamp;
uint8_t command; uint8_t command;
@ -20,7 +21,13 @@ struct MidiEvent {
}; };
struct MidiTrack { struct MidiTrack {
std::queue<MidiEvent> events; bool endOfTrack;
uint8_t *data;
uint32_t offset, size;
MidiEvent event;
void rewind();
const MidiEvent *nextEvent();
}; };
struct File; struct File;
@ -28,6 +35,9 @@ struct File;
#define MAX_TRACKS 16 #define MAX_TRACKS 16
struct MidiParser { struct MidiParser {
MidiParser();
void loadMid(File *f); void loadMid(File *f);
int _tracksCount; int _tracksCount;

View File

@ -90,8 +90,8 @@ static bool isMusicSfx(int num) {
return (num >= 68 && num <= 75); return (num >= 68 && num <= 75);
} }
void Mixer::playMusic(int num) { void Mixer::playMusic(int num, int tempo) {
debug(DBG_SND, "Mixer::playMusic(%d)", num); debug(DBG_SND, "Mixer::playMusic(%d, %d)", num, tempo);
int trackNum = -1; int trackNum = -1;
if (num == 1) { // menu screen if (num == 1) { // menu screen
trackNum = 2; trackNum = 2;
@ -119,7 +119,7 @@ void Mixer::playMusic(int num) {
_musicType = MT_SFX; _musicType = MT_SFX;
} }
} else { // cutscene } else { // cutscene
_mod.play(num); _mod.play(num, tempo);
if (_mod._playing) { if (_mod._playing) {
_musicType = MT_MOD; _musicType = MT_MOD;
return; return;
@ -156,7 +156,7 @@ void Mixer::stopMusic() {
break; break;
} }
_musicType = MT_NONE; _musicType = MT_NONE;
if (_musicTrack != -1) { if (_musicTrack > 2) { // do not resume menu music
switch (_backgroundMusicType) { switch (_backgroundMusicType) {
case MT_OGG: case MT_OGG:
_ogg.resumeTrack(); _ogg.resumeTrack();
@ -194,18 +194,21 @@ void Mixer::mix(int16_t *out, int len) {
MixerChannel *ch = &_channels[i]; MixerChannel *ch = &_channels[i];
if (ch->active) { if (ch->active) {
for (int pos = 0; pos < len; ++pos) { 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; ch->active = false;
break; break;
} }
const int sample = ch->chunk.getPCM(ch->chunkPos >> FRAC_BITS) * ch->volume / Mixer::MAX_VOLUME; const int sample8 = ch->chunk.getPCM(cpos) * ch->volume / Mixer::MAX_VOLUME;
out[pos] = ADDC_S16(out[pos], S8_to_S16(sample)); 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; ch->chunkPos += ch->chunkInc;
} }
} }
} }
if (kUseNr) { if (kUseNr) {
nr(out, len); nr(out, len * 2); // stereo
} }
} }

View File

@ -84,7 +84,7 @@ struct Mixer {
bool isPlaying(const uint8_t *data) const; bool isPlaying(const uint8_t *data) const;
uint32_t getSampleRate() const; uint32_t getSampleRate() const;
void stopAll(); void stopAll();
void playMusic(int num); void playMusic(int num, int tempo = 0);
void stopMusic(); void stopMusic();
void mix(int16_t *buf, int len); void mix(int16_t *buf, int len);

View File

@ -16,6 +16,7 @@ struct ModPlayer_impl {
ModPlugFile *_mf; ModPlugFile *_mf;
ModPlug_Settings _settings; ModPlug_Settings _settings;
int _songTempo;
bool _repeatIntro; bool _repeatIntro;
ModPlayer_impl() ModPlayer_impl()
@ -26,7 +27,7 @@ struct ModPlayer_impl {
memset(&_settings, 0, sizeof(_settings)); memset(&_settings, 0, sizeof(_settings));
ModPlug_GetSettings(&_settings); ModPlug_GetSettings(&_settings);
_settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION; _settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION;
_settings.mChannels = 1; _settings.mChannels = 2;
_settings.mBits = 16; _settings.mBits = 16;
_settings.mFrequency = rate; _settings.mFrequency = rate;
_settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; _settings.mResamplingMode = MODPLUG_RESAMPLE_FIR;
@ -59,7 +60,7 @@ struct ModPlayer_impl {
ModPlug_SeekOrder(_mf, 1); ModPlug_SeekOrder(_mf, 1);
_repeatIntro = false; _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 // setting mLoopCount to non-zero does not trigger any looping in
// my test and ModPlug_Read returns 0. // my test and ModPlug_Read returns 0.
// looking at the libmodplug-0.8.8 tarball, it seems the variable // looking at the libmodplug-0.8.8 tarball, it seems the variable
@ -82,6 +83,7 @@ struct ModPlayer_impl {
NUM_TRACKS = 4, NUM_TRACKS = 4,
NUM_PATTERNS = 128, NUM_PATTERNS = 128,
FRAC_BITS = 12, FRAC_BITS = 12,
BASE_TEMPO = 125,
PAULA_FREQ = 3546897 PAULA_FREQ = 3546897
}; };
@ -234,7 +236,7 @@ bool ModPlayer_impl::load(File *f) {
_currentTick = 0; _currentTick = 0;
_patternDelay = 0; _patternDelay = 0;
_songSpeed = 6; _songSpeed = 6;
_songTempo = 125; _songTempo = BASE_TEMPO;
_patternLoopPos = 0; _patternLoopPos = 0;
_patternLoopCount = -1; _patternLoopCount = -1;
_samplesLeft = 0; _samplesLeft = 0;
@ -506,6 +508,7 @@ void ModPlayer_impl::handleEffect(int trackNum, bool tick) {
tk->volume = 0; tk->volume = 0;
} }
} }
break;
case 0xD: // delay sample case 0xD: // delay sample
if (!tick) { if (!tick) {
tk->delayCounter = effectY; tk->delayCounter = effectY;
@ -616,8 +619,11 @@ void ModPlayer_impl::mixSamples(int16_t *buf, int samplesLen) {
curLen = 0; curLen = 0;
} }
while (count--) { while (count--) {
const int out = si->getPCM(pos >> FRAC_BITS) * tk->volume / 64; const int sample8 = si->getPCM(pos >> FRAC_BITS) * tk->volume / 64;
*mixbuf = ADDC_S16(*mixbuf, S8_to_S16(out)); const int sample16 = S8_to_S16(sample8);
*mixbuf = ADDC_S16(*mixbuf, sample16);
++mixbuf;
*mixbuf = ADDC_S16(*mixbuf, sample16);
++mixbuf; ++mixbuf;
pos += deltaPos; pos += deltaPos;
} }
@ -628,9 +634,9 @@ void ModPlayer_impl::mixSamples(int16_t *buf, int samplesLen) {
} }
bool ModPlayer_impl::mix(int16_t *buf, int len) { 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) { if (_playing) {
const int samplesPerTick = _mixingRate / (50 * _songTempo / 125); const int samplesPerTick = _mixingRate / (50 * _songTempo / BASE_TEMPO);
while (len != 0) { while (len != 0) {
if (_samplesLeft == 0) { if (_samplesLeft == 0) {
handleTick(); handleTick();
@ -643,7 +649,7 @@ bool ModPlayer_impl::mix(int16_t *buf, int len) {
_samplesLeft -= count; _samplesLeft -= count;
len -= count; len -= count;
mixSamples(buf, count); mixSamples(buf, count);
buf += count; buf += count * 2; // stereo
} }
} }
return _playing; return _playing;
@ -659,20 +665,24 @@ ModPlayer::~ModPlayer() {
delete _impl; delete _impl;
} }
void ModPlayer::play(int num) { void ModPlayer::play(int num, int tempo) {
if (num < _modulesFilesCount) { if (num * 2 < _namesCount) {
File f; File f;
for (uint8_t i = 0; i < ARRAYSIZE(_modulesFiles[num]); ++i) { if (!f.open(_names[num * 2], "rb", _fs)) {
if (f.open(_modulesFiles[num][i], "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()); _impl->init(_mix->getSampleRate());
if (_impl->load(&f)) { if (_impl->load(&f)) {
_impl->_songTempo = tempo;
_impl->_repeatIntro = (num == 0) && !_isAmiga; _impl->_repeatIntro = (num == 0) && !_isAmiga;
_mix->setPremixHook(mixCallback, _impl); _mix->setPremixHook(mixCallback, _impl);
_playing = true; _playing = true;
} }
return;
}
}
} }
} }

View File

@ -16,8 +16,8 @@ struct ModPlayer_impl;
struct ModPlayer { struct ModPlayer {
static const uint16_t _periodTable[]; static const uint16_t _periodTable[];
static const char *_modulesFiles[][2]; static const char *_names[];
static const int _modulesFilesCount; static const int _namesCount;
bool _isAmiga; bool _isAmiga;
bool _playing; bool _playing;
@ -28,7 +28,7 @@ struct ModPlayer {
ModPlayer(Mixer *mixer, FileSystem *fs); ModPlayer(Mixer *mixer, FileSystem *fs);
~ModPlayer(); ~ModPlayer();
void play(int num); void play(int num, int tempo);
void stop(); void stop();
static bool mixCallback(void *param, int16_t *buf, int len); static bool mixCallback(void *param, int16_t *buf, int len);

View File

@ -119,8 +119,9 @@ struct OggDecoder_impl {
case 2: case 2:
assert((len & 3) == 0); assert((len & 3) == 0);
for (int i = 0; i < len / 2; i += 2) { for (int i = 0; i < len / 2; i += 2) {
const int16_t s16 = (_readBuf[i] + _readBuf[i + 1]) / 2; *dst = ADDC_S16(*dst, _readBuf[i]);
*dst = ADDC_S16(*dst, s16); ++dst;
*dst = ADDC_S16(*dst, _readBuf[i + 1]);
++dst; ++dst;
} }
break; break;
@ -128,6 +129,8 @@ struct OggDecoder_impl {
for (int i = 0; i < len / 2; ++i) { for (int i = 0; i < len / 2; ++i) {
*dst = ADDC_S16(*dst, _readBuf[i]); *dst = ADDC_S16(*dst, _readBuf[i]);
++dst; ++dst;
*dst = ADDC_S16(*dst, _readBuf[i]);
++dst;
} }
break; break;
} }
@ -187,8 +190,9 @@ struct OggDecoder_impl {
if (_decodedSamplesLen != 0) { if (_decodedSamplesLen != 0) {
const int len = MIN(_decodedSamplesLen, samples); const int len = MIN(_decodedSamplesLen, samples);
for (int i = 0; i < len; ++i) { for (int i = 0; i < len; ++i) {
const int sample = (_decodedSamples[0][i] + _decodedSamples[1][i]) / 2; *dst = ADDC_S16(*dst, ((_decodedSamples[0][i] * kMusicVolume) >> 8));
*dst = ADDC_S16(*dst, ((sample * kMusicVolume) >> 8)); ++dst;
*dst = ADDC_S16(*dst, ((_decodedSamples[1][i] * kMusicVolume) >> 8));
++dst; ++dst;
} }
total += len; total += len;
@ -229,8 +233,9 @@ struct OggDecoder_impl {
for (int i = 0; i < len; ++i) { for (int i = 0; i < len; ++i) {
const int l = int(outputs[0][i] * 32768 + .5); const int l = int(outputs[0][i] * 32768 + .5);
const int r = int(outputs[1][i] * 32768 + .5); const int r = int(outputs[1][i] * 32768 + .5);
const int sample = (l + r) / 2; *dst = ADDC_S16(*dst, ((l * kMusicVolume) >> 8));
*dst = ADDC_S16(*dst, ((sample * kMusicVolume) >> 8)); ++dst;
*dst = ADDC_S16(*dst, ((r * kMusicVolume) >> 8));
++dst; ++dst;
} }
if (count > remain) { if (count > remain) {
@ -270,12 +275,15 @@ OggPlayer::~OggPlayer() {
_impl = 0; _impl = 0;
} }
// https://www.amigaremix.com/remix/191
static const char *kMenuThemeRemix = "deadly_cookie_-_flashback.ogg";
bool OggPlayer::playTrack(int num) { bool OggPlayer::playTrack(int num) {
stopTrack(); stopTrack();
char buf[16]; char buf[16];
snprintf(buf, sizeof(buf), "track%02d.ogg", num); snprintf(buf, sizeof(buf), "track%02d.ogg", num);
if (_impl->load(buf, _fs, _mix->getSampleRate())) { if (_impl->load(buf, _fs, _mix->getSampleRate())
debug(DBG_INFO, "Playing '%s'", buf); || (num == 2 && _impl->load(kMenuThemeRemix, _fs, _mix->getSampleRate()))) {
_mix->setPremixHook(mixCallback, this); _mix->setPremixHook(mixCallback, this);
return true; return true;
} }

View File

@ -1641,21 +1641,24 @@ int Game::pge_o_unk0x71(ObjectOpcodeArgs *args) {
} }
int Game::pge_o_unk0x72(ObjectOpcodeArgs *args) { int Game::pge_o_unk0x72(ObjectOpcodeArgs *args) {
int8_t *var4 = &_res._ctData[0x100] + args->pge->room_location * 0x70; int8_t *grid_data = &_res._ctData[0x100] + args->pge->room_location * 0x70;
var4 += (((args->pge->pos_y / 36) & ~1) + args->a) * 16 + (args->pge->pos_x + 8) / 16; 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; CollisionSlot2 *_di = _col_slots2Next;
int _cx = 0x100; int count = 256; // ARRAYSIZE(_col_slots2)
while (_di && _cx != 0) { while (_di && count != 0) {
if (_di->unk2 != var4) { if (_di->unk2 != grid_data) {
_di = _di->next_slot; _di = _di->next_slot;
--_cx; --count;
} else { } else {
memcpy(_di->unk2, _di->data_buf, _di->data_size + 1); memcpy(_di->unk2, _di->data_buf, _di->data_size + 1);
break; break;
} }
} }
return 0xFFFF; // XXX var4; // original returns the pointer to ctData
return 0xFFFF;
} }
int Game::pge_o_unk0x73(ObjectOpcodeArgs *args) { 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); memset(grid_data, value, pge_collision_data_len);
return 1; return 1;
} else { } 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; ++i;
slot1 = slot1->next_slot; slot1 = slot1->next_slot;
if (--i == 0) { if (--i == 0) {
break; break;

View File

@ -167,8 +167,10 @@ void PrfPlayer::play() {
for (int i = 0; i < _parser._tracksCount; ++i) { for (int i = 0; i < _parser._tracksCount; ++i) {
PrfTrack *current_track = &_tracks[i]; PrfTrack *current_track = &_tracks[i];
MidiTrack *track = &_parser._tracks[i]; MidiTrack *track = &_parser._tracks[i];
MidiEvent ev = track->events.front(); track->rewind();
current_track->counter = ev.timestamp; const MidiEvent *ev = track->nextEvent();
current_track->counter = ev ? ev->timestamp : 0;
current_track->counter2 = current_track->counter % _prfData.timerMod;
} }
_timerTick = _musicTick = 0; _timerTick = _musicTick = 0;
_samplesLeft = 0; _samplesLeft = 0;
@ -233,7 +235,7 @@ void PrfPlayer::adlibNoteOff(int track, int note, int velocity) {
void PrfPlayer::handleTick() { void PrfPlayer::handleTick() {
for (int i = 0; i < _parser._tracksCount; ++i) { for (int i = 0; i < _parser._tracksCount; ++i) {
MidiTrack *track = &_parser._tracks[i]; MidiTrack *track = &_parser._tracks[i];
if (track->events.size() == 0) { if (track->endOfTrack) {
continue; continue;
} }
const int track_index = i; const int track_index = i;
@ -242,12 +244,8 @@ void PrfPlayer::handleTick() {
--current_track->counter; --current_track->counter;
continue; continue;
} }
next_event: while (1) {
MidiEvent ev = track->events.front(); const MidiEvent &ev = track->event;
track->events.pop();
if (current_track->loop_flag) {
track->events.push(ev);
}
switch (ev.command & 0xF0) { switch (ev.command & 0xF0) {
case MIDI_COMMAND_NOTE_OFF: { case MIDI_COMMAND_NOTE_OFF: {
int note = ev.param1; int note = ev.param1;
@ -358,13 +356,24 @@ next_event:
warning("Unhandled MIDI event command 0x%x", ev.command); warning("Unhandled MIDI event command 0x%x", ev.command);
break; break;
} }
if (track->events.size() != 0) { const MidiEvent *next = track->nextEvent();
ev = track->events.front(); if (!next) {
current_track->counter = ev.timestamp; if (current_track->loop_flag == 0) {
if (current_track->counter == 0) { track->rewind();
goto next_event; 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; --current_track->counter;
break;
}
} }
} }
} }
@ -377,19 +386,13 @@ int PrfPlayer::readSamples(int16_t *samples, int count) {
const int total = count; const int total = count;
while (count != 0) { while (count != 0) {
if (_samplesLeft == 0) { if (_samplesLeft == 0) {
//++_timerTick;
//if (_timerTick == _prfData.timerTicks) {
//fprintf(stdout, "musicTick #%d of %d\n", _musicTick, _prfData.totalDurationTicks);
handleTick(); handleTick();
//_timerTick = 0;
if (_prfData.totalDurationTicks != 0) { if (_prfData.totalDurationTicks != 0) {
++_musicTick; ++_musicTick;
if (_musicTick == _prfData.totalDurationTicks + 1) { if (_musicTick == _prfData.totalDurationTicks + 1) {
debug(DBG_PRF, "End of music"); debug(DBG_PRF, "End of music");
//break;
} }
} }
//}
_samplesLeft = _samplesPerTick * _prfData.timerTicks; _samplesLeft = _samplesPerTick * _prfData.timerTicks;
} }
const int len = (count < _samplesLeft) ? count : _samplesLeft; 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) { bool PrfPlayer::mix(int16_t *buf, int len) {
int16_t *p = (int16_t *)alloca(len * sizeof(int16_t) * 2); const int count = readSamples(buf, len);
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 count != 0;
} }
return false;
}

View File

@ -32,6 +32,7 @@ struct PrfData {
struct PrfTrack { struct PrfTrack {
uint8_t instrument_num; uint8_t instrument_num;
uint32_t counter; uint32_t counter;
uint32_t counter2;
uint8_t hw_channel_num; uint8_t hw_channel_num;
uint8_t mt32_program_num; uint8_t mt32_program_num;
uint8_t loop_flag; uint8_t loop_flag;

View File

@ -334,8 +334,12 @@ bool SeqPlayer::mix(int16_t *buf, int samples) {
} }
while (_soundQueue && samples > 0) { while (_soundQueue && samples > 0) {
const int count = MIN(samples, _soundQueue->size - _soundQueue->read); const int count = MIN(samples, _soundQueue->size - _soundQueue->read);
memcpy(buf, _soundQueue->data + _soundQueue->read, count * sizeof(int16_t)); const int16_t *src = (const int16_t *)(_soundQueue->data + _soundQueue->read);
buf += count; for (int i = 0; i < count; ++i) {
const int16_t sample = *src++;
*buf++ = sample;
*buf++ = sample;
}
_soundQueue->read += count; _soundQueue->read += count;
if (_soundQueue->read == _soundQueue->size) { if (_soundQueue->read == _soundQueue->size) {
SoundBufferQueue *next = _soundQueue->next; SoundBufferQueue *next = _soundQueue->next;

View File

@ -13,7 +13,7 @@
static const int kMasterVolume = 64 * 3; static const int kMasterVolume = 64 * 3;
// 12 dB/oct Butterworth low-pass filter at 3.3 kHz // 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 NZEROS 2
#define NPOLES 2 #define NPOLES 2
@ -151,8 +151,11 @@ void SfxPlayer::mixSamples(int16_t *buf, int samplesLen) {
curLen = 0; curLen = 0;
} }
while (count--) { while (count--) {
const int out = si->getPCM(pos >> FRAC_BITS) * si->vol / kMasterVolume; const int sample8 = si->getPCM(pos >> FRAC_BITS) * si->vol / kMasterVolume;
*mixbuf = ADDC_S16(*mixbuf, S8_to_S16(out)); const int sample16 = S8_to_S16(sample8);
*mixbuf = ADDC_S16(*mixbuf, sample16);
++mixbuf;
*mixbuf = ADDC_S16(*mixbuf, sample16);
++mixbuf; ++mixbuf;
pos += deltaPos; pos += deltaPos;
} }
@ -163,7 +166,7 @@ void SfxPlayer::mixSamples(int16_t *buf, int samplesLen) {
} }
bool SfxPlayer::mix(int16_t *buf, int len) { 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) { if (_playing) {
const int samplesPerTick = _mix->getSampleRate() / 50; const int samplesPerTick = _mix->getSampleRate() / 50;
while (len != 0) { while (len != 0) {
@ -179,9 +182,9 @@ bool SfxPlayer::mix(int16_t *buf, int len) {
len -= count; len -= count;
mixSamples(buf, count); mixSamples(buf, count);
if (kLowPassFilter) { if (kLowPassFilter) {
butterworth(buf, count); butterworth(buf, count * 2); // stereo
} }
buf += count; buf += count * 2; // stereo
} }
} }
return _playing; return _playing;

View File

@ -537,7 +537,7 @@ const uint16_t Cutscene::_creditsCutSeq[] = {
0x00, 0x05, 0x2F, 0x32, 0x36, 0x3E, 0x30, 0x39, 0x3F, 0x14, 0x34, 0xFFFF 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, 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, 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, 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 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[] = { const uint8_t Cutscene::_protectionShapeData[] = {
0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x06, 0xD4, 0x00, 0x00, 0x00, 0x92, 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, 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 216, 203, 192, 181, 171, 161, 152, 144, 136, 128, 121, 114 // C-3 to B-3 Finetune -1
}; };
const char *ModPlayer::_modulesFiles[][2] = { const char *ModPlayer::_names[] = {
{ "intro", "mod.flashback-introb" }, // introl3 "intro", "introb",
{ "options", "mod.flashback-options2" }, // option3 "options", "options2",
{ "journal", "mod.flashback-options1" }, // journal3 "journal", "options1",
{ "ceinture", "mod.flashback-ceinturea" }, // chute3 "ceinture", "ceinturea",
{ "desinteg", "mod.flashback-desintegr" }, // desinte3 "desinteg", "desintegr",
{ "reunion", "mod.flashback-reunion" }, // capture3 "reunion", 0,
{ "voyage", "mod.flashback-voyage" }, // voyage3 "voyage", 0,
{ "level4", "mod.flashback-teleporta" }, // telepor3 "level4", "teleporta",
{ "planetexplo", "mod.flashback-teleport2" }, // planexp3 "planetexplo", "teleport2",
{ "fin", "mod.flashback-fin" }, // end31 "fin", 0,
{ "ascenseur", "mod.flashback-ascenseur" }, // lift3 "ascenseur", 0,
{ "logo", "mod.flashback-logo" }, // present3 "logo", 0,
{ "game_over", "mod.flashback-game_over" }, // gameove3 "game_over", 0,
{ "holocube", "mod.flashback-holocube" }, // holo3 "holocube", 0,
{ "memoire", "mod.flashback-memoire" }, // memory3 "memoire", 0,
{ "chute", "mod.flashback-chute" }, // chutevi3 "chute", 0,
{ "debut", "mod.flashback-jungle" }, // reveil3 "debut", "jungle",
{ "missions", "mod.flashback-missionca" }, // misvali3 "missions", "missionca",
{ "taxi", "mod.flashback-taxi" }, // taxi3 "taxi", 0,
{ "donneobj", "mod.flashback-donneobjt" }, // donner3 "donneobj", "donneobjt",
{ "missions2", "mod.flashback-fin2" } // mission3 "missions2", "fin2",
// { 0, 0, }, // objet3
// { 0, 0, }, // recharg3
// { 0, 0, }, // generat3
// { 0, 0, }, // pont3
// { 0, 0, } // rechage3
}; };
const int ModPlayer::_modulesFilesCount = ARRAYSIZE(_modulesFiles); const int ModPlayer::_namesCount = ARRAYSIZE(_names);
const char *SeqPlayer::_namesTable[] = { const char *SeqPlayer::_namesTable[] = {
/* 0x00 */ /* 0x00 */

View File

@ -928,7 +928,8 @@ uint32_t SystemStub_SDL::getTimeStamp() {
static void mixAudioS16(void *param, uint8_t *buf, int len) { static void mixAudioS16(void *param, uint8_t *buf, int len) {
SystemStub_SDL *stub = (SystemStub_SDL *)param; SystemStub_SDL *stub = (SystemStub_SDL *)param;
memset(buf, 0, len); 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) { 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)); memset(&desired, 0, sizeof(desired));
desired.freq = kAudioHz; desired.freq = kAudioHz;
desired.format = AUDIO_S16SYS; desired.format = AUDIO_S16SYS;
desired.channels = 1; desired.channels = 2;
desired.samples = 2048; desired.samples = 2048;
desired.callback = mixAudioS16; desired.callback = mixAudioS16;
desired.userdata = this; desired.userdata = this;

3
util.h
View File

@ -24,7 +24,8 @@ enum {
DBG_SFX = 1 << 11, DBG_SFX = 1 << 11,
DBG_FILE = 1 << 12, DBG_FILE = 1 << 12,
DBG_DEMO = 1 << 13, DBG_DEMO = 1 << 13,
DBG_PRF = 1 << 14 DBG_PRF = 1 << 14,
DBG_MIDI = 1 << 15
}; };
extern uint16_t g_debugMask; extern uint16_t g_debugMask;