#7328 closed feature request (fixed)
AUDIO: LOOM Mac music support
Reported by: | SF/jamieson630 | Owned by: | SF/jamieson630 |
---|---|---|---|
Priority: | normal | Component: | Engine: SCUMM |
Version: | Keywords: | ||
Cc: | Game: | Loom |
Description
I didn't find a bug or RFE anywhere else regarding Loom- Mac music, so here's where I'm going to post notes about the custom format as I discover things.
Ticket imported from: #824221. Ticket imported from: feature-requests/144.
Change History (11)
comment:1 by , 21 years ago
comment:2 by , 21 years ago
I would like to help with this jamieson. I forgot... was it you or superqult that didn't have the resources but only the game data files?
Anyway, I'd be happy to help with information on them, remapping them to MIDI perhaps, like you did with Monkey 1.
There are 10 samples in the Loom application :
Res. ID Name Size(bytes) 'sounds like' in GM 1000 "Dual Harp" 5082 - Pizzicato Strings (46) 10895 "harp1" 7254 - Orchestral Harp (47) 11445 "strings1" 6493 - String ensemble (49) 11548 "silent" 50 - nothing 13811 "staff1" 7944 - music box (11) 15703 "brass1" 10463 - saw wave (82) 16324 "flute1" 8458 - flute (74) 25614 "accordion1" 8394 - accordion (22) 28110 "f horn1" 8121 - french horn (61) 29042 "bassoon1" 2423 - bassoon (71)
They are all sampled at C4 (MIDI 60). The GM values are estimated by ear from singular sounds, not from the music mix.
Hopefully the resource IDs match some of the values in the 'unknown' fields. Else, just ask me for more information...
comment:4 by , 21 years ago
Summary: | LOOM Mac music support → AUDIO: LOOM Mac music support |
---|
comment:5 by , 21 years ago
lechimp, thanks for the resource information. Those do indeed match up with information in the header -- 2 bytes for each stream, corresponding apparently to which instrument that stream is supposed to use.
Also, the channels-within-a-stream thing doesn't appear to be correct. The streams themselves may very well be the equivalent of simultaneous channels. If that's the case, then what's interesting is that data from one channel seems to show up in other channels, but there may be a flag I'm missing that is used to mute those events so that they only serve as timing references. I'm not sure yet.
So discard the information in my original notes, and here's the new information:
RESOURCE DATA LE 2 bytes Resource size 2 bytes Unknown 2 bytes 'so' 14 bytes Unknown BE 2 bytes Instrument for Stream 1 BE 2 bytes Instrument for Stream 2 BE 2 bytes Instrument for Stream 3 BE 2 bytes Instrument for Stream 4 BE 2 bytes Instrument for Stream 5 BE 2 bytes Offset to Stream 1 BE 2 bytes Offset to Stream 2 BE 2 bytes Offset to Stream 3 BE 2 bytes Offset to Stream 4 BE 2 bytes Offset to Stream 5 ? bytes The streams
STREAM DATA BE 2 bytes Unknown (always 1?) 2 bytes Unknown (always 0?) BE 2 bytes Number of events in stream ? bytes Stream data
Each stream event is exactly 3 bytes, therefore one can assert that numEvents == (streamSize - 6) / 3. The polyphony of a stream appears to be 1; in other words, only one note at a time can be playing in each stream. The next event is not executed until the current note (or rest) is finished playing; therefore, note duration also serves as the time delta between events.
FOR EACH EVENTS BE 2 bytes Note duration 1 byte Note number to play (0 = rest/silent)
comment:6 by , 21 years ago
Oh, and quick speculation -- Stream 1 may be used for a single-voice interleaved version of the music, where Stream 2- 5 represent a version of the music in up to 4-voice polyphony, one voice per stream. I postulate thus because the first stream of the Mac Loom theme music contains interleaved voices, whereas the second stream seemed to contain only the pizzicato bottom-end harp. Stream 5, in this example, is empty, so if my speculation is correct, this particular musical number supports 3-voice polyphony at most. I must check out Streams 3 and 4 to see what they contain.
comment:7 by , 21 years ago
I am only happy to help :-) Is there anything else short of hardcore programming or reverse engineering I can help with?
I am looking forward to play Loom again and maybe I'll try to run it on my old dusty LC II for which it was originally bought for. I could make some comparison tests even...
comment:8 by , 12 years ago
I recently bought a used copy of Loom for the Mac, and using the information collected here I was able to get the Loom music playing. I don't know whether or not it sounds correct, but it sounds reasonable enough to me.
The current version can be found as a ScummVM pull request at https://github.com/scummvm/scummvm/pull/291
comment:9 by , 12 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
comment:10 by , 12 years ago
The Mac MI1/Loom music patch was accepted a month ago. I just forgot to close this feature request. As I said, it's possible that it's still not quite right but it sounds reasonable enough to me.
Thanks to everyone who worked to figure out the music format and handling Macintosh instruments! All I had to do was to put the final pieces together.
comment:11 by , 6 years ago
Component: | → Engine: SCUMM |
---|---|
Game: | → Loom |
The Loom Mac music resources consist of a header and up to 5 streams. Here is what is known so far:
RESOURCE DATA LE 2 bytes Resource size 2 bytes Unknown 2 bytes 'so' 24 bytes Unknown BE 2 bytes Offset to Stream 1 BE 2 bytes Offset to Stream 2 BE 2 bytes Offset to Stream 3 BE 2 bytes Offset to Stream 4 BE 2 bytes Offset to Stream 5 ? bytes The streams
STREAM DATA BE 2 bytes Unknown (always 1?) 2 bytes Unknown (always 0?) 2 bytes Number of events in stream ? bytes Stream data
Each stream event is exactly 3 bytes, therefore one can assert that numEvents == (streamSize - 6) / 3.
A stream can contain events for multiple "channels". There are two types of events: channel and universal (or System Common in MIDI terms).
UNIVERSAL EVENTS 1 byte 0x7F 1 byte ? 1 byte ?
CHANNEL EVENTS 1 byte Channel number (0-?) 1 byte Note duration or delay before next event? 1 byte Note number to play (0 = rest)
(The first event in the stream, presumably, has a delay of 0, since the delay before an event is embedded in the previous event.)
The second byte of the event appears to be a temporal measure because sequences of these bytes, with multiple channels active, appear to add up to patterns of summations (something that would not happen if the byte carried command data). However, a few events contain second bytes that completely depart from this pattern.