Sophie

Sophie

distrib > Mageia > 7 > armv7hl > media > core-release > by-pkgid > 5fcfcb7517038d1f44ab4e478e6e61fa > files > 2766

csound-doc-6.10.0-1.mga7.noarch.rpm

'''
Copyright 2005, 22013, 2014 by Michael Gogins.

A concise geometric approach to common operations
in pragmatic music theory,
for use in score generating algorithms.

When run as a standalone program,
displays a model of the voice-leading space
for trichords and can orbit a chord through
the space, playing the results using Csound.

Voice-leading space is an orbifold of chords
with one dimension per voice, voices ordered by pitch,
pitch measured in tones per octave,
and a modulus equal to the range of the voices.
I.e., it is a complete ChordSpace3.
Root progressions are motions more or less
up and down the 'columns' of identically
structured chords. The closest voice-leadings are
between the closest chords in the space.
The 'best' voice-leadings are closest first
by 'smoothness,' and then  by 'parsimony.'
See Dmitri Tymoczko,
_The Geometry of Musical Chords_, 2005
(Princeton University).

This script also demonstrates the triadic
neo-Riemannian transformations
of leading-tone exchange (press l),
parallel (press p),
relative (press r),
and dominant (press d) progression.
See Alissa S. Crans, Thomas M. Fiore, and Raymon Satyendra,
_Musical Actions of Dihedral Groups_, 2008
(arXiv:0711.1873v2).

You can do plain old transpositions
by pressing 1, 2, 3, 4, 5, or 6.

You can move each voice independently with the arrow keys:
up arrow to move voice 1 up 1 semitone (shift for down),
right arrow to move voice 2 in the same way,
down arrow to move voice 3.

'''
print __doc__
import gc
import operator
import sys
import traceback
import time
import random
import sets
import threading
import copy
import collections
from visual import *
#from numpy import *
#import Image
#import ImageGrab
#import ImageOps

import csnd6
import CsoundAC
'''
Represents operations on chords in a voice-leading orbifold.
Chords can actually have more dimensions than voices,
but voice-leading operations affect only the specified number of voices,
which can be a lower subspace of the orbifold.
This is to enable using the lower subspace of chords to represent pitches,
and the higher subspace to represent other properties of music;
e.g. [0:4] can be a tetrachord, [4:8] durations, [8:12] loudnesses, and so on.
'''
class ChordSpace3(object):
    def __init__(self, voiceCount=3, cubeOctaveCount=2,octaveCount=3, tonesPerOctave=12, isCube=False, isPrism=False, isNormalPrism = False, debug=False):
        self.N = voiceCount
        self.octaveCount = octaveCount
        self.tonesPerOctave = tonesPerOctave
        self.isCube = isCube
        self.isPrism = isPrism
        self.isNormalPrism = isNormalPrism
        self.debug = debug
        self.R = self.tonesPerOctave * self.octaveCount
        self.NR = self.N * self.R
        self.cubeOctaveCount = cubeOctaveCount
        self.cubeTessitura = self.tonesPerOctave * self.cubeOctaveCount
        self.cubeRadius = 0.07
        self.prismRadius = self.cubeRadius * 2.0
        self.normalPrismRadius = self.prismRadius #* 2.0
    def getTessitura(self):
        if self.isCube:
            return self.cubeTessitura
        else:
            return self.R
    def sort(self, chord):
        c = array(chord, 'd').copy()
        d = c[0:self.N]
        d.sort()
        c[0:self.N] = d
        return c
    '''
    Move 1 voice.
    '''
    def move(self, chord_, voice, interval):
        chord = list(chord_)
        print 'Move %d by %f.' % (voice, interval)
        chord[voice] = chord[voice] + interval
        chord = tuple(self.bounceInside(chord))
        return chord
    '''
    Do a root progression by tranposition.
    '''
    def pT(self, chord, interval):
        chord = self.firstInversion(chord)
        print 'Transpose by %f.' % interval
        for i in xrange(3):
            chord[i] = chord[i] + interval
        chord = tuple(self.bounceInside(chord))
        return chord
    '''
    Perform the leading tone exchange neo-Riemannian transformation.
    '''
    def nrL(self, chord):
        print 'Leading-tone exchange transformation.'
        chord = self.firstInversion(chord)
        z1 = self.zeroFormFirstInversion(chord)
        if   z1[1] == 4.0:
            chord[0] = chord[0] - 1
        elif z1[1] == 3.0:
            chord[2] = chord[2] + 1
        chord = tuple(self.keepInside(chord))
        return chord
    '''
    Perform the parallel neo-Riemannian transformation.
    '''
    def nrP(self, chord):
        print 'Parallel transformation.'
        chord = self.firstInversion(chord)
        z1 = self.zeroFormFirstInversion(chord)
        if   z1[1] == 4.0:
            chord[1] = chord[1] - 1
        elif z1[1] == 3.0:
            chord[1] = chord[1] + 1
        chord = tuple(self.keepInside(chord))
        return chord
    '''
    Perform the relative neo-Riemannian transformation.
    '''
    def nrR(self, chord):
        print 'Relative transformation.'
        chord = self.firstInversion(chord)
        z1 = self.zeroFormFirstInversion(chord)
        if   z1[1] == 4.0:
            chord[2] = chord[2] + 2
        elif z1[1] == 3.0:
            chord[0] = chord[0] - 2
        chord = tuple(self.keepInside(chord))
        return chord
    '''
    Perform the dominant neo-Riemannian transformation.
    '''
    def nrD(self, chord):
        print 'Dominant transformation.'
        chord = self.firstInversion(chord)
        chord[0] = chord[0] - 7
        chord[1] = chord[1] - 7
        chord[2] = chord[2] - 7
        chord = tuple(self.keepInside(chord))
        return chord
    def tones(self, chord):
        c = array(chord, 'd').copy()
        for i in xrange(self.N):
           c[i] = c[i] % self.tonesPerOctave
        return self.sort(c)
    def zeroFormModulus(self, chord):
        c = array(chord, 'd').copy()
        for i in xrange(self.N):
           c[i] = c[i] % self.tonesPerOctave
        m = min(c)
        for i in xrange(self.N):
            c[i] = c[i] - m
        return c
    def zeroForm(self, chord):
        c = array(chord).copy()
        m = min(c[:self.N])
        for i in xrange(self.N):
            c[i] = c[i] - m
        return c
    def range(self, chord):
        c = chord[0:self.N]
        return max(c) - min(c)
        #c = self.sort(chord).copy()
        #return c[self.N-1] - c[0]
    def firstInversion(self, chord):
        inversions = self.rotations(chord)
        inversionDistances = {}
        origin = []
        for i in xrange(self.N):
            origin.append(0.)
        for inversion in inversions:
            zi = self.zeroForm(inversion)
            #z = float(sum(zi)) / float(self.N)
            d = self.euclidean(zi, origin)
            if self.debug:
                print 'distance %f zeroform %s inversion %s' % (d, zi, inversion)
            inversionDistances[d] = inversion
        return inversionDistances[min(inversionDistances.keys())]
    def zeroFormFirstInversion(self, chord):
        return self.zeroForm(self.firstInversion(chord))
    def equalTones(self, a, b):
        a = self.tones(a)
        b = self.tones(b)
        if a == b:
            return True
        else:
            return False
    def inversions_(self, tones, iterating_chord, voice, inversions):
        if voice >= self.N:
            return
        if self.isPrism:
            beginning = -self.getTessitura() * 2
            end = self.getTessitura() * 2
        elif self.isCube:
            beginning = -self.getTessitura()
            end = self.getTessitura()
        p = beginning
        increment = 1.0
        while p < end:
            if self.pitchclass(p) == tones[voice]:
                iterating_chord[voice] = p
                increment = self.tonesPerOctave
                si = self.sort(iterating_chord)
                if self.isInside(si, self.getTessitura()):
                    ic = tuple(si.tolist())
                    inversions.add(ic)
                self.inversions_(tones, iterating_chord, voice + 1, inversions)
            p = p + increment
    def inversions(self, chord):
        inversions = sets.Set()
        tones = self.tones(chord)
        iterating_chords = self.rotations(tones)
        for iterating_chord in iterating_chords:
            voice = 0
            self.inversions_(tones, iterating_chord, voice, inversions)
        l = list(inversions)
        for i in xrange(len(l)):
            l[i] = array(l[i])
        return l
    def euclidean(self, a, b):
        ss = 0.0
        for i in xrange(self.N):
            ss += ((a[i] - b[i]) ** 2.0)
        return math.sqrt(ss)
    def voiceleading(self, a, b):
        v = []
        for i in xrange(self.N):
            v.append(b[i] - a[i])
        return v
    def areParallel(self, a, b):
        return CsoundAC.areParallel(a,b)
##        if self.debug:
##            v = self.voiceleading(a, b)
##        for i in xrange(self.N):
##            if v.count(v[i]) > 1:
##                for j in xrange(self.N):
##                    if i != j:
##                        if (math.fabs(a[i] - a[j]) == 7) and (math.fabs(b[i] - b[j]) == 7):
##                            if self.debug:
##                                print a, b, v, 'parallel fifth'
##                            return True
##        return false
    def smoothness(self, a, b):
        L1 = 0.0
        for i in xrange(self.N):
            L1 += math.fabs(b[i] - a[i])
        return L1
    def smoother(self, source, destination1, destination2, avoidParallels=False):
        s1 = self.smoothness(source, destination1)
        s2 = self.smoothness(source, destination2)
        if avoidParallels:
            if self.areParallel(source, destination1):
                return destination2
            if self.areParallel(source, destination2):
                return destination1
        if s1 <= s2:
            return destination1
        else:
            return destination2
    def simpler(self, source, destination1, destination2, avoidParallels=False):
        v1 = self.voiceleading(source, destination1)
        v1 = sort(v1)
        v2 = self.voiceleading(source, destination2)
        v2 = sort(v2)
        for i in xrange(self.N - 1, -1, -1):
            if v1[i] < v2[i]:
                return destination1
            if v2[i] < v1[i]:
                return destination2
        return destination1
    def closer(self, source, destination1, destination2, avoidParallels=False):
        if avoidParallels:
            if self.areParallel(source, destination1):
                return destination2
            if self.areParallel(source, destination2):
                return destination1
        s1 = self.smoothness(source, destination1)
        s2 = self.smoothness(source, destination2)
        if s1 < s2:
            return destination1
        if s1 > s2:
            return destination2
        return self.simpler(source, destination1, destination2, avoidParallels)
    def closest(self, source, destinations, avoidParallels=False):
        d = destinations[0]
        for i in xrange(1, len(destinations)):
            d = self.closer(source, d, destinations[i], avoidParallels)
        return d
    def isFirstInversion(self, chord):
        return tuple(self.zeroForm(chord)) == tuple(self.zeroFormFirstInversion(chord))
    def rotate(self, a, n=1):
        l = a.tolist()
        for i in xrange(n):
            tail = l.pop(self.N - 1)
            l.insert(0, tail)
        return array(l, 'd')
    def invert(self, chord):
        chord = array(chord)
        c = chord[1:self.N].tolist()
        c.append(chord[0] + self.tonesPerOctave)
        d = chord.copy()
        d[0:self.N] = c
        return d
    def rotations(self, chord):
        chord = self.tones(chord)
        rotations = [chord]
        for i in xrange(1, self.N):
            #chord = self.rotate(chord, i)
            chord = self.invert(chord)
            rotations.append(chord)
        return rotations
    def isInside(self, chord, range):
        if self.isPrism:
            return self.isInFundamentalDomain(chord)
            #return self.isInsidePrism(chord, range)
        else:
            return self.isInsideCube(chord, range)
    def isInsideCube(self, chord, range):
        for i in xrange(self.N):
            if chord[i] < -range/2.0:
                return False
            if chord[i] >  range/2.0:
                return False
        return True
    def isInsideNormalPrism(self, chord, range):
        if not self.isInsidePrism(chord, range):
            return False
        if self.isFirstInversion(chord):
            return True
        return False
    def layer(self, chord):
        return sum(chord[0:self.N])
    def isInsidePrism(self, chord, range):
        if chord[0] < -range:
            return False
        elif chord[0] > range:
            return False
        for i in xrange(1, self.N):
            if chord[i] > chord[0] + range:
                return False
            elif chord[i] < chord[0]:
                return False
        s = sum(chord[0:self.N])
        if 0 <= s and s <= range:
            return True
        else:
            return False
    def isInFundamentalDomain(self, chord):
        if self.isInLayer(chord) and self.isInOrder(chord):
            if self.debug:
                print 'Chord',chord,'in F'
            return True
        else:
            if self.debug:
                print 'Chord',chord,'not in F'
            return False
    def isInLayer(self, chord):
        L = self.layer(chord)
        if not (0 <= L and L <= self.R):
            return False
        return True
    def isInOrder(self, chord):
        for i in xrange(self.N - 1):
            if not chord[i] <= chord[i + 1]:
                return False
        if not chord[self.N - 1] <= (chord[0] + self.R):
            return False
        return True
    def O(self, c):
        if self.debug:
            print "O: ",c,
        r = []
        for i in xrange(1, self.N):
            r.append(c[i] - (self.R / self.N))
        r.append(c[0] + (self.R - (self.R / self.N)))
        c[0:self.N] = r
        if self.debug:
            print c
        return c
    def bounceInside(self, chord):
        inversions = self.inversions(chord)
        if self.debug:
            print inversions
        for inversion in inversions:
            if tuple(inversion) in self.trichords:
                return inversion
            return None
    def keepInside(self, chord):
        if self.isInFundamentalDomain(chord):
            return chord
        else:
            inversions = self.inversions(chord)
            if self.debug:
                print inversions
            for inversion in inversions:
                if self.isInOrder(inversion):
                    c = list(inversion)
                    for i in xrange(self.N):
                        if self.isInLayer(c):
                            return array(c)
                        c = self.O(c)
            return None
    def stayInside(self, chord):
        if self.isInside(chord, self.getTessitura()):
            return chord
        chord = self.sort(chord)
        if self.isPrism:
            inversions = self.inversions(chord)
            if self.debug:
                print 'inversions',inversions
            distances = {}
            for inversion in inversions:
                distances[self.euclidean(chord, inversion)] = inversion
            c = distances[max(distances.keys())]
            if self.debug:
                print 'keepInside:', 't =',self.getTessitura(), 'original =',chord, 'inside =',c
            return c
        else:
            c = array(chord)
            for i in xrange(self.N):
                while c[i] <  -self.getTessitura()/2:
                    c[i] += self.getTessitura()
                while c[i] >= self.getTessitura()/2:
                    c[i] -= self.getTessitura()
            c = self.sort(c)
            if self.debug:
                print chord,'keeps inside as',c
            return c
    def pitchclasses(self, chord):
        c = array(chord, 'd').copy()
        for i in xrange(self.N):
            c[i] = self.pitchclass(chord[i])
        return c
    def pitchclass(self, pitch):
        return pitch % self.tonesPerOctave
    '''
    Returns the best bijective voice-leading,
    first by smoothness then by parsimony,
    optionally avoiding parallel fifths,
    from a given source chord of pitches
    to a new chord of pitches
    that belong to the pitch-class set of a target chord,
    and lie within a specified range.
    The algorithm makes an exhaustive search
    of potential target chords in the space.
    '''
    def voicelead(self, a, b, avoidParallels):
        if self.debug:
            print '   From:', a
            print '     To:', b
            print 'Through:'
        invs = self.inversions(b)
        if self.debug:
            for inv in invs:
                print '        ',inv
            c = self.closest(a, invs, avoidParallels)
        if self.debug:
            print '(%d inversions) is:' % len(invs)
            print '        ', c
            print 'Leading:', self.voiceleading(a,c)
        return c
    def label(self, chord):
        c = array(chord[0:self.N])
        return 'C   %s\nT   %s\n0   %s\n1   %s\n0-1 %s\nSum %f' % (c, self.tones(c), self.zeroForm(c), self.firstInversion(c), self.zeroFormFirstInversion(chord), sum(chord[0:self.N]))

class ChordSpace3Model(ChordSpace3):
    def __init__(self, octaveCount=1, tonesPerOctave=12, isCube=False, isPrism=True, isNormalPrism=False, doCycle=False, showFirstInversion=False, doConnect=False, enableCsound=False, debug=False, showUnordered=False):
        ChordSpace3.__init__(self, 3, octaveCount=octaveCount, tonesPerOctave=tonesPerOctave, isCube=isCube, isPrism=isPrism, isNormalPrism=isNormalPrism, debug=debug)
        self.trichords = {}
        self.balls = {}
        self.ballsForChordTypes = {}
        self.doConnect = doConnect
        self.doCycle = doCycle
        self.showUnordered = showUnordered
        self.showFirstInversion = showFirstInversion
        self.firstInversions = []
        self.enableCsound = enableCsound
        if self.enableCsound:
            self.csound = csnd6.CppSound()
        if self.isCube:
            for x in xrange(-self.cubeTessitura/2, self.cubeTessitura/2):
                for y in xrange(-self.cubeTessitura/2, self.cubeTessitura/2):
                    for z in xrange(-self.cubeTessitura/2, self.cubeTessitura/2):
                        trichord = (x,y,z)
                        radius = 0.125
                        if trichord not in self.trichords:
                            self.trichords[trichord] = trichord
                            ball = sphere(pos = trichord, radius = self.cubeRadius)
                            ball.trichord = trichord
                            self.balls[ball.trichord] = ball
                            self.setColor(ball)
                            ball.name = self.label(trichord)
                            ball.label = label(pos = trichord, text = ball.name, height = 11, box = 2, opacity = 0.3, linecolor=(0.9,0.5,0.9), visible = 0, line = 2, xoffset = 20, yoffset = 20)
        if self.isPrism or self.isNormalPrism:
            for x in xrange(-self.R, self.R+1):
                for y in xrange(x, x + self.R+1):
                    for z in xrange(x, x + self.R+1):
                        trichord = array((x,y,z), 'd')
                        trichord = tuple(self.sort(trichord))
                        if self.isPrism and self.isInsidePrism(trichord, self.R):
                            if trichord not in self.trichords:
                                self.trichords[trichord] = trichord
                                tones = tuple(self.tones(trichord))
                                ball = sphere(pos = trichord, radius = self.prismRadius)
                                ball.trichord = trichord
                                self.balls[ball.trichord] = ball
                                self.setColor(ball)
                                ball.name = self.label(trichord)
                                ball.label = label(pos = trichord, text = ball.name, height = 11, box = 2, opacity = 0.3, linecolor=(0.9,0.5,0.9), visible = 0, line = 2, xoffset = 20, yoffset = 20)
                            else:
                                self.balls[trichord].radius = self.prismRadius
                        if self.isNormalPrism and self.isInsideNormalPrism(trichord, self.R):
                            if trichord not in self.trichords:
                                self.trichords[trichord] = trichord
                                tones = tuple(self.tones(trichord))
                                ball = sphere(pos = trichord, radius = self.normalPrismRadius)
                                ball.trichord = trichord
                                self.balls[ball.trichord] = ball
                                self.setColor(ball)
                                ball.name = self.label(trichord)
                                ball.label = label(pos = trichord,  text = ball.name, height = 11, box = 2, opacity = 0.3, linecolor=(0.5,0.5,0.5), visible = 0, line = 2, xoffset = -20, yoffset = 20)

                            else:
                                self.balls[trichord].radius = self.normalPrismRadius
        if self.doConnect:
            for trichord in self.trichords.values():
                self.connect(trichord, self.sort((trichord[0] + 1.0, trichord[1], trichord[2])))
                self.connect(trichord, self.sort((trichord[0], trichord[1] + 1.0, trichord[2])))
                self.connect(trichord, self.sort((trichord[0], trichord[1], trichord[2] + 1.0)))
                self.connect(trichord, self.sort((trichord[0] - 1.0, trichord[1], trichord[2])))
                self.connect(trichord, self.sort((trichord[0], trichord[1] - 1.0, trichord[2])))
                self.connect(trichord, self.sort((trichord[0], trichord[1], trichord[2] - 1.0)))
    def setColor(self, ball):
        z = tuple(self.zeroFormFirstInversion(ball.trichord))
        if z in self.ballsForChordTypes:
            ball.color = self.ballsForChordTypes[z].color
        else:
            # Color major triads red.
            if   z == (0, 4, 7):
                ball.color = (1.0,0.0,0.0)
            # Color augmented triads white.
            elif z == (0, 4, 8):
                ball.color = (1.0,1.0,1.0)
            # Color minor triads blue.
            elif z == (0, 3, 7):
                ball.color = (0.67,0.67,1.0)
            else:
                hue = (z[0] + z[1] * 2.0 + z[2]) / 44.0
                saturation = 1.0
                value = 1.0
                ball.color = color.hsv_to_rgb((hue, saturation, value))
    def showAsFirstInversion(self, trichord):
        if not self.showFirstInversion:
            return False
        elif self.isFirstInversion(trichord):
            return True
        else:
            return False
    def connect(self, origin, neighbor):
        o = tuple(origin)
        n = tuple(neighbor)
        if n in self.trichords:
            curve(pos = [o, n], color = (0.65, 0.65, 0.65), radius = 0.020)
    def runGrab(self, filename, bbox=None):
        while scene.visible:
            if scene.mouse.clicked:
                print 'CURRENT POINT:'
                print 'center =',scene.center
                print 'forward =',scene.forward
                print 'up =',scene.up
                print 'scale =',scene.scale
                print 'fov = ',scene.fov
                print
                try:
                    if bbox:
                        pass #image = ImageGrab.grab(bbox)
                    else:
                        pass #image = ImageGrab.grab()
                    #image = ImageOps.grayscale(image)
                    #image.save(filename)
                    #print 'Captured screen shot in "%s".' % (filename)
                except:
                    traceback.print_exc()
                scene.mouse.events = 0
    def playBall(self, pickedBall):
        pickedBall.label.visible = 1
        print pickedBall.name
        note1 = "i 1 0 4 %d 70 0 -.75" % (60 + pickedBall.pos[0])
        note2 = "i 2 0 4 %d 70 0  .0"  % (60 + pickedBall.pos[1])
        note3 = "i 3 0 4 %d 70 0  .75" % (60 + pickedBall.pos[2])
        print '%s\n%s\n%s' % (note1, note2, note3)
        if self.enableCsound:
            self.csound.inputMessage(note1)
            self.csound.inputMessage(note2)
            self.csound.inputMessage(note3)
        print

    def run(self):
        pickedBall = None
        oldBall = None
        movingChord = ( 0, 4, 7)
        translation = (1,1,1)
        while scene.visible:
            rate(100)
            movingChord = tuple(self.sort(movingChord))
            if scene.kb.keys:
                k = scene.kb.getkey()
                print 'key: %s' % k
                if   k == 'up':
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 0
                    movingChord = self.move(movingChord, 0,  1.0)
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 1
                    self.playBall(movingBall)
                    oldBall = movingBall
                elif k == 'right':
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 0
                    movingChord = self.move(movingChord, 1,  1.0)
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 1
                    self.playBall(movingBall)
                    oldBall = movingBall
                elif k == 'down':
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 0
                    movingChord = self.move(movingChord, 2,  1.0)
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 1
                    self.playBall(movingBall)
                    oldBall = movingBall
                elif k == 'shift+up':
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 0
                    movingChord = self.move(movingChord, 0, -1.0)
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 1
                    self.playBall(movingBall)
                    oldBall = movingBall
                elif k == 'shift+right':
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 0
                    movingChord = self.move(movingChord, 1, -1.0)
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 1
                    self.playBall(movingBall)
                    oldBall = movingBall
                elif k == 'shift+down':
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 0
                    movingChord = self.move(movingChord, 2, -1.0)
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 1
                    self.playBall(movingBall)
                    oldBall = movingBall
                if k in ('p', 'P'):
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 0
                    movingChord = self.nrP(movingChord)
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 1
                    self.playBall(movingBall)
                    oldBall = movingBall
                elif k in ('l', 'L'):
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 0
                    movingChord = self.nrL(movingChord)
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 1
                    self.playBall(movingBall)
                    oldBall = movingBall
                elif k in ('r', 'R'):
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 0
                    movingChord = self.nrR(movingChord)
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 1
                    self.playBall(movingBall)
                    oldBall = movingBall
                elif k in ('d', 'D'):
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 0
                    movingChord = self.nrD(movingChord)
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 1
                    self.playBall(movingBall)
                    oldBall = movingBall
                elif k in ('1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b'):
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 0
                    if k == 'a':
                        k = 10
                    if k == 'b':
                        k = 11
                    movingChord = self.pT(movingChord, float(k))
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible = 1
                    self.playBall(movingBall)
                    oldBall = movingBall
                elif k == 'g':
                    self.runGrab("orbifold.png")
                elif k in ('x', 'X', 'q', 'Q'):
                    sys.exit()
            if scene.mouse.clicked:
                try:
                    m = scene.mouse.getclick()
                    if oldBall:
                        oldBall.label.visible = 0
                    if pickedBall:
                        pickedBall.label.visible = 0
                    oldBall = pickedBall
                    pickedBall = m.pick
                    if pickedBall:
                        movingBall = pickedBall
                        movingChord = tuple(movingBall.pos)
                        self.playBall(pickedBall)
                except:
                    traceback.print_exc()
                    print self.label(movingChord)
                scene.mouse.events = 0
            elif self.doCycle:
                try:
                    movingBall = self.balls[movingChord]
                    movingBall.label.visible=1
                    print movingBall.name
                    time.sleep(2)
                    movingBall.label.visible=0
                    a = (movingChord[0], movingChord[1], movingChord[2])
                    print 'Old chord',a
                    movingChord = (movingChord[0] + translation[0], movingChord[1] + translation[1], movingChord[2] + translation[2])
                    print 'New chord',movingChord
                    #movingChord = self.voiceLead(a, b, True)
                    movingChord = tuple(self.keepInside(movingChord))
                    self.playBall(movingChord)
                except:
                    traceback.print_exc()
                    print self.label(movingChord)
                    return
        print "Finished."

def runModel(model):
    began = time.clock()
    scene.background = (1,1,1)
    scene.background = (0,0,0)
    scene.autocenter = 1
    sort(model.firstInversions)
    if model.enableCsound:
        model.csound.setOrchestra('''
sr                              =                       44100
ksmps                           =                       100
nchnls                          =                       2

                                connect                 "FenderRhodesModel",    "out", "Reverberation", "in"
                                connect                 "FMWaterBell",          "out", "Reverberation", "in"
                                connect                 "Xing",                 "out", "Reverberation", "in"
                                connect                 "Reverberation",        "out", "MasterOutput", "in"

                                alwayson                "Reverberation"
                                alwayson                "MasterOutput"

gkFenderRhodesModelGain         init                    0
gkFenderRhodesModelGainCorrection init                  60
                                instr                   FenderRhodesModel
                                //////////////////////////////////////////////////////
                                // Original by Perry Cook.
                                // Adapted by Michael Gogins.
                                //////////////////////////////////////////////////////
i_instrument                    =                       p1
i_time                          =                       p2
i_duration                      =                       p3
i_midikey                       =                       p4
i_midivelocity                  =                       p5
i_phase                         =                       p6
i_pan                           =                       p7
i_depth                         =                       p8
i_height                        =                       p9
i_pitchclassset                 =                       p10
i_homogeneity                   =                       p11
iattack                         =                       0.01
isustain                        =                       p3
irelease                        =                       0.125
p3                              =                       isustain + iattack + irelease
adeclick                        linsegr                 0.0, iattack, 1.0, isustain, 1.0, irelease, 0.0
iindex                          =                       4
icrossfade                      =                       3
ivibedepth                      =                       0.2
iviberate                       =                       6
isine                           ftgenonce               0, 0, 65536,    10,     1
icosine                         ftgenonce               0, 0, 65536,    11,     1
icookblank                      ftgenonce               0, 0, 65536,     10,     0 ; Blank wavetable for some Cook FM opcodes.
ifn1                            =                       isine
ifn2                            =                       icosine
ifn3                            =                       isine
ifn4                            =                       icookblank
ivibefn                         =                       isine
ifrequency                      =                       cpsmidinn(i_midikey)
asignal                         fmrhode                 1, ifrequency, iindex, icrossfade, ivibedepth, iviberate, ifn1, ifn2, ifn3, ifn4, ivibefn
kdbgain                         =                       gkFenderRhodesModelGainCorrection + (gkFenderRhodesModelGain - 0.5) * 100.0
kiamplitude                     =                       ampdb(i_midivelocity) * pow(10, kdbgain / 20)
aoutleft, aoutright		        pan2			        asignal * adeclick * kiamplitude, i_pan
aout[]                          init                    2
aout[0]                         =                       aoutleft
aout[1]                         =                       aoutright
                                outletv                 "out",  aout
                                endin

gkFMWaterBellGain               init                    0.0
gkFMWaterBellGainCorrection     init                    50.0
                                instr                   FMWaterBell
                                //////////////////////////////////////////////
                                // Original by Steven Yi.
                                // Adapted by Michael Gogins.
                                //////////////////////////////////////////////
i_instrument                    =                       p1
i_time                          =                       p2
i_duration                      =                       p3
i_midikey                       =                       p4
i_midivelocity                  =                       p5
i_phase                         =                       p6
i_pan                           =                       p7
i_depth                         =                       p8
i_height                        =                       p9
i_pitchclassset                 =                       p10
i_homogeneity                   =                       p11
ipch                            =                       cpsmidinn(i_midikey)
iamplitude                      =                       ampdb(i_midivelocity)
ipch2                           =                       ipch
kpchline 	                    line                    ipch, i_duration, ipch2
iamp 	                        =                       2
ienvType	                    =                       2
kenv 	                        init 	                0
                                if ienvType == 0 kgoto env0  ; adsr
                                if ienvType == 1 kgoto env1  ; pyramid
                                if ienvType == 2 kgoto env2  ; ramp
env0:
kenv	                        adsr	                .3, .2, .9, .5
                                kgoto                   endEnvelope
env1:
kenv 	                        linseg	                0, i_duration * .5, 1, i_duration * .5, 0
                                kgoto                   endEnvelope
env2:
kenv	                        linseg 	                0, i_duration - .1, 1, .1, 0
kgoto                           endEnvelope
endEnvelope:
kc1                             =                       5
kc2                             =                       5
kvdepth                         =                       0.005
kvrate                          =                       6
icosine                  	    ftgenonce               0, 0, 65536, 11, 1
ifn1                            =                       icosine
ifn2                            =                       icosine
ifn3                            =                       icosine
ifn4                            =                       icosine
ivfn                            =                       icosine
asignal                         fmbell	                iamp, kpchline, kc1, kc2, kvdepth, kvrate, ifn1, ifn2, ifn3, ifn4, ivfn
iattack                         =                       0.003
isustain                        =                       p3
irelease                        =                       0.06
p3                              =                       isustain + iattack + irelease
adeclick                        linsegr                 0.0, iattack, 1.0, isustain, 1.0, irelease, 0.0
kdbgain                         =                       gkFMWaterBellGainCorrection + (gkFMWaterBellGain - 0.5) * 100.0
kiamplitude                     =                       ampdb(i_midivelocity) * pow(10, kdbgain / 20)
aoutleft, aoutright		        pan2			        asignal * adeclick * kiamplitude, i_pan
aout[] init 2
aout[0] = aoutleft
aout[1] = aoutright
                                outletv                 "out",  aout
                                endin

gkXingGain                      init                    0.0
gkXingGainCorrection            init                    60.0
                                instr                   Xing
                                //////////////////////////////////////////////
                                // Original by Andrew Horner.
                                // Adapted by Michael Gogins.
                                // p4 pitch in octave.pch
                                // original pitch        = A6
                                // range                 = C6 - C7
                                // extended range        = F4 - C7
                                //////////////////////////////////////////////
insno           		        =                       p1
itime           		        =                       p2
iduration       		        =                       p3
ikey            		        =                       p4
ivelocity                       =                       p5
iphase                          =                       p6
ipan                            =                       p7
idepth                          =                       p8
iheight                         =                       p9
ipcs                            =                       p10
ihomogeneity                    =                       p11
kgain			    	        =                       1.25
iHz                             =                       cpsmidinn(ikey)
kHz                             =                       k(iHz)
iattack                         =                       (440.0 / iHz) * 0.01
                                print                   iHz, iattack
isustain                        =                       p3
irelease                        =                       .3
p3                              =                       iattack + isustain + irelease
iduration                       =                       p3
iamplitude                      =                       ampdb(ivelocity).
isine                           ftgenonce               0, 0, 65536,    10,     1
kfreq                           =                       cpsmidinn(ikey)
iamp                            =                       1
inorm                           =                       32310
aamp1                           linseg                  0,.001,5200,.001,800,.001,3000,.0025,1100,.002,2800,.0015,1500,.001,2100,.011,1600,.03,1400,.95,700,1,320,1,180,1,90,1,40,1,20,1,12,1,6,1,3,1,0,1,0
adevamp1                        linseg                  0, .05, .3, iduration - .05, 0
adev1                           poscil                  adevamp1, 6.7, isine, .8
amp1                            =                       aamp1 * (1 + adev1)
aamp2                           linseg                  0,.0009,22000,.0005,7300,.0009,11000,.0004,5500,.0006,15000,.0004,5500,.0008,2200,.055,7300,.02,8500,.38,5000,.5,300,.5,73,.5,5.,5,0,1,1
adevamp2                        linseg                  0,.12,.5,iduration-.12,0
adev2                           poscil                  adevamp2, 10.5, isine, 0
amp2                            =                       aamp2 * (1 + adev2)
aamp3                           linseg                  0,.001,3000,.001,1000,.0017,12000,.0013,3700,.001,12500,.0018,3000,.0012,1200,.001,1400,.0017,6000,.0023,200,.001,3000,.001,1200,.0015,8000,.001,1800,.0015,6000,.08,1200,.2,200,.2,40,.2,10,.4,0,1,0
adevamp3                        linseg                  0, .02, .8, iduration - .02, 0
adev3                           poscil                  adevamp3, 70, isine ,0
amp3                            =                       aamp3 * (1 + adev3)
awt1                            poscil                  amp1, kfreq, isine
awt2                            poscil                  amp2, 2.7 * kfreq, isine
awt3                            poscil                  amp3, 4.95 * kfreq, isine
asig                            =                       awt1 + awt2 + awt3
arel                            linenr                  1,0, iduration, .06
; asignal                         =                       asig * arel * (iamp / inorm) * iamplitude * kgain
asignal                         =                       asig * (iamp / inorm) * kgain
adeclick                        linsegr                 0, iattack, 1, isustain, 1, irelease, 0
kdbgain                         =                       gkXingGainCorrection + (gkXingGain - 0.5) * 100.0
kiamplitude                     =                       ampdb(ivelocity) * pow(10, kdbgain / 20)
aoutleft, aoutright		        pan2			        asignal * adeclick * kiamplitude, ipan
aout[] init 2
aout[0] = aoutleft
aout[1] = aoutright
                                outletv                 "out",  aout
                                endin


gkReverberationEnabled          init                    1
gkReverberationDelay            init                    0.4
gkReverberationWet          	init                    0.25
                                instr                   Reverberation
                                //////////////////////////////////////////////
                                // By Michael Gogins.
                                //////////////////////////////////////////////
ain[]                           init 2
ain                             inletv                  "in"
ainleft                         =                       ain[0]
ainright                        =                       ain[1]
if (gkReverberationEnabled == 1) then
aoutleft                        =                       ainleft
aoutright                       =                       ainright
kdry				            =			            1.0 - gkReverberationWet
else
awetleft, awetright             reverbsc                ainleft, ainright, gkReverberationDelay, 18000.0
aoutleft			            =			            ainleft *  kdry + awetleft  * gkReverberationWet
aoutright			            =			            ainright * kdry + awetright * gkReverberationWet
endif
aout[]                          init                    2
aout[0]                         =                       aoutleft
aout[1]                         =                       aoutright
                                outletv                 "out", aout
                                endin

gkMasterLevel                   init                    1.0
                                instr                   MasterOutput
                                //////////////////////////////////////////////
                                // By Michael Gogins.
                                //////////////////////////////////////////////
ain[]                           init 2
ain                             inletv                  "in"
ain[0]                          =                       gkMasterLevel * ain[0]
ain[1]                          =                       gkMasterLevel * ain[1]
                                out                     ain
                                endin

            ''')
        model.csound.setScore('''
            f1 0 8192 10 1
            f0 6000
            e
            ''')
        model.csound.setCommand('csound -h -d -r 48000 -k 1000 -m227 -b1000 -B1000 -odac')
        #gc.disable()
        model.csound.compile()
        performanceThread = csnd6.CsoundPerformanceThread(model.csound)
        performanceThread.Play()
    fg = (1,1,1)
    arrowcolor = (0.7,0.7,0.7)
    size = model.getTessitura() * 1.125
    shaftwidth = model.cubeRadius * 1.0
    arrow(pos = (0,0,0), axis=(size/3,0,0), fixedwidth=1, shaftwidth=shaftwidth, color = arrowcolor)
    label(pos = (size/3,0,0), text = 'Voice 1', color=fg, height = 20, box = 0, linecolor=(0.5,0.5,0.5), opacity = 0.1, visible = 1, line = 0, xoffset =  5, yoffset =  5, zoffset = 5)
    arrow(pos = (0,0,0), axis=(0,size/1.5,0), fixedwidth=1, shaftwidth=shaftwidth, color = arrowcolor)
    label(pos = (0,size/1.5,0), text = 'Voice 2', color=fg, height = 20, box = 0, linecolor=(0.5,0.5,0.5), opacity = 0.1, visible = 1, line = 0, xoffset =   5, yoffset = 5, zoffset = 5)
    arrow(pos = (0,0,0), axis=(0,0,size), fixedwidth=1, shaftwidth=shaftwidth, color = arrowcolor)
    label(pos = (0,0,size), text = 'Voice 3', color=fg, height = 20, box = 0, linecolor=(0.5,0.5,0.5), opacity = 0.1, visible = 1, line = 10, xoffset = 5, yoffset = 5, zoffset = 5)
    arrow(pos = (0,0,0), axis=(size/2.5,size/2.5,size/2.5), fixedwidth=1, shaftwidth=shaftwidth, color=arrowcolor)
    label(pos = (size/2.5,size/2.5,size/2.5), text = 'Orthogonal axis', color=fg, height = 20, box = 0, linecolor=(0.5,0.5,0.5), opacity = 0.1, visible = 1, line = 0, xoffset = 5, yoffset = 5, zoffset = 5)
    ended = time.clock()
    elapsed = ended - began
    print 'elapsed: %f' % (elapsed)
    model.run()
    print 'Visual finished.'
    if model.enableCsound:
        performanceThread.Stop()
        print 'Csound finished.'


if __name__ == '__main__':
    #scene.fullscreen = False
    #scene.width = 300 * 7
    #scene.height = 300 * 5
    # ChordSpace3 for trichords
    model = ChordSpace3Model(octaveCount=1, doCycle=False, doConnect=True, isPrism=True, enableCsound=True)
    # Ranged chord space
    #model = ChordSpace3Model(octaveCount=2, doCycle=False, doConnect=False, isCube=True, isPrism=False)
    # ChordSpace3 in ranged chord space
    #model = ChordSpace3Model(octaveCount=1, doCycle=True, doConnect=False, isPrism=True, isCube=True)
    # Voice-leading space
    #model = ChordSpace3Model(octaveCount=3, doCycle=True, doConnect=False, isPrism=True, isNormalPrism=False)
    # Normal chord space
    #model = ChordSpace3Model(octaveCount=3, doCycle=False, doConnect=False, isPrism=False, isNormalPrism=True)
    # Normal chord space in voice-leading space
    #model = ChordSpace3Model(octaveCount=3, doCycle=False, doConnect=False, isPrism=True, isNormalPrism=True)
    runModel(model)
    print 'Program finished.'