A musical keyboard for BWise and Midi connections

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).

Image BWise midikeyb1.jpg

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

Image BWise piano4.jpg

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.