aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/sound.cpp
diff options
context:
space:
mode:
authorTorbjörn Andersson2012-11-11 13:56:06 +0100
committerTorbjörn Andersson2012-11-11 13:56:06 +0100
commit8bb595453b7bce6e219acc94c105a1f76e5ff734 (patch)
treebbedbfa057dfeccda5af3061dc651b71bce93dcb /engines/scumm/sound.cpp
parentda0490f9b296864e8826c63295e91515f8617839 (diff)
downloadscummvm-rg350-8bb595453b7bce6e219acc94c105a1f76e5ff734.tar.gz
scummvm-rg350-8bb595453b7bce6e219acc94c105a1f76e5ff734.tar.bz2
scummvm-rg350-8bb595453b7bce6e219acc94c105a1f76e5ff734.zip
SCUMM: Added support for Macintosh music in Monkey Island 1
This is based on the old Mac0-to-General MIDI conversion that we used to do (and which this patch removes), as well as the code for playing the Monkey Island 2 and Fate of Atlantis Macintosh music. I'm not sure how accurate it is, particularly in tempo and volume, but at this point it seems to work pretty well. Looping music is perhaps a bit off, but it was before as well. There is an annoying drawn out note in the music when you're following the shopkeeper, but that appears to have been there in the original as well.
Diffstat (limited to 'engines/scumm/sound.cpp')
-rw-r--r--engines/scumm/sound.cpp220
1 files changed, 1 insertions, 219 deletions
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index a1cecfa0b3..57345126d3 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -1086,9 +1086,6 @@ void Sound::saveLoadWithSerializer(Serializer *ser) {
#pragma mark --- Sound resource handling ---
#pragma mark -
-static void convertMac0Resource(ResourceManager *res, ResId idx, byte *src_ptr, int size);
-
-
/*
* TODO: The way we handle sound/music resources really is one huge hack.
* We probably should reconsider how we do this, and maybe come up with a
@@ -1208,11 +1205,9 @@ int ScummEngine::readSoundResource(ResId idx) {
case MKTAG('M','a','c','0'):
_fileHandle->seek(-12, SEEK_CUR);
total_size = _fileHandle->readUint32BE() - 8;
- ptr = (byte *)calloc(total_size, 1);
+ ptr = _res->createResource(rtSound, idx, total_size);
_fileHandle->read(ptr, total_size);
//dumpResource("sound-", idx, ptr);
- convertMac0Resource(_res, idx, ptr, total_size);
- free(ptr);
return 1;
case MKTAG('M','a','c','1'):
@@ -1445,219 +1440,6 @@ static byte *writeVLQ(byte *ptr, int value) {
return ptr;
}
-static byte Mac0ToGMInstrument(uint32 type, int &transpose) {
- transpose = 0;
- switch (type) {
- case MKTAG('M','A','R','I'): return 12;
- case MKTAG('P','L','U','C'): return 45;
- case MKTAG('H','A','R','M'): return 22;
- case MKTAG('P','I','P','E'): return 19;
- case MKTAG('T','R','O','M'): transpose = -12; return 57;
- case MKTAG('S','T','R','I'): return 48;
- case MKTAG('H','O','R','N'): return 60;
- case MKTAG('V','I','B','E'): return 11;
- case MKTAG('S','H','A','K'): return 77;
- case MKTAG('P','A','N','P'): return 75;
- case MKTAG('W','H','I','S'): return 76;
- case MKTAG('O','R','G','A'): return 17;
- case MKTAG('B','O','N','G'): return 115;
- case MKTAG('B','A','S','S'): transpose = -24; return 35;
- default:
- error("Unknown Mac0 instrument %s found", tag2str(type));
- }
-}
-
-static void convertMac0Resource(ResourceManager *res, ResId idx, byte *src_ptr, int size) {
- /*
- From Markus Magnuson (superqult) we got this information:
- Mac0
- ---
- 4 bytes - 'SOUN'
- BE 4 bytes - block length
-
- 4 bytes - 'Mac0'
- BE 4 bytes - (blockLength - 27)
- 28 bytes - ???
-
- do this three times (once for each channel):
- 4 bytes - 'Chan'
- BE 4 bytes - channel length
- 4 bytes - instrument name (e.g. 'MARI')
-
- do this for ((chanLength-24)/4) times:
- 2 bytes - note duration
- 1 byte - note value
- 1 byte - note velocity
-
- 4 bytes - ???
- 4 bytes - 'Loop'/'Done'
- 4 bytes - ???
-
- 1 byte - 0x09
- ---
-
- Instruments (General Midi):
- "MARI" - Marimba (12)
- "PLUC" - Pizzicato Strings (45)
- "HARM" - Harmonica (22)
- "PIPE" - Church Organ? (19) or Flute? (73) or Bag Pipe (109)
- "TROM" - Trombone (57)
- "STRI" - String Ensemble (48 or 49)
- "HORN" - French Horn? (60) or English Horn? (69)
- "VIBE" - Vibraphone (11)
- "SHAK" - Shakuhachi? (77)
- "PANP" - Pan Flute (75)
- "WHIS" - Whistle (78) / Bottle (76)
- "ORGA" - Drawbar Organ (16; but could also be 17-20)
- "BONG" - Woodblock? (115)
- "BASS" - Bass (32-39)
-
-
- Now the task could be to convert this into MIDI, to be fed into iMuse.
- Or we do something similiar to what is done in Player_V3, assuming
- we can identify SFX in the MI datafiles for each of the instruments
- listed above.
- */
-
-#if 0
- byte *ptr = _res->createResource(rtSound, idx, size);
- memcpy(ptr, src_ptr, size);
-#else
- const int ppqn = 480;
- byte *ptr, *start_ptr;
-
- int total_size = 0;
- total_size += kMIDIHeaderSize; // Header
- total_size += 7; // Tempo META
- total_size += 3 * 3; // Three program change mesages
- total_size += 22; // Possible jump SysEx
- total_size += 5; // EOT META
-
- int i, len;
- byte track_instr[3];
- byte *track_data[3];
- int track_len[3];
- int track_transpose[3];
- bool looped = false;
-
- src_ptr += 8;
- // TODO: Decipher the unknown bytes in the header. For now, skip 'em
- src_ptr += 28;
-
- // Parse the three channels
- for (i = 0; i < 3; i++) {
- assert(READ_BE_UINT32(src_ptr) == MKTAG('C','h','a','n'));
- len = READ_BE_UINT32(src_ptr + 4);
- track_len[i] = len - 24;
- track_instr[i] = Mac0ToGMInstrument(READ_BE_UINT32(src_ptr + 8), track_transpose[i]);
- track_data[i] = src_ptr + 12;
- src_ptr += len;
- looped = (READ_BE_UINT32(src_ptr - 8) == MKTAG('L','o','o','p'));
-
- // For each note event, we need up to 6 bytes for the
- // Note On (3 VLQ, 3 event), and 6 bytes for the Note
- // Off (3 VLQ, 3 event). So 12 bytes total.
- total_size += 12 * track_len[i];
- }
- assert(*src_ptr == 0x09);
-
- // Create sound resource
- start_ptr = res->createResource(rtSound, idx, total_size);
-
- // Insert MIDI header
- ptr = writeMIDIHeader(start_ptr, "GMD ", ppqn, total_size);
-
- // Write a tempo change Meta event
- // 473 / 4 Hz, convert to micro seconds.
- uint32 dw = 1000000 * 437 / 4 / ppqn; // 1000000 * ppqn * 4 / 473;
- memcpy(ptr, "\x00\xFF\x51\x03", 4); ptr += 4;
- *ptr++ = (byte)((dw >> 16) & 0xFF);
- *ptr++ = (byte)((dw >> 8) & 0xFF);
- *ptr++ = (byte)(dw & 0xFF);
-
- // Insert program change messages
- *ptr++ = 0; // VLQ
- *ptr++ = 0xC0;
- *ptr++ = track_instr[0];
- *ptr++ = 0; // VLQ
- *ptr++ = 0xC1;
- *ptr++ = track_instr[1];
- *ptr++ = 0; // VLQ
- *ptr++ = 0xC2;
- *ptr++ = track_instr[2];
-
- // And now, the actual composition. Please turn all cell phones
- // and pagers off during the performance. Thank you.
- uint16 nextTime[3] = { 1, 1, 1 };
- int stage[3] = { 0, 0, 0 };
-
- while (track_len[0] | track_len[1] | track_len[2]) {
- int best = -1;
- uint16 bestTime = 0xFFFF;
- for (i = 0; i < 3; ++i) {
- if (track_len[i] && nextTime[i] < bestTime) {
- bestTime = nextTime[i];
- best = i;
- }
- }
- assert (best != -1);
-
- if (!stage[best]) {
- // We are STARTING this event.
- if (track_data[best][2] > 1) {
- // Note On
- ptr = writeVLQ(ptr, nextTime[best]);
- *ptr++ = 0x90 | best;
- *ptr++ = track_data[best][2] + track_transpose[best];
- *ptr++ = track_data[best][3] * 127 / 100; // Scale velocity
- for (i = 0; i < 3; ++i)
- nextTime[i] -= bestTime;
- }
- nextTime[best] += READ_BE_UINT16 (track_data[best]);
- stage[best] = 1;
- } else {
- // We are ENDING this event.
- if (track_data[best][2] > 1) {
- // There was a Note On, so do a Note Off
- ptr = writeVLQ(ptr, nextTime[best]);
- *ptr++ = 0x80 | best;
- *ptr++ = track_data[best][2] + track_transpose[best];
- *ptr++ = track_data[best][3] * 127 / 100; // Scale velocity
- for (i = 0; i < 3; ++i)
- nextTime[i] -= bestTime;
- }
- track_data[best] += 4;
- track_len[best] -= 4;
- stage[best] = 0;
- }
- }
-
- // Is this a looped song? If so, effect a loop by
- // using the S&M maybe_jump SysEx command.
- // FIXME: Jamieson630: The jump seems to be happening
- // too quickly! There should maybe be a pause after
- // the last Note Off? But I couldn't find one in the
- // MI1 Lookout music, where I was hearing problems.
- if (looped) {
- memcpy(ptr, "\x00\xf0\x13\x7d\x30\00", 6); ptr += 6; // maybe_jump
- memcpy(ptr, "\x00\x00", 2); ptr += 2; // cmd -> 0 means always jump
- memcpy(ptr, "\x00\x00\x00\x00", 4); ptr += 4; // track -> 0 (only track)
- memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // beat -> 1 (first beat)
- memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // tick -> 1
- memcpy(ptr, "\x00\xf7", 2); ptr += 2; // SysEx end marker
- }
-
- // Insert end of song META
- memcpy(ptr, "\x00\xff\x2f\x00\x00", 5); ptr += 5;
-
- assert(ptr <= start_ptr + total_size);
-
- // Rewrite MIDI header, this time with true size
- total_size = ptr - start_ptr;
- ptr = writeMIDIHeader(start_ptr, "GMD ", ppqn, total_size);
-#endif
-}
-
static void convertADResource(ResourceManager *res, const GameSettings& game, ResId idx, byte *src_ptr, int size) {
// We will ignore the PPQN in the original resource, because
// it's invalid anyway. We use a constant PPQN of 480.