Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
#!/usr/bin/wish # (find-es "tcl" "snack") # (find-es "tcl" "piano.tcl") # Copied from a Tcler's Wiki page by Richard Suchenwirth, with no changes. # "A toy piano": http://wiki.tcl.tk/3948 # (find-bgprocess "~/TCL/piano.tcl") # Bug: # Could not gain access to /dev/sound/dsp for writing. # Could not gain access to /dev/sound/dsp for writing. # while executing # "$::snd play -filter $::filter" # invoked from within # "if $freq { # $c move $id 1 1 # $::filter configure $freq # $::snd play -filter $::filter # $::filter2 configure [expr {$freq/2.}] ..." # (procedure "play" line 2) # invoked from within # "play .c 22 440.0" # (command bound to event) package require Tk ;# to make Starkit'ting this easier package require sound ;# we don't yet use the Tk goodies of snack set snd [snack::sound -rate 22050] set snd2 [snack::sound -rate 22050] ;# second sound to add volume set filter [snack::filter generator 1000 30000 0.7 sine] set filter2 [snack::filter generator 1000 30000 0.0 sine] # compute sound frequencies, given a' = 440 Hz set a 440 # Logarithm to base 2 allows us to proceed linearly in 1/12 steps set lda [expr {log($a)/log(2)}] # But this list starts from c'', so we have to add 3/12 set names {c c# d d# e f f# g g# a bb b} set freqs {} for {set i 0} {$i<12} {incr i} { lappend freqs [expr {pow(2, $lda + (3+$i)/12.)}] } proc play {c id freq} { if $freq { $c move $id 1 1 $::filter configure $freq $::snd play -filter $::filter $::filter2 configure [expr {$freq/2.}] ;# one octave lower $::snd2 play -filter $::filter2 } else { $c move $id -1 -1 after 20 $::snd stop after 120 $::snd2 stop } } proc nameof {name factor} { if {$factor==0.25} {set name [string toupper $name]} while {$factor>=1} { append name ' set factor [expr {$factor/2.}] } set name } set x0 5; set y0 5 ;# top left corner to start set y1 100 ;# length of white keys set y05 [expr $y1*.67] ;# length of black keys set dx 18 ;# width of white keys set dx2 [expr {$dx/2}] ;# offset of black keys set c [canvas .c -bg brown -height [expr $y1+5] -width [expr $dx*31]] $c config -cursor hand2 ;# so we see the single finger that plays pack $c wm resizable . 0 0 ;# keep the window fixed-size foreach factor {0.25 0.5 1 2 4} { foreach name $names freq $freqs { set f [expr {$freq * $factor}] if {[string length $name] == 1} { set id [$c create rect $x0 $y0 [expr {$x0+$dx}] $y1 -fill white] incr x0 $dx; incr x0 1 } else { set x [expr {$x0 - $dx*.35}] set id [$c create rect $x $y0 [expr {$x + $dx*0.65}] $y05 \ -fill black -tag black] } $c bind $id <1> "play $c $id $f" ;# sound on $c bind $id <ButtonRelease-1> "play $c $id 0" ;# sound off $c bind $id <Enter> \ [list wm title . "piano: [nameof $name $factor] [format %.1f $f]"] if {$factor == 4 && $name == "c"} break ;# extra c key at right } } $c raise black ;# otherwise half-hidden by next white key