diff options
author | David Robillard <d@drobilla.net> | 2007-10-14 05:45:31 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2007-10-14 05:45:31 +0000 |
commit | 14dcc3f0170f8c723bcef0a59562adce0e8e7596 (patch) | |
tree | ca173319a68bd575a0721236df90d14a57cbda86 | |
parent | 15af67c179ce336867c8f783388384f3ce214a99 (diff) |
Fix displaying of notes in auto-created MIDI region when it's the first region in the track.
Fix crash after recording long phrases of MIDI.
Fix MIDI looping (kinda).
Add note-off exposure to MidiModel::iterator.
Fix first-note-is-stuck-note problem.
Fix resolving long notes while recording.
Fix several other things I forget now. MIDI works pretty well.....
git-svn-id: svn://localhost/ardour2/trunk@2555 d708f5d6-7413-0410-9779-e7cbd77b26cf
29 files changed, 532 insertions, 385 deletions
diff --git a/gtk2_ardour/ardour.bindings.in b/gtk2_ardour/ardour.bindings.in index e8a918cb1f..c7ead238bb 100644 --- a/gtk2_ardour/ardour.bindings.in +++ b/gtk2_ardour/ardour.bindings.in @@ -171,6 +171,7 @@ ; (gtk_accel_path "<Actions>/Transport/TogglePunchOut" "") (gtk_accel_path "<Actions>/Editor/select-all-in-loop-range" "<Control>l") (gtk_accel_path "<Actions>/Editor/show-editor-mixer" "<Shift>e") +(gtk_accel_path "<Actions>/Editor/show-editor-list" "<Shift>l") ; (gtk_accel_path "<Actions>/options/SoloInPlace" "") ; (gtk_accel_path "<Actions>/Main/Options" "") ; (gtk_accel_path "<Actions>/options/MeterFalloffMedium" "") diff --git a/gtk2_ardour/ardour.menus b/gtk2_ardour/ardour.menus index a24041374b..871aa15aee 100644 --- a/gtk2_ardour/ardour.menus +++ b/gtk2_ardour/ardour.menus @@ -208,6 +208,7 @@ <menuitem action='ToggleMeasureVisibility'/> <separator/> <menuitem action='show-editor-mixer'/> + <menuitem action='show-editor-list'/> <menuitem action='SyncEditorAndMixerTrackOrder'/> </menu> <menu name='JACK' action='JACK'> diff --git a/gtk2_ardour/ardour2_ui_default.conf b/gtk2_ardour/ardour2_ui_default.conf index 034dd75b0f..721d8c3116 100644 --- a/gtk2_ardour/ardour2_ui_default.conf +++ b/gtk2_ardour/ardour2_ui_default.conf @@ -4,20 +4,20 @@ <Option name="waveform" value="000000cc"/> <Option name="clipped waveform" value="ff0000e5"/> <Option name="region base" value="bfbfc1aa"/> - <Option name="selected region base" value="b591a8ff"/> + <Option name="selected region base" value="8888ffaa"/> <Option name="audio track base" value="c6d3d868"/> <Option name="audio bus base" value="dbd1ea68"/> - <Option name="midi track base" value="ff8f8f3d"/> - <Option name="midi bus base" value="ff0000ee"/> + <Option name="midi track base" value="c67e7e5f"/> + <Option name="midi bus base" value="ffceea40"/> <Option name="time-stretch-fill" value="e2b5b596"/> <Option name="time-stretch-outline" value="63636396"/> <Option name="automation line" value="44bc59ff"/> <Option name="processor automation line" value="7aa3f9ff"/> - <Option name="control point fill" value="000000ff"/> - <Option name="control point outline" value="000000ff"/> + <Option name="control point fill" value="ffffff66"/> + <Option name="control point outline" value="ffffffaa"/> <Option name="entered control point outline" value="ff0000ee"/> <Option name="entered control point selected" value="ff3535ff"/> - <Option name="entered control point" value="000000cc"/> + <Option name="entered control point" value="ffffffaa"/> <Option name="control point selected" value="00ff00ff"/> <Option name="control point" value="ff0000ff"/> <Option name="automation track fill" value="a0a0ce68"/> @@ -40,7 +40,7 @@ <Option name="location punch" value="7c3a3aff"/> <Option name="verbose canvas cursor" value="f4f214bc"/> <Option name="marker label" value="000000ff"/> - <Option name="marker bar separator" value="30303088"/> + <Option name="marker bar separator" value="aaaaaa77"/> <Option name="tempo bar" value="72727fff"/> <Option name="meterbar" value="666672ff"/> <Option name="markerbar" value="7f7f8cff"/> @@ -60,8 +60,8 @@ <Option name="EnteredMarker" value="dd6363ff"/> <Option name="MeterMarker" value="f2425bff"/> <Option name="TempoMarker" value="f2425bff"/> - <Option name="MeasureLineBeat" value="72727266"/> - <Option name="MeasureLineBar" value="8c8c988c"/> + <Option name="MeasureLineBeat" value="b5b5b576"/> + <Option name="MeasureLineBar" value="d9d9d99c"/> <Option name="GhostTrackBase" value="44007c7f"/> <Option name="GhostTrackWave" value="02fd004c"/> <Option name="GhostTrackWaveClip" value="ff000000"/> @@ -74,8 +74,8 @@ <Option name="RecordingRect" value="e5c6c6ff"/> <Option name="SelectionRect" value="e8f4d377"/> <Option name="Selection" value="636363b2"/> - <Option name="VestigialFrame" value="44007c0f"/> - <Option name="TimeAxisFrame" value="44007c0f"/> + <Option name="VestigialFrame" value="0000000f"/> + <Option name="TimeAxisFrame" value="0000000f"/> <Option name="NameHighlightFill" value="0000ffff"/> <Option name="NameHighlightOutline" value="7c00ff96"/> <Option name="FrameHandle" value="7c00ff96"/> @@ -83,6 +83,15 @@ <Option name="TrimHandle" value="1900ff44"/> <Option name="EditCursor" value="0000ffff"/> <Option name="PlayHead" value="ff0000ff"/> + <Option name="MidiSelectRectOutline" value="5555ffff"/> + <Option name="MidiSelectRectFill" value="8888ff88"/> + <Option name="MidiNoteOutlineMin" value="22ff22b0"/> + <Option name="MidiNoteOutlineMid" value="ffff22b0"/> + <Option name="MidiNoteOutlineMax" value="ff2222b0"/> + <Option name="MidiNoteFillMin" value="33ee338a"/> + <Option name="MidiNoteFillMid" value="eeee338a"/> + <Option name="MidiNoteFillMax" value="ee33338a"/> + <Option name="MidiNoteSelectedOutline" value="5566ffee"/> </Canvas> </Ardour> diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 962ada5035..491939bd97 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -296,14 +296,14 @@ ARDOUR_UI::post_engine () MIDI::Manager::instance()->set_api_data (engine->jack()); setup_midi (); - check_memory_locking(); - - ActionManager::init (); + ActionManager::init (); _tooltips.enable(); if (setup_windows ()) { throw failed_constructor (); } + + check_memory_locking(); /* this is the first point at which all the keybindings are available */ diff --git a/gtk2_ardour/canvas-hit.h b/gtk2_ardour/canvas-hit.h index b924f981bb..9a65f8067a 100644 --- a/gtk2_ardour/canvas-hit.h +++ b/gtk2_ardour/canvas-hit.h @@ -29,8 +29,15 @@ namespace Canvas { class CanvasHit : public Diamond, public CanvasMidiEvent { public: - CanvasHit(MidiRegionView& region, Group& group, double size, const ARDOUR::Note* note=NULL) - : Diamond(group, size), CanvasMidiEvent(region, this, note) {} + CanvasHit( + MidiRegionView& region, + Group& group, + double size, + const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>()) + + : Diamond(group, size), CanvasMidiEvent(region, this, note) + { + } // FIXME double x1() { return 0.0; } diff --git a/gtk2_ardour/canvas-midi-event.cc b/gtk2_ardour/canvas-midi-event.cc index 5981e7b78e..2987435090 100644 --- a/gtk2_ardour/canvas-midi-event.cc +++ b/gtk2_ardour/canvas-midi-event.cc @@ -31,24 +31,17 @@ namespace Gnome { namespace Canvas { -CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item, const ARDOUR::Note* note, bool copy_note) +CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item, + const boost::shared_ptr<ARDOUR::Note> note) : _region(region) , _item(item) , _state(None) - , _note((copy_note && note) ? new ARDOUR::Note(*note) : note) - , _own_note(copy_note) + , _note(note) , _selected(false) { } -CanvasMidiEvent::~CanvasMidiEvent() -{ - if (_own_note) - delete _note; -} - - void CanvasMidiEvent::selected(bool yn) { diff --git a/gtk2_ardour/canvas-midi-event.h b/gtk2_ardour/canvas-midi-event.h index d7b83bc212..3fa009ed33 100644 --- a/gtk2_ardour/canvas-midi-event.h +++ b/gtk2_ardour/canvas-midi-event.h @@ -43,8 +43,12 @@ namespace Canvas { */ class CanvasMidiEvent { public: - CanvasMidiEvent(MidiRegionView& region, Item* item, const ARDOUR::Note* note=NULL, bool copy_note=false); - virtual ~CanvasMidiEvent(); + CanvasMidiEvent( + MidiRegionView& region, + Item* item, + const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>()); + + virtual ~CanvasMidiEvent() {} bool on_event(GdkEvent* ev); @@ -62,17 +66,17 @@ public: const Item* item() const { return _item; } Item* item() { return _item; } - const ARDOUR::Note* note() { return _note; } + const boost::shared_ptr<ARDOUR::Note> note() { return _note; } protected: enum State { None, Pressed, Dragging }; - MidiRegionView& _region; - Item* const _item; - State _state; - const ARDOUR::Note* _note; - bool _own_note; - bool _selected; + MidiRegionView& _region; + Item* const _item; + State _state; + const boost::shared_ptr<ARDOUR::Note> _note; + bool _own_note; + bool _selected; }; } // namespace Gnome diff --git a/gtk2_ardour/canvas-note.h b/gtk2_ardour/canvas-note.h index 4fde2281ca..3948765d66 100644 --- a/gtk2_ardour/canvas-note.h +++ b/gtk2_ardour/canvas-note.h @@ -30,8 +30,12 @@ namespace Canvas { class CanvasNote : public SimpleRect, public CanvasMidiEvent { public: - CanvasNote(MidiRegionView& region, Group& group, const ARDOUR::Note* note=NULL, bool copy_note=false) - : SimpleRect(group), CanvasMidiEvent(region, this, note, copy_note) + CanvasNote( + MidiRegionView& region, + Group& group, + const boost::shared_ptr<ARDOUR::Note> note = boost::shared_ptr<ARDOUR::Note>()) + + : SimpleRect(group), CanvasMidiEvent(region, this, note) { } diff --git a/gtk2_ardour/editing_syms.h b/gtk2_ardour/editing_syms.h index a520b0d318..f731d33efe 100644 --- a/gtk2_ardour/editing_syms.h +++ b/gtk2_ardour/editing_syms.h @@ -62,8 +62,8 @@ MOUSEMODE(MouseZoom) MOUSEMODE(MouseAudition) MOUSEMODE(MouseNote) -MIDIEDITMODE(MidiEditSelect) MIDIEDITMODE(MidiEditPencil) +MIDIEDITMODE(MidiEditSelect) MIDIEDITMODE(MidiEditErase) /* Changing this order will break the menu */ diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index f0a2bfa809..3a7009e7b7 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -158,8 +158,8 @@ Gdk::Cursor* Editor::zoom_cursor = 0; Gdk::Cursor* Editor::time_fx_cursor = 0; Gdk::Cursor* Editor::fader_cursor = 0; Gdk::Cursor* Editor::speaker_cursor = 0; -Gdk::Cursor* Editor::midi_select_cursor = 0; Gdk::Cursor* Editor::midi_pencil_cursor = 0; +Gdk::Cursor* Editor::midi_select_cursor = 0; Gdk::Cursor* Editor::midi_erase_cursor = 0; Gdk::Cursor* Editor::wait_cursor = 0; Gdk::Cursor* Editor::timebar_cursor = 0; @@ -349,7 +349,7 @@ Editor::Editor () range_marker_drag_rect = 0; marker_drag_line = 0; - set_midi_edit_mode (MidiEditSelect, true); + set_midi_edit_mode (MidiEditPencil, true); set_mouse_mode (MouseObject, true); frames_per_unit = 2048; /* too early to use reset_zoom () */ @@ -1310,8 +1310,8 @@ Editor::build_cursors () time_fx_cursor = new Gdk::Cursor (SIZING); wait_cursor = new Gdk::Cursor (WATCH); timebar_cursor = new Gdk::Cursor(LEFT_PTR); - midi_select_cursor = new Gdk::Cursor (CENTER_PTR); midi_pencil_cursor = new Gdk::Cursor (PENCIL); + midi_select_cursor = new Gdk::Cursor (CENTER_PTR); midi_erase_cursor = new Gdk::Cursor (DRAPED_BOX); } @@ -1666,12 +1666,14 @@ Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items) if (have_selected_audio_region) { Menu* envelopes_menu = manage (new Menu); - MenuList& envelopes_items = envelopes_menu->items(); + envelopes_menu->set_name ("ArdourContextMenu"); #if FIXUP_REGION_MENU XXX NEED TO RESOLVE ONE v. MANY REGION ISSUE + + MenuList& envelopes_items = envelopes_menu->items(); RegionView* rv = sv->find_view (ar); AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv); @@ -2751,44 +2753,44 @@ Editor::setup_midi_toolbar () vector<ToggleButton *> midi_tool_buttons; - midi_tool_select_button.add (*(manage (new Image (::get_icon("midi_tool_select"))))); - midi_tool_select_button.set_relief(Gtk::RELIEF_NONE); - midi_tool_buttons.push_back (&midi_tool_select_button); midi_tool_pencil_button.add (*(manage (new Image (::get_icon("midi_tool_pencil"))))); midi_tool_pencil_button.set_relief(Gtk::RELIEF_NONE); midi_tool_buttons.push_back (&midi_tool_pencil_button); + midi_tool_select_button.add (*(manage (new Image (::get_icon("midi_tool_select"))))); + midi_tool_select_button.set_relief(Gtk::RELIEF_NONE); + midi_tool_buttons.push_back (&midi_tool_select_button); midi_tool_erase_button.add (*(manage (new Image (::get_icon("midi_tool_erase"))))); midi_tool_erase_button.set_relief(Gtk::RELIEF_NONE); midi_tool_buttons.push_back (&midi_tool_erase_button); - midi_tool_select_button.set_active(true); + midi_tool_pencil_button.set_active(true); midi_tool_button_set = new GroupedButtons (midi_tool_buttons); midi_tool_button_box.set_border_width (2); midi_tool_button_box.set_spacing(4); midi_tool_button_box.set_spacing(1); - midi_tool_button_box.pack_start(midi_tool_select_button, true, true); midi_tool_button_box.pack_start(midi_tool_pencil_button, true, true); + midi_tool_button_box.pack_start(midi_tool_select_button, true, true); midi_tool_button_box.pack_start(midi_tool_erase_button, true, true); midi_tool_button_box.set_homogeneous(true); - midi_tool_select_button.set_name ("MouseModeButton"); midi_tool_pencil_button.set_name ("MouseModeButton"); + midi_tool_select_button.set_name ("MouseModeButton"); midi_tool_erase_button.set_name ("MouseModeButton"); - ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_select_button, _("Select/Move Notes")); ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_pencil_button, _("Add/Move/Stretch Notes")); + ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_select_button, _("Select/Move Notes")); ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_erase_button, _("Erase Notes")); - midi_tool_select_button.unset_flags (CAN_FOCUS); midi_tool_pencil_button.unset_flags (CAN_FOCUS); + midi_tool_select_button.unset_flags (CAN_FOCUS); midi_tool_erase_button.unset_flags (CAN_FOCUS); - midi_tool_select_button.signal_toggled().connect (bind (mem_fun(*this, - &Editor::midi_edit_mode_toggled), Editing::MidiEditSelect)); midi_tool_pencil_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::midi_edit_mode_toggled), Editing::MidiEditPencil)); + midi_tool_select_button.signal_toggled().connect (bind (mem_fun(*this, + &Editor::midi_edit_mode_toggled), Editing::MidiEditSelect)); midi_tool_erase_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::midi_edit_mode_toggled), Editing::MidiEditErase)); diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 63cbc042ac..7e96f9c9c5 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -833,8 +833,8 @@ class Editor : public PublicEditor static Gdk::Cursor* time_fx_cursor; static Gdk::Cursor* fader_cursor; static Gdk::Cursor* speaker_cursor; - static Gdk::Cursor* midi_select_cursor; static Gdk::Cursor* midi_pencil_cursor; + static Gdk::Cursor* midi_select_cursor; static Gdk::Cursor* midi_erase_cursor; static Gdk::Cursor* wait_cursor; static Gdk::Cursor* timebar_cursor; @@ -1137,6 +1137,8 @@ class Editor : public PublicEditor std::set<boost::shared_ptr<ARDOUR::Playlist> > motion_frozen_playlists; void region_drag_motion_callback (ArdourCanvas::Item*, GdkEvent*); void region_drag_finished_callback (ArdourCanvas::Item*, GdkEvent*); + void create_region_drag_motion_callback (ArdourCanvas::Item*, GdkEvent*); + void create_region_drag_finished_callback (ArdourCanvas::Item*, GdkEvent*); bool _dragging_playhead; @@ -1157,6 +1159,7 @@ class Editor : public PublicEditor gint mouse_rename_region (ArdourCanvas::Item*, GdkEvent*); void start_region_grab (ArdourCanvas::Item*, GdkEvent*); + void start_create_region_grab (ArdourCanvas::Item*, GdkEvent*); void start_region_copy_grab (ArdourCanvas::Item*, GdkEvent*); void start_region_brush_grab (ArdourCanvas::Item*, GdkEvent*); void start_selection_grab (ArdourCanvas::Item*, GdkEvent*); @@ -1439,8 +1442,8 @@ class Editor : public PublicEditor Gtk::HBox midi_tool_button_box; Gtkmm2ext::TearOff* midi_tool_tearoff; - Gtk::ToggleButton midi_tool_select_button; Gtk::ToggleButton midi_tool_pencil_button; + Gtk::ToggleButton midi_tool_select_button; Gtk::ToggleButton midi_tool_erase_button; GroupedButtons *midi_tool_button_set; void midi_edit_mode_toggled (Editing::MidiEditMode m); diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index da95d50d95..c6bbf5ca5f 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -28,6 +28,7 @@ #include <pbd/error.h> #include <gtkmm2ext/utils.h> #include <pbd/memento_command.h> +#include <pbd/basename.h> #include "ardour_ui.h" #include "editor.h" @@ -52,6 +53,7 @@ #include <ardour/route.h> #include <ardour/audio_track.h> #include <ardour/audio_diskstream.h> +#include <ardour/midi_diskstream.h> #include <ardour/playlist.h> #include <ardour/audioplaylist.h> #include <ardour/audioregion.h> @@ -59,6 +61,7 @@ #include <ardour/dB.h> #include <ardour/utils.h> #include <ardour/region_factory.h> +#include <ardour/source_factory.h> #include <bitset> @@ -318,22 +321,19 @@ Editor::midi_edit_mode_toggled (MidiEditMode m) } switch (m) { - case MidiEditSelect: - if (midi_tool_select_button.get_active()) { + case MidiEditPencil: + if (midi_tool_pencil_button.get_active()) set_midi_edit_mode (m); - } break; - - case MidiEditPencil: - if (midi_tool_pencil_button.get_active()) { + + case MidiEditSelect: + if (midi_tool_select_button.get_active()) set_midi_edit_mode (m); - } break; - + case MidiEditErase: - if (midi_tool_erase_button.get_active()) { + if (midi_tool_erase_button.get_active()) set_midi_edit_mode (m); - } break; default: @@ -362,14 +362,14 @@ Editor::set_midi_edit_mode (MidiEditMode m, bool force) ignore_midi_edit_mode_toggle = true; switch (midi_edit_mode) { - case MidiEditSelect: - midi_tool_select_button.set_active (true); - break; - case MidiEditPencil: midi_tool_pencil_button.set_active (true); break; - + + case MidiEditSelect: + midi_tool_select_button.set_active (true); + break; + case MidiEditErase: midi_tool_erase_button.set_active (true); break; @@ -388,12 +388,14 @@ void Editor::set_midi_edit_cursor (MidiEditMode m) { switch (midi_edit_mode) { - case MidiEditSelect: - current_canvas_cursor = midi_select_cursor; - break; case MidiEditPencil: current_canvas_cursor = midi_pencil_cursor; break; + + case MidiEditSelect: + current_canvas_cursor = midi_select_cursor; + break; + case MidiEditErase: current_canvas_cursor = midi_erase_cursor; break; @@ -777,6 +779,10 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp /* rest handled in motion & release */ break; + case MouseNote: + start_create_region_grab (item, event); + break; + default: break; } @@ -1117,7 +1123,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT session->request_transport_speed (0.0); } break; - + default: break; @@ -1621,6 +1627,7 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item case MouseRange: case MouseZoom: case MouseTimeFX: + case MouseNote: if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK || (event->motion.state & GDK_BUTTON2_MASK))) { if (!from_autoscroll) { @@ -2793,6 +2800,19 @@ Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event) } void +Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event) +{ + drag_info.copy = false; + drag_info.item = item; + drag_info.data = clicked_axisview; + drag_info.last_trackview = clicked_axisview; + drag_info.motion_callback = &Editor::create_region_drag_motion_callback; + drag_info.finished_callback = &Editor::create_region_drag_finished_callback; + + start_grab (event); +} + +void Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) { if (selection->regions.empty() || clicked_regionview == 0) { @@ -3605,6 +3625,59 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event } } + +void +Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) +{ + if (drag_info.move_threshold_passed) { + if (drag_info.first_move) { + // TODO: create region-create-drag region view here + drag_info.first_move = false; + } + + // TODO: resize region-create-drag region view here + } +} + +void +Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event) +{ + MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.last_trackview); + if (!mtv) + return; + + const boost::shared_ptr<MidiDiskstream> diskstream = + boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream()); + + if (!diskstream) { + warning << "Cannot create non-MIDI region" << endl; + return; + } + + if (drag_info.first_move) { + begin_reversible_command (_("create region")); + XMLNode &before = mtv->playlist()->get_state(); + + nframes_t start = drag_info.grab_frame; + snap_to (start, -1); + const Meter& m = session->tempo_map().meter_at(start); + const Tempo& t = session->tempo_map().tempo_at(start); + double length = m.frames_per_bar(t, session->frame_rate()); + + boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get()); + + mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>(RegionFactory::create( + src, 0, length, PBD::basename_nosuffix(src->name()))), start); + XMLNode &after = mtv->playlist()->get_state(); + session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after)); + commit_reversible_command(); + + } else { + create_region_drag_motion_callback (item, event); + // TODO: create region-create-drag region here + } +} + void Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event) { diff --git a/gtk2_ardour/main.cc b/gtk2_ardour/main.cc index 1e9b27d8a7..5203346d57 100644 --- a/gtk2_ardour/main.cc +++ b/gtk2_ardour/main.cc @@ -57,7 +57,7 @@ TextReceiver text_receiver ("ardour"); extern int curvetest (string); static ARDOUR_UI *ui = 0; -static char* localedir = LOCALEDIR; +static const char* localedir = LOCALEDIR; gint show_ui_callback (void *arg) diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 7dcba64bae..ec326f1e2e 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -302,7 +302,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) clear_selection(); break; case MidiEditPencil: - trackview.editor.snap_to(event_frame); + trackview.editor.snap_to(event_frame, -1); event_x = trackview.editor.frame_to_pixel(event_frame); create_note_at(event_x, event_y, _default_note_length); default: @@ -359,9 +359,9 @@ MidiRegionView::create_note_at(double x, double y, double dur) //double dur = m.frames_per_bar(t, trackview.session().frame_rate()) / m.beats_per_bar(); // Add a 1 beat long note (for now) - const Note new_note(stamp, dur, (uint8_t)note, 0x40); + const boost::shared_ptr<Note> new_note(new Note(stamp, dur, (uint8_t)note, 0x40)); - view->update_bounds(new_note.note()); + view->update_bounds(new_note->note()); MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note"); cmd->add(new_note); @@ -406,7 +406,7 @@ MidiRegionView::redisplay_model() _model->read_lock(); for (size_t i=0; i < _model->n_notes(); ++i) - add_note(_model->note_at(i), false); + add_note(_model->note_at(i)); end_write(); @@ -596,30 +596,34 @@ MidiRegionView::extend_active_notes() * event arrives, to properly display the note. */ void -MidiRegionView::add_note(const Note& note, bool copy_note) +MidiRegionView::add_note(const boost::shared_ptr<Note> note) { - assert(note.time() >= 0); - //assert(note.time() < _region->length()); + assert(note->time() >= 0); + //assert(note->time() < _region->length()); ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group(); if (midi_view()->note_mode() == Sustained) { - const double y1 = midi_stream_view()->note_to_y(note.note()); + + //cerr << "MRV::add_note sustained " << note->note() << " @ " << note->time() + // << " .. " << note->end_time() << endl; + + const double y1 = midi_stream_view()->note_to_y(note->note()); - CanvasNote* ev_rect = new CanvasNote(*this, *group, ¬e, copy_note); - ev_rect->property_x1() = trackview.editor.frame_to_pixel((nframes_t)note.time()); + CanvasNote* ev_rect = new CanvasNote(*this, *group, note); + ev_rect->property_x1() = trackview.editor.frame_to_pixel((nframes_t)note->time()); ev_rect->property_y1() = y1; - if (note.duration() > 0) - ev_rect->property_x2() = trackview.editor.frame_to_pixel((nframes_t)(note.end_time())); + if (note->duration() > 0) + ev_rect->property_x2() = trackview.editor.frame_to_pixel((nframes_t)(note->end_time())); else ev_rect->property_x2() = trackview.editor.frame_to_pixel(_region->length()); ev_rect->property_y2() = y1 + floor(midi_stream_view()->note_height()); - ev_rect->property_fill_color_rgba() = note_fill_color(note.velocity()); - ev_rect->property_outline_color_rgba() = note_outline_color(note.velocity()); + ev_rect->property_fill_color_rgba() = note_fill_color(note->velocity()); + ev_rect->property_outline_color_rgba() = note_outline_color(note->velocity()); - if (note.duration() == 0) { - _active_notes[note.note()] = ev_rect; + if (note->duration() == 0) { + _active_notes[note->note()] = ev_rect; /* outline all but right edge */ ev_rect->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8); } else { @@ -631,15 +635,18 @@ MidiRegionView::add_note(const Note& note, bool copy_note) _events.push_back(ev_rect); } else if (midi_view()->note_mode() == Percussive) { + + //cerr << "MRV::add_note percussive " << note->note() << " @ " << note->time() << endl; + const double diamond_size = midi_stream_view()->note_height() / 2.0; - const double x = trackview.editor.frame_to_pixel((nframes_t)note.time()); - const double y = midi_stream_view()->note_to_y(note.note()) + ((diamond_size-2) / 4.0); + const double x = trackview.editor.frame_to_pixel((nframes_t)note->time()); + const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0); CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size); ev_diamond->move(x, y); ev_diamond->show(); - ev_diamond->property_fill_color_rgba() = note_fill_color(note.velocity()); - ev_diamond->property_outline_color_rgba() = note_outline_color(note.velocity()); + ev_diamond->property_fill_color_rgba() = note_fill_color(note->velocity()); + ev_diamond->property_outline_color_rgba() = note_outline_color(note->velocity()); _events.push_back(ev_diamond); } } @@ -651,7 +658,7 @@ MidiRegionView::delete_selection() for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) if ((*i)->selected()) - _delta_command->remove(*(*i)->note()); + _delta_command->remove((*i)->note()); _selection.clear(); } @@ -755,10 +762,10 @@ MidiRegionView::note_dropped(CanvasMidiEvent* ev, double dt, uint8_t dnote) for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { command_remove_note(*i); - Note copy(*(*i)->note()); + const boost::shared_ptr<Note> copy(new Note(*(*i)->note().get())); - copy.set_time((*i)->note()->time() + dt); - copy.set_note((*i)->note()->note() + dnote); + copy->set_time((*i)->note()->time() + dt); + copy->set_note((*i)->note()->note() + dnote); command_add_note(copy); } @@ -773,7 +780,7 @@ MidiRegionView::note_entered(ArdourCanvas::CanvasMidiEvent* ev) if (ev->note() && _mouse_state == EraseTouchDragging) { start_delta_command(); ev->selected(true); - _delta_command->remove(*ev->note()); + _delta_command->remove(ev->note()); } else if (_mouse_state == SelectTouchDragging) { note_selected(ev, true); } diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 6b0dcd1804..20c7671f5b 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -76,7 +76,7 @@ class MidiRegionView : public RegionView GhostRegion* add_ghost (AutomationTimeAxisView&); - void add_note(const ARDOUR::Note& note, bool copy_note); + void add_note(const boost::shared_ptr<ARDOUR::Note> note); void resolve_note(uint8_t note_num, double end_time); void begin_write(); @@ -99,14 +99,14 @@ class MidiRegionView : public RegionView _delta_command = _model->new_delta_command(); } - void command_add_note(ARDOUR::Note& note) { + void command_add_note(const boost::shared_ptr<ARDOUR::Note> note) { if (_delta_command) _delta_command->add(note); } void command_remove_note(ArdourCanvas::CanvasMidiEvent* ev) { if (_delta_command && ev->note()) { - _delta_command->remove(*ev->note()); + _delta_command->remove(ev->note()); ev->selected(true); } } diff --git a/gtk2_ardour/midi_streamview.cc b/gtk2_ardour/midi_streamview.cc index a2b4229650..9e0f9f368f 100644 --- a/gtk2_ardour/midi_streamview.cc +++ b/gtk2_ardour/midi_streamview.cc @@ -146,13 +146,10 @@ MidiStreamView::display_region(MidiRegionView* region_view, bool load_model) if (load_model) source->load_model(); - if (source->model()) { - // Find our note range - for (size_t i=0; i < source->model()->n_notes(); ++i) { - const Note& note = source->model()->note_at(i); - update_bounds(note.note()); - } - } + // Find our note range + if (source->model()) + for (size_t i=0; i < source->model()->n_notes(); ++i) + update_bounds(source->model()->note_at(i)->note()); // Display region contents region_view->display_model(source->model()); @@ -467,24 +464,26 @@ MidiStreamView::update_rec_regions (boost::shared_ptr<MidiModel> data, nframes_t MidiRegionView* mrv = (MidiRegionView*)iter->second; for (size_t i=0; i < data->n_notes(); ++i) { - const Note& note = data->note_at(i); + // FIXME: slooooooooow! - if (note.time() + region->position() < start) + const boost::shared_ptr<Note> note = data->note_at(i); + + if (note->duration() > 0 && note->end_time() + region->position() > start) + mrv->resolve_note(note->note(), note->end_time()); + + if (note->time() + region->position() < start) continue; - if (note.time() + region->position() > start + dur) + if (note->time() + region->position() > start + dur) break; - mrv->add_note(note, true); - - if (note.duration() > 0 && note.end_time() >= start) - mrv->resolve_note(note.note(), note.end_time()); + mrv->add_note(note); - if (note.note() < _lowest_note) { - _lowest_note = note.note(); + if (note->note() < _lowest_note) { + _lowest_note = note->note(); update_range = true; - } else if (note.note() > _highest_note) { - _highest_note = note.note(); + } else if (note->note() > _highest_note) { + _highest_note = note->note(); update_range = true; } } diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h index a35a332e01..f7ec164edc 100644 --- a/gtk2_ardour/midi_time_axis.h +++ b/gtk2_ardour/midi_time_axis.h @@ -42,7 +42,6 @@ namespace ARDOUR { class Session; - class MidiDiskstream; class RouteGroup; class Processor; class Location; diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h index 871ec0fb9c..03d5897c1a 100644 --- a/gtk2_ardour/public_editor.h +++ b/gtk2_ardour/public_editor.h @@ -146,7 +146,7 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulThingWithGoingAway */ virtual Editing::MouseMode current_mouse_mode () const = 0; - /** Set the midi edit mode (select, pencil, eraser, etc.) + /** Set the midi edit mode (pencil, select, eraser, etc.) * @param m Midi edit mode (defined in editing_syms.h) * @param force Perform the effects of the change even if no change is required * (ie even if the current midi edit mode is equal to \ref m) diff --git a/libs/ardour/ardour/midi_event.h b/libs/ardour/ardour/midi_event.h index c430c708b7..ee6ea7b749 100644 --- a/libs/ardour/ardour/midi_event.h +++ b/libs/ardour/ardour/midi_event.h @@ -83,16 +83,19 @@ struct MidiEvent { inline const MidiEvent& operator=(const MidiEvent& copy) { _time = copy._time; - if (!_owns_buffer) { - _buffer = copy._buffer; - } else if (copy._buffer) { - if (!_buffer || _size < copy._size) - _buffer = (Byte*)realloc(_buffer, copy._size); - memcpy(_buffer, copy._buffer, copy._size); + if (_owns_buffer) { + if (copy._buffer) { + if (!_buffer || _size < copy._size) + _buffer = (Byte*)realloc(_buffer, copy._size); + memcpy(_buffer, copy._buffer, copy._size); + } else { + free(_buffer); + _buffer = NULL; + } } else { - free(_buffer); - _buffer = NULL; + _buffer = copy._buffer; } + _size = copy._size; return *this; } diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 28a747683c..8e7d786722 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -22,6 +22,7 @@ #define __ardour_midi_model_h__ #include <queue> +#include <deque> #include <utility> #include <boost/utility.hpp> #include <glibmm/thread.h> @@ -72,20 +73,24 @@ public: /** Resizes vector if necessary (NOT realtime safe) */ void append(const MidiEvent& ev); - inline const Note& note_at(unsigned i) const { return _notes[i]; } + inline const boost::shared_ptr<const Note> note_at(unsigned i) const { return _notes[i]; } + inline const boost::shared_ptr<Note> note_at(unsigned i) { return _notes[i]; } inline size_t n_notes() const { return _notes.size(); } inline bool empty() const { return _notes.size() == 0 && _controls.size() == 0; } - typedef std::vector<Note> Notes; + /* FIXME: use better data structure */ + typedef std::vector< boost::shared_ptr<Note> > Notes; - inline static bool note_time_comparator (const Note& a, const Note& b) { - return a.time() < b.time(); + inline static bool note_time_comparator (const boost::shared_ptr<const Note> a, + const boost::shared_ptr<const Note> b) { + return a->time() < b->time(); } struct LaterNoteEndComparator { typedef const Note* value_type; - inline bool operator()(const Note* const a, const Note* const b) { + inline bool operator()(const boost::shared_ptr<const Note> a, + const boost::shared_ptr<const Note> b) const { return a->end_time() > b->end_time(); } }; @@ -111,14 +116,14 @@ public: /*int set_state (const XMLNode&); XMLNode& get_state ();*/ - void add(const Note& note); - void remove(const Note& note); + void add(const boost::shared_ptr<Note> note); + void remove(const boost::shared_ptr<Note> note); private: - MidiModel& _model; - std::string _name; - std::list<Note> _added_notes; - std::list<Note> _removed_notes; + MidiModel& _model; + const std::string _name; + std::list< boost::shared_ptr<Note> > _added_notes; + std::list< boost::shared_ptr<Note> > _removed_notes; }; MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit"); @@ -140,19 +145,28 @@ public: const_iterator(const MidiModel& model, double t); ~const_iterator(); + inline bool locked() const { return _locked; } + const MidiEvent& operator*() const { return _event; } const MidiEvent* operator->() const { return &_event; } const const_iterator& operator++(); // prefix only bool operator==(const const_iterator& other) const; bool operator!=(const const_iterator& other) const { return ! operator==(other); } + + const_iterator& operator=(const const_iterator& other); private: + friend class MidiModel; + const MidiModel* _model; MidiEvent _event; - typedef std::priority_queue<const Note*,std::vector<const Note*>, LaterNoteEndComparator> - ActiveNotes; + typedef std::priority_queue< + boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >, + LaterNoteEndComparator> + ActiveNotes; + mutable ActiveNotes _active_notes; bool _is_end; @@ -167,8 +181,8 @@ public: private: friend class DeltaCommand; - void add_note_unlocked(const Note& note); - void remove_note_unlocked(const Note& note); + void add_note_unlocked(const boost::shared_ptr<Note> note); + void remove_note_unlocked(const boost::shared_ptr<const Note> note); friend class const_iterator; bool control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter) const; @@ -193,16 +207,13 @@ private: const const_iterator _end_iter; - mutable nframes_t _next_read; + mutable nframes_t _next_read; mutable const_iterator _read_iter; - // note state for read(): - // (TODO: Remove and replace with iterator) - - typedef std::priority_queue<const Note*,std::vector<const Note*>, - LaterNoteEndComparator> ActiveNotes; - - //mutable ActiveNotes _active_notes; + typedef std::priority_queue< + boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >, + LaterNoteEndComparator> + ActiveNotes; }; } /* namespace ARDOUR */ diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc index 2b47689911..e971e9ae8f 100644 --- a/libs/ardour/midi_buffer.cc +++ b/libs/ardour/midi_buffer.cc @@ -193,8 +193,8 @@ void MidiBuffer::silence(nframes_t dur, nframes_t offset) { // FIXME use parameters - assert(offset == 0); - //assert(dur == _capacity); + if (offset != 0) + cerr << "WARNING: MidiBuffer::silence w/ offset != 0 (not implemented)" << endl; memset(_events, 0, sizeof(MidiEvent) * _capacity); memset(_data, 0, sizeof(Byte) * _capacity * MAX_EVENT_SIZE); diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index 2b8f70a307..3a2842be7a 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -141,7 +141,10 @@ MidiDiskstream::~MidiDiskstream () void MidiDiskstream::non_realtime_locate (nframes_t position) { + //cerr << "MDS: non_realtime_locate: " << position << endl; + assert(_write_source); _write_source->set_timeline_position (position); + seek(position, true); // correct? } @@ -204,25 +207,7 @@ MidiDiskstream::get_input_sources () _source_port = _io->midi_input(0); - /* I don't get it.... - const char **connections = _io->input(0)->get_connections (); - - if (connections == 0 || connections[0] == 0) { - - if (_source_port) { - // _source_port->disable_metering (); - } - - _source_port = 0; - - } else { - _source_port = dynamic_cast<MidiPort*>( - _session.engine().get_port_by_name (connections[0]) ); - } - - if (connections) { - free (connections); - }*/ + // do... stuff? } int @@ -685,6 +670,7 @@ MidiDiskstream::set_pending_overwrite (bool yn) int MidiDiskstream::overwrite_existing_buffers () { + cerr << "MDS: overwrite_existing_buffers() (does nothing)" << endl; return 0; } @@ -693,6 +679,8 @@ MidiDiskstream::seek (nframes_t frame, bool complete_refill) { Glib::Mutex::Lock lm (state_lock); int ret = -1; + + //cerr << "MDS: seek: " << frame << endl; _playback_buf->reset(); _capture_buf->reset(); @@ -722,6 +710,8 @@ MidiDiskstream::can_internal_playback_seek (nframes_t distance) int MidiDiskstream::internal_playback_seek (nframes_t distance) { + cerr << "MDS: internal_playback_seek " << distance << endl; + first_recordable_frame += distance; playback_sample += distance; @@ -1203,6 +1193,10 @@ MidiDiskstream::engage_record_enable () _source_port->request_monitor_input (!(Config->get_auto_input() && rolling)); } + // FIXME: Why is this necessary? Isn't needed for AudioDiskstream... + if (!_write_source) + use_new_write_source(); + _write_source->mark_streaming_midi_write_started (_note_mode, _session.transport_frame()); RecordEnableChanged (); /* EMIT SIGNAL */ @@ -1405,6 +1399,7 @@ MidiDiskstream::reset_write_sources (bool mark_write_complete, bool force) if (_write_source && mark_write_complete) { _write_source->mark_streaming_write_completed (); } + use_new_write_source (0); if (record_enabled()) { diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index d7167ed2ec..9a87e89ab8 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -42,7 +42,7 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t) , _is_end( (t == DBL_MAX) || model.empty()) , _locked( ! _is_end) { - //cerr << "Created MIDI iterator @ " << t << "(is end: " << _is_end << ")" << endl; + //cerr << "Created MIDI iterator @ " << t << " (is end: " << _is_end << ")" << endl; if (_is_end) return; @@ -52,7 +52,7 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t) _note_iter = model.notes().end(); for (MidiModel::Notes::const_iterator i = model.notes().begin(); i != model.notes().end(); ++i) { - if ((*i).time() >= t) { + if ((*i)->time() >= t) { _note_iter = i; break; } @@ -70,8 +70,8 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t) double x, y; bool ret = i->second->list()->rt_safe_earliest_event_unlocked(t, DBL_MAX, x, y); if (!ret) { - cerr << "MIDI Iterator: CC " << i->first.id() << " (size " << i->second->list()->size() - << ") has no events past " << t << endl; + /*cerr << "MIDI Iterator: CC " << i->first.id() << " (size " << i->second->list()->size() + << ") has no events past " << t << endl;*/ continue; } @@ -92,7 +92,8 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t) } if (_note_iter != model.notes().end()) { - _event = MidiEvent(_note_iter->on_event(), false); + _event = MidiEvent((*_note_iter)->on_event(), false); + _active_notes.push(*_note_iter); ++_note_iter; } @@ -106,9 +107,9 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t) _is_end = true; _model->read_unlock(); _locked = false; - //} else { - // printf("MIDI Iterator = %X @ %lf\n", _event.type(), _event.time()); - } + } /*else { + printf("MIDI Iterator = %X @ %lf\n", _event.type(), _event.time()); + }*/ } @@ -155,30 +156,51 @@ MidiModel::const_iterator::operator++() } } - enum Type { NIL, NOTE, CC }; - Type type = NIL; + enum Type { NIL, NOTE_ON, NOTE_OFF, CC }; + + Type type = NIL; + double t = 0; - if (_note_iter != _model->notes().end()) - type = NOTE; + // Next earliest note on + if (_note_iter != _model->notes().end()) { + type = NOTE_ON; + t = (*_note_iter)->time(); + } + // Use the next earliest note off iff it's earlier than the note on + if (_model->note_mode() == Sustained && (! _active_notes.empty())) { + if (type == NIL || _active_notes.top()->end_time() <= (*_note_iter)->time()) { + type = NOTE_OFF; + t = _active_notes.top()->end_time(); + } + } + + // Use the next earliest controller iff it's earlier than the note event if (_control_iter != _control_iters.end() && _control_iter->second.first != DBL_MAX) - if (_note_iter == _model->notes().end() || _control_iter->second.first < _note_iter->time()) + if (type == NIL || _control_iter->second.first < t) type = CC; - if (type == NOTE) { - //cerr << "MIDI Iterator = note" << endl; - _event = MidiEvent(_note_iter->on_event(), false); + if (type == NOTE_ON) { + //cerr << "********** MIDI Iterator = note on" << endl; + _event = MidiEvent((*_note_iter)->on_event(), false); + _active_notes.push(*_note_iter); ++_note_iter; + } else if (type == NOTE_OFF) { + //cerr << "********** MIDI Iterator = note off" << endl; + _event = MidiEvent(_active_notes.top()->off_event(), false); + _active_notes.pop(); } else if (type == CC) { - //cerr << "MIDI Iterator = CC" << endl; + //cerr << "********** MIDI Iterator = CC" << endl; _model->control_to_midi_event(_event, *_control_iter); } else { - //cerr << "MIDI Iterator = NIL" << endl; + //cerr << "********** MIDI Iterator = END" << endl; _is_end = true; _model->read_unlock(); _locked = false; } + assert(_is_end || _event.size() > 0); + return *this; } @@ -186,14 +208,36 @@ MidiModel::const_iterator::operator++() bool MidiModel::const_iterator::operator==(const const_iterator& other) const { - if (_is_end) - if (other._is_end) - return true; - else - return false; + if (_is_end || other._is_end) + return (_is_end == other._is_end); else return (_event == other._event); } + + +MidiModel::const_iterator& +MidiModel::const_iterator::operator=(const const_iterator& other) +{ + if (_locked && _model != other._model) + _model->read_unlock(); + + assert( ! other._event.owns_buffer()); + + _model = other._model; + _event = other._event; + _is_end = other._is_end; + _locked = other._locked; + _note_iter = other._note_iter; + _control_iters = other._control_iters; + _control_iter = other._control_iter; + + assert( ! _event.owns_buffer()); + + if (_locked) + _model->read_lock(); + + return *this; +} // MidiModel @@ -204,11 +248,12 @@ MidiModel::MidiModel(Session& s, size_t size) , _note_mode(Sustained) , _writing(false) , _edited(false) - //, _active_notes(LaterNoteEndComparator()) , _end_iter(*this, DBL_MAX) , _next_read(UINT32_MAX) , _read_iter(*this, DBL_MAX) { + assert(_end_iter._is_end); + assert( ! _end_iter._locked); } @@ -219,84 +264,34 @@ MidiModel::MidiModel(Session& s, size_t size) size_t MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset) const { + //cerr << this << " MM::read @ " << start << " * " << nframes << " + " << stamp_offset << endl; + //cerr << this << " MM # notes: " << n_notes() << endl; + size_t read_events = 0; if (start != _next_read) { _read_iter = const_iterator(*this, (double)start); - // cerr << "Repositioning iterator from " << _next_read << " to " << start << endl; - //} else { - // cerr << "Using cached iterator at " << _next_read << endl; + //cerr << "Repositioning iterator from " << _next_read << " to " << start << endl; + } else { + //cerr << "Using cached iterator at " << _next_read << endl; + } + + if (_read_iter == end()) { + //cerr << this << " MM::read: at end @ " << _read_iter->time() << endl; + } else { + //cerr << this << " MM::read: at " << _read_iter->time() << endl; } _next_read = start + nframes; while (_read_iter != end() && _read_iter->time() < start + nframes) { + assert(_read_iter->size() > 0); dst.write(_read_iter->time() + stamp_offset, _read_iter->size(), _read_iter->buffer()); + //cerr << this << " MM::read event @ " << _read_iter->time() << endl; ++_read_iter; ++read_events; } -#if 0 - /* FIXME: cache last lookup value to avoid O(n) search every time */ - - if (_note_mode == Sustained) { - - for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) { - - while ( ! _active_notes.empty() ) { - const Note* const earliest_off = _active_notes.top(); - const MidiEvent& off_ev = earliest_off->off_event(); - if (off_ev.time() < start + nframes && off_ev.time() <= n->time()) { - dst.write(off_ev.time() + stamp_offset, off_ev.size(), off_ev.buffer()); - _active_notes.pop(); - ++read_events; - } else { - break; - } - } - - if (n->time() >= start + nframes) - break; - - // Note on - if (n->time() >= start) { - const MidiEvent& on_ev = n->on_event(); - dst.write(on_ev.time() + stamp_offset, on_ev.size(), on_ev.buffer()); - _active_notes.push(&(*n)); - ++read_events; - } - - } - - // Write any trailing note offs - while ( ! _active_notes.empty() ) { - const Note* const earliest_off = _active_notes.top(); - const MidiEvent& off_ev = earliest_off->off_event(); - if (off_ev.time() < start + nframes) { - dst.write(off_ev.time() + stamp_offset, off_ev.size(), off_ev.buffer()); - _active_notes.pop(); - ++read_events; - } else { - break; - } - } - - // Percussive - } else { - for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) { - // Note on - if (n->time() >= start) { - if (n->time() < start + nframes) { - const MidiEvent& ev = n->on_event(); - dst.write(ev.time() + stamp_offset, ev.size(), ev.buffer()); - ++read_events; - } else { - break; - } - } - } - } -#endif return read_events; } @@ -323,83 +318,6 @@ MidiModel::control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter) } -/** Return the earliest MIDI event in the given range. - * - * \return true if \a output has been set to the earliest event in the given range. - */ -#if 0 -bool -MidiModel::earliest_note_event(MidiEvent& output, nframes_t start, nframes_t nframes) const -{ - /* FIXME: cache last lookup value to avoid O(n) search every time */ - - const Note* const earliest_on = NULL; - const Note* const earliest_off = NULL; - const MidiEvent* const earliest_cc = NULL; - - /* Notes */ - - if (_note_mode == Sustained) { - - for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) { - - if ( ! _active_notes.empty() ) { - const Note* const earliest_off = _active_notes.top(); - const MidiEvent& off_ev = earliest_off->off_event(); - if (off_ev.time() < start + nframes && off_ev.time() <= n->time()) { - output = off_ev; - //dst.write(off_ev.time() + stamp_offset, off_ev.size(), off_ev.buffer()); - _active_notes.pop(); - return true; - } - } - - if (n->time() >= start + nframes) - break; - - // Note on - if (n->time() >= start) { - earliest_on = &n->on_event(); - //dst.write(on_ev.time() + stamp_offset, on_ev.size(), on_ev.buffer()); - _active_notes.push(&(*n)); - return true; - } - - } - - // Write any trailing note offs - while ( ! _active_notes.empty() ) { - const Note* const earliest_off = _active_notes.top(); - const MidiEvent& off_ev = earliest_off->off_event(); - if (off_ev.time() < start + nframes) { - dst.write(off_ev.time() + stamp_offset, off_ev.size(), off_ev.buffer()); - _active_notes.pop(); - ++read_events; - } else { - break; - } - } - - // Percussive - } else { - for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) { - // Note on - if (n->time() >= start) { - if (n->time() < start + nframes) { - const MidiEvent& ev = n->on_event(); - dst.write(ev.time() + stamp_offset, ev.size(), ev.buffer()); - ++read_events; - } else { - break; - } - } - } - } - - return read_events; -} -#endif - /** Begin a write of events to the model. * * If \a mode is Sustained, complete notes with duration are constructed as note @@ -435,8 +353,8 @@ MidiModel::end_write(bool delete_stuck) if (_note_mode == Sustained && delete_stuck) { for (Notes::iterator n = _notes.begin(); n != _notes.end() ; ) { - if (n->duration() == 0) { - cerr << "WARNING: Stuck note lost: " << n->note() << endl; + if ((*n)->duration() == 0) { + cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl; n = _notes.erase(n); } else { ++n; @@ -461,7 +379,7 @@ MidiModel::append(const MidiEvent& ev) { write_lock(); - assert(_notes.empty() || ev.time() >= _notes.back().time()); + assert(_notes.empty() || ev.time() >= _notes.back()->time()); assert(_writing); if (ev.is_note_on()) @@ -483,7 +401,7 @@ MidiModel::append_note_on_unlocked(double time, uint8_t note_num, uint8_t veloci //cerr << "MidiModel " << this << " note " << (int)note_num << " on @ " << time << endl; assert(_writing); - _notes.push_back(Note(time, 0, note_num, velocity)); + _notes.push_back(boost::shared_ptr<Note>(new Note(time, 0, note_num, velocity))); if (_note_mode == Sustained) { //cerr << "MM Sustained: Appending active note on " << (unsigned)(uint8_t)note_num << endl; _write_notes.push_back(_notes.size() - 1); @@ -512,7 +430,7 @@ MidiModel::append_note_off_unlocked(double time, uint8_t note_num) * keys that send it */ for (WriteNotes::iterator n = _write_notes.begin(); n != _write_notes.end(); ++n) { - Note& note = _notes[*n]; + Note& note = *_notes[*n].get(); //cerr << (unsigned)(uint8_t)note.note() << " ? " << (unsigned)note_num << endl; if (note.note() == note_num) { assert(time > note.time()); @@ -538,7 +456,7 @@ MidiModel::append_cc_unlocked(double time, uint8_t number, uint8_t value) void -MidiModel::add_note_unlocked(const Note& note) +MidiModel::add_note_unlocked(const boost::shared_ptr<Note> note) { //cerr << "MidiModel " << this << " add note " << (int)note.note() << " @ " << note.time() << endl; Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note, note_time_comparator); @@ -547,7 +465,7 @@ MidiModel::add_note_unlocked(const Note& note) void -MidiModel::remove_note_unlocked(const Note& note) +MidiModel::remove_note_unlocked(const boost::shared_ptr<const Note> note) { //cerr << "MidiModel " << this << " remove note " << (int)note.note() << " @ " << note.time() << endl; Notes::iterator n = find(_notes.begin(), _notes.end(), note); @@ -562,10 +480,10 @@ MidiModel::is_sorted() const { bool t = 0; for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) - if (n->time() < t) + if ((*n)->time() < t) return false; else - t = n->time(); + t = (*n)->time(); return true; } @@ -605,7 +523,7 @@ MidiModel::apply_command(Command* cmd) void -MidiModel::DeltaCommand::add(const Note& note) +MidiModel::DeltaCommand::add(const boost::shared_ptr<Note> note) { //cerr << "MEC: apply" << endl; @@ -615,7 +533,7 @@ MidiModel::DeltaCommand::add(const Note& note) void -MidiModel::DeltaCommand::remove(const Note& note) +MidiModel::DeltaCommand::remove(const boost::shared_ptr<Note> note) { //cerr << "MEC: remove" << endl; @@ -630,15 +548,27 @@ MidiModel::DeltaCommand::operator()() // This could be made much faster by using a priority_queue for added and // removed notes (or sort here), and doing a single iteration over _model + // Need to reset iterator to drop the read lock it holds, or we'll deadlock + const bool reset_iter = (_model._read_iter.locked()); + const double iter_time = _model._read_iter->time(); + + if (reset_iter) + _model._read_iter = _model.end(); // drop read lock + + assert( ! _model._read_iter.locked()); + _model.write_lock(); - for (std::list<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) + for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) _model.add_note_unlocked(*i); - for (std::list<Note>::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) + for (std::list< boost::shared_ptr<Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) _model.remove_note_unlocked(*i); _model.write_unlock(); + + if (reset_iter) + _model._read_iter = const_iterator(_model, iter_time); _model.ContentsChanged(); /* EMIT SIGNAL */ } @@ -650,16 +580,28 @@ MidiModel::DeltaCommand::undo() // This could be made much faster by using a priority_queue for added and // removed notes (or sort here), and doing a single iteration over _model + // Need to reset iterator to drop the read lock it holds, or we'll deadlock + const bool reset_iter = (_model._read_iter.locked()); + const double iter_time = _model._read_iter->time(); + + if (reset_iter) + _model._read_iter = _model.end(); // drop read lock + + assert( ! _model._read_iter.locked()); + _model.write_lock(); - for (std::list<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) + for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) _model.remove_note_unlocked(*i); - for (std::list<Note>::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) + for (std::list< boost::shared_ptr<Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) _model.add_note_unlocked(*i); _model.write_unlock(); + if (reset_iter) + _model._read_iter = const_iterator(_model, iter_time); + _model.ContentsChanged(); /* EMIT SIGNAL */ } @@ -697,9 +639,9 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source) // Write any pending note offs earlier than this note on while ( ! active_notes.empty() ) { - const Note* const earliest_off = active_notes.top(); - const MidiEvent& off_ev = earliest_off->off_event(); - if (off_ev.time() <= n->time()) { + const boost::shared_ptr<const Note> earliest_off = active_notes.top(); + const MidiEvent& off_ev = earliest_off->off_event(); + if (off_ev.time() <= (*n)->time()) { source->append_event_unlocked(off_ev); active_notes.pop(); } else { @@ -708,9 +650,9 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source) } // Write this note on - source->append_event_unlocked(n->on_event()); - if (n->duration() > 0) - active_notes.push(&(*n)); + source->append_event_unlocked((*n)->on_event()); + if ((*n)->duration() > 0) + active_notes.push(*n); } // Write any trailing note offs diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 3b62fb0f84..d81f1f2cef 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -354,13 +354,11 @@ MidiTrack::no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_fram bool session_state_changing, bool can_record, bool rec_monitors_input) { if (n_outputs().n_midi() == 0) { - //return 0; - throw; // FIXME + return 0; } if (!_active) { silence (nframes, offset); - //return 0; // FIXME } if (session_state_changing) { diff --git a/libs/ardour/quantize.cc b/libs/ardour/quantize.cc index 5da30e8466..c29144996c 100644 --- a/libs/ardour/quantize.cc +++ b/libs/ardour/quantize.cc @@ -70,13 +70,13 @@ Quantize::run (boost::shared_ptr<Region> r) double q_frames = _q * (m.frames_per_bar(t, session.frame_rate()) / (double)m.beats_per_bar()); for (MidiModel::Notes::iterator i = model->notes().begin(); i != model->notes().end(); ++i) { - const double new_time = lrint(i->time() / q_frames) * q_frames; - const double new_dur = ((i->time() != 0 && new_dur < (q_frames * 1.5)) + const double new_time = lrint((*i)->time() / q_frames) * q_frames; + const double new_dur = (((*i)->time() != 0 && new_dur < (q_frames * 1.5)) ? q_frames - : lrint(i->duration() / q_frames) * q_frames); + : lrint((*i)->duration() / q_frames) * q_frames); - i->set_time(new_time); - i->set_duration(new_dur); + (*i)->set_time(new_time); + (*i)->set_duration(new_dur); } model->set_edited(true); diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 2473490f5b..abd8a0abba 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -1482,11 +1482,13 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) char track_name[32]; uint32_t track_id = 0; uint32_t n = 0; - uint32_t channels_used = 0; string port; RouteList new_routes; list<boost::shared_ptr<MidiTrack> > ret; + //uint32_t control_id; + // FIXME: need physical I/O and autoconnect stuff for MIDI + /* count existing midi tracks */ { @@ -1496,18 +1498,29 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) if (dynamic_cast<MidiTrack*>((*i).get()) != 0) { if (!(*i)->is_hidden()) { n++; - channels_used += (*i)->n_inputs().n_midi(); + //channels_used += (*i)->n_inputs().n_midi(); } } } } + /* + vector<string> physinputs; + vector<string> physoutputs; + uint32_t nphysical_in; + uint32_t nphysical_out; + + _engine.get_physical_outputs (physoutputs); + _engine.get_physical_inputs (physinputs); + control_id = ntracks() + nbusses() + 1; + */ + while (how_many) { /* check for duplicate route names, since we might have pre-existing - routes with this name (e.g. create Midi1, Midi2, delete Midi1, + routes with this name (e.g. create Audio1, Audio2, delete Audio1, save, close,restart,add new route - first named route is now - Midi2) + Audio2) */ @@ -1522,17 +1535,71 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) } while (track_id < (UINT_MAX-1)); + /* + if (Config->get_input_auto_connect() & AutoConnectPhysical) { + nphysical_in = min (n_physical_inputs, (uint32_t) physinputs.size()); + } else { + nphysical_in = 0; + } + + if (Config->get_output_auto_connect() & AutoConnectPhysical) { + nphysical_out = min (n_physical_outputs, (uint32_t) physinputs.size()); + } else { + nphysical_out = 0; + } + */ + + shared_ptr<MidiTrack> track; + try { - shared_ptr<MidiTrack> track (new MidiTrack (*this, track_name, Route::Flag (0), mode)); + track = boost::shared_ptr<MidiTrack>((new MidiTrack (*this, track_name, Route::Flag (0), mode))); - if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::MIDI, 1), false, this)) { + if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::AUDIO, 1), false, this)) { error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg; + goto failed; + } + + /* + if (nphysical_in) { + for (uint32_t x = 0; x < track->n_inputs().n_midi() && x < nphysical_in; ++x) { + + port = ""; + + if (Config->get_input_auto_connect() & AutoConnectPhysical) { + port = physinputs[(channels_used+x)%nphysical_in]; + } + + if (port.length() && track->connect_input (track->input (x), port, this)) { + break; + } + } + } + + for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) { + + port = ""; + + if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) { + port = physoutputs[(channels_used+x)%nphysical_out]; + } else if (Config->get_output_auto_connect() & AutoConnectMaster) { + if (_master_out) { + port = _master_out->input (x%_master_out->n_inputs().n_midi())->name(); + } + } + + if (port.length() && track->connect_output (track->output (x), port, this)) { + break; + } } channels_used += track->n_inputs ().n_midi(); + */ + + track->midi_diskstream()->non_realtime_input_change(); + track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes)); - track->set_remote_control_id (ntracks()); + //track->set_remote_control_id (control_id); new_routes.push_back (track); ret.push_back (track); @@ -1540,14 +1607,43 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many) catch (failed_constructor &err) { error << _("Session: could not create new midi track.") << endmsg; - // XXX should we delete the tracks already created? - ret.clear (); - return ret; + + if (track) { + /* we need to get rid of this, since the track failed to be created */ + /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */ + + { + RCUWriter<DiskstreamList> writer (diskstreams); + boost::shared_ptr<DiskstreamList> ds = writer.get_copy(); + ds->remove (track->midi_diskstream()); + } + } + + goto failed; } - + + catch (AudioEngine::PortRegistrationFailure& pfe) { + + error << _("No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks.") << endmsg; + + if (track) { + /* we need to get rid of this, since the track failed to be created */ + /* XXX arguably, MidiTrack::MidiTrack should not do the Session::add_diskstream() */ + + { + RCUWriter<DiskstreamList> writer (diskstreams); + boost::shared_ptr<DiskstreamList> ds = writer.get_copy(); + ds->remove (track->midi_diskstream()); + } + } + + goto failed; + } + --how_many; } + failed: if (!new_routes.empty()) { add_routes (new_routes, false); save_state (_current_snapshot_name); @@ -2732,8 +2828,6 @@ Session::source_by_id (const PBD::ID& id) source = i->second; } - /* XXX search MIDI or other searches here */ - return source; } diff --git a/libs/ardour/session_events.cc b/libs/ardour/session_events.cc index aa5a1b87d4..bf1d9f1a09 100644 --- a/libs/ardour/session_events.cc +++ b/libs/ardour/session_events.cc @@ -397,7 +397,7 @@ Session::process_event (Event* ev) break; case Event::Overwrite: - overwrite_some_buffers (static_cast<AudioDiskstream*>(ev->ptr)); + overwrite_some_buffers (static_cast<Diskstream*>(ev->ptr)); break; case Event::SetDiskstreamSpeed: diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index 3bc53cb538..b8d82bdb9d 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -326,7 +326,7 @@ SMFSource::read_event(uint32_t* delta_t, uint32_t* size, Byte** buf) const nframes_t SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const { - //cerr << "SMF - read " << start << ", count=" << cnt << ", offset=" << stamp_offset << endl; + //cerr << "SMF " << name() << " read " << start << ", count=" << cnt << ", offset=" << stamp_offset << endl; // 64 bits ought to be enough for anybody uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index d07b9d6d69..3a657bc0e2 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -169,8 +169,10 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) #endif } else if (type == DataType::MIDI) { - - boost::shared_ptr<Source> ret (new SMFSource (s, node)); + boost::shared_ptr<Source> ret (new SMFSource (s, node)); + + SourceCreated (ret); + return ret; } return boost::shared_ptr<Source>(); |