五線譜を演奏する?プログラムを作る。

とりあえず、マウスをクリックすると音符ができて、playできるところまで。
…もうすこしスマートなプログラムにしたい。
…暑くってやる気が起きない。

#!/usr/bin/env python

import Tkinter
import math
import sndPlay

class scorePlayer:
    def __init__(self):
        self.width = 800
        self.height = 200
        self.window = Tkinter.Tk()
        self.canvas = Tkinter.Canvas(self.window,bg='white',
                                     width=self.width,height=self.height)
        self.canvas.bind("<Motion>",self.motionCanvas)
        self.canvas.bind("<ButtonPress-1>",self.press1Canvas)

        freqMap = [{'inc':3,'color':'white'},          # C
                   {'inc':5,'color':'white'},          # D
                   {'inc':7,'color':'black'},          # E
                   {'inc':8,'color':'white'},          # F
                   {'inc':10,'color':'black'},         # G
                   {'inc':12,'color':'white'},         # A
                   {'inc':14,'color':'black'},         # B
                   {'inc':15,'color':'white'},         # C
                   {'inc':17,'color':'black'},         # D
                   {'inc':19,'color':'white'},         # E
                   {'inc':20,'color':'black'},         # F
                   {'inc':22,'color':'white'},         # G
                   {'inc':24,'color':'white'},         # A
                   ]
        inc = math.pow(2,1.0/12)
        A = 440
        self.toneMap = [{'index':ii,'freq':math.pow(inc,freqMap[ii]['inc'])*A,'color':freqMap[ii]['color']} for ii in range(len(freqMap))]
        self.toneMap.reverse()
        self.notes = []
        self.canvas.pack()
        self.currentNote = None
        self.xStart = 40
        self.currentX = 0
        self.xInterval = 30
        self.scoreYInterval = 10
        self.noteWidth,self.noteHeight = 15,8

        self.paint()
        self.playButton = Tkinter.Button(self.window,text="play",
                                         command=self.play)
        self.clearButton = Tkinter.Button(self.window,text='clear',
                                          command=self.clear)
        self.playButton.pack(side=Tkinter.RIGHT)
        self.clearButton.pack(side=Tkinter.RIGHT)
        self.window.mainloop()
    def clear(self):
        for note in self.notes:
            self.canvas.delete(note['index'])
        self.notes = []
        self.currentX = 0
    def play(self):
        s = sndPlay.sndPlay()
        s.A = 440
        for note in self.notes:
            print note
#            self.canvas.itemconfigure(note['index'],outline='red')
            s.addWave(note['tone']['freq'],0.5)
            s.addWave(0,0.1)
#            self.canvas.itemconfigure(note['index'],outline='black')
        s.closeFile()

    def convertYtoTone(self,y):
        interval = self.scoreYInterval / 2
        for tone in self.toneMap:
            jj = tone['y']
            if jj-interval < y < jj+interval: return tone
        return None

    def press1Canvas(self,event):
        width,height = self.noteWidth,self.noteHeight
        tone = self.convertYtoTone(event.y)
        if tone==None: return
        y = tone['y']
        x = self.currentX*self.xInterval+self.xStart
        index = self.canvas.create_oval(x-width/2,y-height/2,
                                        x+width/2,y+height/2,
                                        fill='black',
                                        width=2)
        self.currentX = self.currentX + 1
        self.notes.append({'tone':tone,'index':index})

    def motionCanvas(self,event):
        width,height = self.noteWidth,self.noteHeight
        x = event.x
        tone = self.convertYtoTone(event.y)
        if tone==None: return
        y = tone['y']
        if( self.currentNote == None ):
            self.currentNote = self.canvas.create_oval(x-width/2,y-height/2,
                                                       x+width/2,y+height/2,
                                                       outline='gray',
                                                       fill='gray',
                                                       width=2)
        else:
            self.canvas.coords(self.currentNote,
                               x-width/2,y-height/2,
                               x+width/2,y+height/2)

    def paint(self):
        xMargin = 30
        yMargin = 30
        scoreYInterval = self.scoreYInterval
        for ii in range(len(self.toneMap)):
            sx = xMargin
            ex = self.width - xMargin
            sy = ey = yMargin + scoreYInterval/2*ii
            self.canvas.create_line(sx,sy,ex,ey,fill=self.toneMap[ii]['color'],width=1)
            self.toneMap[ii]['y'] = sy

if __name__ == '__main__':
    scorePlayer()