The Big Change: Store time in MidiModel as tempo time, not frame time.
authorDavid Robillard <d@drobilla.net>
Sun, 15 Feb 2009 17:30:42 +0000 (17:30 +0000)
committerDavid Robillard <d@drobilla.net>
Sun, 15 Feb 2009 17:30:42 +0000 (17:30 +0000)
The time stamp of an event is now always tempo, from file to model and
back again.  Frame time is only relevant at playback or recording time,
in the audio thread (MidiModel and MidiBuffer).

I think perhaps we don't need to change the actual time from double (which is
convenient for math), it is the time base conversion that caused problems.
Using a correct equality comparison (i.e.  not == which is not correct for
floating point) should probably make the undo issues go away, in 99.99% of
cases anyway.

There's almost certainly some regressions in here somewhere, but they do not
seem to be time related.  The bugs I'm hitting in testing are old ones that
seem unrelated now, so it's checkpoint time.

This sets us up for fancy things like tempo map import and tempo/meter changes
halfway through MIDI regions, but for now it's still assumed that the tempo
at the start of the region is valid for the duration of the entire region.

git-svn-id: svn://localhost/ardour2/branches/3.0@4582 d708f5d6-7413-0410-9779-e7cbd77b26cf

14 files changed:
gtk2_ardour/canvas-note-event.cc
gtk2_ardour/canvas-program-change.cc
gtk2_ardour/editor_rulers.cc
gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_region_view.h
libs/ardour/ardour/midi_source.h
libs/ardour/ardour/smf_source.h
libs/ardour/ardour/types.h
libs/ardour/audiofilesource.cc
libs/ardour/import.cc
libs/ardour/meter.cc
libs/ardour/midi_model.cc
libs/ardour/midi_source.cc
libs/ardour/smf_source.cc

index aa296d1fb280fd3c62fb88e372944893a431ee45..a81692d01e9d3c40cb513471ac88f3b39a0f4794 100644 (file)
@@ -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;
index d544d9d8aa21f1109e2f005feaeea8ff0f992fa5..badadd22a7bbd44c47a1f6f8abdbb056451cf73d 100644 (file)
@@ -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)
index 0d355832f235caa124795e8bf710e1928462f55b..0835cd81abb5e7ff3adf19f7ae4e80f052b58567 100644 (file)
@@ -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;
 }
index a090c1e566f27f5cb7eb73feeac819c099e7b781..8d688fa0826ed437c29a1b27f00dfd1a0b025f6f 100644 (file)
@@ -64,11 +64,12 @@ using namespace PBD;
 using namespace Editing;
 using namespace ArdourCanvas;
 
-MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color)
+MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
+               boost::shared_ptr<MidiRegion> 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<MidiRegion> r, double spu, Gdk::Color& basic_color, TimeAxisViewItem::Visibility visibility)
+MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
+               boost::shared_ptr<MidiRegion> 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<MidiRegion> other_region)
-       : RegionView (other, boost::shared_ptr<Region> (other_region))
+MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
+       : RegionView (other, boost::shared_ptr<Region> (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<NoteType> new_note(new NoteType(0,
+                       frames_to_beats(start_frames + _region->start()), length,
+                       (uint8_t)note, 0x40));
 
-       const boost::shared_ptr<NoteType> 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<MidiModel> 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<NoteType> 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<NoteType> note)
        RouteUI* route_ui = dynamic_cast<RouteUI*> (&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<NoteType> note)
        RouteUI* route_ui = dynamic_cast<RouteUI*> (&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<NoteType> 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<NoteType> 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<NoteType> 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<Evoral::Control>  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<Evoral::Control>  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<NoteType> 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<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
-               SimpleRect     *resize_rect = (*i)->resize_rect;
-               CanvasNote     *canvas_note = (*i)->canvas_note;
+               SimpleRectresize_rect = (*i)->resize_rect;
+               CanvasNotecanvas_note = (*i)->canvas_note;
 
                const double region_start = get_position_pixels();
 
index 16b11e176b34e1022618d75e66feaf7250709770..5c00175cbf00683018d61238c12f89ab92d675b4 100644 (file)
@@ -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<ARDOUR::MidiModel> 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<NoteType> note);
-         
+
        void clear_events();
        void switch_source(boost::shared_ptr<ARDOUR::Source> 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); }
index 2126b4b162324740b9844a0c0d27c0573b6282e5..f5c660d9cb1b2ea16b7acf248ee18536cc7a04af 100644 (file)
@@ -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<nframes_t>& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset, nframes_t negative_stamp_offset) const;
+       virtual nframes_t midi_read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t cnt,
+                       nframes_t stamp_offset, nframes_t negative_stamp_offset) const;
        virtual nframes_t midi_write (MidiRingBuffer<nframes_t>& src, nframes_t cnt);
 
-       virtual void append_event_unlocked(EventTimeUnit unit, const Evoral::Event<TimeType>& ev) = 0;
+       virtual void append_event_unlocked_beats(const Evoral::Event<double>& ev) = 0;
+       virtual void append_event_unlocked_frames(const Evoral::Event<nframes_t>& 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<MidiModel> model() { return _model; }
        void set_model(boost::shared_ptr<MidiModel> m) { _model = m; }
+       void drop_model() { _model.reset(); }
 
   protected:
        virtual void flush_midi() = 0;
index 82f41ebf2d74a906d9de7f9d22f77a88a7c13cd6..6cbb449633f5960ce97c6e802c5d5eee7de852bf 100644 (file)
@@ -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<double>& ev);
+       
+       void append_event_unlocked_beats(const Evoral::Event<double>& ev);
+       void append_event_unlocked_frames(const Evoral::Event<nframes_t>& 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;
 };
index 7532c63312e762215bfae821516ecafe981898c4..bbdf0da1d68b8bbf4cfbcbb7c5c783a923dd0c58 100644 (file)
@@ -148,11 +148,6 @@ namespace ARDOUR {
                TrackColor
        };
 
-       enum EventTimeUnit {
-               Frames,
-               Beats
-       };
-
        struct BBT_Time {
            uint32_t bars;
            uint32_t beats;
index 0c8e21b50364709e639f0e0ee9a2355317f3e387..0064fd0b8eec05065177e53c1dc37cf9c2c55558 100644 (file)
@@ -86,11 +86,11 @@ struct SizedSampleBuffer {
 
 Glib::StaticPrivate<SizedSampleBuffer> 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<ustring>::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;
index 1b6d604d8f5832c6e831bd40bc97cba66f27a679..824cdb43f713fb24999861a28e86fe38def7fbbe 100644 (file)
@@ -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<SMFSource> smfs = boost::dynamic_pointer_cast<SMFSource>(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<double>(
-                                               0,
-                                               (double)t / (double)source->ppqn(),
-                                               size,
-                                               buf));
+                       smfs->append_event_unlocked_beats(Evoral::Event<double>(0,
+                                       (double)t / (double)source->ppqn(),
+                                       size,
+                                       buf));
 
                        if (status.progress < 0.99)
                                status.progress += 0.01;
index 31a88b16f0f206bcf121c9b6861bc0679aa6cc45..af6b5a0a361f7390de0fab36a282bcbe94fe049c 100644 (file)
@@ -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
index 4e755d8717d53666a494a3f575bf332e13d79e51..6573bcfd68bdbac4317a11f77d1d4034e2db7b2d 100644 (file)
@@ -295,9 +295,11 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
 
        const bool old_percussive = percussive();
        set_percussive(false);
+
+       source->drop_model();
        
        for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
-               source->append_event_unlocked(Frames, *i);
+               source->append_event_unlocked_beats(*i);
        }
                
        set_percussive(old_percussive);
index 4307749e4aa7911bbdfb4e6e4e9ebbde6a54beb7..e5b1d813c503a607caeffb5db7c4339fa2441b5c 100644 (file)
 #include <pbd/pthread_utils.h>
 #include <pbd/basename.h>
 
-#include <ardour/midi_source.h>
+#include <ardour/audioengine.h>
 #include <ardour/midi_ring_buffer.h>
+#include <ardour/midi_source.h>
 #include <ardour/session.h>
 #include <ardour/session_directory.h>
 #include <ardour/source_factory.h>
+#include <ardour/tempo.h>
 
 #include "i18n.h"
 
@@ -105,28 +107,39 @@ MidiSource::set_state (const XMLNode& node)
 }
 
 nframes_t
-MidiSource::midi_read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset, nframes_t negative_stamp_offset) const
+MidiSource::midi_read (MidiRingBuffer<nframes_t>& 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<double>::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
index efa99c2c00eb09ea58a24051f8cb91f4802e595e..7072cc8634c93bf4b99d6615466bb30dc5056902 100644 (file)
@@ -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<nframes_t>& 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<nframes_t>& dst, nframes_t start, nfram
 
 /** All stamps in audio frames */
 nframes_t
-SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t cnt)
+SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t dur)
 {
        _write_data_count = 0;
                
@@ -220,11 +219,11 @@ SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t cnt)
                _model->start_write();
        }
 
-       Evoral::MIDIEvent<double> ev(0, 0.0, 4, NULL, true);
+       Evoral::MIDIEvent<nframes_t> 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<nframes_t>& 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<nframes_t>& 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<double>& ev)
+SMFSource::append_event_unlocked_beats(const Evoral::Event<double>& 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_ev_time_beats) {
+               cerr << "SMFSource: Warning: Skipping event with non-monotonic time" << endl;
+               return;
+       }
        
-       if (ev.time() < last_event_time()) {
-               cerr << "SMFSource: Warning: Skipping event with ev.time() < last.time()" << endl;
+       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());
+
+       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<nframes_t>& 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");*/
        
-       uint32_t delta_time = 0;
+       assert(ev.time() >= 0);
+       if (ev.time() < _last_ev_time_frames) {
+               cerr << "SMFSource: Warning: Skipping event with non-monotonic time" << endl;
+               return;
+       }
        
-       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));
+       // 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));
 
-               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());
-       }
+       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 = ev.time();
+       _last_ev_time_frames = ev.time();
 
        _write_data_count += ev.size();
+
+       if (_model) {
+               double beat_time = ev.time() / frames_per_beat;
+               const Evoral::Event<double> 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<string>::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);
                }