VocalEasel/mma/MMA/pat.py

2235 lines
63 KiB
Python
Raw Permalink Normal View History

2006-11-10 08:07:56 +00:00
# pat.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
2007-04-29 06:47:40 +00:00
Bob van der Poel <bob@mellowood.ca>
2006-11-10 08:07:56 +00:00
"""
import copy
import random
import math
2011-07-26 22:49:39 +00:00
import MMA.notelen
2006-11-10 08:07:56 +00:00
import MMA.translate
import MMA.midi
import MMA.midiC
import MMA.mdefine
import MMA.volume
2009-05-17 22:34:44 +00:00
import MMA.alloc
import MMA.seqrnd
import gbl
from MMA.common import *
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
pats = {} # Storage for all pattern defines
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
def getIncDec(v):
""" See if 'v' starts with -/+. Strip and return. """
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
if v.startswith('-'):
incr = -1
v = v[1:]
elif v.startswith('+'):
incr = 1
v = v[1:]
else:
incr = 0
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
return incr, v
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
def getIncDecValue(orig, inc, new):
""" Set value based on original, increment and new values. """
if inc == -1:
return orig - new
elif inc == 1:
return orig + new
else:
return new
def getRndPair(l, trk, min, max):
""" Parse off a pair (or single) value for random settings (rvolume, etc.). """
if not l:
error("%s: expecting a value or value pair." % trk)
if l.count(',')>1:
error("%s: Random pairs can only have one ','!" % trk)
if l.count(',') == 1:
n1, n2 = l.split(',')
try:
n1 = int(n1)
n2 = int(n2)
except:
error("%s: Expecting integers, not '%s' or '%s'." % (trk, n1, n2))
else:
n1 = l.strip()
try:
n1 = int(n1)
except:
error("%s: Expecting integer, not '%s'." % trk)
n2 = n1 * -1
if n2 < n1:
n1, n2 = n2, n1
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
if n1 < min or n1 > max or n2 < min or n2 > max:
error("%s: Max range is %s to %s." % (trk, min, max) )
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
return (n1, n2)
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
class PC:
""" Pattern class.
Define classes for processing drum, chord, arp, and chord track.
These are mostly the same, so we create a base class and derive
the others from it.
We have a class for each track type. They are all derived
from the base PC class. Some classes have their own __init__().
They call back here after doing their own special stuff.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def __init__(self, nm):
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
self.inited = 0
self.name = nm
self.channel = 0
self.grooves = {}
self.saveVols = {}
self.ssvoice = -1 # Track the voice set for the track
self.smidiVoice = () # Track MIDIVoice cmds to avoid dups
self.midiSent = 0 # if set, MIDICLEAR invoked.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
""" Midi commands like Pan, Glis, etc. are stacked until musical
data is actually written to the track. Each item in
the midiPending list is a name (PAN, GLIS, etc), timeoffset, value.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.midiPending = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.riff = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.disable = 0
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.clearSequence()
2011-07-26 22:49:39 +00:00
2009-05-17 22:34:44 +00:00
self.nextVolume = None
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.inited = 1
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
if gbl.muteTracks and nm not in gbl.muteTracks:
self.disable = 1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
##########################################
## These are called from process() to set options
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setCompress(self, ln):
""" set/unset the compress flag. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, '%s Compress' % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
tmp = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
n = stoi(n, "Argument for %s Compress must be a value" \
% self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if n < 0 or n > 5:
error("Compress %s out-of-range; must be 0 to 5" % n)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if n and self.vtype=='CHORD' and self.voicing.mode:
vwarn = 1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
tmp.append(n)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.compress = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype not in ("CHORD", "ARPEGGIO"):
warning ("Compress is ignored in %s tracks" % self.vtype)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s Compress to:" % self.name,
printList(self.compress)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setRange(self, ln):
""" set range. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, '%s Range' % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
tmp = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
n = stof(n)
if n == 0:
n=1
if n <= 0 or n >= 6:
error("Range %s out-of-range; must be between 0..6, not %s" % (self.name, n))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
tmp.append(n)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.chordRange = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype not in ("SCALE", "ARPEGGIO", "ARIA"):
warning ("Range ignored in '%s' tracks" % self.vtype)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s Range to:" % self.name,
printList(self.chordRange)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setVoicing(self, ln):
""" set the Voicing Mode options.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
This is a stub. The real code is in patChord.py (settings are
only valid for that type). """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
error("Voicing is not supported for %s tracks" % self.vtype)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setForceOut(self):
""" Set the force output flag. This does 2 things: assigns
a midi channel and sends the voicing setting to the track.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if not self.channel:
self.setChannel()
self.clearPending()
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.insertVoice()
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setDupRoot(self, ln):
""" set/unset root duplication.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
This is a stub. Only valid for CHORDs and that is where the code is."""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
warning("RootDup has no effect in %s tracks" % self.vtype)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setChordLimit(self, ln):
""" set/unset the chordLimit flag. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
n = stoi(ln, "Argument for %s ChordLimit must be a value" % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if n < 0 or n > 8:
error("ChordLimit %s out-of-range; must be 0 to 8" % n)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.chordLimit = n
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype not in ("CHORD", "ARPEGGIO"):
warning ("Limit is ignored in %s tracks" % self.vtype)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s ChordLimit to %s" % (self.name, n)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setChannel(self, ln=None):
""" Set the midi-channel number for a track.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
- Checks for channel duplication
- Auto assigns channel number if ln==''
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
If no track number was passed, then we try to
auto-alloc a track. First, we see if a preference
was set via MidiChPref. If these is no preference,
or if the preferred channel is already allocated
we go though the list, top to bottom, to find
an available channel.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if not ln:
try:
c=gbl.midiChPrefs[self.name]
except:
c=0
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if not c or gbl.midiAvail[c]:
c=-1
for a in range(16, 0, -1):
if a!=10 and not gbl.midiAvail[a]:
c=a
break
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if c < 0:
error("No MIDI channel is available for %s,\n"
"Try CHShare or Delete unused tracks" % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
else:
c = stoi(ln, "%s Channel assignment expecting Value, not %s" %
(self.name, ln))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if c<0 or c>16:
error("%s Channel must be 0..16, not %s" % (self.name, ln))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if c == 10:
if self.vtype == 'DRUM':
pass
elif self.vtype in ('SOLO', 'MELODY') and self.drumType:
pass
else:
error("Channel 10 is reserved for DRUM, not %s" % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype == 'DRUM' and c != 10:
error("DRUM tracks must be assigned to channel 10")
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
# Disable the channel.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if c == 0:
if gbl.midiAvail[self.channel]:
gbl.midiAvail[self.channel] -= 1
s="%s channel disabled" % self.name
if gbl.midiAvail[self.channel]:
s+=" Other tracks are still using channel %s" % self.channel
else:
s+=" Channel %s available" % self.channel
warning(s)
self.channel = 0
self.disable = 1
return
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if c != 10:
for a, tr in gbl.tnames.items():
if a == self.name: # okay to reassign same number
continue
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if tr.channel == c:
error("Channel %s is assigned to %s" % (c, tr.name ) )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.channel = c
if not self.name in gbl.midiAssigns[c]:
gbl.midiAssigns[c].append(self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
gbl.midiAvail[c]+=1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if not c in gbl.mtrks:
gbl.mtrks[c]=MMA.midi.Mtrk(c)
offset=0
if gbl.debug:
print "MIDI channel %s buffer created" % c
else:
offset = gbl.tickOffset
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if c != 10:
f=0
for a, i in enumerate(self.midiPending):
if i[0]=='TNAME':
f=1
if not f:
self.midiPending.append(('TNAME', 0, self.name.title() ))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "MIDI Channel %s assigned to %s" % (self.channel, self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setChShare(self, ln):
""" Share midi-channel setting. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.channel: # If channel already assigned, ignore
warning("Channel for %s has previously been assigned "
"(can't ChShare)" % self.name)
return
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
""" Get name of track to share with and make sure it exists.
2009-05-17 22:34:44 +00:00
If not, trackAlloc() will create the track. Do some
sanity checks and ensure that the shared track has
a channel assigned.
2007-04-29 06:47:40 +00:00
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
sc = ln.upper()
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
MMA.alloc.trackAlloc(sc, 1)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if not sc in gbl.tnames:
error("Channel '%s' does not exist. No such name" % sc)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if sc == self.name:
error("%s can't share MIDI channel with itself" % sc)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if not gbl.tnames[sc].channel:
gbl.tnames[sc].setChannel()
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
schannel = gbl.tnames[sc].channel
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if not schannel:
error("CHShare attempted to assign MIDI channel for %s, but "
"none avaiable" % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
""" Actually do the assignment. Also copy voice/octave from
base track to this one ... it's going to use that voice anyway?
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.channel = schannel
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.voice = gbl.tnames[sc].voice[:]
self.octave = gbl.tnames[sc].octave[:]
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
# Update the avail. lists
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
gbl.midiAssigns[self.channel].append(self.name)
gbl.midiAvail[self.channel]+=1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setChannelVolume(self, v):
2011-07-26 22:49:39 +00:00
""" LowLevel MIDI command. Set Channel Volume. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.midiPending.append(( "CVOLUME", gbl.tickOffset, v) )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s MIDIChannelVolume to %s" % (self.name, v)
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
def setMidiCresc(self, v1, v2, count):
""" Low level MIDI (de)cresc channel volume."""
t = v2-v1 # total number of steps
step = (count * gbl.BperQ * gbl.QperBar) / abs(t) # step rate
p=gbl.tickOffset
if v2<v1:
dir=-1
else:
dir=1
for v in range(v1,v2+dir,dir):
self.midiPending.append(("CVOLUME", int(p), v))
p+=step
if gbl.debug:
print "%s MIDIChannelVolume: Added %s changes" % (self.name, t)
2007-04-29 06:47:40 +00:00
def setTname(self, n):
""" Set the track name.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
This is stacked and only gets output if track generates MIDI.
It is a handy way to override MMA's track naming.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.midiPending.append(('TNAME', 0, n ))
if gbl.debug:
print "Set %s track name for MIDI to %s" % (self.name, n)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setPan(self, ln):
2009-05-17 22:34:44 +00:00
""" Set MIDI Pan for this track. Parse sends 1 or 3 args. """
2011-07-26 22:49:39 +00:00
2009-05-17 22:34:44 +00:00
if len(ln)==3:
beats = stof(ln[2])
if beats < 1:
error("Beat value must be positive count, not '%s'." % beats)
initPan = stoi(ln[0], "Expecting integer value 0..127")
newPan = stoi(ln[1], "Expecting integer value 0..127")
else:
beats = 0
initPan = 0
newPan = stoi(ln[0], "Expecting integer value 0..127")
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ticks = beats * gbl.BperQ # convert beats to midi ticks
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
if initPan<0 or initPan>127:
error("Initial MidiPAN value must be 0..127")
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
if newPan<0 or newPan>127:
error("Final MidiPAN value must be 0..127")
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
if newPan<initPan:
span = initPan-newPan
changer=-1
else:
span = newPan-initPan
changer=1
2011-07-26 22:49:39 +00:00
2009-05-17 22:34:44 +00:00
if span>0:
step = ticks/span
else:
beats=0
if beats:
v=initPan
off=gbl.tickOffset
for a in range(span+1):
self.midiPending.append( ("PAN", int(off), v))
off+=step
v+=changer
2011-07-26 22:49:39 +00:00
2009-05-17 22:34:44 +00:00
else:
self.midiPending.append( ("PAN", gbl.tickOffset, newPan))
2011-07-26 22:49:39 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
2009-05-17 22:34:44 +00:00
if beats:
print "Set %s MIDIPan from %s to %s over %s beats." % \
(self.name, initPan, newPan, beats)
else:
print "Set %s MIDIPan to %s" % (self.name, newPan)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setGlis(self, ln):
""" Set MIDI Glis for this track. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
v = stoi(ln, "Expecting integer for Portamento")
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if v<0 or v>127:
error("Value for Portamento must be 0..127")
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.midiPending.append( ("GLIS", gbl.tickOffset, v))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s MIDIPortamento to %s" % (self.name, v)
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
def setMidiText(self, ln):
""" Set/queue midi text. """
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
self.midiPending.append( ("MIDITEXT", gbl.tickOffset, ln))
if gbl.debug:
print "Set %s MIDIText '%s'." % ln
def setMidiCue(self, ln):
""" Set/queue midi cue. """
self.midiPending.append( ("MIDICUE", gbl.tickOffset, ln))
if gbl.debug:
print "Set %s MIDICue '%s'." % ln
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setStrum(self, ln):
2011-07-26 22:49:39 +00:00
""" Set Strum time. """
ln = lnExpand(ln, '%s Strum' % self.name)
tmp = []
for n in ln:
if ',' in n:
a,b = n.split(',', 1)
a = stoi(a, "Argument for %s Strum must be an integer" % self.name)
b = stoi(b, "Argument for %s Strum must be an integer" % self.name)
else:
a = stoi(n, "Argument for %s Strum must be an integer" % self.name)
b = a
if a>b:
xx=a
a=b
b=xx
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
if a < -300 or a > 300 or b < -300 or b > 300:
error("STRUM: %s out-of-range. All values must be -300..300" % n)
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
if a == 0 and b == 0:
tmp.append(None)
else:
tmp.append((a,b))
self.strum = seqBump(tmp)
if self.vtype == "DRUM":
warning("Strum has no effect in %s tracks" % self.name)
if gbl.debug:
print "Set %s Strum to %s" % (self.name, self.strum)
def getStrum(self, sc):
""" Returns the strum factor. Note that if strum==(0,0) a 0 is returned."""
if not self.strum[sc]:
return 0
else:
return random.randint(self.strum[sc][0], self.strum[sc][1])
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setTone(self, ln):
""" Set Tone. Error trap, only drum tracks have tone. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
error("Tone command not supported for %s track" % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setOn(self):
""" Turn ON track. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
if gbl.muteTracks and self.name not in gbl.muteTracks:
warning("Attempt to enable muted track %s ignored." % self.name)
2011-07-26 22:49:39 +00:00
2009-05-17 22:34:44 +00:00
else:
self.disable = 0
self.ssvoice = -1
2011-07-26 22:49:39 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "%s Enabled" % self.name
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setOff(self):
""" Turn OFF track. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.disable = 1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "%s Disabled" % self.name
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setRVolume(self, ln):
""" Set the volume randomizer for a track. """
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
msg = "%s Rvolume" % self.name
ln = lnExpand(ln, msg)
2007-04-29 06:47:40 +00:00
tmp = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
2011-07-26 22:49:39 +00:00
n1, n2 = getRndPair(n, msg, -100, 100)
tmp.append( [ n1/100. , n2/100. ] )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.rVolume = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
2011-07-26 22:49:39 +00:00
print "%s:" % msg,
for n1, n2 in self.rVolume:
n1 = int(n1 * 100)
n2 = int(n2 * 100)
if abs(n1) == n2:
print "%s" % n2,
else:
print "%s,%s" % (n1, n2),
2007-04-29 06:47:40 +00:00
print
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setRSkip(self, ln):
""" Set the note random skip factor for a track. """
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
msg = "%s RSkip" % self.name
ln = lnExpand(ln, msg)
2007-04-29 06:47:40 +00:00
tmp = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
2011-07-26 22:49:39 +00:00
n = stoi(n, "%s: Expecting integer" % msg)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if n < 0 or n > 99:
2011-07-26 22:49:39 +00:00
error("%s: arg must be 0..99" % msg)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
tmp.append(n/100.)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.rSkip = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
2011-07-26 22:49:39 +00:00
print "%s set to:" % msg,
2007-04-29 06:47:40 +00:00
for n in self.rSkip:
print int(n * 100),
print
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setRTime(self, ln):
""" Set the timing randomizer for a track. """
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
msg = "%s RTime" % self.name
ln = lnExpand(ln, msg)
2007-04-29 06:47:40 +00:00
tmp = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
2011-07-26 22:49:39 +00:00
n1, n2 = getRndPair(n, msg, -100, 100)
tmp.append([n1, n2])
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.rTime = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
2011-07-26 22:49:39 +00:00
print "%s:" % msg,
for n1, n2 in self.rVolume:
if abs(n1) == n2:
print "%s" % n2,
else:
print "%s,%s" % (n1, n2),
print
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setRnd(self, arg):
""" Enable random pattern selection from sequence."""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if arg in ("ON", "1"):
self.seqRnd = 1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
elif arg in ("OFF", "0"):
self.seqRnd = 0
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
else:
error("SeqRnd: '%s' is not a valid option" % arg)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
if self.seqRnd:
a="On"
else:
a="Off"
print "%s SeqRnd: %s" % (self.name, a)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setRndWeight(self, ln):
""" Set weighting factors for seqrnd. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, "%s SeqRndWeight" % self.name)
2007-04-29 06:47:40 +00:00
tmp = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
n = stoi(n)
if n < 0: error("SeqRndWeight: Values must be 0 or greater")
tmp.append(n)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.seqRndWeight = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "%s SeqRndWeight:" % self.name,
printList(self.seqRndWeight)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setDirection(self, ln):
""" Set scale direction. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, "%s Direction" % self.name)
2007-04-29 06:47:40 +00:00
tmp = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
n = n.upper()
if not n in ('UP', 'DOWN', 'BOTH', 'RANDOM'):
error("Unknown %s Direction '%s'" % (self.name, n) )
tmp.append(n)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.direction = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype == 'SCALE':
self.lastChord = None
self.lastNote = -1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s Direction to:" % self.name,
printList(self.direction)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setScaletype(self, ln):
""" Set scale type.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
This is a error stub. The real code is in the permitted track code.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
warning("ScaleType has no effect in %s tracks") % self.vtype
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setInvert(self, ln):
""" Set inversion for track.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
This can be applied to any track,
but has no effect in drum tracks. It inverts the chord
by one rotation for each value.
"""
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, "%s Invert" % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
vwarn = 0
tmp = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
n = stoi(n, "Argument for %s Invert must be an integer" % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if n and self.vtype=='CHORD' and self.voicing.mode:
vwarn = 1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
tmp.append(n)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.invert = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype not in ("CHORD", "ARPEGGIO"):
warning ("Invert is ignored in %s tracks" % self.vtype)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if vwarn:
warning("Setting both Voicing Mode and Invert is not a good idea")
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s Invert to:" % self.name,
printList(self.invert)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setOctave(self, ln):
2011-07-26 22:49:39 +00:00
""" Set the octave for a track. Use 0-10, -x, +x. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, '%s Octave' % self.name)
2011-07-26 22:49:39 +00:00
ln = seqBump(ln) # Needed for +/- values to complete
2007-04-29 06:47:40 +00:00
tmp = []
2011-07-26 22:49:39 +00:00
i = 0
2007-04-29 06:47:40 +00:00
for n in ln:
2011-07-26 22:49:39 +00:00
incr, n = getIncDec(n)
2007-04-29 06:47:40 +00:00
n = stoi(n, "Argument for %s Octave must be an integer" % self.name)
2011-07-26 22:49:39 +00:00
n = getIncDecValue(self.octave[i]/12, incr, n)
2007-04-29 06:47:40 +00:00
if n < 0 or n > 10:
error("Octave %s out-of-range; must be 0..10" % n)
tmp.append( n * 12 )
2011-07-26 22:49:39 +00:00
i+=1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.octave = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s Octave to:" % self.name,
for i in self.octave:
print i/12,
print
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setSpan(self, start, end):
""" Set span.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
Note: The start/end parm has been verified in parser.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype == 'DRUM':
warning("Span has no effect in Drum tracks")
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.spanStart = start
self.spanEnd = end
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s Span to %s...%s" % (self.name, self.spanStart, self.spanEnd)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setHarmony(self, ln):
""" Set the harmony. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, '%s Harmony' % self.name)
2007-04-29 06:47:40 +00:00
tmp = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
n = n.upper()
if n in ( '-', '-0', 'NONE'):
n = None
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
tmp.append(n)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.harmony = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype in ( 'CHORD', 'DRUM' ):
warning("Harmony setting for %s track ignored" % self.vtype)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s Harmony to:" % self.name,
printList(self.harmony)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setHarmonyOnly(self, ln):
""" Set the harmony only. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, '%s HarmonyOnly' % self.name)
2007-04-29 06:47:40 +00:00
tmp = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
n = n.upper()
if n in ('-', '0'):
n = None
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
tmp.append(n)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.harmony = seqBump(tmp)
self.harmonyOnly = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype in ( 'CHORD', 'DRUM'):
warning("HarmonyOnly setting for %s track ignored" % self.vtype)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s HarmonyOnly to:" % self.name,
printList(self.harmonyOnly)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setHarmonyVolume(self, ln):
""" Set harmony volume adjustment. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, '%s HarmonyOnly' % self.name)
2007-04-29 06:47:40 +00:00
tmp = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
v=stoi(n)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if v<0:
error("HarmonyVolume adjustment must be positive integer")
tmp.append(v/100.)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.harmonyVolume = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype in ( 'CHORD', 'DRUM' ):
warning("HarmonyVolume adjustment for %s track ignored" % self.vtype)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s HarmonyVolume to:" % self.name,
printList(self.harmonyVolume)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setSeqSize(self):
2011-07-26 22:49:39 +00:00
""" Expand existing pattern list.
Track functions my have their own and do a callback.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.sequence = seqBump(self.sequence)
if self.midiVoice:
self.midiVoice = seqBump(self.midiVoice)
if self.midiSeq:
self.midiSeq = seqBump(self.midiSeq)
self.invert = seqBump(self.invert)
self.artic = seqBump(self.artic)
self.volume = seqBump(self.volume)
self.voice = seqBump(self.voice)
self.rVolume = seqBump(self.rVolume)
self.rSkip = seqBump(self.rSkip)
self.rTime = seqBump(self.rTime)
self.seqRndWeight = seqBump(self.seqRndWeight)
self.strum = seqBump(self.strum)
self.octave = seqBump(self.octave)
self.harmony = seqBump(self.harmony)
self.harmonyOnly = seqBump(self.harmonyOnly)
self.harmonyVolume = seqBump(self.harmonyVolume)
self.direction = seqBump(self.direction)
self.scaleType = seqBump(self.scaleType)
self.compress = seqBump(self.compress)
self.chordRange = seqBump(self.chordRange)
self.dupRoot = seqBump(self.dupRoot)
self.unify = seqBump(self.unify)
self.accent = seqBump(self.accent)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setVoice(self, ln):
""" Set the voice for a track.
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
Note, this just sets flags, the voice is set in bar().
ln[] is not nesc. set to the correct length.
the voice can be gm-string, user-def-string, or value.
A value can be xx.yy.vv, yy.vv or vv
2007-04-29 06:47:40 +00:00
"""
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, '%s Voice' % self.name)
2007-04-29 06:47:40 +00:00
tmp = []
2011-07-26 22:49:39 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
n = MMA.translate.vtable.get(n)
2009-05-17 22:34:44 +00:00
voc=MMA.midiC.instToValue(n)
if voc < 0 and n[0].isalpha():
error("Voice '%s' is not defined." % n)
if voc < 0: # not a valid name, assume vv.msb(ctrl0).lsb(ctrl32) value
nn = n.split('.')
if len(nn) > 3 or len(nn) < 1:
error("Expecting a voice value Prog.MSB.LSB, not '%s'" % n)
voc = 0
if len(nn) > 2:
i = stoi(nn[2])
if i<0 or i>127:
error("LSB must be 0..127, not '%s'" % i)
voc = i << 16
if len(nn) > 1:
i = stoi(nn[1])
if i<0 or i>127:
error("MSB must be 0..127, not '%s'" % i)
voc += i << 8
i = stoi(nn[0])
if i<0 or i>127:
error("Program must be 0..127, not '%s'" % i)
voc += i
tmp.append( voc )
2011-07-26 22:49:39 +00:00
2007-04-29 06:47:40 +00:00
self.voice = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
if self.channel and len(gbl.midiAssigns[self.channel])>1:
2007-04-29 06:47:40 +00:00
a=''
for n in gbl.midiAssigns[self.channel]:
if n != self.name:
a += ' %s' % n
warning("Track %s is shared with %s,\n"
" changing voice may create conflict" % (a,self.name))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s Voice to:" % self.name,
2009-05-17 22:34:44 +00:00
for i in self.voice:
print MMA.midiC.valueToInst(i),
2007-04-29 06:47:40 +00:00
print
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setMidiClear(self, ln):
""" Set MIDIclear sequences. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if ln[0] in 'zZ-':
self.midiClear = None
else:
self.midiClear = MMA.mdefine.mdef.get(ln[0])
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "%s MIDIClear: %s" % (self.name, self.midiSeqFmt(self.midiClear))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def doMidiClear(self):
""" Reset MIDI settings. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.midiSent:
if not self.midiClear:
warning("%s: Midi data has been inserted with MIDIVoice/Seq "
"but no MIDIClear data is present" % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
else:
for i in self.midiClear:
gbl.mtrks[self.channel].addCtl(gbl.tickOffset, i[1])
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.midiSent = 0
2011-07-26 22:49:39 +00:00
2009-05-17 22:34:44 +00:00
def doChannelReset(self):
""" Reset the midi voicing to 'sane'. Called when track ended. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
if self.ssvoice > 127:
gbl.mtrks[self.channel].addProgChange( gbl.tickOffset, 0, self.ssvoice)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setMidiSeq(self, ln):
""" Set a midi sequence for a track.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
This is sent for every bar. Syntax is:
<beat> <ctrl> hh .. ; ...
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
or a single '-' to disable.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
""" lnExpand() works here! The midi data has been converted to
pseudo-macros already in the parser. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, "%s MidiSeq" % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
seq = []
for a in ln:
if a in 'zZ-':
seq.append(None)
else:
seq.append(MMA.mdefine.mdef.get(a.upper()))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if seq.count(None) == len(seq):
self.midiSeq = []
else:
self.midiSeq = seqBump( seq )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "%s MIDISeq:" % self.name,
for l in seq:
print '{ %s }' % self.midiSeqFmt(l),
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setMidiVoice(self, ln):
""" Set a MIDI sequence for a track.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
This is sent whenever we send a VOICE. Syntax is:
<beat> <ctrl> hh .. ; ...
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
or a single '-' to disable.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
""" lnExpand() works here! The midi data has been converted to
pseudo-macros already in the parser. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, '%s MIDIVoice' % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
seq = []
for a in ln:
if a in 'zZ':
seq.append(None)
else:
seq.append(MMA.mdefine.mdef.get(a.upper()))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if seq.count(None) == len(seq):
self.midiVoice = []
else:
self.midiVoice = seqBump( seq )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "%s MIDIVoice:" % self.name,
for l in seq:
print '{ %s }' % self.midiSeqFmt(l),
print
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def midiSeqFmt(self, lst):
""" Used by setMidiVoice/Clear/Seq for debugging format. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if lst == None:
return ''
ret=''
for i in lst:
ret += "%s %s 0x%02x ; " % (i[0],
MMA.midiC.valueToCtrl(ord(i[1][0])),
ord(i[1][1]))
return ret.rstrip("; ")
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setVolume(self, ln):
""" Set the volume for a pattern.
ln - list of volume names (pp, mf, etc)
ln[] not nesc. correct length
"""
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, '%s Volume' % self.name)
2007-04-29 06:47:40 +00:00
tmp = [None] * len(ln)
2011-07-26 22:49:39 +00:00
self.futureVols = [] # clear off dangling (de)cresc for voice.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for i,n in enumerate(ln):
a = MMA.volume.calcVolume(n, self.volume[i])
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype == 'DRUM':
a=MMA.translate.drumVolTable.get(self.toneList[i], a)
else:
a=MMA.translate.voiceVolTable.get(self.voice[i], a)
tmp[i] = a
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.volume = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s Volume to:" % self.name,
for a in self.volume:
print int(a * 100),
print
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setCresc(self, dir, ln):
""" Set Crescendo for a track. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if len(ln) == 3:
self.setVolume([ln[0]])
ln=ln[1:]
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
if len(ln) != 2:
error("Cresc expecting 2 or 3 args.")
2007-04-29 06:47:40 +00:00
vol = self.volume[0]
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.volume.count(vol) != len(self.volume):
warning("(De)Crescendo being used with track with variable sequence volumes")
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.futureVols = MMA.volume.fvolume(dir, vol, ln)
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
if gbl.debug:
print "Set %s Cresc to:" % self.name,
for a in self.futureVols:
print int(a*100),
print
def setSwell(self, ln):
""" Set a swell (cresc<>decresc) for track. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
if len(ln) == 3: # 3 args, 1st is intial setting
self.setVolume([ln[0]])
ln=ln[1:]
if len(ln) != 2:
error("%s Swell expecting 2 or 3 args." % self.name)
if self.volume.count(self.volume[0]) != len(self.volume):
warning("%s Swell being used with track with variable sequence volumes." \
% self.name)
2011-07-26 22:49:39 +00:00
2009-05-17 22:34:44 +00:00
count = stoi(ln[1])
if count < 2:
error("%s Swell bar count must be 2 or greater." % self.name)
count += 1
c = int(count/2)
if count % 2: # even number of bars (we bumped count)
offset = 1
c+=1
else: # odd number
offset = 0
c=str(c)
self.futureVols = MMA.volume.fvolume(0 ,self.volume[0],
[ ln[0], c ] )
self.futureVols.extend(MMA.volume.fvolume(0, self.futureVols[-1],
[str(int(self.volume[0]*100)), c ] )[offset:] )
2011-07-26 22:49:39 +00:00
2009-05-17 22:34:44 +00:00
if gbl.debug:
print "Set %s Swell to:" % self.name,
for a in self.futureVols:
print int(a*100),
print
2011-07-26 22:49:39 +00:00
2007-04-29 06:47:40 +00:00
def setMallet(self, ln):
""" Mallet (repeat) settngs. """
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
if self.vtype == "PLECTRUM":
warning("%s: Mallet has no effect, ignored." % self.name)
return
2007-04-29 06:47:40 +00:00
for l in ln:
try:
mode, val = l.upper().split('=')
except:
error("Each Mallet option must contain a '=', not '%s'" % l)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if mode == 'RATE':
2011-07-26 22:49:39 +00:00
if val == '0' or val.upper() == 'NONE':
self.mallet = 0
else:
self.mallet = MMA.notelen.getNoteLen(val)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
elif mode == 'DECAY':
val = stof(val, "Mallet Decay must be a value, not '%s'" % val)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if val < -50 or val > 50:
error("Mallet Decay rate must be -50..+50")
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.malletDecay = val/100
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "%s Mallet Rate:%s Decay:%s" % \
(self.name, self.mallet, self.malletDecay)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setAccent(self, ln):
""" Set the accent values. This is a list of lists, a list for each seq. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
tmp = []
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
""" 2 ways to call this:
Track Accent 1 20 3 -10 -- fix up by adding {} around entire option list,
Track Accent {1 20 3 -10} -- okay
Track Accent {1 20} / {/} {3 20} -- okay.
2011-07-26 22:49:39 +00:00
2009-05-17 22:34:44 +00:00
At this point ln is a string. We extract each set of {}s and build a
new ln[].
2011-07-26 22:49:39 +00:00
Note that the "/" can or not have {}s.
2007-04-29 06:47:40 +00:00
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
ln = ' '.join(ln)
if not ln.startswith('{'):
ln='{' + ln +"}"
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
l=[]
2011-07-26 22:49:39 +00:00
while ln:
2009-05-17 22:34:44 +00:00
if ln[0] == "/": # / not delimited with {}
ln = "{/}" + ln[1:]
a,b = pextract(ln, "{", "}", 1)
ln=a.strip()
if len(b)==1 and b[0]=='/': # convert ['/'] to '/' for lnExpand()
l.append('/')
2007-04-29 06:47:40 +00:00
else:
2009-05-17 22:34:44 +00:00
l.append(b[0].split())
2011-07-26 22:49:39 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(l, '%s Accent' % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for l in ln:
tt=[]
2009-05-17 22:34:44 +00:00
if len(l)/2*2 != len(l):
2007-04-29 06:47:40 +00:00
error("Use: %s Accent Beat Percentage [...]" % self.name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for b, v in zip(l[::2], l[1::2]):
b=self.setBarOffset( b )
2009-05-17 22:34:44 +00:00
v=stoi(v, "Beat offset must be a value, not '%s'" % v)
2007-04-29 06:47:40 +00:00
if v < -100 or v > 100:
error("Velocity adjustment (as percentage) must "
"be -100..100, not '%s'" % v)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
tt.append( (b, v/100. ) )
tmp.append(tt)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.accent = seqBump( tmp )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "%s Accent:" % self.name,
for s in self.accent:
print "{",
for b,v in s:
print '%s %s' % (1+(b/float(gbl.BperQ)), int(v*100)),
print "}",
print
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setArtic(self, ln):
""" Set the note articuation value. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, '%s Articulate' % self.name)
2011-07-26 22:49:39 +00:00
ln = seqBump(ln)
2007-04-29 06:47:40 +00:00
tmp = []
2011-07-26 22:49:39 +00:00
incr = 0
i = 0
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
2011-07-26 22:49:39 +00:00
incr, n = getIncDec(n)
2007-04-29 06:47:40 +00:00
a = stoi(n, "Expecting value in articulation setting")
2011-07-26 22:49:39 +00:00
a = getIncDecValue(self.artic[i], incr, a)
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
if a < 1 or a > 500:
error("%s: Articulation setting must be 1..500, not %s" % (self.name,a))
if a > 200:
warning("%s: Articulation '%s' is a large value." % (self.name, a))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
tmp.append(a)
2011-07-26 22:49:39 +00:00
i += 1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.artic = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s Articulate to:" % self.name,
printList(self.artic)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setUnify(self, ln):
""" Set unify. """
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, "%s Unify" % self.name)
2007-04-29 06:47:40 +00:00
tmp = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in ln:
n=n.upper()
if n in ( 'ON', '1'):
tmp.append(1)
elif n in( 'OFF', '0'):
tmp.append(0)
else:
error("Unify accepts ON | OFF | 0 | 1")
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.unify = seqBump(tmp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.debug:
print "Set %s Unify to:" % self.name,
printList(self.unify)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def copySettings(self, cp):
""" Copy the voicing from a 2nd voice to the current one. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if not cp in gbl.tnames:
error("CopySettings does not know track '%s'" % cp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
cp=gbl.tnames[cp]
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if cp.vtype != self.vtype:
error("Tracks must be of same type for copy ... "
"%s and %s aren't" % (self.name, cp.name))
self.volume = cp.volume[:]
2011-07-26 22:49:39 +00:00
self.rVolume = copy.deepcopy(cp.rVolume)
2007-04-29 06:47:40 +00:00
self.accent = cp.accent[:]
self.rSkip = cp.rSkip[:]
2011-07-26 22:49:39 +00:00
self.rTime = copy.deepcopy(cp.rTime)
2007-04-29 06:47:40 +00:00
self.strum = cp.strum[:]
self.octave = cp.octave[:]
self.harmony = cp.harmony[:]
self.harmonyOnly = cp.harmonyOnly[:]
self.harmonyVolume = cp.harmonyVolume[:]
self.direction = cp.direction[:]
self.scaleType = cp.scaleType[:]
self.voice = cp.voice[:]
self.invert = cp.invert[:]
self.artic = cp.artic[:]
self.compress = cp.compress[:]
self.riff = cp.riff[:]
if self.vtype == 'DRUM':
self.toneList = cp.toneList[:]
if gbl.debug:
print "Settings from %s copied to %s" % (cp.name, self.name)
##################################################
## Save/restore grooves
def saveGroove(self, gname):
""" Define a groove.
Called by the 'DefGroove Name'. This is called for
each track.
If 'gname' is already defined it is overwritten.
2011-07-26 22:49:39 +00:00
Note that tracks may have their own function for local/specific variables.
They call here first to create storage, then do their own.
2007-04-29 06:47:40 +00:00
"""
self.grooves[gname] = {
'ACCENT': self.accent[:],
'ARTIC': self.artic[:],
'COMPRESS': self.compress[:],
'DIR': self.direction[:],
'DUPROOT': self.dupRoot[:],
'HARMONY': self.harmony[:],
'HARMONYO': self.harmonyOnly[:],
'HARMONYV': self.harmonyVolume[:],
'INVERT': self.invert[:],
2009-05-17 22:34:44 +00:00
'LIMIT': self.chordLimit,
'RANGE': self.chordRange[:],
2007-04-29 06:47:40 +00:00
'OCTAVE': self.octave[:],
2009-05-17 22:34:44 +00:00
'RSKIP': self.rSkip[:],
2011-07-26 22:49:39 +00:00
'RTIME': copy.deepcopy(self.rTime),
'RVOLUME': copy.deepcopy(self.rVolume),
2009-05-17 22:34:44 +00:00
'SCALE': self.scaleType[:],
'SEQ': self.sequence[:],
2007-04-29 06:47:40 +00:00
'SEQRND': self.seqRnd,
2009-05-17 22:34:44 +00:00
'SEQRNDWT': self.seqRndWeight[:],
'STRUM': self.strum[:],
'VOICE': self.voice[:],
2007-04-29 06:47:40 +00:00
'VOLUME': self.volume[:],
2009-05-17 22:34:44 +00:00
'UNIFY': self.unify[:],
'MIDISEQ': self.midiSeq[:],
'MIDIVOICE': self.midiVoice[:],
'MIDICLEAR': self.midiClear[:],
'SPAN': (self.spanStart, self.spanEnd),
'MALLET': (self.mallet, self.malletDecay),
2007-04-29 06:47:40 +00:00
}
def restoreGroove(self, gname):
2011-07-26 22:49:39 +00:00
""" Restore a defined groove.
Some tracks will override to restore their own variables. They
then call back to this to finish the job.
"""
2007-04-29 06:47:40 +00:00
self.doMidiClear()
g = self.grooves[gname]
self.sequence = g['SEQ']
self.volume = g['VOLUME']
self.accent = g['ACCENT']
self.rTime = g['RTIME']
self.rVolume = g['RVOLUME']
self.rSkip = g['RSKIP']
self.strum = g['STRUM']
self.octave = g['OCTAVE']
self.voice = g['VOICE']
self.harmonyOnly= g['HARMONYO']
self.harmony = g['HARMONY']
self.harmonyVolume = g['HARMONYV']
self.direction = g['DIR']
self.scaleType = g['SCALE']
self.invert = g['INVERT']
self.artic = g['ARTIC']
self.seqRnd = g['SEQRND' ]
self.seqRndWeight = g['SEQRNDWT']
self.compress = g['COMPRESS']
self.chordRange = g['RANGE']
self.dupRoot = g['DUPROOT']
self.chordLimit = g['LIMIT']
self.unify = g['UNIFY']
self.midiClear = g['MIDICLEAR']
self.midiSeq = g['MIDISEQ']
self.midiVoice = g['MIDIVOICE']
self.spanStart, self.spanEnd = g['SPAN']
self.mallet, self.malletDecay = g['MALLET']
""" It's quite possible that the track was created after
the groove was saved. This means that the data restored
was just the default stuff inserted when the track
was created ... which is fine, but the sequence size
isn't necs. right. We can probably test any list, and octave[]
is as good as any.
"""
if len(self.octave) != gbl.seqSize:
self.setSeqSize()
####################################
## Sequence functions
def setSequence(self, ln):
""" Set the sequence for a track.
The ln passed from the parser should be a list of existing
patterns, plus the special 'patterns' Z, z, -, and *. Remember
that the parser has already converted {} patterns to a special
pattern line _1.
First we expand ln to the proper length. lnExpand() also
duplicates '/' to the previous pattern.
Then we step though ln:
- convert 'z', 'Z' and '-' to empty patterns.
- duplicate the existing pattern for '*'
- copy the defined pattern for everything else.
There's a bit of Python reference trickery here.
Eg, if we have the line:
Bass Sequence B1 B2
the sequence is set with pointers to the existing
patterns defined for B1 and B2. Now, if we later change
the definitions for B1 or B2, the stored pointer DOEN'T
change. So, changing pattern definitions has NO EFFECT.
"""
2009-05-17 22:34:44 +00:00
ln = lnExpand(ln, '%s Sequence' % self.name)
2007-04-29 06:47:40 +00:00
tmp = [None] * len(ln)
for i, n in enumerate(ln):
n=n.upper()
if n in ('Z', '-'):
tmp[i] = None
elif n == '*':
tmp[i] = self.sequence[i]
else:
p= (self.vtype, n)
if not p in pats:
error("Track %s does not have pattern '%s'" % p )
tmp[i] = pats[p]
self.sequence = seqBump(tmp)
if gbl.seqshow:
print "%s sequence set:" % self.name,
for a in ln:
if a in "Zz-":
print "-",
else:
print a,
print
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def clearSequence(self):
""" Clear sequence for track.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
This is also called from __init__() to set the initial defaults for each track.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype != 'SOLO' or not self.inited:
self.artic = [90]
self.sequence = [None]
self.seqRnd = 0
self.seqRndWeight = [1]
2011-07-26 22:49:39 +00:00
self.scaleType = ['AUTO']
self.rVolume = [[0,0]]
2007-04-29 06:47:40 +00:00
self.rSkip = [0]
2011-07-26 22:49:39 +00:00
self.rTime = [[0,0]]
2007-04-29 06:47:40 +00:00
self.octave = [4 * 12]
self.voice = [0]
self.chordRange = [1]
self.harmony = [None]
self.harmonyOnly = [None]
self.harmonyVolume = [.8]
2011-07-26 22:49:39 +00:00
self.strum = [None]
2007-04-29 06:47:40 +00:00
self.volume = [MMA.volume.vols['M'] ]
self.compress = [0]
self.dupRoot = [0]
self.chordLimit = 0
self.invert = [0]
self.lastChord = []
self.accent = [ [] ]
self.unify = [0]
self.midiClear = []
self.midiSeq = []
self.midiVoice = []
self.spanStart = 0
self.spanEnd = 127
self.mallet = 0
self.malletDecay = 0
self.futureVols = []
2011-07-26 22:49:39 +00:00
self.direction = ['BOTH']
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
# for midinote (see midinote.py)
self.transpose = 0 # transpose off=0, on=1
self.useticks = 1 # offsets beats=0, ticks=1 defaults to ticks
self.tickdur = 1 # duration notes=0, ticks=1 defaults to ticks
self.articulate = 0 # honor articulate, defaults to Off
self.tadjust = 0 # time adjustment factor
self.vadjust = 1 # volume adjustment factor (percentage)
self.oadjust = 0 # octave (12 pitches) adjustment
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.riff:
if len(self.riff) > 1:
warning("%s sequence clear deleting %s riffs" % (self.name, len(self.riff)))
else:
warning("%s sequence clear deleting unused riff" % self.name )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.riff = []
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.setSeqSize()
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
############################
### Pattern functions
############################
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def definePattern(self, name, ln):
""" Define a Pattern.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
All patterns are stored in pats{}. The keys for this
are tuples -- (track type, pattern name).
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
name = name.upper()
slot = (self.vtype,name)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
# This is just for the debug code
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if name.startswith('_'):
redef = "dynamic define"
elif slot in pats:
redef = name + ' redefined'
else:
redef = name + ' created'
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
ln = ln.rstrip('; ') # Delete optional trailing ';' & WS
pats[slot] = self.defPatRiff(ln)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.pshow:
print "%s pattern %s:" % (self.name.title(), redef )
self.printPattern(pats[slot])
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
def dupRiff(self, ln):
""" Duplicate an existing set of riffs from one solo track to another."""
if not self.riff:
error("%s DupRiff: No data to copy." % self.name)
for t in ln:
t=t.upper()
if not t in gbl.tnames:
error("%s DupRiff: Destination track %s does not exist." \
% (self.name, t))
tr = gbl.tnames[t]
if self.vtype != tr.vtype:
error("%s DupRiff: Can't copy to %s, incompatible types (%s != %s)." \
% (self.name,t, self.vtype, tr.vtype))
if tr.riff:
error("%s DupRiff: Destination track %s has pending data." \
% (self.name, tr.name))
tr.riff = copy.deepcopy(self.riff)
if gbl.debug:
print "%s DupRiff copied to %s." % (self.name, tr.name)
2007-04-29 06:47:40 +00:00
def setRiff(self, ln):
""" Define and set a Riff. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
solo = self.vtype in ("MELODY", "SOLO")
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if solo:
self.riff.append(ln)
else:
ln = ln.rstrip('; ')
if len(ln) == 1 and (ln[0] in ('Z','z','-')):
self.riff.append([])
else:
self.riff.append(self.defPatRiff(ln))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if gbl.pshow:
print "%s Riff:" % self.name,
if solo:
print self.riff[-1]
else:
self.printPattern(self.riff[-1])
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def defPatRiff(self, ln):
""" Worker function to define pattern. Shared by definePattern()
and setRiff().
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def mulPatRiff(oldpat, fact):
""" Multiply a pattern. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
fact = stoi(fact, "The multiplier arg must be an integer not '%s'" % fact)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if fact<1 or fact >100:
error("The multiplier arg must be in the range 2 to 100")
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
""" Make N copies of pattern, adjusted so that the new copy has
all note lengths and start times adjusted.
eg: [[1, 2, 66], [3, 2, 88]] * 2
becomes [[1,4,66], [2,4,88], [3,4,66], [4,4,88]].
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
new = []
add = 0
step = (gbl.BperQ * gbl.QperBar)/fact
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for n in range(fact):
orig = copy.deepcopy(oldpat)
for z in orig:
z.offset = (z.offset / fact) + add
z.duration /= fact
if z.duration < 1:
z.duration = 1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
new.append(z)
add += step
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
return tuple( new )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def shiftPatRiff(oldpat, fact):
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
fact = stof(fact, "The shift arg must be a value, not '%s'" % fact)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
# Adjust all the beat offsets
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
new = copy.deepcopy(oldpat)
max = gbl.BperQ * (gbl.QperBar)
for n in new:
n.offset += fact * gbl.BperQ
if n.offset < 0 or n.offset > max:
error("Pattern shift with factor %f has resulted in an "
"illegal offset" % fact )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
return tuple( new )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def patsort(c1, c2):
""" Sort a pattern tuple. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if c1.offset < c2.offset: return -1
if c1.offset == c2.offset: return 0
else: return 1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
### Start of main function...
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
# Convert the string to list...
# "1 2 3; 4 5 6" ---> [ [1,2,3], [4,5,6] ]
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
p = []
ln = ln.upper().split(';')
for l in ln:
p.append(l.split())
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
plist=[]
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for ev in p:
more=[]
for i,e in enumerate(ev):
if e.upper() in ('SHIFT', '*'):
if i == 0:
error("Pattern definition can't start with SHIFT or *")
more = ev[i:]
ev=ev[:i]
break
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if len(ev) == 1:
nm = (self.vtype, ev[0])
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if nm in pats:
if nm[0].startswith('_'):
error("You can't use a pattern name beginning with an underscore")
pt = pats[nm]
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
else:
error("%s is not an existing %s pattern" % (nm[1], nm[0].title()) )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
else:
pt = [self.getPgroup(ev)]
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
while more:
cmd = more.pop(0)
if cmd not in ('SHIFT', '*'):
error("Expecting SHIFT or *, not '%s'" % cmd)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if not more:
error("Expecting factor after %s" % cmd)
if cmd == 'SHIFT':
pt = shiftPatRiff(pt, more.pop(0))
elif cmd == '*':
pt = mulPatRiff(pt, more.pop(0))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
plist.extend(pt)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
plist.sort(patsort)
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
if MMA.swing.mode:
plist = MMA.swing.pattern(plist, self.vtype)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
return plist
2011-07-26 22:49:39 +00:00
2007-04-29 06:47:40 +00:00
def printPattern(self, pat):
""" Print a pattern. Used by debugging code."""
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
print self.formatPattern(pat)
def formatPattern(self, pat):
pp=[]
if not pat:
return ' z '
2007-04-29 06:47:40 +00:00
for p in pat:
2009-05-17 22:34:44 +00:00
s=[]
s.append("%.2f %.0f" % (1+(p.offset/float(gbl.BperQ)),
2007-04-29 06:47:40 +00:00
p.duration))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.vtype == 'CHORD':
for a in p.vol:
2009-05-17 22:34:44 +00:00
s.append( "%.0f" % a)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
elif self.vtype == 'BASS':
f=str(p.noteoffset+1)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if p.accidental == 1:
f+="#"
elif p.accidental == -1:
f+="b"
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if p.addoctave > 0:
f+="+" * (p.addoctave/12)
elif p.addoctave < 0:
f+="-" * (p.addoctave/-12)
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
s.append( "%s %.0f" % (f, p.vol ) )
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
elif self.vtype in ('ARPEGGIO', 'SCALE', 'DRUM', 'WALK'):
2009-05-17 22:34:44 +00:00
s.append( "%.0f " % p.vol )
2006-11-10 08:07:56 +00:00
2009-05-17 22:34:44 +00:00
pp.append(' '.join(s))
2011-07-26 22:49:39 +00:00
2009-05-17 22:34:44 +00:00
return "; ".join(pp)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def insertVoice(self):
""" Called from bar() and setForceOut(). Adds voice stuff to track."""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
sc = gbl.seqCount
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
""" 1st pass for MIDIVOICE. There's a separate slot for
2009-05-17 22:34:44 +00:00
each bar in the sequence, plus the data can be sent
before or after 'voice' commands. This first loop
sends MIDIVOICE data with an offset of 0. Note, we
don't set the value for 'self.smidiVoice' until we
do this again, later. All this is needed since some
MIDIVOICE commands NEED to be sent BEFORE voice selection,
and others AFTER.
2007-04-29 06:47:40 +00:00
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.midiVoice:
v = self.midiVoice[sc]
if v and v != self.smidiVoice:
for i in v:
if not i[0]:
gbl.mtrks[self.channel].addCtl(gbl.tickOffset, i[1])
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
# Set the voice in the midi track if not previously done.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
v=self.voice[sc]
if v != self.ssvoice:
2009-05-17 22:34:44 +00:00
gbl.mtrks[self.channel].addProgChange( gbl.tickOffset, v, self.ssvoice)
2007-04-29 06:47:40 +00:00
self.ssvoice = v
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
# Mark ssvoice also in shared tracks
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for a in gbl.midiAssigns[self.channel]:
2009-05-17 22:34:44 +00:00
try:
2007-04-29 06:47:40 +00:00
gbl.tnames[a].ssvoice = v
2009-05-17 22:34:44 +00:00
except KeyError:
pass
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
if gbl.debug:
print "%s Voice '%s' inserted at %s" % \
(self.name, MMA.midiC.valueToInst(v), gbl.tickOffset )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
""" Our 2nd stab at MIDIVOICE. This time any sequences
with offsets >0 are sent. AND the smidiVoice and midiSent
variables are set.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.midiVoice:
v = self.midiVoice[sc]
if v and v != self.smidiVoice:
for i in v:
if i[0]:
gbl.mtrks[self.channel].addCtl(gbl.tickOffset, i[1])
self.smidiVoice = v
self.midiSent = 1 # used by MIDICLEAR
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
#########################
## Music processing
#########################
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def bar(self, ctable):
""" Process a bar of music for this track. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
# Future vol == de(cresc). Done if track is on or off!
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.futureVols:
self.volume = seqBump([self.futureVols.pop(0)])
2009-05-17 22:34:44 +00:00
if self.futureVols:
self.nextVolume = self.futureVols[0]
else:
self.nextVolume = None
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
# If track is off don't do anything else.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.disable:
if self.riff:
self.riff.pop(0)
return
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
""" Decide which seq to use. This is either the current
seqCount, or if SeqRnd has been set for the track
it is a random pattern in the sequence.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
The class variable self.seq is set to the sequence to use.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.seqRnd:
2009-05-17 22:34:44 +00:00
self.seq = MMA.seqrnd.getrndseq(self.seqRndWeight)
2007-04-29 06:47:40 +00:00
else:
self.seq = gbl.seqCount
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
sc = self.seq
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
""" Get pattern for this sequence. Either a Riff or a Pattern. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.riff:
pattern = self.riff.pop(0)
else:
pattern = self.sequence[sc]
if not pattern:
return
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
""" MIDI Channel assignment. If no channel is assigned try
to find an unused number and assign that.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if not self.channel:
self.setChannel()
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
# We are ready to create musical data. 1st do pending midi commands.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.clearPending()
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.insertVoice()
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
# Do MIDISeq for this voice
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.midiSeq:
l = self.midiSeq[sc]
if l:
for i in l:
gbl.mtrks[self.channel].addCtl( getOffset(i[0]), i[1] )
self.midiSent = 1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
self.trackBar(pattern, ctable)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def clearPending(self):
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
while self.midiPending:
c, off, v = self.midiPending.pop(0)
2011-07-26 22:49:39 +00:00
2007-04-29 06:47:40 +00:00
if c == 'TNAME':
gbl.mtrks[self.channel].addTrkName(off, v)
if gbl.debug:
print "%s Track name inserted at offset %s" % \
(self.name, off)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
elif c == 'GLIS':
gbl.mtrks[self.channel].addGlis(off, v)
if gbl.debug:
print "%s Glis at offset %s set to %s" % \
(self.name, off, ord(chr(v)))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
elif c == 'PAN':
gbl.mtrks[self.channel].addPan(off, v)
if gbl.debug:
print "%s Pan at offset %s set to %s" % \
(self.name, off, v)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
elif c == 'CVOLUME':
gbl.mtrks[self.channel].addChannelVol(off, v)
if gbl.debug:
print "%s ChannelVolume at offset %s set to %s" % \
(self.name, off, v)
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
elif c == 'MIDITEXT':
gbl.mtrks[self.channel].addText(off, v)
if gbl.debug:
print "%s MidiText inserted at %s." % (self.name, off)
elif c == 'MIDICUE':
gbl.mtrks[self.channel].addCuePoint(off, v)
if gbl.debug:
print "%s MidiCue inserted at %s." % (self.name, off)
2007-04-29 06:47:40 +00:00
else:
error("Unknown midi command pending. Call Bob")
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
def getChordInPos( self, offset, ctabs):
2007-04-29 06:47:40 +00:00
""" Compare an offset to a list of ctables and return
2011-07-26 22:49:39 +00:00
the table entry active for the given beat.
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
The chord start/end offsets generated by parseChordLine() will be in
the range 0... (BperQ*QperBar). So, negative offset in patterns need
to be reset to 0; and a hit at the end of the bar could be missed if we
don't assume that anything out-of-range is in the last chord. Sort of
ugly, but it's quick and it works.
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
Returns a CTable structure.
2007-04-29 06:47:40 +00:00
"""
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
for c in ctabs:
if offset < c.chEnd:
2007-04-29 06:47:40 +00:00
break
2011-07-26 22:49:39 +00:00
return c
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def adjustVolume(self, v, beat):
""" Adjust a note volume based on the track and global volume
setting.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if not v:
return 0
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
sc = self.seq
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.rSkip[sc] and random.random() < self.rSkip[sc]:
return 0
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
a1 = self.volume[sc]
if not a1:
return 0
2009-05-17 22:34:44 +00:00
if self.nextVolume: # inter-bar cresc adjust
bt=beat
if bt<1: # might have negative offsets, cres code ignores
bt=0
a1 += (self.nextVolume - a1) * bt / (gbl.BperQ * gbl.QperBar)
2011-07-26 22:49:39 +00:00
2007-04-29 06:47:40 +00:00
a1 *= MMA.volume.vTRatio
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
a2 = MMA.volume.volume
2011-07-26 22:49:39 +00:00
2007-04-29 06:47:40 +00:00
if not a2:
return 0
2009-05-17 22:34:44 +00:00
if MMA.volume.nextVolume: # inter-bar cresc adjust
bt=beat
if bt<1: # might have negative offsets, cres code ignores
bt=0
a2 += (MMA.volume.nextVolume - a2) * bt / (gbl.BperQ * gbl.QperBar)
2011-07-26 22:49:39 +00:00
2007-04-29 06:47:40 +00:00
a2 *= MMA.volume.vMRatio
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
v *= ( a1 + a2 )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for b,a in self.accent[sc]:
if b==beat:
v += (v * a)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
# take .rVolume % of current volume, add/sub result to current
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if self.rVolume[sc]:
2011-07-26 22:49:39 +00:00
a1 = int(v * self.rVolume[sc][0])
a2 = int(v * self.rVolume[sc][1])
if a1 or a2:
v += random.randrange(a1, a2)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if v > 127:
v = 127
elif v < 1:
v = 1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
return int(v)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def adjustNote(self, n):
""" Adjust a note for a given octave/transposition.
2011-07-26 22:49:39 +00:00
Ensure that the note is in range.
2007-04-29 06:47:40 +00:00
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
n += self.octave[self.seq] + gbl.transpose
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
while n < 0 or n < self.spanStart:
2007-04-29 06:47:40 +00:00
n += 12
2011-07-26 22:49:39 +00:00
while n > 127 or n > self.spanEnd:
2007-04-29 06:47:40 +00:00
n -= 12
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
return n
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def setBarOffset(self, v):
""" Convert a string into a valid bar offset in midi ticks. """
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
m=v.find('-')
p=v.find('+')
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if m>-1 and p>-1:
if m>p:
sp = p
sign = 1
else:
sp = m
sign = -1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
elif m >- 1:
sp = m
sign = -1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
elif p >- 1:
sp = p
sign = 1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
else:
sp = None
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if sp:
note = v[sp+1:]
v = v[:sp]
else:
note = None
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
v=stof(v, "Value for %s bar offset must be integer/float" % self.name)
v = (v-1) * gbl.BperQ
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if note:
2009-05-17 22:34:44 +00:00
v += MMA.notelen.getNoteLen(note) * sign
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if v < 0:
if v<-gbl.BperQ:
error("Defining %s Pattern, bar offset must be 0 or greater" %
self.name)
else:
warning("Offset in '%s' is '%s ticks' before bar start!" % (self.name, -v))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if v >= gbl.QperBar * gbl.BperQ:
error("Defining %s Pattern, bar offset must be less than %s" %
(self.name, gbl.QperBar + 1))
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
return int(v)
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def getDur(self, d):
""" Return the adjusted duration for a note.
2006-11-10 08:07:56 +00:00
2011-07-26 22:49:39 +00:00
The adjustment makes notes longer or shorter. Valid
adjustments are 1 to 200.
2007-04-29 06:47:40 +00:00
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
d = (d * self.artic[self.seq]) / 100
if not d:
2011-07-26 22:49:39 +00:00
d = 1 # force a value if we end with 0.
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
return d
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
def sendNote( self, offset, duration, note, velocity):
""" Send a note to the MIDI machine. This is called from all
track classes and handles niceties like mallet-repeat.
"""
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
if not velocity:
return
2011-07-26 22:49:39 +00:00
2007-04-29 06:47:40 +00:00
sc = self.seq
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
rptr = self.mallet
if rptr and duration > rptr:
ll = self.getDur(rptr)
offs = 0
vel = velocity
count =0
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
for q in range(duration/rptr):
gbl.mtrks[self.channel].addPairToTrack(
offset + offs,
2011-07-26 22:49:39 +00:00
self.rTime[sc][0], self.rTime[sc][1],
2007-04-29 06:47:40 +00:00
ll,
note,
vel,
None )
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
offs += rptr
if self.malletDecay:
vel = int( vel + (vel * self.malletDecay) )
if vel < 1:
vel = 1
if vel > 255:
vel=255
count+=1
2006-11-10 08:07:56 +00:00
2007-04-29 06:47:40 +00:00
else:
gbl.mtrks[self.channel].addPairToTrack(
offset,
2011-07-26 22:49:39 +00:00
self.rTime[sc][0], self.rTime[sc][1],
2007-04-29 06:47:40 +00:00
duration,
note,
velocity,
self.unify[sc] )
2006-11-10 08:07:56 +00:00