1 #include <gtkmm/stock.h>
2 #include <gtkmm2ext/utils.h>
4 #include <pbd/memento_command.h>
6 #include <ardour/transient_detector.h>
7 #include <ardour/audiosource.h>
8 #include <ardour/audioregion.h>
9 #include <ardour/playlist.h>
10 #include <ardour/region_factory.h>
11 #include <ardour/session.h>
13 #include "rhythm_ferret.h"
14 #include "audio_region_view.h"
15 #include "public_editor.h"
24 using namespace ARDOUR;
26 /* order of these must match the AnalysisMode enums
29 static const gchar * _analysis_mode_strings[] = {
30 N_("Percussive Onset"),
35 RhythmFerret::RhythmFerret (PublicEditor& e)
36 : ArdourDialog (_("Rhythm Ferret"))
38 , operation_frame (_("Operation"))
39 , selection_frame (_("Selection"))
40 , ferret_frame (_("Analysis"))
42 , region_split_button (operation_button_group, _("Split Region"))
43 , tempo_button (operation_button_group, _("Set Tempo Map"))
44 , region_conform_button (operation_button_group, _("Conform Region"))
45 , analysis_mode_label (_("Mode"))
46 , detection_threshold_adjustment (3, 0, 20, 1, 4)
47 , detection_threshold_scale (detection_threshold_adjustment)
48 , detection_threshold_label (_("Threshold"))
49 , sensitivity_adjustment (40, 0, 100, 1, 10)
50 , sensitivity_scale (sensitivity_adjustment)
51 , sensitivity_label (_("Sensitivity"))
52 , analyze_button (_("Analyze"))
53 , trigger_gap_adjustment (3, 0, 100, 1, 10)
54 , trigger_gap_spinner (trigger_gap_adjustment)
55 , trigger_gap_label (_("Trigger gap (msecs)"))
56 , action_button (Stock::APPLY)
59 upper_hpacker.set_spacing (6);
61 upper_hpacker.pack_start (ferret_frame, true, true);
62 upper_hpacker.pack_start (selection_frame, true, true);
63 upper_hpacker.pack_start (operation_frame, true, true);
65 op_packer.pack_start (region_split_button, false, false);
66 op_packer.pack_start (tempo_button, false, false);
67 op_packer.pack_start (region_conform_button, false, false);
69 operation_frame.add (op_packer);
73 ferret_packer.set_spacing (6);
74 ferret_packer.set_border_width (6);
76 vector<string> strings;
78 analysis_mode_strings = I18N (_analysis_mode_strings);
79 Gtkmm2ext::set_popdown_strings (analysis_mode_selector, analysis_mode_strings);
80 analysis_mode_selector.set_active_text (analysis_mode_strings.front());
82 box = manage (new HBox);
84 box->pack_start (analysis_mode_label, false, false);
85 box->pack_start (analysis_mode_selector, true, true);
86 ferret_packer.pack_start (*box, false, false);
88 box = manage (new HBox);
90 box->pack_start (detection_threshold_label, false, false);
91 box->pack_start (detection_threshold_scale, true, true);
92 ferret_packer.pack_start (*box, false, false);
94 box = manage (new HBox);
96 box->pack_start (sensitivity_label, false, false);
97 box->pack_start (sensitivity_scale, true, true);
98 ferret_packer.pack_start (*box, false, false);
100 box = manage (new HBox);
101 box->set_spacing (6);
102 box->pack_start (trigger_gap_label, false, false);
103 box->pack_start (trigger_gap_spinner, false, false);
104 ferret_packer.pack_start (*box, false, false);
106 ferret_packer.pack_start (analyze_button, false, false);
108 analyze_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::run_analysis));
110 ferret_frame.add (ferret_packer);
112 logo = manage (new Gtk::Image (::get_icon (X_("ferret_02"))));
115 lower_hpacker.pack_start (*logo, false, false);
118 lower_hpacker.pack_start (operation_clarification_label, true, true);
119 lower_hpacker.pack_start (action_button, false, false);
120 lower_hpacker.set_border_width (6);
121 lower_hpacker.set_spacing (6);
123 action_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::do_action));
125 get_vbox()->set_border_width (6);
126 get_vbox()->set_spacing (6);
127 get_vbox()->pack_start (upper_hpacker, true, true);
128 get_vbox()->pack_start (lower_hpacker, false, false);
133 RhythmFerret::~RhythmFerret()
140 RhythmFerret::AnalysisMode
141 RhythmFerret::get_analysis_mode () const
143 string str = analysis_mode_selector.get_active_text ();
145 if (str == _(_analysis_mode_strings[(int) NoteOnset])) {
149 return PercussionOnset;
153 RhythmFerret::get_action () const
155 if (tempo_button.get_active()) {
156 return DefineTempoMap;
157 } else if (region_conform_button.get_active()) {
158 return ConformRegion;
165 RhythmFerret::run_analysis ()
171 RegionSelection& regions (editor.get_selection().regions);
173 current_results.clear ();
175 if (regions.empty()) {
179 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
181 boost::shared_ptr<Readable> rd = boost::static_pointer_cast<AudioRegion> ((*i)->region());
183 switch (get_analysis_mode()) {
184 case PercussionOnset:
185 run_percussion_onset_analysis (rd, (*i)->region()->position(), current_results);
193 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
194 (*i)->get_time_axis_view().show_temporary_lines (current_results);
200 RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr<Readable> readable, nframes64_t offset, AnalysisFeatureList& results)
202 TransientDetector t (session->frame_rate());
204 for (uint32_t i = 0; i < readable->n_channels(); ++i) {
206 AnalysisFeatureList these_results;
209 t.set_threshold (detection_threshold_adjustment.get_value());
210 t.set_sensitivity (sensitivity_adjustment.get_value());
212 if (t.run ("", readable.get(), i, these_results)) {
216 /* translate all transients to give absolute position */
218 for (AnalysisFeatureList::iterator x = these_results.begin(); x != these_results.end(); ++x) {
224 results.insert (results.end(), these_results.begin(), these_results.end());
225 these_results.clear ();
228 if (!results.empty()) {
229 TransientDetector::cleanup_transients (results, session->frame_rate(), trigger_gap_adjustment.get_value());
236 RhythmFerret::do_action ()
238 if (!session || current_results.empty()) {
242 switch (get_action()) {
253 RhythmFerret::do_split_action ()
255 /* this can/will change the current selection, so work with a copy */
257 RegionSelection& regions (editor.get_selection().regions);
259 if (regions.empty()) {
263 session->begin_reversible_command (_("split regions (rhythm ferret)"));
265 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ) {
267 RegionSelection::iterator tmp;
272 (*i)->get_time_axis_view().hide_temporary_lines ();
274 editor.split_region_at_points ((*i)->region(), current_results);
276 /* i is invalid at this point */
284 RhythmFerret::set_session (Session* s)
286 ArdourDialog::set_session (s);
287 current_results.clear ();