ardour
midi_streamview.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2001-2007 Paul Davis
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18 
19 #include <cmath>
20 #include <utility>
21 
22 #include <gtkmm.h>
23 
24 #include <gtkmm2ext/gtk_ui.h>
25 
26 #include "canvas/line_set.h"
27 #include "canvas/rectangle.h"
28 
29 #include "ardour/midi_region.h"
30 #include "ardour/midi_source.h"
31 #include "ardour/midi_track.h"
32 #include "ardour/operations.h"
33 #include "ardour/region_factory.h"
34 #include "ardour/session.h"
35 #include "ardour/smf_source.h"
36 
37 #include "ardour_ui.h"
38 #include "global_signals.h"
39 #include "gui_thread.h"
40 #include "midi_region_view.h"
41 #include "midi_streamview.h"
42 #include "midi_time_axis.h"
43 #include "midi_util.h"
44 #include "public_editor.h"
45 #include "region_selection.h"
46 #include "region_view.h"
47 #include "rgb_macros.h"
48 #include "selection.h"
49 #include "utils.h"
50 
51 #include "i18n.h"
52 
53 using namespace std;
54 using namespace ARDOUR;
55 using namespace ARDOUR_UI_UTILS;
56 using namespace PBD;
57 using namespace Editing;
58 
60  : StreamView (tv)
61  , note_range_adjustment(0.0f, 0.0f, 0.0f)
62  , _range_dirty(false)
63  , _range_sum_cache(-1.0)
64  , _lowest_note(60)
65  , _highest_note(71)
66  , _data_note_min(60)
67  , _data_note_max(71)
68  , _note_lines (0)
69  , _updates_suspended (false)
70 {
71  /* use a group dedicated to MIDI underlays. Audio underlays are not in this group. */
72  midi_underlay_group = new ArdourCanvas::Container (_canvas_group);
73  midi_underlay_group->lower_to_bottom();
74 
75  /* put the note lines in the timeaxisview's group, so it
76  can be put below ghost regions from MIDI underlays
77  */
78  _note_lines = new ArdourCanvas::LineSet (_canvas_group, ArdourCanvas::LineSet::Horizontal);
79 
80  _note_lines->Event.connect(
81  sigc::bind(sigc::mem_fun(_trackview.editor(),
84 
85  _note_lines->lower_to_bottom();
86 
87  color_handler ();
88 
89  ColorsChanged.connect(sigc::mem_fun(*this, &MidiStreamView::color_handler));
90 
93 
94  note_range_adjustment.signal_value_changed().connect(
95  sigc::mem_fun(*this, &MidiStreamView::note_range_adjustment_changed));
96 }
97 
99 {
100 }
101 
102 RegionView*
104 {
106 
107  if (region == 0) {
108  return 0;
109  }
110 
111  RegionView* region_view = NULL;
112  if (recording) {
113  region_view = new MidiRegionView (
114  _canvas_group, _trackview, region,
115  _samples_per_pixel, region_color, recording,
117  } else {
118  region_view = new MidiRegionView (_canvas_group, _trackview, region,
120  }
121 
122  region_view->init (false);
123 
124  return region_view;
125 }
126 
127 RegionView*
129 {
131 
132  if (!region) {
133  return 0;
134  }
135 
136  for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
137  if ((*i)->region() == r) {
138 
139  /* great. we already have a MidiRegionView for this Region. use it again. */
140 
141  (*i)->set_valid (true);
142 
143  display_region(dynamic_cast<MidiRegionView*>(*i), wait_for_data);
144 
145  return 0;
146  }
147  }
148 
149  MidiRegionView* region_view = dynamic_cast<MidiRegionView*> (create_region_view (r, wait_for_data, recording));
150  if (region_view == 0) {
151  return 0;
152  }
153 
154  region_views.push_front (region_view);
155 
156  /* display events and find note range */
157  display_region (region_view, wait_for_data);
158 
159  /* fit note range if we are importing */
162  }
163 
164  /* catch regionview going away */
165  boost::weak_ptr<Region> wr (region); // make this explicit
166  region->DropReferences.connect (*this, invalidator (*this), boost::bind (&MidiStreamView::remove_region_view, this, wr), gui_context());
167 
168  RegionViewAdded (region_view);
169 
170  return region_view;
171 }
172 
173 void
174 MidiStreamView::display_region(MidiRegionView* region_view, bool load_model)
175 {
176  if (!region_view) {
177  return;
178  }
179 
180  region_view->enable_display (true);
181  region_view->set_height (child_height());
182 
183  boost::shared_ptr<MidiSource> source(region_view->midi_region()->midi_source(0));
184  if (!source) {
185  error << _("attempt to display MIDI region with no source") << endmsg;
186  return;
187  }
188 
189  if (load_model) {
190  Glib::Threads::Mutex::Lock lm(source->mutex());
191  source->load_model(lm);
192  }
193 
194  if (!source->model()) {
195  error << _("attempt to display MIDI region with no model") << endmsg;
196  return;
197  }
198 
200  source->model()->lowest_note(),
201  source->model()->highest_note());
202 
203  // Display region contents
204  region_view->display_model(source->model());
205 }
206 
207 
208 void
210 {
212 
213  draw_note_lines();
214 
216 }
217 
218 void
220 {
222  if (mr) {
223  Glib::Threads::Mutex::Lock lm(mr->midi_source(0)->mutex());
224  mr->midi_source(0)->load_model(lm);
226  mr->model()->lowest_note(),
227  mr->model()->highest_note());
228  }
229 }
230 
231 bool
232 MidiStreamView::update_data_note_range(uint8_t min, uint8_t max)
233 {
234  bool dirty = false;
235  if (min < _data_note_min) {
236  _data_note_min = min;
237  dirty = true;
238  }
239  if (max > _data_note_max) {
240  _data_note_max = max;
241  dirty = true;
242  }
243  return dirty;
244 }
245 
246 void
248 {
249  if (!_trackview.is_midi_track()) {
250  return;
251  }
252 
253  list<RegionView*>::iterator i;
254 
255  // Load models if necessary, and find note range of all our contents
256  _range_dirty = false;
257  _data_note_min = 127;
258  _data_note_max = 0;
260  sigc::mem_fun (*this, &StreamView::update_contents_metrics));
261 
262  // No notes, use default range
263  if (!_range_dirty) {
264  _data_note_min = 60;
265  _data_note_max = 71;
266  }
267 
268  // Flag region views as invalid and disable drawing
269  for (i = region_views.begin(); i != region_views.end(); ++i) {
270  (*i)->set_valid(false);
271  (*i)->enable_display(false);
272  }
273 
274  // Add and display region views, and flag them as valid
276  sigc::hide_return (sigc::mem_fun (*this, &StreamView::add_region_view)));
277 
278  // Stack regions by layer, and remove invalid regions
279  layer_regions();
280 
281  // Update note range (not regions which are correct) and draw note lines
283 }
284 
285 
286 void
288 {
290 
291  _note_lines->set_extent (ArdourCanvas::COORD_MAX);
292 
294 }
295 
296 void
298 {
300  return;
301  }
302 
303  double y;
304  double prev_y = .5;
305  uint32_t color;
306 
307  _note_lines->clear();
308 
309  if (child_height() < 140 || note_height() < 3) {
310  /* track is too small for note lines, or there are too many */
311  return;
312  }
313 
314  /* do this is order of highest ... lowest since that matches the
315  * coordinate system in which y=0 is at the top
316  */
317 
318  for (int i = highest_note() + 1; i >= lowest_note(); --i) {
319 
320  y = floor(note_to_y (i)) + .5;
321 
322  /* this is the line actually corresponding to the division
323  * between notes
324  */
325 
326  if (i <= highest_note()) {
327  _note_lines->add (y, 1.0, ARDOUR_UI::config()->color ("piano roll black outline"));
328  }
329 
330  /* now add a thicker line/bar which covers the entire vertical
331  * height of this note.
332  */
333 
334  switch (i % 12) {
335  case 1:
336  case 3:
337  case 6:
338  case 8:
339  case 10:
340  color = ARDOUR_UI::config()->color_mod ("piano roll black", "piano roll black");
341  break;
342  default:
343  color = ARDOUR_UI::config()->color_mod ("piano roll white", "piano roll white");
344  break;
345  }
346 
347  double h = y - prev_y;
348  double mid = y + (h/2.0);
349 
350  if (height > 1.0) { // XXX ? should that not be h >= 1 ?
351  _note_lines->add (mid, h, color);
352  }
353 
354  prev_y = y;
355  }
356 }
357 
358 void
360 {
361  if (r == FullRange) {
362  _lowest_note = 0;
363  _highest_note = 127;
364  } else {
367  }
368 
370 }
371 
372 void
373 MidiStreamView::apply_note_range(uint8_t lowest, uint8_t highest, bool to_region_views)
374 {
375  _highest_note = highest;
376  _lowest_note = lowest;
377 
378  int const max_note_height = 20; // This should probably be based on text size...
379  int const range = _highest_note - _lowest_note;
380  int const pixels_per_note = floor (child_height () / range);
381 
382  /* do not grow note height beyond 10 pixels */
383  if (pixels_per_note > max_note_height) {
384 
385  int const available_note_range = floor (child_height() / max_note_height);
386  int additional_notes = available_note_range - range;
387 
388  /* distribute additional notes to higher and lower ranges, clamp at 0 and 127 */
389  for (int i = 0; i < additional_notes; i++){
390 
391  if (i % 2 && _highest_note < 127){
392  _highest_note++;
393  }
394  else if (i % 2) {
395  _lowest_note--;
396  }
397  else if (_lowest_note > 0){
398  _lowest_note--;
399  }
400  else {
401  _highest_note++;
402  }
403  }
404  }
405 
406  note_range_adjustment.set_page_size(_highest_note - _lowest_note);
407  note_range_adjustment.set_value(_lowest_note);
408 
409  draw_note_lines();
410 
411  if (to_region_views) {
413  }
414 
416 }
417 
418 void
420 {
421  if (!_updates_suspended) {
422  for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
424  }
425  }
426 }
427 
428 void
430 {
431  _data_note_min = min(_data_note_min, note_num);
432  _data_note_max = max(_data_note_max, note_num);
433 }
434 
435 void
437 {
438  // cerr << _trackview.name() << " streamview SRB\n";
439 
441 
442  if (!rec_active &&
443  _trackview.session()->record_status() == Session::Recording &&
445 
446  if (ARDOUR_UI::config()->get_show_waveforms_while_recording() && rec_regions.size() == rec_rects.size()) {
447 
448  /* add a new region, but don't bother if they set show-waveforms-while-recording mid-record */
449 
450  MidiRegion::SourceList sources;
451 
453 
454  sources.push_back (_trackview.midi_track()->write_source());
455 
456  // handle multi
457 
458  framepos_t start = 0;
459  if (rec_regions.size() > 0) {
460  start = rec_regions.back().first->start()
462  }
463 
464  if (!rec_regions.empty()) {
465  MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rec_regions.back().second);
466  mrv->end_write ();
467  }
468 
469  PropertyList plist;
470 
471  plist.add (ARDOUR::Properties::start, start);
472  plist.add (ARDOUR::Properties::length, 1);
473  /* Just above we're setting this nascent region's length to 1. I think this
474  is so that the RegionView gets created with a non-zero width, as apparently
475  creating a RegionView with a zero width causes it never to be displayed
476  (there is a warning in TimeAxisViewItem::init about this). However, we
477  must also set length_beats to something non-zero, otherwise the frame length
478  of 1 causes length_beats to be set to some small quantity << 1. Then
479  when the position is set up below, this length_beats is used to recompute
480  length using BeatsFramesConverter::to, which is slightly innacurate for small
481  beats values because it converts floating point beats to bars, beats and
482  integer ticks. The upshot of which being that length gets set back to 0,
483  meaning no region view is ever seen, meaning no MIDI notes during record (#3820).
484  */
486  plist.add (ARDOUR::Properties::name, string());
487  plist.add (ARDOUR::Properties::layer, 0);
488 
489  boost::shared_ptr<MidiRegion> region (boost::dynamic_pointer_cast<MidiRegion>
490  (RegionFactory::create (sources, plist, false)));
491  if (region) {
495 
496  RegionView* rv = add_region_view_internal (region, false, true);
497  MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rv);
498  mrv->begin_write ();
499 
500  /* rec region will be destroyed in setup_rec_box */
501  rec_regions.push_back (make_pair (region, rv));
502 
503  /* we add the region later */
504  setup_new_rec_layer_time (region);
505  } else {
506  error << _("failed to create MIDI region") << endmsg;
507  }
508  }
509 
510  /* start a new rec box */
511 
513 
514  } else if (rec_active &&
515  (_trackview.session()->record_status() != Session::Recording ||
517  screen_update_connection.disconnect();
518  rec_active = false;
519  rec_updating = false;
520  }
521 
522  } else {
523 
524  // cerr << "\tNOT rolling, rec_rects = " << rec_rects.size() << " rec_regions = " << rec_regions.size() << endl;
525 
526  if (!rec_rects.empty() || !rec_regions.empty()) {
527 
528  /* disconnect rapid update */
529  screen_update_connection.disconnect();
531  rec_updating = false;
532  rec_active = false;
533 
534  /* remove temp regions */
535 
536  for (list<pair<boost::shared_ptr<Region>,RegionView*> >::iterator iter = rec_regions.begin(); iter != rec_regions.end();) {
537  list<pair<boost::shared_ptr<Region>,RegionView*> >::iterator tmp;
538 
539  tmp = iter;
540  ++tmp;
541 
542  (*iter).first->drop_references ();
543 
544  iter = tmp;
545  }
546 
547  rec_regions.clear();
548 
549  // cerr << "\tclear " << rec_rects.size() << " rec rects\n";
550 
551  /* transport stopped, clear boxes */
552  for (vector<RecBoxInfo>::iterator iter=rec_rects.begin(); iter != rec_rects.end(); ++iter) {
553  RecBoxInfo &rect = (*iter);
554  delete rect.rectangle;
555  }
556 
557  rec_rects.clear();
558 
559  }
560  }
561 }
562 
563 void
565 {
566  draw_note_lines ();
567 
568  if (_trackview.is_midi_track()) {
569  canvas_rect->set_fill_color (ARDOUR_UI::config()->color_mod ("midi track base", "midi track base"));
570  } else {
571  canvas_rect->set_fill_color (ARDOUR_UI::config()->color ("midi bus base"));
572  }
573 }
574 
575 void
577 {
578  double sum = note_range_adjustment.get_value() + note_range_adjustment.get_page_size();
579  int lowest = (int) floor(note_range_adjustment.get_value());
580  int highest;
581 
582  if (sum == _range_sum_cache) {
583  //cerr << "cached" << endl;
584  highest = (int) floor(sum);
585  } else {
586  //cerr << "recalc" << endl;
587  highest = lowest + (int) floor(note_range_adjustment.get_page_size());
588  _range_sum_cache = sum;
589  }
590 
591  if (lowest == _lowest_note && highest == _highest_note) {
592  return;
593  }
594 
595  //cerr << "note range adjustment changed: " << lowest << " " << highest << endl;
596  //cerr << " val=" << v_zoom_adjustment.get_value() << " page=" << v_zoom_adjustment.get_page_size() << " sum=" << v_zoom_adjustment.get_value() + v_zoom_adjustment.get_page_size() << endl;
597 
598  _lowest_note = lowest;
599  _highest_note = highest;
600  apply_note_range(lowest, highest, true);
601 }
602 
603 void
605 {
607 
608  if (rec_regions.empty()) {
609  return;
610  }
611 
612  /* Update the region being recorded to reflect where we currently are */
613  boost::shared_ptr<ARDOUR::Region> region = rec_regions.back().first;
615 
616  MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rec_regions.back().second);
617  mrv->extend_active_notes ();
618 }
619 
620 uint8_t
622 {
623  int const n = ((contents_height() - y - 1) / contents_height() * (double)contents_note_range())
624  + lowest_note();
625 
626  if (n < 0) {
627  return 0;
628  } else if (n > 127) {
629  return 127;
630  }
631 
632  return n;
633 }
634 
638 void
640 {
641  _updates_suspended = true;
642 }
643 
647 void
649 {
650  _updates_suspended = false;
651 
652  draw_note_lines ();
654 
655  _canvas_group->redraw ();
656 }
657 
660  return a->region()->position() < b->region()->position();
661  }
662 };
663 
664 bool
666 {
667  /* Paste into the first region which starts on or before pos. Only called when
668  using an internal editing tool. */
669 
670  if (region_views.empty()) {
671  return false;
672  }
673 
675 
676  list<RegionView*>::const_iterator prev = region_views.begin ();
677 
678  for (list<RegionView*>::const_iterator i = region_views.begin(); i != region_views.end(); ++i) {
679  if ((*i)->region()->position() > pos) {
680  break;
681  }
682  prev = i;
683  }
684 
685  boost::shared_ptr<Region> r = (*prev)->region ();
686 
687  /* If *prev doesn't cover pos, it's no good */
688  if (r->position() > pos || ((r->position() + r->length()) < pos)) {
689  return false;
690  }
691 
692  MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*prev);
693  return mrv ? mrv->paste(pos, selection, ctx) : false;
694 }
PBD::ScopedConnectionList rec_data_ready_connections
Definition: streamview.h:177
framepos_t get_capture_start_frame(uint32_t n=0) const
Definition: track.cc:783
bool transport_rolling() const
Definition: session.h:592
void display_track(boost::shared_ptr< ARDOUR::Track > tr)
std::list< std::pair< boost::shared_ptr< ARDOUR::Region >, RegionView * > > rec_regions
Definition: streamview.h:162
virtual void update_contents_height()
Definition: streamview.cc:629
boost::shared_ptr< ARDOUR::MidiTrack > midi_track() const
Definition: route_ui.cc:1762
bool rec_updating
Definition: streamview.h:163
void layer_regions()
Definition: streamview.cc:224
PBD::Signal0< void > DropReferences
Definition: destructible.h:34
PublicEditor & editor() const
void setup_new_rec_layer_time(boost::shared_ptr< ARDOUR::Region >)
Definition: streamview.cc:712
void note_range_adjustment_changed()
bool is_midi_track() const
Definition: route_ui.cc:1756
LIBARDOUR_API PBD::PropertyDescriptor< layer_t > layer
Definition: region.cc:67
sigc::signal< void, RegionView * > RegionViewAdded
Definition: streamview.h:122
virtual bool canvas_stream_view_event(GdkEvent *event, ArdourCanvas::Item *, RouteTimeAxisView *)=0
void update_contents_height()
double _samples_per_pixel
Definition: streamview.h:158
bool update_data_note_range(uint8_t min, uint8_t max)
uint8_t _data_note_max
in data
ArdourCanvas::Container * _canvas_group
Definition: streamview.h:152
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
LIBARDOUR_API PBD::PropertyDescriptor< std::string > name
Lists of selected things.
Definition: selection.h:66
void display_track(boost::shared_ptr< ARDOUR::Track >)
Definition: streamview.cc:216
framecnt_t get_captured_frames(uint32_t n=0) const
Definition: track.cc:723
framepos_t current_capture_end() const
Definition: track.cc:807
tuple f
Definition: signals.py:35
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
RegionView * add_region_view_internal(boost::shared_ptr< ARDOUR::Region >, bool wait_for_waves, bool recording=false)
bool operator()(RegionView *a, RegionView *b)
RecordState record_status() const
Definition: session.h:276
virtual void remove_region_view(boost::weak_ptr< ARDOUR::Region >)
Definition: streamview.cc:180
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > start
Definition: region.cc:63
std::vector< RecBoxInfo > rec_rects
Definition: streamview.h:161
LIBARDOUR_API GQuark insert_file
Definition: operations.cc:27
#define invalidator(x)
Definition: gui_thread.h:40
sigc::signal< void > NoteRangeChanged
uint8_t lowest_note() const
bool operation_in_progress(GQuark) const
Definition: session.cc:5532
#define _(Text)
Definition: i18n.h:11
uint8_t contents_note_range() const
uint8_t _data_note_min
in data
RegionView * create_region_view(boost::shared_ptr< ARDOUR::Region >, bool, bool)
void set_position(framepos_t)
Definition: region.cc:579
boost::shared_ptr< ARDOUR::Region > region() const
Definition: region_view.h:66
void update_contents_metrics(boost::shared_ptr< ARDOUR::Region > r)
uint8_t _lowest_note
currently visible
const boost::shared_ptr< ARDOUR::MidiRegion > midi_region() const
virtual void update_rec_box()
Definition: streamview.cc:445
virtual void init(bool wait_for_data)
Definition: region_view.cc:149
void display_model(boost::shared_ptr< ARDOUR::MidiModel > model)
framepos_t transport_frame() const
Definition: session.h:551
Definition: amp.h:29
ARDOUR::Session * session() const
Definition: axis_view.h:52
ArdourCanvas::Rectangle * rectangle
Definition: streamview.h:49
boost::shared_ptr< ARDOUR::Track > track() const
Definition: route_ui.cc:1738
#define gui_context()
Definition: gui_thread.h:36
boost::shared_ptr< Playlist > playlist()
Definition: track.cc:590
void update_note_range(uint8_t note_num)
bool paste(ARDOUR::framepos_t pos, const Selection &selection, PasteContext &ctx)
bool record_enabled() const
Definition: track.cc:215
int64_t framepos_t
Definition: types.h:66
void foreach_region(boost::function< void(boost::shared_ptr< Region >)>)
Definition: playlist.cc:2833
RouteTimeAxisView & _trackview
Definition: streamview.h:151
void create_rec_box(framepos_t frame_pos, double width)
Definition: streamview.cc:409
RegionViewList region_views
Definition: streamview.h:156
MidiStreamView(MidiTimeAxisView &)
framepos_t position() const
Definition: region.h:112
void apply_note_range(uint8_t lowest, uint8_t highest, bool to_region_views)
sigc::connection screen_update_connection
Definition: streamview.h:160
ArdourCanvas::Rectangle * canvas_rect
Definition: streamview.h:153
uint8_t _highest_note
currently visible
boost::shared_ptr< SMFSource > write_source(uint32_t n=0)
Definition: midi_track.cc:770
bool rec_active
Definition: streamview.h:164
double child_height() const
Definition: streamview.cc:613
double note_to_y(uint8_t note) const
void enable_display(bool)
double note_height() const
void set_length(framecnt_t)
Definition: region.cc:430
ArdourCanvas::Color color_mod(std::string const &color, std::string const &modifier) const
Definition: ui_config.cc:555
LIBARDOUR_API PBD::PropertyDescriptor< Evoral::Beats > length_beats
Definition: midi_region.cc:57
static UIConfiguration * config()
Definition: ardour_ui.h:188
virtual void update_contents_metrics(boost::shared_ptr< ARDOUR::Region >)
Definition: streamview.h:103
void add_region_view(boost::weak_ptr< ARDOUR::Region >)
Definition: streamview.cc:165
void set_note_range(VisibleNoteRange r)
uint8_t highest_note() const
sigc::signal< void > ColorsChanged
Definition: debug.h:30
uint8_t y_to_note(double y) const
bool paste(framepos_t pos, const ::Selection &selection, PasteContext &ctx)
void display_region(MidiRegionView *region_view, bool load_model)
void set_height(double)
framecnt_t length() const
Definition: region.h:114
boost::shared_ptr< MidiSource > midi_source(uint32_t n=0) const
Definition: midi_region.cc:390
ArdourCanvas::LineSet * _note_lines
double contents_height() const
void apply_note_range_to_regions()
uint32_t region_color
Contained region color.
Definition: streamview.h:166
Gtk::Adjustment note_range_adjustment
std::vector< boost::shared_ptr< Source > > SourceList
Definition: types.h:520
bool add(PropertyBase *prop)
boost::shared_ptr< MidiModel > model()
Definition: midi_region.cc:378
framepos_t current_capture_start() const
Definition: track.cc:801
ArdourCanvas::Container * midi_underlay_group
double height
Definition: streamview.h:175
void set_start(framepos_t)
Definition: region.cc:675
LIBARDOUR_API PBD::PropertyDescriptor< framecnt_t > length
Definition: region.cc:64
LIBARDOUR_API PBD::PropertyDescriptor< bool > color
Definition: route_group.cc:50