, _force_channel(-1)
, _last_channel_selection(0xFFFF)
, _default_note_length(0.0)
+ , _current_range_min(0)
+ , _current_range_max(0)
, _active_notes(0)
, _note_group(new ArdourCanvas::Group(*parent))
, _delta_command(NULL)
const boost::shared_ptr<Evoral::Note> new_note(new Evoral::Note(
0, new_note_time, new_note_length, (uint8_t)note, 0x40));
- view->update_bounds(new_note->note());
+ view->update_note_range(new_note->note());
MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
cmd->add(new_note);
}
}
- for (std::vector<CanvasNoteEvent*>::iterator i = _events.begin(); i != _events.end(); ++i)
+ for (Events::iterator i = _events.begin(); i != _events.end(); ++i)
delete *i;
_events.clear();
void
MidiRegionView::set_height (gdouble height)
{
+ static const double FUDGE = 2;
+ const double old_height = _height;
RegionView::set_height(height);
+ _height = height - FUDGE;
- // FIXME: ick
- height -= 2;
+ apply_note_range(midi_stream_view()->lowest_note(),
+ midi_stream_view()->highest_note(),
+ height != old_height + FUDGE);
- _height = height;
-
- if (_enable_display) {
+ if (name_text) {
+ name_text->raise_to_top();
+ }
+}
- _model->read_lock();
- for (std::vector<CanvasNoteEvent*>::const_iterator i = _events.begin(); i != _events.end(); ++i) {
+/** Apply the current note range from the stream view
+ * by repositioning/hiding notes as necessary
+ */
+void
+MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
+{
+ if (_enable_display) {
+ if (!force && _current_range_min == min && _current_range_max == max) {
+ return;
+ }
+
+ _current_range_min = min;
+ _current_range_max = max;
+
+ for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
CanvasNoteEvent* event = *i;
Item* item = dynamic_cast<Item*>(event);
assert(item);
if (event && event->note()) {
- if (event->note()->note() < midi_stream_view()->lowest_note() ||
- event->note()->note() > midi_stream_view()->highest_note()) {
-
+ if (event->note()->note() < _current_range_min || event->note()->note() > _current_range_max) {
if (canvas_item_visible(item)) {
item->hide();
}
note->property_y1() = y1;
note->property_y2() = y2;
- }
- if (CanvasHit* hit = dynamic_cast<CanvasHit*>(event)) {
+ } else if (CanvasHit* hit = dynamic_cast<CanvasHit*>(event)) {
double x = trackview.editor.frame_to_pixel((nframes64_t)
event->note()->time() - _region->start());
const double diamond_size = midi_stream_view()->note_height() / 2.0;
}
}
- _model->read_unlock();
- }
-
- if (name_text) {
- name_text->raise_to_top();
}
}
ghost->set_duration (_region->length() / samples_per_unit);
ghosts.push_back (ghost);
- for (std::vector<CanvasNoteEvent*>::iterator i = _events.begin(); i != _events.end(); ++i) {
+ for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
ghost->add_note(note);
}
#endif
if (x1 < x2) {
- for (std::vector<CanvasNoteEvent*>::iterator i = _events.begin(); i != _events.end(); ++i) {
+ for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
#ifndef NDEBUG
// Events should always be sorted by increasing x1() here
assert((*i)->x1() >= last_x1);
}
}
} else {
- for (std::vector<CanvasNoteEvent*>::iterator i = _events.begin(); i != _events.end(); ++i) {
+ for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
#ifndef NDEBUG
// Events should always be sorted by increasing x1() here
assert((*i)->x1() >= last_x1);
// care about notes being moved beyond the upper/lower bounds on the canvas
if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
- highest_note_in_selection > midi_stream_view()->highest_note()) {
+ highest_note_in_selection > midi_stream_view()->highest_note()) {
midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
}
}
};
// Update notes for selection
- for (std::vector<ArdourCanvas::CanvasNoteEvent*>::iterator i = _events.begin();
- i != _events.end(); ++i) {
+ for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
(*i)->on_channel_selection_change(mask);
}
{ return midi_view()->midi_view(); }
void set_height (double);
+ void apply_note_range(uint8_t lowest, uint8_t highest, bool force=false);
void set_frame_color();
int8_t _force_channel;
uint16_t _last_channel_selection;
double _default_note_length;
+ uint8_t _current_range_min;
+ uint8_t _current_range_max;
- boost::shared_ptr<ARDOUR::MidiModel> _model;
- std::vector<ArdourCanvas::CanvasNoteEvent*> _events;
- std::vector< boost::shared_ptr<ArdourCanvas::CanvasProgramChange> > _pgm_changes;
- ArdourCanvas::CanvasNote** _active_notes;
- ArdourCanvas::Group* _note_group;
- ARDOUR::MidiModel::DeltaCommand* _delta_command;
+ typedef std::vector<ArdourCanvas::CanvasNoteEvent*> Events;
+ typedef std::vector< boost::shared_ptr<ArdourCanvas::CanvasProgramChange> > PgmChanges;
+
+ boost::shared_ptr<ARDOUR::MidiModel> _model;
+ Events _events;
+ PgmChanges _pgm_changes;
+ ArdourCanvas::CanvasNote** _active_notes;
+ ArdourCanvas::Group* _note_group;
+ ARDOUR::MidiModel::DeltaCommand* _delta_command;
MouseState _mouse_state;
int _pressed_button;
MidiStreamView::MidiStreamView (MidiTimeAxisView& tv)
: StreamView (tv)
, note_range_adjustment(0.0f, 0.0f, 0.0f)
- , _range(ContentsRange)
+ , _range_dirty(false)
, _range_sum_cache(-1.0)
, _lowest_note(60)
- , _highest_note(60)
+ , _highest_note(71)
+ , _data_note_min(60)
+ , _data_note_max(71)
{
if (tv.is_track())
stream_base_color = ARDOUR_UI::config()->canvasvar_MidiTrackBase.get();
_note_lines->signal_event().connect (bind (mem_fun (_trackview.editor, &PublicEditor::canvas_stream_view_event), _note_lines, &_trackview));
_note_lines->lower_to_bottom();
- note_range_adjustment.signal_value_changed().connect (mem_fun (*this, &MidiStreamView::note_range_adjustment_changed));
ColorsChanged.connect(mem_fun(*this, &MidiStreamView::draw_note_lines));
+
+ note_range_adjustment.set_page_size(_highest_note - _lowest_note);
+ note_range_adjustment.set_value(_lowest_note);
+
+ note_range_adjustment.signal_value_changed().connect (mem_fun (*this, &MidiStreamView::note_range_adjustment_changed));
}
MidiStreamView::~MidiStreamView ()
{
}
+static void
+veto_note_range(uint8_t& min, uint8_t& max)
+{
+ /* Legal notes, thanks */
+ if (max > 127)
+ max = 127;
+ if (min > 127)
+ min = 127;
+
+ /* Always display at least one octave in [0, 127] */
+ if (max == 127) {
+ if (min > (127 - 11)) {
+ min = 127 - 11;
+ }
+ } else if (max < min + 11) {
+ uint8_t d = 11 - (max - min);
+ if (max + d/2 > 127) {
+ min -= d;
+ } else {
+ min -= d / 2;
+ max += d / 2;
+ }
+ }
+ assert(max - min >= 11);
+ assert(max < 127);
+ assert(min < 127);
+}
RegionView*
MidiStreamView::add_region_view_internal (boost::shared_ptr<Region> r, bool wfd, bool recording)
/* great. we already have a MidiRegionView for this Region. use it again. */
(*i)->set_valid (true);
- (*i)->enable_display(wfd);
+
display_region(dynamic_cast<MidiRegionView*>(*i), wfd);
return NULL;
region_view->init (region_color, false);
region_views.push_front (region_view);
-
- /* follow global waveform setting */
-
- if (wfd) {
- region_view->enable_display(true);
- region_view->midi_region()->midi_source(0)->load_model();
- }
-
+
/* display events and find note range */
display_region(region_view, wfd);
- /* always display at least 1 octave range */
- _highest_note = max(_highest_note, static_cast<uint8_t>(_lowest_note + 11));
-
/* catch regionview going away */
region->GoingAway.connect (bind (mem_fun (*this, &MidiStreamView::remove_region_view), region));
{
if ( ! region_view)
return;
+
+ region_view->enable_display(true);
boost::shared_ptr<MidiSource> source(region_view->midi_region()->midi_source(0));
if (load_model)
source->load_model();
- // Find our note range
- if (source->model())
- for (size_t i=0; i < source->model()->n_notes(); ++i)
- update_bounds(source->model()->note_at(i)->note());
-
+ _range_dirty = update_data_note_range(
+ source->model()->lowest_note(),
+ source->model()->highest_note());
+
// Display region contents
region_view->display_model(source->model());
}
draw_note_lines();
NoteRangeChanged();
}
+
+bool
+MidiStreamView::update_data_note_range(uint8_t min, uint8_t max)
+{
+ bool dirty = false;
+ if (min < _data_note_min) {
+ _data_note_min = min;
+ dirty = true;
+ }
+ if (max > _data_note_max) {
+ _data_note_max = max;
+ dirty = true;
+ }
+ return dirty;
+}
// FIXME: code duplication with AudioStreamView
void
{
list<RegionView *>::iterator i, tmp;
+ _range_dirty = false;
+ _data_note_min = 127;
+ _data_note_max = 0;
+
for (i = region_views.begin(); i != region_views.end(); ++i) {
- (*i)->enable_display(true); // FIXME: double display, remove
(*i)->set_valid (false);
+ (*i)->enable_display (false);
- /* FIXME: slow. MidiRegionView needs a find_note_range method
- * that finds the range without wasting time drawing the events */
-
// Load model if it isn't already, to get note range
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
- mrv->midi_region()->midi_source(0)->load_model();
+ if (mrv) {
+ mrv->midi_region()->midi_source(0)->load_model();
+ _range_dirty = update_data_note_range(
+ mrv->midi_region()->model()->lowest_note(),
+ mrv->midi_region()->model()->highest_note());
+ }
+ }
+
+ // No notes, use default range
+ if (!_range_dirty) {
+ _data_note_min = 60;
+ _data_note_max = 71;
}
+ bool range_changed = false;
+
+ // Extend visible range to show newly recorded data, if necessary
+ if (_data_note_min < _lowest_note) {
+ _lowest_note = _data_note_min;
+ range_changed = true;
+ }
+ if (_data_note_max > _highest_note) {
+ _highest_note = _data_note_max;
+ range_changed = true;
+ }
+
+ veto_note_range(_lowest_note, _highest_note);
+
if (_trackview.is_midi_track()) {
_trackview.get_diskstream()->playlist()->foreach_region (
static_cast<StreamView*>(this), &StreamView::add_region_view);
}
- /* Always display at least one octave */
- if (_highest_note == 127) {
- if (_lowest_note > (127 - 11)) {
- _lowest_note = 127 - 11;
- }
- } else if (_highest_note < _lowest_note + 11) {
- _highest_note = _lowest_note + 11;
- }
-
RegionViewList copy;
/* Place regions */
/* Fix canvas layering */
for (RegionViewList::iterator j = copy.begin(); j != copy.end(); ++j) {
- (*j)->enable_display(true); // FIXME: do this?
region_layered (*j);
}
- /* Update note range and draw note lines */
- note_range_adjustment.set_page_size(_highest_note - _lowest_note);
- note_range_adjustment.set_value(_lowest_note);
+ /* Update note range and re-draw note lines if necessary */
+ apply_note_range(_lowest_note, _highest_note);
NoteRangeChanged();
- draw_note_lines();
}
_note_lines->clear();
- for(int i = _lowest_note; i <= _highest_note; ++i) {
+ for (int i = lowest_note(); i <= highest_note(); ++i) {
y = floor(note_to_y(i));
_note_lines->add_line(prev_y, 1.0, ARDOUR_UI::config()->canvasvar_PianoRollBlackOutline.get());
- switch(i % 12) {
+ switch (i % 12) {
case 1:
case 3:
case 6:
break;
}
- if(i == _highest_note) {
+ if (i == highest_note()) {
_note_lines->add_line(y, prev_y - y, color);
- }
- else {
+ } else {
_note_lines->add_line(y + 1.0, prev_y - y - 1.0, color);
}
prev_y = y;
}
}
-
void
MidiStreamView::set_note_range(VisibleNoteRange r)
{
- _range = r;
if (r == FullRange) {
_lowest_note = 0;
_highest_note = 127;
} else {
- _lowest_note = 60;
- _highest_note = 60;
+ _lowest_note = _data_note_min;
+ _highest_note = _data_note_max;
}
- redisplay_diskstream();
+
+ apply_note_range(_lowest_note, _highest_note);
}
void
-MidiStreamView::set_note_range(uint8_t lowest, uint8_t highest) {
- if(_range == ContentsRange) {
- _lowest_note = lowest;
- _highest_note = highest;
-
- list<RegionView *>::iterator i;
- for (i = region_views.begin(); i != region_views.end(); ++i) {
- (*i)->set_height(height); // apply note range
- }
+MidiStreamView::apply_note_range(uint8_t lowest, uint8_t highest)
+{
+ _highest_note = highest;
+ _lowest_note = lowest;
+ note_range_adjustment.set_page_size(_highest_note - _lowest_note);
+ note_range_adjustment.set_value(_lowest_note);
+ draw_note_lines();
+
+ for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
+ ((MidiRegionView*)(*i))->apply_note_range(lowest, highest);
}
- draw_note_lines();
NoteRangeChanged();
}
-
+
void
-MidiStreamView::update_bounds(uint8_t note_num)
+MidiStreamView::update_note_range(uint8_t note_num)
{
- _lowest_note = min(_lowest_note, note_num);
- _highest_note = max(_highest_note, note_num);
+ assert(note_num <= 127);
+ _data_note_min = min(_data_note_min, note_num);
+ _data_note_max = max(_data_note_max, note_num);
}
-
-
+
void
MidiStreamView::setup_rec_box ()
{
void
MidiStreamView::color_handler ()
{
-
//case cMidiTrackBase:
if (_trackview.is_midi_track()) {
//canvas_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiTrackBase.get();
}
void
-MidiStreamView::note_range_adjustment_changed() {
+MidiStreamView::note_range_adjustment_changed()
+{
double sum = note_range_adjustment.get_value() + note_range_adjustment.get_page_size();
int lowest = (int) floor(note_range_adjustment.get_value());
int highest;
- if(sum == _range_sum_cache) {
+ if (sum == _range_sum_cache) {
//cerr << "cached" << endl;
highest = (int) floor(sum);
- }
- else {
+ } else {
//cerr << "recalc" << endl;
highest = lowest + (int) floor(note_range_adjustment.get_page_size());
_range_sum_cache = sum;
}
- if(lowest == lowest_note() && highest == highest_note()) {
+ if (lowest == _lowest_note && highest == _highest_note) {
return;
}
- //cerr << "note range changed: " << lowest << " " << highest << endl;
+ //cerr << "note range adjustment changed: " << lowest << " " << highest << endl;
//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;
- set_note_range(lowest, highest);
+ _lowest_note = lowest;
+ _highest_note = highest;
+ apply_note_range(lowest, highest);
}
+
Gtk::Adjustment note_range_adjustment;
ArdourCanvas::Group* midi_underlay_group;
- VisibleNoteRange note_range() { return _range; }
void set_note_range(VisibleNoteRange r);
- void set_note_range(uint8_t lowest, uint8_t highest);
- uint8_t lowest_note() const { return (_range == FullRange) ? 0 : _lowest_note; }
- uint8_t highest_note() const { return (_range == FullRange) ? 127 : _highest_note; }
+ inline uint8_t lowest_note() const { return _lowest_note; }
+ inline uint8_t highest_note() const { return _highest_note; }
- void update_bounds(uint8_t note_num);
+ void update_note_range(uint8_t note_num);
void redisplay_diskstream ();
inline double note_to_y(uint8_t note) const
{ return contents_height()
- - (note + 1 - _lowest_note) * note_height() + 1; }
+ - (note + 1 - lowest_note()) * note_height() + 1; }
inline uint8_t y_to_note(double y) const
{ return (uint8_t)((contents_height() - y - 1)
/ contents_height() * (double)contents_note_range())
- + _lowest_note; }
+ + lowest_note(); }
inline double note_height() const
{ return contents_height() / (double)contents_note_range(); }
inline uint8_t contents_note_range() const
- { return _highest_note - _lowest_note + 1; }
+ { return highest_note() - lowest_note() + 1; }
sigc::signal<void> NoteRangeChanged;
private:
void setup_rec_box ();
- void rec_data_range_ready (jack_nframes_t start, jack_nframes_t dur, boost::weak_ptr<ARDOUR::Source> src);
- void update_rec_regions (boost::shared_ptr<ARDOUR::MidiModel> data, jack_nframes_t start, jack_nframes_t dur);
+
+ void rec_data_range_ready (
+ jack_nframes_t start,
+ jack_nframes_t dur,
+ boost::weak_ptr<ARDOUR::Source> src);
+
+ void update_rec_regions (
+ boost::shared_ptr<ARDOUR::MidiModel> data,
+ jack_nframes_t start,
+ jack_nframes_t dur);
- RegionView* add_region_view_internal (boost::shared_ptr<ARDOUR::Region>, bool wait_for_waves, bool recording = false);
- void display_region(MidiRegionView* region_view, bool load_model);
- void display_diskstream (boost::shared_ptr<ARDOUR::Diskstream> ds);
+ RegionView* add_region_view_internal (
+ boost::shared_ptr<ARDOUR::Region>,
+ bool wait_for_waves,
+ bool recording = false);
+
+ void display_region(MidiRegionView* region_view, bool load_model);
+ void display_diskstream (boost::shared_ptr<ARDOUR::Diskstream> ds);
void update_contents_height ();
void draw_note_lines();
+ void apply_note_range(uint8_t lowest, uint8_t highest);
+ bool update_data_note_range(uint8_t min, uint8_t max);
void color_handler ();
void note_range_adjustment_changed();
- VisibleNoteRange _range;
+ bool _range_dirty;
double _range_sum_cache;
- uint8_t _lowest_note;
- uint8_t _highest_note;
+ uint8_t _lowest_note; ///< currently visible
+ uint8_t _highest_note; ///< currently visible
+ uint8_t _data_note_min; ///< in data
+ uint8_t _data_note_max; ///< in data
ArdourCanvas::Lineset* _note_lines;
};
MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session& sess, boost::shared_ptr<Route> rt, Canvas& canvas)
- : AxisView(sess) // FIXME: won't compile without this, why??
+ : AxisView(sess) // virtually inherited
, RouteTimeAxisView(ed, sess, rt, canvas)
+ , _ignore_signals(false)
, _range_scroomer(0)
, _piano_roll_header(0)
, _note_mode(Sustained)
MenuList& range_items = range_menu->items();
range_menu->set_name ("ArdourContextMenu");
- RadioMenuItem::Group range_group;
-
- range_items.push_back (RadioMenuElem (range_group, _("Show Full Range"), bind (
+ range_items.push_back (MenuElem (_("Show Full Range"), bind (
mem_fun(*this, &MidiTimeAxisView::set_note_range),
MidiStreamView::FullRange)));
- range_items.push_back (RadioMenuElem (range_group, _("Fit Contents"), bind (
+ range_items.push_back (MenuElem (_("Fit Contents"), bind (
mem_fun(*this, &MidiTimeAxisView::set_note_range),
MidiStreamView::ContentsRange)));
- ((Gtk::CheckMenuItem&)range_items.back()).set_active(true);
-
items.push_back (MenuElem (_("Note range"), *range_menu));
}
void
MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
{
- //if (midi_view()->note_range() != range) {
+ if (!_ignore_signals)
midi_view()->set_note_range(range);
- midi_view()->redisplay_diskstream();
- //}
}
void channel_selector_toggled();
+ bool _ignore_signals;
Gtk::Menu _subplugin_menu;
-
MidiScroomer* _range_scroomer;
PianoRollHeader* _piano_roll_header;
ARDOUR::NoteMode _note_mode;
void add_note_unlocked(const boost::shared_ptr<Note> note);
void remove_note_unlocked(const boost::shared_ptr<const Note> note);
+ uint8_t lowest_note() const { return _lowest_note; }
+ uint8_t highest_note() const { return _highest_note; }
+
protected:
mutable const_iterator _read_iter;
bool _edited;
mutable nframes_t _next_read;
bool _percussive;
+ uint8_t _lowest_note;
+ uint8_t _highest_note;
+
typedef std::priority_queue<
boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
LaterNoteEndComparator>
const ControlEvent* first = NULL;
const ControlEvent* next = NULL;
+ /* No events past start (maybe?) */
+ if (next && next->when < start)
+ return false;
+
/* Step is after first */
if (range.first == _events.begin() || (*range.first)->when == start) {
first = *range.first;
return true;
}
- if (abs(first->value - next->value) <= 1) {
+ if (fabs(first->value - next->value) <= 1) {
if (next->when <= end && (!inclusive || next->when > start)) {
x = next->when;
y = next->value;
, _end_iter(*this, DBL_MAX)
, _next_read(UINT32_MAX)
, _percussive(false)
+ , _lowest_note(127)
+ , _highest_note(0)
{
debugout << "Sequence (size " << size << ") constructed: " << this << endl;
assert(_end_iter._is_end);
assert(_writing);
_edited = true;
+ if (note_num < _lowest_note)
+ _lowest_note = note_num;
+ if (note_num > _highest_note)
+ _highest_note = note_num;
+
boost::shared_ptr<Note> new_note(new Note(chan, time, 0, note_num, velocity));
_notes.push_back(new_note);
if (!_percussive) {