diff --git a/SConstruct b/SConstruct index 746809a3c0..c5ab5f78b6 100644 --- a/SConstruct +++ b/SConstruct @@ -534,7 +534,7 @@ if env['FFT_ANALYSIS']: if env['LV2']: conf = env.Configure(custom_tests = { 'CheckPKGExists' : CheckPKGExists }) - if conf.CheckPKGExists ('\"slv2 >= 0.6.0\"'): + if conf.CheckPKGExists ('slv2'): libraries['slv2'] = LibraryInfo() libraries['slv2'].ParseConfig('pkg-config --cflags --libs slv2') env.Append (CCFLAGS="-DHAVE_LV2") diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 421906fbfc..94981803b4 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -159,6 +159,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) auditioning_alert_button (_("AUDITION")), solo_alert_button (_("SOLO")), + midi_panic_button (_("Panic")), shown_flag (false), error_log_button (_("Errors")) diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h index 0eb02d8821..e3dd58a310 100644 --- a/gtk2_ardour/ardour_ui.h +++ b/gtk2_ardour/ardour_ui.h @@ -479,6 +479,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI Gtk::ToggleButton auditioning_alert_button; Gtk::ToggleButton solo_alert_button; + Gtk::ToggleButton midi_panic_button; + Gtk::VBox alert_box; void solo_blink (bool); @@ -490,6 +492,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI void solo_alert_toggle (); void audition_alert_toggle (); + void midi_panic_toggle (); void big_clock_value_changed (); void primary_clock_value_changed (); diff --git a/gtk2_ardour/ardour_ui2.cc b/gtk2_ardour/ardour_ui2.cc index 959d5fc128..014de41f23 100644 --- a/gtk2_ardour/ardour_ui2.cc +++ b/gtk2_ardour/ardour_ui2.cc @@ -344,12 +344,15 @@ ARDOUR_UI::setup_transport () solo_alert_button.signal_pressed().connect (mem_fun(*this,&ARDOUR_UI::solo_alert_toggle)); auditioning_alert_button.set_name ("TransportAuditioningAlert"); auditioning_alert_button.signal_pressed().connect (mem_fun(*this,&ARDOUR_UI::audition_alert_toggle)); + midi_panic_button.set_name("TransportMidiPanic"); + midi_panic_button.signal_clicked().connect (mem_fun(*this, &ARDOUR_UI::midi_panic_toggle)); tooltips().set_tip (solo_alert_button, _("When active, something is soloed.\nClick to de-solo everything")); tooltips().set_tip (auditioning_alert_button, _("When active, auditioning is taking place\nClick to stop the audition")); alert_box.pack_start (solo_alert_button, false, false); alert_box.pack_start (auditioning_alert_button, false, false); + alert_box.pack_start (midi_panic_button, false, false); transport_tearoff_hbox.set_border_width (3); @@ -518,6 +521,16 @@ ARDOUR_UI::solo_alert_toggle () } } +void +ARDOUR_UI::midi_panic_toggle () +{ + if (session) { + session->midi_panic(); + midi_panic_button.set_active (false); + midi_panic_button.set_state (STATE_NORMAL); + } +} + void ARDOUR_UI::solo_blink (bool onoff) { diff --git a/gtk2_ardour/canvas-midi-event.cc b/gtk2_ardour/canvas-midi-event.cc index 8b14bea986..49851fb3f6 100644 --- a/gtk2_ardour/canvas-midi-event.cc +++ b/gtk2_ardour/canvas-midi-event.cc @@ -35,12 +35,41 @@ CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item, const boost::shared_ptr note) : _region(region) , _item(item) + , _text(0) , _state(None) , _note(note) , _selected(false) { + _text = new Text(*(item->property_parent())); } +void +CanvasMidiEvent::move_event(double dx, double dy) +{ + _item->move(dx, dy); + _text->move(dx, dy); +} + +void +CanvasMidiEvent::show_velocity(void) +{ + _text->property_x() = (x1() + x2()) /2; + _text->property_y() = (y1() + y2()) /2; + ostringstream velo(ios::ate); + velo << int(_note->velocity()); + _text->property_text() = velo.str(); + _text->property_justification() = Gtk::JUSTIFY_CENTER; + _text->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get(); + _text->show(); + _text->lower_to_bottom(); + _text->raise(2); +} + +void +CanvasMidiEvent::hide_velocity(void) +{ + _text->hide(); +} void CanvasMidiEvent::selected(bool yn) @@ -49,11 +78,13 @@ CanvasMidiEvent::selected(bool yn) return; } else if (yn) { set_fill_color(UINT_INTERPOLATE(note_fill_color(_note->velocity()), - ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get(), 0.85)); + ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get(), 0.1)); set_outline_color(ARDOUR_UI::config()->canvasvar_MidiNoteSelectedOutline.get()); + show_velocity(); } else { set_fill_color(note_fill_color(_note->velocity())); set_outline_color(note_outline_color(_note->velocity())); + hide_velocity(); } _selected = yn; @@ -70,11 +101,38 @@ CanvasMidiEvent::on_event(GdkEvent* ev) double event_x, event_y, dx, dy; nframes_t event_frame; bool select_mod; + uint8_t d_velocity = 10; if (_region.get_time_axis_view().editor.current_mouse_mode() != Editing::MouseNote) return false; switch (ev->type) { + case GDK_SCROLL: + if (Keyboard::modifier_state_equals (ev->scroll.state, Keyboard::Level4Modifier)) { + d_velocity = 1; + } + + if(ev->scroll.direction == GDK_SCROLL_UP) { + _region.note_selected(this, true); + if (_region.mouse_state() == MidiRegionView::SelectTouchDragging) { + // TODO: absolute velocity + } else { + _region.change_velocity(d_velocity, true); + } + return true; + } else if(ev->scroll.direction == GDK_SCROLL_DOWN) { + + _region.note_selected(this, true); + if (_region.mouse_state() == MidiRegionView::SelectTouchDragging) { + // TODO: absolute velocity + } else { + _region.change_velocity(-d_velocity, true); + } + return true; + } else { + return false; + } + case GDK_KEY_PRESS: if (_note && ev->key.keyval == GDK_Delete) { selected(true); @@ -92,11 +150,15 @@ CanvasMidiEvent::on_event(GdkEvent* ev) case GDK_ENTER_NOTIFY: _region.note_entered(this); _item->grab_focus(); + show_velocity(); Keyboard::magic_widget_grab_focus(); break; case GDK_LEAVE_NOTIFY: Keyboard::magic_widget_drop_focus(); + if(! selected()) { + hide_velocity(); + } _region.get_canvas_group()->grab_focus(); break; diff --git a/gtk2_ardour/canvas-midi-event.h b/gtk2_ardour/canvas-midi-event.h index 3fa009ed33..488bf2d42e 100644 --- a/gtk2_ardour/canvas-midi-event.h +++ b/gtk2_ardour/canvas-midi-event.h @@ -21,6 +21,7 @@ #define __gtk_ardour_canvas_midi_event_h__ #include "simplerect.h" +#include #include class Editor; @@ -48,13 +49,18 @@ public: Item* item, const boost::shared_ptr note = boost::shared_ptr()); - virtual ~CanvasMidiEvent() {} + virtual ~CanvasMidiEvent() { if(_text) delete _text; } bool on_event(GdkEvent* ev); bool selected() const { return _selected; } void selected(bool yn); + void move_event(double dx, double dy); + + void show_velocity(); + void hide_velocity(); + virtual void set_outline_color(uint32_t c) = 0; virtual void set_fill_color(uint32_t c) = 0; @@ -63,9 +69,6 @@ public: virtual double x2() = 0; virtual double y2() = 0; - const Item* item() const { return _item; } - Item* item() { return _item; } - const boost::shared_ptr note() { return _note; } protected: @@ -73,6 +76,7 @@ protected: MidiRegionView& _region; Item* const _item; + Text* _text; State _state; const boost::shared_ptr _note; bool _own_note; diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc index d5c1305f66..f2583c690d 100644 --- a/gtk2_ardour/editor_canvas_events.cc +++ b/gtk2_ardour/editor_canvas_events.cc @@ -40,6 +40,7 @@ #include "control_point.h" #include "canvas_impl.h" #include "simplerect.h" +#include "canvas-midi-event.h" #include "i18n.h" @@ -48,6 +49,7 @@ using namespace std; using namespace ARDOUR; using namespace PBD; using namespace Gtk; +using namespace ArdourCanvas; bool Editor::track_canvas_scroll (GdkEventScroll* ev) @@ -56,6 +58,7 @@ Editor::track_canvas_scroll (GdkEventScroll* ev) double wx, wy; nframes_t xdelta; int direction = ev->direction; + CanvasMidiEvent *midi_event = dynamic_cast(track_canvas->get_item_at(ev->x, ev->y)); retry: switch (direction) { @@ -94,6 +97,9 @@ Editor::track_canvas_scroll (GdkEventScroll* ev) current_stepping_trackview->step_height (true); return true; } else { + if(midi_event) { + return midi_event->on_event(reinterpret_cast(ev)); + } scroll_tracks_up_line (); return true; } @@ -129,6 +135,9 @@ Editor::track_canvas_scroll (GdkEventScroll* ev) current_stepping_trackview->step_height (false); return true; } else { + if(midi_event) { + return midi_event->on_event(reinterpret_cast(ev)); + } scroll_tracks_down_line (); return true; } diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 86e06d0a3c..180a0a71fb 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -423,8 +423,22 @@ MidiRegionView::redisplay_model() _model->read_lock(); - for (size_t i=0; i < _model->n_notes(); ++i) + /* + MidiModel::Notes notes = _model->notes(); + cerr << endl << "Model contains " << notes.size() << " Notes:" << endl; + for(MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) { + Note note = *(*i).get(); + cerr << "MODEL: Note time: " << note.time() << " duration: " << note.duration() + << " end-time: " << note.end_time() + << " velocity: " << int(note.velocity()) + //<< " Note-on: " << note.on_event(). + //<< " Note-off: " << note.off_event() + << endl; + }*/ + + for (size_t i=0; i < _model->n_notes(); ++i) { add_note(_model->note_at(i)); + } end_write(); @@ -538,11 +552,15 @@ MidiRegionView::set_y_position_and_height (double y, double h) note->show(); } + note->hide_velocity(); note->property_y1() = y1; note->property_y2() = y2; + if(note->selected()) { + note->show_velocity(); } } } + } _model->read_unlock(); } @@ -609,6 +627,7 @@ MidiRegionView::end_write() { delete[] _active_notes; _active_notes = NULL; + _marked_for_selection.clear(); } @@ -657,6 +676,8 @@ MidiRegionView::add_note(const boost::shared_ptr note) ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group(); + CanvasMidiEvent *event = 0; + if (midi_view()->note_mode() == Sustained) { //cerr << "MRV::add_note sustained " << note->note() << " @ " << note->time() @@ -687,6 +708,7 @@ MidiRegionView::add_note(const boost::shared_ptr note) ev_rect->show(); _events.push_back(ev_rect); + event = ev_rect; MidiGhostRegion* gr; @@ -711,8 +733,18 @@ MidiRegionView::add_note(const boost::shared_ptr note) 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); - + event = ev_diamond; + } else { + event = 0; } + + if(event) { + Note *note = event->note().get(); + + if(_marked_for_selection.find(note) != _marked_for_selection.end()) { + note_selected(event, true); + } +} } void @@ -813,7 +845,7 @@ void MidiRegionView::move_selection(double dx, double dy) { for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) - (*i)->item()->move(dx, dy); + (*i)->move_event(dx, dy); } @@ -851,7 +883,6 @@ MidiRegionView::note_dropped(CanvasMidiEvent* ev, double dt, uint8_t dnote) // Make sure the note pitch does not exceed the MIDI standard range if (dnote <= 127 && (highest_note_in_selection + dnote > 127)) { highest_note_difference = highest_note_in_selection - 127; - cerr << "Highest note difference: " << (int) highest_note_difference; } start_delta_command(); @@ -868,9 +899,15 @@ MidiRegionView::note_dropped(CanvasMidiEvent* ev, double dt, uint8_t dnote) copy->set_time(0); } - uint8_t new_pitch = (*i)->note()->note() + dnote - highest_note_difference; - if(new_pitch > 127) { - new_pitch = 127; + uint8_t original_pitch = (*i)->note()->note(); + 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)) { + new_pitch = original_pitch; } lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch); @@ -880,19 +917,16 @@ MidiRegionView::note_dropped(CanvasMidiEvent* ev, double dt, uint8_t dnote) command_add_note(copy); - _selection.erase(i); + _marked_for_selection.insert(copy.get()); i = next; } apply_command(); - //cerr << "new lowest note (selection): " << int(lowest_note_in_selection) << " new highest note(selection): " << int(highest_note_in_selection) << endl; - // care about notes being moved beyond the upper/lower bounds on the canvas if(lowest_note_in_selection < midi_stream_view()->lowest_note() || highest_note_in_selection > midi_stream_view()->highest_note() ) { - //cerr << "resetting note range" << endl; midi_stream_view()->set_note_range(MidiStreamView::ContentsRange); } } @@ -1031,12 +1065,15 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo command_remove_note(canvas_note); copy->on_event().time() = current_frame; command_add_note(copy); + _marked_for_selection.insert(copy.get()); } // resize end of note if (note_end == CanvasNote::NOTE_OFF && current_frame > copy->time()) { + command_remove_note(canvas_note); command_remove_note(canvas_note); copy->off_event().time() = current_frame; command_add_note(copy); + _marked_for_selection.insert(copy.get()); } delete resize_rect; @@ -1045,9 +1082,45 @@ MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bo _resize_data.clear(); apply_command(); - clear_selection(); } + +void +MidiRegionView::change_velocity(uint8_t velocity, bool relative) +{ + start_delta_command(); + for (Selection::iterator i = _selection.begin(); i != _selection.end();) { + Selection::iterator next = i; + ++next; + + CanvasMidiEvent *event = *i; + const boost::shared_ptr copy(new Note(*(event->note().get()))); + + if(relative) { + uint8_t new_velocity = copy->velocity() + velocity; + clamp_0_to_127(new_velocity); + + copy->set_velocity(new_velocity); + } else { // absolute + copy->set_velocity(velocity); + } + + command_remove_note(event); + command_add_note(copy); + + _marked_for_selection.insert(copy.get()); + i = next; + } + + // dont keep notes selected if tweaking a single note + if(_marked_for_selection.size() == 1) { + _marked_for_selection.clear(); + } + + apply_command(); +} + + void MidiRegionView::note_entered(ArdourCanvas::CanvasMidiEvent* ev) { diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 767d057f57..9f8f8f8b3e 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -103,6 +103,7 @@ class MidiRegionView : public RegionView void command_remove_note(ArdourCanvas::CanvasMidiEvent* ev) { if (_delta_command && ev->note()) { + _selection.erase(ev); _delta_command->remove(ev->note()); ev->selected(true); } @@ -162,6 +163,14 @@ class MidiRegionView : public RegionView */ void commit_resizing(ArdourCanvas::CanvasNote::NoteEnd note_end, double event_x, bool relative); + /** + * This function is called while the user adjusts the velocity on a selection of notes + * @param velocity the relative or absolute velocity, dependin on the value of relative + * @param relative true if the given velocity represents a delta to be applied to all notes, false + * if the absolute value of the note shoud be set + */ + void change_velocity(uint8_t velocity, bool relative=false); + enum MouseState { None, Pressed, SelectTouchDragging, SelectRectDragging, AddDragging, EraseTouchDragging }; MouseState mouse_state() const { return _mouse_state; } @@ -221,9 +230,17 @@ class MidiRegionView : public RegionView MouseState _mouse_state; int _pressed_button; + /// currently selected CanvasMidiEvents typedef std::set Selection; Selection _selection; + /** + * this enables vanilla notes to be marked for selection + * they are added to _selection when redisplay_model is called + * this is necessary for selecting notes during/after model manipulations + */ + std::set _marked_for_selection; + std::vector _resize_data; }; diff --git a/gtk2_ardour/midi_util.h b/gtk2_ardour/midi_util.h index 451fc55ea1..d56f1680e0 100644 --- a/gtk2_ardour/midi_util.h +++ b/gtk2_ardour/midi_util.h @@ -54,5 +54,14 @@ inline static uint32_t note_fill_color(uint8_t vel) } } +inline static void clamp_0_to_127(uint8_t &val) +{ + if( (127 < val) && (val < 192) ) { + val = 127; + } else if( (192 <= val) && (val < 255) ) { + val = 0; + } +} + #endif /* __gtk_ardour_midi_util_h__ */ diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 11ef4e0f76..0852108077 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -58,10 +58,10 @@ public: MidiModel(Session& s, size_t size=0); // This is crap. - void write_lock() { _lock.writer_lock(); _automation_lock.lock(); } - void write_unlock() { _lock.writer_unlock(); _automation_lock.unlock(); } - void read_lock() const { _lock.reader_lock(); /*_automation_lock.lock();*/ } - void read_unlock() const { _lock.reader_unlock(); /*_automation_lock.unlock();*/ } + void write_lock(); + void write_unlock(); + void read_lock() const; + void read_unlock() const; void clear() { _notes.clear(); } diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h index 1b3ab50f91..136d587550 100644 --- a/libs/ardour/ardour/midi_ring_buffer.h +++ b/libs/ardour/ardour/midi_ring_buffer.h @@ -280,8 +280,8 @@ MidiRingBuffer::read_contents(size_t size, Byte* buf) inline size_t MidiRingBuffer::write(double time, size_t size, const Byte* buf) { - //printf("MRB - write %#X %d %d with time %lf\n", - // buf[0], buf[1], buf[2], time); + printf("MRB - write %#X %d %d with time %lf\n", + buf[0], buf[1], buf[2], time); assert(size > 0); diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index c9ead65d49..c2c7bed056 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -70,6 +70,7 @@ public: int set_state(const XMLNode& node); + void midi_panic(void); bool write_immediate_event(size_t size, const Byte* buf); struct MidiControl : public AutomationControl { diff --git a/libs/ardour/ardour/note.h b/libs/ardour/ardour/note.h index 41c18358d3..5b5a38d645 100644 --- a/libs/ardour/ardour/note.h +++ b/libs/ardour/ardour/note.h @@ -40,7 +40,7 @@ public: const Note& operator=(const Note& copy); inline bool operator==(const Note& other) - { return time() == other.time() && note() == other.note(); } + { return time() == other.time() && note() == other.note() && duration() == other.duration(); } inline double time() const { return _on_event.time(); } inline double end_time() const { return _off_event.time(); } diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 33ac03f77e..a2380357d3 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -754,6 +754,7 @@ class Session : public PBD::StatefulDestructible /* MIDI */ + void midi_panic(void); int set_mtc_port (string port_tag); int set_mmc_port (string port_tag); int set_midi_port (string port_tag); diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 84fd06d079..0f13b05a09 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -527,7 +527,8 @@ AudioEngine::remove_session () session = 0; } - remove_all_ports (); + //FIXME: Preliminary bugfix for http://tracker.ardour.org/view.php?id=1985 + //remove_all_ports (); } void @@ -555,6 +556,7 @@ AudioEngine::register_port (DataType dtype, const string& portname, bool input, { Port* newport = 0; + cerr << "trying to register port with name " << portname << endl; try { if (dtype == DataType::AUDIO) { newport = new AudioPort (portname, (input ? Port::IsInput : Port::IsOutput), publish, frames_per_cycle()); @@ -564,11 +566,17 @@ AudioEngine::register_port (DataType dtype, const string& portname, bool input, throw unknown_type(); } + cerr << "successfully got port " << portname << " with address " << newport << endl; + RCUWriter writer (ports); boost::shared_ptr ps = writer.get_copy (); + cerr << "Address of ports list: " << ps << endl + << "Ports set size before insert: " << ps->size() << endl; ps->insert (ps->begin(), newport); + cerr << "Ports set size after insert: " << ps->size() << endl; /* writer goes out of scope, forces update */ + return newport; } @@ -608,22 +616,31 @@ AudioEngine::unregister_port (Port& port) { /* caller must hold process lock */ + cerr << "about to unregister Port xx x" << &port << "\n"; + if (!_running) { /* probably happening when the engine has been halted by JACK, in which case, there is nothing we can do here. */ + cerr << "not running\n"; return 0; } { + cerr << "before getcopy\n"; RCUWriter writer (ports); boost::shared_ptr ps = writer.get_copy (); + cerr << "Ports set size: " << ps.get()->size() << endl; + for (Ports::iterator i = ps->begin(); i != ps->end(); ++i) { + cerr << "before delete" << endl; if ((*i) == &port) { + cerr << "About to delete " << &port << endl; delete *i; ps->erase (i); + cerr << "After erasing ports size: " << ps->size(); break; } } @@ -631,6 +648,7 @@ AudioEngine::unregister_port (Port& port) /* writer goes out of scope, forces update */ } + cerr << "before remove_connections\n"; remove_connections_for (port); return 0; diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc index a77a608fcd..4eae1eefc6 100644 --- a/libs/ardour/midi_buffer.cc +++ b/libs/ardour/midi_buffer.cc @@ -117,7 +117,7 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset) for (size_t i=0; i < msrc.size(); ++i) { const MIDI::Event& ev = msrc[i]; if (ev.time() >= offset && ev.time() < offset+nframes) { - //cout << "MidiBuffer::read_from got event, " << ev.time() << endl; + cout << "MidiBuffer::read_from got event, " << int(ev.type()) << " time: " << ev.time() << " buffer size: " << _size << endl; push_back(ev); } else { cerr << "MidiBuffer event out of range, " << ev.time() << endl; @@ -148,7 +148,7 @@ MidiBuffer::push_back(const MIDI::Event& ev) _events[_size].set_buffer(write_loc, false); ++_size; - //cerr << "MidiBuffer: pushed, size = " << _size << endl; + cerr << "MidiBuffer: pushed, size = " << _size << endl; _silent = false; @@ -177,7 +177,7 @@ MidiBuffer::push_back(const jack_midi_event_t& ev) _events[_size].set_buffer(write_loc, false); ++_size; - //cerr << "MidiBuffer: pushed, size = " << _size << endl; + cerr << "MidiBuffer: pushed, size = " << _size << endl; _silent = false; @@ -207,7 +207,7 @@ MidiBuffer::reserve(double time, size_t size) _events[_size].set_buffer(write_loc, false); ++_size; - //cerr << "MidiBuffer: reserved, size = " << _size << endl; + cerr << "MidiBuffer: reserved, size = " << _size << endl; _silent = false; diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index a49094df91..4aa229b450 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -36,6 +36,34 @@ using namespace std; using namespace ARDOUR; +void +MidiModel::write_lock() +{ + _lock.writer_lock(); + _automation_lock.lock(); +} + +void +MidiModel::write_unlock() +{ + _lock.writer_unlock(); + _automation_lock.unlock(); +} + +void +MidiModel::read_lock() const +{ + _lock.reader_lock(); + /*_automation_lock.lock();*/ +} + +void +MidiModel::read_unlock() const +{ + _lock.reader_unlock(); + /*_automation_lock.unlock();*/ +} + // Read iterator (const_iterator) MidiModel::const_iterator::const_iterator(const MidiModel& model, double t) @@ -51,7 +79,7 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t) model.read_lock(); _note_iter = model.notes().end(); - + // find first note which begins after t for (MidiModel::Notes::const_iterator i = model.notes().begin(); i != model.notes().end(); ++i) { if ((*i)->time() >= t) { _note_iter = i; @@ -95,6 +123,7 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t) if (_note_iter != model.notes().end()) { _event = MIDI::Event((*_note_iter)->on_event(), false); _active_notes.push(*_note_iter); + cerr << " new const iterator: size active notes: " << _active_notes.size() << " is empty: " << _active_notes.empty() << endl; ++_note_iter; } @@ -106,19 +135,22 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t) if (_event.size() == 0) { //cerr << "Created MIDI iterator @ " << t << " is at end." << endl; _is_end = true; + if(_locked) { _model->read_unlock(); _locked = false; - } /*else { + } + } else { printf("MIDI Iterator = %X @ %lf\n", _event.type(), _event.time()); - }*/ +} } MidiModel::const_iterator::~const_iterator() { - if (_locked) + if (_locked) { _model->read_unlock(); } +} const MidiModel::const_iterator& @@ -168,6 +200,7 @@ MidiModel::const_iterator::operator++() t = (*_note_iter)->time(); } + cerr << " operator++ before test: size active notes: " << _active_notes.size() << " is empty: " << _active_notes.empty() << endl; // 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()) { @@ -182,22 +215,20 @@ MidiModel::const_iterator::operator++() type = CC; if (type == NOTE_ON) { - //cerr << "********** MIDI Iterator = note on" << endl; + cerr << "********** MIDI Iterator = note on" << endl; _event = MIDI::Event((*_note_iter)->on_event(), false); _active_notes.push(*_note_iter); ++_note_iter; } else if (type == NOTE_OFF) { - //cerr << "********** MIDI Iterator = note off" << endl; + cerr << "********** MIDI Iterator = note off" << endl; _event = MIDI::Event(_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 = END" << endl; + cerr << "********** MIDI Iterator = END" << endl; _is_end = true; - _model->read_unlock(); - _locked = false; } assert(_is_end || _event.size() > 0); @@ -226,6 +257,7 @@ MidiModel::const_iterator::operator=(const const_iterator& other) _model = other._model; _event = other._event; + _active_notes = other._active_notes; _is_end = other._is_end; _locked = other._locked; _note_iter = other._note_iter; @@ -234,9 +266,6 @@ MidiModel::const_iterator::operator=(const const_iterator& other) assert( ! _event.owns_buffer()); - if (_locked) - _model->read_lock(); - return *this; } @@ -265,16 +294,16 @@ 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; + 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; + cerr << "Repositioning iterator from " << _next_read << " to " << start << endl; } else { - //cerr << "Using cached iterator at " << _next_read << endl; + cerr << "Using cached iterator at " << _next_read << endl; } _next_read = start + nframes; @@ -282,7 +311,11 @@ MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, 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; + cerr << this << " MM::read event @ " << _read_iter->time() + << " type: " << hex << int(_read_iter->type()) << dec + << " note: " << int(_read_iter->note()) + << " velocity: " << int(_read_iter->velocity()) + << endl; ++_read_iter; ++read_events; } diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index 794959328a..eab87376eb 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -105,8 +105,8 @@ MidiSource::midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nfra { Glib::Mutex::Lock lm (_lock); if (_model) { - /*const size_t n_events = */_model->read(dst, start, cnt, stamp_offset); - //cout << "Read " << n_events << " events from model." << endl; + const size_t n_events = _model->read(dst, start, cnt, stamp_offset); + cout << "Read " << n_events << " events from model." << endl; return cnt; } else { return read_unlocked (dst, start, cnt, stamp_offset); diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index bf97b19106..cbd9c52bf6 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -697,6 +697,19 @@ MidiTrack::set_note_mode (NoteMode m) midi_diskstream()->set_note_mode(m); } +void +MidiTrack::midi_panic() +{ + for(uint8_t channel = 0; channel <= 0xF; channel++) { + Byte ev[3] = { MIDI_CMD_CONTROL | channel, MIDI_CTL_SUSTAIN, 0 }; + write_immediate_event(3, ev); + ev[1] = MIDI_CTL_ALL_NOTES_OFF; + write_immediate_event(3, ev); + ev[1] = MIDI_CTL_RESET_CONTROLLERS; + write_immediate_event(3, ev); + } +} + /** \return true on success, false on failure (no buffer space left) */ bool diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc index eca6d25ea8..b4938636a2 100644 --- a/libs/ardour/session_midi.cc +++ b/libs/ardour/session_midi.cc @@ -26,6 +26,8 @@ #include #include +#include + #include #include #include @@ -37,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +55,22 @@ using namespace MIDI; MachineControl::CommandSignature MMC_CommandSignature; MachineControl::ResponseSignature MMC_ResponseSignature; + +void +Session::midi_panic() +{ + { + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + MidiTrack *track = dynamic_cast((*i).get()); + if (track != 0) { + track->midi_panic(); + } + } + } +} + int Session::use_config_midi_ports () { diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index 720626e936..6df78f3b29 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -192,7 +192,7 @@ SMFSource::flush_header () _fd = freopen(path().c_str(), "r+", _fd); assert(_fd); - fseek(_fd, 0, 0); + fseek(_fd, 0, SEEK_SET); write_chunk("MThd", 6, data); write_chunk_header("MTrk", _track_size); @@ -204,9 +204,8 @@ SMFSource::flush_header () int SMFSource::flush_footer() { - seek_to_end(); + fseek(_fd, 0, SEEK_END); write_footer(); - seek_to_end(); return 0; } @@ -321,11 +320,11 @@ SMFSource::read_event(uint32_t* delta_t, uint32_t* size, Byte** buf) const if (event_size > 1) fread((*buf) + 1, 1, *size - 1, _fd); - /*printf("%s read event: delta = %u, size = %u, data = ", _name.c_str(), *delta_t, *size); + printf("%s read event: delta = %u, size = %u, data = ", _name.c_str(), *delta_t, *size); for (size_t i=0; i < *size; ++i) { printf("%X ", (*buf)[i]); } - printf("\n");*/ + printf("\n"); return (int)*size; } @@ -334,7 +333,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 " << name() << " read " << start << ", count=" << cnt << ", offset=" << stamp_offset << endl; + cerr << "SMF read_unlocked " << 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 @@ -349,7 +348,7 @@ SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, n size_t scratch_size = 0; // keep track of scratch to minimize reallocs // FIXME: don't seek to start and search every read (brutal!) - fseek(_fd, _header_size, 0); + fseek(_fd, _header_size, SEEK_SET); // FIXME: assumes tempo never changes after start const double frames_per_beat = _session.tempo_map().tempo_at(_timeline_position).frames_per_beat(