change charset for it_IT
[ardour2.git] / gtk2_ardour / rhythm_ferret.cc
blob4a4f3d4ec477a367cdf2497c40c53c73c7856485
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/onset_detector.h>
8 #include <ardour/audiosource.h>
9 #include <ardour/audioregion.h>
10 #include <ardour/playlist.h>
11 #include <ardour/region_factory.h>
12 #include <ardour/session.h>
14 #include "rhythm_ferret.h"
15 #include "audio_region_view.h"
16 #include "public_editor.h"
17 #include "utils.h"
19 #include "i18n.h"
21 using namespace std;
22 using namespace Gtk;
23 using namespace Gdk;
24 using namespace PBD;
25 using namespace ARDOUR;
27 /* order of these must match the AnalysisMode enums
28 in rhythm_ferret.h
30 static const gchar * _analysis_mode_strings[] = {
31 N_("Percussive Onset"),
32 N_("Note Onset"),
36 static const gchar * _onset_function_strings[] = {
37 N_("Energy Based"),
38 N_("Spectral Difference"),
39 N_("High-Frequency Content"),
40 N_("Complex Domain"),
41 N_("Phase Deviation"),
42 N_("Kullback-Liebler"),
43 N_("Modified Kullback-Liebler"),
47 RhythmFerret::RhythmFerret (PublicEditor& e)
48 : ArdourDialog (_("Rhythm Ferret"))
49 , editor (e)
50 , operation_frame (_("Operation"))
51 , selection_frame (_("Selection"))
52 , ferret_frame (_("Analysis"))
53 , logo (0)
54 , region_split_button (operation_button_group, _("Split Region"))
55 , tempo_button (operation_button_group, _("Set Tempo Map"))
56 , region_conform_button (operation_button_group, _("Conform Region"))
57 , analysis_mode_label (_("Mode"))
58 , detection_threshold_adjustment (3, 0, 20, 1, 4)
59 , detection_threshold_scale (detection_threshold_adjustment)
60 , detection_threshold_label (_("Threshold"))
61 , sensitivity_adjustment (40, 0, 100, 1, 10)
62 , sensitivity_scale (sensitivity_adjustment)
63 , sensitivity_label (_("Sensitivity"))
64 , analyze_button (_("Analyze"))
65 , onset_function_label (_("Detection function"))
66 , peak_picker_threshold_adjustment (0.3, 0.0, 1.0, 0.01, 0.1)
67 , peak_picker_threshold_scale (peak_picker_threshold_adjustment)
68 , peak_picker_label (_("Peak Threshold"))
69 , silence_threshold_adjustment (-90.0, -120.0, 0.0, 1, 10)
70 , silence_threshold_scale (silence_threshold_adjustment)
71 , silence_label (_("Silent Threshold (dB)"))
72 , trigger_gap_adjustment (3, 0, 100, 1, 10)
73 , trigger_gap_spinner (trigger_gap_adjustment)
74 , trigger_gap_label (_("Trigger gap (msecs)"))
75 , action_button (Stock::APPLY)
78 upper_hpacker.set_spacing (6);
80 upper_hpacker.pack_start (ferret_frame, true, true);
81 upper_hpacker.pack_start (selection_frame, true, true);
82 upper_hpacker.pack_start (operation_frame, true, true);
84 op_packer.pack_start (region_split_button, false, false);
85 op_packer.pack_start (tempo_button, false, false);
86 op_packer.pack_start (region_conform_button, false, false);
88 operation_frame.add (op_packer);
90 HBox* box;
92 ferret_packer.set_spacing (6);
93 ferret_packer.set_border_width (6);
95 vector<string> strings;
97 analysis_mode_strings = I18N (_analysis_mode_strings);
98 Gtkmm2ext::set_popdown_strings (analysis_mode_selector, analysis_mode_strings);
99 analysis_mode_selector.set_active_text (analysis_mode_strings.front());
100 analysis_mode_selector.signal_changed().connect (mem_fun (*this, &RhythmFerret::analysis_mode_changed));
102 onset_function_strings = I18N (_onset_function_strings);
103 Gtkmm2ext::set_popdown_strings (onset_detection_function_selector, onset_function_strings);
104 /* Onset plugin uses complex domain as default function
105 XXX there should be a non-hacky way to set this
107 onset_detection_function_selector.set_active_text (onset_function_strings[3]);
109 box = manage (new HBox);
110 box->set_spacing (6);
111 box->pack_start (analysis_mode_label, false, false);
112 box->pack_start (analysis_mode_selector, true, true);
113 ferret_packer.pack_start (*box, false, false);
115 ferret_packer.pack_start (analysis_packer, false, false);
117 box = manage (new HBox);
118 box->set_spacing (6);
119 box->pack_start (trigger_gap_label, false, false);
120 box->pack_start (trigger_gap_spinner, false, false);
121 ferret_packer.pack_start (*box, false, false);
123 ferret_packer.pack_start (analyze_button, false, false);
125 analyze_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::run_analysis));
127 box = manage (new HBox);
128 box->set_spacing (6);
129 box->pack_start (detection_threshold_label, false, false);
130 box->pack_start (detection_threshold_scale, true, true);
131 perc_onset_packer.pack_start (*box, false, false);
133 box = manage (new HBox);
134 box->set_spacing (6);
135 box->pack_start (sensitivity_label, false, false);
136 box->pack_start (sensitivity_scale, true, true);
137 perc_onset_packer.pack_start (*box, false, false);
139 box = manage (new HBox);
140 box->set_spacing (6);
141 box->pack_start (onset_function_label, false, false);
142 box->pack_start (onset_detection_function_selector, true, true);
143 note_onset_packer.pack_start (*box, false, false);
145 box = manage (new HBox);
146 box->set_spacing (6);
147 box->pack_start (peak_picker_label, false, false);
148 box->pack_start (peak_picker_threshold_scale, true, true);
149 note_onset_packer.pack_start (*box, false, false);
151 box = manage (new HBox);
152 box->set_spacing (6);
153 box->pack_start (silence_label, false, false);
154 box->pack_start (silence_threshold_scale, true, true);
155 note_onset_packer.pack_start (*box, false, false);
157 analysis_mode_changed ();
159 ferret_frame.add (ferret_packer);
161 logo = manage (new Gtk::Image (::get_icon (X_("ferret_02"))));
163 if (logo) {
164 lower_hpacker.pack_start (*logo, false, false);
167 lower_hpacker.pack_start (operation_clarification_label, true, true);
168 lower_hpacker.pack_start (action_button, false, false);
169 lower_hpacker.set_border_width (6);
170 lower_hpacker.set_spacing (6);
172 action_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::do_action));
174 get_vbox()->set_border_width (6);
175 get_vbox()->set_spacing (6);
176 get_vbox()->pack_start (upper_hpacker, true, true);
177 get_vbox()->pack_start (lower_hpacker, false, false);
179 show_all ();
182 RhythmFerret::~RhythmFerret()
184 if (logo) {
185 delete logo;
189 void
190 RhythmFerret::analysis_mode_changed ()
192 analysis_packer.children().clear ();
194 switch (get_analysis_mode()) {
195 case PercussionOnset:
196 analysis_packer.pack_start (perc_onset_packer);
197 break;
199 case NoteOnset:
200 analysis_packer.pack_start (note_onset_packer);
201 break;
204 analysis_packer.show_all ();
207 RhythmFerret::AnalysisMode
208 RhythmFerret::get_analysis_mode () const
210 string str = analysis_mode_selector.get_active_text ();
212 if (str == analysis_mode_strings[(int) NoteOnset]) {
213 return NoteOnset;
216 return PercussionOnset;
219 RhythmFerret::Action
220 RhythmFerret::get_action () const
222 if (tempo_button.get_active()) {
223 return DefineTempoMap;
224 } else if (region_conform_button.get_active()) {
225 return ConformRegion;
228 return SplitRegion;
231 void
232 RhythmFerret::run_analysis ()
234 if (!session) {
235 return;
238 RegionSelection& regions (editor.get_selection().regions);
240 current_results.clear ();
242 if (regions.empty()) {
243 return;
246 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
248 boost::shared_ptr<Readable> rd = boost::static_pointer_cast<AudioRegion> ((*i)->region());
250 switch (get_analysis_mode()) {
251 case PercussionOnset:
252 run_percussion_onset_analysis (rd, (*i)->region()->position(), current_results);
253 break;
254 case NoteOnset:
255 run_note_onset_analysis (rd, (*i)->region()->position(), current_results);
256 break;
257 default:
258 break;
263 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
264 (*i)->get_time_axis_view().show_feature_lines (current_results);
270 RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr<Readable> readable, nframes64_t offset, AnalysisFeatureList& results)
272 TransientDetector t (session->frame_rate());
274 for (uint32_t i = 0; i < readable->n_channels(); ++i) {
276 AnalysisFeatureList these_results;
278 t.reset ();
279 t.set_threshold (detection_threshold_adjustment.get_value());
280 t.set_sensitivity (sensitivity_adjustment.get_value());
282 if (t.run ("", readable.get(), i, these_results)) {
283 continue;
286 /* translate all transients to give absolute position */
288 for (AnalysisFeatureList::iterator x = these_results.begin(); x != these_results.end(); ++x) {
289 (*x) += offset;
292 /* merge */
294 results.insert (results.end(), these_results.begin(), these_results.end());
295 these_results.clear ();
298 if (!results.empty()) {
299 TransientDetector::cleanup_transients (results, session->frame_rate(), trigger_gap_adjustment.get_value());
302 return 0;
306 RhythmFerret::get_note_onset_function ()
308 string txt = onset_detection_function_selector.get_active_text();
310 for (int n = 0; _onset_function_strings[n]; ++n) {
311 /* compare translated versions */
312 if (txt == onset_function_strings[n]) {
313 return n;
316 fatal << string_compose (_("programming error: %1 (%2)"), X_("illegal note onset function string"), txt)
317 << endmsg;
318 /*NOTREACHED*/
319 return -1;
323 RhythmFerret::run_note_onset_analysis (boost::shared_ptr<Readable> readable, nframes64_t offset, AnalysisFeatureList& results)
325 try {
326 OnsetDetector t (session->frame_rate());
328 for (uint32_t i = 0; i < readable->n_channels(); ++i) {
330 AnalysisFeatureList these_results;
332 t.reset ();
334 t.set_function (get_note_onset_function());
335 t.set_silence_threshold (silence_threshold_adjustment.get_value());
336 t.set_peak_threshold (peak_picker_threshold_adjustment.get_value());
338 if (t.run ("", readable.get(), i, these_results)) {
339 continue;
342 /* translate all transients to give absolute position */
344 for (AnalysisFeatureList::iterator x = these_results.begin(); x != these_results.end(); ++x) {
345 (*x) += offset;
348 /* merge */
350 results.insert (results.end(), these_results.begin(), these_results.end());
351 these_results.clear ();
354 } catch (failed_constructor& err) {
355 error << "Could not load note onset detection plugin" << endmsg;
356 return -1;
359 if (!results.empty()) {
360 OnsetDetector::cleanup_onsets (results, session->frame_rate(), trigger_gap_adjustment.get_value());
363 return 0;
366 void
367 RhythmFerret::do_action ()
369 if (!session || current_results.empty()) {
370 return;
373 switch (get_action()) {
374 case SplitRegion:
375 do_split_action ();
376 break;
378 default:
379 break;
383 void
384 RhythmFerret::do_split_action ()
386 /* this can/will change the current selection, so work with a copy */
388 RegionSelection& regions (editor.get_selection().regions);
390 if (regions.empty()) {
391 return;
394 session->begin_reversible_command (_("split regions (rhythm ferret)"));
396 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ) {
398 RegionSelection::iterator tmp;
400 tmp = i;
401 ++tmp;
403 (*i)->get_time_axis_view().hide_feature_lines ();
405 editor.split_region_at_points ((*i)->region(), current_results, false);
407 /* i is invalid at this point */
409 i = tmp;
411 session->commit_reversible_command ();
415 void
416 RhythmFerret::set_session (Session* s)
418 ArdourDialog::set_session (s);
419 current_results.clear ();
422 static void hide_time_axis_features (TimeAxisView& tav)
424 tav.hide_feature_lines ();
427 void
428 RhythmFerret::on_hide ()
430 editor.foreach_time_axis_view (sigc::ptr_fun (hide_time_axis_features));
431 ArdourDialog::on_hide ();