note description: "chocolate doom mus2mid, originally by Ben Ryves" license: "[ Copyright (C) 1993-1996 by id Software, Inc. Copyright (C) 2005-2014 Simon Howard Copyright (C) 2006 Ben Ryves 2006 Copyright (C) 2021 Ilgiz Mustafin 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ]" class MUS2MID create make feature Num_channels: INTEGER_32 = 16 Midi_percussion_chan: NATURAL_8 = 6 Mus_percussion_chan: NATURAL_8 = 15 feature -- musevent, MUS event codes Mus_releasekey: NATURAL_8 = 0 Mus_presskey: NATURAL_8 = 16 Mus_pitchwheel: NATURAL_8 = 32 Mus_systemevent: NATURAL_8 = 48 Mus_changecontroller: NATURAL_8 = 64 Mus_scoreend: NATURAL_8 = 96 feature -- midievent, MIDI event codes Midi_releasekey: NATURAL_8 = 128 Midi_presskey: NATURAL_8 = 144 Midi_aftertouchkey: NATURAL_8 = 160 Midi_changecontroller: NATURAL_8 = 176 Midi_changepatch: NATURAL_8 = 192 Midi_aftertouchchannel: NATURAL_8 = 208 Midi_pitchwheel: NATURAL_8 = 224 feature write_midiheader do output.extend (('M').to_character_8.code.to_natural_8) output.extend (('T').to_character_8.code.to_natural_8) output.extend (('h').to_character_8.code.to_natural_8) output.extend (('d').to_character_8.code.to_natural_8) output.extend (0) output.extend (0) output.extend (0) output.extend (6) output.extend (0) output.extend (0) output.extend (0) output.extend (1) output.extend (0) output.extend (70) output.extend (('M').to_character_8.code.to_natural_8) output.extend (('T').to_character_8.code.to_natural_8) output.extend (('r').to_character_8.code.to_natural_8) output.extend (('k').to_character_8.code.to_natural_8) output.extend (0) output.extend (0) output.extend (0) output.extend (0) end channelvelocities: ARRAY [NATURAL_8] -- Cached channel velocities do create Result.make_filled (127, 0, 15) end queuedtime: NATURAL_32 -- Timestamps between sequences of MUS events Controller_map: ARRAY [NATURAL_8] once create Result.make_filled (0, 0, 15 - 1) Result [0] := 0 Result [1] := 32 Result [2] := 1 Result [3] := 7 Result [4] := 10 Result [5] := 11 Result [6] := 91 Result [7] := 93 Result [8] := 64 Result [9] := 67 Result [10] := 120 Result [11] := 123 Result [12] := 126 Result [13] := 127 Result [14] := 121 end Channel_map: ARRAY [INTEGER_32] once create Result.make_filled (-1, 0, Num_channels - 1) end feature writetime (a_time: NATURAL_32) -- Write timestamp to a MIDI file local buffer: NATURAL_32 writeval: NATURAL_8 done: BOOLEAN time: NATURAL_32 do time := a_time from buffer := time & 127 time := time |>> 7 until time = 0 loop buffer := buffer |<< 8 buffer := buffer | ((time & 127) | 128) time := time |>> 7 end from done := False until done loop writeval := buffer.to_natural_8 & 255 output.extend (writeval) if buffer & 128 /= 0 then buffer := buffer |>> 8 else queuedtime := 0 done := True end end end writeendtrack -- Write the end of track marker do writetime (queuedtime) output.extend (255) output.extend (47) output.extend (0) end writepresskey (channel, key, velocity: NATURAL_8) -- Write a key press event do writetime (queuedtime) output.extend (Midi_presskey | channel) output.extend (key & 127) output.extend (velocity & 127) end writereleasekey (channel, key: NATURAL_8) -- Write a key release event do writetime (queuedtime) output.extend (Midi_releasekey | channel) output.extend (key & 127) output.extend (0) end writepitchwheel (channel: NATURAL_8; wheel: INTEGER_16) -- Write a pitch/bend event do writetime (queuedtime) output.extend (Midi_pitchwheel | channel) output.extend ((wheel).to_natural_8 & 127) output.extend ((wheel |>> 7).to_natural_8 & 127) end writechangepatch (channel, patch: NATURAL_8) -- Write a patch change event do writetime (queuedtime) output.extend (Midi_changepatch | channel) output.extend (patch & 127) end writechangecontroller_valued (channel, control, value: NATURAL_8) -- Write a valued controller change event local working: NATURAL_8 do writetime (queuedtime) output.extend (Midi_changecontroller | channel) output.extend (control & 127) working := value if (working & 128) /= 0 then working := 127 end output.extend (working) end writechangecontroller_valueless (channel, control: NATURAL_8) -- Write a valueless controller change event do writechangecontroller_valued (channel, control, 0) end feature allocatemidichannel: NATURAL_8 -- Allocate a free MIDI channel local max: INTEGER_32 i: INTEGER_32 do from max := -1 i := 0 until i >= Num_channels loop if Channel_map [i] > max then max := Channel_map [i] end i := i + 1 end Result := (max + 1).as_natural_8 if Result = Midi_percussion_chan then Result := Result + 1 end ensure Result >= 0 Result.to_integer_32 < Num_channels end getmidichannel (mus_channel: INTEGER_32): NATURAL_8 -- Given a MUS channel number, get the MIDI channel number to use -- in the outputted file do if mus_channel = Mus_percussion_chan.to_integer_32 then Result := Midi_percussion_chan else if Channel_map [mus_channel] = -1 then Channel_map [mus_channel] := allocatemidichannel.to_integer_32 writechangecontroller_valueless (Channel_map [mus_channel].as_natural_8, 123) end Result := Channel_map [mus_channel].as_natural_8 end end feature readmusheader (header: MUSHEADER) do header.Id [0] := read_natural_8 header.Id [1] := read_natural_8 header.Id [2] := read_natural_8 header.Id [3] := read_natural_8 header.scorelength := read_natural_16 header.scorestart := read_natural_16 header.primarychannels := read_natural_16 header.secondarychannels := read_natural_16 header.instrumentcount := read_natural_16 end mus2mid -- Read a MUS file from a stream (musinput) and output a MIDI file to -- a stream (midioutput) local musfileheader: MUSHEADER eventdescriptor: NATURAL_8 hitscoreend: BOOLEAN working: NATURAL_8 timedelay: NATURAL_32 eventdone: BOOLEAN timedone: BOOLEAN do create musfileheader readmusheader (musfileheader) pos := musfileheader.scorestart.to_integer_32 write_midiheader from until hitscoreend loop from eventdone := False until hitscoreend or eventdone loop eventdescriptor := read_natural_8 hitscoreend := read_event (eventdescriptor) if (eventdescriptor & 128) /= 0 then eventdone := True end end if not hitscoreend then from timedelay := 0 timedone := False until timedone loop working := read_natural_8 timedelay := timedelay * 128 + (working & 127).to_natural_32 if working & 128 = 0 then timedone := True end end queuedtime := queuedtime + timedelay end end writeendtrack output [19] := (tracksize |>> 24).to_natural_8 & 255 output [20] := (tracksize |>> 16).to_natural_8 & 255 output [21] := (tracksize |>> 8).to_natural_8 & 255 output [22] := tracksize.to_natural_8 & 255 end tracksize: NATURAL_32 do Result := output.count.as_natural_32 - 22 end read_event (eventdescriptor: NATURAL_8): BOOLEAN -- Read one event, return true if it was score end local channel: NATURAL_8 event: NATURAL_8 key: NATURAL_8 controllernumber: NATURAL_8 controllervalue: NATURAL_8 do channel := getmidichannel (eventdescriptor & 15.to_integer_32) event := eventdescriptor & 112 if event = Mus_releasekey then key := read_natural_8 writereleasekey (channel, key) elseif event = Mus_presskey then key := read_natural_8 if key & 128 /= 0 then channelvelocities [channel] := read_natural_8 & 127 end writepresskey (channel, key, channelvelocities [channel.to_integer_32]) elseif event = Mus_pitchwheel then key := read_natural_8 writepitchwheel (channel, key.to_integer_16 * 64) elseif event = Mus_systemevent then controllernumber := read_natural_8 check controllernumber >= 10 and controllernumber <= 14 end writechangecontroller_valueless (channel, Controller_map [controllernumber.to_integer_32]) elseif event = Mus_changecontroller then controllernumber := read_natural_8 controllervalue := read_natural_8 if controllernumber = 0 then writechangepatch (channel, controllervalue) else check controllernumber >= 1 and controllernumber <= 9 end writechangecontroller_valued (channel, Controller_map [controllernumber.to_integer_32], controllervalue) end elseif event = Mus_scoreend then Result := True else {I_MAIN}.i_error ("Unknown event " + event.out) end end feature make (a_input: MANAGED_POINTER) do create output.make (0) input := a_input pos := 0 end fill_output do mus2mid end output: ARRAYED_LIST [NATURAL_8] input: MANAGED_POINTER feature pos: INTEGER_32 read_natural_16: NATURAL_16 do Result := input.read_natural_16_le (pos) pos := pos + {PLATFORM}.integer_16_bytes end read_natural_8: NATURAL_8 do Result := input.read_natural_8_le (pos) pos := pos + {PLATFORM}.character_8_bytes end end -- class MUS2MID
Generated by ISE EiffelStudio