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
This commit is contained in:
parent
15af67c179
commit
14dcc3f017
|
@ -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" "")
|
||||
|
|
|
@ -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'>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
set_midi_edit_mode (m);
|
||||
}
|
||||
break;
|
||||
|
||||
case MidiEditPencil:
|
||||
if (midi_tool_pencil_button.get_active()) {
|
||||
if (midi_tool_pencil_button.get_active())
|
||||
set_midi_edit_mode (m);
|
||||
}
|
||||
break;
|
||||
|
||||
case MidiEditErase:
|
||||
if (midi_tool_erase_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())
|
||||
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) {
|
||||
|
@ -2792,6 +2799,19 @@ Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
|
|||
begin_reversible_command (_("move region(s)"));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
CanvasNote* ev_rect = new CanvasNote(*this, *group, ¬e, copy_note);
|
||||
ev_rect->property_x1() = trackview.editor.frame_to_pixel((nframes_t)note.time());
|
||||
const double y1 = midi_stream_view()->note_to_y(note->note());
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
|
||||
namespace ARDOUR {
|
||||
class Session;
|
||||
class MidiDiskstream;
|
||||
class RouteGroup;
|
||||
class Processor;
|
||||
class Location;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
if (_note_iter != _model->notes().end())
|
||||
type = NOTE;
|
||||
enum Type { NIL, NOTE_ON, NOTE_OFF, CC };
|
||||
|
||||
Type type = NIL;
|
||||
double t = 0;
|
||||
|
||||
// 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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>();
|
||||
|
|
Loading…
Reference in New Issue