[Scummvm-git-logs] scummvm master -> cd0a7d779c6f46c0fe6a03c1e6fbb32ae6abf410

NMIError noreply at scummvm.org
Mon Jun 6 19:18:27 UTC 2022


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
cd0a7d779c CHEWY: Finish TMF to MOD conversion


Commit: cd0a7d779c6f46c0fe6a03c1e6fbb32ae6abf410
    https://github.com/scummvm/scummvm/commit/cd0a7d779c6f46c0fe6a03c1e6fbb32ae6abf410
Author: Coen Rampen (crampen at gmail.com)
Date: 2022-06-06T21:18:13+02:00

Commit Message:
CHEWY: Finish TMF to MOD conversion

This finishes the conversion code of the TMF music format to standard MOD.

Changed paths:
    engines/chewy/sound.cpp
    engines/chewy/sound.h


diff --git a/engines/chewy/sound.cpp b/engines/chewy/sound.cpp
index bfc6e174634..382172ec329 100644
--- a/engines/chewy/sound.cpp
+++ b/engines/chewy/sound.cpp
@@ -32,6 +32,24 @@
 
 namespace Chewy {
 
+const uint8 Sound::TMF_MOD_SONG_NAME[] = {
+	'S', 'C', 'U', 'M', 'M',
+	'V', 'M', ' ', 'M', 'O',
+	'D', 'U', 'L', 'E', '\0',
+	'\0', '\0', '\0', '\0', '\0'};
+const uint8 Sound::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'};
+// TODO Verify period values used by the game; this is an educated guess.
+const uint16 Sound::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
+};
+
 Sound::Sound(Audio::Mixer *mixer) {
 	_mixer = mixer;
 	_speechRes = new SoundResource("speech.tvp");
@@ -114,30 +132,14 @@ void Sound::playMusic(int num, bool loop) {
 }
 
 void Sound::playMusic(uint8 *data, uint32 size, bool loop, DisposeAfterUse::Flag dispose) {
-#if 0
 	uint8 *modData = nullptr;
 	uint32 modSize;
 
-	/*
-	// TODO: Finish and use convertTMFToMod()
-	warning("The current music playing implementation is wrong");
-	modSize = size;
-	modData = (uint8 *)MALLOC(modSize);
-	memcpy(modData, data, size);
-	
-	Audio::AudioStream *stream = Audio::makeLoopingAudioStream(
-	                                 Audio::makeRawStream(modData,
-	                                         modSize, 22050, Audio::FLAG_UNSIGNED,
-	                                         dispose),
-	                                 loop ? 0 : 1);
-	*/
-
 	convertTMFToMod(data, size, modData, modSize);
 	Audio::AudioStream *stream = Audio::makeProtrackerStream(
-		new Common::MemoryReadStream(data, size));
+		new Common::MemoryReadStream(modData, modSize));
 
 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, stream);
-#endif
 }
 
 void Sound::pauseMusic() {
@@ -213,58 +215,59 @@ void Sound::stopAll() {
 	_mixer->stopAll();
 }
 
-void Sound::convertTMFToMod(uint8 *tmfData, uint32 tmfSize, uint8 *modData, uint32 &modSize) {
-	const int maxInstruments = 31;
-
-	modSize = tmfSize + 20 + maxInstruments * 22 + 4;
+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;
 
-	const uint8 songName[20] = {
-		'S', 'C', 'U', 'M', 'M',
-		'V', 'M', ' ', 'M', 'O',
-		'D', 'U', 'L', 'E', '\0',
-		'\0', '\0', '\0', '\0', '\0'
-	};
-	const uint8 instrumentName[22] = {
-		'S', 'C', 'U', 'M', 'M',
-		'V', 'M', ' ', 'I', 'N',
-		'S', 'T', 'R', 'U', 'M',
-		'E', 'N', 'T', '\0', '\0',
-		'\0', '\0'
-	};
-
+	// Check TMF fourCC.
 	if (READ_BE_UINT32(tmfPtr) != MKTAG('T', 'M', 'F', '\0'))
 		error("Corrupt TMF resource");
 	tmfPtr += 4;
 
-	memcpy(modPtr, songName, 20);
+	// Write song name (not present in TMF data).
+	memcpy(modPtr, TMF_MOD_SONG_NAME, 20);
 	modPtr += 20;
 
+	// Copy instrument data.
 	uint8 fineTune, instVolume;
-	uint16 repeatPoint, repeatLength, sampleLength;
+	uint32 repeatPoint, repeatLength, sampleLength;
+	uint32 totalSampleLength = 0;
 
-	for (int i = 0; i < maxInstruments; i++) {
+	for (int i = 0; i < TMF_NUM_INSTRUMENTS; i++) {
 		fineTune = *tmfPtr++;
 		instVolume = *tmfPtr++;
-		repeatPoint = READ_BE_UINT16(tmfPtr);
-		tmfPtr += 2;
-		repeatLength = READ_BE_UINT16(tmfPtr);
-		tmfPtr += 2;
-		sampleLength = READ_BE_UINT16(tmfPtr);
-		tmfPtr += 2;
-
-		memcpy(modPtr, instrumentName, 18);
+		// 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++ = i / 10;
-		*modPtr++ = i % 10;
+		*modPtr++ = '0' + i / 10;
+		*modPtr++ = '0' + i % 10;
 		*modPtr++ = '\0';
 
 		WRITE_BE_UINT16(modPtr, sampleLength / 2);
 		modPtr += 2;
-		*modPtr++ = fineTune;
+		// 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;
@@ -272,15 +275,58 @@ void Sound::convertTMFToMod(uint8 *tmfData, uint32 tmfSize, uint8 *modData, uint
 		modPtr += 2;
 	}
 
+	// Copy pattern table.
 	*modPtr++ = *tmfPtr++;
-	*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;
 
-	// TODO: Finish this
+	// 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() {
diff --git a/engines/chewy/sound.h b/engines/chewy/sound.h
index 4bfccbf83e3..2f07cd7e246 100644
--- a/engines/chewy/sound.h
+++ b/engines/chewy/sound.h
@@ -33,6 +33,12 @@ class SoundResource;
 #define MAX_SOUND_EFFECTS 14
 
 class Sound {
+private:
+	static const int TMF_NUM_INSTRUMENTS = 31;
+	static const uint8 TMF_MOD_SONG_NAME[20];
+	static const uint8 TMF_MOD_INSTRUMENT_NAME[22];
+	static const uint16 TMF_MOD_PERIODS[36];
+
 public:
 	Sound(Audio::Mixer *mixer);
 	virtual ~Sound();
@@ -91,7 +97,7 @@ private:
 	SoundResource *_speechRes;
 	SoundResource *_soundRes;
 
-	void convertTMFToMod(uint8 *tmfData, uint32 tmfSize, uint8 *modData, uint32 &modSize);
+	void convertTMFToMod(uint8 *tmfData, uint32 tmfSize, uint8 *&modData, uint32 &modSize);
 };
 
 } // End of namespace Chewy




More information about the Scummvm-git-logs mailing list