Guidance
指路人
g.yi.org
Software / Reginald / Examples / rxmidi examples / disasm.rex

Register 
新用户注册
Search 搜索
首页 
Home Home
Software
Upload

  
/*
GUIBEGIN
WINDOW , 62, 149, 337, 41, POPUP|CAPTION|SYSMENU|MINBOX|MAXBOX|THICK, , MIDI DisAssembler
	FONT 8, 400, MS Shell Dlg
	TEXT 5, 29, 327, 8, GROUP, , Message, , Click on "Pick file" to convert a MIDI file to text.
	PUSH 145, 4, 40, 14, TABSTOP, , PickButton, ALT "P", &Pick file
DEND
GUIEND
*/

/* This is a REXX version of the MIDI File Disassembler. It converts
 * a MIDI file into a specially formatted text file. It also uses REXX
 * GUI for the file dialog and main window.
 */

OPTIONS "C_CALL LABELCHECK NOSOURCE"

/* Load REXXGUI.DLL, RXMIDI.DLL, and register all their functions. */
LIBRARY rexxgui, rxmidi

/* Let RxMidi functions raise ERROR condition if they fail. So, we don't have
 * to check the return values from functions such as MIDISetEvent().
 */
midierr = "ERROR"
midiheading = 1

/* Let Reginald raise SYNTAX condition for any error in a Gui function call. */
guierr = "SYNTAX"
guiheading = 1

/* Create our window, and activate/show it */
guicreatewindow('NORMAL')

/* If the user supplied the text filename when he ran this script, then load/convert it. */
tofilename = ""
fromfilename = ARG(1)
IF fromfilename \== "" THEN converttext()

again:
DO FOREVER

	guigetmsg()

	/* None of our handlers below calls GuiWake(). Also, we have no Child Window Layout
	 * scripts. So we omit further checks of GuiObject/GuiSignal.
	 */

	CATCH NOTREADY
		CONDITION('M')
		SIGNAL again

	CATCH ERROR
		CONDITION('M')
		SIGNAL again

	CATCH SYNTAX
		CONDITION('M')
		SIGNAL again

	FINALLY
		guidestroywindow()
END
RETURN











/* Called by Reginald when user clicks on the "Pick file" button. */

wm_click_pickbutton:

/* We call GuiGetMsg() below. If ToFilename isn't "" then, he has
 * clicked the "Pick file" button during a conversion. In that case,
 * we abort the conversion.
 */
IF tofilename \== "" THEN DO
	tofilename = ""
	guiaddctltext("PickButton", "Aborting...")
	RETURN
END

/* Let the user pick the text file. */
DO
	guifile('FromFilename', 'EXISTING', 'Select a MIDI file to convert to text', 'MIDI files (*.mid, *.rmi) | *.mid;*.rmi | All files (*.*) | *.*')

	CATCH SYNTAX
		IF CONDITION('D') \== "CANCEL" THEN CONDITION('M')
		RETURN
END

converttext:

/* Create the text file name from the MIDI filename by chopping
 * off any .MID extension and replacing with .TXT.
 */
tofilename = EDITNAME(fromfilename, "*.txt", "U")

DO
	/* Change "Pick file" to "Loading..." */
	guiaddctltext("PickButton", "Loading...")

	/* Load the MIDI file into RAM. */
	MIDIOpenFile(fromfilename)

	/* Open/Overwrite the text file. */
	STREAM(tofilename, 'C', 'OPEN WRITE REPLACE')

	/* Format the MThd line. */
	LINEOUT(tofilename, "MThd | Format="||midigetinfo('TYPE')||" | # of Tracks="MIDIGetInfo('TRKS')||" | Division="||midigetinfo('BASE'))

	/* Assume key signature of C. */
	key = 0

	/* Move to the start of the track (select this track as the "currently selected track") */
	DO WHILE MIDITrack("") \== 0

		/* Add a blank line. */
		LINEOUT(tofilename,"")

		/* Print the track heading. */
		LINEOUT(tofilename,"Track #"||miditrack() - 1 "******************************************")

		/* Don't omit any time components for the first event in this track. */
		measure = ""
		beat = ""
		clock = ""

		/* Check if the CLOSE BOX has been clicked. We do this only at the beginning of
		 * each track being disassembled. We could put it inside the DO WHILE loop below
		 * to allow a quicker response to the user wanting to abort (ie, abort during
		 * writing out a track), but that can slow down the loop too.
		 */
		guigetmsg('CLEAR')
		IF tofilename == "" THEN RETURN		

		/* For each event in this track... */
		DO WHILE MIDIGetEvent() == ""

			/* Format the time. We pad the Measures:Beats:Clocks to fit nicely in a column,
			 * and omit times that are the same as the previous event.
			 */
			line = ""
			IF measure \== midievent.!measure THEN DO
				line = LEFT("", 4 - LENGTH(midievent.!measure)) || midievent.!measure || ':'
				spaces = 0
			END
			ELSE spaces = 5

			IF spaces == 0 | beat \== midievent.!beat THEN DO
				line = line || LEFT("", (spaces + 2) - LENGTH(midievent.!beat)) || midievent.!beat || ':'
				spaces = 0
			END
			ELSE spaces = spaces + 3

			IF spaces == 0 | clock \== midievent.!clock THEN DO
				line = line || LEFT("", (spaces + 3) - LENGTH(midievent.!clock)) || midievent.!clock
				spaces = 0
			END
			ELSE spaces = spaces + 3

			IF spaces \== 0 THEN line = LEFT("", spaces)

			/* Save these times to compare to the next event's times. */
			measure = midievent.!measure
			beat = midievent.!beat
			clock = midievent.!clock

			/* Get the event name (and save the status). */
			type = midievent.!type
			midievent.!type = MIDIEventProp()

			/* A MIDI Voice Category message? (These are messages that have a MIDI channel). */
			IF midievent.!channel \== "" THEN DO

				/* Format the type and Channel */
				IF midievent.!channel < 10 THEN spaces = " "
				ELSE spaces = ""
				line = line || " |" || midievent.!type || LEFT("", 12 - LENGTH(midievent.!type)) || "| chan=" || spaces || midievent.!channel || "   | "

				/* Write out the line so far, replacing spaces with TABs. */
				line = EXPAND(line, 'T')
				CHAROUT(tofilename, line)

				/* Write out this event's remaining information upon one text line. Each
				 * piece of info is separated by a '|' character.
				 */
				SELECT type

					/* On Note or (Off) Note */
					WHEN 144 THEN DO
						IF midievent.!data2 \== 0 THEN LINEOUT(tofilename, "pitch=" || MIDINoteName(key) "| vol=" || midievent.!data2)
						ELSE LINEOUT(tofilename, "pitch=" || MIDINoteName(key))
					END

					/* Off Note */
					WHEN 128 THEN LINEOUT(tofilename, "pitch=" || MIDINoteName(key) "| vol=" || midievent.!data2)

					/* Aftertouch */
					WHEN 160 THEN LINEOUT(tofilename, "pitch=" || MIDINoteName(key) "| press=" || midievent.!data2)

					/* Controller */
					WHEN 176 THEN LINEOUT(tofilename, "contr=" || MIDICtlName() "| value=" || midievent.!data2)

					/* Program */
					WHEN 192 THEN DO
						spaces = ""
						IF midievent.!data1 < 100 THEN spaces = " "
						IF midievent.!data1 < 10 THEN spaces = "  "
						LINEOUT(tofilename, "pgm #=" || spaces || midievent.!data1 + 1)
					END /* Program */

					/* Poly Press */
					WHEN 208 THEN LINEOUT(tofilename, "press=" || midievent.!data1)

					/* Pitch Wheel */
					WHEN 224 THEN LINEOUT(tofilename, "bend=" || midievent.!data3)

				END /* SELECT */

			END /* Voice messages */

			/* An event other than a MIDI Voice Message. Write out this event's information
			 * upon one text line (except for Sysex, and text type of events). Each piece of
			 * info is separated by a '|' character.
			 */
			ELSE DO

				/* If a Port # event, we're going to substitute a Device Name */
				IF type == 33 THEN DO
					type = 9
					midievent.!type = MIDIEventProp(, 9)
					midievent.!data1 = MIDIPortName()
				END

				/* Format the type */
				line = line || " |" || midievent.!type || LEFT("", 12 - LENGTH(midievent.!type)) || "| "

				/* Write out the line so far, replacing spaces with TABs */
				line = EXPAND(line, 'T')
				CHAROUT(tofilename, line)

				/* Write out the remainder of this event, depending upon its type */

				/* Text Meta-events (ie, "Text" to "Device Name"), "Specific", "Unknown Meta" */
				IF (type \== 0 & type < 10) | type == 127 THEN DO

					len = LENGTH(midievent.!data1)

					LINEOUT(tofilename, "")

					/* We need to write out 11 characters per line -- first in hex, and then the actual characters enclosed in <> */
					bytes = 1
					line = "            "
					line2 = "<"
					DO total = 1 TO len

						/* Extract the next character */
						chr = SUBSTR(midievent.!data1, total, 1)

						/* Convert to hex and append to Line */
						IF bytes \== 1 THEN line = line || " "
						line = line || "0x" || C2X(chr)

						/* Append character to the end of Line2. If not a printable char, substitute a '.' */
						IF chr <<= '19'x | char >> '7F'x THEN chr = '.'
						line2 = line2 || chr

						/* Increment count of chars on this line */
						bytes = bytes + 1

						/* Time to write out this line (ie, 11 chars formatted)? */
						IF bytes > 11 THEN DO
							LINEOUT(tofilename, EXPAND(line || LEFT("", 67 - LENGTH(line)) || line2 || ">", 'T'))
							bytes = 1
							line = "            "
							line2 = "<"
						END
					END

					/* Write out any partial, final line */
					IF bytes \== 1 THEN LINEOUT(tofilename, EXPAND(line || LEFT("", 67 - LENGTH(line)) || line2 || ">", 'T'))

				END /* Text Meta-events */

				/* Sysex */
				/* First Packet */
				/* Packet */
				/* Last Packet */
				ELSE IF type == 240 | type == 247 | type == 255 THEN DO

					LINEOUT(tofilename, "")

					/* We want to write out 11 bytes per line */
					total = 0
					DO WHILE total < midievent.!data1

						/* Retrieve the next 11 bytes, formatted C style hex */
						chr = MIDISysex(11, total + 1, 'C')

						/* Write the line */
						LINEOUT(tofilename, "            " || chr)

						/* Next 'start' */
						total = total + 11
					END

				END /* Sysex */

				ELSE SELECT type

					/* Seq # */
					WHEN 0 THEN LINEOUT(tofilename, midievent.!data1)

					/* Chan # */
					WHEN 32 THEN LINEOUT(tofilename, midievent.!data1)

					/* Tempo */
					WHEN 81 THEN LINEOUT(tofilename, "BPM=" || midievent.!data2 || LEFT("           ", 6 - LENGTH(midievent.!data2)) || "| micros\quarter=" || midievent.!data1)

					/* Time Sig */
					WHEN 88 THEN DO
						spaces = " "
						pad = 1
						IF LENGTH(midievent.!data1) > 1 THEN DO
							spaces = ""
							pad = 0
						END
						LINEOUT(tofilename, spaces || midievent.!data1 || "/" || midievent.!data2 || LEFT("           ", 9 - (LENGTH(midievent.!data1) + LENGTH(midievent.!data2) + pad)) || "| MIDI-clocks\click=" || midievent.!data3 "| 32nds\quarter=" || midievent.!data4)
					END

					/* SMPTE */
					WHEN 84 THEN LINEOUT(tofilename, "hour=" || midievent.!data1 "| min=" || midievent.!data2 "| sec=" || midievent.!data3 "| frame=" || midievent.!data4 "| subs=" || midievent.!data5)

					/* Key Sig */
					WHEN 89 THEN DO
						/* Save the sharps/flats so that we can pass this to MIDINoteName() */
						key = midievent.!data1
						IF key = 0 & midievent.!data2 == 1 THEN key = -1
						LINEOUT(tofilename, midievent.!data3)
					END

					/* MTC Quarter Frame */
					WHEN 241 THEN LINEOUT(tofilename, midievent.!data1)

					/* Song Position Pointer */
					WHEN 242 THEN LINEOUT(tofilename, midievent.!data3)

					/* Song Select */
					WHEN 243 THEN LINEOUT(tofilename, midievent.!data1)

					/* End of Track */
					/* Tune Request */
					/* MIDI Clock */
					/* MIDI Start */
					/* MIDI Stop */
					/* MIDI Continue */
					/* Active Sense */
					/* These all have no additional fields to write out */
					OTHERWISE LINEOUT(tofilename, "")

				END /* SELECT */

			END /* Non-Voice message. */

		END /* DO WHILE MIDIGetEvent. */

	END /* DO UNTIL MIDITrack. */

	FINALLY
		/* Close the text file. */
		STREAM(tofilename, 'C', 'CLOSE')

		/* Change back to "Pick file". */
		guiaddctltext("PickButton", "Pick file")

		/* Indicate that the disassembly is done. */
		guiaddctltext('Message', 'Done converting' TRANSLATE(tofilename, "/", "\"))

		/* No longer converting a file. */
		tofilename = ""
END

RETURN
掌柜推荐
 
 
 
 
 
 
 
 
 
 
 
 
© Tue 2017-9-26  Guidance Laboratory Inc.
Email:webmaster1g.yi.org Hits:0 Last modified:2010-07-16 20:49:22