VocalEasel/mma/MMA/midifuncs.py

346 lines
8.0 KiB
Python
Raw Normal View History

2011-07-26 22:49:39 +00:00
# midifuncs.py
"""
This module is an integeral part of the program
MMA - Musical Midi Accompaniment.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Bob van der Poel <bob@mellowood.ca>
Low level entry points, mostly called directly from the parser.
"""
import gbl
import MMA.mdefine
from MMA.common import *
# non-track functions
def setTimeSig(ln):
""" Set the midi time signature. """
if len(ln) == 1:
a=ln[0].upper()
if a == 'COMMON':
ln=('4','4')
elif a == 'CUT':
ln=('2','2')
if len(ln) != 2:
error("TimeSig: Usage (num dem) or ('cut' or 'common')")
nn = stoi(ln[0])
if nn<1 or nn>126:
error("Timesig NN must be 1..126")
dd = stoi(ln[1])
if dd == 1: dd = 0
elif dd == 2: dd = 1
elif dd == 4: dd = 2
elif dd == 8: dd = 3
elif dd == 16: dd = 4
elif dd == 32: dd = 5
elif dd == 64: dd = 6
else:
error("Unknown value for timesig denominator")
MMA.midi.timeSig.set(nn,dd)
def midiMarker(ln):
""" Parse off midi marker. """
if len(ln) == 2:
offset = stof(ln[0])
msg = ln[1]
elif len(ln) == 1:
offset = 0
msg = ln[0]
else:
error("Usage: MidiMark [offset] Label")
offset = int(gbl.tickOffset + (gbl.BperQ * offset))
if offset < 0:
error("MidiMark offset points before start of file")
gbl.mtrks[0].addMarker(offset, msg)
def setMidiCue(ln):
""" Insert MIDI cue (text) event into meta track."""
if not ln:
error("MidiCue requires text.")
gbl.mtrks[0].addCuePoint(gbl.tickOffset, ' '.join(ln))
def rawMidi(ln):
""" Send hex bytes as raw midi stream. """
mb=''
for a in ln:
a=stoi(a)
if a<0 or a >0xff:
error("All values must be in the range 0 to 0xff, not '%s'" % a)
mb += chr(a)
gbl.mtrks[0].addToTrack(gbl.tickOffset, mb)
if gbl.debug:
print "Inserted raw midi in metatrack: ",
for b in mb:
print '%02x' % ord(b),
print
def setMidiFileType(ln):
""" Set some MIDI file generation flags. """
if not ln:
error("USE: MidiFile [SMF=0/1] [RUNNING=0/1]")
for l in ln:
try:
mode, val = l.upper().split('=')
except:
error("Each arg must contain an '=', not '%s'" % l)
if mode == 'SMF':
if val == '0':
gbl.midiFileType = 0
elif val == '1':
gbl.midiFileType = 1
else:
error("Use: MIDIFile SMF=0/1")
if gbl.debug:
print "Midi Filetype set to", gbl.midiFileType
elif mode == 'RUNNING':
if val == '0':
gbl.runningStatus = 0
elif val == '1':
gbl.runningStatus = 1
else:
error("Use: MIDIFile RUNNING=0/1")
if gbl.debug:
print "Midi Running Status Generation set to",
if gbl.runningStatus:
print 'ON (Default)'
else:
print 'OFF'
else:
error("Use: MIDIFile [SMF=0/1] [RUNNING=0/1]")
def setChPref(ln):
""" Set MIDI Channel Preference. """
if not ln:
error("Use: ChannelPref TRACKNAME=CHANNEL [...]")
for i in ln:
if '=' not in i:
error("Each item in ChannelPref must have an '='")
n,c = i.split('=')
c = stoi(c, "Expecting an integer for ChannelPref, not '%s'" % c)
if c<1 or c>16:
error("Channel for ChannelPref must be 1..16, not %s" % c)
gbl.midiChPrefs[n.upper()]=c
if gbl.debug:
print "ChannelPref:",
for n,c in gbl.midiChPrefs.items():
print "%s=%s" % (n,c),
print
def setMidiCopyright(ln):
""" Add a copyright message to the file. This is inserted into
the meta track at offset 0.
"""
if not ln:
error("MidiCopyright needs text message.")
gbl.mtrks[0].addCopyright(0, ' '.join(ln))
def setMidiName(ln):
""" Set global/meta track name. This will overwrite the song name set in main."""
if not ln:
error("Use: TrackName text")
gbl.mtrks[0].addTrkName(0, ' '.join(ln) )
def setMidiText(ln):
""" Set midi text into meta track."""
if not ln:
error("Use: MidiText text")
gbl.mtrks[0].addText(gbl.tickOffset, ' '.join(ln))
################################################
## Track functions
def trackGlis(name, ln):
""" Enable/disable portamento. """
if len(ln) != 1:
error("Use: %s MidiGlis NN, off=0, 1..127==on" % name)
gbl.tnames[name].setGlis(ln[0])
def trackPan(name, ln):
""" Set the Midi Pan value for a track."""
if len(ln)==1 or len(ln)==3:
gbl.tnames[name].setPan(ln)
else:
error("Use %s MidiPAN [Value] OR [Initvalue DestValue Beats]." % name)
def trackMidiText(name, ln):
""" Insert midi text event. """
if not ln:
error("Use: %s Text" % name)
# this calls func in pat.py since the event is queued and only
# sent if the track is created.
gbl.tnames[name].setMidiText(' '.join(ln))
def trackMidiCue(name, ln):
""" Insert MIDI cue (text) event."""
if not ln:
error("Use: %s TrackName" % name)
# this calls func in pat.py since the event is queued and only
# sent if the track is created.
gbl.tnames[name].setMidiCue(' '.join(ln))
def trackMidiExt(ln):
""" Helper for trackMidiSeq() and trackMidiVoice()."""
ids=1
while 1:
sp = ln.find("{")
if sp<0:
break
ln, s = pextract(ln, "{", "}", 1)
if not s:
error("Did not find matching '}' for '{'")
pn = "_%s" % ids
ids+=1
MMA.mdefine.mdef.set(pn, s[0])
ln = ln[:sp] + ' ' + pn + ' ' + ln[sp:]
return ln.split()
def trackMidiSeq(name, ln):
""" Set reoccurring MIDI command for track. """
if not ln:
error("Use %s MidiSeq Controller Data" % name)
if ln[0][0] != '{': # add {} wrapper if missing
ln.insert(0, '{')
ln.extend('}')
if len(ln) == 1 and ln[0]== '-':
gbl.tnames[name].setMidiSeq('-')
else:
gbl.tnames[name].setMidiSeq( trackMidiExt(' '.join(ln) ))
def trackMidiVoice(name, ln):
""" Set single shot MIDI command for track. """
if not ln:
error("Use %s MidiVoice Controller Data" % name)
if ln[0][0] != '{': # add {} wrapper if missing
ln.insert(0, '{')
ln.extend('}')
if len(ln) == 1 and ln[0] == '-':
gbl.tnames[name].setMidiVoice( '-' )
else:
gbl.tnames[name].setMidiVoice( trackMidiExt(' '.join(ln) ))
def trackMidiClear(name, ln):
""" Set MIDI command to send at end of groove. """
if not ln:
error("Use %s MIDIClear Controller Data" % name)
if len(ln) == 1 and ln[0] == '-':
gbl.tnames[name].setMidiClear( '-' )
else:
ln=' '.join(ln)
if '{' in ln or '}' in ln:
error("{}s are not permitted in %s MIDIClear command" % name)
gbl.tnames[name].setMidiClear( trackMidiExt( '{' + ln + '}' ))
def trackMidiName(name,ln):
""" Set channel track name."""
if not ln:
error("Use: %s TrackName" % name)
# this calls func in pat.py since the event is queued and only
# sent if the track is created.
gbl.tnames[name].setTname(ln[0])