require 'open3'
require 'tty-cursor'
require 'tty-screen'
KEYBOARD = 'Keystation 49es'
LOW_C = 0
CUR = TTY::Cursor
HEIGHT, WIDTH = TTY::Screen.size
NOTENAMES = %i(C C♯/D♭ D D♯/E♭ E F F♯/G♭ G G♯/A♭ A A♯/B♭ B)
INTERVAL_NAMES = %i(P1 m2 M2 m3 M3 P4 A4/d5 P5 m6 M6 m7 M7 P8
m9 M9 m10 M10 P11 A11/d12 P12 m13 M13 m14 M14 P15)
# incomplete
CHORDNAMES = {
'027' => 'sus2',
'026' => 'sus2 ♭5',
'0510' => 'sus2 (1:st inv)',
'036' => 'diminished',
'039' => 'diminished (1:st inv)',
'069' => 'diminished (2:nd inv)',
'037' => 'minor',
'049' => 'minor (1:st inv)',
'058' => 'minor (2:nd inv)',
'047' => 'major',
'046' => 'major ♭5' ,
'038' => 'major (1:st inv)',
'059' => 'major (2:nd inv)',
'048' => 'augmented',
'057' => 'sus4',
}
alias ¤ print
¤ CUR.hide
# Ctrl-C hook
¤ CUR.move_to(0, HEIGHT-2); ¤ '-' * WIDTH
Signal.trap("SIGINT") {
puts "\n", '-' * WIDTH
¤ CUR.show; RD.close; exit(true)
}
# get keyboard and data or quit
nope = -> { puts %(Can't find keyboard!); exit }
data = `aseqdump -l | grep '#{KEYBOARD}'`
(data == '') ? nope[] : (port = data.split[0])
# open pipe (skip write)
WR, RD = Open3.popen2("aseqdump -p #{port}"); WR.close
def notename(no) = ( NOTENAMES[no.divmod(12)[1]].upcase )
def interval(tup) = ( INTERVAL_NAMES[tup[1]-tup[0]] )
def tritone(tri) = ( CHORDNAMES[tri.map {|n| n - tri[0]}.join] )
def show(ch)
¤ CUR.move_to(0,HEIGHT)
¤ CUR.clear_line
case ch.length
when 1
¤ 'note: %s' % notename(ch.first)
when 2
¤ 'interval: %s' % interval(ch)
when 3
¤ 'tritone: (base: %s) %s' % [notename(ch.first), tritone(ch)]
else
¤ 'complex: %s (%s)' % [ch.join(' '), 'not implemented']
end
end
# loop
chord = []
while line = RD.gets
chord = [] if /Control change/ =~line # foot pedal *)
if /Note (on|off)/ =~ line
note = line.split[5].chomp(',')
($1 == 'on') ? (chord << note.to_i) : chord.delete(note.to_i)
show(chord.sort) unless chord.empty?
end
end
=begin
*) reset 'hung' midi with foot-pedal.
=end