2006-11-10 08:07:56 +00:00
|
|
|
|
|
|
|
# patWalk.py
|
|
|
|
|
|
|
|
"""
|
2007-04-29 06:47:40 +00:00
|
|
|
This module is an integeral part of the program
|
2006-11-10 08:07:56 +00:00
|
|
|
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 random
|
|
|
|
|
2009-05-17 22:34:44 +00:00
|
|
|
|
|
|
|
import MMA.harmony
|
|
|
|
import MMA.notelen
|
|
|
|
|
2006-11-10 08:07:56 +00:00
|
|
|
import gbl
|
|
|
|
from MMA.common import *
|
|
|
|
from MMA.pat import PC
|
|
|
|
|
|
|
|
class Walk(PC):
|
2007-04-29 06:47:40 +00:00
|
|
|
""" Pattern class for a walking bass track. """
|
|
|
|
|
|
|
|
vtype = 'WALK'
|
|
|
|
walkChoice = 0
|
|
|
|
|
|
|
|
def getPgroup(self, ev):
|
|
|
|
""" Get group for walking bass pattern.
|
|
|
|
|
|
|
|
Fields - start, length, volume
|
|
|
|
"""
|
|
|
|
|
|
|
|
if len(ev) != 3:
|
|
|
|
error("There must be at exactly 3 items in each group in "
|
|
|
|
"a Walking Bass definition, not <%s>" % ' '.join(ev))
|
|
|
|
|
|
|
|
a = struct()
|
|
|
|
|
|
|
|
a.offset = self.setBarOffset(ev[0])
|
2009-05-17 22:34:44 +00:00
|
|
|
a.duration = MMA.notelen.getNoteLen(ev[1])
|
2007-04-29 06:47:40 +00:00
|
|
|
a.vol = stoi(ev[2], "Type error in Walking Bass definition")
|
|
|
|
|
|
|
|
return a
|
|
|
|
|
|
|
|
|
|
|
|
def restart(self):
|
|
|
|
self.ssvoice = -1
|
|
|
|
self.walkChoice = 0
|
|
|
|
|
|
|
|
|
|
|
|
def trackBar(self, pattern, ctable):
|
|
|
|
""" Do a waling bass bar.
|
|
|
|
|
|
|
|
Called from self.bar()
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
sc=self.seq
|
|
|
|
dir = self.direction[sc]
|
|
|
|
unify = self.unify[sc]
|
|
|
|
|
|
|
|
for p in pattern:
|
|
|
|
|
|
|
|
tb = self.getChordInPos(p.offset, ctable)
|
|
|
|
|
|
|
|
if tb.walkZ:
|
|
|
|
continue
|
|
|
|
|
|
|
|
root = tb.chord.rootNote # root note of chord
|
|
|
|
|
|
|
|
""" Create a note list from the current scale. We do
|
|
|
|
this for each beat, but it's pretty fast. The note
|
|
|
|
list is simply notes 0..6 of the scale PLUS notes
|
|
|
|
1..5 reversed. So, a Cmajor chord would result in
|
|
|
|
the note list (0,2,4,5,7,9,7,5,4,2).
|
|
|
|
|
|
|
|
Note that we deliberately skip the 7th. Too often
|
|
|
|
the chord is a Major but the melody note will be
|
|
|
|
the dom. 7th and the M7 will sound off. So, just
|
|
|
|
err on the side of caution.
|
|
|
|
|
|
|
|
If DIR is UP or DOWN we don't append the 2nd half
|
|
|
|
of the scale.
|
|
|
|
|
|
|
|
If DIR is DOWN we reverse the order as well.
|
|
|
|
"""
|
|
|
|
|
|
|
|
wNotes = list(tb.chord.scaleList[0:6])
|
|
|
|
if dir not in ('UP', 'DOWN'):
|
|
|
|
b = list(tb.chord.scaleList[1:5])
|
|
|
|
b.reverse()
|
|
|
|
wNotes += b
|
|
|
|
|
|
|
|
if dir == 'DOWN':
|
|
|
|
wNotes.reverse()
|
|
|
|
|
|
|
|
|
|
|
|
# Ensure that the offset is in range.
|
|
|
|
|
|
|
|
if self.walkChoice >= len(wNotes) or self.walkChoice < 0:
|
|
|
|
self.walkChoice = 0
|
|
|
|
|
|
|
|
""" Even with a walking bass it's nice to have the chord root on
|
|
|
|
beat 1 ... not all the time, but most. This bit of code ensures
|
|
|
|
that more that 50% of beat ones will have the root.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
if p.offset == 0 and random.choice((0,1)):
|
|
|
|
self.walkChoice=0
|
|
|
|
|
|
|
|
note = wNotes[self.walkChoice]
|
|
|
|
|
|
|
|
""" Adjust offset for NEXT TIME. If the direction is
|
|
|
|
up/down we just increment the pointer. If we have
|
|
|
|
direction set to RANDOM then we select either -1,
|
|
|
|
0 or 1 with equal change for moving up, down or
|
|
|
|
not-at-all. With BOTH we have a preference to move up.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
if dir in ('UP', 'DOWN'):
|
|
|
|
self.walkChoice += 1
|
|
|
|
elif dir == 'RANDOM':
|
|
|
|
self.walkChoice += random.choice((0,1,-1))
|
|
|
|
else: # BOTH
|
|
|
|
self.walkChoice += random.choice( (-1,0,0,2,2,1,1,1,1,1,1,1))
|
|
|
|
|
|
|
|
|
|
|
|
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]:
|
|
|
|
ch = self.getChordInPos(p.offset, ctable).chord.noteList
|
2009-05-17 22:34:44 +00:00
|
|
|
h = MMA.harmony.harmonize(self.harmony[sc], note, ch)
|
2007-04-29 06:47:40 +00:00
|
|
|
for n in h:
|
|
|
|
self.sendNote(
|
|
|
|
p.offset,
|
|
|
|
self.getDur(p.duration),
|
|
|
|
self.adjustNote(n),
|
|
|
|
self.adjustVolume(p.vol * self.harmonyVolume[sc], -1) )
|
2006-11-10 08:07:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
|