Started by TV
Based on RS's A toy piano, which is a nice example of efficient programming, I make the keyboard appear on a bwise canvas, where it can send signals to other bwise blocks. Also, I make a block (or procedure which can be used with Automatically generate Bwise blocks from procedures) to generate Midi signals on the bwise canvas, to begin with on linux, using Alsa midi. There isn't much of a Midi library for tcl although the language is fun to play with for automatic composition or user interfaces like parameter control, this application simply will use an external program (with open | ) to generate some midi notes, or, a socket can be used instead to drive a program which makes audio or audio waves (.wav files).
To work: this is the tryout code, of course the main part is from RS:
# 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 nameof {name factor} { if {$factor==0.25} {set name [string toupper $name]} while {$factor>=1} { append name ' set factor [expr {$factor/2.}] } set name } proc play { {c} {id} {freq} } { global keyb.out if $freq { $c move $id 1 1 } else { $c move $id -1 -1 } set keyb.out $freq net_funprop keyb } 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 $mc.keyb -bg brown -height [expr $y1+5] -width [expr $dx*31]] $mc.keyb config -cursor hand2 ;# so we see the single finger that plays $mc create window [expr 4] [expr 4] -window $mc.keyb -tags "keyb" -anchor nw # pack $c newblock keyb 0 0 562 109 {in} {out} keyb set keyb.bfunc {} 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
Now the input don't work (does nothing), and the output only gives out the frequency of a note being pressed, and the block is a trigger-generatingblock, a net_funprop will be issued when a key is pressed, which sends the frequency on the output pin to connected blocks, and fires the whole sequence of them in proper order, until all have fired. When the note is released, '0' is sent.
See also : Creating wave formulas with BWise to create formulas which are time evolution formulas to for instance create music samples with.