#!/usr/bin/python #### #### This is GPL'ed software #### from sys import * from sched import * from time import * from string import * from os import environ user_start_argument = 0 try: envport = environ['MIDIPLAYPORT'] outport = open(envport,'w') except: outport =open('/dev/ttyS0','w') only_channel = -1 ### default, play all channels ! if len(argv) > 2: if '-t' in argv: time_argument = argv.index('-t') + 1 time_argument = argv[time_argument] time_argument = time_argument.split(':') time_argument[0] = int(time_argument[0])*60 time_argument[1] = int(time_argument[1]) user_start_argument = time_argument[0]+time_argument[1] running_status = 1 status_byte = 0x90 if '-p' in argv: port_argument = argv.index('-p') + 1 port_argument = argv[port_argument] outport = open(port_argument,'w') if '-c' in argv: only_channel = argv.index('-c') + 1 only_channel = argv[only_channel] only_channel = int(only_channel)-1 midi_file = open(argv[-1],'r') ### file is last argument midi_file.seek(-1,2) song_end = midi_file.tell() midi_file.seek(0) tempo = 500000 bendstring = '' running_status = 0 def rel_time(): global start_time, user_start_argument return (time()-start_time) + user_start_argument s = scheduler(rel_time,sleep) def portwrite(string): global outport outport.write("%s" % string) outport.flush() def all_notes_off(): global outport for channel in range(16): outport.write("%c%c%c" % (0xB0+channel, 123, 0)) outport.flush() def read_var_length(): output = 0 inbyte = ord(midi_file.read(1)) output = inbyte & 0x7f while (inbyte & 0x80) == 0x80: inbyte = ord(midi_file.read(1)) output = (output << 7 ) + (inbyte & 0x7f) return output def header_read(): global midi_file,type,tracks,timebase if midi_file.read(4) != 'MThd': print "Not a midi file, sorry" exit() midi_file.read(4) ###pass the '00 00 00 06' midi_file.read(1) type = ord(midi_file.read(1)) midi_file.read(1) tracks = ord(midi_file.read(1)) if type == 0 and tracks != 1: print "Midi files of type 0 should only have one track." exit() timebase = (ord(midi_file.read(1)) << 8) + ord(midi_file.read(1)) def track_read(): global midi_file,song_end,miditime if midi_file.read(4) != 'MTrk': print "No midi track header found at 0x%x" % (midi_file.tell()-4) exit() track_length = (ord(midi_file.read(1)) << 24) + (ord(midi_file.read(1)) << 16) + (ord(midi_file.read(1)) << 8) + ord(midi_file.read(1)) miditime = 0 #### zero our track time song_position = midi_file.tell() #### where are we in the file? if track_length == 0: track_end = song_end else: track_end = song_position + track_length while song_position < track_end : parse() song_position = midi_file.tell() def parse(): global midi_file,miditime,tempo,timebase,running_status,status_byte,status_byte_size,bendstring delta = read_var_length() miditime = miditime + ((delta/(timebase+0.0))*(tempo/1000000.0)) ##### check to see if next byte is a status byte: byte = midi_file.read(1) if ord(byte) == 0xFF : ##### meta events meta_byte = ord(midi_file.read(1)) if meta_byte == 0x51: midi_file.read(1) ### pass the superfluous '03' length, we know what to do! tempo = (ord(midi_file.read(1)) << 16) + (ord(midi_file.read(1)) << 8) + ord(midi_file.read(1)) elif meta_byte == 0x2f: midi_file.read(1) else: ### we don't care what the meta_event is, buddy!!!! text_length = read_var_length() midi_file.read(text_length) ##### skip it! elif (ord(byte) == 0xF0) or (ord(byte) == 0xF7): #### sysex events sysex_length = read_var_length() sysex_message = [midi_file.read(sysex_length)] s.enterabs(miditime, 1, portwrite, (sysex_message)) elif (0xC0 <= ord(byte) <= 0xD0): ##### program change, aftertouch running_status = 1 status_byte = ord(byte) status_byte_size = 1 outstring = ["%c%c" % (byte, midi_file.read(1))] s.enterabs(miditime, 1, portwrite, (outstring)) elif (ord(byte) & 0x80) == 0x80: #### everything else running_status = 1 status_byte = ord(byte) status_byte_size = 2 outstring = ["%c%s" % (byte, midi_file.read(2))] if (only_channel == -1) or (status_byte & 0x0F == only_channel): if (status_byte & 0xF0) == 0xE0: s.enterabs(miditime, 1, portwrite, (outstring)) else: s.enterabs(miditime, 2, portwrite,(outstring)) elif running_status: midi_file.seek(-1,1) outstring = ["%c%s" % (status_byte,midi_file.read(status_byte_size))] s.enterabs(miditime, 1, portwrite, (outstring)) try: header_read() for x in range(tracks): track_read() ##### load the sequencer queue if type == 1: ##### this is only necessary print "wait a sec...sorting the type 1 midi_tracks into sequential order....." s.queue.sort() ##### if the file has separate midi tracks print "ok...playing....." if type == 2: print "sorry, type 2 midi files are not playable from this sequencer." exit() if user_start_argument > 0: queue_index = 0 try: while s.queue[queue_index][0] < user_start_argument: queue_index += 1 except: print "time argument too big for this file." print "fool around with a smaller value..." exit() s.queue.__delslice__(0,queue_index) start_time = time() #### play reference point starts now s.run() #### run the sequencer queue midi_file.close() outport.close() except KeyboardInterrupt: all_notes_off() midi_file.close() outport.close()