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"
23 using namespace ARDOUR;
25 /* order of these must match the AnalysisMode enums
28 static const gchar * _analysis_mode_strings[] = {
29 N_("Percussive Onset"),
34 RhythmFerret::RhythmFerret (PublicEditor& e)
35 : ArdourDialog (_("Rhythm Ferret"))
37 , operation_frame (_("Operation"))
38 , selection_frame (_("Selection"))
39 , ferret_frame (_("Analysis"))
41 , region_split_button (operation_button_group, _("Split Region"))
42 , tempo_button (operation_button_group, _("Set Tempo Map"))
43 , region_conform_button (operation_button_group, _("Conform Region"))
44 , analysis_mode_label (_("Mode"))
45 , detection_threshold_adjustment (3, 0, 20, 1, 4)
46 , detection_threshold_scale (detection_threshold_adjustment)
47 , detection_threshold_label (_("Threshold"))
48 , sensitivity_adjustment (40, 0, 100, 1, 10)
49 , sensitivity_scale (sensitivity_adjustment)
50 , sensitivity_label (_("Sensitivity"))
51 , analyze_button (_("Analyze"))
52 , trigger_gap_adjustment (3, 0, 100, 1, 10)
53 , trigger_gap_spinner (trigger_gap_adjustment)
54 , trigger_gap_label (_("Trigger gap (msecs)"))
55 , action_button (Stock::APPLY)
58 upper_hpacker.set_spacing (6);
60 upper_hpacker.pack_start (operation_frame, true, true);
61 upper_hpacker.pack_start (selection_frame, true, true);
62 upper_hpacker.pack_start (ferret_frame, true, true);
64 op_packer.pack_start (region_split_button, false, false);
65 op_packer.pack_start (tempo_button, false, false);
66 op_packer.pack_start (region_conform_button, false, false);
68 operation_frame.add (op_packer);
72 ferret_packer.set_spacing (6);
73 ferret_packer.set_border_width (6);
75 vector<string> strings;
77 analysis_mode_strings = I18N (_analysis_mode_strings);
78 Gtkmm2ext::set_popdown_strings (analysis_mode_selector, analysis_mode_strings);
79 analysis_mode_selector.set_active_text (analysis_mode_strings.front());
81 box = manage (new HBox);
83 box->pack_start (analysis_mode_label, false, false);
84 box->pack_start (analysis_mode_selector, true, true);
85 ferret_packer.pack_start (*box, false, false);
87 box = manage (new HBox);
89 box->pack_start (detection_threshold_label, false, false);
90 box->pack_start (detection_threshold_scale, true, true);
91 ferret_packer.pack_start (*box, false, false);
93 box = manage (new HBox);
95 box->pack_start (sensitivity_label, false, false);
96 box->pack_start (sensitivity_scale, true, true);
97 ferret_packer.pack_start (*box, false, false);
99 box = manage (new HBox);
100 box->set_spacing (6);
101 box->pack_start (trigger_gap_label, false, false);
102 box->pack_start (trigger_gap_spinner, false, false);
103 ferret_packer.pack_start (*box, false, false);
105 ferret_packer.pack_start (analyze_button, false, false);
107 analyze_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::run_analysis));
109 ferret_frame.add (ferret_packer);
111 // Glib::RefPtr<Pixbuf> logo_pixbuf ("somefile");
114 lower_hpacker.pack_start (*logo, false, false);
117 lower_hpacker.pack_start (operation_clarification_label, false, false);
118 lower_hpacker.pack_start (action_button, false, false);
120 action_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::do_action));
122 get_vbox()->set_border_width (6);
123 get_vbox()->set_spacing (6);
124 get_vbox()->pack_start (upper_hpacker, true, true);
125 get_vbox()->pack_start (lower_hpacker, false, false);
130 RhythmFerret::~RhythmFerret()
137 RhythmFerret::AnalysisMode
138 RhythmFerret::get_analysis_mode () const
140 string str = analysis_mode_selector.get_active_text ();
142 if (str == _(_analysis_mode_strings[(int) NoteOnset])) {
146 return PercussionOnset;
150 RhythmFerret::get_action () const
152 if (tempo_button.get_active()) {
153 return DefineTempoMap;
154 } else if (region_conform_button.get_active()) {
155 return ConformRegion;
162 RhythmFerret::run_analysis ()
168 RegionSelection& regions (editor.get_selection().regions);
170 current_results.clear ();
172 if (regions.empty()) {
176 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
178 boost::shared_ptr<Readable> rd = boost::static_pointer_cast<AudioRegion> ((*i)->region());
180 switch (get_analysis_mode()) {
181 case PercussionOnset:
182 run_percussion_onset_analysis (rd, (*i)->region()->position(), current_results);
190 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
191 (*i)->get_time_axis_view().show_temporary_lines (current_results);
197 RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr<Readable> readable, nframes64_t offset, vector<nframes64_t>& results)
199 TransientDetector t (session->frame_rate());
201 for (uint32_t i = 0; i < readable->n_channels(); ++i) {
203 vector<nframes64_t> these_results;
206 t.set_threshold (detection_threshold_adjustment.get_value());
207 t.set_sensitivity (sensitivity_adjustment.get_value());
209 if (t.run ("", readable.get(), i, these_results)) {
213 /* translate all transients to give absolute position */
215 for (vector<nframes64_t>::iterator i = these_results.begin(); i != these_results.end(); ++i) {
221 results.insert (results.end(), these_results.begin(), these_results.end());
224 if (!results.empty()) {
226 /* now resort to bring transients from different channels together */
228 sort (results.begin(), results.end());
230 /* remove duplicates or other things that are too close */
232 vector<nframes64_t>::iterator i = results.begin();
233 nframes64_t curr = (*i);
234 nframes64_t gap_frames = (nframes64_t) floor (trigger_gap_adjustment.get_value() * (session->frame_rate() / 1000.0));
238 while (i != results.end()) {
239 if (((*i) == curr) || (((*i) - curr) < gap_frames)) {
240 i = results.erase (i);
253 RhythmFerret::do_action ()
255 if (!session || current_results.empty()) {
259 switch (get_action()) {
270 RhythmFerret::do_split_action ()
272 /* this can/will change the current selection, so work with a copy */
274 RegionSelection& regions (editor.get_selection().regions);
276 if (regions.empty()) {
280 session->begin_reversible_command (_("split regions (rhythm ferret)"));
282 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ) {
284 RegionSelection::iterator tmp;
289 (*i)->get_time_axis_view().hide_temporary_lines ();
291 editor.split_region_at_points ((*i)->region(), current_results);
293 /* i is invalid at this point */
301 RhythmFerret::set_session (Session* s)
303 ArdourDialog::set_session (s);
304 current_results.clear ();