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.
@@ -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=(v)
61
- notes.each{|n| n.velocity = v}
58
+ def velocity!(v)
59
+ notes.each{|n| n.velocity!(v)}; self
62
60
  end
63
- def length=(v)
64
- notes.each{|n| n.length = v}
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 << get_property(:model, destination_ptr)
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 << get_property(:model, source_ptr)
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
- val = Interface::CFString.CFStringCreateWithCString(nil, name.to_s, 0)
142
- Interface::MIDIObjectGetStringProperty(from, prop, val)
143
- Interface::CFString.CFStringGetCStringPtr(val.read_pointer, 0).read_string
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)
@@ -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
 
@@ -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[:updated_at] = ::Time.now
55
- min = p[:min]
56
- max = p[:max]
57
- if p[:type] == :cont
58
- p[:value] = min + (max - min)*value.to_f/127.0
59
- else
60
- p[:value] = value == 127 ? true : false
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
@@ -16,6 +16,14 @@ module Zgomot::Midi
16
16
  clock.to_s
17
17
  end
18
18
 
19
+ def flush
20
+ @queue.clear
21
+ end
22
+
23
+ def done?
24
+ qmutex.synchronize{queue.empty? and playing.empty?}
25
+ end
26
+
19
27
  def enqueue(ch)
20
28
  ch.offset = clock.ceil
21
29
  qmutex.synchronize do
@@ -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, :velocity, :length
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
@@ -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
- sleep(ch.length_to_sec)
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)
@@ -2,7 +2,7 @@ module Zgomot::UI
2
2
  WIDTH = 80
3
3
  GLOBALS_HEIGHT = 6
4
4
  STREAMS_HEIGHT = 20
5
- CCS_TOP = GLOBALS_HEIGHT + STREAMS_HEIGHT
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
- @cc_window = CCWindow.new(Curses.lines - CCS_TOP, CCS_TOP)
89
- @str_window = StrWindow.new(GLOBALS_HEIGHT)
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
- case Curses.getch
94
- when ?t
95
- str_window.set_tog_mode
96
- update
97
- when Curses::Key::UP
98
- str_window.dec_selected
99
- update
100
- when Curses::Key::DOWN
101
- str_window.inc_selected
102
- update
103
- when 10
104
- str_window.tog
105
- update
106
- when ?p
107
- Zgomot::Midi::Stream.play
108
- update
109
- when ?s
110
- Zgomot::Midi::Stream.stop
111
- update
112
- when ?q
113
- @thread.kill
114
- Curses.close_screen
115
- break
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, :title_window, :input_window, :output_window, :time_signature_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
- @title_window = TitleWindow.new('zgomot', COLOR_BORDER, 0, COLOR_PINK)
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, :tog_mode, :window, :rows, :widths
160
- def initialize(top)
161
- @tog_mode, @selected = false, 0
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
- (0..streams.length-1).each do |i|
169
- stream = streams[i]
170
- rows[i].display(stream.info, stream_color(stream, i))
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 tog_mode
175
- @selected = (selected + 1) % streams.length
193
+ if select_mode
194
+ @current = (current + 1) % streams.length
176
195
  end
177
196
  end
178
197
  def dec_selected
179
- if tog_mode
180
- @selected = (selected - 1) % streams.length
198
+ if select_mode
199
+ @current = (current - 1) % streams.length
181
200
  end
182
201
  end
183
- def set_tog_mode
184
- @selected = 0
185
- @tog_mode = tog_mode ? false : true
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
- stream = streams[selected]
189
- Zgomot::Midi::Stream.tog(stream.name)
190
- set_tog_mode
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
- @rows = (0..streams.length-1).map do |i|
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
- (STREAMS_HEIGHT - streams.length - 4).times do
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 tog_mode && i == selected
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
- (0..ccs.length-1).each{|i| rows[i].display(ccs[i], cc_color(ccs[i]))}
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
- @rows = ccs.map do |cc_params|
231
- puts cc_color(cc_params)
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(cc)
250
- COLOR_IDLE
251
- if cc_type(cc).eql?('cont')
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(cc).eql?('true') ? COLOR_CC_SWITCH_TRUE : COLOR_CC_SWITCH_FALSE
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 :windows, :text, :top, :color, :text_color
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