diff options
author | Carl Hetherington <carl@carlh.net> | 2010-08-12 23:26:59 +0000 |
---|---|---|
committer | Carl Hetherington <carl@carlh.net> | 2010-08-12 23:26:59 +0000 |
commit | 5ecf08a3076634df3b170176f38d31894716ef94 (patch) | |
tree | 60978508c5d4eb0b0f4b14f7bedeee7fba9babc8 | |
parent | 9196535878dfe2a6eb7bde909b0b2312ade8ad29 (diff) |
Add option to normalize across all selected regions. Clean up the region context menu a bit with respect to its handling of multiple selected regions.
git-svn-id: svn://localhost/ardour2/branches/3.0@7611 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r-- | gtk2_ardour/editor.cc | 201 | ||||
-rw-r--r-- | gtk2_ardour/editor.h | 2 | ||||
-rw-r--r-- | gtk2_ardour/editor_ops.cc | 42 | ||||
-rw-r--r-- | libs/ardour/ardour/audioregion.h | 3 | ||||
-rw-r--r-- | libs/ardour/audioregion.cc | 72 |
5 files changed, 211 insertions, 109 deletions
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index a9e2b1d6ac..872566f54f 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -1536,11 +1536,18 @@ Editor::build_track_region_context_menu (nframes64_t frame) // there's already a multiple selection: just add a // single region context menu that will act on all // selected regions - boost::shared_ptr<Region> dummy_region; // = NULL - add_region_context_items (rtv->view(), dummy_region, edit_items); + + list<boost::shared_ptr<Region> > regions; + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + regions.push_back ((*i)->region ()); + } + + add_region_context_items (rtv->view(), regions, edit_items); } else { for (Playlist::RegionList::reverse_iterator i = regions->rbegin(); i != regions->rend(); ++i) { - add_region_context_items (rtv->view(), (*i), edit_items); + list<boost::shared_ptr<Region> > regions; + regions.push_back (*i); + add_region_context_items (rtv->view(), regions, edit_items); } } @@ -1584,11 +1591,18 @@ Editor::build_track_crossfade_context_menu (nframes64_t frame) // there's already a multiple selection: just add a // single region context menu that will act on all // selected regions - boost::shared_ptr<Region> dummy_region; // = NULL - add_region_context_items (atv->audio_view(), dummy_region, edit_items); + + list<boost::shared_ptr<Region> > regions; + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + regions.push_back ((*i)->region ()); + } + + add_region_context_items (atv->audio_view(), regions, edit_items); } else { for (Playlist::RegionList::reverse_iterator i = regions->rbegin(); i != regions->rend(); ++i) { - add_region_context_items (atv->audio_view(), (*i), edit_items); + list<boost::shared_ptr<Region> > regions; + regions.push_back (*i); + add_region_context_items (atv->audio_view(), regions, edit_items); } } delete regions; @@ -1711,7 +1725,7 @@ Editor::xfade_edit_right_region () } void -Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> region, Menu_Helpers::MenuList& edit_items) +Editor::add_region_context_items (StreamView* sv, list<boost::shared_ptr<Region> > regions, Menu_Helpers::MenuList& edit_items) { using namespace Menu_Helpers; Gtk::MenuItem* foo_item; @@ -1719,24 +1733,100 @@ Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> regi MenuList& items = region_menu->items(); region_menu->set_name ("ArdourContextMenu"); - boost::shared_ptr<AudioRegion> ar; - boost::shared_ptr<MidiRegion> mr; + /* Look through the regions that we are handling and make notes about what we have got */ + + bool have_audio = false; + bool have_midi = false; + bool have_locked = false; + bool have_unlocked = false; + bool have_position_lock_style_audio = false; + bool have_position_lock_style_music = false; + bool have_muted = false; + bool have_unmuted = false; + bool have_opaque = false; + bool have_non_opaque = false; + bool have_not_at_natural_position = false; + bool have_envelope_visible = false; + bool have_envelope_invisible = false; + bool have_envelope_active = false; + bool have_envelope_inactive = false; + bool have_non_unity_scale_amplitude = false; + + for (list<boost::shared_ptr<Region> >::const_iterator i = regions.begin(); i != regions.end(); ++i) { + boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*i); + if (ar) { + have_audio = true; + } + if (boost::dynamic_pointer_cast<MidiRegion> (*i)) { + have_midi = true; + } + + if ((*i)->locked()) { + have_locked = true; + } else { + have_unlocked = true; + } + + if ((*i)->position_lock_style() == MusicTime) { + have_position_lock_style_music = true; + } else { + have_position_lock_style_audio = true; + } - if (region) { - ar = boost::dynamic_pointer_cast<AudioRegion> (region); - mr = boost::dynamic_pointer_cast<MidiRegion> (region); + if ((*i)->muted()) { + have_muted = true; + } else { + have_unmuted = true; + } + + if ((*i)->opaque()) { + have_opaque = true; + } else { + have_non_opaque = true; + } + + if (!(*i)->at_natural_position()) { + have_not_at_natural_position = true; + } + + if (ar) { + RegionView* rv = sv->find_view (ar); + AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv); + + if (arv->envelope_visible()) { + have_envelope_visible = true; + } else { + have_envelope_invisible = true; + } + + if (ar->envelope_active()) { + have_envelope_active = true; + } else { + have_envelope_inactive = true; + } + + if (ar->scale_amplitude() != 1) { + have_non_unity_scale_amplitude = true; + } + } + } + + if (regions.size() == 1) { /* when this particular menu pops up, make the relevant region become selected. */ region_menu->signal_map_event().connect ( - sigc::bind (sigc::mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr<Region>(region))); + sigc::bind (sigc::mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr<Region> (regions.front())) + ); items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &Editor::rename_region))); - if (mr) { + + if (have_midi) { items.push_back (MenuElem (_("List Editor..."), sigc::mem_fun(*this, &Editor::show_midi_list_editor))); } + items.push_back (MenuElem (_("Region Properties..."), sigc::mem_fun(*this, &Editor::edit_region))); } @@ -1754,107 +1844,96 @@ Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> regi items.push_back (MenuElem (_("Export..."), sigc::mem_fun(*this, &Editor::export_region))); items.push_back (MenuElem (_("Bounce"), sigc::mem_fun(*this, &Editor::bounce_region_selection))); - if (ar) { + if (have_audio) { items.push_back (MenuElem (_("Spectral Analysis..."), sigc::mem_fun(*this, &Editor::analyze_region_selection))); } items.push_back (SeparatorElem()); - sigc::connection fooc; - boost::shared_ptr<Region> region_to_check; - - if (region) { - region_to_check = region; - } else { - region_to_check = selection->regions.front()->region(); - } - items.push_back (CheckMenuElem (_("Lock"))); CheckMenuItem* region_lock_item = static_cast<CheckMenuItem*>(&items.back()); - if (region_to_check->locked()) { + if (have_locked && !have_unlocked) { region_lock_item->set_active(); + } else if (have_locked && have_unlocked) { + region_lock_item->set_inconsistent (); } region_lock_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_lock)); items.push_back (CheckMenuElem (_("Glue to Bars and Beats"))); CheckMenuItem* bbt_glue_item = static_cast<CheckMenuItem*>(&items.back()); - switch (region_to_check->position_lock_style()) { - case MusicTime: - bbt_glue_item->set_active (true); - break; - default: - bbt_glue_item->set_active (false); - break; + if (have_position_lock_style_music && !have_position_lock_style_audio) { + bbt_glue_item->set_active (); + } else if (have_position_lock_style_music && have_position_lock_style_audio) { + bbt_glue_item->set_inconsistent (); } bbt_glue_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_region_lock_style)); items.push_back (CheckMenuElem (_("Mute"))); CheckMenuItem* region_mute_item = static_cast<CheckMenuItem*>(&items.back()); - fooc = region_mute_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_mute)); - if (region_to_check->muted()) { - fooc.block (true); + + if (have_muted && !have_unmuted) { region_mute_item->set_active(); - fooc.block (false); + } else if (have_muted && have_unmuted) { + region_mute_item->set_inconsistent (); } + region_mute_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_mute)); + items.push_back (MenuElem (_("Transpose..."), mem_fun(*this, &Editor::pitch_shift_regions))); if (!Profile->get_sae()) { items.push_back (CheckMenuElem (_("Opaque"))); CheckMenuItem* region_opaque_item = static_cast<CheckMenuItem*>(&items.back()); - fooc = region_opaque_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_opaque)); - if (region_to_check->opaque()) { - fooc.block (true); + if (have_opaque && !have_non_opaque) { region_opaque_item->set_active(); - fooc.block (false); + } else if (have_opaque && have_non_opaque) { + region_opaque_item->set_inconsistent (); } + region_opaque_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_opaque)); } items.push_back (CheckMenuElem (_("Original Position"), sigc::mem_fun(*this, &Editor::naturalize))); - if (region_to_check->at_natural_position()) { + if (!have_not_at_natural_position) { items.back().set_sensitive (false); } items.push_back (SeparatorElem()); - if (ar) { - - RegionView* rv = sv->find_view (ar); - AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv); + if (have_audio) { if (!Profile->get_sae()) { items.push_back (MenuElem (_("Reset Envelope"), sigc::mem_fun(*this, &Editor::reset_region_gain_envelopes))); items.push_back (CheckMenuElem (_("Envelope Visible"))); CheckMenuItem* region_envelope_visible_item = static_cast<CheckMenuItem*> (&items.back()); - fooc = region_envelope_visible_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_visibility)); - if (arv->envelope_visible()) { - fooc.block (true); - region_envelope_visible_item->set_active (true); - fooc.block (false); + if (have_envelope_visible && !have_envelope_invisible) { + region_envelope_visible_item->set_active (); + } else if (have_envelope_visible && have_envelope_invisible) { + region_envelope_visible_item->set_inconsistent (); } + region_envelope_visible_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_visibility)); items.push_back (CheckMenuElem (_("Envelope Active"))); CheckMenuItem* region_envelope_active_item = static_cast<CheckMenuItem*> (&items.back()); - fooc = region_envelope_active_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_active)); - if (ar->envelope_active()) { - fooc.block (true); - region_envelope_active_item->set_active (true); - fooc.block (false); + if (have_envelope_active && !have_envelope_inactive) { + region_envelope_active_item->set_active (); + } else if (have_envelope_active && have_envelope_inactive) { + region_envelope_active_item->set_inconsistent (); } + region_envelope_active_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_active)); items.push_back (SeparatorElem()); } items.push_back (MenuElem (_("Normalize..."), sigc::mem_fun(*this, &Editor::normalize_region))); - if (ar->scale_amplitude() != 1) { + if (have_non_unity_scale_amplitude) { items.push_back (MenuElem (_("Reset Gain"), sigc::mem_fun(*this, &Editor::reset_region_scale_amplitude))); } - } else if (mr) { + } else if (have_midi) { items.push_back (MenuElem (_("Quantize"), sigc::mem_fun(*this, &Editor::quantize_region))); items.push_back (MenuElem (_("Fork"), sigc::mem_fun(*this, &Editor::fork_region))); items.push_back (SeparatorElem()); @@ -1916,8 +1995,10 @@ Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> regi region_edit_menu_split_item->set_sensitive (false); } - items.push_back (MenuElem (_("Make Mono Regions"), (sigc::mem_fun(*this, &Editor::split_multichannel_region)))); - region_edit_menu_split_multichannel_item = &items.back(); + if (have_audio) { + items.push_back (MenuElem (_("Make Mono Regions"), (sigc::mem_fun(*this, &Editor::split_multichannel_region)))); + region_edit_menu_split_multichannel_item = &items.back(); + } items.push_back (MenuElem (_("Duplicate"), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)))); items.push_back (MenuElem (_("Multi-Duplicate..."), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), true)))); @@ -1934,7 +2015,7 @@ Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> regi */ string::size_type pos = 0; - string menu_item_name = (region) ? region->name() : _("Selected Regions"); + string menu_item_name = (regions.size() == 1) ? regions.front()->name() : _("Selected Regions"); while ((pos = menu_item_name.find ("_", pos)) != string::npos) { menu_item_name.replace (pos, 1, "__"); diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 98c9251b9d..a29b8417a0 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -675,7 +675,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD Gtk::Menu* build_track_selection_context_menu (nframes64_t); void add_dstream_context_items (Gtk::Menu_Helpers::MenuList&); void add_bus_context_items (Gtk::Menu_Helpers::MenuList&); - void add_region_context_items (StreamView*, boost::shared_ptr<ARDOUR::Region>, Gtk::Menu_Helpers::MenuList&); + void add_region_context_items (StreamView*, std::list<boost::shared_ptr<ARDOUR::Region> >, Gtk::Menu_Helpers::MenuList&); void add_crossfade_context_items (AudioStreamView*, boost::shared_ptr<ARDOUR::Crossfade>, Gtk::Menu_Helpers::MenuList&, bool many); void add_selection_context_items (Gtk::Menu_Helpers::MenuList&); diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index eed740c652..501d05020b 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -4551,6 +4551,11 @@ Editor::normalize_region () } Dialog dialog (rs.size() > 1 ? _("Normalize regions") : _("Normalize region")); + + VBox vbox; + vbox.set_spacing (6); + vbox.set_border_width (6); + HBox hbox; hbox.set_spacing (6); hbox.set_border_width (6); @@ -4562,9 +4567,18 @@ Editor::normalize_region () hbox.pack_start (spin); spin.set_value (_last_normalization_value); hbox.pack_start (*manage (new Label (_("dbFS")))); - hbox.show_all (); + vbox.pack_start (hbox); + + CheckButton* normalize_across_all = manage (new CheckButton (_("Normalize across all selected regions"))); + vbox.pack_start (*normalize_across_all); + if (rs.size() <= 1) { + normalize_across_all->set_sensitive (false); + } + + vbox.show_all (); + dialog.get_vbox()->set_spacing (12); - dialog.get_vbox()->pack_start (hbox); + dialog.get_vbox()->pack_start (vbox); dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL); dialog.add_button (_("Normalize"), RESPONSE_ACCEPT); @@ -4577,14 +4591,28 @@ Editor::normalize_region () track_canvas->get_window()->set_cursor (*wait_cursor); gdk_flush (); + double maxamp = 0; + if (normalize_across_all->get_active ()) { + for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) { + AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r); + if (!arv) { + continue; + } + maxamp = max (maxamp, arv->audio_region()->maximum_amplitude ()); + } + } + for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) { - AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r); - if (!arv) + AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r); + if (!arv) { continue; - arv->region()->clear_history (); - arv->audio_region()->normalize_to (spin.get_value()); + } + arv->region()->clear_history (); + + double const amp = normalize_across_all->get_active() ? maxamp : arv->audio_region()->maximum_amplitude (); + + arv->audio_region()->normalize (amp, spin.get_value ()); _session->add_command (new StatefulDiffCommand (arv->region())); - } commit_reversible_command (); diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index d1238cb43c..71a948c352 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -82,7 +82,8 @@ class AudioRegion : public Region void set_scale_amplitude (gain_t); gain_t scale_amplitude() const { return _scale_amplitude; } - void normalize_to (float target_in_dB = 0.0f); + void normalize (float, float target_in_dB = 0.0f); + double maximum_amplitude () const; bool envelope_active () const { return _envelope_active; } bool fade_in_active () const { return _fade_in_active; } diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index f0219ce729..a1cb58458b 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -1111,74 +1111,66 @@ AudioRegion::set_scale_amplitude (gain_t g) send_change (PropertyChange (Properties::scale_amplitude)); } -void -AudioRegion::normalize_to (float target_dB) +/** @return the maximum (linear) amplitude of the region */ +double +AudioRegion::maximum_amplitude () const { - const framecnt_t blocksize = 64 * 1024; - Sample buf[blocksize]; - framepos_t fpos; - framepos_t fend; - framecnt_t to_read; + framepos_t fpos = _start; + framepos_t const fend = _start + _length; double maxamp = 0; - gain_t target = dB_to_coefficient (target_dB); - - if (target == 1.0f) { - /* do not normalize to precisely 1.0 (0 dBFS), to avoid making it appear - that we may have clipped. - */ - target -= FLT_EPSILON; - } - - fpos = _start; - fend = _start + _length; - - /* first pass: find max amplitude */ + framecnt_t const blocksize = 64 * 1024; + Sample buf[blocksize]; + while (fpos < fend) { uint32_t n; - to_read = min (fend - fpos, blocksize); + framecnt_t const to_read = min (fend - fpos, blocksize); for (n = 0; n < n_channels(); ++n) { /* read it in */ if (read_raw_internal (buf, fpos, to_read, 0) != to_read) { - return; + return 0; } maxamp = compute_peak (buf, to_read, maxamp); } fpos += to_read; - }; + } - if (maxamp == 0.0f) { + return maxamp; +} + +/** Normalize using a given maximum amplitude and target, so that region + * _scale_amplitude becomes target / max_amplitude. + */ +void +AudioRegion::normalize (float max_amplitude, float target_dB) +{ + gain_t target = dB_to_coefficient (target_dB); + + if (target == 1.0f) { + /* do not normalize to precisely 1.0 (0 dBFS), to avoid making it appear + that we may have clipped. + */ + target -= FLT_EPSILON; + } + + if (max_amplitude == 0.0f) { /* don't even try */ return; } - if (maxamp == target) { + if (max_amplitude == target) { /* we can't do anything useful */ return; } - /* compute scale factor */ - - _scale_amplitude = target/maxamp; - - /* tell the diskstream we're in */ - - boost::shared_ptr<Playlist> pl (playlist()); - - if (pl) { - pl->ContentsChanged(); - } - - /* tell everybody else */ - - send_change (PropertyChange (Properties::scale_amplitude)); + set_scale_amplitude (target / max_amplitude); } void |