zgomot 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +52 -14
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/examples/cycle.rb +13 -0
- data/examples/cycle_note_list.rb +14 -0
- data/examples/dynamic_length.rb +18 -0
- data/examples/dynamic_length_perc.rb +22 -0
- data/examples/geco_input.rb +16 -0
- data/examples/simple_input.rb +6 -2
- data/examples/simple_note_list.rb +12 -0
- data/examples/zgomot_streams.rb +7 -3
- data/lib/zgomot.rb +1 -0
- data/lib/zgomot/boot.rb +2 -0
- data/lib/zgomot/comp.rb +1 -0
- data/lib/zgomot/comp/chord.rb +8 -2
- data/lib/zgomot/comp/mode.rb +1 -1
- data/lib/zgomot/comp/note_list.rb +68 -0
- data/lib/zgomot/comp/pattern.rb +12 -4
- data/lib/zgomot/comp/perc.rb +34 -64
- data/lib/zgomot/comp/progression.rb +20 -7
- data/lib/zgomot/drivers/core_midi.rb +14 -5
- data/lib/zgomot/main.rb +50 -1
- data/lib/zgomot/midi/cc.rb +15 -9
- data/lib/zgomot/midi/dispatcher.rb +8 -0
- data/lib/zgomot/midi/note.rb +8 -11
- data/lib/zgomot/midi/stream.rb +11 -1
- data/lib/zgomot/ui/windows.rb +139 -66
- data/zgomot.gems +1 -0
- data/zgomot.gemspec +7 -2
- metadata +25 -2
@@ -20,9 +20,7 @@ module Zgomot::Comp
|
|
20
20
|
end.unshift(tonic)
|
21
21
|
end
|
22
22
|
def new_respond_to?(meth, include_private=false)
|
23
|
-
old_respond_to?(meth) or
|
24
|
-
notes.any?{|n| n.respond_to?(meth)} or
|
25
|
-
(items.respond_to?(meth) and [:reverse!, :shift, :pop, :push, :unshift].include?(meth))
|
23
|
+
old_respond_to?(meth) or notes.any?{|n| n.respond_to?(meth)}
|
26
24
|
end
|
27
25
|
alias_method :old_respond_to?, :respond_to?
|
28
26
|
alias_method :respond_to?, :new_respond_to?
|
@@ -57,15 +55,30 @@ module Zgomot::Comp
|
|
57
55
|
def [](*args)
|
58
56
|
@items = args.flatten; self
|
59
57
|
end
|
60
|
-
def velocity
|
61
|
-
notes.each{|n| n.velocity
|
58
|
+
def velocity!(v)
|
59
|
+
notes.each{|n| n.velocity!(v)}; self
|
62
60
|
end
|
63
|
-
def length
|
64
|
-
notes.each{|n| n.length
|
61
|
+
def length!(v)
|
62
|
+
notes.each{|n| n.length!(v)}; self
|
65
63
|
end
|
66
64
|
def note(number)
|
67
65
|
notes.map{|n| n.note(number)}
|
68
66
|
end
|
67
|
+
def shift
|
68
|
+
@notes = nil; @items.shift
|
69
|
+
end
|
70
|
+
def unshift(n)
|
71
|
+
@notes = nil; @items.unshift(n); self
|
72
|
+
end
|
73
|
+
def pop
|
74
|
+
@notes = nil; @items.pop
|
75
|
+
end
|
76
|
+
def push(n)
|
77
|
+
@notes = nil; @items.push(n); self
|
78
|
+
end
|
79
|
+
def reverse!
|
80
|
+
@notes = nil; @items.reverse!; self
|
81
|
+
end
|
69
82
|
|
70
83
|
#.........................................................................................................
|
71
84
|
# midi interface
|
@@ -109,14 +109,14 @@ class Zgomot::Drivers
|
|
109
109
|
def load_destinations
|
110
110
|
@destinations = (0..(Interface.MIDIGetNumberOfDestinations()-1)).reduce([]) do |dest, i|
|
111
111
|
destination_ptr = Interface.MIDIGetDestination(i)
|
112
|
-
dest <<
|
112
|
+
dest << get_entity_name(destination_ptr)
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
116
|
def load_sources
|
117
117
|
@sources = (0..(Interface.MIDIGetNumberOfSources()-1)).reduce([]) do |src, i|
|
118
118
|
source_ptr = Interface.MIDIGetSource(i)
|
119
|
-
src <<
|
119
|
+
src << get_entity_name(source_ptr)
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
@@ -136,11 +136,20 @@ class Zgomot::Drivers
|
|
136
136
|
@output = @destinations[iac_index]
|
137
137
|
end
|
138
138
|
|
139
|
+
def get_entity_name(from)
|
140
|
+
name = get_property(:model, from)
|
141
|
+
name.nil? ? (get_property(:name, from) || 'Unknown') : name
|
142
|
+
end
|
143
|
+
|
139
144
|
def get_property(name, from)
|
140
145
|
prop = Interface::CFString.CFStringCreateWithCString(nil, name.to_s, 0)
|
141
|
-
|
142
|
-
Interface::MIDIObjectGetStringProperty(from, prop,
|
143
|
-
|
146
|
+
val_ptr = FFI::MemoryPointer.new(:pointer)
|
147
|
+
Interface::MIDIObjectGetStringProperty(from, prop, val_ptr)
|
148
|
+
if val_ptr.read_pointer.address > 0
|
149
|
+
Interface::CFString.CFStringGetCStringPtr(val_ptr.read_pointer, 0).read_string
|
150
|
+
else
|
151
|
+
nil
|
152
|
+
end
|
144
153
|
end
|
145
154
|
|
146
155
|
def create_client(name)
|
data/lib/zgomot/main.rb
CHANGED
@@ -13,7 +13,7 @@ module Zgomot
|
|
13
13
|
end
|
14
14
|
delegate Zgomot::Boot, :before_start
|
15
15
|
delegate Zgomot::Midi::Clock, :set_config
|
16
|
-
delegate Zgomot::Midi::Stream, :str, :run, :play, :pause, :stop, :tog
|
16
|
+
delegate Zgomot::Midi::Stream, :str, :run, :play, :pause, :stop, :tog, :delete
|
17
17
|
delegate Zgomot::Midi::Dispatcher, :clk
|
18
18
|
delegate Zgomot::Midi::CC, :cc, :add_cc, :learn_cc
|
19
19
|
delegate Zgomot::Comp::Pattern, :np, :cp, :c, :n, :pr
|
@@ -22,6 +22,55 @@ module Zgomot
|
|
22
22
|
:add_input, :remove_input
|
23
23
|
delegate Zgomot::UI::MainWindow, :dash
|
24
24
|
delegate Zgomot::UI::Output, :lstr, :lcc, :lconfig
|
25
|
+
delegate Zgomot, :watch
|
26
|
+
delegate Zgomot::Comp::NoteList, :nl
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.last_error
|
30
|
+
@last_error
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.set_last_error(error)
|
34
|
+
@last_error = error
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.watch(dir=nil)
|
38
|
+
dir ||= '.'
|
39
|
+
raise(Zgomot::Error, "Directory '#{dir}' does not exist") unless Dir.exists?(dir)
|
40
|
+
Zgomot.logger.info "WATCHING '#{dir}' FOR UPDATES"
|
41
|
+
Thread.new do
|
42
|
+
FSSM.monitor(dir) do
|
43
|
+
update do |dir, file|
|
44
|
+
unless /.*\.rb$/.match(file).nil?
|
45
|
+
Zgomot.set_last_error(nil)
|
46
|
+
path = File.join(dir, file)
|
47
|
+
Zgomot.logger.info "LOADED UPDATED FILE: #{path}"
|
48
|
+
playing_streams = Zgomot::Midi::Stream.streams.values.select{|s| s.status_eql?(:playing)}
|
49
|
+
playing_streams.each{|s| Zgomot::Midi::Stream.pause(s.name)}
|
50
|
+
sleep(Zgomot::Midi::Clock.measure_sec)
|
51
|
+
begin
|
52
|
+
load path
|
53
|
+
rescue Exception => e
|
54
|
+
Zgomot.set_last_error(e.message)
|
55
|
+
end
|
56
|
+
playing_streams.each{|s| Zgomot::Midi::Stream.play(s.name)}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
create do |dir, file|
|
60
|
+
unless /.*\.rb$/.match(file).nil?
|
61
|
+
Zgomot.set_last_error(nil)
|
62
|
+
path = File.join(dir, file)
|
63
|
+
Zgomot.logger.info "LOADED CREATED FILE: #{path}"
|
64
|
+
begin
|
65
|
+
load path
|
66
|
+
rescue Exception => e
|
67
|
+
Zgomot.set_last_error(e.message)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
dir
|
25
74
|
end
|
26
75
|
end
|
27
76
|
|
data/lib/zgomot/midi/cc.rb
CHANGED
@@ -8,7 +8,7 @@ module Zgomot::Midi
|
|
8
8
|
|
9
9
|
attr_reader :ccs, :params
|
10
10
|
|
11
|
-
def add_cc(name, cc, args)
|
11
|
+
def add_cc(name, cc, args, &blk)
|
12
12
|
channel = args[:channel].nil? ? 1 : args[:channel]
|
13
13
|
min = args[:min].nil? ? 0.0 : args[:min]
|
14
14
|
max = args[:max].nil? ? 1.0 : args[:max]
|
@@ -20,7 +20,8 @@ module Zgomot::Midi
|
|
20
20
|
:value => init,
|
21
21
|
:type => type,
|
22
22
|
:updated_at => ::Time.now,
|
23
|
-
:cc => cc
|
23
|
+
:cc => cc,
|
24
|
+
:blk => blk}
|
24
25
|
Zgomot.logger.info "ADDED CC #{cc}:#{name}:#{init}:#{channel}"
|
25
26
|
end
|
26
27
|
def learn_cc(name, cc, args)
|
@@ -51,13 +52,18 @@ module Zgomot::Midi
|
|
51
52
|
unless name.nil?
|
52
53
|
Zgomot.logger.info "UPDATED CC #{cc_num}:#{name}:#{value}:#{channel}"
|
53
54
|
p = @params[name][channel]
|
54
|
-
p
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
p[:
|
59
|
-
|
60
|
-
|
55
|
+
unless p.nil?
|
56
|
+
p[:updated_at] = ::Time.now
|
57
|
+
min = p[:min]
|
58
|
+
max = p[:max]
|
59
|
+
if p[:type] == :cont
|
60
|
+
p[:value] = min + (max - min)*value.to_f/127.0
|
61
|
+
else
|
62
|
+
p[:value] = value == 127 ? true : false
|
63
|
+
end
|
64
|
+
if p[:blk]
|
65
|
+
p[:blk].call(p)
|
66
|
+
end
|
61
67
|
end
|
62
68
|
end
|
63
69
|
end
|
data/lib/zgomot/midi/note.rb
CHANGED
@@ -26,9 +26,8 @@ module Zgomot::Midi
|
|
26
26
|
|
27
27
|
end
|
28
28
|
|
29
|
-
attr_reader :pitch_class, :octave, :midi, :time_scale
|
30
|
-
attr_accessor :time, :offset, :global_offset, :channel
|
31
|
-
|
29
|
+
attr_reader :pitch_class, :octave, :midi, :time_scale, :velocity, :length
|
30
|
+
attr_accessor :time, :offset, :global_offset, :channel
|
32
31
|
def initialize(args)
|
33
32
|
@pitch_class, @octave = case args[:pitch]
|
34
33
|
when Array then args[:pitch]
|
@@ -43,35 +42,33 @@ module Zgomot::Midi
|
|
43
42
|
raise(Zgomot::Error, "#{args[:pitch].inspect} is invalid") if midi.nil?
|
44
43
|
raise(Zgomot::Error, "#{velocity} is invalid velocity") unless velocity < 1.0
|
45
44
|
end
|
46
|
-
|
47
45
|
def to_s
|
48
46
|
"[#{pitch_class.to_s},#{octave}].#{length}.#{midi}.#{velocity}"
|
49
47
|
end
|
50
|
-
|
51
48
|
def bpm!(bpm)
|
52
49
|
@time_scale = 1.0/bpm.to_f; self
|
53
50
|
end
|
54
|
-
|
55
51
|
def octave!(oct)
|
56
52
|
@octave = oct; self
|
57
53
|
end
|
58
|
-
|
54
|
+
def length!(v)
|
55
|
+
@length = v; self
|
56
|
+
end
|
57
|
+
def valocity!(v)
|
58
|
+
@length = v; self
|
59
|
+
end
|
59
60
|
def note_on
|
60
61
|
time + offset
|
61
62
|
end
|
62
|
-
|
63
63
|
def length_to_sec
|
64
64
|
time_scale*Clock.whole_note_sec/length
|
65
65
|
end
|
66
|
-
|
67
66
|
def note_off
|
68
67
|
note_on + length_to_sec
|
69
68
|
end
|
70
|
-
|
71
69
|
def to_midi
|
72
70
|
self
|
73
71
|
end
|
74
|
-
|
75
72
|
def pitch_to_midi(pitch_class, octave)
|
76
73
|
if PITCH_CLASS[pitch_class]
|
77
74
|
(midi = 12*(octave+1)+PITCH_CLASS[pitch_class]) <= 127 ? midi : nil
|
data/lib/zgomot/midi/stream.rb
CHANGED
@@ -43,6 +43,12 @@ module Zgomot::Midi
|
|
43
43
|
stream.status_eql?(:playing) ? pause(name) : play(name)
|
44
44
|
end
|
45
45
|
end
|
46
|
+
def delete(name)
|
47
|
+
apply_to_stream(name) do |stream|
|
48
|
+
stream.update_status(:paused)
|
49
|
+
streams.delete(name.to_s).name
|
50
|
+
end
|
51
|
+
end
|
46
52
|
def apply_to_stream(name)
|
47
53
|
stream = streams.values.find{|s| s.name == name.to_s}
|
48
54
|
if stream
|
@@ -96,7 +102,11 @@ module Zgomot::Midi
|
|
96
102
|
end
|
97
103
|
Zgomot.logger.info "STREAM:#{count}:#{name}"
|
98
104
|
patterns << Zgomot::Comp::Pattern.new(ch.pattern)
|
99
|
-
|
105
|
+
if ch.length_to_sec > 0.0
|
106
|
+
sleep(ch.length_to_sec)
|
107
|
+
else
|
108
|
+
break
|
109
|
+
end
|
100
110
|
end
|
101
111
|
Zgomot.logger.info "STREAM FINISHED:#{name}"
|
102
112
|
update_status(:paused)
|
data/lib/zgomot/ui/windows.rb
CHANGED
@@ -2,7 +2,7 @@ module Zgomot::UI
|
|
2
2
|
WIDTH = 80
|
3
3
|
GLOBALS_HEIGHT = 6
|
4
4
|
STREAMS_HEIGHT = 20
|
5
|
-
|
5
|
+
ERROR_WINDOW_HEIGHT = 4
|
6
6
|
COLOR_GREY = 100
|
7
7
|
COLOR_GOLD = 101
|
8
8
|
COLOR_GREEN = 202
|
@@ -15,6 +15,7 @@ module Zgomot::UI
|
|
15
15
|
COLOR_YELLOW_GREEN = 211
|
16
16
|
COLOR_LIGHT_BLUE = 212
|
17
17
|
COLOR_ORANGE = 213
|
18
|
+
COLOR_RED = 214
|
18
19
|
|
19
20
|
COLOR_STREAM_PLAYING_SELECTED = 205
|
20
21
|
COLOR_STREAM_PAUSED_SELECTED = 206
|
@@ -25,6 +26,7 @@ module Zgomot::UI
|
|
25
26
|
COLOR_BORDER = 201
|
26
27
|
COLOR_CC_IDLE = 200
|
27
28
|
COLOR_CC_ACTIVE = 199
|
29
|
+
COLOR_ERROR = 198
|
28
30
|
|
29
31
|
module Utils
|
30
32
|
def set_color(color, &blk)
|
@@ -37,7 +39,7 @@ module Zgomot::UI
|
|
37
39
|
end
|
38
40
|
class MainWindow
|
39
41
|
class << self
|
40
|
-
attr_reader :globals_window, :cc_window, :str_window, :main_window
|
42
|
+
attr_reader :globals_window, :cc_window, :str_window, :main_window, :error_window
|
41
43
|
def init_curses
|
42
44
|
Curses.init_screen
|
43
45
|
Curses.noecho
|
@@ -54,6 +56,7 @@ module Zgomot::UI
|
|
54
56
|
Curses.init_color(COLOR_YELLOW_GREEN, 750, 980, 410)
|
55
57
|
Curses.init_color(COLOR_LIGHT_BLUE,600, 1000, 1000)
|
56
58
|
Curses.init_color(COLOR_ORANGE,1000 , 600, 0)
|
59
|
+
Curses.init_color(COLOR_RED, 750, 0, 0)
|
57
60
|
Curses.init_pair(COLOR_GOLD,COLOR_GOLD,COLOR_BLACK)
|
58
61
|
Curses.init_pair(COLOR_GREEN,COLOR_GREEN,COLOR_BLACK)
|
59
62
|
Curses.init_pair(COLOR_PINK,COLOR_PINK,COLOR_BLACK)
|
@@ -67,11 +70,13 @@ module Zgomot::UI
|
|
67
70
|
Curses.init_pair(COLOR_CC_SWITCH_FALSE,COLOR_LIGHT_BLUE,COLOR_BLACK)
|
68
71
|
Curses.init_pair(COLOR_CC_IDLE,COLOR_VIOLET,COLOR_BLACK)
|
69
72
|
Curses.init_pair(COLOR_CC_ACTIVE,COLOR_BLACK,COLOR_VIOLET)
|
73
|
+
Curses.init_pair(COLOR_ERROR,COLOR_RED,COLOR_BLACK)
|
70
74
|
end
|
71
75
|
def update
|
72
76
|
globals_window.display
|
73
77
|
cc_window.display
|
74
78
|
str_window.display
|
79
|
+
error_window.display
|
75
80
|
Curses.refresh
|
76
81
|
end
|
77
82
|
def poll
|
@@ -85,34 +90,45 @@ module Zgomot::UI
|
|
85
90
|
def dash
|
86
91
|
init_curses
|
87
92
|
@globals_window = GlobalsWindow.new(0)
|
88
|
-
|
89
|
-
@
|
93
|
+
str_height = 2*(Curses.lines - GLOBALS_HEIGHT - ERROR_WINDOW_HEIGHT)/3
|
94
|
+
@cc_window = CCWindow.new(Curses.lines - str_height - GLOBALS_HEIGHT - ERROR_WINDOW_HEIGHT, str_height + GLOBALS_HEIGHT)
|
95
|
+
@str_window = StrWindow.new(str_height, GLOBALS_HEIGHT)
|
96
|
+
@error_window = ErrorWindow.new(Curses.lines - ERROR_WINDOW_HEIGHT)
|
90
97
|
Curses.refresh
|
91
98
|
poll
|
92
99
|
loop do
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
100
|
+
begin
|
101
|
+
case Curses.getch
|
102
|
+
when ?t
|
103
|
+
str_window.tog
|
104
|
+
str_window.set_select_mode
|
105
|
+
update
|
106
|
+
when ?d
|
107
|
+
str_window.delete
|
108
|
+
str_window.set_select_mode
|
109
|
+
update
|
110
|
+
when Curses::Key::UP
|
111
|
+
str_window.dec_selected
|
112
|
+
update
|
113
|
+
when Curses::Key::DOWN
|
114
|
+
str_window.inc_selected
|
115
|
+
update
|
116
|
+
when 10
|
117
|
+
str_window.select
|
118
|
+
update
|
119
|
+
when ?p
|
120
|
+
Zgomot::Midi::Stream.play
|
121
|
+
update
|
122
|
+
when ?s
|
123
|
+
Zgomot::Midi::Stream.stop
|
124
|
+
update
|
125
|
+
when ?q
|
126
|
+
@thread.kill
|
127
|
+
Curses.close_screen
|
128
|
+
break
|
129
|
+
end
|
130
|
+
rescue Exception => e
|
131
|
+
Zgomot.set_last_error(e.message)
|
116
132
|
end
|
117
133
|
end
|
118
134
|
end
|
@@ -122,7 +138,7 @@ module Zgomot::UI
|
|
122
138
|
class GlobalsWindow
|
123
139
|
ITEM_WIDTH = 32
|
124
140
|
TIME_WIDTH = 15
|
125
|
-
attr_reader :time_window, :
|
141
|
+
attr_reader :time_window, :input_window, :output_window, :time_signature_window,
|
126
142
|
:beats_per_minute_window, :seconds_per_beat_window, :resolution_window
|
127
143
|
def initialize(top)
|
128
144
|
output = Zgomot::Drivers::Mgr.output
|
@@ -131,7 +147,7 @@ module Zgomot::UI
|
|
131
147
|
time_signature = Zgomot::Midi::Clock.time_signature
|
132
148
|
resolution = "1/#{Zgomot::Midi::Clock.resolution.to_i}"
|
133
149
|
seconds_per_beat = Zgomot::Midi::Clock.beat_sec.to_s
|
134
|
-
|
150
|
+
TitleWindow.new('zgomot', COLOR_BORDER, 0, COLOR_PINK)
|
135
151
|
@input_window = TextWithValueWindow.new('Input', input, COLOR_BORDER, 3, 0, COLOR_IDLE)
|
136
152
|
@output_window = TextWithValueWindow.new('Output', output, COLOR_BORDER, 4, 0, COLOR_IDLE)
|
137
153
|
@time_signature_window = TextWithValueWindow.new('Time Signature', time_signature, COLOR_BORDER, 5, 0, COLOR_IDLE)
|
@@ -144,63 +160,86 @@ module Zgomot::UI
|
|
144
160
|
"%#{TIME_WIDTH}s" % /(\d*:\d*)/.match(Zgomot::Midi::Dispatcher.clk).captures.first
|
145
161
|
end
|
146
162
|
def display
|
147
|
-
title_window.display
|
148
163
|
time_window.display(time_to_s)
|
149
|
-
input_window.display
|
150
|
-
output_window.display
|
151
|
-
time_signature_window.display
|
152
|
-
beats_per_minute_window.display
|
153
|
-
seconds_per_beat_window.display
|
154
|
-
resolution_window.display
|
164
|
+
input_window.display(Zgomot::Drivers::Mgr.input || 'None')
|
165
|
+
output_window.display(Zgomot::Drivers::Mgr.output)
|
166
|
+
time_signature_window.display(Zgomot::Midi::Clock.time_signature)
|
167
|
+
beats_per_minute_window.display(Zgomot::Midi::Clock.beats_per_minute.to_i.to_s)
|
168
|
+
seconds_per_beat_window.display(Zgomot::Midi::Clock.beat_sec.to_s)
|
169
|
+
resolution_window.display("1/#{Zgomot::Midi::Clock.resolution.to_i}")
|
155
170
|
end
|
156
171
|
end
|
157
172
|
|
158
173
|
class StrWindow
|
159
|
-
attr_reader :selected, :
|
160
|
-
def initialize(top)
|
161
|
-
@
|
174
|
+
attr_reader :selected, :select_mode, :window, :rows, :widths, :top, :current, :height
|
175
|
+
def initialize(height, top)
|
176
|
+
@select_mode, @selected, @top, @current, @height = false, [], top, 0, height
|
162
177
|
@widths = Zgomot::UI::Output::STREAM_OUTPUT_FORMAT_WIDTHS
|
163
178
|
TitleWindow.new('Streams', COLOR_BORDER, top, COLOR_BLUE)
|
164
179
|
TableRowWindow.new(Zgomot::UI::Output::STREAM_HEADER, widths, COLOR_BORDER, top + 3, COLOR_BORDER)
|
165
180
|
add_streams(top + 3)
|
166
181
|
end
|
167
182
|
def display
|
168
|
-
|
169
|
-
|
170
|
-
|
183
|
+
if streams.length == rows.length
|
184
|
+
(0..streams.length-1).each do |i|
|
185
|
+
stream = streams[i]
|
186
|
+
rows[i].display(stream.info, stream_color(stream, i))
|
187
|
+
end
|
188
|
+
else
|
189
|
+
add_streams(top + 3)
|
171
190
|
end
|
172
191
|
end
|
173
192
|
def inc_selected
|
174
|
-
if
|
175
|
-
@
|
193
|
+
if select_mode
|
194
|
+
@current = (current + 1) % streams.length
|
176
195
|
end
|
177
196
|
end
|
178
197
|
def dec_selected
|
179
|
-
if
|
180
|
-
@
|
198
|
+
if select_mode
|
199
|
+
@current = (current - 1) % streams.length
|
181
200
|
end
|
182
201
|
end
|
183
|
-
def
|
184
|
-
|
185
|
-
|
202
|
+
def select
|
203
|
+
if selected.include?(current)
|
204
|
+
@selected.delete(current)
|
205
|
+
else
|
206
|
+
@selected << current
|
207
|
+
end
|
208
|
+
end
|
209
|
+
def set_select_mode
|
210
|
+
if select_mode
|
211
|
+
@select_mode = false
|
212
|
+
@selected = []
|
213
|
+
@current = 0
|
214
|
+
else
|
215
|
+
@select_mode = true
|
216
|
+
end
|
186
217
|
end
|
187
218
|
def tog
|
188
|
-
|
189
|
-
|
190
|
-
|
219
|
+
selected.each do |s|
|
220
|
+
stream = streams[s]
|
221
|
+
Zgomot::Midi::Stream.tog(stream.name)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
def delete
|
225
|
+
selected.each do |s|
|
226
|
+
stream = streams[s]
|
227
|
+
Zgomot::Midi::Stream.delete(stream.name)
|
228
|
+
end
|
191
229
|
end
|
192
230
|
private
|
193
231
|
def add_streams(top)
|
194
|
-
|
232
|
+
displayed_streams = streams.length > (height - 4) ? (height - 4) : streams.length
|
233
|
+
@rows = (0..displayed_streams-1).map do |i|
|
195
234
|
stream = streams[i]
|
196
235
|
TableRowWindow.new(stream.info, widths, COLOR_BORDER, top += 1, stream_color(stream, i))
|
197
236
|
end
|
198
|
-
(
|
237
|
+
(height - displayed_streams - 4).times do
|
199
238
|
TableRowWindow.new(nil, widths, COLOR_BORDER, top += 1)
|
200
239
|
end
|
201
240
|
end
|
202
241
|
def stream_color(stream, i)
|
203
|
-
if
|
242
|
+
if select_mode && (selected.include?(i) || current == i)
|
204
243
|
stream.status_eql?(:playing) ? COLOR_STREAM_PLAYING_SELECTED : COLOR_STREAM_PAUSED_SELECTED
|
205
244
|
else
|
206
245
|
stream.status_eql?(:playing) ? COLOR_ACTIVE : COLOR_IDLE
|
@@ -212,9 +251,9 @@ module Zgomot::UI
|
|
212
251
|
end
|
213
252
|
|
214
253
|
class CCWindow
|
215
|
-
attr_reader :height, :widths, :rows
|
254
|
+
attr_reader :height, :widths, :rows, :top
|
216
255
|
def initialize(height, top)
|
217
|
-
@height = height
|
256
|
+
@height, @top = height, top
|
218
257
|
@widths = Zgomot::UI::Output::CC_OUTPUT_FORMAT_WIDTHS
|
219
258
|
TitleWindow.new('Input CCs', COLOR_BORDER, top, COLOR_BLUE)
|
220
259
|
TableRowWindow.new(Zgomot::UI::Output::CC_HEADER, widths, COLOR_BORDER, top + 3, COLOR_BORDER)
|
@@ -222,13 +261,17 @@ module Zgomot::UI
|
|
222
261
|
end
|
223
262
|
def display
|
224
263
|
ccs = get_ccs
|
225
|
-
|
264
|
+
if ccs.length == rows.length
|
265
|
+
(0..ccs.length-1).each{|i| rows[i].display(ccs[i], cc_color(ccs[i]))}
|
266
|
+
else
|
267
|
+
add_ccs(top + 3)
|
268
|
+
end
|
226
269
|
end
|
227
270
|
private
|
228
271
|
def add_ccs(top)
|
229
272
|
ccs = get_ccs
|
230
|
-
|
231
|
-
|
273
|
+
displayed_ccs = ccs.length > (height - 4) ? (height - 4) : ccs.length
|
274
|
+
@rows = ccs[0..(displayed_ccs-1)].map do |cc_params|
|
232
275
|
TableRowWindow.new(cc_params, widths, COLOR_BORDER, top += 1, cc_color(cc_params))
|
233
276
|
end
|
234
277
|
(height - ccs.length - 4).times do
|
@@ -246,17 +289,47 @@ module Zgomot::UI
|
|
246
289
|
cc_mgr = Zgomot::Midi::CC
|
247
290
|
cc_mgr.cc_names.reduce([]){|c, cc_name| c + cc_mgr.info(cc_name)}
|
248
291
|
end
|
249
|
-
def cc_color(
|
250
|
-
|
251
|
-
|
252
|
-
delta = Time.now - cc_updated_at(cc)
|
292
|
+
def cc_color(cc_params)
|
293
|
+
if cc_type(cc_params).eql?('cont')
|
294
|
+
delta = Time.now - cc_updated_at(cc_params)
|
253
295
|
delta > Zgomot::Midi::Clock.beat_sec ? COLOR_CC_IDLE : COLOR_CC_ACTIVE
|
254
296
|
else
|
255
|
-
cc_value(
|
297
|
+
cc_value(cc_params).eql?('true') ? COLOR_CC_SWITCH_TRUE : COLOR_CC_SWITCH_FALSE
|
256
298
|
end
|
257
299
|
end
|
258
300
|
end
|
259
301
|
|
302
|
+
class ErrorWindow
|
303
|
+
include Utils
|
304
|
+
attr_reader :top
|
305
|
+
def initialize(top)
|
306
|
+
@top = top
|
307
|
+
display
|
308
|
+
end
|
309
|
+
def display
|
310
|
+
error_message = Zgomot.last_error
|
311
|
+
text, text_color = if error_message.nil?
|
312
|
+
[["it's cool"], COLOR_BORDER]
|
313
|
+
else
|
314
|
+
[error_message.scan(/.{1,#{WIDTH-4}}/), COLOR_ERROR]
|
315
|
+
end
|
316
|
+
set_color(COLOR_BORDER) {
|
317
|
+
write(top, 0, '-' * WIDTH)
|
318
|
+
write(top + 1, 0, '|')
|
319
|
+
write(top + 1, WIDTH-1, '|')
|
320
|
+
write(top + 2, 0, '|')
|
321
|
+
write(top + 2, WIDTH-1, '|')
|
322
|
+
write(top + 3, 0, '-' * WIDTH)
|
323
|
+
}
|
324
|
+
set_color(text_color) {
|
325
|
+
lines = text.length > 1 ? 2 : 1
|
326
|
+
lines.times{|i| write(top + 1 + i, 2, "%-#{WIDTH-4}s" % text[i])}
|
327
|
+
blank_width = WIDTH - 4
|
328
|
+
(2 - lines).times{|i| write(top + 2 - i, 2, ' ' * blank_width)}
|
329
|
+
}
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
260
333
|
class TextWindow
|
261
334
|
include Utils
|
262
335
|
attr_reader :text, :top, :color, :left
|
@@ -336,7 +409,7 @@ module Zgomot::UI
|
|
336
409
|
|
337
410
|
class TitleWindow
|
338
411
|
include Utils
|
339
|
-
attr_reader :
|
412
|
+
attr_reader :text, :top, :color, :text_color
|
340
413
|
def initialize(text, color, top, text_color = nil)
|
341
414
|
@text_color = text_color || color
|
342
415
|
@text, @color, @top = text, color, top
|