aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJamieson Christian2003-10-10 21:40:25 +0000
committerJamieson Christian2003-10-10 21:40:25 +0000
commit478e068ca807c9f4a92d7dd1f0cdd9fc40ce7cac (patch)
tree7ca3012475009d6342aef2a27ab863853a69c4ed
parent4675d193510d4e8191330132b6d58bc85ac5ac5a (diff)
downloadscummvm-rg350-478e068ca807c9f4a92d7dd1f0cdd9fc40ce7cac.tar.gz
scummvm-rg350-478e068ca807c9f4a92d7dd1f0cdd9fc40ce7cac.tar.bz2
scummvm-rg350-478e068ca807c9f4a92d7dd1f0cdd9fc40ce7cac.zip
Feature Request [781797] Support music in the Mac version of MI1
Finished the Mac0 -> SMF (GMD) conversion code. Sounds good (albeit thin, with only 3 instruments), except that the looping songs are not looping with the right timing. svn-id: r10730
-rw-r--r--scumm/resource.cpp122
1 files changed, 87 insertions, 35 deletions
diff --git a/scumm/resource.cpp b/scumm/resource.cpp
index 6c55856115..b7b37b7349 100644
--- a/scumm/resource.cpp
+++ b/scumm/resource.cpp
@@ -1093,7 +1093,7 @@ void ScummEngine::convertMac0Resource(int type, int idx, byte *src_ptr, int size
listed above.
*/
-#if 1
+#if 0
byte *ptr = createResource(type, idx, size);
memcpy(ptr, src_ptr, size);
#else
@@ -1101,14 +1101,14 @@ void ScummEngine::convertMac0Resource(int type, int idx, byte *src_ptr, int size
byte *ptr, *start_ptr;
int total_size = 0;
- total_size += kMIDIHeaderSize; // Header
- total_size += 5; // end of song sysex
- total_size += 3 * 2; // Three programm change mesages
+ 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];
- int current_note[3];
- int track_time[3];
byte *track_data[3];
int track_len[3];
bool looped = false;
@@ -1118,20 +1118,18 @@ void ScummEngine::convertMac0Resource(int type, int idx, byte *src_ptr, int size
// Parse the three channels
for (i = 0; i < 3; i++) {
- assert(READ_BE_UINT32(src_ptr) == MKID('Chan'));
+ assert(*((uint32*)src_ptr) == MKID('Chan'));
len = READ_BE_UINT32(src_ptr + 4);
- track_len[i] = (len - 24) / 4;
- track_instr[i] = Mac0ToGMInstrument(READ_BE_UINT32(src_ptr + 8));
- track_data[i] = src_ptr + 8;
- current_note[i] = -1;
- track_time[i] = -1;
+ track_len[i] = len - 24;
+ track_instr[i] = Mac0ToGMInstrument(*(uint32*)(src_ptr + 8));
+ track_data[i] = src_ptr + 12;
src_ptr += len;
- looped = (READ_BE_UINT32(src_ptr - 8) == MKID('Loop'));
+ looped = (*((uint32*)(src_ptr - 8)) == MKID('Loop'));
- // For each note event, we need up to 3 bytes for the VLQ, and 3 bytes
- // for the note on. Finally, up to 3 bytes for the note off.
- // That means up to 9 bytes may be used for each note.
- total_size += 9 * track_len[i];
+ // 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);
@@ -1141,33 +1139,87 @@ void ScummEngine::convertMac0Resource(int type, int idx, byte *src_ptr, int 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.
- // FIXME: This is copied from the SFX case in convertADResource()
- // and probably is not the proper value, but for now it's
- // sufficient to act as placeholder.
- uint32 dw = 1000000 * ppqn * 4 / 473;
+ 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);
-*/
- // Time 0
-// ptr = writeVLQ(ptr, 0);
-
- // Insert programm change status messages
- ptr[0] = 'C0'; ptr[1] = track_instr[0];
- ptr[2] = 'C1'; ptr[3] = track_instr[1];
- ptr[4] = 'C2'; ptr[5] = track_instr[2];
- ptr += 6;
+ // 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];
+ *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];
+ *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;
+ }
+ }
- // TODO: now use the information computed above, and create a MIDI track,
- // similiar to what is done in convertADResource
- // ...
+ // 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 sysex
+ // Insert end of song META
memcpy(ptr, "\x00\xff\x2f\x00\x00", 5); ptr += 5;
assert(ptr <= start_ptr + total_size);