*/
#include <cmath>
-#include <cassert>
#include <utility>
#include <gtkmm.h>
#include <gtkmm2ext/gtk_ui.h>
-#include "ardour/midi_diskstream.h"
-#include "ardour/midi_playlist.h"
+#include "canvas/line_set.h"
+#include "canvas/rectangle.h"
+
#include "ardour/midi_region.h"
#include "ardour/midi_source.h"
#include "ardour/midi_track.h"
+#include "ardour/operations.h"
#include "ardour/region_factory.h"
-#include "ardour/smf_source.h"
#include "ardour/session.h"
+#include "ardour/smf_source.h"
-#include "ardour_ui.h"
-#include "canvas-simplerect.h"
-#include "global_signals.h"
#include "gui_thread.h"
-#include "lineset.h"
#include "midi_region_view.h"
#include "midi_streamview.h"
#include "midi_time_axis.h"
#include "region_view.h"
#include "rgb_macros.h"
#include "selection.h"
-#include "simplerect.h"
+#include "ui_config.h"
#include "utils.h"
+#include "i18n.h"
+
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Editing;
, _updates_suspended (false)
{
/* use a group dedicated to MIDI underlays. Audio underlays are not in this group. */
- midi_underlay_group = new ArdourCanvas::Group (*_canvas_group);
+ midi_underlay_group = new ArdourCanvas::Container (_canvas_group);
midi_underlay_group->lower_to_bottom();
/* put the note lines in the timeaxisview's group, so it
- can be put below ghost regions from MIDI underlays*/
- _note_lines = new ArdourCanvas::LineSet(*_canvas_group,
- ArdourCanvas::LineSet::Horizontal);
-
- _note_lines->property_x1() = 0;
- _note_lines->property_y1() = 0;
- _note_lines->property_x2() = DBL_MAX;
- _note_lines->property_y2() = 0;
-
- _note_lines->signal_event().connect(
+ can be put below ghost regions from MIDI underlays
+ */
+ _note_lines = new ArdourCanvas::LineSet (_canvas_group, ArdourCanvas::LineSet::Horizontal);
+
+ _note_lines->Event.connect(
sigc::bind(sigc::mem_fun(_trackview.editor(),
&PublicEditor::canvas_stream_view_event),
_note_lines, &_trackview));
color_handler ();
- ColorsChanged.connect(sigc::mem_fun(*this, &MidiStreamView::color_handler));
+ UIConfiguration::instance().ColorsChanged.connect(sigc::mem_fun(*this, &MidiStreamView::color_handler));
note_range_adjustment.set_page_size(_highest_note - _lowest_note);
note_range_adjustment.set_value(_lowest_note);
{
}
-static void
-veto_note_range(uint8_t& min, uint8_t& max)
-{
- /* Legal notes, thanks */
- clamp_to_0_127(min);
- clamp_to_0_127(max);
-
- /* 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::create_region_view (boost::shared_ptr<Region> r, bool /*wfd*/, bool)
+MidiStreamView::create_region_view (boost::shared_ptr<Region> r, bool /*wfd*/, bool recording)
{
boost::shared_ptr<MidiRegion> region = boost::dynamic_pointer_cast<MidiRegion> (r);
return 0;
}
- RegionView* region_view = new MidiRegionView (_canvas_group, _trackview, region,
- _samples_per_unit, region_color);
+ RegionView* region_view = NULL;
+ if (recording) {
+ region_view = new MidiRegionView (
+ _canvas_group, _trackview, region,
+ _samples_per_pixel, region_color, recording,
+ TimeAxisViewItem::Visibility(TimeAxisViewItem::ShowFrame));
+ } else {
+ region_view = new MidiRegionView (_canvas_group, _trackview, region,
+ _samples_per_pixel, region_color);
+ }
- region_view->init (region_color, false);
+ region_view->init (false);
return region_view;
}
RegionView*
-MidiStreamView::add_region_view_internal (boost::shared_ptr<Region> r, bool wfd, bool recording)
+MidiStreamView::add_region_view_internal (boost::shared_ptr<Region> r, bool wait_for_data, bool recording)
{
boost::shared_ptr<MidiRegion> region = boost::dynamic_pointer_cast<MidiRegion> (r);
- if (region == 0) {
+ if (!region) {
return 0;
}
(*i)->set_valid (true);
- display_region(dynamic_cast<MidiRegionView*>(*i), wfd);
+ display_region(dynamic_cast<MidiRegionView*>(*i), wait_for_data);
return 0;
}
}
- MidiRegionView* region_view = dynamic_cast<MidiRegionView*> (create_region_view (r, wfd, recording));
+ MidiRegionView* region_view = dynamic_cast<MidiRegionView*> (create_region_view (r, wait_for_data, recording));
if (region_view == 0) {
return 0;
}
region_views.push_front (region_view);
- if (_trackview.editor().internal_editing()) {
- region_view->hide_rect ();
- } else {
- region_view->show_rect ();
- }
-
/* display events and find note range */
- display_region (region_view, wfd);
+ display_region (region_view, wait_for_data);
+
+ /* fit note range if we are importing */
+ if (_trackview.session()->operation_in_progress (Operations::insert_file)) {
+ set_note_range (ContentsRange);
+ }
/* catch regionview going away */
- region->DropReferences.connect (*this, invalidator (*this), boost::bind (&MidiStreamView::remove_region_view, this, region), gui_context());
+ boost::weak_ptr<Region> wr (region); // make this explicit
+ region->DropReferences.connect (*this, invalidator (*this), boost::bind (&MidiStreamView::remove_region_view, this, wr), gui_context());
RegionViewAdded (region_view);
return;
}
- region_view->enable_display(true);
+ region_view->enable_display (true);
+ region_view->set_height (child_height());
boost::shared_ptr<MidiSource> source(region_view->midi_region()->midi_source(0));
+ if (!source) {
+ error << _("attempt to display MIDI region with no source") << endmsg;
+ return;
+ }
if (load_model) {
- source->load_model();
+ Glib::Threads::Mutex::Lock lm(source->mutex());
+ source->load_model(lm);
+ }
+
+ if (!source->model()) {
+ error << _("attempt to display MIDI region with no model") << endmsg;
+ return;
}
_range_dirty = update_data_note_range(
source->model()->highest_note());
// Display region contents
- region_view->set_height (child_height());
region_view->display_model(source->model());
}
+
void
MidiStreamView::display_track (boost::shared_ptr<Track> tr)
{
{
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(r);
if (mr) {
- mr->midi_source(0)->load_model();
+ Glib::Threads::Mutex::Lock lm(mr->midi_source(0)->mutex());
+ mr->midi_source(0)->load_model(lm);
_range_dirty = update_data_note_range(
mr->model()->lowest_note(),
mr->model()->highest_note());
_data_note_max = 71;
}
- // Extend visible range to show newly recorded data, if necessary
- _lowest_note = std::min(_lowest_note, _data_note_min);
- _highest_note = std::max(_highest_note, _data_note_max);
-
- veto_note_range(_lowest_note, _highest_note);
-
// Flag region views as invalid and disable drawing
for (i = region_views.begin(); i != region_views.end(); ++i) {
(*i)->set_valid(false);
MidiStreamView::update_contents_height ()
{
StreamView::update_contents_height();
- _note_lines->property_y2() = child_height ();
+
+ _note_lines->set_extent (ArdourCanvas::COORD_MAX);
apply_note_range (lowest_note(), highest_note(), true);
}
}
double y;
- double prev_y = contents_height();
+ double prev_y = .5;
uint32_t color;
_note_lines->clear();
- if (child_height() < 140){
+ if (child_height() < 140 || note_height() < 3) {
+ /* track is too small for note lines, or there are too many */
return;
}
- for (int i = lowest_note(); i <= highest_note(); ++i) {
- y = floor(note_to_y(i));
+ /* do this is order of highest ... lowest since that matches the
+ * coordinate system in which y=0 is at the top
+ */
+
+ for (int i = highest_note() + 1; i >= lowest_note(); --i) {
+
+ y = floor(note_to_y (i)) + .5;
+
+ /* this is the line actually corresponding to the division
+ * between notes
+ */
+
+ if (i <= highest_note()) {
+ _note_lines->add (y, 1.0, UIConfiguration::instance().color ("piano roll black outline"));
+ }
- _note_lines->add_line(prev_y, 1.0, ARDOUR_UI::config()->canvasvar_PianoRollBlackOutline.get());
+ /* now add a thicker line/bar which covers the entire vertical
+ * height of this note.
+ */
switch (i % 12) {
case 1:
case 6:
case 8:
case 10:
- color = ARDOUR_UI::config()->canvasvar_PianoRollBlack.get();
+ color = UIConfiguration::instance().color_mod ("piano roll black", "piano roll black");
break;
default:
- color = ARDOUR_UI::config()->canvasvar_PianoRollWhite.get();
+ color = UIConfiguration::instance().color_mod ("piano roll white", "piano roll white");
break;
}
- if (i == highest_note()) {
- _note_lines->add_line(y, prev_y - y, color);
- } else {
- _note_lines->add_line(y + 1.0, prev_y - y - 1.0, color);
+ double h = y - prev_y;
+ double mid = y + (h/2.0);
+
+ if (height > 1.0) { // XXX ? should that not be h >= 1 ?
+ _note_lines->add (mid, h, color);
}
prev_y = y;
void
MidiStreamView::update_note_range(uint8_t note_num)
{
- assert(note_num <= 127);
_data_note_min = min(_data_note_min, note_num);
_data_note_max = max(_data_note_max, note_num);
}
_trackview.session()->record_status() == Session::Recording &&
_trackview.track()->record_enabled()) {
- if (Config->get_show_waveforms_while_recording() && rec_regions.size() == rec_rects.size()) {
+ if (UIConfiguration::instance().get_show_waveforms_while_recording() && rec_regions.size() == rec_rects.size()) {
/* add a new region, but don't bother if they set show-waveforms-while-recording mid-record */
boost::shared_ptr<MidiRegion> region (boost::dynamic_pointer_cast<MidiRegion>
(RegionFactory::create (sources, plist, false)));
-
- assert(region);
- region->set_start (_trackview.track()->current_capture_start() - _trackview.track()->get_capture_start_frame (0));
- region->set_position (_trackview.track()->current_capture_start());
- RegionView* rv = add_region_view_internal (region, false);
- MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rv);
- mrv->begin_write ();
-
- rec_regions.push_back (make_pair (region, rv));
-
- // rec regions are destroyed in setup_rec_box
-
- /* we add the region later */
-
- setup_new_rec_layer_time (region);
+ if (region) {
+ region->set_start (_trackview.track()->current_capture_start()
+ - _trackview.track()->get_capture_start_frame (0));
+ region->set_position (_trackview.session()->transport_frame());
+
+ RegionView* rv = add_region_view_internal (region, false, true);
+ MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rv);
+ mrv->begin_write ();
+
+ /* rec region will be destroyed in setup_rec_box */
+ rec_regions.push_back (make_pair (region, rv));
+
+ /* we add the region later */
+ setup_new_rec_layer_time (region);
+ } else {
+ error << _("failed to create MIDI region") << endmsg;
+ }
}
/* start a new rec box */
- boost::shared_ptr<MidiTrack> mt = _trackview.midi_track(); /* we know what it is already */
- framepos_t const frame_pos = mt->current_capture_start ();
- gdouble const xstart = _trackview.editor().frame_to_pixel (frame_pos);
- gdouble const xend = xstart;
- uint32_t fill_color;
-
- assert(_trackview.midi_track()->mode() == Normal);
-
- fill_color = ARDOUR_UI::config()->canvasvar_RecordingRect.get();
-
- ArdourCanvas::SimpleRect * rec_rect = new Gnome::Canvas::SimpleRect (*_canvas_group);
- rec_rect->property_x1() = xstart;
- rec_rect->property_y1() = 1.0;
- rec_rect->property_x2() = xend;
- rec_rect->property_y2() = (double) _trackview.current_height() - 1;
- rec_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RecordingRect.get();
- rec_rect->property_fill_color_rgba() = fill_color;
- rec_rect->lower_to_bottom();
-
- RecBoxInfo recbox;
- recbox.rectangle = rec_rect;
- recbox.start = _trackview.session()->transport_frame();
- recbox.length = 0;
-
- rec_rects.push_back (recbox);
-
- screen_update_connection.disconnect();
- screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
- sigc::mem_fun (*this, &MidiStreamView::update_rec_box));
- rec_updating = true;
- rec_active = true;
+ create_rec_box(_trackview.midi_track()->current_capture_start(), 0);
} else if (rec_active &&
(_trackview.session()->record_status() != Session::Recording ||
/* disconnect rapid update */
screen_update_connection.disconnect();
rec_data_ready_connections.drop_connections ();
-
rec_updating = false;
rec_active = false;
draw_note_lines ();
if (_trackview.is_midi_track()) {
- canvas_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiTrackBase.get();
+ canvas_rect->set_fill_color (UIConfiguration::instance().color_mod ("midi track base", "midi track base"));
} else {
- canvas_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiBusBase.get();;
+ canvas_rect->set_fill_color (UIConfiguration::instance().color ("midi bus base"));
}
}
MidiStreamView::resume_updates ()
{
_updates_suspended = false;
-
+
draw_note_lines ();
apply_note_range_to_regions ();
+
+ _canvas_group->redraw ();
+}
+
+struct RegionPositionSorter {
+ bool operator() (RegionView* a, RegionView* b) {
+ return a->region()->position() < b->region()->position();
+ }
+};
+
+bool
+MidiStreamView::paste (ARDOUR::framepos_t pos, const Selection& selection, PasteContext& ctx)
+{
+ /* Paste into the first region which starts on or before pos. Only called when
+ using an internal editing tool. */
+
+ if (region_views.empty()) {
+ return false;
+ }
+
+ region_views.sort (RegionView::PositionOrder());
+
+ list<RegionView*>::const_iterator prev = region_views.begin ();
+
+ for (list<RegionView*>::const_iterator i = region_views.begin(); i != region_views.end(); ++i) {
+ if ((*i)->region()->position() > pos) {
+ break;
+ }
+ prev = i;
+ }
+
+ boost::shared_ptr<Region> r = (*prev)->region ();
+
+ /* If *prev doesn't cover pos, it's no good */
+ if (r->position() > pos || ((r->position() + r->length()) < pos)) {
+ return false;
+ }
+
+ MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*prev);
+ return mrv ? mrv->paste(pos, selection, ctx) : false;
}