# This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # mid2dxm # mid2dxm/conf.rb # mid2dxm/mid2dxm # echo c - mid2dxm mkdir -p mid2dxm > /dev/null 2>&1 echo x - mid2dxm/conf.rb sed 's/^X//' >mid2dxm/conf.rb << 'END-of-mid2dxm/conf.rb' X# Copyright (c) 2002 M.T. X# All rights reserved. X# X X$order = 0x01020304 X$title = "Title" X$artist = "Artist" X$genre = "Genre" X$copyright = "Copyright" X$creator = "Creator" X$composer = "Composer" X$lyrics = "Lyrics" X$arranger = "Arranger" END-of-mid2dxm/conf.rb echo x - mid2dxm/mid2dxm sed 's/^X//' >mid2dxm/mid2dxm << 'END-of-mid2dxm/mid2dxm' X#!/usr/local/bin/ruby X# X# Copyright (c) 2002-2003 M.T. X# All rights reserved. X# X# Redistribution and use in source and binary forms, with or without X# modification, are permitted provided that the following conditions X# are met: X# 1. Redistributions of source code must retain the above copyright X# notice, this list of conditions and the following disclaimer. X# 2. Redistributions in binary form must reproduce the above copyright X# notice, this list of conditions and the following disclaimer in the X# documentation and/or other materials provided with the distribution. X# 3. Neither the name of the author nor the names of its contributors may X# be used to endorse or promote products derived from this software X# without specific prior written permission. X# X# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS X# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT X# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR X# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT X# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, X# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT X# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, X# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY X# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT X# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE X# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. X# X Xrequire 'getopts' Xrequire 'nkf' X Xclass FsConv X DXM_TIMEUNIT = 24 X X def initialize X @smf_timeunit = DXM_TIMEUNIT X end X def convert(infile) X process_smf_header(infile) X process_smf_data(infile) X process_dxm X end X def output_smf X data = '' X data += @smf_header X data += number2chrs(@smf_data.size, 4) X data += @smf_data X return data X end X def output_dxm; @dxm_data; end X def process_smf_header(infile) X# begin Header Chunk X# Chunk Type X data = get(infile, 4) X raise "input file is not SMF!" if (data != "MThd") X @smf_header = "CThd" X# Data Length X @smf_header += get(infile, 4) X# Format X data = get(infile, 2) X raise "Can't read SMF format #{num}!" if (num = chrs2number(data)) != 0 X @smf_header += data X# Numer of Tracks X get(infile, 2) X @smf_header += number2chrs(1, 2) X# Time Unit X @smf_timeunit = chrs2number(get(infile, 2)) X @smf_header += number2chrs(DXM_TIMEUNIT, 2) X# end Header Chunk X X# begin Track Chunk X# Chunk Type X data = get(infile, 4) X raise "Can't read!" if (data != "MTrk") X @smf_header += "CTrk" X# read Data Length (and disposed) X get(infile, 4) X end X def process_smf_data(infile) X @smf_data = '' X @smf_delta_sum = 0 X @dxm_delta_sum = 0 X @start_program_number = Array.new(4, 0) X @program_number = Array.new(4, 0) X unprocessed_delta = 0 X while true X delta = get_delta(infile) X data = get_msg(infile) X if (!data.nil?) X @smf_data += number2delta(unprocessed_delta + delta2number(delta)) X @smf_data += data X unprocessed_delta = 0 X else X unprocessed_delta += delta2number(delta) X end X break if data == 0xff.chr + 0x2f.chr + 0.chr X end X end X def get(infile, bytes) X data = '' X for i in 0..(bytes - 1) do X data += infile.getc.chr X end X return data X end X def get_delta(infile) X data = '' X while true X c = infile.getc X data += c.chr X break if (c[7] == 0) X end X smf_delta = delta2number(data) X @smf_delta_sum += smf_delta X num = (@smf_delta_sum.to_f * DXM_TIMEUNIT / @smf_timeunit).to_i X dxm_delta = num - @dxm_delta_sum X @dxm_delta_sum = num X return number2delta(dxm_delta) X end X def get_msg(infile) X c = infile.getc X if (@prev_msg == c) X data = '' X else X data = c.chr X end X if (c == 0xff) X meta = get_meta(infile) X return nil if meta.nil? X data += meta X return data X end X if (c >> 4) < 0x8 X data = '' X infile.ungetc(c) X c = @prev_msg X end X case (c >> 4) X when 0x8, 0x9 X no = get_noteon(infile, c & 0xf) X return nil if no.nil? X data += no X when 0xb X cc = get_control_change(infile, c & 0xf) X return nil if cc.nil? X data += cc X when 0xc X pc = get_program_change(infile, c & 0xf) X return nil if pc.nil? X data += pc X when 0xe X pb = get_pitch_bend(infile, c & 0xf) X return nil if pb.nil? X data += pb X when 0xa, 0xd, 0xf X get_other(infile, c) X return nil X else Xraise X return nil X end X @prev_msg = c X return data X end X def get_meta(infile) X c = infile.getc X data = c.chr X case c X when 0x2f X data += infile.getc.chr X when 0x51 X c = infile.getc X data += c.chr X @time_per_beat = get(infile, c) X @tempo = (60e6 / chrs2number(@time_per_beat)).to_i X data += @time_per_beat X else X c = infile.getc X get(infile, c) X return nil X end X return data X end X def get_noteon(infile, channel) X _data = infile.getc.chr X c = infile.getc X return nil if (c == 0 && channel == 9) \ X || !(channel <= 3 || channel == 9) X _data += c.chr X return _data X end X def get_control_change(infile, channel) X c = infile.getc X _data = c.chr X c2 = infile.getc.chr X return nil if !(channel <= 3 || channel == 9) X case c X when 1 X _data += c2 X when 7 X _data += c2 X when 0x40 X _data += c2 X else X return nil X end X return _data X end X def get_program_change(infile, channel) X c = infile.getc X return nil if !(channel <= 3 || channel == 9) X @start_program_number[channel] = c if (@dxm_delta_sum == 0 && channel != 9) X @program_number[channel] = c X return c.chr X end X def get_pitch_bend(infile, channel) X data = get(infile, 2) X return nil if !(channel <= 3 || channel == 9) X return data X end X def get_other(infile, c) X if (c >> 4) == 0xf X get(infile, delta2number(get_delta(infile))) X else X c2 = infile.getc X return if ((c >> 4) == 0xd) X infile.getc X end X end X def chrs2number(chrs) X num = 0 X for i in 1..(chrs.size) do X num += chrs[i - 1] * (256 ** (chrs.size - i)) X end X return num X end X def number2chrs(number, bytes) X data = '' X for i in 0..(bytes - 1) do X data += ((number >> ((bytes - 1 - i) * 8)) & 0xff).chr X end X return data X end X def delta2number(delta) X num = 0 X for i in 1..(delta.size) do X num += (delta[i - 1] & 0x7f) * (128 ** (delta.size - i)) X end X return num X end X def number2delta(number) X num = number X data = (num & 0x7f).chr X while true X num = (num >> 7) X break if num == 0 X data = ((num & 0x7f) | 0x80).chr + data X end X return data X end X def process_dxm X pn_data = '' X for item in @start_program_number do X pn_data += item.chr X end X if (chrs2number(pn_data) > 0) X pn_size = 4 X else X pn_size = 0 X end X smf = output_smf X create_time = 0 X for item in Time.now.strftime("%Y,%m,%d,%H,%M,%S").split(",") do X create_time = (create_time << 8) X create_time += item.to_i X end X X dxm_directive_array = [[0x0000, 4, 0x30312e30], X [0x0001, 0, 0], X [0x0010, 4, 0xffff], X [0x0011, 10, 0x0010000000000000ffff], X [0x0020, 0, 0], X [0x0021, 0, 0], X [0x0030, 0, 0], X [0x0031, 0, 0], X [0x0200, 0, 0], X [0x0201, 0, 0], X [0x0202, 2, @tempo], # Tempo X [0x0203, 4, $order], #Melody Bass etc. order X [0x0204, 1, 0], X [0x0205, pn_size, pn_data], # Program Numbers when play start time, can omit X [0x0240, smf.size, smf], # SMF Data X [0x0280, 4, (((chrs2number(@time_per_beat).to_f * @dxm_delta_sum / (DXM_TIMEUNIT * 1000)) + 0.99).to_i)], # Time per beat * sum of delta time / Time unit(24) X [0x0281, 4, smf.size], # SMF Data Length X [0x0282, 4, 0x32383536], X [0x0283, 8, create_time], # Create time, 00ccyymmddhhmmss X [0x0284, 0, 0], X [0x0285, 1, 0], X [0x0286, 0, 0], X [0x02C0, $title.size, chrs2number($title)], # Title, Can omit X [0x02C1, $artist.size, chrs2number($artist)], # Artist, Can omit X [0x02C2, $genre.size, chrs2number($genre)], # Genre, Can omit X [0x02C3, $copyright.size, chrs2number($copyright)], # Copyright, Can omit X [0x02C4, $creator.size, chrs2number($creator)], # Creator, Can omit X [0x02C5, $composer.size, chrs2number($composer)], # Composer, Can omit X [0x02C6, $lyrics.size, chrs2number($lyrics)], # Lyrics, Can omit X [0x02C7, $arranger.size, chrs2number($arranger)], # Arranger, Can omit X [0xFFFF, 0, 0]] X X order = [0x0000, 0x0010, 0x0011, 0x0202, 0x0203, X 0x0204, 0x0205, 0x0280, 0x0281, X 0x0282, 0x0283, 0x0285, X 0x02C0, 0x02C1, 0x02C2, 0x02C3, X 0x02C4, 0x02C5, 0x02C6, 0x02C7, X 0x0240] X X datastart_addr = dxm_directive_array.size * 10 + 4 X idx = 0 X X for item in order do X for directive in dxm_directive_array do X if (directive[0] == item) X directive.push(datastart_addr + idx) X idx += directive[1] X break X end X end X end X @dxm_data = "MCDF" X for directive in dxm_directive_array do X @dxm_data += (directive[0] >> 8).chr X @dxm_data += (directive[0] & 0xff).chr X if (directive[1] == 0) X for i in 0..7 do X @dxm_data += 0.chr X end X else X @dxm_data += number2chrs(directive[3], 4) X @dxm_data += number2chrs(directive[1], 4) X end X end X X # begin data X X for item in order do X for directive in dxm_directive_array do X if (directive[0] == item) X if (directive[2].kind_of?(Numeric)) X @dxm_data += number2chrs(directive[2], directive[1]) X else X for i in 1..(directive[1]) do X @dxm_data += directive[2][i - 1].chr X end X end X break X end X end X end X end # process_dxm X X def convert_dxm2mid(infile) X raise "input file is not DXM!" if (get(infile, 4) != "MCDF") X position = 4 X headers = Array.new X while (true) X id = get(infile, 2) X add = get(infile, 4) X data = get(infile, 4) X position += 10 X headers.push([id, add, data]) X break if (chrs2number(id) == 0xffff) X end X smfdata = headers.assoc(0x02.chr + 0x40.chr) X get(infile, chrs2number(smfdata[1]) - position) X return get(infile, chrs2number(smfdata[2])).sub("CThd", "MThd").sub("CTrk", "MTrk") X end # convert_dxm2mid Xend # class FsConv X Xdef usage X if ($0 =~/dxm2mid/) X $stderr.print "Usage: #{$0} [-o outputfile] source.dxm\n" X else X $stderr.print <