From 79db28b0b87a6fb67ed6f31ec4753f52aad4358a Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 4 Oct 2007 05:15:28 +0000 Subject: [PATCH] Merge the two separate 'add notes to midi region' interfaces (note and midievent). Update note range dynamically while recording to fit incoming notes. git-svn-id: svn://localhost/ardour2/trunk@2512 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/canvas-midi-event.cc | 13 +++- gtk2_ardour/canvas-midi-event.h | 5 +- gtk2_ardour/canvas-note.h | 4 +- gtk2_ardour/midi_region_view.cc | 111 ++++++++++++------------------- gtk2_ardour/midi_region_view.h | 4 +- gtk2_ardour/midi_streamview.cc | 20 ++++-- libs/ardour/ardour/midi_event.h | 2 +- libs/ardour/note.cc | 2 + 8 files changed, 79 insertions(+), 82 deletions(-) diff --git a/gtk2_ardour/canvas-midi-event.cc b/gtk2_ardour/canvas-midi-event.cc index 642ae170b1..5981e7b78e 100644 --- a/gtk2_ardour/canvas-midi-event.cc +++ b/gtk2_ardour/canvas-midi-event.cc @@ -31,14 +31,23 @@ namespace Gnome { namespace Canvas { -CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item, const ARDOUR::Note* note) +CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item, const ARDOUR::Note* note, bool copy_note) : _region(region) , _item(item) , _state(None) - , _note(note) + , _note((copy_note && note) ? new ARDOUR::Note(*note) : note) + , _own_note(copy_note) , _selected(false) { } + + +CanvasMidiEvent::~CanvasMidiEvent() +{ + if (_own_note) + delete _note; +} + void CanvasMidiEvent::selected(bool yn) diff --git a/gtk2_ardour/canvas-midi-event.h b/gtk2_ardour/canvas-midi-event.h index 63b32cf0ca..d7b83bc212 100644 --- a/gtk2_ardour/canvas-midi-event.h +++ b/gtk2_ardour/canvas-midi-event.h @@ -43,8 +43,8 @@ namespace Canvas { */ class CanvasMidiEvent { public: - CanvasMidiEvent(MidiRegionView& region, Item* item, const ARDOUR::Note* note = NULL); - virtual ~CanvasMidiEvent() {} + CanvasMidiEvent(MidiRegionView& region, Item* item, const ARDOUR::Note* note=NULL, bool copy_note=false); + virtual ~CanvasMidiEvent(); bool on_event(GdkEvent* ev); @@ -71,6 +71,7 @@ protected: Item* const _item; State _state; const ARDOUR::Note* _note; + bool _own_note; bool _selected; }; diff --git a/gtk2_ardour/canvas-note.h b/gtk2_ardour/canvas-note.h index b12958a8ab..4fde2281ca 100644 --- a/gtk2_ardour/canvas-note.h +++ b/gtk2_ardour/canvas-note.h @@ -30,8 +30,8 @@ namespace Canvas { class CanvasNote : public SimpleRect, public CanvasMidiEvent { public: - CanvasNote(MidiRegionView& region, Group& group, const ARDOUR::Note* note=NULL) - : SimpleRect(group), CanvasMidiEvent(region, this, note) + CanvasNote(MidiRegionView& region, Group& group, const ARDOUR::Note* note=NULL, bool copy_note=false) + : SimpleRect(group), CanvasMidiEvent(region, this, note, copy_note) { } diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 8dc4029d26..5defec6413 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -406,7 +406,7 @@ MidiRegionView::redisplay_model() _model->read_lock(); for (size_t i=0; i < _model->n_notes(); ++i) - add_note(_model->note_at(i)); + add_note(_model->note_at(i), false); end_write(); @@ -495,8 +495,23 @@ MidiRegionView::set_y_position_and_height (double y, double h) { RegionView::set_y_position_and_height(y, h - 1); - if (_enable_display) - redisplay_model(); + if (_enable_display) { + + _model->read_lock(); + + for (std::vector::const_iterator i = _events.begin(); i != _events.end(); ++i) { + CanvasNote* note = dynamic_cast(*i); + if (note && note->note()) { + const double y1 = midi_stream_view()->note_to_y(note->note()->note()); + const double y2 = y1 + floor(midi_stream_view()->note_height()); + + note->property_y1() = y1; + note->property_y2() = y2; + } + } + + _model->read_unlock(); + } if (name_text) { name_text->raise_to_top(); @@ -544,68 +559,18 @@ MidiRegionView::end_write() } -/** Add a MIDI event. - * - * This is used while recording, and handles displaying still-unresolved notes. - * Displaying an existing model is simpler, and done with add_note. +/** Resolve an active MIDI note (while recording). */ void -MidiRegionView::add_event (const MidiEvent& ev) +MidiRegionView::resolve_note(uint8_t note, double end_time) { - /*printf("MRV add Event, time = %f, size = %u, data = ", ev.time(), ev.size()); - for (size_t i=0; i < ev.size(); ++i) { - printf("%X ", ev.buffer()[i]); - } - printf("\n\n");*/ + if (midi_view()->note_mode() != Sustained) + return; - //ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group(); - ArdourCanvas::Group* const group = _note_group; - - if (midi_view()->note_mode() == Sustained) { - if ((ev.buffer()[0] & 0xF0) == MIDI_CMD_NOTE_ON) { - const Byte& note = ev.buffer()[1]; - const double y1 = midi_stream_view()->note_to_y(note); - - CanvasNote* ev_rect = new CanvasNote(*this, *group); - ev_rect->property_x1() = trackview.editor.frame_to_pixel ( - (nframes_t)ev.time()); - ev_rect->property_y1() = y1; - ev_rect->property_x2() = trackview.editor.frame_to_pixel ( - _region->length()); - ev_rect->property_y2() = y1 + floor(midi_stream_view()->note_height()); - ev_rect->property_fill_color_rgba() = note_fill_color(ev.velocity()); - ev_rect->property_outline_color_rgba() = note_outline_color(ev.velocity()); - /* outline all but right edge */ - ev_rect->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8); - - ev_rect->raise_to_top(); - - _events.push_back(ev_rect); - if (_active_notes) - _active_notes[note] = ev_rect; - - } else if ((ev.buffer()[0] & 0xF0) == MIDI_CMD_NOTE_OFF) { - const Byte& note = ev.buffer()[1]; - if (_active_notes && _active_notes[note]) { - _active_notes[note]->property_x2() = trackview.editor.frame_to_pixel((nframes_t)ev.time()); - _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges - _active_notes[note] = NULL; - } - } - - } else if (midi_view()->note_mode() == Percussive) { - const Byte& note = ev.buffer()[1]; - const double diamond_size = midi_stream_view()->note_height() / 2.0; - const double x = trackview.editor.frame_to_pixel((nframes_t)ev.time()); - const double y = midi_stream_view()->note_to_y(note) + ((diamond_size-2) / 4.0); - - CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size); - ev_diamond->move(x, y); - ev_diamond->show(); - ev_diamond->property_fill_color_rgba() = note_fill_color(ev.velocity()); - ev_diamond->property_outline_color_rgba() = note_outline_color(ev.velocity()); - - _events.push_back(ev_diamond); + if (_active_notes && _active_notes[note]) { + _active_notes[note]->property_x2() = trackview.editor.frame_to_pixel((nframes_t)end_time); + _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges + _active_notes[note] = NULL; } } @@ -626,11 +591,12 @@ MidiRegionView::extend_active_notes() /** Add a MIDI note to the view (with duration). * - * This does no 'realtime' note resolution, notes from a MidiModel have a - * duration so they can be drawn in full immediately. + * If in sustained mode, notes with duration 0 will be considered active + * notes, and resolve_note should be called when the corresponding note off + * event arrives, to properly display the note. */ void -MidiRegionView::add_note (const Note& note) +MidiRegionView::add_note (const Note& note, bool copy_note) { assert(note.time() >= 0); //assert(note.time() < _region->length()); @@ -640,16 +606,27 @@ MidiRegionView::add_note (const Note& note) if (midi_view()->note_mode() == Sustained) { const double y1 = midi_stream_view()->note_to_y(note.note()); - CanvasNote* ev_rect = new CanvasNote(*this, *group, ¬e); + CanvasNote* ev_rect = new CanvasNote(*this, *group, ¬e, copy_note); ev_rect->property_x1() = trackview.editor.frame_to_pixel((nframes_t)note.time()); ev_rect->property_y1() = y1; - ev_rect->property_x2() = trackview.editor.frame_to_pixel((nframes_t)(note.end_time())); + if (note.duration() > 0) + ev_rect->property_x2() = trackview.editor.frame_to_pixel((nframes_t)(note.end_time())); + else + ev_rect->property_x2() = trackview.editor.frame_to_pixel(_region->length()); ev_rect->property_y2() = y1 + floor(midi_stream_view()->note_height()); ev_rect->property_fill_color_rgba() = note_fill_color(note.velocity()); ev_rect->property_outline_color_rgba() = note_outline_color(note.velocity()); - ev_rect->property_outline_what() = (guint32) 0xF; // all edges + if (note.duration() == 0) { + _active_notes[note.note()] = ev_rect; + /* outline all but right edge */ + ev_rect->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8); + } else { + /* outline all edges */ + ev_rect->property_outline_what() = (guint32) 0xF; + } + ev_rect->show(); _events.push_back(ev_rect); diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index d2c0df182d..6b0dcd1804 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -76,8 +76,8 @@ class MidiRegionView : public RegionView GhostRegion* add_ghost (AutomationTimeAxisView&); - void add_event(const ARDOUR::MidiEvent& ev); - void add_note(const ARDOUR::Note& note); + void add_note(const ARDOUR::Note& note, bool copy_note); + void resolve_note(uint8_t note_num, double end_time); void begin_write(); void end_write(); diff --git a/gtk2_ardour/midi_streamview.cc b/gtk2_ardour/midi_streamview.cc index 903197072d..73f14a2e40 100644 --- a/gtk2_ardour/midi_streamview.cc +++ b/gtk2_ardour/midi_streamview.cc @@ -417,6 +417,7 @@ MidiStreamView::update_rec_regions (boost::shared_ptr data, nframes_t if (use_rec_regions) { uint32_t n = 0; + bool update_range = false; for (list,RegionView*> >::iterator iter = rec_regions.begin(); iter != rec_regions.end(); n++) { @@ -474,14 +475,18 @@ MidiStreamView::update_rec_regions (boost::shared_ptr data, nframes_t break; if (note.time() >= start) - if (data->note_mode() == Percussive || note.duration() > 0) - mrv->add_note(note); - else - mrv->add_event(note.on_event()); - + mrv->add_note(note, true); + if (note.duration() > 0 && note.end_time() >= start) - mrv->add_event(note.off_event()); + mrv->resolve_note(note.note(), note.end_time()); + if (note.note() < _lowest_note) { + _lowest_note = note.note(); + update_range = true; + } else if (note.note() > _highest_note) { + _highest_note = note.note(); + update_range = true; + } } mrv->extend_active_notes(); @@ -517,6 +522,9 @@ MidiStreamView::update_rec_regions (boost::shared_ptr data, nframes_t iter = tmp; } + + if (update_range) + update_contents_y_position_and_height(); } } diff --git a/libs/ardour/ardour/midi_event.h b/libs/ardour/ardour/midi_event.h index bd16440d05..c430c708b7 100644 --- a/libs/ardour/ardour/midi_event.h +++ b/libs/ardour/ardour/midi_event.h @@ -148,7 +148,7 @@ struct MidiEvent { inline uint8_t cc_number() const { return (_buffer[1]); } inline uint8_t cc_value() const { return (_buffer[2]); } inline const Byte* buffer() const { return _buffer; } - inline Byte*& buffer() { return _buffer; } + inline Byte*& buffer() { return _buffer; } private: double _time; /**< Sample index (or beat time) at which event is valid */ diff --git a/libs/ardour/note.cc b/libs/ardour/note.cc index 029daab3ba..1cd691c3d7 100644 --- a/libs/ardour/note.cc +++ b/libs/ardour/note.cc @@ -45,6 +45,8 @@ Note::Note(const Note& copy) : _on_event(copy._on_event, true) , _off_event(copy._off_event, true) { + assert(_on_event.buffer()); + assert(_off_event.buffer()); /* assert(copy._on_event.size == 3); _on_event.buffer = _on_event_buffer;