factor out shuttle controller to its own class, new design
[ardour2.git] / gtk2_ardour / analysis_window.cc
blobafa277e8e44919e8bf96b5686f5af5dcba3aa7b6
1 /*
2 Copyright (C) 2006 Paul Davis
3 Written by Sampo Savolainen
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <gtkmm2ext/gtk_ui.h>
22 #include <gtkmm/stock.h>
23 #include <gtkmm/label.h>
24 #include <gtkmm/treemodel.h>
25 #include <gtkmm/treeiter.h>
27 #include "ardour/audioregion.h"
28 #include "ardour/audioplaylist.h"
29 #include "ardour/types.h"
31 #include "analysis_window.h"
33 #include "route_ui.h"
34 #include "time_axis_view.h"
35 #include "public_editor.h"
36 #include "selection.h"
37 #include "audio_region_view.h"
39 #include "i18n.h"
41 using namespace ARDOUR;
42 using namespace PBD;
44 AnalysisWindow::AnalysisWindow() :
46 source_selection_label (_("Signal source")),
47 source_selection_ranges_rb (_("Selected ranges")),
48 source_selection_regions_rb (_("Selected regions")),
50 display_model_label (_("Display model")),
51 display_model_composite_separate_rb (_("Composite graphs for each track")),
52 display_model_composite_all_tracks_rb (_("Composite graph of all tracks")),
54 show_minmax_button (_("Show frequency power range")),
55 show_normalized_button (_("Normalize values")),
57 fft_graph (16384)
59 set_name(_("FFT analysis window"));
60 set_title (_("Spectral Analysis"));
62 track_list_ready = false;
64 // Left side: track list + controls
65 tlmodel = Gtk::ListStore::create(tlcols);
66 track_list.set_model (tlmodel);
67 track_list.append_column(_("Track"), tlcols.trackname);
68 track_list.append_column_editable(_("Show"), tlcols.visible);
69 track_list.set_headers_visible(true);
70 track_list.set_reorderable(false);
71 track_list.get_selection()->set_mode (Gtk::SELECTION_NONE);
74 Gtk::TreeViewColumn* track_col = track_list.get_column(0);
75 Gtk::CellRendererText* renderer = dynamic_cast<Gtk::CellRendererText*>(track_list.get_column_cell_renderer (0));
77 track_col->add_attribute(renderer->property_foreground_gdk(), tlcols.color);
78 track_col->set_expand(true);
81 tlmodel->signal_row_changed().connect (
82 sigc::mem_fun(*this, &AnalysisWindow::track_list_row_changed) );
84 fft_graph.set_analysis_window(this);
86 vbox.pack_start(track_list);
89 // "Signal source"
90 vbox.pack_start(source_selection_label, false, false);
93 Gtk::RadioButtonGroup group = source_selection_ranges_rb.get_group();
94 source_selection_regions_rb.set_group(group);
96 source_selection_ranges_rb.set_active();
98 vbox.pack_start (source_selection_ranges_rb, false, false);
99 vbox.pack_start (source_selection_regions_rb, false, false);
101 // "Selected ranges" radio
102 source_selection_ranges_rb.signal_toggled().connect (
103 sigc::bind ( sigc::mem_fun(*this, &AnalysisWindow::source_selection_changed), &source_selection_ranges_rb));
105 // "Selected regions" radio
106 source_selection_regions_rb.signal_toggled().connect (
107 sigc::bind ( sigc::mem_fun(*this, &AnalysisWindow::source_selection_changed), &source_selection_regions_rb));
110 vbox.pack_start(hseparator1, false, false);
112 // "Display model"
113 vbox.pack_start(display_model_label, false, false);
115 Gtk::RadioButtonGroup group = display_model_composite_separate_rb.get_group();
116 display_model_composite_all_tracks_rb.set_group (group);
118 display_model_composite_separate_rb.set_active();
120 vbox.pack_start (display_model_composite_separate_rb, false, false);
121 vbox.pack_start (display_model_composite_all_tracks_rb, false, false);
123 // "Composite graphs for all tracks"
124 display_model_composite_separate_rb.signal_toggled().connect (
125 sigc::bind ( sigc::mem_fun(*this, &AnalysisWindow::display_model_changed), &display_model_composite_separate_rb));
127 // "Composite graph of all tracks"
128 display_model_composite_all_tracks_rb.signal_toggled().connect (
129 sigc::bind ( sigc::mem_fun(*this, &AnalysisWindow::display_model_changed), &display_model_composite_all_tracks_rb));
132 // Analyze button
134 refresh_button.set_name("EditorGTKButton");
135 refresh_button.set_label(_("Re-analyze data"));
137 refresh_button.signal_clicked().connect ( sigc::bind ( sigc::mem_fun(*this, &AnalysisWindow::analyze_data), &refresh_button));
139 vbox.pack_start(refresh_button, false, false, 10);
142 // Feature checkboxes
144 // minmax
145 show_minmax_button.signal_toggled().connect( sigc::mem_fun(*this, &AnalysisWindow::show_minmax_changed));
146 vbox.pack_start(show_minmax_button, false, false);
148 // normalize
149 show_normalized_button.signal_toggled().connect( sigc::mem_fun(*this, &AnalysisWindow::show_normalized_changed));
150 vbox.pack_start(show_normalized_button, false, false);
156 hbox.pack_start(vbox, Gtk::PACK_SHRINK);
158 // Analysis window on the right
159 fft_graph.ensure_style();
161 hbox.add(fft_graph);
165 // And last we pack the hbox
166 add(hbox);
167 show_all();
168 track_list.show_all();
171 AnalysisWindow::~AnalysisWindow()
176 void
177 AnalysisWindow::show_minmax_changed()
179 fft_graph.set_show_minmax(show_minmax_button.get_active());
182 void
183 AnalysisWindow::show_normalized_changed()
185 fft_graph.set_show_normalized(show_normalized_button.get_active());
188 void
189 AnalysisWindow::set_rangemode()
191 source_selection_ranges_rb.set_active(true);
194 void
195 AnalysisWindow::set_regionmode()
197 source_selection_regions_rb.set_active(true);
200 void
201 AnalysisWindow::track_list_row_changed(const Gtk::TreeModel::Path& /*path*/, const Gtk::TreeModel::iterator& /*iter*/)
203 if (track_list_ready) {
204 fft_graph.redraw();
209 void
210 AnalysisWindow::clear_tracklist()
212 // Empty track list & free old graphs
213 Gtk::TreeNodeChildren children = track_list.get_model()->children();
215 for (Gtk::TreeIter i = children.begin(); i != children.end(); i++) {
216 Gtk::TreeModel::Row row = *i;
218 FFTResult *delete_me = row[tlcols.graph];
219 if (delete_me == 0)
220 continue;
222 // Make sure it's not drawn
223 row[tlcols.graph] = 0;
225 delete delete_me;
228 tlmodel->clear();
231 void
232 AnalysisWindow::analyze()
234 analyze_data(&refresh_button);
237 void
238 AnalysisWindow::analyze_data (Gtk::Button */*button*/)
240 track_list_ready = false;
242 Glib::Mutex::Lock lm (track_list_lock);
244 // Empty track list & free old graphs
245 clear_tracklist();
247 // first we gather the FFTResults of all tracks
249 Sample *buf = (Sample *) malloc(sizeof(Sample) * fft_graph.windowSize());
250 Sample *mixbuf = (Sample *) malloc(sizeof(Sample) * fft_graph.windowSize());
251 float *gain = (float *) malloc(sizeof(float) * fft_graph.windowSize());
253 Selection& s (PublicEditor::instance().get_selection());
254 TimeSelection ts = s.time;
255 RegionSelection ars = s.regions;
257 for (TrackSelection::iterator i = s.tracks.begin(); i != s.tracks.end(); ++i) {
258 boost::shared_ptr<AudioPlaylist> pl
259 = boost::dynamic_pointer_cast<AudioPlaylist>((*i)->playlist());
261 if (!pl)
262 continue;
264 RouteUI *rui = dynamic_cast<RouteUI *>(*i);
265 int n_inputs = rui->route()->n_inputs().n_audio(); // FFT is audio only
267 // Busses don't have playlists, so we need to check that we actually are working with a playlist
268 if (!pl || !rui)
269 continue;
271 FFTResult *res = fft_graph.prepareResult(rui->color(), rui->route()->name());
273 // if timeSelection
274 if (source_selection_ranges_rb.get_active()) {
275 // cerr << "Analyzing ranges on track " << *&rui->route().name() << endl;
277 for (std::list<AudioRange>::iterator j = ts.begin(); j != ts.end(); ++j) {
279 int n;
280 for (int channel = 0; channel < n_inputs; channel++) {
281 framecnt_t x = 0;
283 while (x < j->length()) {
284 // TODO: What about stereo+ channels? composite all to one, I guess
286 n = fft_graph.windowSize();
288 if (x + n >= (*j).length() ) {
289 n = (*j).length() - x;
292 n = pl->read(buf, mixbuf, gain, (*j).start + x, n, channel);
294 if ( n < fft_graph.windowSize()) {
295 for (int j = n; j < fft_graph.windowSize(); j++) {
296 buf[j] = 0.0;
300 res->analyzeWindow(buf);
302 x += n;
306 } else if (source_selection_regions_rb.get_active()) {
307 // cerr << "Analyzing selected regions on track " << *&rui->route().name() << endl;
309 TimeAxisView *current_axis = (*i);
311 for (RegionSelection::iterator j = ars.begin(); j != ars.end(); ++j) {
312 // Check that the region is actually audio (so we can analyze it)
313 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*j);
314 if (!arv)
315 continue;
317 // Check that the region really is selected on _this_ track/solo
318 if ( &arv->get_time_axis_view() != current_axis)
319 continue;
321 // cerr << " - " << (*j)->region().name() << ": " << (*j)->region().length() << " samples starting at " << (*j)->region().position() << endl;
322 int n;
323 for (int channel = 0; channel < n_inputs; channel++) {
325 framecnt_t x = 0;
327 framecnt_t length = arv->region()->length();
329 while (x < length) {
330 // TODO: What about stereo+ channels? composite all to one, I guess
332 n = fft_graph.windowSize();
333 if (x + n >= length ) {
334 n = length - x;
337 n = arv->audio_region()->read_at(buf, mixbuf, gain, arv->region()->position() + x, n, channel);
339 if (n == 0)
340 break;
342 if ( n < fft_graph.windowSize()) {
343 for (int j = n; j < fft_graph.windowSize(); j++) {
344 buf[j] = 0.0;
348 res->analyzeWindow(buf);
350 x += n;
353 // cerr << "Found: " << (*j)->get_item_name() << endl;
358 res->finalize();
361 Gtk::TreeModel::Row newrow = *(tlmodel)->append();
362 newrow[tlcols.trackname] = rui->route()->name();
363 newrow[tlcols.visible] = true;
364 newrow[tlcols.color] = rui->color();
365 newrow[tlcols.graph] = res;
369 free(buf);
370 free(mixbuf);
372 track_list_ready = true;
373 } /* end lock */
375 fft_graph.redraw();
378 void
379 AnalysisWindow::source_selection_changed (Gtk::RadioButton *button)
381 // We are only interested in activation signals, not deactivation signals
382 if (!button->get_active())
383 return;
386 cerr << "AnalysisWindow: signal source = ";
388 if (button == &source_selection_ranges_rb) {
389 cerr << "selected ranges" << endl;
391 } else if (button == &source_selection_regions_rb) {
392 cerr << "selected regions" << endl;
394 } else {
395 cerr << "unknown?" << endl;
400 void
401 AnalysisWindow::display_model_changed (Gtk::RadioButton *button)
403 // We are only interested in activation signals, not deactivation signals
404 if (!button->get_active())
405 return;
408 cerr << "AnalysisWindow: display model = ";
410 if (button == &display_model_composite_separate_rb) {
411 cerr << "separate composites of tracks" << endl;
412 } else if (button == &display_model_composite_all_tracks_rb) {
413 cerr << "composite of all tracks" << endl;
414 } else {
415 cerr << "unknown?" << endl;