ardour
analysis_window.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 Paul Davis
3  Written by Sampo Savolainen
4 
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.
9 
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.
14 
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.
18 
19 */
20 
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>
26 
27 #include "ardour/audioregion.h"
28 #include "ardour/audioplaylist.h"
29 #include "ardour/types.h"
30 
31 #include "analysis_window.h"
32 
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"
38 
39 #include "i18n.h"
40 
41 using namespace ARDOUR;
42 using namespace PBD;
43 
45 
46  source_selection_label (_("Signal source")),
47  source_selection_ranges_rb (_("Selected ranges")),
48  source_selection_regions_rb (_("Selected regions")),
49 
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")),
53 
54  show_minmax_button (_("Show frequency power range")),
55  show_normalized_button (_("Normalize values")),
56 
57  fft_graph (16384)
58 {
59  set_name(_("FFT analysis window"));
60  set_title (_("Spectral Analysis"));
61 
62  track_list_ready = false;
63 
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);
72 
73 
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));
76 
77  track_col->add_attribute(renderer->property_foreground_gdk(), tlcols.color);
78  track_col->set_expand(true);
79 
80 
81  tlmodel->signal_row_changed().connect (
82  sigc::mem_fun(*this, &AnalysisWindow::track_list_row_changed) );
83 
85 
86  vbox.pack_start(track_list);
87 
88 
89  // "Signal source"
90  vbox.pack_start(source_selection_label, false, false);
91 
92  {
93  Gtk::RadioButtonGroup group = source_selection_ranges_rb.get_group();
94  source_selection_regions_rb.set_group(group);
95 
96  source_selection_ranges_rb.set_active();
97 
98  vbox.pack_start (source_selection_ranges_rb, false, false);
99  vbox.pack_start (source_selection_regions_rb, false, false);
100 
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));
104 
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));
108  }
109 
110  vbox.pack_start(hseparator1, false, false);
111 
112  // "Display model"
113  vbox.pack_start(display_model_label, false, false);
114  {
115  Gtk::RadioButtonGroup group = display_model_composite_separate_rb.get_group();
116  display_model_composite_all_tracks_rb.set_group (group);
117 
119 
120  vbox.pack_start (display_model_composite_separate_rb, false, false);
121  vbox.pack_start (display_model_composite_all_tracks_rb, false, false);
122 
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));
126 
127  // "Composite graph of all tracks"
128  display_model_composite_all_tracks_rb.signal_toggled().connect (
130  }
131 
132  // Analyze button
133 
134  refresh_button.set_name("EditorGTKButton");
135  refresh_button.set_label(_("Re-analyze data"));
136 
137  refresh_button.signal_clicked().connect ( sigc::bind ( sigc::mem_fun(*this, &AnalysisWindow::analyze_data), &refresh_button));
138 
139  vbox.pack_start(refresh_button, false, false, 10);
140 
141 
142  // Feature checkboxes
143 
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);
147 
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);
151 
152 
153 
154 
155 
156  hbox.pack_start(vbox, Gtk::PACK_SHRINK);
157 
158  // Analysis window on the right
159  fft_graph.ensure_style();
160 
161  hbox.add(fft_graph);
162 
163 
164 
165  // And last we pack the hbox
166  add(hbox);
167  show_all();
168  track_list.show_all();
169 }
170 
172 {
173 
174 }
175 
176 void
178 {
180 }
181 
182 void
184 {
186 }
187 
188 void
190 {
191  source_selection_ranges_rb.set_active(true);
192 }
193 
194 void
196 {
197  source_selection_regions_rb.set_active(true);
198 }
199 
200 void
201 AnalysisWindow::track_list_row_changed(const Gtk::TreeModel::Path& /*path*/, const Gtk::TreeModel::iterator& /*iter*/)
202 {
203  if (track_list_ready) {
204  fft_graph.redraw();
205  }
206 }
207 
208 
209 void
211 {
212  // Empty track list & free old graphs
213  Gtk::TreeNodeChildren children = track_list.get_model()->children();
214 
215  for (Gtk::TreeIter i = children.begin(); i != children.end(); i++) {
216  Gtk::TreeModel::Row row = *i;
217 
218  FFTResult *delete_me = row[tlcols.graph];
219  if (delete_me == 0)
220  continue;
221 
222  // Make sure it's not drawn
223  row[tlcols.graph] = 0;
224 
225  delete delete_me;
226  }
227 
228  tlmodel->clear();
229 }
230 
231 void
233 {
235 }
236 
237 void
238 AnalysisWindow::analyze_data (Gtk::Button * /*button*/)
239 {
240  track_list_ready = false;
241  {
243 
244  // Empty track list & free old graphs
245  clear_tracklist();
246 
247  // first we gather the FFTResults of all tracks
248 
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());
252 
253  Selection& s (PublicEditor::instance().get_selection());
254 
255 
256  // if timeSelection
257  if (source_selection_ranges_rb.get_active()) {
258  TimeSelection ts = s.time;
259 
260  for (TrackSelection::iterator i = s.tracks.begin(); i != s.tracks.end(); ++i) {
262  = boost::dynamic_pointer_cast<AudioPlaylist>((*i)->playlist());
263 
264  if (!pl)
265  continue;
266 
267  RouteUI *rui = dynamic_cast<RouteUI *>(*i);
268  int n_inputs = rui->route()->n_inputs().n_audio(); // FFT is audio only
269 
270  // Busses don't have playlists, so we need to check that we actually are working with a playlist
271  if (!pl || !rui)
272  continue;
273 
274  // std::cerr << "Analyzing ranges on track " << rui->route()->name() << std::endl;
275 
276  FFTResult *res = fft_graph.prepareResult(rui->color(), rui->route()->name());
277  for (std::list<AudioRange>::iterator j = ts.begin(); j != ts.end(); ++j) {
278 
279  int n;
280  for (int channel = 0; channel < n_inputs; channel++) {
281  framecnt_t x = 0;
282 
283  while (x < j->length()) {
284  // TODO: What about stereo+ channels? composite all to one, I guess
285 
286  n = fft_graph.windowSize();
287 
288  if (x + n >= (*j).length() ) {
289  n = (*j).length() - x;
290  }
291 
292  n = pl->read(buf, mixbuf, gain, (*j).start + x, n, channel);
293 
294  if ( n < fft_graph.windowSize()) {
295  for (int j = n; j < fft_graph.windowSize(); j++) {
296  buf[j] = 0.0;
297  }
298  }
299 
300  res->analyzeWindow(buf);
301 
302  x += n;
303  }
304  }
305  }
306  res->finalize();
307 
308  Gtk::TreeModel::Row newrow = *(tlmodel)->append();
309  newrow[tlcols.trackname] = rui->route()->name();
310  newrow[tlcols.visible] = true;
311  newrow[tlcols.color] = rui->color();
312  newrow[tlcols.graph] = res;
313  }
314  } else if (source_selection_regions_rb.get_active()) {
315  RegionSelection ars = s.regions;
316  // std::cerr << "Analyzing selected regions" << std::endl;
317 
318  for (RegionSelection::iterator j = ars.begin(); j != ars.end(); ++j) {
319  // Check that the region is actually audio (so we can analyze it)
320  AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*j);
321  if (!arv)
322  continue;
323 
324  // std::cerr << " - " << (*j)->region().name() << ": " << (*j)->region().length() << " samples starting at " << (*j)->region().position() << std::endl;
325  RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(&arv->get_time_axis_view());
326  if (!rtav) {
327  /* shouldn't happen... */
328  continue;
329  }
330  FFTResult *res = fft_graph.prepareResult(rtav->color(), arv->get_item_name());
331  int n;
332  for (unsigned int channel = 0; channel < arv->region()->n_channels(); channel++) {
333 
334  framecnt_t x = 0;
335  framecnt_t length = arv->region()->length();
336 
337  while (x < length) {
338  // TODO: What about stereo+ channels? composite all to one, I guess
339 
340  n = fft_graph.windowSize();
341  if (x + n >= length ) {
342  n = length - x;
343  }
344 
345  memset (buf, 0, n * sizeof (Sample));
346  n = arv->audio_region()->read_at(buf, mixbuf, gain, arv->region()->position() + x, n, channel);
347 
348  if (n == 0)
349  break;
350 
351  if ( n < fft_graph.windowSize()) {
352  for (int j = n; j < fft_graph.windowSize(); j++) {
353  buf[j] = 0.0;
354  }
355  }
356 
357  res->analyzeWindow(buf);
358  x += n;
359  }
360  }
361  // std::cerr << "Found: " << (*j)->get_item_name() << std::endl;
362  res->finalize();
363 
364  Gtk::TreeModel::Row newrow = *(tlmodel)->append();
365  newrow[tlcols.trackname] = arv->get_item_name();
366  newrow[tlcols.visible] = true;
367  newrow[tlcols.color] = rtav->color();
368  newrow[tlcols.graph] = res;
369 
370  }
371 
372  }
373 
374 
375  free(buf);
376  free(mixbuf);
377  free(gain);
378 
379  track_list_ready = true;
380  } /* end lock */
381 
382  fft_graph.redraw();
383 }
384 
385 void
387 {
388  // We are only interested in activation signals, not deactivation signals
389  if (!button->get_active())
390  return;
391 
392  /*
393  cerr << "AnalysisWindow: signal source = ";
394 
395  if (button == &source_selection_ranges_rb) {
396  cerr << "selected ranges" << endl;
397 
398  } else if (button == &source_selection_regions_rb) {
399  cerr << "selected regions" << endl;
400 
401  } else {
402  cerr << "unknown?" << endl;
403  }
404  */
405 }
406 
407 void
408 AnalysisWindow::display_model_changed (Gtk::RadioButton *button)
409 {
410  // We are only interested in activation signals, not deactivation signals
411  if (!button->get_active())
412  return;
413 
414  /*
415  cerr << "AnalysisWindow: display model = ";
416 
417  if (button == &display_model_composite_separate_rb) {
418  cerr << "separate composites of tracks" << endl;
419  } else if (button == &display_model_composite_all_tracks_rb) {
420  cerr << "composite of all tracks" << endl;
421  } else {
422  cerr << "unknown?" << endl;
423  }
424  */
425 }
426 
427 
framecnt_t read(Sample *dst, Sample *mixdown, float *gain_buffer, framepos_t start, framecnt_t cnt, uint32_t chan_n=0)
Gdk::Color color() const
Definition: route_ui.cc:2138
TimeAxisView & get_time_axis_view() const
Gtk::TreeView track_list
void finalize()
Definition: fft_result.cc:91
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
Gtk::RadioButton source_selection_regions_rb
Lists of selected things.
Definition: selection.h:66
void show_normalized_changed()
uint32_t n_audio() const
Definition: chan_count.h:63
Gtk::TreeModelColumn< Gdk::Color > color
LIBARDOUR_API PBD::PropertyDescriptor< bool > gain
Definition: route_group.cc:44
virtual std::string get_item_name() const
boost::shared_ptr< ARDOUR::AudioRegion > audio_region() const
Gtk::Label source_selection_label
TrackListColumns tlcols
#define _(Text)
Definition: i18n.h:11
Gtk::Button refresh_button
Gtk::TreeModelColumn< bool > visible
int64_t framecnt_t
Definition: types.h:76
void set_show_normalized(bool v)
Definition: fft_graph.h:59
float Sample
Definition: types.h:54
boost::shared_ptr< ARDOUR::Region > region() const
Definition: region_view.h:66
Gtk::RadioButton source_selection_ranges_rb
Gtk::TreeModelColumn< std::string > trackname
Definition: amp.h:29
void set_analysis_window(AnalysisWindow *a_window)
Definition: fft_graph.cc:164
RegionSelection regions
Definition: selection.h:82
Glib::RefPtr< Gtk::ListStore > tlmodel
void display_model_changed(Gtk::RadioButton *)
ChanCount n_inputs() const
Definition: route.h:92
FFTResult * prepareResult(Gdk::Color color, std::string trackname)
Definition: fft_graph.cc:155
void redraw()
Definition: fft_graph.cc:274
framepos_t position() const
Definition: region.h:112
Gtk::TreeModelColumn< FFTResult * > graph
void show_minmax_changed()
void analyzeWindow(float *window)
Definition: fft_result.cc:57
int windowSize() const
Definition: fft_graph.h:48
Gtk::HSeparator hseparator1
static PublicEditor & instance()
Gtk::RadioButton display_model_composite_all_tracks_rb
Gtk::RadioButton display_model_composite_separate_rb
Glib::Threads::Mutex track_list_lock
void analyze_data(Gtk::Button *)
uint32_t n_channels() const
Definition: region.h:259
std::string name() const
TimeSelection time
Definition: selection.h:83
Gtk::CheckButton show_normalized_button
TrackSelection tracks
Definition: selection.h:81
Definition: debug.h:30
Gtk::Label display_model_label
virtual framecnt_t read_at(Sample *buf, Sample *mixdown_buf, float *gain_buf, framepos_t position, framecnt_t cnt, uint32_t chan_n=0) const
Definition: audioregion.cc:473
framecnt_t length() const
Definition: region.h:114
void source_selection_changed(Gtk::RadioButton *)
void set_show_minmax(bool v)
Definition: fft_graph.h:58
boost::shared_ptr< ARDOUR::Route > route() const
Definition: route_ui.h:76
Gtk::CheckButton show_minmax_button
LIBARDOUR_API PBD::PropertyDescriptor< framecnt_t > length
Definition: region.cc:64
void track_list_row_changed(const Gtk::TreeModel::Path &path, const Gtk::TreeModel::iterator &iter)