diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index cef1329ed5..e7a9547d48 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -28,6 +28,7 @@ #include #include #include +#include #include "streamview.h" #include "midi_region_view.h" @@ -35,7 +36,6 @@ #include "simplerect.h" #include "simpleline.h" #include "public_editor.h" -//#include "midi_region_editor.h" #include "ghostregion.h" #include "midi_time_axis.h" #include "utils.h" @@ -53,12 +53,14 @@ using namespace ArdourCanvas; MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr r, double spu, Gdk::Color& basic_color) : RegionView (parent, tv, r, spu, basic_color) + , _active_notes(0) { } MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr r, double spu, Gdk::Color& basic_color, TimeAxisViewItem::Visibility visibility) : RegionView (parent, tv, r, spu, basic_color, visibility) + , _active_notes(0) { } @@ -89,6 +91,7 @@ MidiRegionView::init (Gdk::Color& basic_color, bool wfd) MidiRegionView::~MidiRegionView () { in_destructor = true; + end_write(); RegionViewGoingAway (this); /* EMIT_SIGNAL */ } @@ -113,3 +116,67 @@ MidiRegionView::add_ghost (AutomationTimeAxisView& atv) return NULL; } + +/** Begin tracking note state for successive calls to add_event + */ +void +MidiRegionView::begin_write() +{ + _active_notes = new ArdourCanvas::SimpleRect*[127]; +} + + +/** Destroy note state for add_event + */ +void +MidiRegionView::end_write() +{ + delete[] _active_notes; + _active_notes = NULL; +} + + +void +MidiRegionView::add_event (const MidiEvent& ev) +{ + /*printf("Event, time = %u, size = %zu, data = ", + ev.time, ev.size); + for (size_t i=0; i < ev.size; ++i) { + printf("%X ", ev.buffer[i]); + } + printf("\n");*/ + double y1 = trackview.height / 2.0; + if ((ev.buffer[0] & 0xF0) == MIDI_CMD_NOTE_ON) { + const Byte& note = ev.buffer[1]; + y1 = (trackview.height / 127.0) * note; + + ArdourCanvas::SimpleRect * ev_rect = new Gnome::Canvas::SimpleRect( + *(ArdourCanvas::Group*)get_canvas_group()); + ev_rect->property_x1() = trackview.editor.frame_to_pixel ( + ev.time); + ev_rect->property_y1() = y1; + ev_rect->property_x2() = trackview.editor.frame_to_pixel ( + _region->length()); + ev_rect->property_y2() = y1 + (trackview.height / 127.0); + ev_rect->property_outline_color_rgba() = 0xFFFFFFAA; + /* outline all but right edge */ + ev_rect->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8); + ev_rect->property_fill_color_rgba() = 0xFFFFFF66; + + _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(ev.time); + _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges + _active_notes[note] = NULL; + } + } + +} + + + diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 03bd0b1cf7..32a96a5f7c 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "region_view.h" #include "route_time_axis.h" @@ -61,6 +62,11 @@ class MidiRegionView : public RegionView GhostRegion* add_ghost (AutomationTimeAxisView&); + void add_event(const ARDOUR::MidiEvent& ev); + + void begin_write(); + void end_write(); + protected: /* this constructor allows derived types @@ -79,6 +85,11 @@ class MidiRegionView : public RegionView void set_flags (XMLNode *); void store_flags (); + + private: + + std::vector _events; + ArdourCanvas::SimpleRect** _active_notes; }; #endif /* __gtk_ardour_midi_region_view_h__ */ diff --git a/gtk2_ardour/midi_streamview.cc b/gtk2_ardour/midi_streamview.cc index be3a1007c6..06cb8b84c8 100644 --- a/gtk2_ardour/midi_streamview.cc +++ b/gtk2_ardour/midi_streamview.cc @@ -45,6 +45,7 @@ #include "gui_thread.h" #include "utils.h" #include "color.h" +#include "simplerect.h" using namespace std; using namespace ARDOUR; @@ -107,8 +108,13 @@ MidiStreamView::add_region_view_internal (boost::shared_ptr r, bool wait // FIXME //region_view->set_waveform_visible(_trackview.editor.show_waveforms()); - /* catch regionview going away */ + /* display events */ + region_view->begin_write(); + for (size_t i=0; i < region->midi_source(0)->model().n_events(); ++i) + region_view->add_event(region->midi_source(0)->model().event_at(i)); + region_view->end_write(); + /* catch regionview going away */ region->GoingAway.connect (bind (mem_fun (*this, &MidiStreamView::remove_region_view), region)); RegionViewAdded (region_view); @@ -337,12 +343,21 @@ MidiStreamView::update_rec_regions (boost::shared_ptr data, nframes_ if (origlen == 1) { /* our special initial length */ iter->second = add_region_view_internal (region, false); + ((MidiRegionView*)iter->second)->begin_write(); } /* also update rect */ ArdourCanvas::SimpleRect * rect = rec_rects[n].rectangle; gdouble xend = _trackview.editor.frame_to_pixel (region->position() + region->length()); rect->property_x2() = xend; + + /* draw events */ + MidiRegionView* mrv = (MidiRegionView*)iter->second; + for (size_t i = 0; i < data->size(); ++i) { + const MidiEvent& ev = (*data.get())[i]; + mrv->add_event(ev); + } + } } diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index d33aaa852f..7ae7024f46 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -57,6 +57,7 @@ midi_diskstream.cc midi_playlist.cc midi_track.cc midi_region.cc +midi_model.cc smf_source.cc auditioner.cc automation.cc diff --git a/libs/ardour/ardour/buffer.h b/libs/ardour/ardour/buffer.h index 86a7aa9f95..61e7a843f1 100644 --- a/libs/ardour/ardour/buffer.h +++ b/libs/ardour/ardour/buffer.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -38,7 +39,7 @@ namespace ARDOUR { * * To actually read/write buffer contents, use the appropriate derived class. */ -class Buffer +class Buffer : public boost::noncopyable { public: virtual ~Buffer() {} @@ -76,11 +77,6 @@ protected: size_t _capacity; size_t _size; bool _silent; - -private: - // Prevent copies (undefined) - Buffer(const Buffer& copy); - void operator=(const Buffer& other); }; @@ -175,10 +171,6 @@ public: { assert(offset + nframes <= _capacity); return _data + offset; } private: - // These are undefined (prevent copies) - AudioBuffer(const AudioBuffer& copy); - AudioBuffer& operator=(const AudioBuffer& copy); - bool _owns_data; Sample* _data; ///< Actual buffer contents }; @@ -197,7 +189,7 @@ public: void read_from(const Buffer& src, nframes_t nframes, nframes_t offset); - bool push_back(const MidiEvent& event); + bool push_back(const ARDOUR::MidiEvent& event); Byte* reserve(nframes_t time, size_t size); const MidiEvent& operator[](size_t i) const { assert(i < _size); return _events[i]; } @@ -206,10 +198,6 @@ public: static size_t max_event_size() { return MAX_EVENT_SIZE; } private: - // These are undefined (prevent copies) - MidiBuffer(const MidiBuffer& copy); - MidiBuffer& operator=(const MidiBuffer& copy); - // FIXME: Jack needs to tell us this static const size_t MAX_EVENT_SIZE = 4; // bytes diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h new file mode 100644 index 0000000000..5d343d7fa9 --- /dev/null +++ b/libs/ardour/ardour/midi_model.h @@ -0,0 +1,52 @@ + +/* + Copyright (C) 2006 Paul Davis + Written by Dave Robillard, 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_midi_model_h__ +#define __ardour_midi_model_h__ + +#include +#include +#include + +namespace ARDOUR { + +/** A dynamically resizable collection of MIDI events, sorted by time + */ +class MidiModel : public boost::noncopyable { +public: + MidiModel(size_t size=0); + ~MidiModel(); + + /** Resizes vector if necessary (NOT realtime safe) */ + void append(const MidiBuffer& data); + + inline const MidiEvent& event_at(unsigned i) const { return _events[i]; } + + inline size_t n_events() const { return _events.size(); } + +private: + std::vector _events; +}; + +} /* namespace ARDOUR */ + +#endif /* __ardour_midi_model_h__ */ + diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index dec9536c74..40ef87f8c9 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -62,12 +63,14 @@ class MidiSource : public Source static sigc::signal MidiSourceCreated; - // The MIDI equivalent to "peaks" + // The MIDI equivalent to "peaks" (but complete data) mutable sigc::signal,nframes_t,nframes_t> ViewDataRangeReady; XMLNode& get_state (); int set_state (const XMLNode&); + MidiModel& model() { return _model; } + protected: virtual nframes_t read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const = 0; virtual nframes_t write_unlocked (MidiRingBuffer& dst, nframes_t cnt) = 0; @@ -77,6 +80,8 @@ class MidiSource : public Source mutable uint32_t _read_data_count; ///< modified in read() mutable uint32_t _write_data_count; ///< modified in write() + MidiModel _model; + private: bool file_changed (string path); }; diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc new file mode 100644 index 0000000000..56bfd14b88 --- /dev/null +++ b/libs/ardour/midi_model.cc @@ -0,0 +1,65 @@ +/* + Copyright (C) 2006 Paul Davis + Written by Dave Robillard, 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include + +using namespace std; +using namespace ARDOUR; + + +MidiModel::MidiModel(size_t size) +: _events(size) +{ +} + +MidiModel::~MidiModel() +{ + for (size_t i=0; i < _events.size(); ++i) + delete _events[i].buffer; +} + + +/** Append contents of \a buf to model. NOT (even remotely) realtime safe. + * + * Timestamps of events in \a buf are expected to be relative to + * the start of this model (t=0) and MUST be monotonically increasing + * and MUST be >= the latest event currently in the model. + * + * Events in buf are deep copied. + */ +void +MidiModel::append(const MidiBuffer& buf) +{ + for (size_t i=0; i < buf.size(); ++i) { + const MidiEvent& buf_event = buf[i]; + assert(buf_event.time >= _events.back().time); + + _events.push_back(buf_event); + MidiEvent& my_event = _events.back(); + assert(my_event.time == buf_event.time); + assert(my_event.size == buf_event.size); + + my_event.buffer = new Byte[my_event.size]; + memcpy(my_event.buffer, buf_event.buffer, my_event.size); + } +} + diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index 52a17997a0..061a2f1234 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -352,9 +352,10 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) // FIXME: start of source time? for (size_t i=0; i < buf.size(); ++i) { - const MidiEvent& ev = buf[i]; + MidiEvent& ev = buf[i]; assert(ev.time >= _timeline_position); - uint32_t delta_time = (ev.time - _timeline_position) - _last_ev_time; + ev.time -= _timeline_position; + uint32_t delta_time = ev.time - _last_ev_time; /*printf("SMF - writing event, delta = %u, size = %zu, data = ", delta_time, ev.size); @@ -376,6 +377,8 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) const nframes_t oldlen = _length; update_length(oldlen, cnt); + _model.append(buf); + ViewDataRangeReady (buf_ptr, oldlen, cnt); /* EMIT SIGNAL */ return cnt;