diff --git a/gtk2_ardour/canvas-note-event.cc b/gtk2_ardour/canvas-note-event.cc index aa296d1fb2..a81692d01e 100644 --- a/gtk2_ardour/canvas-note-event.cc +++ b/gtk2_ardour/canvas-note-event.cc @@ -131,11 +131,10 @@ CanvasNoteEvent::show_channel_selector(void) _channel_selector->channel_selected.connect( sigc::mem_fun(this, &CanvasNoteEvent::on_channel_change)); - _channel_selector_widget = - new Widget(*(_item->property_parent()), - x1(), - y2() + 2, - (Gtk::Widget &) *_channel_selector); + _channel_selector_widget = new Widget(*(_item->property_parent()), + x1(), + y2() + 2, + (Gtk::Widget &) *_channel_selector); _channel_selector_widget->hide(); _channel_selector_widget->property_height() = 100; @@ -186,8 +185,8 @@ CanvasNoteEvent::base_color() ColorMode mode = _region.color_mode(); - const uint8_t minimal_opaqueness = 15; - uint8_t opaqueness = std::max(minimal_opaqueness, uint8_t(_note->velocity() + _note->velocity())); + const uint8_t min_opacity = 15; + uint8_t opacity = std::max(min_opacity, uint8_t(_note->velocity() + _note->velocity())); switch (mode) { case TrackColor: @@ -197,12 +196,12 @@ CanvasNoteEvent::base_color() SCALE_USHORT_TO_UINT8_T(color.get_red()), SCALE_USHORT_TO_UINT8_T(color.get_green()), SCALE_USHORT_TO_UINT8_T(color.get_blue()), - opaqueness); + opacity); } case ChannelColors: return UINT_RGBA_CHANGE_A(CanvasNoteEvent::midi_channel_colors[_note->channel()], - opaqueness); + opacity); default: return meter_style_fill_color(_note->velocity()); @@ -221,9 +220,13 @@ CanvasNoteEvent::on_event(GdkEvent* ev) double event_x, event_y, dx, dy; bool select_mod; uint8_t d_velocity = 10; - - if (_region.get_time_axis_view().editor().current_mouse_mode() != Editing::MouseNote) + + if (_region.get_time_axis_view().editor().current_mouse_mode() != Editing::MouseNote) { return false; + } + + const Editing::MidiEditMode midi_edit_mode + = _region.midi_view()->editor().current_midi_edit_mode(); switch (ev->type) { case GDK_SCROLL: @@ -284,7 +287,7 @@ CanvasNoteEvent::on_event(GdkEvent* ev) switch (_state) { case Pressed: // Drag begin - if (_region.midi_view()->editor().current_midi_edit_mode() == Editing::MidiEditSelect + if (midi_edit_mode == Editing::MidiEditSelect && _region.mouse_state() != MidiRegionView::SelectTouchDragging && _region.mouse_state() != MidiRegionView::EraseTouchDragging) { _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, @@ -311,12 +314,10 @@ CanvasNoteEvent::on_event(GdkEvent* ev) } _item->property_parent().get_value()->w2i(event_x, event_y); - // Snap event_x = _region.snap_to_pixel(event_x); - dx = event_x - last_x; - dy = event_y - last_y; - + dx = event_x - last_x; + dy = event_y - last_y; last_x = event_x; drag_delta_x += dx; @@ -356,16 +357,16 @@ CanvasNoteEvent::on_event(GdkEvent* ev) switch (_state) { case Pressed: // Clicked - if (_region.midi_view()->editor().current_midi_edit_mode() == Editing::MidiEditSelect) { + if (midi_edit_mode == Editing::MidiEditSelect) { _state = None; - - if (_selected && !select_mod && _region.selection_size() > 1) + if (_selected && !select_mod && _region.selection_size() > 1) { _region.unique_select(this); - else if (_selected) + } else if (_selected) { _region.note_deselected(this, select_mod); - else + } else { _region.note_selected(this, select_mod); - } else if (_region.midi_view()->editor().current_midi_edit_mode() == Editing::MidiEditErase) { + } + } else if (midi_edit_mode == Editing::MidiEditErase) { _region.start_delta_command(); _region.command_remove_note(this); _region.apply_command(); @@ -375,12 +376,9 @@ CanvasNoteEvent::on_event(GdkEvent* ev) case Dragging: // Dropped _item->ungrab(ev->button.time); _state = None; - - if (_note) - _region.note_dropped(this, - _region.midi_view()->editor().pixel_to_frame(abs(drag_delta_x)) - * ((drag_delta_x < 0.0) ? -1 : 1), - drag_delta_note); + if (_note) { + _region.note_dropped(this, drag_delta_x, drag_delta_note); + } return true; default: break; diff --git a/gtk2_ardour/canvas-program-change.cc b/gtk2_ardour/canvas-program-change.cc index d544d9d8aa..badadd22a7 100644 --- a/gtk2_ardour/canvas-program-change.cc +++ b/gtk2_ardour/canvas-program-change.cc @@ -12,18 +12,17 @@ using namespace MIDI::Name; using namespace std; CanvasProgramChange::CanvasProgramChange( - MidiRegionView& region, - Group& parent, - string& text, - double height, - double x, - double y, - string& model_name, - string& custom_device_mode, - nframes_t event_time, - uint8_t channel, - uint8_t program - ) + MidiRegionView& region, + Group& parent, + string& text, + double height, + double x, + double y, + string& model_name, + string& custom_device_mode, + nframes_t event_time, + uint8_t channel, + uint8_t program) : CanvasFlag( region, parent, @@ -31,8 +30,7 @@ CanvasProgramChange::CanvasProgramChange( ARDOUR_UI::config()->canvasvar_MidiProgramChangeOutline.get(), ARDOUR_UI::config()->canvasvar_MidiProgramChangeFill.get(), x, - y - ) + y) , _model_name(model_name) , _custom_device_mode(custom_device_mode) , _event_time(event_time) diff --git a/gtk2_ardour/editor_rulers.cc b/gtk2_ardour/editor_rulers.cc index 0d355832f2..0835cd81ab 100644 --- a/gtk2_ardour/editor_rulers.cc +++ b/gtk2_ardour/editor_rulers.cc @@ -508,7 +508,7 @@ Editor::popup_ruler_menu (nframes64_t where, ItemType t) ruler_items.push_back (MenuElem (*action->create_menu_item())); } - editor_ruler_menu->popup (1, gtk_get_current_event_time()); + editor_ruler_menu->popup (1, gtk_get_current_event_time()); no_ruler_shown_update = false; } diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index a090c1e566..8d688fa082 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -64,11 +64,12 @@ using namespace PBD; using namespace Editing; using namespace ArdourCanvas; -MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr r, double spu, Gdk::Color& basic_color) +MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, + boost::shared_ptr r, double spu, Gdk::Color& basic_color) : RegionView (parent, tv, r, spu, basic_color) , _force_channel(-1) , _last_channel_selection(0xFFFF) - , _default_note_length(0.0) + , _default_note_length(1.0) , _current_range_min(0) , _current_range_max(0) , _model_name(string()) @@ -82,11 +83,13 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & _note_group->raise_to_top(); } -MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr r, double spu, Gdk::Color& basic_color, TimeAxisViewItem::Visibility visibility) +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, false, visibility) , _force_channel(-1) , _last_channel_selection(0xFFFF) - , _default_note_length(0.0) + , _default_note_length(1.0) , _model_name(string()) , _custom_device_mode(string()) , _active_notes(0) @@ -104,7 +107,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) : RegionView (other) , _force_channel(-1) , _last_channel_selection(0xFFFF) - , _default_note_length(0.0) + , _default_note_length(1.0) , _model_name(string()) , _custom_device_mode(string()) , _active_notes(0) @@ -122,11 +125,11 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) init (c, false); } -MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr other_region) - : RegionView (other, boost::shared_ptr (other_region)) +MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr region) + : RegionView (other, boost::shared_ptr (region)) , _force_channel(-1) , _last_channel_selection(0xFFFF) - , _default_note_length(0.0) + , _default_note_length(1.0) , _model_name(string()) , _custom_device_mode(string()) , _active_notes(0) @@ -151,11 +154,6 @@ MidiRegionView::init (Gdk::Color& basic_color, bool wfd) midi_region()->midi_source(0)->load_model(); } - const Meter& m = trackview.session().tempo_map().meter_at(_region->position()); - const Tempo& t = trackview.session().tempo_map().tempo_at(_region->position()); - _default_note_length = m.frames_per_bar(t, trackview.session().frame_rate()) - / m.beats_per_bar(); - _model = midi_region()->midi_source(0)->model(); _enable_display = false; @@ -208,14 +206,14 @@ MidiRegionView::canvas_event(GdkEvent* ev) if (trackview.editor().current_mouse_mode() != MouseNote) return false; - - // Mmmm, spaghetti + + const Editing::MidiEditMode midi_edit_mode = trackview.editor().current_midi_edit_mode(); switch (ev->type) { case GDK_KEY_PRESS: if (ev->key.keyval == GDK_Delete && !delete_mod) { delete_mod = true; - original_mode = trackview.editor().current_midi_edit_mode(); + original_mode = midi_edit_mode; trackview.editor().set_midi_edit_mode(MidiEditErase); start_delta_command(_("erase notes")); _mouse_state = EraseTouchDragging; @@ -282,7 +280,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) case Pressed: // Drag start // Select drag start - if (_pressed_button == 1 && trackview.editor().current_midi_edit_mode() == MidiEditSelect) { + if (_pressed_button == 1 && midi_edit_mode == MidiEditSelect) { group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, Gdk::Cursor(Gdk::FLEUR), ev->motion.time); last_x = event_x; @@ -305,7 +303,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) return true; // Add note drag start - } else if (trackview.editor().current_midi_edit_mode() == MidiEditPencil) { + } else if (midi_edit_mode == MidiEditPencil) { group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, Gdk::Cursor(Gdk::FLEUR), ev->motion.time); last_x = event_x; @@ -316,13 +314,14 @@ MidiRegionView::canvas_event(GdkEvent* ev) drag_rect = new ArdourCanvas::SimpleRect(*group); drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame); - drag_rect->property_y1() = midi_stream_view()->note_to_y(midi_stream_view()->y_to_note(event_y)); + drag_rect->property_y1() = midi_stream_view()->note_to_y( + midi_stream_view()->y_to_note(event_y)); drag_rect->property_x2() = event_x; - drag_rect->property_y2() = drag_rect->property_y1() + floor(midi_stream_view()->note_height()); + drag_rect->property_y2() = drag_rect->property_y1() + + floor(midi_stream_view()->note_height()); drag_rect->property_outline_what() = 0xFF; drag_rect->property_outline_color_rgba() = 0xFFFFFF99; - - drag_rect->property_fill_color_rgba() = 0xFFFFFF66; + drag_rect->property_fill_color_rgba() = 0xFFFFFF66; _mouse_state = AddDragging; return true; @@ -385,7 +384,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) switch (_mouse_state) { case Pressed: // Clicked - switch (trackview.editor().current_midi_edit_mode()) { + switch (midi_edit_mode) { case MidiEditSelect: case MidiEditResize: clear_selection(); @@ -408,7 +407,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) const double length = trackview.editor().pixel_to_frame( drag_rect->property_x2() - drag_rect->property_x1()); - create_note_at(x, drag_rect->property_y1(), length); + create_note_at(x, drag_rect->property_y1(), frames_to_beats(length)); } delete drag_rect; @@ -423,7 +422,10 @@ MidiRegionView::canvas_event(GdkEvent* ev) } -/** Add a note to the model, and the view, at a canvas (click) coordinate */ +/** Add a note to the model, and the view, at a canvas (click) coordinate. + * \param x horizontal position in pixels + * \param y vertical position in pixels + * \param length duration of the note in beats */ void MidiRegionView::create_note_at(double x, double y, double length) { @@ -435,29 +437,18 @@ MidiRegionView::create_note_at(double x, double y, double length) assert(note >= 0.0); assert(note <= 127.0); - nframes64_t new_note_time = trackview.editor().pixel_to_frame (x); - assert(new_note_time >= 0); - new_note_time += _region->start(); + // Start of note in frames relative to region start + nframes64_t start_frames = snap_to_frame(trackview.editor().pixel_to_frame(x)); + assert(start_frames >= 0); - /* - const Meter& m = trackview.session().tempo_map().meter_at(new_note_time); - const Tempo& t = trackview.session().tempo_map().tempo_at(new_note_time); - double length = m.frames_per_bar(t, trackview.session().frame_rate()) / m.beats_per_bar(); - */ - - // we need to snap here again in nframes64_t in order to be sample accurate - // since note time is region-absolute but snap_to_frame expects position-relative - // time we have to coordinate transform back and forth here. - nframes64_t new_note_time_position_relative = new_note_time - _region->start(); - new_note_time = snap_to_frame(new_note_time_position_relative) + _region->start(); - - // we need to snap the length too to be sample accurate - nframes64_t new_note_length = nframes_t(length); - new_note_length = snap_to_frame(new_note_time_position_relative + new_note_length) + _region->start() - - new_note_time; + // Snap length + length = frames_to_beats( + snap_to_frame(start_frames + beats_to_frames(length)) - start_frames); + + const boost::shared_ptr new_note(new NoteType(0, + frames_to_beats(start_frames + _region->start()), length, + (uint8_t)note, 0x40)); - const boost::shared_ptr new_note(new NoteType( - 0, new_note_time, new_note_length, (uint8_t)note, 0x40)); view->update_note_range(new_note->note()); MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note"); @@ -478,8 +469,9 @@ MidiRegionView::clear_events() } } - for (Events::iterator i = _events.begin(); i != _events.end(); ++i) + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { delete *i; + } _events.clear(); _pgm_changes.clear(); @@ -491,26 +483,29 @@ MidiRegionView::display_model(boost::shared_ptr model) { _model = model; - if (_enable_display) + if (_enable_display) { redisplay_model(); + } } void MidiRegionView::start_delta_command(string name) { - if (!_delta_command) + if (!_delta_command) { _delta_command = _model->new_delta_command(name); + } } void MidiRegionView::command_add_note(const boost::shared_ptr note, bool selected) { - if (_delta_command) + if (_delta_command) { _delta_command->add(note); - - if (selected) + } + if (selected) { _marked_for_selection.insert(note); + } } void @@ -558,14 +553,12 @@ MidiRegionView::redisplay_model() return; if (_model) { - clear_events(); _model->read_lock(); - MidiModel::Notes notes = _model->notes(); /* - cerr << endl << _model->midi_source()->name() << " : redisplaying " << notes.size() << " notes:" << endl; + cerr << _model->midi_source()->name() << " : redisplaying " << notes.size() << endl; for (MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) { cerr << "NOTE time: " << (*i)->time() << " pitch: " << int((*i)->note()) @@ -580,7 +573,7 @@ MidiRegionView::redisplay_model() add_note(_model->note_at(i)); } - find_and_insert_program_change_flags(); + display_program_change_flags(); // Is this necessary? /*for (Automatable::Controls::const_iterator i = _model->controls().begin(); @@ -619,22 +612,22 @@ MidiRegionView::redisplay_model() } void -MidiRegionView::find_and_insert_program_change_flags() +MidiRegionView::display_program_change_flags() { - // Draw program change 'flags' for (Automatable::Controls::iterator control = _model->controls().begin(); control != _model->controls().end(); ++control) { if (control->first.type() == MidiPgmChangeAutomation) { Glib::Mutex::Lock list_lock (control->second->list()->lock()); - uint8_t channel = control->first.channel(); + uint8_t channel = control->first.channel(); for (AutomationList::const_iterator event = control->second->list()->begin(); event != control->second->list()->end(); ++event) { double event_time = (*event)->when; double program_number = floor((*event)->value + 0.5); - //cerr << " got program change on channel " << int(channel) << " time: " << event_time << " number: " << program_number << endl; + //cerr << " got program change on channel " << int(channel) + // << " time: " << event_time << " number: " << program_number << endl; // find bank select msb and lsb for the program change Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK); @@ -651,7 +644,8 @@ MidiRegionView::find_and_insert_program_change_flags() lsb = uint8_t(floor(lsb_control->get_float(true, event_time) + 0.5)); } - //cerr << " got msb " << int(msb) << " and lsb " << int(lsb) << " thread_id: " << pthread_self() << endl; + //cerr << " got msb " << int(msb) << " and lsb " << int(lsb) + // << " thread_id: " << pthread_self() << endl; MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number); @@ -660,13 +654,13 @@ MidiRegionView::find_and_insert_program_change_flags() _model_name, _custom_device_mode, channel, - patch_key - ); + patch_key); ControlEvent program_change(nframes_t(event_time), uint8_t(program_number), channel); if (patch != 0) { - //cerr << " got patch with name " << patch->name() << " number " << patch->number() << endl; + //cerr << " got patch with name " << patch->name() + // << " number " << patch->number() << endl; add_pgm_change(program_change, patch->name()); } else { char buf[4]; @@ -676,7 +670,8 @@ MidiRegionView::find_and_insert_program_change_flags() } break; } else if (control->first.type() == MidiCCAutomation) { - //cerr << " found CC Automation of channel " << int(control->first.channel()) << " and id " << control->first.id() << endl; + //cerr << " found CC Automation of channel " << int(control->first.channel()) + // << " and id " << control->first.id() << endl; } } } @@ -705,8 +700,9 @@ MidiRegionView::region_resized (Change what_changed) RegionView::region_resized(what_changed); if (what_changed & ARDOUR::PositionChanged) { - if (_enable_display) + if (_enable_display) { redisplay_model(); + } } } @@ -716,14 +712,15 @@ MidiRegionView::reset_width_dependent_items (double pixel_width) RegionView::reset_width_dependent_items(pixel_width); assert(_pixel_width == pixel_width); - if (_enable_display) + if (_enable_display) { redisplay_model(); + } } void -MidiRegionView::set_height (gdouble height) +MidiRegionView::set_height (double height) { - static const double FUDGE = 2; + static const double FUDGE = 2.0; const double old_height = _height; RegionView::set_height(height); _height = height - FUDGE; @@ -811,8 +808,7 @@ MidiRegionView::add_ghost (TimeAxisView& tv) audio waveforms under it. */ ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position); - } - else { + } else { ghost = new MidiGhostRegion (tv, trackview, unit_position); } @@ -861,8 +857,9 @@ MidiRegionView::end_write() void MidiRegionView::resolve_note(uint8_t note, double end_time) { - if (midi_view()->note_mode() != Sustained) + if (midi_view()->note_mode() != Sustained) { return; + } if (_active_notes && _active_notes[note]) { _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel((nframes64_t)end_time); @@ -898,9 +895,11 @@ MidiRegionView::play_midi_note(boost::shared_ptr note) RouteUI* route_ui = dynamic_cast (&trackview); assert(route_ui); - route_ui->midi_track()->write_immediate_event(note->on_event().size(), note->on_event().buffer()); + route_ui->midi_track()->write_immediate_event( + note->on_event().size(), note->on_event().buffer()); - nframes_t note_length_ms = (note->off_event().time() - note->on_event().time()) + const double note_length_beats = (note->off_event().time() - note->on_event().time()); + nframes_t note_length_ms = beats_to_frames(note_length_beats) * (1000 / (double)route_ui->session().nominal_frame_rate()); Glib::signal_timeout().connect(bind(mem_fun(this, &MidiRegionView::play_midi_note_off), note), note_length_ms, G_PRIORITY_DEFAULT); @@ -912,7 +911,8 @@ MidiRegionView::play_midi_note_off(boost::shared_ptr note) RouteUI* route_ui = dynamic_cast (&trackview); assert(route_ui); - route_ui->midi_track()->write_immediate_event(note->off_event().size(), note->off_event().buffer()); + route_ui->midi_track()->write_immediate_event( + note->off_event().size(), note->off_event().buffer()); return false; } @@ -930,11 +930,14 @@ MidiRegionView::add_note(const boost::shared_ptr note) assert(note->time() >= 0); assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive); + const nframes64_t note_start_frames = beats_to_frames(note->time()); + const nframes64_t note_end_frames = beats_to_frames(note->end_time()); + // dont display notes beyond the region bounds - if ( note->time() - _region->start() >= _region->length() || - note->time() < _region->start() || - note->note() < midi_stream_view()->lowest_note() || - note->note() > midi_stream_view()->highest_note() ) { + if (note_start_frames - _region->start() >= _region->length() || + note_start_frames < _region->start() || + note->note() < midi_stream_view()->lowest_note() || + note->note() > midi_stream_view()->highest_note() ) { return; } @@ -942,13 +945,12 @@ MidiRegionView::add_note(const boost::shared_ptr note) CanvasNoteEvent* event = 0; - const double x = trackview.editor().frame_to_pixel((nframes64_t)note->time() - _region->start()); + const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start()); if (midi_view()->note_mode() == Sustained) { - const double y1 = midi_stream_view()->note_to_y(note->note()); const double note_endpixel = - trackview.editor().frame_to_pixel((nframes64_t)note->end_time() - _region->start()); + trackview.editor().frame_to_pixel(note_end_frames - _region->start()); CanvasNote* ev_rect = new CanvasNote(*this, *group, note); ev_rect->property_x1() = x; @@ -996,10 +998,6 @@ MidiRegionView::add_note(const boost::shared_ptr note) } } else if (midi_view()->note_mode() == Percussive) { - - //cerr << "MRV::add_note percussive " << note->note() << " @ " << note->time() - // << " .. " << note->end_time() << endl; - const double diamond_size = midi_stream_view()->note_height() / 2.0; const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0); @@ -1054,7 +1052,6 @@ MidiRegionView::add_pgm_change(ControlEvent& program, string displaytext) void MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) { - cerr << "getting patch key at " << time << " for channel " << channel << endl; Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK); boost::shared_ptr msb_control = _model->control(bank_select_msb); @@ -1090,7 +1087,6 @@ MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::Patch void MidiRegionView::alter_program_change(ControlEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch) { - // TODO: Get the real event here and alter them at the original times Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK); boost::shared_ptr msb_control = _model->control(bank_select_msb); @@ -1132,8 +1128,7 @@ MidiRegionView::previous_program(CanvasProgramChange& program) _model_name, _custom_device_mode, program.channel(), - key - ); + key); ControlEvent program_change_event(program.event_time(), program.program(), program.channel()); if (patch) { @@ -1152,8 +1147,7 @@ MidiRegionView::next_program(CanvasProgramChange& program) _model_name, _custom_device_mode, program.channel(), - key - ); + key); ControlEvent program_change_event(program.event_time(), program.program(), program.channel()); if (patch) { alter_program_change(program_change_event, patch->patch_primary_key()); @@ -1341,28 +1335,24 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) const boost::shared_ptr copy(new NoteType(*(*i)->note().get())); // we need to snap here again in nframes64_t in order to be sample accurate - double new_note_time = (*i)->note()->time(); - new_note_time += dt; + double start_frames = snap_to_frame(beats_to_frames((*i)->note()->time()) + dt); // keep notes inside region if dragged beyond left region bound - if (new_note_time < _region->start()) { - new_note_time = _region->start(); + if (start_frames < _region->start()) { + start_frames = _region->start(); } - // since note time is region-absolute but snap_to_frame expects position-relative - // time we have to coordinate transform back and forth here. - new_note_time = snap_to_frame(nframes64_t(new_note_time) - _region->start()) + _region->start(); - - copy->set_time(new_note_time); + copy->set_time(frames_to_beats(start_frames)); uint8_t original_pitch = (*i)->note()->note(); - uint8_t new_pitch = original_pitch + dnote - highest_note_difference; + uint8_t new_pitch = original_pitch + dnote - highest_note_difference; // keep notes in standard midi range clamp_0_to_127(new_pitch); - //notes which are dragged beyond the standard midi range snap back to their original place - if ((original_pitch != 0 && new_pitch == 0) || (original_pitch != 127 && new_pitch == 127)) { + // keep original pitch if note is dragged outside valid midi range + if ((original_pitch != 0 && new_pitch == 0) + || (original_pitch != 127 && new_pitch == 127)) { new_pitch = original_pitch; } @@ -1391,13 +1381,10 @@ nframes64_t MidiRegionView::snap_to_frame(double x) { PublicEditor &editor = trackview.editor(); - // x is region relative - // convert x to global frame + // x is region relative, convert it to global absolute frames nframes64_t frame = editor.pixel_to_frame(x) + _region->position(); editor.snap_to(frame); - // convert event_frame back to local coordinates relative to position - frame -= _region->position(); - return frame; + return frame - _region->position(); // convert back to region relative } nframes64_t @@ -1426,6 +1413,22 @@ MidiRegionView::get_position_pixels() return trackview.editor().frame_to_pixel(region_frame); } +nframes64_t +MidiRegionView::beats_to_frames(double beats) const +{ + const Meter& m = trackview.session().tempo_map().meter_at(_region->position()); + const Tempo& t = trackview.session().tempo_map().tempo_at(_region->position()); + return lrint(beats * m.frames_per_bar(t, trackview.session().frame_rate()) / m.beats_per_bar()); +} + +double +MidiRegionView::frames_to_beats(nframes64_t frames) const +{ + const Meter& m = trackview.session().tempo_map().meter_at(_region->position()); + const Tempo& t = trackview.session().tempo_map().tempo_at(_region->position()); + return frames / m.frames_per_bar(t, trackview.session().frame_rate()) * m.beats_per_bar(); +} + void MidiRegionView::begin_resizing(CanvasNote::NoteEnd note_end) { @@ -1440,32 +1443,25 @@ MidiRegionView::begin_resizing(CanvasNote::NoteEnd note_end) resize_data->canvas_note = note; // create a new SimpleRect from the note which will be the resize preview - SimpleRect *resize_rect = - new SimpleRect( - *group, - note->x1(), - note->y1(), - note->x2(), - note->y2()); + SimpleRect *resize_rect = new SimpleRect( + *group, note->x1(), note->y1(), note->x2(), note->y2()); // calculate the colors: get the color settings - uint32_t fill_color = - UINT_RGBA_CHANGE_A( - ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(), - 128); + uint32_t fill_color = UINT_RGBA_CHANGE_A( + ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(), + 128); // make the resize preview notes more transparent and bright fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5); // calculate color based on note velocity - resize_rect->property_fill_color_rgba() = - UINT_INTERPOLATE( + resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE( CanvasNoteEvent::meter_style_fill_color(note->note()->velocity()), fill_color, 0.85); - resize_rect->property_outline_color_rgba() = - CanvasNoteEvent::calculate_outline(ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get()); + resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline( + ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get()); resize_data->resize_rect = resize_rect; @@ -1484,8 +1480,8 @@ void MidiRegionView::update_resizing(CanvasNote::NoteEnd note_end, double x, bool relative) { for (std::vector::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) { - SimpleRect *resize_rect = (*i)->resize_rect; - CanvasNote *canvas_note = (*i)->canvas_note; + SimpleRect* resize_rect = (*i)->resize_rect; + CanvasNote* canvas_note = (*i)->canvas_note; const double region_start = get_position_pixels(); diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 16b11e176b..5c00175cbf 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -150,13 +150,13 @@ class MidiRegionView : public RegionView /** Displays all program changed events in the region as flags on the canvas. */ - void find_and_insert_program_change_flags(); + void display_program_change_flags(); void begin_write(); void end_write(); void extend_active_notes(); - void create_note_at(double x, double y, double duration); + void create_note_at(double x, double y, double length); void display_model(boost::shared_ptr model); @@ -175,19 +175,16 @@ class MidiRegionView : public RegionView size_t selection_size() { return _selection.size(); } void move_selection(double dx, double dy); - void note_dropped(ArdourCanvas::CanvasNoteEvent* ev, double dt, uint8_t dnote); + void note_dropped(ArdourCanvas::CanvasNoteEvent* ev, double d_frames, uint8_t d_note); - /** Get the region position in pixels. - * This function is needed to subtract the region start in pixels - * from world coordinates submitted by the mouse - */ + /** Get the region position in pixels relative to session. */ double get_position_pixels(); /** Begin resizing of some notes. * Called by CanvasMidiNote when resizing starts. * @param note_end which end of the note, NOTE_ON or NOTE_OFF */ - void begin_resizing(ArdourCanvas::CanvasNote::NoteEnd note_end); + void begin_resizing(ArdourCanvas::CanvasNote::NoteEnd note_end); /** Update resizing notes while user drags. * @param note_end which end of the note, NOTE_ON or NOTE_OFF @@ -215,7 +212,15 @@ class MidiRegionView : public RegionView */ void change_channel(uint8_t channel); - enum MouseState { None, Pressed, SelectTouchDragging, SelectRectDragging, AddDragging, EraseTouchDragging }; + enum MouseState { + None, + Pressed, + SelectTouchDragging, + SelectRectDragging, + AddDragging, + EraseTouchDragging + }; + MouseState mouse_state() const { return _mouse_state; } struct NoteResizeData { @@ -243,8 +248,13 @@ class MidiRegionView : public RegionView */ nframes64_t snap_to_frame(nframes64_t x); + /** Convert a timestamp in beats to frames (both relative to region start) */ + nframes64_t beats_to_frames(double beats) const; + + /** Convert a timestamp in frames to beats (both relative to region start) */ + double frames_to_beats(nframes64_t beats) const; + protected: - /** Allows derived types to specify their visibility requirements * to the TimeAxisViewItem parent class. */ @@ -272,7 +282,7 @@ class MidiRegionView : public RegionView * (scheduled by @ref play_midi_note()). */ bool play_midi_note_off(boost::shared_ptr note); - + void clear_events(); void switch_source(boost::shared_ptr src); @@ -282,7 +292,7 @@ class MidiRegionView : public RegionView void midi_channel_mode_changed(ARDOUR::ChannelMode mode, uint16_t mask); void midi_patch_settings_changed(std::string model, std::string custom_device_mode); - void change_note_velocity(ArdourCanvas::CanvasNoteEvent* ev, int8_t velocity, bool relative=false); + void change_note_velocity(ArdourCanvas::CanvasNoteEvent* ev, int8_t vel, bool relative=false); void clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev); void clear_selection() { clear_selection_except(NULL); } diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index 2126b4b162..f5c660d9cb 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -57,10 +57,12 @@ class MidiSource : public Source virtual uint32_t n_channels () const { return 1; } // FIXME: integrate this with the Readable::read interface somehow - virtual nframes_t midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset, nframes_t negative_stamp_offset) const; + virtual nframes_t midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, + nframes_t stamp_offset, nframes_t negative_stamp_offset) const; virtual nframes_t midi_write (MidiRingBuffer& src, nframes_t cnt); - virtual void append_event_unlocked(EventTimeUnit unit, const Evoral::Event& ev) = 0; + virtual void append_event_unlocked_beats(const Evoral::Event& ev) = 0; + virtual void append_event_unlocked_frames(const Evoral::Event& ev) = 0; virtual void mark_for_remove() = 0; virtual void mark_streaming_midi_write_started (NoteMode mode, nframes_t start_time); @@ -95,6 +97,7 @@ class MidiSource : public Source boost::shared_ptr model() { return _model; } void set_model(boost::shared_ptr m) { _model = m; } + void drop_model() { _model.reset(); } protected: virtual void flush_midi() = 0; diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h index 82f41ebf2d..6cbb449633 100644 --- a/libs/ardour/ardour/smf_source.h +++ b/libs/ardour/ardour/smf_source.h @@ -63,8 +63,9 @@ class SMFSource : public MidiSource, public Evoral::SMF { void set_allow_remove_if_empty (bool yn); void mark_for_remove(); - - void append_event_unlocked(EventTimeUnit unit, const Evoral::Event& ev); + + void append_event_unlocked_beats(const Evoral::Event& ev); + void append_event_unlocked_frames(const Evoral::Event& ev); int move_to_trash (const string trash_dir_name); @@ -83,8 +84,6 @@ class SMFSource : public MidiSource, public Evoral::SMF { void load_model(bool lock=true, bool force_reload=false); void destroy_model(); - double last_event_time() const { return _last_ev_time; } - void flush_midi(); private: @@ -111,7 +110,8 @@ class SMFSource : public MidiSource, public Evoral::SMF { Flag _flags; string _take_id; bool _allow_remove_if_empty; - double _last_ev_time; + double _last_ev_time_beats; + nframes_t _last_ev_time_frames; static string _search_path; }; diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 7532c63312..bbdf0da1d6 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -148,11 +148,6 @@ namespace ARDOUR { TrackColor }; - enum EventTimeUnit { - Frames, - Beats - }; - struct BBT_Time { uint32_t bars; uint32_t beats; diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 0c8e21b503..0064fd0b8e 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -86,11 +86,11 @@ struct SizedSampleBuffer { Glib::StaticPrivate thread_interleave_buffer = GLIBMM_STATIC_PRIVATE_INIT; +/** Constructor used for existing internal-to-session files. File must exist. */ AudioFileSource::AudioFileSource (Session& s, ustring path, Flag flags) : AudioSource (s, path), _flags (flags), _channel (0) { - /* constructor used for existing external to session files. file must exist already */ _is_embedded = AudioFileSource::determine_embeddedness (path); if (init (path, true)) { @@ -99,11 +99,11 @@ AudioFileSource::AudioFileSource (Session& s, ustring path, Flag flags) } +/** Constructor used for new internal-to-session files. File cannot exist. */ AudioFileSource::AudioFileSource (Session& s, ustring path, Flag flags, SampleFormat samp_format, HeaderFormat hdr_format) : AudioSource (s, path), _flags (flags), _channel (0) { - /* constructor used for new internal-to-session files. file cannot exist */ _is_embedded = false; if (init (path, false)) { @@ -111,12 +111,11 @@ AudioFileSource::AudioFileSource (Session& s, ustring path, Flag flags, SampleFo } } +/** Constructor used for existing internal-to-session files. File must exist. */ AudioFileSource::AudioFileSource (Session& s, const XMLNode& node, bool must_exist) : AudioSource (s, node), _flags (Flag (Writable|CanRename)) - /* _channel is set in set_state() or init() */ + /* _channel is set in set_state() or init() */ { - /* constructor used for existing internal-to-session files. file must exist */ - if (set_state (node)) { throw failed_constructor (); } @@ -363,31 +362,23 @@ AudioFileSource::move_to_trash (const ustring& trash_dir_name) return -1; } - ustring newpath; - if (!writable()) { return -1; } - /* don't move the file across filesystems, just - stick it in the `trash_dir_name' directory - on whichever filesystem it was already on. + /* don't move the file across filesystems, just stick it in the + trash_dir_name directory on whichever filesystem it was already on */ + ustring newpath; newpath = Glib::path_get_dirname (_path); newpath = Glib::path_get_dirname (newpath); - cerr << "from " << _path << " dead dir looks like " << newpath << endl; - - newpath += '/'; - newpath += trash_dir_name; - newpath += '/'; + newpath += string("/") + trash_dir_name + "/"; newpath += Glib::path_get_basename (_path); + /* the new path already exists, try versioning */ if (access (newpath.c_str(), F_OK) == 0) { - - /* the new path already exists, try versioning */ - char buf[PATH_MAX+1]; int version = 1; ustring newpath_v; @@ -401,23 +392,19 @@ AudioFileSource::move_to_trash (const ustring& trash_dir_name) } if (version == 999) { - error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"), - newpath) - << endmsg; + PBD::error << string_compose ( + _("there are already 1000 files with names like %1; versioning discontinued"), + newpath) + << endmsg; } else { newpath = newpath_v; } - - } else { - - /* it doesn't exist, or we can't read it or something */ - } if (::rename (_path.c_str(), newpath.c_str()) != 0) { - error << string_compose (_("cannot rename audio file source from %1 to %2 (%3)"), - _path, newpath, strerror (errno)) - << endmsg; + PBD::error << string_compose ( + _("cannot rename midi file source from %1 to %2 (%3)"), + _path, newpath, strerror (errno)) << endmsg; return -1; } @@ -434,7 +421,6 @@ AudioFileSource::move_to_trash (const ustring& trash_dir_name) peakpath = ""; /* file can not be removed twice, since the operation is not idempotent */ - _flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty)); return 0; @@ -467,7 +453,6 @@ AudioFileSource::find (ustring& pathstr, bool must_exist, bool& isnew, uint16_t& cnt = 0; for (vector::iterator i = dirs.begin(); i != dirs.end(); ++i) { - fullpath = *i; if (fullpath[fullpath.length()-1] != '/') { fullpath += '/'; @@ -544,15 +529,14 @@ AudioFileSource::find (ustring& pathstr, bool must_exist, bool& isnew, uint16_t& } /* Current find() is unable to parse relative path names to yet non-existant - sources. QuickFix(tm) */ - if (keeppath == "") { - if (must_exist) { - error << "AudioFileSource::find(), keeppath = \"\", but the file must exist" << endl; - } else { - keeppath = pathstr; - } - - } + sources. QuickFix(tm) */ + if (keeppath == "") { + if (must_exist) { + error << "AudioFileSource::find(), keeppath = \"\", but the file must exist" << endl; + } else { + keeppath = pathstr; + } + } _name = pathstr; _path = keeppath; diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index 1b6d604d8f..824cdb43f7 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -320,8 +320,8 @@ write_midi_data_to_new_files (Evoral::SMF* source, Session::import_status& statu try { for (unsigned i = 1; i <= source->num_tracks(); ++i) { - boost::shared_ptr smfs = boost::dynamic_pointer_cast(newfiles[i-1]); + smfs->drop_model(); source->seek_to_track(i); @@ -346,11 +346,10 @@ write_midi_data_to_new_files (Evoral::SMF* source, Session::import_status& statu continue; } - smfs->append_event_unlocked(Beats, Evoral::Event( - 0, - (double)t / (double)source->ppqn(), - size, - buf)); + smfs->append_event_unlocked_beats(Evoral::Event(0, + (double)t / (double)source->ppqn(), + size, + buf)); if (status.progress < 0.99) status.progress += 0.01; diff --git a/libs/ardour/meter.cc b/libs/ardour/meter.cc index 31a88b16f0..af6b5a0a36 100644 --- a/libs/ardour/meter.cc +++ b/libs/ardour/meter.cc @@ -44,7 +44,6 @@ PeakMeter::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_f // Meter what we have (midi) for ( ; n < limit; ++n) { - float val = 0; // GUI needs a better MIDI meter, not much information can be diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index 4e755d8717..6573bcfd68 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -295,9 +295,11 @@ MidiModel::write_to(boost::shared_ptr source) const bool old_percussive = percussive(); set_percussive(false); + + source->drop_model(); for (Evoral::Sequence::const_iterator i = begin(); i != end(); ++i) { - source->append_event_unlocked(Frames, *i); + source->append_event_unlocked_beats(*i); } set_percussive(old_percussive); diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index 4307749e4a..e5b1d813c5 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -32,11 +32,13 @@ #include #include -#include +#include #include +#include #include #include #include +#include #include "i18n.h" @@ -105,28 +107,39 @@ MidiSource::set_state (const XMLNode& node) } nframes_t -MidiSource::midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset, nframes_t negative_stamp_offset) const +MidiSource::midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, + nframes_t stamp_offset, nframes_t negative_stamp_offset) const { + Glib::Mutex::Lock lm (_lock); if (_model) { + // FIXME: assumes tempo never changes after start + const Tempo& tempo = _session.tempo_map().tempo_at(_timeline_position); + const double frames_per_beat = tempo.frames_per_beat( + _session.engine().frame_rate(), + _session.tempo_map().meter_at(_timeline_position)); +#define BEATS_TO_FRAMES(t) (((t) * frames_per_beat) + stamp_offset - negative_stamp_offset) + Evoral::Sequence::const_iterator& i = _model_iter; if (_last_read_end == 0 || start != _last_read_end) { - i = _model->begin(); - cerr << "MidiSource::midi_read seeking to " << start << endl; - while (i != _model->end() && i->time() < start) - ++i; + cerr << "MidiSource::midi_read seeking to frame " << start << endl; + for (i = _model->begin(); i != _model->end(); ++i) { + if (BEATS_TO_FRAMES(i->time()) >= start) { + break; + } + } } _last_read_end = start + cnt; - if (i == _model->end()) { - return cnt; - } - - while (i->time() < start + cnt && i != _model->end()) { - dst.write(i->time(), i->event_type(), i->size(), i->buffer()); - ++i; + for (; i != _model->end(); ++i) { + const nframes_t time_frames = BEATS_TO_FRAMES(i->time()); + if (time_frames < start + cnt) { + dst.write(time_frames, i->event_type(), i->size(), i->buffer()); + } else { + break; + } } return cnt; } else { @@ -148,7 +161,7 @@ MidiSource::file_changed (string path) int e1 = stat (path.c_str(), &stat_file); - return ( !e1 ); + return !e1; } void diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index efa99c2c00..7072cc8634 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -48,15 +48,15 @@ using namespace ARDOUR; string SMFSource::_search_path; +/** Constructor used for new internal-to-session files. File cannot exist. */ SMFSource::SMFSource(Session& s, std::string path, Flag flags) : MidiSource(s, region_name_from_path(path, false)) , Evoral::SMF() , _flags(flags) , _allow_remove_if_empty(true) - , _last_ev_time(0) + , _last_ev_time_beats(0.0) + , _last_ev_time_frames(0) { - /* Constructor used for new internal-to-session files. File cannot exist. */ - if (init(path, false)) { throw failed_constructor (); } @@ -68,14 +68,14 @@ SMFSource::SMFSource(Session& s, std::string path, Flag flags) assert(_name.find("/") == string::npos); } +/** Constructor used for existing internal-to-session files. File must exist. */ SMFSource::SMFSource(Session& s, const XMLNode& node) : MidiSource(s, node) , _flags(Flag(Writable|CanRename)) , _allow_remove_if_empty(true) - , _last_ev_time(0) + , _last_ev_time_beats(0.0) + , _last_ev_time_frames(0) { - /* Constructor used for existing internal-to-session files. File must exist. */ - if (set_state(node)) { throw failed_constructor (); } @@ -146,7 +146,6 @@ SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nfram // FIXME: assumes tempo never changes after start const Tempo& tempo = _session.tempo_map().tempo_at(_timeline_position); - const double frames_per_beat = tempo.frames_per_beat( _session.engine().frame_rate(), _session.tempo_map().meter_at(_timeline_position)); @@ -205,7 +204,7 @@ SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nfram /** All stamps in audio frames */ nframes_t -SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) +SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t dur) { _write_data_count = 0; @@ -220,11 +219,11 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) _model->start_write(); } - Evoral::MIDIEvent ev(0, 0.0, 4, NULL, true); + Evoral::MIDIEvent ev(0, 0.0, 4, NULL, true); while (true) { bool ret = src.peek_time(&time); - if (!ret || time - _timeline_position > _length + cnt) { + if (!ret || time - _timeline_position > _length + dur) { break; } @@ -255,11 +254,7 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) continue; } - append_event_unlocked(Frames, ev); - - if (_model) { - _model->append(ev); - } + append_event_unlocked_frames(ev); } if (_model) { @@ -270,55 +265,83 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) free(buf); const nframes_t oldlen = _length; - update_length(oldlen, cnt); + update_length(oldlen, dur); - ViewDataRangeReady(_timeline_position + oldlen, cnt); /* EMIT SIGNAL */ - - return cnt; + ViewDataRangeReady(_timeline_position + oldlen, dur); /* EMIT SIGNAL */ + + return dur; } +/** Append an event with a timestamp in beats (double) */ void -SMFSource::append_event_unlocked(EventTimeUnit unit, const Evoral::Event& ev) +SMFSource::append_event_unlocked_beats(const Evoral::Event& ev) { if (ev.size() == 0) { - cerr << "SMFSource: Warning: skipping empty event" << endl; return; } - /* - printf("SMFSource: %s - append_event_unlocked time = %lf, size = %u, data = ", + /*printf("SMFSource: %s - append_event_unlocked_beats time = %lf, size = %u, data = ", name().c_str(), ev.time(), ev.size()); - for (size_t i=0; i < ev.size(); ++i) { - printf("%X ", ev.buffer()[i]); - } printf("\n"); - */ + for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/ assert(ev.time() >= 0); - - if (ev.time() < last_event_time()) { - cerr << "SMFSource: Warning: Skipping event with ev.time() < last.time()" << endl; + if (ev.time() < _last_ev_time_beats) { + cerr << "SMFSource: Warning: Skipping event with non-monotonic time" << endl; return; } - uint32_t delta_time = 0; - - if (unit == Frames) { - // FIXME: assumes tempo never changes after start - const double frames_per_beat = _session.tempo_map().tempo_at(_timeline_position).frames_per_beat( - _session.engine().frame_rate(), - _session.tempo_map().meter_at(_timeline_position)); + const double delta_time_beats = ev.time() - _last_ev_time_beats; + const uint32_t delta_time_ticks = (uint32_t)lrint(delta_time_beats * (double)ppqn()); - delta_time = (uint32_t)((ev.time() - last_event_time()) / frames_per_beat * ppqn()); - } else { - assert(unit == Beats); - delta_time = (uint32_t)((ev.time() - last_event_time()) * ppqn()); - } - - Evoral::SMF::append_event_delta(delta_time, ev.size(), ev.buffer()); - _last_ev_time = ev.time(); + Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer()); + _last_ev_time_beats = ev.time(); _write_data_count += ev.size(); + + if (_model) { + _model->append(ev); + } +} + +/** Append an event with a timestamp in frames (nframes_t) */ +void +SMFSource::append_event_unlocked_frames(const Evoral::Event& ev) +{ + if (ev.size() == 0) { + return; + } + + /*printf("SMFSource: %s - append_event_unlocked_frames time = %u, size = %u, data = ", + name().c_str(), ev.time(), ev.size()); + for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/ + + assert(ev.time() >= 0); + if (ev.time() < _last_ev_time_frames) { + cerr << "SMFSource: Warning: Skipping event with non-monotonic time" << endl; + return; + } + + // FIXME: assumes tempo never changes after start + const Tempo& tempo = _session.tempo_map().tempo_at(_timeline_position); + const double frames_per_beat = tempo.frames_per_beat( + _session.engine().frame_rate(), + _session.tempo_map().meter_at(_timeline_position)); + + uint32_t delta_time = (uint32_t)((ev.time() - _last_ev_time_frames) + / frames_per_beat * (double)ppqn()); + + Evoral::SMF::append_event_delta(delta_time, ev.size(), ev.buffer()); + _last_ev_time_frames = ev.time(); + + _write_data_count += ev.size(); + + if (_model) { + double beat_time = ev.time() / frames_per_beat; + const Evoral::Event beat_ev( + ev.event_type(), beat_time, ev.size(), (uint8_t*)ev.buffer()); + _model->append(beat_ev); + } } @@ -368,7 +391,8 @@ SMFSource::mark_streaming_midi_write_started (NoteMode mode, nframes_t start_fra { MidiSource::mark_streaming_midi_write_started (mode, start_frame); Evoral::SMF::begin_write (); - _last_ev_time = 0; + _last_ev_time_beats = 0.0; + _last_ev_time_frames = 0; } void @@ -395,29 +419,23 @@ SMFSource::mark_take (string id) int SMFSource::move_to_trash (const string trash_dir_name) { - string newpath; - if (!writable()) { return -1; } - /* don't move the file across filesystems, just - stick it in the 'trash_dir_name' directory - on whichever filesystem it was already on. + /* don't move the file across filesystems, just stick it in the + trash_dir_name directory on whichever filesystem it was already on */ - + + Glib::ustring newpath; newpath = Glib::path_get_dirname (_path); - newpath = Glib::path_get_dirname (newpath); + newpath = Glib::path_get_dirname (newpath); - newpath += '/'; - newpath += trash_dir_name; - newpath += '/'; + newpath += string("/") + trash_dir_name + "/"; newpath += Glib::path_get_basename (_path); + /* the new path already exists, try versioning */ if (access (newpath.c_str(), F_OK) == 0) { - - /* the new path already exists, try versioning */ - char buf[PATH_MAX+1]; int version = 1; string newpath_v; @@ -431,28 +449,24 @@ SMFSource::move_to_trash (const string trash_dir_name) } if (version == 999) { - PBD::error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"), - newpath) - << endmsg; + PBD::error << string_compose ( + _("there are already 1000 files with names like %1; versioning discontinued"), + newpath) << endmsg; } else { newpath = newpath_v; } - - } else { - - /* it doesn't exist, or we can't read it or something */ - } if (::rename (_path.c_str(), newpath.c_str()) != 0) { - PBD::error << string_compose (_("cannot rename midi file source from %1 to %2 (%3)"), - _path, newpath, strerror (errno)) - << endmsg; + PBD::error << string_compose ( + _("cannot rename midi file source from %1 to %2 (%3)"), + _path, newpath, strerror (errno)) << endmsg; return -1; } + _path = newpath; + /* file can not be removed twice, since the operation is not idempotent */ - _flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty)); return 0; @@ -468,19 +482,10 @@ SMFSource::safe_file_extension(const Glib::ustring& file) bool SMFSource::find (string pathstr, bool must_exist, bool& isnew) { - string::size_type pos; bool ret = false; isnew = false; - /* clean up PATH:CHANNEL notation so that we are looking for the correct path */ - - if ((pos = pathstr.find_last_of (':')) == string::npos) { - pathstr = pathstr; - } else { - pathstr = pathstr.substr (0, pos); - } - if (pathstr[0] != '/') { /* non-absolute pathname: find pathstr in search path */ @@ -500,7 +505,6 @@ SMFSource::find (string pathstr, bool must_exist, bool& isnew) cnt = 0; for (vector::iterator i = dirs.begin(); i != dirs.end(); ++i) { - fullpath = *i; if (fullpath[fullpath.length()-1] != '/') { fullpath += '/'; @@ -640,24 +644,15 @@ SMFSource::load_model(bool lock, bool force_reload) size_t scratch_size = 0; // keep track of scratch and minimize reallocs - // FIXME: assumes tempo never changes after start - const Tempo& tempo = _session.tempo_map().tempo_at(_timeline_position); - - const double frames_per_beat = tempo.frames_per_beat( - _session.engine().frame_rate(), - _session.tempo_map().meter_at(_timeline_position)); - uint32_t delta_t = 0; uint32_t size = 0; uint8_t* buf = NULL; int ret; while ((ret = read_event(&delta_t, &size, &buf)) >= 0) { - ev.set(buf, size, 0.0); time += delta_t; + ev.set(buf, size, time / (double)ppqn()); if (ret > 0) { // didn't skip (meta) event - // make ev.time absolute time in frames - ev.time() = time * frames_per_beat / (double)ppqn(); ev.set_event_type(EventTypeMap::instance().midi_event_type(buf[0])); _model->append(ev); }