Differentiate between pitch-shift (for audio) and transpose (for MIDI). Fixes #3940.
authorCarl Hetherington <carl@carlh.net>
Wed, 6 Apr 2011 02:04:37 +0000 (02:04 +0000)
committerCarl Hetherington <carl@carlh.net>
Wed, 6 Apr 2011 02:04:37 +0000 (02:04 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@9299 d708f5d6-7413-0410-9779-e7cbd77b26cf

13 files changed:
gtk2_ardour/ardour.menus.in
gtk2_ardour/editor.h
gtk2_ardour/editor_actions.cc
gtk2_ardour/editor_ops.cc
gtk2_ardour/editor_selection.cc
gtk2_ardour/time_fx_dialog.cc
gtk2_ardour/transpose_dialog.cc [new file with mode: 0644]
gtk2_ardour/transpose_dialog.h [new file with mode: 0644]
gtk2_ardour/wscript
libs/ardour/ardour/midi_model.h
libs/ardour/ardour/midi_region.h
libs/ardour/midi_model.cc
libs/ardour/midi_region.cc

index db5a78eab78caa7313094e49f8e88684b98c0ed8..7e6c16a50e88e99651b0fb9b32aff35b9bb55814 100644 (file)
              <menuitem action='toggle-opaque-region'/>
               <menuitem action='toggle-region-mute'/>
              <menuitem action='pitch-shift-region'/>
+             <menuitem action='transpose-region'/>
              <menuitem action='naturalize-region'/>
              <menuitem action='split-region'/>
              <menuitem action='split-multichannel-region'/>
              <menuitem action='toggle-opaque-region'/>
               <menuitem action='toggle-region-mute'/>
              <menuitem action='pitch-shift-region'/>
+             <menuitem action='transpose-region'/>
              <menuitem action='naturalize-region'/>
              <menuitem action='split-region'/>
              <menuitem action='split-multichannel-region'/>
index aefe9ac6423e9506e1cb5fa59005f15268e23881..6ac03194535ddad15ff499f0ddc8991cf21240e9 100644 (file)
@@ -1816,6 +1816,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        void pitch_shift_region ();
        int time_fx (RegionSelection&, float val, bool pitching);
 
+       void transpose_region ();
+
        /* editor-mixer strip */
 
        MixerStrip *current_mixer_strip;
index 0c10267b718fb1819085e08664d2f63938c52813..7f3d6db148d531fac7525f6b1465129c4eae2f38 100644 (file)
@@ -1250,8 +1250,11 @@ Editor::register_region_actions ()
        /* Cut selected region gain */
        reg_sens (_region_actions, "cut-region-gain", _("Cut Gain"), sigc::bind (sigc::mem_fun(*this, &Editor::adjust_region_gain), false));
        
-       /* Open the pitch shift dialogue for the selected regions */
-       reg_sens (_region_actions, "pitch-shift-region", _("Pitch Shift"), sigc::mem_fun (*this, &Editor::pitch_shift_region));
+       /* Open the pitch shift dialogue for any selected audio regions */
+       reg_sens (_region_actions, "pitch-shift-region", _("Pitch Shift..."), sigc::mem_fun (*this, &Editor::pitch_shift_region));
+
+       /* Open the transpose dialogue for any selected MIDI regions */
+       reg_sens (_region_actions, "transpose-region", _("Transpose..."), sigc::mem_fun (*this, &Editor::transpose_region));
        
        /* Toggle selected region opacity */
        toggle_reg_sens (_region_actions, "toggle-opaque-region", _("Opaque"), sigc::mem_fun (*this, &Editor::toggle_opaque_region));
index dac504bf261c9940f4af35e63f8488a13379c8a5..b88cd3cf4c4e8d8c40eaf084555c30c321f4d912 100644 (file)
@@ -87,6 +87,7 @@
 #include "editor_cursors.h"
 #include "mouse_cursors.h"
 #include "patch_change_dialog.h"
+#include "transpose_dialog.h"
 
 #include "i18n.h"
 
@@ -5344,11 +5345,42 @@ Editor::pitch_shift_region ()
 {
        RegionSelection rs = get_regions_from_selection_and_entered ();
 
-       if (rs.empty()) {
+       RegionSelection audio_rs;
+       for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
+               if (dynamic_cast<AudioRegionView*> (*i)) {
+                       audio_rs.push_back (*i);
+               }
+       }
+
+       if (audio_rs.empty()) {
                return;
        }
 
-       pitch_shift (rs, 1.2);
+       pitch_shift (audio_rs, 1.2);
+}
+
+void
+Editor::transpose_region ()
+{
+       RegionSelection rs = get_regions_from_selection_and_entered ();
+
+       list<MidiRegionView*> midi_region_views;
+       for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
+               MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
+               if (mrv) {
+                       midi_region_views.push_back (mrv);
+               }
+       }
+
+       TransposeDialog d;
+       int const r = d.run ();
+       if (r != RESPONSE_ACCEPT) {
+               return;
+       }
+
+       for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
+               (*i)->midi_region()->transpose (d.semitones ());
+       }
 }
 
 void
index 72c115738c0c056ceb176c987687f5b76b0246fc..7998a4bfa6adaa627efb8e383fae354ef4e71b88 100644 (file)
@@ -1072,6 +1072,7 @@ Editor::sensitize_the_right_region_actions ()
                _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
                _region_actions->get_action("quantize-region")->set_sensitive (false);
                _region_actions->get_action("fork-region")->set_sensitive (false);
+               _region_actions->get_action("transpose-region")->set_sensitive (false);
        }
 
        if (_edit_point == EditAtMouse) {
@@ -1102,6 +1103,7 @@ Editor::sensitize_the_right_region_actions ()
                _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
                _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
                _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
+               _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
                
        }
 
index f166768834517130f2f5ab549ac78ab59c03f706..1884a54af6353e3d2a2ddc477d34d58c94794db5 100644 (file)
@@ -81,9 +81,9 @@ TimeFXDialog::TimeFXDialog (Editor& e, bool pitch)
        set_name (N_("TimeFXDialog"));
 
        if (pitching) {
-               set_title (_("Pitch Shift"));
+               set_title (_("Pitch Shift Audio"));
        } else {
-               set_title (_("Time Stretch"));
+               set_title (_("Time Stretch Audio"));
        }
 
        cancel_button = add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
diff --git a/gtk2_ardour/transpose_dialog.cc b/gtk2_ardour/transpose_dialog.cc
new file mode 100644 (file)
index 0000000..6a3c5bf
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+    Copyright (C) 2011 Paul Davis
+    Author: Carl Hetherington <cth@carlh.net>
+
+    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 <gtkmm/table.h>
+#include <gtkmm/label.h>
+#include <gtkmm/stock.h>
+#include "transpose_dialog.h"
+
+using namespace Gtk;
+
+TransposeDialog::TransposeDialog ()
+       : ArdourDialog (_("Transpose MIDI"))
+       , _octaves_adjustment (0.0, -4.0, 4.0, 1, 2.0)
+       , _semitones_adjustment (0.0, -12.0, 12.0, 1.0, 4.0)
+       , _octaves_spinner (_octaves_adjustment)
+       , _semitones_spinner (_semitones_adjustment)
+{
+       Table* t = manage (new Table (2, 2));
+       t->set_row_spacings (6);
+       t->set_col_spacings (6);
+
+       int r = 0;
+       Label* l = manage (new Label (_("Octaves:"), ALIGN_LEFT, ALIGN_CENTER, false));
+       t->attach (*l, 0, 1, r, r + 1, FILL, EXPAND, 0, 0);
+       t->attach (_octaves_spinner, 1, 2, r, r + 1, FILL, EXPAND & FILL, 0, 0);
+       ++r;
+
+       l = manage (new Label (_("Semitones:"), ALIGN_LEFT, ALIGN_CENTER, false));
+       t->attach (*l, 0, 1, r, r + 1, FILL, EXPAND, 0, 0);
+       t->attach (_semitones_spinner, 1, 2, r, r + 1, FILL, EXPAND & FILL, 0, 0);
+       ++r;
+
+       get_vbox()->set_spacing (6);
+       get_vbox()->pack_start (*t, false, false);
+
+       add_button (Stock::CANCEL, RESPONSE_CANCEL);
+       add_button (_("Transpose"), RESPONSE_ACCEPT);
+
+       show_all_children ();
+}
+
+int
+TransposeDialog::semitones () const
+{
+       return _octaves_spinner.get_value () * 12 + _semitones_spinner.get_value ();
+}
diff --git a/gtk2_ardour/transpose_dialog.h b/gtk2_ardour/transpose_dialog.h
new file mode 100644 (file)
index 0000000..b2f41ce
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+    Copyright (C) 2011 Paul Davis
+    Author: Carl Hetherington <cth@carlh.net>
+
+    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 <gtkmm/spinbutton.h>
+#include "ardour_dialog.h"
+
+/** A dialog box to select a transposition to apply to a MIDI region.
+ *  It asks for octaves and semitones, with the transposition being
+ *  the sum of the two.
+ */
+
+class TransposeDialog : public ArdourDialog
+{
+public:
+       TransposeDialog ();
+
+       int semitones () const;
+
+private:
+       Gtk::Adjustment _octaves_adjustment;
+       Gtk::Adjustment _semitones_adjustment;
+       Gtk::SpinButton _octaves_spinner;
+       Gtk::SpinButton _semitones_spinner;
+};
index 84c5fe76d8fa3c347627c733e4c66f5ad6bad582..a3c0936bd1d184283c255b30aa1dd4565a174fa6 100644 (file)
@@ -219,6 +219,7 @@ gtk2_ardour_sources = [
         'time_selection.cc',
         'track_selection.cc',
         'track_view_list.cc',
+        'transpose_dialog.cc',
         'ui_config.cc',
         'utils.cc',
         'version.cc',
index 23e4b9de56f3c4ad8fc84d6a853e1b35180936bc..e32d31d2a16210ea01b509e0e134d24904a0df94 100644 (file)
@@ -261,6 +261,7 @@ public:
        boost::shared_ptr<Evoral::Control> control_factory(const Evoral::Parameter& id);
 
        void insert_silence_at_start (TimeType);
+       void transpose (TimeType, TimeType, int);
 
 protected:
         int resolve_overlaps_unlocked (const NotePtr, void* arg = 0);
index 016536b8b47a54444aa846a55aff42bad2d5e559..bb9f011ec8037c3b94bd6ea989a5b4816f119d84 100644 (file)
@@ -109,6 +109,7 @@ class MidiRegion : public Region
        boost::shared_ptr<const MidiModel> model() const { return midi_source()->model(); }
 
        void fix_negative_start ();
+       void transpose (int);
 
   protected:
        
index 1691380c93da9abb932eedfa1d61047f33da6b59..e1a955d43c833d18cf9462fbeb54dbe8822abd39 100644 (file)
@@ -1899,3 +1899,43 @@ MidiModel::insert_silence_at_start (TimeType t)
                apply_command_as_subcommand (s->session(), c);
        }
 }
+
+/** Transpose notes in a time range by a given number of semitones.  Notes
+ *  will be clamped at 0 and 127 if the transposition would make them exceed
+ *  that range.
+ *
+ *  @param from Start time.
+ *  @param end End time.
+ *  @param semitones Number of semitones to transpose by (+ve is higher, -ve is lower).
+ */
+void
+MidiModel::transpose (TimeType from, TimeType to, int semitones)
+{
+       boost::shared_ptr<const MidiSource> s = midi_source ();
+       
+       NoteDiffCommand* c = new_note_diff_command (_("transpose"));
+       
+       for (Notes::iterator i = notes().begin(); i != notes().end(); ++i) {
+
+               if ((*i)->time() >= to) {
+                       
+                       /* finished */
+                       break;
+                       
+               } else if ((*i)->time() >= from) {
+
+                       int new_note = (*i)->note() + semitones;
+                       
+                       if (new_note < 0) {
+                               new_note = 0;
+                       } else if (new_note > 127) {
+                               new_note = 127;
+                       }
+
+                       c->change (*i, NoteDiffCommand::NoteNumber, (uint8_t) new_note);
+                       
+               }
+       }
+
+       apply_command (s->session (), c);
+}
index f4058377b6c22af2877950d3da9083ab57a8a845..f8640e01977b77553918b37a3b15f6e2c6261a9d 100644 (file)
@@ -378,3 +378,11 @@ MidiRegion::fix_negative_start ()
        model()->insert_silence_at_start (c.from (-_start));
        _start = 0;
 }
+
+/** Transpose the notes in this region by a given number of semitones */
+void
+MidiRegion::transpose (int semitones)
+{
+       BeatsFramesConverter c (_session.tempo_map(), _start);
+       model()->transpose (c.from (_start), c.from (_start + _length), semitones);
+}