[Scummvm-git-logs] scummvm master -> cf5b500874ae6e3828d90f0801d6dcbc9a92eeed
NMIError
noreply at scummvm.org
Thu Jun 9 15:14:22 UTC 2022
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
caca2ea61f AUDIO: Make subclassing Module and ProtrackerStream easier
cf5b500874 CHEWY: Move TMF loading code to Stream subclass
Commit: caca2ea61f70013dd9ac9337777546d2978971ab
https://github.com/scummvm/scummvm/commit/caca2ea61f70013dd9ac9337777546d2978971ab
Author: Coen Rampen (crampen at gmail.com)
Date: 2022-06-09T17:13:56+02:00
Commit Message:
AUDIO: Make subclassing Module and ProtrackerStream easier
To implement a MOD variant for the Chewy engine, this commit makes some changes
to make subclassing of the ProtrackerStream and associated Module class easier.
Changed paths:
audio/mods/module.h
audio/mods/paula.cpp
audio/mods/paula.h
audio/mods/protracker.cpp
audio/mods/protracker.h
diff --git a/audio/mods/module.h b/audio/mods/module.h
index b08aca7b016..47e11e5f5f0 100644
--- a/audio/mods/module.h
+++ b/audio/mods/module.h
@@ -76,11 +76,11 @@ public:
Module();
~Module();
- bool load(Common::SeekableReadStream &stream, int offs);
+ virtual bool load(Common::SeekableReadStream &stream, int offs);
static byte periodToNote(int16 period, byte finetune = 0);
static int16 noteToPeriod(byte note, byte finetune = 0);
-private:
+protected:
static const int16 periods[16][60];
static const uint32 signatures[];
};
diff --git a/audio/mods/paula.cpp b/audio/mods/paula.cpp
index 01dae134d4d..371b52a5724 100644
--- a/audio/mods/paula.cpp
+++ b/audio/mods/paula.cpp
@@ -52,10 +52,10 @@ Paula::Paula(bool stereo, int rate, uint interruptFreq, FilterMode filterMode, i
_filterState.a0[2] = filterCalculateA0(rate, 7000);
clearVoices();
- _voice[0].panning = 191;
- _voice[1].panning = 63;
- _voice[2].panning = 63;
- _voice[3].panning = 191;
+ _voice[0].panning = PANNING_RIGHT;
+ _voice[1].panning = PANNING_LEFT;
+ _voice[2].panning = PANNING_LEFT;
+ _voice[3].panning = PANNING_RIGHT;
if (_intFreq == 0)
_intFreq = _rate;
diff --git a/audio/mods/paula.h b/audio/mods/paula.h
index a7b7b920725..f3ac433f1e2 100644
--- a/audio/mods/paula.h
+++ b/audio/mods/paula.h
@@ -36,6 +36,10 @@ namespace Audio {
class Paula : public AudioStream {
public:
static const int NUM_VOICES = 4;
+ // Default panning value for left channels.
+ static const int PANNING_LEFT = 63;
+ // Default panning value for right channels.
+ static const int PANNING_RIGHT = 191;
enum {
kPalSystemClock = 7093790,
kNtscSystemClock = 7159090,
diff --git a/audio/mods/protracker.cpp b/audio/mods/protracker.cpp
index c79fbd9f141..70928d21fa8 100644
--- a/audio/mods/protracker.cpp
+++ b/audio/mods/protracker.cpp
@@ -28,114 +28,6 @@
namespace Modules {
-class ProtrackerStream : public ::Audio::Paula {
-private:
- Module _module;
-
- int _tick;
- int _row;
- int _pos;
-
- int _speed;
- int _bpm;
-
- // For effect 0xB - Jump To Pattern;
- bool _hasJumpToPattern;
- int _jumpToPattern;
-
- // For effect 0xD - PatternBreak;
- bool _hasPatternBreak;
- int _skipRow;
-
- // For effect 0xE6 - Pattern Loop
- bool _hasPatternLoop;
- int _patternLoopCount;
- int _patternLoopRow;
-
- // For effect 0xEE - Pattern Delay
- byte _patternDelay;
-
- static const int16 sinetable[];
-
- struct Track {
- byte sample;
- byte lastSample;
- uint16 period;
- Offset offset;
-
- byte vol;
- byte finetune;
-
- // For effect 0x0 - Arpeggio
- bool arpeggio;
- byte arpeggioNotes[3];
-
- // For effect 0x3 - Porta to note
- uint16 portaToNote;
- byte portaToNoteSpeed;
-
- // For effect 0x4 - Vibrato
- int vibrato;
- byte vibratoPos;
- byte vibratoSpeed;
- byte vibratoDepth;
-
- // For effect 0xED - Delay sample
- byte delaySample;
- byte delaySampleTick;
- } _track[4];
-
-public:
- ProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo);
-
- Modules::Module *getModule() {
- // Ordinarily, the Module is not meant to be seen outside of
- // this class, but occasionally, it's useful to be able to
- // manipulate it directly. The Hopkins engine uses this to
- // repair a broken song.
- return &_module;
- }
-
-private:
- void interrupt() override;
-
- void doPorta(int track) {
- if (_track[track].portaToNote && _track[track].portaToNoteSpeed) {
- int distance = _track[track].period - _track[track].portaToNote;
- int sign = distance > 0 ? 1 : -1;
-
- if ((sign * distance) > _track[track].portaToNoteSpeed)
- _track[track].period -= sign * _track[track].portaToNoteSpeed;
- else
- _track[track].period = _track[track].portaToNote;
- }
- }
- void doVibrato(int track) {
- _track[track].vibrato =
- (_track[track].vibratoDepth * sinetable[_track[track].vibratoPos]) / 128;
- _track[track].vibratoPos += _track[track].vibratoSpeed;
- _track[track].vibratoPos %= 64;
- }
- void doVolSlide(int track, byte ex, byte ey) {
- int vol = _track[track].vol;
- if (ex == 0)
- vol -= ey;
- else if (ey == 0)
- vol += ex;
-
- if (vol < 0)
- vol = 0;
- else if (vol > 64)
- vol = 64;
-
- _track[track].vol = vol;
- }
-
- void updateRow();
- void updateEffects();
-
-};
-
const int16 ProtrackerStream::sinetable[64] = {
0, 24, 49, 74, 97, 120, 141, 161,
180, 197, 212, 224, 235, 244, 250, 253,
@@ -147,10 +39,8 @@ const int16 ProtrackerStream::sinetable[64] = {
-180, -161, -141, -120, -97, -74, -49, -24
};
-ProtrackerStream::ProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo) :
- Paula(stereo, rate, rate/50) {
- bool result = _module.load(*stream, offs);
- assert(result);
+ProtrackerStream::ProtrackerStream(int rate, bool stereo) : Paula(stereo, rate, rate / 50) {
+ _module = nullptr;
_tick = _row = _pos = 0;
@@ -170,17 +60,31 @@ ProtrackerStream::ProtrackerStream(Common::SeekableReadStream *stream, int offs,
_patternDelay = 0;
ARRAYCLEAR(_track);
+}
+
+ProtrackerStream::ProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo) :
+ ProtrackerStream(rate, stereo) {
+ _module = new Module();
+ bool result = _module->load(*stream, offs);
+ assert(result);
startPaula();
}
+ProtrackerStream::~ProtrackerStream() {
+ if (_module) {
+ delete _module;
+ _module = nullptr;
+ }
+}
+
void ProtrackerStream::updateRow() {
for (int track = 0; track < 4; track++) {
_track[track].arpeggio = false;
_track[track].vibrato = 0;
_track[track].delaySampleTick = 0;
const note_t note =
- _module.pattern[_module.songpos[_pos]][_row][track];
+ _module->pattern[_module->songpos[_pos]][_row][track];
const int effect = note.effect >> 8;
@@ -190,14 +94,14 @@ void ProtrackerStream::updateRow() {
}
_track[track].sample = note.sample;
_track[track].lastSample = note.sample;
- _track[track].finetune = _module.sample[note.sample - 1].finetune;
- _track[track].vol = _module.sample[note.sample - 1].vol;
+ _track[track].finetune = _module->sample[note.sample - 1].finetune;
+ _track[track].vol = _module->sample[note.sample - 1].vol;
}
if (note.period) {
if (effect != 3 && effect != 5) {
if (_track[track].finetune)
- _track[track].period = _module.noteToPeriod(note.note, _track[track].finetune);
+ _track[track].period = _module->noteToPeriod(note.note, _track[track].finetune);
else
_track[track].period = note.period;
@@ -215,7 +119,7 @@ void ProtrackerStream::updateRow() {
case 0x0:
if (exy) {
_track[track].arpeggio = true;
- byte trackNote = _module.periodToNote(_track[track].period);
+ byte trackNote = _module->periodToNote(_track[track].period);
_track[track].arpeggioNotes[0] = trackNote;
_track[track].arpeggioNotes[1] = trackNote + ex;
_track[track].arpeggioNotes[2] = trackNote + ey;
@@ -276,10 +180,10 @@ void ProtrackerStream::updateRow() {
break;
case 0x5: // Set finetune
_track[track].finetune = ey;
- _module.sample[_track[track].sample].finetune = ey;
+ _module->sample[_track[track].sample].finetune = ey;
if (note.period) {
if (ey)
- _track[track].period = _module.noteToPeriod(note.note, ey);
+ _track[track].period = _module->noteToPeriod(note.note, ey);
else
_track[track].period = note.period;
}
@@ -342,7 +246,7 @@ void ProtrackerStream::updateEffects() {
_track[track].vibrato = 0;
const note_t note =
- _module.pattern[_module.songpos[_pos]][_row][track];
+ _module->pattern[_module->songpos[_pos]][_row][track];
const int effect = note.effect >> 8;
@@ -355,7 +259,7 @@ void ProtrackerStream::updateEffects() {
if (exy) {
const int idx = (_tick == 1) ? 0 : (_tick % 3);
_track[track].period =
- _module.noteToPeriod(_track[track].arpeggioNotes[idx],
+ _module->noteToPeriod(_track[track].arpeggioNotes[idx],
_track[track].finetune);
}
break;
@@ -395,7 +299,7 @@ void ProtrackerStream::updateEffects() {
_track[track].sample = _track[track].delaySample;
_track[track].offset = Offset(0);
if (_track[track].sample)
- _track[track].vol = _module.sample[_track[track].sample - 1].vol;
+ _track[track].vol = _module->sample[_track[track].sample - 1].vol;
}
break;
default:
@@ -414,7 +318,7 @@ void ProtrackerStream::interrupt() {
for (track = 0; track < 4; track++) {
_track[track].offset = getChannelOffset(track);
if (_tick == 0 && _track[track].arpeggio) {
- _track[track].period = _module.noteToPeriod(_track[track].arpeggioNotes[0],
+ _track[track].period = _module->noteToPeriod(_track[track].arpeggioNotes[0],
_track[track].finetune);
}
}
@@ -427,7 +331,7 @@ void ProtrackerStream::interrupt() {
} else if (_hasPatternBreak) {
_hasPatternBreak = false;
_row = _skipRow;
- _pos = (_pos + 1) % _module.songlen;
+ _pos = (_pos + 1) % _module->songlen;
_patternLoopRow = 0;
} else if (_hasPatternLoop) {
_hasPatternLoop = false;
@@ -435,7 +339,7 @@ void ProtrackerStream::interrupt() {
}
if (_row >= 64) {
_row = 0;
- _pos = (_pos + 1) % _module.songlen;
+ _pos = (_pos + 1) % _module->songlen;
_patternLoopRow = 0;
}
@@ -453,7 +357,7 @@ void ProtrackerStream::interrupt() {
setChannelVolume(track, _track[track].vol);
setChannelPeriod(track, _track[track].period + _track[track].vibrato);
if (_track[track].sample) {
- sample_t &sample = _module.sample[_track[track].sample - 1];
+ sample_t &sample = _module->sample[_track[track].sample - 1];
setChannelData(track,
sample.data,
sample.replen > 2 ? sample.data + sample.repeat : nullptr,
@@ -465,6 +369,40 @@ void ProtrackerStream::interrupt() {
}
}
+void ProtrackerStream::doPorta(int track) {
+ if (_track[track].portaToNote && _track[track].portaToNoteSpeed) {
+ int distance = _track[track].period - _track[track].portaToNote;
+ int sign = distance > 0 ? 1 : -1;
+
+ if ((sign * distance) > _track[track].portaToNoteSpeed)
+ _track[track].period -= sign * _track[track].portaToNoteSpeed;
+ else
+ _track[track].period = _track[track].portaToNote;
+ }
+}
+
+void ProtrackerStream::doVibrato(int track) {
+ _track[track].vibrato =
+ (_track[track].vibratoDepth * sinetable[_track[track].vibratoPos]) / 128;
+ _track[track].vibratoPos += _track[track].vibratoSpeed;
+ _track[track].vibratoPos %= 64;
+}
+
+void ProtrackerStream::doVolSlide(int track, byte ex, byte ey) {
+ int vol = _track[track].vol;
+ if (ex == 0)
+ vol -= ey;
+ else if (ey == 0)
+ vol += ex;
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > 64)
+ vol = 64;
+
+ _track[track].vol = vol;
+}
+
} // End of namespace Modules
namespace Audio {
diff --git a/audio/mods/protracker.h b/audio/mods/protracker.h
index 73913521777..9c08c09ffd8 100644
--- a/audio/mods/protracker.h
+++ b/audio/mods/protracker.h
@@ -26,17 +26,107 @@
* - parallaction
* - gob
* - hopkins
+ * - chewy (subclass)
*/
#ifndef AUDIO_MODS_PROTRACKER_H
#define AUDIO_MODS_PROTRACKER_H
+#include "audio/mods/paula.h"
+#include "audio/mods/module.h"
+
namespace Common {
class SeekableReadStream;
}
namespace Modules {
-class Module;
+
+class ProtrackerStream : public ::Audio::Paula {
+protected:
+ Module *_module;
+
+private:
+ int _tick;
+ int _row;
+ int _pos;
+
+ int _speed;
+ int _bpm;
+
+ // For effect 0xB - Jump To Pattern;
+ bool _hasJumpToPattern;
+ int _jumpToPattern;
+
+ // For effect 0xD - PatternBreak;
+ bool _hasPatternBreak;
+ int _skipRow;
+
+ // For effect 0xE6 - Pattern Loop
+ bool _hasPatternLoop;
+ int _patternLoopCount;
+ int _patternLoopRow;
+
+ // For effect 0xEE - Pattern Delay
+ byte _patternDelay;
+
+ static const int16 sinetable[];
+
+ struct Track {
+ byte sample;
+ byte lastSample;
+ uint16 period;
+ Offset offset;
+
+ byte vol;
+ byte finetune;
+
+ // For effect 0x0 - Arpeggio
+ bool arpeggio;
+ byte arpeggioNotes[3];
+
+ // For effect 0x3 - Porta to note
+ uint16 portaToNote;
+ byte portaToNoteSpeed;
+
+ // For effect 0x4 - Vibrato
+ int vibrato;
+ byte vibratoPos;
+ byte vibratoSpeed;
+ byte vibratoDepth;
+
+ // For effect 0xED - Delay sample
+ byte delaySample;
+ byte delaySampleTick;
+ } _track[4];
+
+public:
+ ProtrackerStream(Common::SeekableReadStream *stream, int offs, int rate, bool stereo);
+
+protected:
+ ProtrackerStream(int rate, bool stereo);
+
+public:
+ virtual ~ProtrackerStream();
+
+ Modules::Module *getModule() {
+ // Ordinarily, the Module is not meant to be seen outside of
+ // this class, but occasionally, it's useful to be able to
+ // manipulate it directly. The Hopkins engine uses this to
+ // repair a broken song.
+ return _module;
+ }
+
+private:
+ void interrupt() override;
+
+ void doPorta(int track);
+ void doVibrato(int track);
+ void doVolSlide(int track, byte ex, byte ey);
+
+ void updateRow();
+ void updateEffects();
+};
+
}
namespace Audio {
Commit: cf5b500874ae6e3828d90f0801d6dcbc9a92eeed
https://github.com/scummvm/scummvm/commit/cf5b500874ae6e3828d90f0801d6dcbc9a92eeed
Author: Coen Rampen (crampen at gmail.com)
Date: 2022-06-09T17:13:56+02:00
Commit Message:
CHEWY: Move TMF loading code to Stream subclass
This commit moves the TMF loading code to a dedicated ProtrackerStream
subclass. This replaces the inefficient parse TMF, convert to MOD, parse MOD
process with directly processing the TMF data. It also allows for setting the
correct stereo panning for the TMF channels.
Changed paths:
A engines/chewy/music/module_tmf.cpp
A engines/chewy/music/module_tmf.h
A engines/chewy/music/tmf_stream.cpp
A engines/chewy/music/tmf_stream.h
engines/chewy/module.mk
engines/chewy/sound.cpp
engines/chewy/sound.h
diff --git a/engines/chewy/module.mk b/engines/chewy/module.mk
index 6f92959647f..5656ba206c7 100644
--- a/engines/chewy/module.mk
+++ b/engines/chewy/module.mk
@@ -36,6 +36,8 @@ MODULE_OBJS = \
dialogs/inventory.o \
dialogs/main_menu.o \
dialogs/options.o \
+ music/module_tmf.o \
+ music/tmf_stream.o \
video/cfo_decoder.o \
video/video_player.o \
rooms/room00.o \
diff --git a/engines/chewy/music/module_tmf.cpp b/engines/chewy/music/module_tmf.cpp
new file mode 100644
index 00000000000..97547260bc1
--- /dev/null
+++ b/engines/chewy/music/module_tmf.cpp
@@ -0,0 +1,144 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "chewy/music/module_tmf.h"
+
+#include "common/array.h"
+#include "common/stream.h"
+
+const uint8 Chewy::Module_TMF::TMF_MOD_SONG_NAME[] = {
+ 'S', 'C', 'U', 'M', 'M',
+ 'V', 'M', ' ', 'M', 'O',
+ 'D', 'U', 'L', 'E', '\0',
+ '\0', '\0', '\0', '\0', '\0', '\0;'
+};
+const uint8 Chewy::Module_TMF::TMF_MOD_INSTRUMENT_NAME[] = {
+ 'S', 'C', 'U', 'M', 'M',
+ 'V', 'M', ' ', 'I', 'N',
+ 'S', 'T', 'R', 'U', 'M',
+ 'E', 'N', 'T', ' ', '\0',
+ '\0', '\0', '\0'
+};
+const uint16 Chewy::Module_TMF::TMF_MOD_PERIODS[] = {
+ 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
+ 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
+ 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113
+};
+
+bool Chewy::Module_TMF::load(Common::SeekableReadStream& stream, int offs) {
+ stream.seek(offs);
+
+ // Check TMF fourCC.
+ if (stream.readUint32BE() != MKTAG('T', 'M', 'F', '\0'))
+ error("Corrupt TMF resource");
+
+ // Write song name (not present in TMF data).
+ Common::copy(TMF_MOD_SONG_NAME, TMF_MOD_SONG_NAME + 21, songname);
+
+ // Copy instrument data.
+ uint8 fineTune, instVolume;
+ uint32 repeatPoint, repeatLength, sampleLength;
+ uint32 totalSampleLength = 0;
+
+ for (int i = 0; i < NUM_SAMPLES; ++i) {
+ fineTune = stream.readByte();
+ instVolume = stream.readByte();
+ // Repeat point, repeat length and sample length are 32 bit LE in bytes
+ // instead of 16 bit BE in words.
+ repeatPoint = stream.readUint32LE();
+ assert(repeatPoint <= 0x1FFFF && repeatPoint % 2 == 0);
+ repeatLength = stream.readUint32LE();
+ assert(repeatLength <= 0x1FFFF && repeatLength % 2 == 0);
+ // Sample length is at the end instead of at the start.
+ sampleLength = stream.readUint32LE();
+ assert(sampleLength <= 0x1FFFF && sampleLength % 2 == 0);
+ totalSampleLength += sampleLength;
+
+ // Instrument name is not present in TMF data.
+ Common::copy(TMF_MOD_INSTRUMENT_NAME, TMF_MOD_INSTRUMENT_NAME + 23, sample[i].name);
+ sample[i].name[19] = '0' + i / 10;
+ sample[i].name[20] = '0' + i % 10;
+
+ sample[i].len = sampleLength;
+ // Finetune is a signed nibble in MOD, but TMF uses a signed byte
+ // (within nibble range).
+ sample[i].finetune = fineTune & 0x0F;
+ sample[i].vol = instVolume;
+ sample[i].repeat = repeatPoint;
+ sample[i].replen = repeatLength;
+ }
+
+ // Copy pattern table.
+ songlen = stream.readByte();
+ // Second byte is the number of different patterns in TMF. This byte is
+ // unused in MOD (usually set to 0x7F).
+ uint8 numPatterns = stream.readByte();
+ undef = 0x7F;
+ stream.read(songpos, 128);
+ // M.K. fourCC is not present in TMF.
+ sig = signatures[0];
+
+ // TMF has a 32 bit LE number for each instrument here; these are probably
+ // offsets for each sample. They are not present in MOD and not needed, so
+ // they are skipped.
+ stream.skip(4 * 31);
+
+ // Copy pattern data.
+ pattern = new Modules::pattern_t[numPatterns];
+ for (int i = 0; i < numPatterns; ++i) {
+ for (int j = 0; j < 64; ++j) {
+ for (int k = 0; k < 4; ++k) {
+ // TMF channel data has this format:
+ // 1 byte note (0-0x23 or 0x30 for "use previous value")
+ // 1 byte sample
+ // 2 bytes effect (byte 3 high nibble is unused)
+ byte note = stream.readByte();
+ assert(note == 0x30 || note < 36);
+ byte sampleNum = stream.readByte();
+ uint16 effect = stream.readUint16BE();
+ assert((effect & 0xF000) == 0);
+ // Note is converted to a MOD 12 bit period using a lookup array.
+ uint16 period = note == 0x30 ? 0 : TMF_MOD_PERIODS[note];
+ pattern[i][j][k].sample = sampleNum;
+ pattern[i][j][k].period = period;
+ pattern[i][j][k].effect = effect;
+ pattern[i][j][k].note = periodToNote(period);
+ }
+ }
+ }
+
+ // Copy sample data.
+ for (int i = 0; i < NUM_SAMPLES; ++i) {
+ if (!sample[i].len) {
+ sample[i].data = nullptr;
+ } else {
+ sample[i].data = new int8[sample[i].len];
+ for (int j = 0; j < sample[i].len; j++) {
+ int tmfSample = stream.readByte();
+ // Convert from unsigned to signed.
+ tmfSample -= 0x80;
+ sample[i].data[j] = tmfSample & 0xFF;
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/engines/chewy/music/module_tmf.h b/engines/chewy/music/module_tmf.h
new file mode 100644
index 00000000000..02e43c82b81
--- /dev/null
+++ b/engines/chewy/music/module_tmf.h
@@ -0,0 +1,42 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CHEWY_MUSIC_MODULE_TMF_H
+#define CHEWY_MUSIC_MODULE_TMF_H
+
+#include "audio/mods/module.h"
+
+namespace Chewy {
+
+// Module variant for the MOD-like TMF format used by Chewy.
+class Module_TMF : public Modules::Module {
+protected:
+ static const uint8 TMF_MOD_SONG_NAME[21];
+ static const uint8 TMF_MOD_INSTRUMENT_NAME[23];
+ static const uint16 TMF_MOD_PERIODS[36];
+
+public:
+ bool load(Common::SeekableReadStream &stream, int offs) override;
+};
+
+} // End of namespace Chewy
+
+#endif
diff --git a/engines/chewy/music/tmf_stream.cpp b/engines/chewy/music/tmf_stream.cpp
new file mode 100644
index 00000000000..fcbfed95a4d
--- /dev/null
+++ b/engines/chewy/music/tmf_stream.cpp
@@ -0,0 +1,38 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "chewy/music/tmf_stream.h"
+
+#include "chewy/music/module_tmf.h"
+
+Chewy::TMFStream::TMFStream(Common::SeekableReadStream *stream, int offs) : ProtrackerStream(44100, true) {
+ _module = new Module_TMF();
+ bool result = _module->load(*stream, offs);
+ assert(result);
+
+ // Channel panning used by TMF is L-R-L-R instead of MOD's L-R-R-L.
+ setChannelPanning(0, PANNING_LEFT);
+ setChannelPanning(1, PANNING_RIGHT);
+ setChannelPanning(2, PANNING_LEFT);
+ setChannelPanning(3, PANNING_RIGHT);
+
+ startPaula();
+}
diff --git a/engines/chewy/music/tmf_stream.h b/engines/chewy/music/tmf_stream.h
new file mode 100644
index 00000000000..3d499a5e408
--- /dev/null
+++ b/engines/chewy/music/tmf_stream.h
@@ -0,0 +1,37 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CHEWY_MUSIC_TMF_STREAM_H
+#define CHEWY_MUSIC_TMF_STREAM_H
+
+#include "audio/mods/protracker.h"
+
+namespace Chewy {
+
+// AudioStream for the MOD-like TMF music format used by Chewy.
+class TMFStream : public Modules::ProtrackerStream {
+public:
+ TMFStream(Common::SeekableReadStream *stream, int offs);
+};
+
+} // End of namespace Chewy
+
+#endif
diff --git a/engines/chewy/sound.cpp b/engines/chewy/sound.cpp
index 382172ec329..5c7b56b6714 100644
--- a/engines/chewy/sound.cpp
+++ b/engines/chewy/sound.cpp
@@ -29,6 +29,7 @@
#include "chewy/sound.h"
#include "chewy/types.h"
#include "chewy/globals.h"
+#include "chewy/music/tmf_stream.h"
namespace Chewy {
@@ -132,12 +133,7 @@ void Sound::playMusic(int num, bool loop) {
}
void Sound::playMusic(uint8 *data, uint32 size, bool loop, DisposeAfterUse::Flag dispose) {
- uint8 *modData = nullptr;
- uint32 modSize;
-
- convertTMFToMod(data, size, modData, modSize);
- Audio::AudioStream *stream = Audio::makeProtrackerStream(
- new Common::MemoryReadStream(modData, modSize));
+ TMFStream *stream = new TMFStream(new Common::MemoryReadStream(data, size), 0);
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, stream);
}
@@ -215,120 +211,6 @@ void Sound::stopAll() {
_mixer->stopAll();
}
-void Sound::convertTMFToMod(uint8 *tmfData, uint32 tmfSize, uint8 *&modData, uint32 &modSize) {
- // TMF fixed data is 4 + 14*31 + 130 + 4*31 = 692
- // MOD fixed data is 20 + 30*31 + 134 = 1084
- // Variable data size is the same, so size difference is 392 bytes.
- modSize = tmfSize + 392;
- modData = (uint8 *)MALLOC(modSize);
- uint8 *tmfPtr = tmfData;
- uint8 *modPtr = modData;
-
- // Check TMF fourCC.
- if (READ_BE_UINT32(tmfPtr) != MKTAG('T', 'M', 'F', '\0'))
- error("Corrupt TMF resource");
- tmfPtr += 4;
-
- // Write song name (not present in TMF data).
- memcpy(modPtr, TMF_MOD_SONG_NAME, 20);
- modPtr += 20;
-
- // Copy instrument data.
- uint8 fineTune, instVolume;
- uint32 repeatPoint, repeatLength, sampleLength;
- uint32 totalSampleLength = 0;
-
- for (int i = 0; i < TMF_NUM_INSTRUMENTS; i++) {
- fineTune = *tmfPtr++;
- instVolume = *tmfPtr++;
- // Repeat point, repeat length and sample length are 32 bit LE in bytes
- // instead of 16 bit BE in words.
- repeatPoint = READ_LE_UINT32(tmfPtr);
- assert(repeatPoint <= 0x1FFFF && repeatPoint % 2 == 0);
- tmfPtr += 4;
- repeatLength = READ_LE_UINT32(tmfPtr);
- assert(repeatLength <= 0x1FFFF && repeatLength % 2 == 0);
- tmfPtr += 4;
- // Sample length is at the end instead of at the start.
- sampleLength = READ_LE_UINT32(tmfPtr);
- assert(sampleLength <= 0x1FFFF && sampleLength % 2 == 0);
- tmfPtr += 4;
- totalSampleLength += sampleLength;
-
- // Instrument name is not present in TMF data.
- memcpy(modPtr, TMF_MOD_INSTRUMENT_NAME, 18);
- modPtr += 18;
- *modPtr++ = ' ';
- *modPtr++ = '0' + i / 10;
- *modPtr++ = '0' + i % 10;
- *modPtr++ = '\0';
-
- WRITE_BE_UINT16(modPtr, sampleLength / 2);
- modPtr += 2;
- // Finetune is a signed nibble in MOD, but TMF uses a signed byte
- // (within nibble range).
- *modPtr++ = fineTune & 0x0F;
- *modPtr++ = instVolume;
- WRITE_BE_UINT16(modPtr, repeatPoint / 2);
- modPtr += 2;
- WRITE_BE_UINT16(modPtr, repeatLength / 2);
- modPtr += 2;
- }
-
- // Copy pattern table.
- *modPtr++ = *tmfPtr++;
- // Second byte is the number of different patterns in TMF. This byte is
- // unused in MOD (usually set to 0x7F).
- uint8 numPatterns = *tmfPtr++;
- *modPtr++ = 0x7F;
- memcpy(modPtr, tmfPtr, 128);
- modPtr += 128;
- tmfPtr += 128;
- // M.K. fourCC is not present in TMF.
- WRITE_BE_UINT32(modPtr, MKTAG('M', '.', 'K', '.'));
- modPtr += 4;
-
- // TMF has a 32 bit LE number for each instrument here; these are probably
- // offsets for each sample. They are not present in MOD and not needed, so
- // they are skipped.
- tmfPtr += 4 * 31;
-
- assert(modSize == 1084 + (numPatterns * 1024) + totalSampleLength);
-
- // Copy pattern data.
- uint32 channelDwords = numPatterns * 1024 / 4;
- // TMF channel data has this format:
- // 1 byte note (0-0x23 or 0x30 for "use previous value")
- // 1 byte sample
- // 2 bytes effect (byte 3 high nibble is unused)
- for (uint32 i = 0; i < channelDwords; i++) {
- byte note = *tmfPtr++;
- assert(note == 0x30 || note < 36);
- byte sample = *tmfPtr++;
- uint16 effect = READ_BE_UINT16(tmfPtr);
- assert((effect & 0xF000) == 0);
- tmfPtr += 2;
-
- // Note is converted to a MOD 12 bit period using a lookup array.
- // Effect 12 bit value is used as-is.
- // Sample is split into the period and effect high nibbles.
- uint16 periodWord = (note == 0x30 ? 0 : TMF_MOD_PERIODS[note]) | ((sample & 0xF0) << 8);
- uint16 effectWord = effect | ((sample & 0x0F) << 12);
- WRITE_BE_UINT16(modPtr, periodWord);
- modPtr += 2;
- WRITE_BE_UINT16(modPtr, effectWord);
- modPtr += 2;
- }
-
- // Copy sample data.
- for (uint32 i = 0; i < totalSampleLength; i++) {
- int sample = *tmfPtr++;
- // Convert from unsigned to signed.
- sample -= 0x80;
- *modPtr++ = sample & 0xFF;
- }
-}
-
void Sound::waitForSpeechToFinish() {
if (speechEnabled()) {
while (isSpeechActive() && !SHOULD_QUIT) {
diff --git a/engines/chewy/sound.h b/engines/chewy/sound.h
index 2f07cd7e246..c2246b2d0e5 100644
--- a/engines/chewy/sound.h
+++ b/engines/chewy/sound.h
@@ -96,8 +96,6 @@ private:
SoundResource *_speechRes;
SoundResource *_soundRes;
-
- void convertTMFToMod(uint8 *tmfData, uint32 tmfSize, uint8 *&modData, uint32 &modSize);
};
} // End of namespace Chewy
More information about the Scummvm-git-logs
mailing list