Intervals

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