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"
34 #include "time_axis_view.h"
35 #include "public_editor.h"
36 #include "selection.h"
37 #include "audio_region_view.h"
41 using namespace ARDOUR;
44 AnalysisWindow::AnalysisWindow()
45 : ArdourDialog(_("analysis window")),
47 source_selection_label (_("Signal source")),
48 source_selection_ranges_rb (_("Selected ranges")),
49 source_selection_regions_rb (_("Selected regions")),
51 display_model_label (_("Display model")),
52 display_model_composite_separate_rb (_("Composite graphs for each track")),
53 display_model_composite_all_tracks_rb (_("Composite graph of all tracks")),
57 track_list_ready = false;
59 // Left side: track list + controls
60 tlmodel = Gtk::ListStore::create(tlcols);
61 track_list.set_model (tlmodel);
62 track_list.append_column(_("Track"), tlcols.trackname);
63 track_list.append_column_editable(_("Show"), tlcols.visible);
64 track_list.set_headers_visible(true);
65 track_list.set_reorderable(false);
66 track_list.get_selection()->set_mode (Gtk::SELECTION_NONE);
69 Gtk::TreeViewColumn* track_col = track_list.get_column(0);
70 Gtk::CellRendererText* renderer = dynamic_cast<Gtk::CellRendererText*>(track_list.get_column_cell_renderer (0));
72 track_col->add_attribute(renderer->property_foreground_gdk(), tlcols.color);
73 track_col->set_expand(true);
76 tlmodel->signal_row_changed().connect (
77 mem_fun(*this, &AnalysisWindow::track_list_row_changed) );
79 fft_graph.set_analysis_window(this);
81 vbox.pack_start(track_list);
85 vbox.pack_start(source_selection_label, false, false);
88 Gtk::RadioButtonGroup group = source_selection_ranges_rb.get_group();
89 source_selection_regions_rb.set_group(group);
91 source_selection_ranges_rb.set_active();
93 vbox.pack_start (source_selection_ranges_rb, false, false);
94 vbox.pack_start (source_selection_regions_rb, false, false);
96 // "Selected ranges" radio
97 source_selection_ranges_rb.signal_toggled().connect (
98 bind ( mem_fun(*this, &AnalysisWindow::source_selection_changed), &source_selection_ranges_rb));
100 // "Selected regions" radio
101 source_selection_regions_rb.signal_toggled().connect (
102 bind ( mem_fun(*this, &AnalysisWindow::source_selection_changed), &source_selection_regions_rb));
105 vbox.pack_start(hseparator1, false, false);
108 vbox.pack_start(display_model_label, false, false);
110 Gtk::RadioButtonGroup group = display_model_composite_separate_rb.get_group();
111 display_model_composite_all_tracks_rb.set_group (group);
113 display_model_composite_separate_rb.set_active();
115 vbox.pack_start (display_model_composite_separate_rb, false, false);
116 vbox.pack_start (display_model_composite_all_tracks_rb, false, false);
118 // "Composite graphs for all tracks"
119 display_model_composite_separate_rb.signal_toggled().connect (
120 bind ( mem_fun(*this, &AnalysisWindow::display_model_changed), &display_model_composite_separate_rb));
122 // "Composite graph of all tracks"
123 display_model_composite_all_tracks_rb.signal_toggled().connect (
124 bind ( mem_fun(*this, &AnalysisWindow::display_model_changed), &display_model_composite_all_tracks_rb));
127 vbox.pack_start(hseparator2, false, false);
129 refresh_button.set_name("EditorGTKButton");
130 refresh_button.set_label(_("Analyze data"));
132 refresh_button.signal_clicked().connect ( bind ( mem_fun(*this, &AnalysisWindow::analyze_data), &refresh_button));
134 vbox.pack_start(refresh_button, false, false, 10);
137 hbox.pack_start(vbox);
139 // Analysis window on the right
140 fft_graph.ensure_style();
146 // And last we pack the hbox
147 get_vbox()->pack_start(hbox);
149 track_list.show_all();
154 source_selection_label.show();
155 source_selection_ranges_rb.show();
156 source_selection_regions_rb.show();
158 display_model_label.show();
159 display_model_composite_separate_rb.show();
160 display_model_composite_all_tracks_rb.show();
162 refresh_button.show();
164 //get_vbox()->show();
167 AnalysisWindow::~AnalysisWindow()
173 AnalysisWindow::set_rangemode()
175 source_selection_ranges_rb.set_active(true);
179 AnalysisWindow::set_regionmode()
181 source_selection_regions_rb.set_active(true);
185 AnalysisWindow::track_list_row_changed(const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter)
187 if (track_list_ready) {
194 AnalysisWindow::clear_tracklist()
196 // Empty track list & free old graphs
197 Gtk::TreeNodeChildren children = track_list.get_model()->children();
199 for (Gtk::TreeIter i = children.begin(); i != children.end(); i++) {
200 Gtk::TreeModel::Row row = *i;
202 FFTResult *delete_me = row[tlcols.graph];
206 // Make sure it's not drawn
207 row[tlcols.graph] = 0;
216 AnalysisWindow::analyze()
218 analyze_data(&refresh_button);
222 AnalysisWindow::analyze_data (Gtk::Button *button)
224 track_list_ready = false;
226 Glib::Mutex::Lock lm (track_list_lock);
228 // Empty track list & free old graphs
231 // first we gather the FFTResults of all tracks
233 Sample *buf = (Sample *) malloc(sizeof(Sample) * fft_graph.windowSize());
234 Sample *mixbuf = (Sample *) malloc(sizeof(Sample) * fft_graph.windowSize());
235 float *gain = (float *) malloc(sizeof(float) * fft_graph.windowSize());
237 Selection s = PublicEditor::instance().get_selection();
238 TimeSelection ts = s.time;
239 RegionSelection ars = s.regions;
241 for (TrackSelection::iterator i = s.tracks.begin(); i != s.tracks.end(); ++i) {
242 boost::shared_ptr<AudioPlaylist> pl
243 = boost::dynamic_pointer_cast<AudioPlaylist>((*i)->playlist());
248 RouteUI *rui = dynamic_cast<RouteUI *>(*i);
250 // Busses don't have playlists, so we need to check that we actually are working with a playlist
254 FFTResult *res = fft_graph.prepareResult(rui->color(), rui->route()->name());
257 if (source_selection_ranges_rb.get_active()) {
258 // cerr << "Analyzing ranges on track " << *&rui->route().name() << endl;
260 for (std::list<AudioRange>::iterator j = ts.begin(); j != ts.end(); ++j) {
265 while ( i < (*j).length() ) {
266 // TODO: What about stereo+ channels? composite all to one, I guess
268 n = fft_graph.windowSize();
270 if (i + n >= (*j).length() ) {
271 n = (*j).length() - i;
274 n = pl->read(buf, mixbuf, gain, (*j).start + i, n);
276 if ( n < fft_graph.windowSize()) {
277 for (int j = n; j < fft_graph.windowSize(); j++) {
282 res->analyzeWindow(buf);
287 } else if (source_selection_regions_rb.get_active()) {
288 // cerr << "Analyzing selected regions on track " << *&rui->route().name() << endl;
290 TimeAxisView *current_axis = (*i);
292 for (RegionSelection::iterator j = ars.begin(); j != ars.end(); ++j) {
293 // Check that the region is actually audio (so we can analyze it)
294 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*j);
298 // Check that the region really is selected on _this_ track/solo
299 if ( &arv->get_time_axis_view() != current_axis)
302 // cerr << " - " << (*j)->region().name() << ": " << (*j)->region().length() << " samples starting at " << (*j)->region().position() << endl;
306 while ( i < arv->region()->length() ) {
307 // TODO: What about stereo+ channels? composite all to one, I guess
309 n = fft_graph.windowSize();
310 if (i + n >= arv->region()->length() ) {
311 n = arv->region()->length() - i;
314 n = arv->audio_region()->read_at(buf, mixbuf, gain, arv->region()->position() + i, n);
316 if ( n < fft_graph.windowSize()) {
317 for (int j = n; j < fft_graph.windowSize(); j++) {
322 res->analyzeWindow(buf);
326 // cerr << "Found: " << (*j)->get_item_name() << endl;
334 Gtk::TreeModel::Row newrow = *(tlmodel)->append();
335 newrow[tlcols.trackname] = rui->route()->name();
336 newrow[tlcols.visible] = true;
337 newrow[tlcols.color] = rui->color();
338 newrow[tlcols.graph] = res;
345 track_list_ready = true;
352 AnalysisWindow::source_selection_changed (Gtk::RadioButton *button)
354 // We are only interested in activation signals, not deactivation signals
355 if (!button->get_active())
359 cerr << "AnalysisWindow: signal source = ";
361 if (button == &source_selection_ranges_rb) {
362 cerr << "selected ranges" << endl;
364 } else if (button == &source_selection_regions_rb) {
365 cerr << "selected regions" << endl;
368 cerr << "unknown?" << endl;
374 AnalysisWindow::display_model_changed (Gtk::RadioButton *button)
376 // We are only interested in activation signals, not deactivation signals
377 if (!button->get_active())
381 cerr << "AnalysisWindow: display model = ";
383 if (button == &display_model_composite_separate_rb) {
384 cerr << "separate composites of tracks" << endl;
385 } else if (button == &display_model_composite_all_tracks_rb) {
386 cerr << "composite of all tracks" << endl;
388 cerr << "unknown?" << endl;