lots of MIDI editing stuff. to be explained on the website when its done
git-svn-id: svn://localhost/ardour2/branches/3.0@5596 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
c6be9b6888
commit
3845af6ce9
@ -81,9 +81,51 @@ style "time_axis_view_item_name"
|
||||
font_name = "%FONT_SMALLER%"
|
||||
}
|
||||
|
||||
style "white_tree_view"
|
||||
{
|
||||
GtkButton::default_border = { 0, 0, 0, 0 }
|
||||
GtkButton::default_outside_border = { 0, 0, 0, 0 }
|
||||
GtkButton::button_relief = GTK_RELIEF_NONE
|
||||
GtkTreeView::vertical-padding = 0
|
||||
GtkTreeView::horizontal-padding = 0
|
||||
GtkTreeView::even-row-color = { 0.70, 0.70, 0.70 }
|
||||
GtkTreeView::odd-row-color = { 0.64, 0.64, 0.64 }
|
||||
|
||||
fg[NORMAL] = { 0.30, 0.30, 0.40 }
|
||||
fg[ACTIVE] = { 0.30, 0.30, 0.40 }
|
||||
fg[PRELIGHT] = { 1.0, 1.0, 1.0 }
|
||||
fg[INSENSITIVE] = { 0.30, 0.30, 0.40 }
|
||||
fg[SELECTED] = { 0.30, 0.30, 0.40 }
|
||||
|
||||
bg[NORMAL] = { 0.80, 0.80, 0.80 }
|
||||
bg[ACTIVE] = { 0.80, 0.80, 0.80 }
|
||||
bg[PRELIGHT] = { 0.80, 0.80, 0.80 }
|
||||
bg[INSENSITIVE] = { 0.80, 0.80, 0.80 }
|
||||
bg[SELECTED] = { 0.80, 0.80, 0.80 }
|
||||
|
||||
text[NORMAL] = { 0.30, 0.30, 0.40 }
|
||||
text[ACTIVE] = { 0.30, 0.30, 0.40 }
|
||||
text[PRELIGHT] = { 0.30, 0.30, 0.40 }
|
||||
text[INSENSITIVE] = { 0.30, 0.30, 0.40 }
|
||||
text[SELECTED] = { 0, 0, 0 }
|
||||
|
||||
base[ACTIVE] = { 0.80, 0.80, 0.80 }
|
||||
base[NORMAL] = { 0.80, 0.80, 0.80 }
|
||||
base[PRELIGHT] = { 0.90, 0.90, 0.90 }
|
||||
base[INSENSITIVE] = "#4c5159"
|
||||
base[SELECTED] = { 0.60, 0.60, 0.80 }
|
||||
|
||||
engine "clearlooks"
|
||||
{
|
||||
menubarstyle = 0 # 0 = flat, 1 = sunken, 2 = flat gradient
|
||||
menuitemstyle = 0 # 0 = flat, 1 = 3d-ish (gradient), 2 = 3d-ish (button)
|
||||
listviewitemstyle = 0 # 0 = flat, 1 = 3d-ish (gradient)
|
||||
progressbarstyle = 1 # 0 = candy bar, 1 = fancy candy bar, 2 = flat
|
||||
}
|
||||
}
|
||||
|
||||
style "default_base" = "medium_text"
|
||||
{
|
||||
|
||||
GtkWidget::cursor_color = {1.0, 1.0, 1.0 }
|
||||
GtkButton::default_border = { 0, 0, 0, 0 }
|
||||
GtkButton::default_outside_border = { 0, 0, 0, 0 }
|
||||
@ -1594,4 +1636,5 @@ widget "*RegionListWholeFile" style:highest "treeview_parent_node"
|
||||
widget "*EditorHScrollbar" style:highest "editor_hscrollbar"
|
||||
widget "*OddPortGroups" style:highest "odd_port_groups"
|
||||
widget "*EvenPortGroups" style:highest "even_port_groups"
|
||||
|
||||
Widget "*MidiListView" style:highest "white_tree_view"
|
||||
Widget "*MidiListView*" style:highest "white_tree_view"
|
||||
|
@ -62,7 +62,7 @@
|
||||
<Option name="midi note meter color max" value="ee3333aa"/>
|
||||
<Option name="midi note meter color mid" value="eeee33aa"/>
|
||||
<Option name="midi note meter color min" value="33ee33aa"/>
|
||||
<Option name="midi note selected" value="8888ffb0"/>
|
||||
<Option name="midi note selected" value="ff0000ff"/>
|
||||
<Option name="midi note velocity text" value="000000ff"/>
|
||||
<Option name="midi program change fill" value="0000ffa0"/>
|
||||
<Option name="midi program change outline" value="a7a7d4ff"/>
|
||||
|
@ -191,6 +191,7 @@ ARDOUR_UI::setup_transport ()
|
||||
{
|
||||
transport_tearoff = manage (new TearOff (transport_tearoff_hbox));
|
||||
transport_tearoff->set_name ("TransportBase");
|
||||
transport_tearoff->tearoff_window().signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), &transport_tearoff->tearoff_window()), false);
|
||||
|
||||
if (Profile->get_sae()) {
|
||||
transport_tearoff->set_can_be_torn_off (false);
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "editor.h"
|
||||
#include "actions.h"
|
||||
#include "mixer_ui.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifdef GTKOSX
|
||||
#include <gtkmm2ext/sync-menu.h>
|
||||
@ -567,7 +568,6 @@ ARDOUR_UI::use_menubar_as_top_menubar ()
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ARDOUR_UI::setup_clock ()
|
||||
{
|
||||
@ -580,11 +580,12 @@ ARDOUR_UI::setup_clock ()
|
||||
big_clock_window->add (big_clock);
|
||||
|
||||
WindowTitle title(Glib::get_application_name());
|
||||
title += _("Clock");
|
||||
title += _("Big Clock");
|
||||
big_clock_window->set_title (title.get_string());
|
||||
big_clock_window->set_type_hint (Gdk::WINDOW_TYPE_HINT_MENU);
|
||||
big_clock_window->set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
|
||||
big_clock_window->signal_realize().connect (bind (sigc::ptr_fun (set_decoration), big_clock_window, (Gdk::DECOR_BORDER|Gdk::DECOR_RESIZEH)));
|
||||
big_clock_window->signal_unmap().connect (bind (sigc::ptr_fun(&ActionManager::uncheck_toggleaction), X_("<Actions>/Common/ToggleBigClock")));
|
||||
big_clock_window->signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), big_clock_window), false);
|
||||
|
||||
manage_window (*big_clock_window);
|
||||
}
|
||||
|
@ -1704,7 +1704,11 @@ Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> regi
|
||||
bind (mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr<Region>(region)));
|
||||
|
||||
items.push_back (MenuElem (_("Rename"), mem_fun(*this, &Editor::rename_region)));
|
||||
items.push_back (MenuElem (_("Popup region editor"), mem_fun(*this, &Editor::edit_region)));
|
||||
if (mr && internal_editing()) {
|
||||
items.push_back (MenuElem (_("Popup list editor"), mem_fun(*this, &Editor::show_midi_list_editor)));
|
||||
} else {
|
||||
items.push_back (MenuElem (_("Popup region editor"), mem_fun(*this, &Editor::edit_region)));
|
||||
}
|
||||
}
|
||||
|
||||
items.push_back (MenuElem (_("Raise to top layer"), mem_fun(*this, &Editor::raise_region_to_top)));
|
||||
@ -2643,23 +2647,23 @@ Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark)
|
||||
break;
|
||||
|
||||
case SnapToAThirtysecondBeat:
|
||||
start = session->tempo_map().round_to_beat_subdivision (start, 32);
|
||||
start = session->tempo_map().round_to_beat_subdivision (start, 32, direction);
|
||||
break;
|
||||
|
||||
case SnapToASixteenthBeat:
|
||||
start = session->tempo_map().round_to_beat_subdivision (start, 16);
|
||||
start = session->tempo_map().round_to_beat_subdivision (start, 16, direction);
|
||||
break;
|
||||
|
||||
case SnapToAEighthBeat:
|
||||
start = session->tempo_map().round_to_beat_subdivision (start, 8);
|
||||
start = session->tempo_map().round_to_beat_subdivision (start, 8, direction);
|
||||
break;
|
||||
|
||||
case SnapToAQuarterBeat:
|
||||
start = session->tempo_map().round_to_beat_subdivision (start, 4);
|
||||
start = session->tempo_map().round_to_beat_subdivision (start, 4, direction);
|
||||
break;
|
||||
|
||||
case SnapToAThirdBeat:
|
||||
start = session->tempo_map().round_to_beat_subdivision (start, 3);
|
||||
start = session->tempo_map().round_to_beat_subdivision (start, 3, direction);
|
||||
break;
|
||||
|
||||
case SnapToMark:
|
||||
@ -2758,45 +2762,6 @@ Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark)
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
Editor::snap_length_beats (nframes64_t start)
|
||||
{
|
||||
if (!session) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
/* FIXME: This could/should also work with non-tempo based snap settings (ie seconds) */
|
||||
|
||||
switch (snap_type) {
|
||||
case SnapToBar:
|
||||
return session->tempo_map().meter_at(start).beats_per_bar();
|
||||
|
||||
case SnapToBeat:
|
||||
return 1.0;
|
||||
|
||||
case SnapToAThirtysecondBeat:
|
||||
return 1.0 / (double)32.0;
|
||||
break;
|
||||
|
||||
case SnapToASixteenthBeat:
|
||||
return 1.0 / (double)16.0;
|
||||
break;
|
||||
|
||||
case SnapToAEighthBeat:
|
||||
return 1.0 / (double)8.0;
|
||||
break;
|
||||
|
||||
case SnapToAQuarterBeat:
|
||||
return 1.0 / (double)4.0;
|
||||
break;
|
||||
|
||||
case SnapToAThirdBeat:
|
||||
return 1.0 / (double)3.0;
|
||||
|
||||
default:
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editor::setup_toolbar ()
|
||||
@ -2846,6 +2811,7 @@ Editor::setup_toolbar ()
|
||||
|
||||
mouse_mode_tearoff = manage (new TearOff (*mode_box));
|
||||
mouse_mode_tearoff->set_name ("MouseModeBase");
|
||||
mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), &mouse_mode_tearoff->tearoff_window()), false);
|
||||
|
||||
if (Profile->get_sae()) {
|
||||
mouse_mode_tearoff->set_can_be_torn_off (false);
|
||||
@ -2985,6 +2951,7 @@ Editor::setup_toolbar ()
|
||||
|
||||
tools_tearoff = manage (new TearOff (*hbox));
|
||||
tools_tearoff->set_name ("MouseModeBase");
|
||||
tools_tearoff->tearoff_window().signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), &tools_tearoff->tearoff_window()), false);
|
||||
|
||||
if (Profile->get_sae()) {
|
||||
tools_tearoff->set_can_be_torn_off (false);
|
||||
@ -3846,6 +3813,60 @@ Editor::playlist_selector () const
|
||||
return *_playlist_selector;
|
||||
}
|
||||
|
||||
Evoral::MusicalTime
|
||||
Editor::get_grid_type_as_beats (bool& success, nframes64_t position)
|
||||
{
|
||||
success = true;
|
||||
|
||||
switch (snap_type) {
|
||||
case SnapToBeat:
|
||||
return 1.0;
|
||||
break;
|
||||
|
||||
case SnapToAThirtysecondBeat:
|
||||
return 1.0/32.0;
|
||||
break;
|
||||
|
||||
case SnapToASixteenthBeat:
|
||||
return 1.0/16.0;
|
||||
break;
|
||||
|
||||
case SnapToAEighthBeat:
|
||||
return 1.0/8.0;
|
||||
break;
|
||||
|
||||
case SnapToAQuarterBeat:
|
||||
return 1.0/4.0;
|
||||
break;
|
||||
|
||||
case SnapToAThirdBeat:
|
||||
return 1.0/3.0;
|
||||
break;
|
||||
|
||||
case SnapToBar:
|
||||
if (session) {
|
||||
return session->tempo_map().meter_at (position).beats_per_bar();
|
||||
}
|
||||
break;
|
||||
|
||||
case SnapToCDFrame:
|
||||
case SnapToSMPTEFrame:
|
||||
case SnapToSMPTESeconds:
|
||||
case SnapToSMPTEMinutes:
|
||||
case SnapToSeconds:
|
||||
case SnapToMinutes:
|
||||
case SnapToRegionStart:
|
||||
case SnapToRegionEnd:
|
||||
case SnapToRegionSync:
|
||||
case SnapToRegionBoundary:
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
nframes64_t
|
||||
Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next)
|
||||
{
|
||||
|
@ -82,6 +82,7 @@ namespace ARDOUR {
|
||||
class Filter;
|
||||
class Crossfade;
|
||||
class ChanCount;
|
||||
class MidiOperator;
|
||||
}
|
||||
|
||||
namespace LADSPA {
|
||||
@ -102,6 +103,7 @@ class Drag;
|
||||
class GlobalPortMatrixWindow;
|
||||
class GroupedButtons;
|
||||
class Marker;
|
||||
class MidiRegionView;
|
||||
class MixerStrip;
|
||||
class PlaylistSelector;
|
||||
class PluginSelector;
|
||||
@ -333,6 +335,7 @@ class Editor : public PublicEditor
|
||||
/* nudge is initiated by transport controls owned by ARDOUR_UI */
|
||||
|
||||
nframes64_t get_nudge_distance (nframes64_t pos, nframes64_t& next);
|
||||
Evoral::MusicalTime get_grid_type_as_beats (bool& success, nframes64_t position);
|
||||
|
||||
void nudge_forward (bool next, bool force_playhead);
|
||||
void nudge_backward (bool next, bool force_playhead);
|
||||
@ -1060,6 +1063,7 @@ class Editor : public PublicEditor
|
||||
void remove_selected_regions ();
|
||||
void remove_clicked_region ();
|
||||
void edit_region ();
|
||||
void show_midi_list_editor ();
|
||||
void rename_region ();
|
||||
void duplicate_some_regions (RegionSelection&, float times);
|
||||
void duplicate_selection (float times);
|
||||
@ -1493,8 +1497,6 @@ public:
|
||||
void snap_to_with_modifier (nframes64_t& first, GdkEvent const *, int32_t direction = 0, bool for_mark = false);
|
||||
void snap_to (nframes64_t& first, nframes64_t& last, int32_t direction = 0, bool for_mark = false);
|
||||
|
||||
double snap_length_beats (nframes64_t start);
|
||||
|
||||
uint32_t bbt_beat_subdivision;
|
||||
|
||||
/* toolbar */
|
||||
@ -1915,6 +1917,9 @@ public:
|
||||
|
||||
void apply_filter (ARDOUR::Filter&, std::string cmd);
|
||||
|
||||
void apply_midi_note_edit_op_to_region (ARDOUR::MidiOperator& op, MidiRegionView& mrv);
|
||||
void apply_midi_note_edit_op (ARDOUR::MidiOperator& op);
|
||||
|
||||
/* handling cleanup */
|
||||
|
||||
int playlist_deletion_dialog (boost::shared_ptr<ARDOUR::Playlist>);
|
||||
|
@ -79,6 +79,7 @@
|
||||
#include "strip_silence_dialog.h"
|
||||
#include "editor_routes.h"
|
||||
#include "editor_regions.h"
|
||||
#include "quantize_dialog.h"
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
@ -2535,6 +2536,13 @@ Editor::edit_region ()
|
||||
selection->foreach_regionview (&RegionView::show_region_editor);
|
||||
}
|
||||
|
||||
/** Show the midi list editor for the selected MIDI regions */
|
||||
void
|
||||
Editor::show_midi_list_editor ()
|
||||
{
|
||||
selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
|
||||
}
|
||||
|
||||
void
|
||||
Editor::rename_region()
|
||||
{
|
||||
@ -4801,6 +4809,48 @@ Editor::strip_region_silence ()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
|
||||
{
|
||||
vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
|
||||
Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
|
||||
|
||||
v.push_back (selected);
|
||||
|
||||
mrv.selection_as_notelist (v.front());
|
||||
op (v);
|
||||
mrv.replace_selected (v.front());
|
||||
}
|
||||
|
||||
void
|
||||
Editor::apply_midi_note_edit_op (MidiOperator& op)
|
||||
{
|
||||
RegionSelection rs;
|
||||
|
||||
get_regions_for_action (rs);
|
||||
|
||||
if (rs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
begin_reversible_command (op.name ());
|
||||
|
||||
for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
|
||||
RegionSelection::iterator tmp = r;
|
||||
++tmp;
|
||||
|
||||
MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
|
||||
|
||||
if (mrv) {
|
||||
apply_midi_note_edit_op_to_region (op, *mrv);
|
||||
}
|
||||
|
||||
r = tmp;
|
||||
}
|
||||
|
||||
commit_reversible_command ();
|
||||
rs.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
Editor::quantize_region ()
|
||||
@ -4809,9 +4859,18 @@ Editor::quantize_region ()
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: varying meter?
|
||||
Quantize quant (*session, snap_length_beats(0));
|
||||
apply_filter (quant, _("quantize regions"));
|
||||
QuantizeDialog* qd = new QuantizeDialog (*this);
|
||||
|
||||
qd->present ();
|
||||
qd->run ();
|
||||
qd->hide ();
|
||||
|
||||
Quantize quant (*session, Plain,
|
||||
qd->snap_start(), qd->snap_end(),
|
||||
qd->start_grid_size(), qd->end_grid_size(),
|
||||
qd->strength(), qd->swing(), qd->threshold());
|
||||
|
||||
apply_midi_note_edit_op (quant);
|
||||
}
|
||||
|
||||
void
|
||||
@ -4834,13 +4893,6 @@ Editor::apply_filter (Filter& filter, string command)
|
||||
RegionSelection::iterator tmp = r;
|
||||
++tmp;
|
||||
|
||||
MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
|
||||
if (mrv) {
|
||||
if (mrv->midi_region()->apply(filter) == 0) {
|
||||
mrv->redisplay_model();
|
||||
}
|
||||
}
|
||||
|
||||
AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
|
||||
if (arv) {
|
||||
boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
|
||||
|
@ -237,6 +237,7 @@ Keyboard::snooper (GtkWidget *widget, GdkEventKey *event)
|
||||
const AccelKey& ak (k->first);
|
||||
|
||||
if (keyval == ak.get_key() && (Gdk::ModifierType)(event->state | Gdk::RELEASE_MASK) == ak.get_mod()) {
|
||||
cerr << "Suppress auto repeat\n";
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
@ -261,6 +262,7 @@ Keyboard::snooper (GtkWidget *widget, GdkEventKey *event)
|
||||
Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (ts.first.c_str(), ts.second.c_str());
|
||||
if (act) {
|
||||
act->activate();
|
||||
cerr << "use repeat, suppress other\n";
|
||||
ret = true;
|
||||
}
|
||||
break;
|
||||
|
99
gtk2_ardour/midi_list_editor.cc
Normal file
99
gtk2_ardour/midi_list_editor.cc
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright (C) 2009 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "evoral/midi_util.h"
|
||||
#include "ardour/midi_region.h"
|
||||
|
||||
#include "midi_list_editor.h"
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace Gtk;
|
||||
using namespace Glib;
|
||||
using namespace ARDOUR;
|
||||
|
||||
MidiListEditor::MidiListEditor (boost::shared_ptr<MidiRegion> r)
|
||||
: ArdourDialog (r->name(), false, false)
|
||||
, region (r)
|
||||
{
|
||||
model = ListStore::create (columns);
|
||||
view.set_model (model);
|
||||
|
||||
view.append_column (_("Channel"), columns.channel);
|
||||
view.append_column (_("Note"), columns.note);
|
||||
view.append_column (_("Name"), columns.note_name);
|
||||
view.append_column (_("Velocity"), columns.velocity);
|
||||
view.append_column (_("Start"), columns.start);
|
||||
view.append_column (_("End"), columns.end);
|
||||
view.append_column (_("Length"), columns.length);
|
||||
view.set_headers_visible (true);
|
||||
view.set_name (X_("MidiListView"));
|
||||
view.set_rules_hint (true);
|
||||
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
CellRendererText* renderer = dynamic_cast<CellRendererText*>(view.get_column_cell_renderer (i));
|
||||
renderer->property_editable() = true;
|
||||
renderer->signal_edited().connect (mem_fun (*this, &MidiListEditor::edited));
|
||||
}
|
||||
|
||||
scroller.add (view);
|
||||
scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
|
||||
|
||||
redisplay_model ();
|
||||
|
||||
view.show ();
|
||||
scroller.show ();
|
||||
|
||||
get_vbox()->pack_start (scroller);
|
||||
set_size_request (400, 400);
|
||||
}
|
||||
|
||||
MidiListEditor::~MidiListEditor ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MidiListEditor::edited (const Glib::ustring& /* path */, const Glib::ustring& /* text */)
|
||||
{
|
||||
redisplay_model ();
|
||||
}
|
||||
|
||||
void
|
||||
MidiListEditor::redisplay_model ()
|
||||
{
|
||||
view.set_model (Glib::RefPtr<Gtk::ListStore>(0));
|
||||
model->clear ();
|
||||
|
||||
MidiModel::Notes notes = region->midi_source(0)->model()->notes();
|
||||
TreeModel::Row row;
|
||||
|
||||
for (MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) {
|
||||
row = *(model->append());
|
||||
row[columns.channel] = (*i)->channel();
|
||||
row[columns.note_name] = Evoral::midi_note_name ((*i)->note());
|
||||
row[columns.note] = (*i)->note();
|
||||
row[columns.velocity] = (*i)->velocity();
|
||||
row[columns.start] = (*i)->time();
|
||||
row[columns.length] = (*i)->length();
|
||||
row[columns.end] = (*i)->end_time();
|
||||
}
|
||||
|
||||
view.set_model (model);
|
||||
}
|
73
gtk2_ardour/midi_list_editor.h
Normal file
73
gtk2_ardour/midi_list_editor.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright (C) 2009 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __ardour_gtk2_midi_list_editor_h_
|
||||
#define __ardour_gtk2_midi_list_editor_h_
|
||||
|
||||
#include <gtkmm/treeview.h>
|
||||
#include <gtkmm/liststore.h>
|
||||
#include <gtkmm/scrolledwindow.h>
|
||||
|
||||
#include "evoral/types.hpp"
|
||||
|
||||
#include "ardour_dialog.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
class MidiRegion;
|
||||
class MidiModel;
|
||||
};
|
||||
|
||||
class MidiListEditor : public ArdourDialog
|
||||
{
|
||||
public:
|
||||
MidiListEditor(boost::shared_ptr<ARDOUR::MidiRegion>);
|
||||
~MidiListEditor();
|
||||
|
||||
private:
|
||||
struct MidiListModelColumns : public Gtk::TreeModel::ColumnRecord {
|
||||
MidiListModelColumns() {
|
||||
add (channel);
|
||||
add (note);
|
||||
add (note_name);
|
||||
add (velocity);
|
||||
add (start);
|
||||
add (length);
|
||||
add (end);
|
||||
};
|
||||
Gtk::TreeModelColumn<uint8_t> channel;
|
||||
Gtk::TreeModelColumn<uint8_t> note;
|
||||
Gtk::TreeModelColumn<std::string> note_name;
|
||||
Gtk::TreeModelColumn<uint8_t> velocity;
|
||||
Gtk::TreeModelColumn<Evoral::MusicalTime> start;
|
||||
Gtk::TreeModelColumn<Evoral::MusicalTime> length;
|
||||
Gtk::TreeModelColumn<Evoral::MusicalTime> end;
|
||||
};
|
||||
|
||||
MidiListModelColumns columns;
|
||||
Glib::RefPtr<Gtk::ListStore> model;
|
||||
Gtk::TreeView view;
|
||||
Gtk::ScrolledWindow scroller;
|
||||
|
||||
boost::shared_ptr<ARDOUR::MidiRegion> region;
|
||||
|
||||
void edited (const Glib::ustring&, const Glib::ustring&);
|
||||
void redisplay_model ();
|
||||
};
|
||||
|
||||
#endif /* __ardour_gtk2_midi_list_editor_h_ */
|
@ -50,6 +50,7 @@
|
||||
#include "gui_thread.h"
|
||||
#include "keyboard.h"
|
||||
#include "midi_cut_buffer.h"
|
||||
#include "midi_list_editor.h"
|
||||
#include "midi_region_view.h"
|
||||
#include "midi_streamview.h"
|
||||
#include "midi_time_axis.h"
|
||||
@ -160,9 +161,7 @@ MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
|
||||
midi_region()->midi_source(0)->load_model();
|
||||
}
|
||||
|
||||
cerr << "Looking up model for midi region\n";
|
||||
_model = midi_region()->midi_source(0)->model();
|
||||
cerr << " model = " << _model << endl;
|
||||
_enable_display = false;
|
||||
|
||||
RegionView::init (basic_color, false);
|
||||
@ -236,53 +235,84 @@ MidiRegionView::canvas_event(GdkEvent* ev)
|
||||
break;
|
||||
|
||||
case GDK_KEY_PRESS:
|
||||
if (ev->key.keyval == GDK_Shift_L || ev->key.keyval == GDK_Control_L) {
|
||||
|
||||
/* since GTK bindings are generally activated on press, and since
|
||||
detectable auto-repeat is the name of the game and only sends
|
||||
repeated presses, carry out key actions at key press, not release.
|
||||
*/
|
||||
|
||||
if (ev->key.keyval == GDK_Alt_L || ev->key.keyval == GDK_Alt_R){
|
||||
_mouse_state = SelectTouchDragging;
|
||||
return true;
|
||||
|
||||
} else if (ev->key.keyval == GDK_Escape) {
|
||||
clear_selection();
|
||||
_mouse_state = None;
|
||||
}
|
||||
return false;
|
||||
|
||||
case GDK_KEY_RELEASE:
|
||||
if (ev->key.keyval == GDK_Delete) {
|
||||
} else if (ev->key.keyval == GDK_comma || ev->key.keyval == GDK_period) {
|
||||
|
||||
bool start = (ev->key.keyval == GDK_comma);
|
||||
bool end = (ev->key.keyval == GDK_period);
|
||||
bool shorter = Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier);
|
||||
bool fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
|
||||
|
||||
change_note_lengths (fine, shorter, start, end);
|
||||
|
||||
return true;
|
||||
|
||||
} else if (ev->key.keyval == GDK_Delete) {
|
||||
|
||||
delete_selection();
|
||||
apply_command();
|
||||
return true;
|
||||
} else if (ev->key.keyval == GDK_Shift_L || ev->key.keyval == GDK_Control_L) {
|
||||
_mouse_state = None;
|
||||
return true;
|
||||
|
||||
} else if (ev->key.keyval == GDK_Tab) {
|
||||
|
||||
if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) {
|
||||
goto_previous_note ();
|
||||
} else {
|
||||
goto_next_note ();
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (ev->key.keyval == GDK_Up) {
|
||||
|
||||
if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) {
|
||||
change_velocities (1, true);
|
||||
} else {
|
||||
transpose (true, false);
|
||||
transpose (true, Keyboard::modifier_state_equals (ev->key.state, Keyboard::SecondaryModifier));
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (ev->key.keyval == GDK_Down) {
|
||||
|
||||
|
||||
if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) {
|
||||
change_velocities (-1, true);
|
||||
} else {
|
||||
transpose (false, false);
|
||||
transpose (false, Keyboard::modifier_state_equals (ev->key.state, Keyboard::SecondaryModifier));
|
||||
}
|
||||
} else if (ev->key.keyval == GDK_Left) {
|
||||
return true;
|
||||
|
||||
} else if (ev->key.keyval == GDK_Left) {
|
||||
|
||||
nudge_notes (false);
|
||||
return true;
|
||||
|
||||
} else if (ev->key.keyval == GDK_Right) {
|
||||
|
||||
nudge_notes (true);
|
||||
return true;
|
||||
|
||||
} else if (ev->key.keyval == GDK_Control_L) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case GDK_KEY_RELEASE:
|
||||
if (ev->key.keyval == GDK_Alt_L || ev->key.keyval == GDK_Alt_R) {
|
||||
_mouse_state = None;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -416,7 +446,9 @@ MidiRegionView::canvas_event(GdkEvent* ev)
|
||||
group->ungrab(ev->button.time);
|
||||
event_frame = trackview.editor().pixel_to_frame(event_x);
|
||||
|
||||
if (_pressed_button != 1) {
|
||||
if (ev->button.button == 3) {
|
||||
return false;
|
||||
} else if (_pressed_button != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -429,7 +461,9 @@ MidiRegionView::canvas_event(GdkEvent* ev)
|
||||
break;
|
||||
case MouseObject:
|
||||
create_note_at(event_x, event_y, _default_note_length);
|
||||
default: break;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_mouse_state = None;
|
||||
break;
|
||||
@ -459,6 +493,12 @@ MidiRegionView::canvas_event(GdkEvent* ev)
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::show_list_editor ()
|
||||
{
|
||||
MidiListEditor* mle = new MidiListEditor (midi_region());
|
||||
mle->show ();
|
||||
}
|
||||
|
||||
/** Add a note to the model, and the view, at a canvas (click) coordinate.
|
||||
* \param x horizontal position in pixels
|
||||
@ -578,7 +618,26 @@ MidiRegionView::apply_command()
|
||||
_marked_for_selection.clear();
|
||||
_marked_for_velocity.clear();
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::apply_command_as_subcommand()
|
||||
{
|
||||
if (!_delta_command) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark all selected notes for selection when model reloads
|
||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
||||
_marked_for_selection.insert((*i)->note());
|
||||
}
|
||||
|
||||
_model->apply_command_as_subcommand(trackview.session(), _delta_command);
|
||||
_delta_command = NULL;
|
||||
midi_view()->midi_track()->diskstream()->playlist_modified();
|
||||
|
||||
_marked_for_selection.clear();
|
||||
_marked_for_velocity.clear();
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::abort_command()
|
||||
@ -673,7 +732,7 @@ void
|
||||
MidiRegionView::display_sysexes()
|
||||
{
|
||||
for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
|
||||
ARDOUR::MidiModel::TimeType time = (*i)->time();
|
||||
Evoral::MusicalTime time = (*i)->time();
|
||||
assert(time >= 0);
|
||||
|
||||
ostringstream str;
|
||||
@ -1033,8 +1092,12 @@ MidiRegionView::add_note(const boost::shared_ptr<NoteType> note)
|
||||
|
||||
if (event) {
|
||||
if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
|
||||
cerr << "Added note " << *note << " was marked for selection\n";
|
||||
note_selected(event, true);
|
||||
} else {
|
||||
cerr << "Added note " << *note << " not selected\n";
|
||||
}
|
||||
|
||||
if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
|
||||
event->show_velocity();
|
||||
}
|
||||
@ -1190,9 +1253,7 @@ MidiRegionView::delete_selection()
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_delta_command) {
|
||||
_delta_command = _model->new_delta_command("delete selection");
|
||||
}
|
||||
start_delta_command (_("delete selection"));
|
||||
|
||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
||||
if ((*i)->selected()) {
|
||||
@ -1201,6 +1262,8 @@ MidiRegionView::delete_selection()
|
||||
}
|
||||
|
||||
_selection.clear();
|
||||
|
||||
apply_command ();
|
||||
}
|
||||
|
||||
void
|
||||
@ -1252,8 +1315,8 @@ MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool
|
||||
} else {
|
||||
/* find end of latest note selected, select all between that and the start of "ev" */
|
||||
|
||||
MidiModel::TimeType earliest = DBL_MAX;
|
||||
MidiModel::TimeType latest = 0;
|
||||
Evoral::MusicalTime earliest = DBL_MAX;
|
||||
Evoral::MusicalTime latest = 0;
|
||||
|
||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
||||
if ((*i)->note()->end_time() > latest) {
|
||||
@ -1265,18 +1328,24 @@ MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool
|
||||
}
|
||||
|
||||
if (ev->note()->end_time() > latest) {
|
||||
earliest = latest;
|
||||
latest = ev->note()->end_time();
|
||||
} else {
|
||||
}
|
||||
|
||||
if (ev->note()->time() < earliest) {
|
||||
earliest = ev->note()->time();
|
||||
}
|
||||
|
||||
|
||||
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
||||
if ((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) {
|
||||
add_to_selection (*i);
|
||||
}
|
||||
|
||||
if ((*i)->note()->end_time() > latest) {
|
||||
/* find notes entirely within OR spanning the earliest..latest range */
|
||||
|
||||
if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
|
||||
((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
|
||||
add_to_selection (*i);
|
||||
}
|
||||
|
||||
if ((*i)->note()->time() > latest) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1431,13 +1500,32 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote)
|
||||
const boost::shared_ptr<NoteType> copy(new NoteType(*(*i)->note().get()));
|
||||
|
||||
nframes64_t start_frames = beats_to_frames((*i)->note()->time());
|
||||
|
||||
cerr << "starting at " << (*i)->note()->time()
|
||||
<< " (" << start_frames << ") delta on drag = " << dt << endl;
|
||||
|
||||
if (dt >= 0) {
|
||||
cerr << "Motion was " << snap_frame_to_frame(trackview.editor().pixel_to_frame(dt)) << endl;
|
||||
start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt));
|
||||
} else {
|
||||
cerr << "rev Motion was " << snap_frame_to_frame(trackview.editor().pixel_to_frame(dt)) << endl;
|
||||
start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt));
|
||||
}
|
||||
|
||||
copy->set_time(frames_to_beats(start_frames));
|
||||
cerr << "start frame will be " << start_frames << " vs. region "
|
||||
<< _region->position ()
|
||||
<< endl;
|
||||
|
||||
Evoral::MusicalTime new_time = frames_to_beats(start_frames);
|
||||
|
||||
if (new_time < 0) {
|
||||
i = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
copy->set_time (new_time);
|
||||
|
||||
cerr << "copy time = " << copy->time() << endl;
|
||||
|
||||
uint8_t original_pitch = (*i)->note()->note();
|
||||
uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
|
||||
@ -1457,6 +1545,7 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote)
|
||||
copy->set_note(new_pitch);
|
||||
|
||||
command_remove_note(*i);
|
||||
cerr << "Adding note: " << *copy << endl;
|
||||
command_add_note(copy, (*i)->selected());
|
||||
|
||||
i = next;
|
||||
@ -1671,7 +1760,63 @@ MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool rela
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::change_note_time (CanvasNoteEvent* event, MidiModel::TimeType delta, bool relative)
|
||||
MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
|
||||
{
|
||||
/* NOTE: the semantics of the two delta arguments are slightly subtle:
|
||||
|
||||
front_delta: if positive - move the start of the note later in time (shortening it)
|
||||
if negative - move the start of the note earlier in time (lengthening it)
|
||||
|
||||
end_delta: if positive - move the end of the note later in time (lengthening it)
|
||||
if negative - move the end of the note earlier in time (shortening it)
|
||||
*/
|
||||
|
||||
const boost::shared_ptr<NoteType> copy(new NoteType(*(event->note().get())));
|
||||
|
||||
cerr << "Trim front by " << front_delta << " end by " << end_delta << endl;
|
||||
|
||||
if (front_delta) {
|
||||
if (front_delta < 0) {
|
||||
if (copy->time() < -front_delta) {
|
||||
copy->set_time (0);
|
||||
} else {
|
||||
copy->set_time (copy->time() + front_delta); // moves earlier
|
||||
}
|
||||
/* start moved toward zero, so move the end point out to where it used to be.
|
||||
Note that front_delta is negative, so this increases the length.
|
||||
*/
|
||||
copy->set_length (copy->length() - front_delta);
|
||||
} else {
|
||||
Evoral::MusicalTime new_pos = copy->time() + front_delta;
|
||||
|
||||
if (new_pos >= copy->end_time()) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy->set_time (copy->time() + front_delta);
|
||||
|
||||
/* start moved toward the end, so move the end point back to where it used to be */
|
||||
copy->set_length (copy->length() - front_delta);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (end_delta) {
|
||||
if (end_delta < 0) {
|
||||
if (copy->length() < -end_delta) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
copy->set_length (copy->length() + end_delta);
|
||||
}
|
||||
|
||||
command_remove_note(event);
|
||||
command_add_note(copy, event->selected(), false);
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
|
||||
{
|
||||
const boost::shared_ptr<NoteType> copy(new NoteType(*(event->note().get())));
|
||||
|
||||
@ -1744,6 +1889,44 @@ MidiRegionView::transpose (bool up, bool fine)
|
||||
apply_command ();
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::change_note_lengths (bool fine, bool shorter, bool start, bool end)
|
||||
{
|
||||
Evoral::MusicalTime delta;
|
||||
|
||||
if (fine) {
|
||||
delta = 1.0/128.0;
|
||||
} else {
|
||||
/* grab the current grid distance */
|
||||
bool success;
|
||||
delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
|
||||
if (!success) {
|
||||
/* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
|
||||
cerr << "Grid type not available as beats - TO BE FIXED\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (shorter) {
|
||||
delta = -delta;
|
||||
}
|
||||
|
||||
start_delta_command (_("change note lengths"));
|
||||
|
||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
|
||||
Selection::iterator next = i;
|
||||
++next;
|
||||
|
||||
/* note the negation of the delta for start */
|
||||
|
||||
trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
|
||||
i = next;
|
||||
}
|
||||
|
||||
apply_command ();
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::nudge_notes (bool forward)
|
||||
{
|
||||
@ -1757,23 +1940,36 @@ MidiRegionView::nudge_notes (bool forward)
|
||||
*/
|
||||
|
||||
nframes64_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
|
||||
nframes64_t next_pos = ref_point;
|
||||
nframes64_t unused;
|
||||
nframes64_t distance;
|
||||
|
||||
if (forward) {
|
||||
next_pos += 1;
|
||||
} else {
|
||||
if (next_pos == 0) {
|
||||
return;
|
||||
if ((distance = trackview.editor().get_nudge_distance (ref_point, unused)) == 0) {
|
||||
|
||||
/* no nudge distance set - use grid */
|
||||
|
||||
nframes64_t next_pos = ref_point;
|
||||
|
||||
if (forward) {
|
||||
/* XXX need check on max_frames, but that needs max_frames64 or something */
|
||||
next_pos += 1;
|
||||
} else {
|
||||
if (next_pos == 0) {
|
||||
return;
|
||||
}
|
||||
next_pos -= 1;
|
||||
}
|
||||
next_pos -= 1;
|
||||
|
||||
cerr << "ref point was " << ref_point << " next was " << next_pos;
|
||||
trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
|
||||
distance = ref_point - next_pos;
|
||||
cerr << " final is " << next_pos << " distance = " << distance << endl;
|
||||
}
|
||||
|
||||
if (distance == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
|
||||
nframes64_t distance = ref_point - next_pos;
|
||||
|
||||
cerr << "ref was " << ref_point << " next is " << next_pos << endl;
|
||||
|
||||
MidiModel::TimeType delta = frames_to_beats (fabs (distance));
|
||||
Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
|
||||
|
||||
if (!forward) {
|
||||
delta = -delta;
|
||||
@ -1911,7 +2107,7 @@ MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
|
||||
MidiCutBuffer*
|
||||
MidiRegionView::selection_as_cut_buffer () const
|
||||
{
|
||||
Evoral::Sequence<MidiModel::TimeType>::Notes notes;
|
||||
NoteList notes;
|
||||
|
||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
|
||||
notes.push_back (boost::shared_ptr<NoteType> (new NoteType (*((*i)->note().get()))));
|
||||
@ -1919,7 +2115,8 @@ MidiRegionView::selection_as_cut_buffer () const
|
||||
|
||||
/* sort them into time order */
|
||||
|
||||
sort (notes.begin(), notes.end(), Evoral::Sequence<MidiModel::TimeType>::note_time_comparator);
|
||||
Evoral::Sequence<Evoral::MusicalTime>::LaterNoteComparator cmp;
|
||||
sort (notes.begin(), notes.end(), cmp);
|
||||
|
||||
MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
|
||||
cb->set (notes);
|
||||
@ -1936,10 +2133,10 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
|
||||
|
||||
start_delta_command (_("paste"));
|
||||
|
||||
MidiModel::TimeType beat_delta;
|
||||
MidiModel::TimeType paste_pos_beats;
|
||||
MidiModel::TimeType duration;
|
||||
MidiModel::TimeType end_point;
|
||||
Evoral::MusicalTime beat_delta;
|
||||
Evoral::MusicalTime paste_pos_beats;
|
||||
Evoral::MusicalTime duration;
|
||||
Evoral::MusicalTime end_point;
|
||||
|
||||
duration = mcb.notes().back()->end_time() - mcb.notes().front()->time();
|
||||
paste_pos_beats = frames_to_beats (pos - _region->position());
|
||||
@ -1950,7 +2147,7 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
|
||||
|
||||
for (int n = 0; n < (int) times; ++n) {
|
||||
|
||||
for (Evoral::Sequence<MidiModel::TimeType>::Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
|
||||
for (NoteList::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
|
||||
|
||||
boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
|
||||
copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
|
||||
@ -2033,3 +2230,66 @@ MidiRegionView::goto_previous_note ()
|
||||
|
||||
unique_select (*(_events.rbegin()));
|
||||
}
|
||||
|
||||
MidiModel::DeltaCommand*
|
||||
MidiRegionView::apply (Filter& filter, const std::string& name)
|
||||
{
|
||||
MidiModel::Notes before = _model->notes();
|
||||
MidiModel::Notes after;
|
||||
|
||||
_region->apply (filter);
|
||||
|
||||
redisplay_model ();
|
||||
|
||||
after = _model->notes();
|
||||
|
||||
start_delta_command (name);
|
||||
|
||||
for (MidiModel::Notes::iterator i = before.begin(); i != before.end(); ++i) {
|
||||
_delta_command->add (*i);
|
||||
}
|
||||
|
||||
for (MidiModel::Notes::iterator i = after.begin(); i != after.end(); ++i) {
|
||||
_delta_command->remove (*i);
|
||||
}
|
||||
|
||||
return _delta_command;
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::selection_as_notelist (NoteList& selected)
|
||||
{
|
||||
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
|
||||
if ((*i)->selected()) {
|
||||
/* make a copy of the original */
|
||||
selected.push_back (boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> >
|
||||
(new Evoral::Note<Evoral::MusicalTime> (*((*i)->note()))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegionView::replace_selected (NoteList& replacements)
|
||||
{
|
||||
start_delta_command ("whatever");
|
||||
|
||||
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
|
||||
Selection::iterator tmp;
|
||||
tmp = i;
|
||||
++tmp;
|
||||
|
||||
command_remove_note (*i);
|
||||
remove_from_selection (*i);
|
||||
|
||||
i = tmp;
|
||||
}
|
||||
|
||||
_selection.clear ();
|
||||
|
||||
for (NoteList::iterator i = replacements.begin(); i != replacements.end(); ++i) {
|
||||
command_add_note (*i, true, false);
|
||||
}
|
||||
|
||||
apply_command_as_subcommand ();
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
||||
namespace ARDOUR {
|
||||
class MidiRegion;
|
||||
class MidiModel;
|
||||
class Filter;
|
||||
};
|
||||
|
||||
namespace MIDI {
|
||||
@ -62,7 +63,8 @@ class MidiCutBuffer;
|
||||
class MidiRegionView : public RegionView
|
||||
{
|
||||
public:
|
||||
typedef Evoral::Note<ARDOUR::MidiModel::TimeType> NoteType;
|
||||
typedef Evoral::Note<Evoral::MusicalTime> NoteType;
|
||||
typedef Evoral::Sequence<Evoral::MusicalTime>::Notes NoteList;
|
||||
|
||||
MidiRegionView (ArdourCanvas::Group *,
|
||||
RouteTimeAxisView&,
|
||||
@ -86,6 +88,8 @@ class MidiRegionView : public RegionView
|
||||
inline MidiStreamView* midi_stream_view() const
|
||||
{ return midi_view()->midi_view(); }
|
||||
|
||||
ARDOUR::MidiModel::DeltaCommand* apply (ARDOUR::Filter&, const std::string& name);
|
||||
|
||||
void set_height (double);
|
||||
void apply_note_range(uint8_t lowest, uint8_t highest, bool force=false);
|
||||
|
||||
@ -168,6 +172,7 @@ class MidiRegionView : public RegionView
|
||||
void command_remove_note(ArdourCanvas::CanvasNoteEvent* ev);
|
||||
|
||||
void apply_command();
|
||||
void apply_command_as_subcommand();
|
||||
void abort_command();
|
||||
|
||||
void note_entered(ArdourCanvas::CanvasNoteEvent* ev);
|
||||
@ -264,9 +269,15 @@ class MidiRegionView : public RegionView
|
||||
void goto_previous_note ();
|
||||
void goto_next_note ();
|
||||
void change_velocities (int8_t velocity, bool relative);
|
||||
void change_note_lengths (bool, bool, bool start, bool end);
|
||||
void transpose (bool up, bool fine);
|
||||
void nudge_notes (bool forward);
|
||||
|
||||
void show_list_editor ();
|
||||
|
||||
void selection_as_notelist (NoteList& selected);
|
||||
void replace_selected (NoteList& replacements);
|
||||
|
||||
protected:
|
||||
/** Allows derived types to specify their visibility requirements
|
||||
* to the TimeAxisViewItem parent class.
|
||||
@ -308,6 +319,8 @@ class MidiRegionView : public RegionView
|
||||
void change_note_velocity(ArdourCanvas::CanvasNoteEvent* ev, int8_t vel, bool relative=false);
|
||||
void change_note_note(ArdourCanvas::CanvasNoteEvent* ev, int8_t note, bool relative=false);
|
||||
void change_note_time(ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::MidiModel::TimeType, bool relative=false);
|
||||
void trim_note(ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::MidiModel::TimeType start_delta,
|
||||
ARDOUR::MidiModel::TimeType end_delta);
|
||||
|
||||
void clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev);
|
||||
void clear_selection() { clear_selection_except(NULL); }
|
||||
@ -361,6 +374,7 @@ class MidiRegionView : public RegionView
|
||||
|
||||
/* connection used to connect to model's ContentChanged signal */
|
||||
sigc::connection content_connection;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __gtk_ardour_midi_region_view_h__ */
|
||||
|
@ -331,11 +331,7 @@ PluginUIWindow::create_lv2_editor(boost::shared_ptr<PluginInsert> insert)
|
||||
bool
|
||||
PluginUIWindow::on_key_press_event (GdkEventKey* event)
|
||||
{
|
||||
if (!key_press_focus_accelerator_handler (*this, event)) {
|
||||
return PublicEditor::instance().on_key_press_event(event);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return relay_key_press (event, this);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <jack/types.h>
|
||||
#include <sigc++/signal.h>
|
||||
|
||||
#include "evoral/types.hpp"
|
||||
#include "ardour/route_group.h"
|
||||
|
||||
#include "pbd/statefuldestructible.h"
|
||||
@ -121,9 +122,6 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulThingWithGoingAway
|
||||
/** Snap a value according to the current snap setting. */
|
||||
virtual void snap_to (nframes64_t& first, int32_t direction = 0, bool for_mark = false) = 0;
|
||||
|
||||
/** Get the current snap value in beats */
|
||||
virtual double snap_length_beats (nframes64_t start) = 0;
|
||||
|
||||
/** Undo some transactions.
|
||||
* @param n Number of transactions to undo.
|
||||
*/
|
||||
@ -264,7 +262,7 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulThingWithGoingAway
|
||||
virtual void foreach_time_axis_view (sigc::slot<void,TimeAxisView&>) = 0;
|
||||
virtual void add_to_idle_resize (TimeAxisView*, int32_t) = 0;
|
||||
virtual nframes64_t get_nudge_distance (nframes64_t pos, nframes64_t& next) = 0;
|
||||
|
||||
virtual Evoral::MusicalTime get_grid_type_as_beats (bool& success, nframes64_t position) = 0;
|
||||
|
||||
#ifdef WITH_CMT
|
||||
virtual void add_imageframe_time_axis(const std::string & track_name, void*) = 0;
|
||||
@ -350,7 +348,7 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulThingWithGoingAway
|
||||
|
||||
static PublicEditor* _instance;
|
||||
|
||||
friend class PluginUIWindow;
|
||||
friend bool relay_key_press (GdkEventKey*, Gtk::Window*);
|
||||
};
|
||||
|
||||
#endif // __gtk_ardour_public_editor_h__
|
||||
|
@ -181,6 +181,7 @@ class Selection : public sigc::trackable
|
||||
|
||||
void foreach_region (void (ARDOUR::Region::*method)(void));
|
||||
void foreach_regionview (void (RegionView::*method)(void));
|
||||
void foreach_midi_regionview (void (MidiRegionView::*method)(void));
|
||||
template<class A> void foreach_region (void (ARDOUR::Region::*method)(A), A arg);
|
||||
|
||||
private:
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "selection.h"
|
||||
#include "region_view.h"
|
||||
#include "midi_region_view.h"
|
||||
|
||||
inline void
|
||||
Selection::foreach_region (void (ARDOUR::Region::*method)(void)) {
|
||||
@ -45,6 +46,16 @@ Selection::foreach_regionview (void (RegionView::*method)(void)) {
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
Selection::foreach_midi_regionview (void (MidiRegionView::*method)(void)) {
|
||||
for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
|
||||
if (mrv) {
|
||||
(mrv->*(method))();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class A> inline void
|
||||
Selection::foreach_region (void (ARDOUR::Region::*method)(A), A arg) {
|
||||
for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "ardour/filesystem_paths.h"
|
||||
|
||||
#include "ardour_ui.h"
|
||||
#include "public_editor.h"
|
||||
#include "keyboard.h"
|
||||
#include "utils.h"
|
||||
#include "i18n.h"
|
||||
@ -508,6 +509,16 @@ extern "C" {
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
relay_key_press (GdkEventKey* ev, Gtk::Window* win)
|
||||
{
|
||||
if (!key_press_focus_accelerator_handler (*win, ev)) {
|
||||
return PublicEditor::instance().on_key_press_event(ev);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
|
||||
{
|
||||
|
@ -82,6 +82,7 @@ bool canvas_item_visible (ArdourCanvas::Item* item);
|
||||
|
||||
void set_color (Gdk::Color&, int);
|
||||
|
||||
bool relay_key_press (GdkEventKey* ev, Gtk::Window* win);
|
||||
bool key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev);
|
||||
bool possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval);
|
||||
|
||||
|
@ -127,6 +127,7 @@ gtk2_ardour_sources = [
|
||||
'marker.cc',
|
||||
'midi_channel_selector.cc',
|
||||
'midi_cut_buffer.cc',
|
||||
'midi_list_editor.cc',
|
||||
'midi_port_dialog.cc',
|
||||
'midi_region_view.cc',
|
||||
'midi_scroomer.cc',
|
||||
@ -157,6 +158,7 @@ gtk2_ardour_sources = [
|
||||
'processor_box.cc',
|
||||
'prompter.cc',
|
||||
'public_editor.cc',
|
||||
'quantize_dialog.cc',
|
||||
'rc_option_editor.cc',
|
||||
'region_gain_line.cc',
|
||||
'region_selection.cc',
|
||||
|
@ -47,7 +47,7 @@ class MidiSource;
|
||||
* Because of this MIDI controllers and automatable controllers/widgets/etc
|
||||
* are easily interchangeable.
|
||||
*/
|
||||
class MidiModel : public AutomatableSequence<double> {
|
||||
class MidiModel : public AutomatableSequence<Evoral::MusicalTime> {
|
||||
public:
|
||||
typedef double TimeType;
|
||||
|
||||
@ -91,6 +91,7 @@ public:
|
||||
|
||||
MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit");
|
||||
void apply_command(Session& session, Command* cmd);
|
||||
void apply_command_as_subcommand(Session& session, Command* cmd);
|
||||
|
||||
bool write_to(boost::shared_ptr<MidiSource> source);
|
||||
|
||||
|
42
libs/ardour/ardour/midi_operator.h
Normal file
42
libs/ardour/ardour/midi_operator.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright (C) 2009 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __libardour_midi_operator_h__
|
||||
#define __libardour_midi_operator_h__
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "evoral/types.hpp"
|
||||
#include "evoral/Sequence.hpp"
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class MidiOperator {
|
||||
public:
|
||||
MidiOperator() {}
|
||||
virtual ~MidiOperator() {}
|
||||
|
||||
virtual int operator() (std::vector<Evoral::Sequence<Evoral::MusicalTime>::Notes>&) = 0;
|
||||
virtual std::string name() const = 0;
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
#endif /* __libardour_midi_operator_h__ */
|
@ -65,7 +65,7 @@ class MidiSource : virtual public Source
|
||||
sframes_t source_start,
|
||||
nframes_t cnt);
|
||||
|
||||
virtual void append_event_unlocked_beats(const Evoral::Event<double>& ev) = 0;
|
||||
virtual void append_event_unlocked_beats(const Evoral::Event<Evoral::MusicalTime>& ev) = 0;
|
||||
|
||||
virtual void append_event_unlocked_frames(const Evoral::Event<nframes_t>& ev,
|
||||
sframes_t source_start) = 0;
|
||||
@ -128,7 +128,7 @@ class MidiSource : virtual public Source
|
||||
boost::shared_ptr<MidiModel> _model;
|
||||
bool _writing;
|
||||
|
||||
mutable Evoral::Sequence<double>::const_iterator _model_iter;
|
||||
mutable Evoral::Sequence<Evoral::MusicalTime>::const_iterator _model_iter;
|
||||
|
||||
mutable double _length_beats;
|
||||
mutable sframes_t _last_read_end;
|
||||
|
@ -84,7 +84,8 @@ protected:
|
||||
int _set_state (const XMLNode&, bool call_base);
|
||||
|
||||
private:
|
||||
void write_controller_messages(MidiBuffer& buf, sframes_t start_frame, sframes_t end_frame, nframes_t nframes);
|
||||
void write_out_of_band_data (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame,
|
||||
nframes_t nframes);
|
||||
|
||||
int set_diskstream (boost::shared_ptr<MidiDiskstream> ds);
|
||||
void use_new_diskstream ();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2007 Paul Davis
|
||||
Copyright (C) 2007-2009 Paul Davis
|
||||
Author: Dave Robillard
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@ -21,19 +21,33 @@
|
||||
#ifndef __ardour_quantize_h__
|
||||
#define __ardour_quantize_h__
|
||||
|
||||
#include "ardour/filter.h"
|
||||
#include "ardour/types.h"
|
||||
#include "ardour/midi_operator.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class Quantize : public Filter {
|
||||
public:
|
||||
Quantize (ARDOUR::Session&, double q);
|
||||
~Quantize ();
|
||||
class Session;
|
||||
|
||||
int run (boost::shared_ptr<ARDOUR::Region>);
|
||||
class Quantize : public MidiOperator {
|
||||
public:
|
||||
Quantize (ARDOUR::Session&, QuantizeType type,
|
||||
bool snap_start, bool snap_end,
|
||||
double start_grid, double end_grid,
|
||||
float strength, float swing, float threshold);
|
||||
~Quantize ();
|
||||
|
||||
int operator() (std::vector<Evoral::Sequence<Evoral::MusicalTime>::Notes>&);
|
||||
std::string name() const { return std::string ("quantize"); }
|
||||
|
||||
private:
|
||||
double _q;
|
||||
ARDOUR::Session& session;
|
||||
bool _snap_start;
|
||||
bool _snap_end;
|
||||
double _start_grid;
|
||||
double _end_grid;
|
||||
float _strength;
|
||||
float _swing;
|
||||
float _threshold;
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
@ -326,6 +326,9 @@ class Route : public SessionObject, public AutomatableControls
|
||||
void passthru (sframes_t start_frame, sframes_t end_frame,
|
||||
nframes_t nframes, int declick);
|
||||
|
||||
virtual void write_out_of_band_data (BufferSet& /* bufs */, sframes_t /* start_frame */, sframes_t /* end_frame */,
|
||||
nframes_t /* nframes */) {}
|
||||
|
||||
virtual void process_output_buffers (BufferSet& bufs,
|
||||
sframes_t start_frame, sframes_t end_frame,
|
||||
nframes_t nframes, bool with_processors, int declick);
|
||||
|
@ -51,7 +51,7 @@ public:
|
||||
|
||||
bool set_name (const std::string& newname) { return (set_source_name(newname, false) == 0); }
|
||||
|
||||
void append_event_unlocked_beats (const Evoral::Event<double>& ev);
|
||||
void append_event_unlocked_beats (const Evoral::Event<Evoral::MusicalTime>& ev);
|
||||
void append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev, sframes_t source_start);
|
||||
|
||||
void mark_streaming_midi_write_started (NoteMode mode, sframes_t start_time);
|
||||
|
@ -204,7 +204,7 @@ class TempoMap : public PBD::StatefulDestructible
|
||||
|
||||
nframes_t round_to_bar (nframes_t frame, int dir);
|
||||
nframes_t round_to_beat (nframes_t frame, int dir);
|
||||
nframes_t round_to_beat_subdivision (nframes_t fr, int sub_num);
|
||||
nframes_t round_to_beat_subdivision (nframes_t fr, int sub_num, int dir);
|
||||
nframes_t round_to_tick (nframes_t frame, int dir);
|
||||
|
||||
void set_length (nframes_t frames);
|
||||
|
@ -446,6 +446,12 @@ namespace ARDOUR {
|
||||
Rectified
|
||||
};
|
||||
|
||||
enum QuantizeType {
|
||||
Plain,
|
||||
Legato,
|
||||
Groove
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
std::istream& operator>>(std::istream& o, ARDOUR::SampleFormat& sf);
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include "pbd/enumwriter.h"
|
||||
#include "pbd/convert.h"
|
||||
|
||||
#include "ardour/midi_buffer.h"
|
||||
|
||||
#include "ardour/delivery.h"
|
||||
#include "ardour/audio_buffer.h"
|
||||
#include "ardour/amp.h"
|
||||
|
@ -70,6 +70,19 @@ MidiModel::apply_command(Session& session, Command* cmd)
|
||||
set_edited(true);
|
||||
}
|
||||
|
||||
/** Apply a command as part of a larger reversible transaction
|
||||
*
|
||||
* Ownership of cmd is taken, it must not be deleted by the caller.
|
||||
* The command will constitute one item on the undo stack.
|
||||
*/
|
||||
void
|
||||
MidiModel::apply_command_as_subcommand(Session& session, Command* cmd)
|
||||
{
|
||||
(*cmd)();
|
||||
session.add_command(cmd);
|
||||
set_edited(true);
|
||||
}
|
||||
|
||||
|
||||
// DeltaCommand
|
||||
|
||||
@ -107,17 +120,19 @@ 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
|
||||
|
||||
|
||||
Glib::Mutex::Lock lm (_model->_midi_source->mutex());
|
||||
_model->_midi_source->invalidate(); // release model read lock
|
||||
_model->write_lock();
|
||||
|
||||
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
|
||||
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
|
||||
_model->add_note_unlocked(*i);
|
||||
}
|
||||
|
||||
for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
|
||||
for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
|
||||
_model->remove_note_unlocked(*i);
|
||||
|
||||
}
|
||||
|
||||
_model->write_unlock();
|
||||
_model->ContentsChanged(); /* EMIT SIGNAL */
|
||||
}
|
||||
@ -132,11 +147,13 @@ MidiModel::DeltaCommand::undo()
|
||||
_model->_midi_source->invalidate(); // release model read lock
|
||||
_model->write_lock();
|
||||
|
||||
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
|
||||
for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
|
||||
_model->remove_note_unlocked(*i);
|
||||
}
|
||||
|
||||
for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
|
||||
for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) {
|
||||
_model->add_note_unlocked(*i);
|
||||
}
|
||||
|
||||
_model->write_unlock();
|
||||
_model->ContentsChanged(); /* EMIT SIGNAL */
|
||||
|
@ -24,7 +24,7 @@ using namespace std;
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
/** Read a block of MIDI events from buffer.
|
||||
/** Read a block of MIDI events from buffer into a MidiBuffer.
|
||||
*
|
||||
* Timestamps of events returned are relative to start (i.e. event with stamp 0
|
||||
* occurred at start), with offset added.
|
||||
@ -106,11 +106,13 @@ MidiRingBuffer<T>::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes
|
||||
// write MIDI buffer contents
|
||||
success = Evoral::EventRingBuffer<T>::full_read(ev_size, write_loc);
|
||||
|
||||
/*cerr << "wrote MidiEvent to Buffer: ";
|
||||
#if 0
|
||||
cerr << "wrote MidiEvent to Buffer: " << hex;
|
||||
for (size_t i=0; i < ev_size; ++i) {
|
||||
printf("%X ", write_loc[i]);
|
||||
cerr << (int) write_loc[i] << ' ';
|
||||
}
|
||||
printf("\n");*/
|
||||
cerr << dec << endl;
|
||||
#endif
|
||||
|
||||
if (success) {
|
||||
if (is_channel_event(status) && get_channel_mode() == ForceChannel) {
|
||||
|
@ -401,7 +401,6 @@ MidiTrack::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
|
||||
_silent = false;
|
||||
|
||||
if ((dret = diskstream->process (transport_frame, nframes, can_record, rec_monitors_input)) != 0) {
|
||||
|
||||
silence (nframes);
|
||||
return dret;
|
||||
}
|
||||
@ -416,10 +415,10 @@ MidiTrack::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
|
||||
|
||||
/* not actually recording, but we want to hear the input material anyway,
|
||||
at least potentially (depending on monitoring options)
|
||||
*/
|
||||
*/
|
||||
|
||||
passthru (start_frame, end_frame, nframes, 0);
|
||||
|
||||
|
||||
} else {
|
||||
/*
|
||||
XXX is it true that the earlier test on n_outputs()
|
||||
@ -434,12 +433,15 @@ MidiTrack::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
|
||||
|
||||
//const size_t limit = n_process_buffers().n_audio();
|
||||
BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
|
||||
|
||||
diskstream->get_playback (bufs.get_midi(0), start_frame, end_frame);
|
||||
|
||||
diskstream->get_playback(bufs.get_midi(0), start_frame, end_frame);
|
||||
/* append immediate messages to the first MIDI buffer (thus sending it to the first output port) */
|
||||
|
||||
write_out_of_band_data (bufs, start_frame, end_frame, nframes);
|
||||
|
||||
process_output_buffers (bufs, start_frame, end_frame, nframes,
|
||||
(!_session.get_record_enabled() || !Config->get_do_not_record_plugins()), declick);
|
||||
|
||||
}
|
||||
|
||||
_main_outs->flush (nframes);
|
||||
@ -448,13 +450,12 @@ MidiTrack::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
|
||||
}
|
||||
|
||||
void
|
||||
MidiTrack::write_controller_messages(MidiBuffer& output_buf, sframes_t /*start*/, sframes_t /*end*/, nframes_t nframes)
|
||||
MidiTrack::write_out_of_band_data (BufferSet& bufs, sframes_t /*start*/, sframes_t /*end*/, nframes_t nframes)
|
||||
{
|
||||
// Append immediate events (UI controls)
|
||||
// Append immediate events
|
||||
|
||||
// XXX TAKE _port_offset in Port into account???
|
||||
|
||||
_immediate_events.read (output_buf, 0, 0, nframes - 1); // all stamps = 0
|
||||
MidiBuffer& buf (bufs.get_midi (0));
|
||||
_immediate_events.read (buf, 0, 0, nframes - 1); // all stamps = 0
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -16,6 +16,7 @@
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
#include <cmath>
|
||||
|
||||
#include "pbd/basename.h"
|
||||
|
||||
@ -31,15 +32,24 @@
|
||||
using namespace std;
|
||||
using namespace ARDOUR;
|
||||
|
||||
|
||||
/** Quantize notes, valid for MIDI regions only.
|
||||
/** Quantize notes
|
||||
*
|
||||
* Q is the quantize value in beats, ie 1.0 = quantize to beats,
|
||||
* grid parameters are the quantize value in beats, ie 1.0 = quantize to beats,
|
||||
* 0.25 = quantize to beats/4, etc.
|
||||
*/
|
||||
Quantize::Quantize (Session& s, double q)
|
||||
: Filter (s)
|
||||
, _q(q)
|
||||
|
||||
Quantize::Quantize (Session& s, QuantizeType /* type */,
|
||||
bool snap_start, bool snap_end,
|
||||
double start_grid, double end_grid,
|
||||
float strength, float swing, float threshold)
|
||||
: session (s)
|
||||
, _snap_start (snap_start)
|
||||
, _snap_end (snap_end)
|
||||
, _start_grid(start_grid)
|
||||
, _end_grid(end_grid)
|
||||
, _strength (strength/100.0)
|
||||
, _swing (swing/100.0)
|
||||
, _threshold (threshold)
|
||||
{
|
||||
}
|
||||
|
||||
@ -48,32 +58,70 @@ Quantize::~Quantize ()
|
||||
}
|
||||
|
||||
int
|
||||
Quantize::run (boost::shared_ptr<Region> r)
|
||||
Quantize::operator () (std::vector<Evoral::Sequence<Evoral::MusicalTime>::Notes>& seqs)
|
||||
{
|
||||
boost::shared_ptr<MidiRegion> region = boost::dynamic_pointer_cast<MidiRegion>(r);
|
||||
if (!region)
|
||||
return -1;
|
||||
bool even;
|
||||
|
||||
// FIXME: how to make a whole file region if it isn't?
|
||||
//assert(region->whole_file());
|
||||
for (std::vector<Evoral::Sequence<Evoral::MusicalTime>::Notes>::iterator s = seqs.begin();
|
||||
s != seqs.end(); ++s) {
|
||||
|
||||
boost::shared_ptr<MidiSource> src = region->midi_source(0);
|
||||
src->load_model();
|
||||
even = false;
|
||||
|
||||
boost::shared_ptr<MidiModel> model = src->model();
|
||||
|
||||
for (Evoral::Sequence<MidiModel::TimeType>::Notes::iterator i = model->notes().begin();
|
||||
i != model->notes().end(); ++i) {
|
||||
const double new_time = lrint((*i)->time() / _q) * _q;
|
||||
double new_dur = lrint((*i)->length() / _q) * _q;
|
||||
if (new_dur == 0.0)
|
||||
new_dur = _q;
|
||||
|
||||
(*i)->set_time(new_time);
|
||||
(*i)->set_length(new_dur);
|
||||
for (Evoral::Sequence<MidiModel::TimeType>::Notes::iterator i = (*s).begin();
|
||||
i != (*s).end(); ++i) {
|
||||
|
||||
double new_start = round ((*i)->time() / _start_grid) * _start_grid;
|
||||
double new_end = round ((*i)->end_time() / _end_grid) * _end_grid;
|
||||
double delta;
|
||||
|
||||
if (_swing > 0.0 && !even) {
|
||||
|
||||
double next_grid = new_start + _start_grid;
|
||||
|
||||
/* find a spot 2/3 (* swing factor) of the way between the grid point
|
||||
we would put this note at, and the nominal position of the next note.
|
||||
*/
|
||||
|
||||
new_start = new_start + (2.0/3.0 * _swing * (next_grid - new_start));
|
||||
|
||||
} else if (_swing < 0.0 && !even) {
|
||||
|
||||
double prev_grid = new_start - _start_grid;
|
||||
|
||||
/* find a spot 2/3 (* swing factor) of the way between the grid point
|
||||
we would put this note at, and the nominal position of the previous note.
|
||||
*/
|
||||
|
||||
new_start = new_start - (2.0/3.0 * _swing * (new_start - prev_grid));
|
||||
|
||||
}
|
||||
|
||||
delta = new_start - (*i)->time();
|
||||
|
||||
if (fabs (delta) >= _threshold) {
|
||||
if (_snap_start) {
|
||||
delta *= _strength;
|
||||
(*i)->set_time ((*i)->time() + delta);
|
||||
}
|
||||
}
|
||||
|
||||
if (_snap_end) {
|
||||
delta = new_end - (*i)->end_time();
|
||||
|
||||
if (fabs (delta) >= _threshold) {
|
||||
double new_dur = new_end - new_start;
|
||||
|
||||
if (new_dur == 0.0) {
|
||||
new_dur = _end_grid;
|
||||
}
|
||||
|
||||
(*i)->set_length (new_dur);
|
||||
}
|
||||
}
|
||||
|
||||
even = !even;
|
||||
}
|
||||
}
|
||||
|
||||
model->set_edited(true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -479,13 +479,16 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
|
||||
}
|
||||
}
|
||||
|
||||
write_out_of_band_data (bufs, start_frame, end_frame, nframes);
|
||||
process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
|
||||
}
|
||||
|
||||
void
|
||||
Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
|
||||
{
|
||||
process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick);
|
||||
BufferSet& bufs (_session.get_silent_buffers (n_process_buffers()));
|
||||
write_out_of_band_data (bufs, start_frame, end_frame, nframes);
|
||||
process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1099,30 +1099,91 @@ TempoMap::round_to_beat (nframes_t fr, int dir)
|
||||
}
|
||||
|
||||
nframes_t
|
||||
|
||||
TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
|
||||
TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num, int dir)
|
||||
{
|
||||
|
||||
BBT_Time the_beat;
|
||||
uint32_t ticks_one_half_subdivisions_worth;
|
||||
uint32_t ticks_one_subdivisions_worth;
|
||||
uint32_t difference;
|
||||
|
||||
bbt_time(fr, the_beat);
|
||||
|
||||
ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
|
||||
ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
|
||||
|
||||
if (dir > 0) {
|
||||
|
||||
/* round to next */
|
||||
|
||||
if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
|
||||
uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
|
||||
if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
|
||||
the_beat.beats++;
|
||||
the_beat.ticks += difference;
|
||||
the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
|
||||
} else {
|
||||
the_beat.ticks += difference;
|
||||
}
|
||||
} else {
|
||||
the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
|
||||
uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
|
||||
|
||||
if (mod == 0) {
|
||||
/* right on the subdivision, so the difference is just the subdivision ticks */
|
||||
difference = ticks_one_subdivisions_worth;
|
||||
|
||||
} else {
|
||||
/* not on subdivision, compute distance to next subdivision */
|
||||
|
||||
difference = ticks_one_subdivisions_worth - mod;
|
||||
}
|
||||
|
||||
if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
|
||||
the_beat.beats++;
|
||||
the_beat.ticks += difference;
|
||||
the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
|
||||
} else {
|
||||
the_beat.ticks += difference;
|
||||
}
|
||||
|
||||
} else if (dir < 0) {
|
||||
|
||||
/* round to previous */
|
||||
|
||||
uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
|
||||
|
||||
if (mod == 0) {
|
||||
/* right on the subdivision, so the difference is just the subdivision ticks */
|
||||
difference = ticks_one_subdivisions_worth;
|
||||
cerr << "On the sub, move by 1 sub = " << difference << endl;
|
||||
} else {
|
||||
/* not on subdivision, compute distance to previous subdivision, which
|
||||
is just the modulus.
|
||||
*/
|
||||
|
||||
difference = mod;
|
||||
cerr << "off the sub, move by 1 sub = " << difference << endl;
|
||||
}
|
||||
|
||||
|
||||
cerr << "ticks = " << the_beat.ticks << endl;
|
||||
|
||||
if (the_beat.ticks < difference) {
|
||||
cerr << "backup beats, set ticks to "
|
||||
<< (uint32_t)Meter::ticks_per_beat - difference << endl;
|
||||
the_beat.beats--;
|
||||
the_beat.ticks = (uint32_t)Meter::ticks_per_beat - difference;
|
||||
} else {
|
||||
cerr << " reduce ticks\n";
|
||||
the_beat.ticks -= difference;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* round to nearest */
|
||||
|
||||
if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
|
||||
difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
|
||||
if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
|
||||
the_beat.beats++;
|
||||
the_beat.ticks += difference;
|
||||
the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
|
||||
} else {
|
||||
the_beat.ticks += difference;
|
||||
}
|
||||
} else {
|
||||
// difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
|
||||
the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
|
||||
}
|
||||
}
|
||||
|
||||
return frame_time (the_beat);
|
||||
@ -1139,7 +1200,9 @@ TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
|
||||
switch (type) {
|
||||
case Bar:
|
||||
if (dir < 0) {
|
||||
/* relax */
|
||||
if (bbt.bars > 1) {
|
||||
bbt.bars--;
|
||||
}
|
||||
} else if (dir > 0) {
|
||||
if (bbt.beats > 0) {
|
||||
bbt.bars++;
|
||||
@ -1157,7 +1220,9 @@ TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
|
||||
|
||||
case Beat:
|
||||
if (dir < 0) {
|
||||
/* relax */
|
||||
if (bbt.beats > 1) {
|
||||
bbt.beats--;
|
||||
}
|
||||
} else if (dir > 0) {
|
||||
if (bbt.ticks > 0) {
|
||||
bbt.beats++;
|
||||
@ -1178,11 +1243,12 @@ TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
cerr << "for " << frame << " round to " << bbt << " using "
|
||||
<< metric.start()
|
||||
<< endl;
|
||||
*/
|
||||
|
||||
return metric.frame() + count_frames_between (metric.start(), bbt);
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "ardour/audioplaylist.h"
|
||||
#include "ardour/audioregion.h"
|
||||
#include "ardour/audiosource.h"
|
||||
#include "ardour/delivery.h"
|
||||
#include "ardour/diskstream.h"
|
||||
#include "ardour/io_processor.h"
|
||||
#include "ardour/meter.h"
|
||||
@ -316,6 +317,8 @@ Track::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
|
||||
passthru (start_frame, end_frame, nframes, false);
|
||||
}
|
||||
|
||||
_main_outs->flush (nframes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,8 @@ struct Event {
|
||||
|
||||
inline const Event& operator=(const Event& copy) {
|
||||
_type = copy._type;
|
||||
_time = copy._time;
|
||||
_original_time = copy._original_time;
|
||||
_nominal_time = copy._nominal_time;
|
||||
if (_owns_buf) {
|
||||
if (copy._buf) {
|
||||
if (copy._size > _size) {
|
||||
@ -84,7 +85,8 @@ struct Event {
|
||||
}
|
||||
|
||||
_type = copy._type;
|
||||
_time = copy._time;
|
||||
_original_time = copy._nominal_time;
|
||||
_nominal_time = copy._nominal_time;
|
||||
_size = copy._size;
|
||||
_buf = copy._buf;
|
||||
}
|
||||
@ -99,7 +101,8 @@ struct Event {
|
||||
_buf = buf;
|
||||
}
|
||||
|
||||
_time = t;
|
||||
_original_time = t;
|
||||
_nominal_time = t;
|
||||
_size = size;
|
||||
}
|
||||
|
||||
@ -107,7 +110,10 @@ struct Event {
|
||||
if (_type != other._type)
|
||||
return false;
|
||||
|
||||
if (_time != other._time)
|
||||
if (_nominal_time != other._nominal_time)
|
||||
return false;
|
||||
|
||||
if (_original_time != other._original_time)
|
||||
return false;
|
||||
|
||||
if (_size != other._size)
|
||||
@ -151,7 +157,8 @@ struct Event {
|
||||
|
||||
inline void clear() {
|
||||
_type = 0;
|
||||
_time = 0;
|
||||
_original_time = 0;
|
||||
_nominal_time = 0;
|
||||
_size = 0;
|
||||
_buf = NULL;
|
||||
}
|
||||
@ -164,8 +171,10 @@ struct Event {
|
||||
|
||||
inline EventType event_type() const { return _type; }
|
||||
inline void set_event_type(EventType t) { _type = t; }
|
||||
inline Time time() const { return _time; }
|
||||
inline Time& time() { return _time; }
|
||||
inline Time time() const { return _nominal_time; }
|
||||
inline Time& time() { return _nominal_time; }
|
||||
inline Time original_time() const { return _original_time; }
|
||||
inline Time& original_time() { return _original_time; }
|
||||
inline uint32_t size() const { return _size; }
|
||||
inline uint32_t& size() { return _size; }
|
||||
|
||||
@ -174,7 +183,8 @@ struct Event {
|
||||
|
||||
protected:
|
||||
EventType _type; /**< Type of event (application relative, NOT MIDI 'type') */
|
||||
Time _time; /**< Sample index (or beat time) at which event is valid */
|
||||
Time _original_time; /**< Sample index (or beat time) at which event is valid */
|
||||
Time _nominal_time; /**< quantized version of _time, used in preference */
|
||||
uint32_t _size; /**< Number of uint8_ts of data in \a buffer */
|
||||
uint8_t* _buf; /**< Raw MIDI data */
|
||||
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
|
||||
/** An abstract (protocol agnostic) note.
|
||||
*
|
||||
* Currently a note is defined as (on event, length, off event).
|
||||
@ -76,5 +75,14 @@ private:
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
template<typename Time>
|
||||
std::ostream& operator<<(std::ostream& o, const Evoral::Note<Time>& n) {
|
||||
o << "Note: pitch = " << (int) n.note()
|
||||
<< " @ " << n.time() << " .. " << n.end_time()
|
||||
<< " velocity " << (int) n.velocity()
|
||||
<< " chn " << (int) n.channel();
|
||||
return o;
|
||||
}
|
||||
|
||||
#endif // EVORAL_NOTE_HPP
|
||||
|
||||
|
@ -92,6 +92,14 @@ public:
|
||||
return a->time() < b->time();
|
||||
}
|
||||
|
||||
struct LaterNoteComparator {
|
||||
typedef const Note<Time>* value_type;
|
||||
inline bool operator()(const boost::shared_ptr< const Note<Time> > a,
|
||||
const boost::shared_ptr< const Note<Time> > b) const {
|
||||
return a->time() > b->time();
|
||||
}
|
||||
};
|
||||
|
||||
struct LaterNoteEndComparator {
|
||||
typedef const Note<Time>* value_type;
|
||||
inline bool operator()(const boost::shared_ptr< const Note<Time> > a,
|
||||
|
@ -21,6 +21,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#include "evoral/midi_events.h"
|
||||
|
||||
@ -113,6 +115,7 @@ midi_event_is_valid(const uint8_t* buffer, size_t len)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string midi_note_name (uint8_t noteval);
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
|
@ -27,6 +27,9 @@ namespace Evoral {
|
||||
/** Frame count (i.e. length of time in audio frames) */
|
||||
typedef uint32_t FrameTime;
|
||||
|
||||
/** Musical time: beats relative to some defined origin */
|
||||
typedef double MusicalTime;
|
||||
|
||||
/** Type of an event (opaque, mapped by application) */
|
||||
typedef uint32_t EventType;
|
||||
|
||||
|
@ -25,7 +25,8 @@ namespace Evoral {
|
||||
template<typename Timestamp>
|
||||
Event<Timestamp>::Event(EventType type, Timestamp time, uint32_t size, uint8_t* buf, bool alloc)
|
||||
: _type(type)
|
||||
, _time(time)
|
||||
, _original_time(time)
|
||||
, _nominal_time(time)
|
||||
, _size(size)
|
||||
, _buf(buf)
|
||||
, _owns_buf(alloc)
|
||||
@ -43,7 +44,8 @@ Event<Timestamp>::Event(EventType type, Timestamp time, uint32_t size, uint8_t*
|
||||
template<typename Timestamp>
|
||||
Event<Timestamp>::Event(const Event& copy, bool owns_buf)
|
||||
: _type(copy._type)
|
||||
, _time(copy._time)
|
||||
, _original_time(copy._original_time)
|
||||
, _nominal_time(copy._nominal_time)
|
||||
, _size(copy._size)
|
||||
, _buf(copy._buf)
|
||||
, _owns_buf(owns_buf)
|
||||
@ -67,7 +69,7 @@ Event<Timestamp>::~Event() {
|
||||
|
||||
#endif // EVORAL_EVENT_ALLOC
|
||||
|
||||
template class Event<double>;
|
||||
template class Event<Evoral::MusicalTime>;
|
||||
template class Event<uint32_t>;
|
||||
|
||||
} // namespace Evoral
|
||||
|
@ -81,7 +81,7 @@ MIDIEvent<Time>::to_xml() const
|
||||
|
||||
#endif // EVORAL_MIDI_XML
|
||||
|
||||
template class MIDIEvent<double>;
|
||||
template class MIDIEvent<Evoral::MusicalTime>;
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
|
@ -73,7 +73,6 @@ Note<Time>::Note(const Note<Time>& copy)
|
||||
assert(channel() == copy.channel());
|
||||
}
|
||||
|
||||
|
||||
template<typename Time>
|
||||
Note<Time>::~Note()
|
||||
{
|
||||
@ -98,6 +97,7 @@ Note<Time>::operator=(const Note<Time>& copy)
|
||||
return *this;
|
||||
}
|
||||
|
||||
template class Note<double>;
|
||||
template class Note<Evoral::MusicalTime>;
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
|
@ -364,6 +364,6 @@ SMF<Time>::write_var_len(uint32_t value)
|
||||
return ret;
|
||||
}
|
||||
|
||||
template class SMF<double>;
|
||||
template class SMF<Evoral::MusicalTime>;
|
||||
|
||||
} // namespace Evoral
|
||||
|
@ -787,7 +787,7 @@ Sequence<Time>::set_notes (const Sequence<Time>::Notes& n)
|
||||
_notes = n;
|
||||
}
|
||||
|
||||
template class Sequence<double>;
|
||||
template class Sequence<Evoral::MusicalTime>;
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
|
55
libs/evoral/src/midi_util.cpp
Normal file
55
libs/evoral/src/midi_util.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
/* This file is part of Evoral.
|
||||
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||
* Copyright (C) 2009 Paul Davis
|
||||
*
|
||||
* Evoral is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "evoral/midi_util.h"
|
||||
#include <cstdio>
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
std::string
|
||||
midi_note_name (uint8_t val)
|
||||
{
|
||||
if (val > 127) {
|
||||
return "???";
|
||||
}
|
||||
|
||||
static const char* notes[] = {
|
||||
"c",
|
||||
"c#",
|
||||
"d",
|
||||
"d#",
|
||||
"e",
|
||||
"f",
|
||||
"f#",
|
||||
"g",
|
||||
"a",
|
||||
"a#",
|
||||
"b",
|
||||
"b#"
|
||||
};
|
||||
|
||||
int octave = val/12;
|
||||
static char buf[8];
|
||||
|
||||
val -= octave*12;
|
||||
|
||||
snprintf (buf, sizeof (buf), "%s%d", notes[val], octave);
|
||||
return buf;
|
||||
}
|
||||
|
||||
}
|
@ -68,6 +68,7 @@ def build(bld):
|
||||
src/ControlSet.cpp
|
||||
src/Curve.cpp
|
||||
src/Event.cpp
|
||||
src/midi_util.cpp
|
||||
src/MIDIEvent.cpp
|
||||
src/Note.cpp
|
||||
src/SMF.cpp
|
||||
|
@ -47,7 +47,7 @@ TearOff::TearOff (Widget& c, bool allow_resize)
|
||||
close_event_box.set_events (BUTTON_PRESS_MASK|BUTTON_RELEASE_MASK);
|
||||
close_event_box.signal_button_release_event().connect (mem_fun (*this, &TearOff::close_click));
|
||||
|
||||
own_window.add_events (BUTTON_PRESS_MASK|BUTTON_RELEASE_MASK|POINTER_MOTION_MASK|POINTER_MOTION_HINT_MASK);
|
||||
own_window.add_events (KEY_PRESS_MASK|KEY_RELEASE_MASK|BUTTON_PRESS_MASK|BUTTON_RELEASE_MASK|POINTER_MOTION_MASK|POINTER_MOTION_HINT_MASK);
|
||||
own_window.set_resizable (allow_resize);
|
||||
own_window.set_type_hint (WINDOW_TYPE_HINT_TOOLBAR);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user