# 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 """ import random import gbl from MMA.notelen import getNoteLen from MMA.common import * from MMA.harmony import harmonize 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 = 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 = 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