Midi_keyboard

Using aconnect and aseqdump to make a simple terminal piano keyboard.

It’s just a sketch.. version 0.0.1.

TODO Link to heading

  • detect screenwidth
  • more fullsize keyboard
  • update ‘chords’ on release
  • detect ‘hung’ keys
  • output intervals and chord-names

CODE Link to heading

require 'set'
require 'open3'

acon = `aconnect -i`.scan(/client (\d+): '(.*?)'/).to_h
puts 'Select Instrument by number :'
acon.each {|k, v| puts '[%s] %s' % [k, v]}

PORT = gets.chomp
PIANO = DATA.read
WHITE,BLACK = [0,2,4,5,7,9,11], [1,3,-1,6,8,10]

abort('No such Instrument') unless inst = acon[PORT]
wr, RD = Open3.popen2("aseqdump -p #{PORT}"); wr.close

X = {
  cls:    proc {print "\e[2J"},
  cursor: proc {|show| print show ? "\e[?25h" : "\e[?25l"},
  at:     proc {|l,c| print "\e[#{l};#{c}H"},
  red:    proc {|s| print "\e[37;41m#{s}\e[0m"},
  rat:    proc {|l,c,str| X.at[l,c]; X.red[str]},
  piano:  proc {X.at[0,0]; print PIANO},
  keys:   proc {|held|
    held.each {|key|
      lc = BLACK.include?(key) ?
        [2, BLACK.index(key) * 4 + 5, ' '] :
        [4, (i=WHITE.index(key)) * 4 + 3, 'CDEFGAB'[i]]
      X.rat[*lc]
    }
  },
  twelve: proc {|s| s.to_a.map {|i| i % 12 }},
  bye:    proc {RD.close; X.cursor[true]; exit}
}.then {|hsh| Struct.new(*hsh.keys).new(*hsh.values)}

trap('SIGINT') { X.bye[] }

X.cls[]; X.cursor[false]; X.piano[]

held = Set[]
# main loop
while line = RD.gets
  if line =~ /Note (on|off).*?note (\d+)/
    $1['on'] ? held.add($2.to_i) : held.delete($2.to_i)
    held.empty? ? X.piano[] : X.keys[X.twelve[held]]
  end
end

X.bye[]

__END__
|  |█| |█|  |  |█| |█| |█|  |
|  |█| |█|  |  |█| |█| |█|  |
|   |   |   |   |   |   |   |
| C | D | E | F | G | A | B |