Beispiel #1
0
    def getPgroup(self, ev):
        """ Get group for chord pattern.

        Tuples: [start, length, volume (,volume ...) ]
        """

        if len(ev) < 3:
            error("There must be at least 3 items in each group "
                  "of a chord pattern definition, not <%s>" % ' '.join(ev))

        a = struct()

        a.offset = self.setBarOffset(ev[0])
        a.duration = getNoteLen(ev[1])

        vv = ev[2:]
        if len(vv) > 8:
            error("Only 8 volumes are permitted in Chord definition, not %s" %
                  len(vv))

        a.vol = [0] * 8
        for i, v in enumerate(vv):
            v = stoi(v,
                     "Expecting integer in volume list for Chord definition")
            a.vol[i] = v

        for i in range(i + 1, 8):  # force remaining volumes
            a.vol[i] = v

        return a
Beispiel #2
0
    def sysfun(self, func, arg):
        if func == 'NOTELEN':
            return "%sT" % getNoteLen(arg)

        elif func == 'ENV':
            return safeEnv(arg)

        else:
            error("Unknown system function %s" % func)
Beispiel #3
0
    def getPgroup(self, ev):
        """ Get group for bass pattern.

            Fields - start, length, note, volume

        """

        if len(ev) != 4:
            error("There must be n groups of 4 in a pattern definition, "
                  "not <%s>" % ' '.join(ev))

        a = struct()

        a.offset = self.setBarOffset(ev[0])
        a.duration = getNoteLen(ev[1])

        offset = ev[2]
        n = offset[0]
        if n in "1234567":
            a.noteoffset = int(n) - 1
        else:
            error("Note offset in Bass must be '1'...'7', not '%s'" % n)

        n = offset[1:2]
        if n == "#":
            a.accidental = 1
            ptr = 2
        elif n == 'b' or n == 'b' or n == '&':
            a.accidental = -1
            ptr = 2
        else:
            a.accidental = 0
            ptr = 1

        a.addoctave = 0

        for n in ev[2][ptr:]:
            if n == '+':
                a.addoctave += 12
            elif n == '-':
                a.addoctave -= 12

            else:
                error(
                    "Only '- + # b &' are permitted after a noteoffset, not '%s'"
                    % n)

        a.vol = stoi(ev[3], "Note volume in Bass definition not int")

        return a
Beispiel #4
0
    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
Beispiel #5
0
    def getPgroup(self, ev):
        """ Get group for a drum pattern.

            Fields - start, length, volume
        """

        if len(ev) != 3:
            error("There must be at exactly 3 items in each "
                  "group of a drum define, not <%s>" % ' '.join(ev) )

        a = struct()

        a.offset   = self.setBarOffset(ev[0])
        a.duration = getNoteLen(ev[1])
        a.vol      = stoi(ev[2], "Type error in Drum volume")

        return a
Beispiel #6
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])
        a.duration = getNoteLen(ev[1])
        a.vol      = stoi(ev[2], "Type error in Walking Bass definition")

        return a
Beispiel #7
0
    def getPgroup(self, ev):
        """ Get group for aria pattern.

            Fields - start, length, velocity

        """

        if len(ev) != 3:
            error("There must be n groups of 3 in a pattern definition, "
                  "not <%s>" % ' '.join(ev))

        a = struct()

        a.offset = self.setBarOffset(ev[0])
        a.duration = getNoteLen(ev[1])
        a.vol = stoi(ev[2], "Note volume in Aria definition not int")

        return a
Beispiel #8
0
    def sysvar(self, s):
        """ Create an internal macro. """

        # Simple/global     system values

        if s == 'CHORDADJUST':
            return ' '.join([
                "%s=%s" % (a, MMA.chords.cdAdjust[a])
                for a in sorted(MMA.chords.cdAdjust)
            ])

        elif s == 'KEYSIG':
            return keySig.getKeysig()

        elif s == 'TIME':
            return str(gbl.QperBar)

        elif s == 'CTABS':
            return ','.join([
                str((float(x) / gbl.BperQ) + 1) for x in MMA.parseCL.chordTabs
            ])

        elif s == 'TIMESIG':
            return timeSig.getAscii()

        elif s == 'TEMPO':
            return str(gbl.tempo)

        elif s == 'OFFSET':
            return str(gbl.tickOffset)

        elif s == 'VOLUME':
            return str(int(MMA.volume.volume * 100))  # INT() is important

        elif s == 'VOLUMERATIO':
            return str((MMA.volume.vTRatio * 100))

        elif s == 'LASTVOLUME':
            return str(int(MMA.volume.lastVolume * 100))

        elif s == 'GROOVE':
            return MMA.grooves.currentGroove

        elif s == 'GROOVELIST':
            return ' '.join(
                sorted([
                    x for x in MMA.grooves.glist.keys() if type(x) == type('')
                ]))

        elif s == 'TRACKLIST':
            return ' '.join(sorted(gbl.tnames.keys()))

        elif s == 'LASTGROOVE':
            return MMA.grooves.lastGroove

        elif s == 'PLUGINS':
            from MMA.regplug import simplePlugs  # to avoid circular import error
            return ' '.join(simplePlugs)

        elif s == 'SEQ':
            return str(gbl.seqCount)

        elif s == 'SEQRND':
            if MMA.seqrnd.seqRnd[0] == 0:
                return "Off"
            if MMA.seqrnd.seqRnd[0] == 1:
                return "On"
            return ' '.join(MMA.seqrnd.seqRnd[1:])

        elif s == 'SEQSIZE':
            return str(gbl.seqSize)

        elif s == 'SWINGMODE':
            return MMA.swing.settings()

        elif s == 'TICKPOS':
            return str(gbl.tickOffset)

        elif s == 'TRANSPOSE':
            return str(gbl.transpose)

        elif s == 'STACKVALUE':
            if not self.pushstack:
                error("Empty push/pull variable stack")
            return self.pushstack.pop()

        elif s == 'DEBUG':
            return "Debug=%s  Filenames=%s Patterns=%s " \
                "Sequence=%s Runtime=%s Warnings=%s Expand=%s " \
                "Roman=%s Plectrum=%s Groove=%s" % \
                (gbl.debug, gbl.showFilenames, gbl.pshow, gbl.seqshow,
                 gbl.showrun,  int(not gbl.noWarn), gbl.showExpand,
                 gbl.rmShow, gbl.plecShow, gbl.gvShow)

        elif s == 'LASTDEBUG':
            return "Debug=%s  Filenames=%s Patterns=%s " \
                "Sequence=%s Runtime=%s Warnings=%s Expand=%s " \
                "Roman=%s Plectrum=%s Groove=%s" % \
                (gbl.Ldebug, gbl.LshowFilenames, gbl.Lpshow, gbl.Lseqshow,
                 gbl.Lshowrun,  int(not gbl.LnoWarn), gbl.LshowExpand,
                 gbl.LrmShow, gbl.LplecShow, gbl.LgvShow)

        elif s == 'VEXPAND':
            if self.expandMode:
                return "On"
            else:
                return "Off"

        elif s == "MIDIPLAYER":
            return "%s Background=%s Delay=%s." % \
                (' '.join(MMA.player.midiPlayer), MMA.player.inBackGround,
                 MMA.player.waitTime)

        elif s == "MIDISPLIT":
            return ' '.join([str(x) for x in MMA.midi.splitChannels])

        elif s.startswith("NOTELEN(") and s.endswith(")"):
            return "%sT" % getNoteLen(s[8:-1])

        elif s == 'SEQRNDWEIGHT':
            return ' '.join([str(x) for x in MMA.seqrnd.seqRndWeight])

        elif s == 'AUTOLIBPATH':
            return ' '.join(MMA.paths.libDirs)

        elif s == 'LIBPATH':
            return ' '.join(MMA.paths.libPath)

        elif s == 'MMAPATH':
            return gbl.MMAdir

        elif s == 'INCPATH':
            return ' '.join(MMA.paths.incPath)

        elif s == 'VOICETR':
            return MMA.translate.vtable.retlist()

        elif s == 'TONETR':
            return MMA.translate.dtable.retlist()

        elif s == 'OUTPATH':
            return gbl.outPath

        elif s == 'BARNUM':
            return str(gbl.barNum + 1)

        elif s == 'LINENUM':
            return str(gbl.lineno)

        elif s == 'LYRIC':
            return lyric.setting()

        # Track vars ... these are in format TRACKNAME_VAR

        a = s.rfind('_')
        if a == -1:
            error("Unknown system variable $_%s" % s)

        tname = s[:a]
        func = s[a + 1:]

        try:
            t = gbl.tnames[tname]
        except KeyError:
            error("System variable $_%s refers to nonexistent track." % s)

        if func == 'ACCENT':
            r = []
            for s in t.accent:
                r.append("{")
                for b, v in s:
                    r.append('%g' % (b / float(gbl.BperQ) + 1))
                    r.append(str(int(v * 100)))
                r.append("}")
            return ' '.join(r)

        elif func == 'ARTICULATE':
            return ' '.join([str(x) for x in t.artic])

        elif func == 'CHORDS':
            r = []
            for l in t.chord:
                r.append('{' + ' '.join(l) + '}')
            return ' '.join(r)

        elif func == 'CHANNEL':
            return str(t.channel)

        elif func == 'COMPRESS':
            return ' '.join([str(x) for x in t.compress])

        elif func == 'DELAY':
            return ' '.join([str(x) for x in t.delay])

        elif func == 'DIRECTION':
            if t.vtype == 'ARIA':
                return ' '.join([str(x) for x in t.selectDir])
            else:
                return ' '.join([str(x) for x in t.direction])

        elif func == 'DUPROOT':
            if t.vtype != "CHORD":
                error("Only CHORD tracks have DUPROOT")
            return t.getDupRootSetting()

        elif func == 'FRETNOISE':
            return t.getFretNoiseOptions()

        elif func == 'HARMONY':
            return ' '.join([str(x) for x in t.harmony])

        elif func == 'HARMONYONLY':
            return ' '.join([str(x) for x in t.harmonyOnly])

        elif func == 'HARMONYVOLUME':
            return ' '.join([str(int(i * 100)) for i in t.harmonyVolume])

        elif func == 'INVERT':
            return ' '.join([str(x) for x in t.invert])

        elif func == 'LIMIT':
            return str(t.chordLimit)

        elif func == 'MALLET':
            if t.vtype not in ("SOLO", "MELODY"):
                error("Mallet only valid in SOLO and MELODY tracks")
            return "Mallet Rate=%i Decay=%i" % (t.mallet, t.malletDecay * 100)

        elif func == 'MIDINOTE':
            return MMA.midinote.mopts(t)

        elif func == 'MIDIVOLUME':
            return "%s" % t.cVolume

        elif func == 'OCTAVE':
            return ' '.join([str(i // 12) for i in t.octave])

        elif func == 'MOCTAVE':
            return ' '.join([str((i // 12) - 1) for i in t.octave])

        elif func == 'ORNAMENT':
            return MMA.ornament.getOrnOpts(t)

        elif func == 'PLUGINS':
            from MMA.regplug import trackPlugs  # avoids circular import
            return ' '.join(trackPlugs)

        elif func == 'RANGE':
            return ' '.join([str(x) for x in t.chordRange])

        elif func == 'RSKIP':
            m = ''
            if t.rSkipBeats:
                m = "Beats=%s " % ','.join(
                    ['%g' % (x / float(gbl.BperQ) + 1) for x in t.rSkipBeats])
            m += ' '.join([str(int(i * 100)) for i in t.rSkip])
            return m

        elif func == 'RDURATION':
            tmp = []
            for a1, a2 in t.rDuration:
                a1 = int(a1 * 100)
                a2 = int(a2 * 100)
                if a1 == a2:
                    tmp.append('%s' % abs(a1))
                else:
                    tmp.append('%s,%s' % (a1, a2))

            return ' '.join(tmp)

        elif func == 'RTIME':
            tmp = []
            for a1, a2 in t.rTime:
                if a1 == a2:
                    tmp.append('%s' % abs(a1))
                else:
                    tmp.append('%s,%s' % (a1, a2))
            return ' '.join(tmp)

        elif func == 'RVOLUME':
            tmp = []
            for a1, a2 in t.rVolume:
                a1 = int(a1 * 100)
                a2 = int(a2 * 100)
                if a1 == a2:
                    tmp.append('%s' % abs(a1))
                else:
                    tmp.append('%s,%s' % (a1, a2))
            return ' '.join(tmp)

        elif func == 'RPITCH':
            return MMA.rpitch.getOpts(t)

        elif func == 'SEQUENCE':
            tmp = []
            for a in range(gbl.seqSize):
                tmp.append('{' + t.formatPattern(t.sequence[a]) + '}')
            return ' '.join(tmp)

        elif func == 'SEQRND':
            if t.seqRnd:
                return 'On'
            else:
                return 'Off'

        elif func == 'SEQRNDWEIGHT':
            return ' '.join([str(x) for x in t.seqRndWeight])

        elif func == 'SPAN':
            return "%s %s" % (t.spanStart, t.spanEnd)

        elif func == 'STICKY':
            if t.sticky:
                return "True"
            else:
                return "False"

        elif func == 'STRUM':
            r = []
            for v in t.strum:
                if v is None:
                    r.append("0")
                else:
                    a, b = v
                    if a == b:
                        r.append("%s" % a)
                    else:
                        r.append("%s,%s" % (a, b))

            return ' '.join(r)

        elif func == 'STRUMADD':
            return ' '.join([str(x) for x in t.strumAdd])

        elif func == 'TRIGGER':
            return MMA.trigger.getTriggerOptions(t)

        elif func == 'TONE':
            if t.vtype != "DRUM":
                error("Only DRUM tracks have TONE")
            return ' '.join([MMA.midiC.valueToDrum(a) for a in t.toneList])

        elif func == 'UNIFY':
            return ' '.join([str(x) for x in t.unify])

        elif func == 'VOICE':
            return ' '.join([MMA.midiC.valueToInst(a) for a in t.voice])

        elif func == 'VOICING':
            if t.vtype != 'CHORD':
                error("Only CHORD tracks have VOICING")
            t = t.voicing
            return "Mode=%s Range=%s Center=%s RMove=%s Move=%s Dir=%s" % \
                (t.mode, t.range, t.center, t.random, t.bcount, t.dir)

        elif func == 'VOLUME':
            return ' '.join([str(int(a * 100)) for a in t.volume])

        else:
            error("Unknown system track variable %s" % s)
Beispiel #9
0
    def getLine(self, pat, ctable):
        """ Extract a melodyline for solo/melody tracks.

            This is only called from trackbar(), but it's nicer
            to isolate it here.


            RETURNS: notes structure. This is a dictionary. Each key represents
                     an offset in MIDI ticks in the current bar. The data for
                     each entry is an array of notes, a duration and velocity:

                     notes[offset].dur         - duration in ticks
                     notes[offset].velocity[]  - velocity for notes
                     notes[offset].defaultVel  - default velocity for this offset
                     notes[offset].nl[]        - list of notes (if the only note value
                                                 is None this is a rest placeholder)

        """

        sc = self.seq
        barEnd = gbl.BperQ * gbl.QperBar

        acc = keySig.getAcc()

        # list of notename to midivalues

        midiNotes = {
            'c': 0,
            'd': 2,
            'e': 4,
            'f': 5,
            'g': 7,
            'a': 9,
            'b': 11,
            'r': None
        }
        """ The initial string is in the format "1ab;4c;;4r;". The trailing
            ';' is important and needed. If we don't have this requirement
            we can't tell if the last note is a repeat of the previous. For
            example, if we have coded "2a;2a;" as "2a;;" and we didn't
            have the 'must end with ;' rule, we end up with "2a;" and
            then we make this into 2 notes...or do we? Easiest just to
            insist that all bars end with a ";".
        """

        if not pat.endswith(';'):
            error("All Solo strings must end with a ';'")
        """ Take our list of note/value pairs and decode into
            a list of midi values. Quite ugly.
        """

        if gbl.swingMode:
            len8 = getNoteLen('8')
            len81 = getNoteLen('81')
            len82 = getNoteLen('82')
            onBeats = [x * gbl.BperQ for x in range(gbl.QperBar)]
            offBeats = [(x * gbl.BperQ + len8) for x in range(gbl.QperBar)]

        length = getNoteLen('4')  # default note length
        lastc = ''  # last parsed note
        velocity = 90  # intial/default velocity for solo notes

        notes = {}  # A dict of NoteList, keys == offset

        if self.drumType:
            isdrum = 1
            lastc = str(self.drumTone)
        else:
            isdrum = None

        pat = pat.replace(' ', '').split(';')[:-1]

        # set initial offset into bar

        if pat[0].startswith("~"):
            pat[0] = pat[0][1:]
            if not self.endTilde or self.endTilde[1] != gbl.tickOffset:
                error("Previous line did not end with '~'")
            else:
                offset = self.endTilde[0]
        else:
            offset = 0
        lastOffset = None

        # Strip off trailing ~

        if pat[-1].endswith("~"):
            self.endTilde = [1, gbl.tickOffset + (gbl.BperQ * gbl.QperBar)]
            pat[-1] = pat[-1][:-1]
        else:
            self.endTilde = []

        # Begin parse loop

        for a in pat:
            if a == '<>':
                continue

            if offset >= barEnd:
                error("Attempt to start Solo note '%s' after end of bar" % a)

            # strip out all '<volume>' setting and adjust velocity

            a, vls = pextract(a, "<", ">")
            if vls:
                if len(vls) > 1:
                    error("Only 1 volume string is permitted per note-set")

                vls = vls[0].upper().strip()
                if not vls in MMA.volume.vols:
                    error("%s string Expecting a valid volume, not '%s'" % \
                        (self.name, vls))
                velocity *= MMA.volume.vols[vls]
            """ Split the chord chunk into a note length and notes. Each
                part of this is optional and defaults to the previously
                parsed value.
            """

            i = 0
            while i < len(a):
                if not a[i] in '1234568.+':
                    break
                else:
                    i += 1

            if i:
                l = getNoteLen(a[0:i])
                c = a[i:]
            else:
                l = length
                c = a

            if not c:
                c = lastc
                if not c:
                    error("You must specify the first note in a solo line")

            length = l  # set defaults for next loop
            lastc = c
            """ Convert the note part into a series of midi values
                Notes can be a single note, or a series of notes. And
                each note can be a letter a-g (or r), a '#,&,n' plus
                a series of '+'s or '-'s. Drum solos must have each
                note separated by ','s: "Snare1,Kick1,44".
            """

            if isdrum:
                c = c.split(',')
            else:
                c = list(c)

            while c:

                # Parse off note name or 'r' for a rest

                name = c.pop(0)

                if name == 'r' and (offset in notes or c):
                    error(
                        "You cannot combine a rest with a note in a chord for solos"
                    )

                if not isdrum:
                    if not name in midiNotes:
                        error("%s encountered illegal note name '%s'" %
                              (self.name, name))

                    v = midiNotes[name]

                    # Parse out a "#', '&' or 'n' accidental.

                    if c and c[0] == '#':
                        c.pop(0)
                        acc[name] = 1

                    elif c and c[0] == '&':
                        c.pop(0)
                        acc[name] = -1

                    elif c and c[0] == 'n':
                        c.pop(0)
                        acc[name] = 0

                    if v != None:
                        v += acc[name]

                    # Parse out +/- (or series) for octave

                    if c and c[0] == '+':
                        while c and c[0] == '+':
                            c.pop(0)
                            v += 12
                    elif c and c[0] == '-':
                        while c and c[0] == '-':
                            c.pop(0)
                            v -= 12

                else:
                    if not name:  # just for leading '.'s
                        continue
                    if name == 'r':
                        v = midiNotes[name]
                    elif name == '*':
                        v = self.drumTone
                    else:
                        v = MMA.translate.dtable.get(name)
                """ Swingmode -- This tests for successive 8ths on/off beat
                If found, the first is converted to 'long' 8th, the 2nd to a 'short'
                and the offset for the 2nd is adjusted to comp. for the 'long'.
                """

                if gbl.swingMode and l==len8 and \
                       offset in offBeats and \
                       lastOffset in onBeats and \
                       lastOffset in notes:
                    if notes[lastOffset].dur == len8:
                        offset = lastOffset + len81
                        notes[lastOffset].dur = len81
                        l = len82

                # create a new note[] entry for this offset

                if not offset in notes:
                    notes[offset] = NoteList(l)

                # add note event to note[] array

                notes[offset].nl.append(v)
                notes[offset].velocity.append(
                    self.adjustVolume(velocity, offset))

                notes[offset].defaultVel = velocity  # needed for addHarmony()

            lastOffset = offset
            offset += l

        if offset <= barEnd:
            if self.endTilde:
                error("Tilde at end of bar has no effect")

        else:
            if self.endTilde:
                self.endTilde[0] = offset - barEnd
            else:
                warning("%s, end of last note overlaps end of bar by %2.3f "
                        "beat(s)." % (self.name,
                                      (offset - barEnd) / float(gbl.BperQ)))

        return notes