VocalEasel/mma/MMA/patArpeggio.py
Matthias Neeracher fb48baaf09 Updated to MMA 1.4
2009-05-17 22:34:44 +00:00

161 lines
4.6 KiB
Python

# patArpeggio.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>
"""
import random
import MMA.notelen
import MMA.harmony
import gbl
from MMA.common import *
from MMA.pat import PC
class Arpeggio(PC):
""" Pattern class for an arpeggio track. """
vtype = 'ARPEGGIO'
arpOffset = -1
arpDirection = 1
def getPgroup(self, ev):
""" Get group for apreggio pattern.
Fields - start, length, volume
"""
a = struct()
if len(ev) != 3:
error("There must be exactly 3 items in each group "
"for apreggio define, not '%s'" % ' '.join(ev) )
a.offset = self.setBarOffset(ev[0])
a.duration = MMA.notelen.getNoteLen(ev[1])
a.vol = stoi(ev[2], "Type error in Arpeggio definition")
return a
def restart(self):
self.ssvoice = -1
self.arpOffset=-1
self.arpDirection=1
def trackBar(self, pattern, ctable):
""" Do a arpeggio bar.
Called from self.bar()
"""
sc = self.seq
direct = self.direction[sc]
for p in pattern:
tb = self.getChordInPos(p.offset, ctable)
if tb.arpeggioZ:
continue
if direct == 'DOWN':
self.arpDirection = -1
if self.chordLimit:
tb.chord.limit(self.chordLimit)
if self.compress[sc]:
tb.chord.compress()
if self.invert[sc]:
tb.chord.invert(self.invert[sc])
# This should be optimized, it recreates the chord for every pattern.
# Problem is that one would need to check all the LIMIT, COMPRESS, etc
# settings each for each bar as well, so it's probably just as easy to
# leave it as is. Besides, this works.
ln = self.chordRange[sc]
o = 0
ourChord = []
while ln >= 1:
for a in tb.chord.noteList:
ourChord.append(a+o)
ln -= 1
o += 12
if ln > 0 and ln < 1: # for fractional lengths
ln = int(tb.chord.noteListLen * ln)
if ln < 2: # important, min of 2 notes in arp.
ln=2
for a in tb.chord.noteList[:ln]:
ourChord.append(a+o)
if direct == 'BOTH':
if self.arpOffset < 0:
self.arpOffset = 1
self.arpDirection = 1
elif self.arpOffset >= len(ourChord):
self.arpOffset = len(ourChord)-2
self.arpDirection = -1
elif direct == 'UP':
if self.arpOffset >= len(ourChord) or self.arpOffset < 0:
self.arpOffset = 0
self.arpDirection = 1
elif direct == 'DOWN':
if self.arpOffset < 0 or self.arpOffset >= len(ourChord):
self.arpOffset = len(ourChord)-1
self.arpDirection = -1
if direct == 'RANDOM':
note = random.choice(ourChord)
else:
note = ourChord[self.arpOffset]
self.arpOffset += self.arpDirection
if not self.harmonyOnly[sc]:
self.sendNote(
p.offset,
self.getDur(p.duration),
self.adjustNote(note),
self.adjustVolume(p.vol, p.offset) )
if self.harmony[sc]:
h = MMA.harmony.harmonize(self.harmony[sc], note, ourChord)
for n in h:
self.sendNote(
p.offset,
self.getDur(p.duration),
self.adjustNote(n),
self.adjustVolume(p.vol * self.harmonyVolume[sc], -1) )
tb.chord.reset() # important, other tracks chord object