X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Frhythm_ferret.cc;h=4d39232d8c734f713d8eb97394470acabc43683a;hb=cf52d6e4b40111eb04b244ec054055a4ec15dbe0;hp=44c3d8a9bb5bc4d8deab4d446de883a9f106c65d;hpb=bb9cc45cd22af67ac275a5e73accbe14fee664d8;p=ardour.git diff --git a/gtk2_ardour/rhythm_ferret.cc b/gtk2_ardour/rhythm_ferret.cc index 44c3d8a9bb..4d39232d8c 100644 --- a/gtk2_ardour/rhythm_ferret.cc +++ b/gtk2_ardour/rhythm_ferret.cc @@ -1,23 +1,39 @@ +/* + Copyright (C) 2012 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 #include #include "pbd/memento_command.h" +#include "pbd/convert.h" -#include "ardour/transient_detector.h" -#include "ardour/onset_detector.h" -#include "ardour/audiosource.h" #include "ardour/audioregion.h" -#include "ardour/playlist.h" -#include "ardour/region_factory.h" +#include "ardour/onset_detector.h" #include "ardour/session.h" +#include "ardour/transient_detector.h" #include "rhythm_ferret.h" #include "audio_region_view.h" -#include "public_editor.h" -#include "utils.h" +#include "editor.h" #include "time_axis_view.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace std; using namespace Gtk; @@ -42,63 +58,49 @@ static const gchar * _onset_function_strings[] = { N_("Phase Deviation"), N_("Kullback-Liebler"), N_("Modified Kullback-Liebler"), +#ifdef HAVE_AUBIO4 + N_("Spectral Flux"), +#endif 0 }; -RhythmFerret::RhythmFerret (PublicEditor& e) +static const gchar * _operation_strings[] = { + N_("Split region"), +#if 0 // these don't do what a user expects + N_("Snap regions"), + N_("Conform regions"), +#endif + 0 +}; + +RhythmFerret::RhythmFerret (Editor& e) : ArdourDialog (_("Rhythm Ferret")) , editor (e) - , operation_frame (_("Operation")) - , selection_frame (_("Selection")) - , ferret_frame (_("Analysis")) - , logo (0) - , region_split_button (operation_button_group, _("Split region")) - , tempo_button (operation_button_group, _("Set tempo map")) - , region_conform_button (operation_button_group, _("Conform region")) - , analysis_mode_label (_("Mode")) - , detection_threshold_adjustment (3, 0, 20, 1, 4) + , detection_threshold_adjustment (-35, -80, -6, 1, 6) , detection_threshold_scale (detection_threshold_adjustment) - , detection_threshold_label (_("Threshold")) , sensitivity_adjustment (40, 0, 100, 1, 10) , sensitivity_scale (sensitivity_adjustment) - , sensitivity_label (_("Sensitivity")) , analyze_button (_("Analyze")) - , onset_function_label (_("Detection function")) , peak_picker_threshold_adjustment (0.3, 0.0, 1.0, 0.01, 0.1) , peak_picker_threshold_scale (peak_picker_threshold_adjustment) - , peak_picker_label (_("Peak Threshold")) , silence_threshold_adjustment (-90.0, -120.0, 0.0, 1, 10) , silence_threshold_scale (silence_threshold_adjustment) - , silence_label (_("Silent Threshold (dB)")) +#ifdef HAVE_AUBIO4 + , minioi_adjustment (4, 0, 40, 1, 5) + , minioi_scale (minioi_adjustment) +#endif , trigger_gap_adjustment (3, 0, 100, 1, 10) , trigger_gap_spinner (trigger_gap_adjustment) - , trigger_gap_label (_("Trigger gap (msecs)")) , action_button (Stock::APPLY) - { - upper_hpacker.set_spacing (6); - - upper_hpacker.pack_start (ferret_frame, true, true); - upper_hpacker.pack_start (selection_frame, true, true); - upper_hpacker.pack_start (operation_frame, true, true); - - op_packer.pack_start (region_split_button, false, false); - op_packer.pack_start (tempo_button, false, false); - op_packer.pack_start (region_conform_button, false, false); - - operation_frame.add (op_packer); - - HBox* box; - - ferret_packer.set_spacing (6); - ferret_packer.set_border_width (6); - - vector strings; + operation_strings = I18N (_operation_strings); + Gtkmm2ext::set_popdown_strings (operation_selector, operation_strings); + operation_selector.set_active (0); analysis_mode_strings = I18N (_analysis_mode_strings); Gtkmm2ext::set_popdown_strings (analysis_mode_selector, analysis_mode_strings); analysis_mode_selector.set_active_text (analysis_mode_strings.front()); - analysis_mode_selector.signal_changed().connect (mem_fun (*this, &RhythmFerret::analysis_mode_changed)); + analysis_mode_selector.signal_changed().connect (sigc::mem_fun (*this, &RhythmFerret::analysis_mode_changed)); onset_function_strings = I18N (_onset_function_strings); Gtkmm2ext::set_popdown_strings (onset_detection_function_selector, onset_function_strings); @@ -106,101 +108,85 @@ RhythmFerret::RhythmFerret (PublicEditor& e) XXX there should be a non-hacky way to set this */ onset_detection_function_selector.set_active_text (onset_function_strings[3]); + detection_threshold_scale.set_digits (3); - box = manage (new HBox); - box->set_spacing (6); - box->pack_start (analysis_mode_label, false, false); - box->pack_start (analysis_mode_selector, true, true); - ferret_packer.pack_start (*box, false, false); - - ferret_packer.pack_start (analysis_packer, false, false); - - box = manage (new HBox); - box->set_spacing (6); - box->pack_start (trigger_gap_label, false, false); - box->pack_start (trigger_gap_spinner, false, false); - ferret_packer.pack_start (*box, false, false); - - ferret_packer.pack_start (analyze_button, false, false); - - analyze_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::run_analysis)); - - box = manage (new HBox); - box->set_spacing (6); - box->pack_start (detection_threshold_label, false, false); - box->pack_start (detection_threshold_scale, true, true); - perc_onset_packer.pack_start (*box, false, false); - - box = manage (new HBox); - box->set_spacing (6); - box->pack_start (sensitivity_label, false, false); - box->pack_start (sensitivity_scale, true, true); - perc_onset_packer.pack_start (*box, false, false); - - box = manage (new HBox); - box->set_spacing (6); - box->pack_start (onset_function_label, false, false); - box->pack_start (onset_detection_function_selector, true, true); - note_onset_packer.pack_start (*box, false, false); - - box = manage (new HBox); - box->set_spacing (6); - box->pack_start (peak_picker_label, false, false); - box->pack_start (peak_picker_threshold_scale, true, true); - note_onset_packer.pack_start (*box, false, false); - - box = manage (new HBox); - box->set_spacing (6); - box->pack_start (silence_label, false, false); - box->pack_start (silence_threshold_scale, true, true); - note_onset_packer.pack_start (*box, false, false); + Table* t = manage (new Table (7, 3)); + t->set_spacings (12); - analysis_mode_changed (); + int n = 0; - ferret_frame.add (ferret_packer); + t->attach (*manage (new Label (_("Mode"), 1, 0.5)), 0, 1, n, n + 1, FILL); + t->attach (analysis_mode_selector, 1, 2, n, n + 1, FILL); + ++n; - logo = manage (new Gtk::Image (::get_icon (X_("ferret_02")))); + t->attach (*manage (new Label (_("Detection function"), 1, 0.5)), 0, 1, n, n + 1, FILL); + t->attach (onset_detection_function_selector, 1, 2, n, n + 1, FILL); + ++n; - if (logo) { - lower_hpacker.pack_start (*logo, false, false); - } + t->attach (*manage (new Label (_("Trigger gap (postproc)"), 1, 0.5)), 0, 1, n, n + 1, FILL); + t->attach (trigger_gap_spinner, 1, 2, n, n + 1, FILL); + t->attach (*manage (new Label (_("ms"))), 2, 3, n, n + 1, FILL); + ++n; + + t->attach (*manage (new Label (_("Peak threshold"), 1, 0.5)), 0, 1, n, n + 1, FILL); + t->attach (peak_picker_threshold_scale, 1, 2, n, n + 1, FILL); + ++n; + + t->attach (*manage (new Label (_("Silence threshold"), 1, 0.5)), 0, 1, n, n + 1, FILL); + t->attach (silence_threshold_scale, 1, 2, n, n + 1, FILL); + t->attach (*manage (new Label (_("dB"))), 2, 3, n, n + 1, FILL); + ++n; + +#ifdef HAVE_AUBIO4 + t->attach (*manage (new Label (_("Min Inter-Onset Time"), 1, 0.5)), 0, 1, n, n + 1, FILL); + t->attach (minioi_scale, 1, 2, n, n + 1, FILL); + t->attach (*manage (new Label (_("ms"))), 2, 3, n, n + 1, FILL); + ++n; +#endif + + + t->attach (*manage (new Label (_("Sensitivity"), 1, 0.5)), 0, 1, n, n + 1, FILL); + t->attach (sensitivity_scale, 1, 2, n, n + 1, FILL); + ++n; - lower_hpacker.pack_start (operation_clarification_label, true, true); - lower_hpacker.pack_start (action_button, false, false); - lower_hpacker.set_border_width (6); - lower_hpacker.set_spacing (6); + t->attach (*manage (new Label (_("Cut Pos Threshold"), 1, 0.5)), 0, 1, n, n + 1, FILL); + t->attach (detection_threshold_scale, 1, 2, n, n + 1, FILL); + t->attach (*manage (new Label (_("dB"))), 2, 3, n, n + 1, FILL); + ++n; - action_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::do_action)); + t->attach (*manage (new Label (_("Operation"), 1, 0.5)), 0, 1, n, n + 1, FILL); + t->attach (operation_selector, 1, 2, n, n + 1, FILL); + ++n; + + analyze_button.signal_clicked().connect (sigc::mem_fun (*this, &RhythmFerret::run_analysis)); + action_button.signal_clicked().connect (sigc::mem_fun (*this, &RhythmFerret::do_action)); get_vbox()->set_border_width (6); get_vbox()->set_spacing (6); - get_vbox()->pack_start (upper_hpacker, true, true); - get_vbox()->pack_start (lower_hpacker, false, false); + get_vbox()->pack_start (*t); - show_all (); -} + add_action_widget (analyze_button, 1); + add_action_widget (action_button, 0); -RhythmFerret::~RhythmFerret() -{ - delete logo; + show_all (); + analysis_mode_changed (); } void RhythmFerret::analysis_mode_changed () { - analysis_packer.children().clear (); - - switch (get_analysis_mode()) { - case PercussionOnset: - analysis_packer.pack_start (perc_onset_packer); - break; - - case NoteOnset: - analysis_packer.pack_start (note_onset_packer); - break; - } - - analysis_packer.show_all (); + bool const perc = get_analysis_mode() == PercussionOnset; + + // would be nice to actually hide/show the rows. + detection_threshold_scale.set_sensitive (perc); + sensitivity_scale.set_sensitive (perc); + trigger_gap_spinner.set_sensitive (!perc); + onset_detection_function_selector.set_sensitive (!perc); + peak_picker_threshold_scale.set_sensitive (!perc); + silence_threshold_scale.set_sensitive (!perc); +#ifdef HAVE_AUBIO4 + minioi_scale.set_sensitive (!perc); +#endif } RhythmFerret::AnalysisMode @@ -218,9 +204,9 @@ RhythmFerret::get_analysis_mode () const RhythmFerret::Action RhythmFerret::get_action () const { - if (tempo_button.get_active()) { - return DefineTempoMap; - } else if (region_conform_button.get_active()) { + if (operation_selector.get_active_row_number() == 1) { + return SnapRegionsToGrid; + } else if (operation_selector.get_active_row_number() == 2) { return ConformRegion; } @@ -230,19 +216,21 @@ RhythmFerret::get_action () const void RhythmFerret::run_analysis () { - if (!session) { + if (!_session) { return; } - RegionSelection& regions (editor.get_selection().regions); + clear_transients (); + + regions_with_transients = editor.get_selection().regions; current_results.clear (); - if (regions.empty()) { + if (regions_with_transients.empty()) { return; } - for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) { + for (RegionSelection::iterator i = regions_with_transients.begin(); i != regions_with_transients.end(); ++i) { boost::shared_ptr rd = boost::static_pointer_cast ((*i)->region()); @@ -257,45 +245,36 @@ RhythmFerret::run_analysis () break; } + (*i)->region()->set_onsets (current_results); + current_results.clear(); } - - for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) { - (*i)->get_time_axis_view().show_feature_lines (current_results); - } - } int -RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr readable, nframes64_t offset, AnalysisFeatureList& results) +RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr readable, frameoffset_t /*offset*/, AnalysisFeatureList& results) { - TransientDetector t (session->frame_rate()); + TransientDetector t (_session->frame_rate()); for (uint32_t i = 0; i < readable->n_channels(); ++i) { AnalysisFeatureList these_results; t.reset (); - t.set_threshold (detection_threshold_adjustment.get_value()); - t.set_sensitivity (sensitivity_adjustment.get_value()); + float dB = detection_threshold_adjustment.get_value(); + float coeff = dB > -80.0f ? pow (10.0f, dB * 0.05f) : 0.0f; + t.set_threshold (coeff); + t.set_sensitivity (4, sensitivity_adjustment.get_value()); if (t.run ("", readable.get(), i, these_results)) { continue; } - /* translate all transients to give absolute position */ - - for (AnalysisFeatureList::iterator x = these_results.begin(); x != these_results.end(); ++x) { - (*x) += offset; - } - /* merge */ results.insert (results.end(), these_results.begin(), these_results.end()); these_results.clear (); - } - if (!results.empty()) { - TransientDetector::cleanup_transients (results, session->frame_rate(), trigger_gap_adjustment.get_value()); + t.update_positions (readable.get(), i, results); } return 0; @@ -312,38 +291,38 @@ RhythmFerret::get_note_onset_function () return n; } } + fatal << string_compose (_("programming error: %1 (%2)"), X_("illegal note onset function string"), txt) << endmsg; - /*NOTREACHED*/ + + abort(); /*NOTREACHED*/ return -1; } int -RhythmFerret::run_note_onset_analysis (boost::shared_ptr readable, nframes64_t offset, AnalysisFeatureList& results) +RhythmFerret::run_note_onset_analysis (boost::shared_ptr readable, frameoffset_t /*offset*/, AnalysisFeatureList& results) { try { - OnsetDetector t (session->frame_rate()); + OnsetDetector t (_session->frame_rate()); for (uint32_t i = 0; i < readable->n_channels(); ++i) { AnalysisFeatureList these_results; - t.reset (); - t.set_function (get_note_onset_function()); t.set_silence_threshold (silence_threshold_adjustment.get_value()); t.set_peak_threshold (peak_picker_threshold_adjustment.get_value()); +#ifdef HAVE_AUBIO4 + t.set_minioi (minioi_adjustment.get_value()); +#endif + + // aubio-vamp only picks up new settings on reset. + t.reset (); if (t.run ("", readable.get(), i, these_results)) { continue; } - /* translate all transients to give absolute position */ - - for (AnalysisFeatureList::iterator x = these_results.begin(); x != these_results.end(); ++x) { - (*x) += offset; - } - /* merge */ results.insert (results.end(), these_results.begin(), these_results.end()); @@ -356,7 +335,7 @@ RhythmFerret::run_note_onset_analysis (boost::shared_ptr readable, nfr } if (!results.empty()) { - OnsetDetector::cleanup_onsets (results, session->frame_rate(), trigger_gap_adjustment.get_value()); + OnsetDetector::cleanup_onsets (results, _session->frame_rate(), trigger_gap_adjustment.get_value()); } return 0; @@ -365,7 +344,7 @@ RhythmFerret::run_note_onset_analysis (boost::shared_ptr readable, nfr void RhythmFerret::do_action () { - if (!session || current_results.empty()) { + if (!_session) { return; } @@ -373,7 +352,13 @@ RhythmFerret::do_action () case SplitRegion: do_split_action (); break; - + case SnapRegionsToGrid: + // split first, select all.. ?! + editor.snap_regions_to_grid(); + break; + case ConformRegion: + editor.close_region_gaps(); + break; default: break; } @@ -382,15 +367,33 @@ RhythmFerret::do_action () void RhythmFerret::do_split_action () { - /* this can/will change the current selection, so work with a copy */ - - RegionSelection& regions (editor.get_selection().regions); + /* XXX: this is quite a special-case; (currently) the only operation which is + performed on the selection only (without entered_regionview or the edit point + being considered) + */ + RegionSelection regions = editor.selection->regions; if (regions.empty()) { return; } - session->begin_reversible_command (_("split regions (rhythm ferret)")); + editor.EditorFreeze(); /* Emit signal */ + + editor.begin_reversible_command (_("split regions (rhythm ferret)")); + + /* Merge the transient positions for regions in consideration */ + AnalysisFeatureList merged_features; + + for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) { + + AnalysisFeatureList features; + (*i)->region()->transients(features); + + merged_features.insert (merged_features.end(), features.begin(), features.end()); + } + + merged_features.sort(); + merged_features.unique(); for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ) { @@ -399,16 +402,15 @@ RhythmFerret::do_split_action () tmp = i; ++tmp; - (*i)->get_time_axis_view().hide_feature_lines (); - - editor.split_region_at_points ((*i)->region(), current_results, false); + editor.split_region_at_points ((*i)->region(), merged_features, false, false); /* i is invalid at this point */ - i = tmp; } - session->commit_reversible_command (); + editor.commit_reversible_command (); + + editor.EditorThaw(); /* Emit signal */ } void @@ -418,15 +420,23 @@ RhythmFerret::set_session (Session* s) current_results.clear (); } -static void hide_time_axis_features (TimeAxisView& tav) +void +RhythmFerret::on_hide () { - tav.hide_feature_lines (); + ArdourDialog::on_hide (); + clear_transients (); } +/* Clear any transients that we have added */ void -RhythmFerret::on_hide () +RhythmFerret::clear_transients () { - editor.foreach_time_axis_view (sigc::ptr_fun (hide_time_axis_features)); - ArdourDialog::on_hide (); + current_results.clear (); + + for (RegionSelection::iterator i = regions_with_transients.begin(); i != regions_with_transients.end(); ++i) { + (*i)->region()->set_onsets (current_results); + } + + regions_with_transients.clear (); }