the return of VST support
[ardour.git] / gtk2_ardour / analysis_window.cc
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     $Id$
20 */
21
22 #include <gtkmm2ext/gtk_ui.h>
23 #include <gtkmm/stock.h>
24 #include <gtkmm/label.h>
25 #include <gtkmm/treemodel.h>
26 #include <gtkmm/treeiter.h>
27
28 #include <ardour/audioregion.h>
29 #include <ardour/playlist.h>
30 #include <ardour/types.h>
31
32 #include "analysis_window.h"
33
34 #include "route_ui.h"
35 #include "time_axis_view.h"
36 #include "public_editor.h"
37 #include "selection.h"
38 #include "regionview.h"
39
40 #include "i18n.h"
41
42 using namespace ARDOUR;
43 using namespace PBD;
44
45 AnalysisWindow::AnalysisWindow()
46         : ArdourDialog(_("analysis window")),
47         
48           fft_graph (2048),
49         
50           source_selection_label       (_("Signal source")),
51           source_selection_ranges_rb   (_("Selected ranges")),
52           source_selection_regions_rb  (_("Selected regions")),
53           
54           display_model_label                   (_("Display model")),
55           display_model_composite_separate_rb   (_("Composite graphs for each track")),
56           display_model_composite_all_tracks_rb (_("Composite graph of all tracks"))
57
58 {
59         track_list_ready = false;
60         
61         // Left side: track list + controls
62         tlmodel = Gtk::ListStore::create(tlcols);
63         track_list.set_model (tlmodel);
64         track_list.append_column(_("Track"), tlcols.trackname);
65         track_list.append_column_editable(_("Visible"), tlcols.visible);
66         track_list.set_headers_visible(true);
67         track_list.set_reorderable(false);
68         track_list.get_selection()->set_mode (Gtk::SELECTION_NONE);
69
70
71         Gtk::TreeViewColumn* track_col = track_list.get_column(0);
72         Gtk::CellRendererText* renderer = dynamic_cast<Gtk::CellRendererText*>(track_list.get_column_cell_renderer (0));
73         
74         track_col->add_attribute(renderer->property_foreground_gdk(), tlcols.color);
75         track_col->set_expand(true);
76
77
78         tlmodel->signal_row_changed().connect (
79                         mem_fun(*this, &AnalysisWindow::track_list_row_changed) );
80         
81         fft_graph.set_analysis_window(this);
82                 
83         vbox.pack_start(track_list);
84
85
86         // "Signal source"
87         vbox.pack_start(source_selection_label, false, false);
88
89         {
90                 Gtk::RadioButtonGroup group = source_selection_ranges_rb.get_group();
91                 source_selection_regions_rb.set_group(group);
92
93                 source_selection_ranges_rb.set_active();
94                 
95                 vbox.pack_start (source_selection_ranges_rb,  false, false);
96                 vbox.pack_start (source_selection_regions_rb, false, false);
97                 
98                 // "Selected ranges" radio
99                 source_selection_ranges_rb.signal_toggled().connect (
100                                 bind ( mem_fun(*this, &AnalysisWindow::source_selection_changed), &source_selection_ranges_rb));
101
102                 // "Selected regions" radio
103                 source_selection_regions_rb.signal_toggled().connect (
104                                 bind ( mem_fun(*this, &AnalysisWindow::source_selection_changed), &source_selection_regions_rb));
105         }
106         
107         vbox.pack_start(hseparator1, false, false);
108         
109         // "Display model"
110         vbox.pack_start(display_model_label, false, false);
111         {
112                 Gtk::RadioButtonGroup group = display_model_composite_separate_rb.get_group();
113                 display_model_composite_all_tracks_rb.set_group (group);
114                 
115                 display_model_composite_separate_rb.set_active();
116                 
117                 vbox.pack_start (display_model_composite_separate_rb,   false, false);
118                 vbox.pack_start (display_model_composite_all_tracks_rb, false, false);
119
120                 // "Composite graphs for all tracks"
121                 display_model_composite_separate_rb.signal_toggled().connect (
122                                 bind ( mem_fun(*this, &AnalysisWindow::display_model_changed), &display_model_composite_separate_rb));
123                 
124                 // "Composite graph of all tracks"
125                 display_model_composite_all_tracks_rb.signal_toggled().connect (
126                                 bind ( mem_fun(*this, &AnalysisWindow::display_model_changed), &display_model_composite_all_tracks_rb));
127         }
128
129         vbox.pack_start(hseparator2, false, false);
130
131         refresh_button.set_name("EditorGTKButton");
132         refresh_button.set_label(_("Analyze data"));
133
134         refresh_button.signal_clicked().connect ( bind ( mem_fun(*this, &AnalysisWindow::analyze_data), &refresh_button)); 
135
136         vbox.pack_start(refresh_button, false, false, 10);
137         
138         
139         hbox.pack_start(vbox);
140         
141         // Analysis window on the right
142         fft_graph.ensure_style();
143
144         hbox.add(fft_graph);
145         
146         
147
148         // And last we pack the hbox
149         get_vbox()->pack_start(hbox);
150
151         track_list.show_all();
152
153         get_vbox()->show_all();
154 }
155
156 AnalysisWindow::~AnalysisWindow()
157 {
158
159 }
160
161 void
162 AnalysisWindow::set_rangemode()
163 {
164         source_selection_ranges_rb.set_active(true);
165 }
166
167 void
168 AnalysisWindow::set_regionmode()
169 {
170         source_selection_regions_rb.set_active(true);
171 }
172
173 void 
174 AnalysisWindow::track_list_row_changed(const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter)
175 {
176         if (track_list_ready) {
177                 fft_graph.redraw();
178         }
179 }
180
181
182 void
183 AnalysisWindow::clear_tracklist()
184 {
185         // Empty track list & free old graphs
186         Gtk::TreeNodeChildren children = track_list.get_model()->children();
187         
188         for (Gtk::TreeIter i = children.begin(); i != children.end(); i++) {
189                 Gtk::TreeModel::Row row = *i;
190
191                 FFTResult *delete_me = row[tlcols.graph];
192                 if (delete_me == 0)
193                         continue;
194
195                 // Make sure it's not drawn
196                 row[tlcols.graph] = 0;
197                 
198                 delete delete_me;
199         }
200                 
201         tlmodel->clear();
202 }
203
204 void
205 AnalysisWindow::analyze()
206 {
207         analyze_data(&refresh_button);
208 }
209
210 void
211 AnalysisWindow::analyze_data (Gtk::Button *button)
212 {
213         track_list_ready = false;
214         {
215                 Glib::Mutex::Lock lm  (track_list_lock);
216
217                 // Empty track list & free old graphs
218                 clear_tracklist();
219         
220                 // first we gather the FFTResults of all tracks
221         
222                 Sample *buf    = (Sample *) malloc(sizeof(Sample) * fft_graph.windowSize());
223                 Sample *mixbuf = (Sample *) malloc(sizeof(Sample) * fft_graph.windowSize());
224                 float  *gain   = (float *)  malloc(sizeof(float) * fft_graph.windowSize());
225                 char   *work   = (char *)   malloc(sizeof(char) * fft_graph.windowSize());
226         
227                 Selection s = PublicEditor::instance().get_selection();
228                 TimeSelection ts = s.time;
229                 AudioRegionSelection ars = s.audio_regions;
230         
231         
232                 for (TrackSelection::iterator i = s.tracks.begin(); i != s.tracks.end(); ++i) {
233                         ARDOUR::Playlist *pl = (*i)->playlist();
234                         RouteUI *rui = dynamic_cast<RouteUI *>(*i);
235                         
236                         // Busses don't have playlists, so we need to check that we actually are working with a playlist
237                         if (!pl || !rui)
238                                 continue;
239
240                         FFTResult *res = fft_graph.prepareResult(*&rui->color(), *&rui->route().name());
241                 
242                         // if timeSelection
243                         if (source_selection_ranges_rb.get_active()) {
244 //                              cerr << "Analyzing ranges on track " << *&rui->route().name() << endl;
245                                 
246                                 for (std::list<ARDOUR::AudioRange>::iterator j = ts.begin(); j != ts.end(); ++j) {
247
248                                         jack_nframes_t i = 0;
249                                         int n;
250                         
251                                         while ( i < (*j).length() ) {
252                                                 // TODO: What about stereo+ channels? composite all to one, I guess
253
254                                                 n = fft_graph.windowSize();
255
256                                                 if (i + n >= (*j).length() ) {
257                                                         n = (*j).length() - i;
258                                                 }
259                                 
260                                                 n = pl->read(buf, mixbuf, gain, work, (*j).start + i, n);
261         
262                                                 if ( n < fft_graph.windowSize()) {
263                                                         for (int j = n; j < fft_graph.windowSize(); j++) {
264                                                                 buf[j] = 0.0;
265                                                         }
266                                                 }
267         
268                                                 res->analyzeWindow(buf);
269                                 
270                                                 i += n;
271                                         }
272                                 }
273                         } else if (source_selection_regions_rb.get_active()) {
274 //                              cerr << "Analyzing selected regions on track " << *&rui->route().name() << endl;
275                                 
276                                 TimeAxisView *current_axis = (*i);
277                                 
278                                 for (std::set<AudioRegionView *>::iterator j = ars.begin(); j != ars.end(); ++j) {
279                                         // Check that the region really is selected on _this_ track/solo
280                                         if ( &(*j)->get_time_axis_view() != current_axis)
281                                                 continue;
282
283 //                                      cerr << " - " << (*j)->region.name() << ": " << (*j)->region.length() << " samples starting at " << (*j)->region.position() << endl;
284                                         jack_nframes_t i = 0;
285                                         int n;
286
287                                         while ( i < (*j)->region.length() ) {
288                                                 // TODO: What about stereo+ channels? composite all to one, I guess
289
290                                                 n = fft_graph.windowSize();
291                                                 if (i + n >= (*j)->region.length() ) {
292                                                         n = (*j)->region.length() - i;
293                                                 }
294                                 
295                                                 n = (*j)->region.read_at(buf, mixbuf, gain, work, (*j)->region.position() + i, n);
296         
297                                                 if ( n < fft_graph.windowSize()) {
298                                                         for (int j = n; j < fft_graph.windowSize(); j++) {
299                                                                 buf[j] = 0.0;
300                                                         }
301                                                 }
302         
303                                                 res->analyzeWindow(buf);
304                                 
305                                                 i += n;
306                                         }
307 //                                      cerr << "Found: " << (*j)->get_item_name() << endl;
308
309                                 }
310
311                         }
312                         res->finalize();
313
314                                 
315                         Gtk::TreeModel::Row newrow = *(tlmodel)->append();
316                         newrow[tlcols.trackname]   = rui->route().name();
317                         newrow[tlcols.visible]     = true;
318                         newrow[tlcols.color]       = *&rui->color();
319                         newrow[tlcols.graph]       = res;
320                 }       
321
322         
323                 free(buf);
324                 free(mixbuf);
325                 free(work);
326
327                 track_list_ready = true;
328         } /* end lock */
329         
330         fft_graph.redraw();
331 }
332
333 void
334 AnalysisWindow::source_selection_changed (Gtk::RadioButton *button)
335 {
336         // We are only interested in activation signals, not deactivation signals
337         if (!button->get_active())
338                 return;
339
340         /*
341         cerr << "AnalysisWindow: signal source = ";
342         
343         if (button == &source_selection_ranges_rb) {
344                 cerr << "selected ranges" << endl;
345                 
346         } else if (button == &source_selection_regions_rb) {
347                 cerr << "selected regions" << endl;
348                 
349         } else {
350                 cerr << "unknown?" << endl;
351         }
352         */
353 }
354
355 void
356 AnalysisWindow::display_model_changed (Gtk::RadioButton *button)
357 {
358         // We are only interested in activation signals, not deactivation signals
359         if (!button->get_active())
360                 return;
361
362         /*
363         cerr << "AnalysisWindow: display model = ";
364         
365         if (button == &display_model_composite_separate_rb) {
366                 cerr << "separate composites of tracks" << endl;
367         } else if (button == &display_model_composite_all_tracks_rb) {
368                 cerr << "composite of all tracks" << endl;
369         } else {
370                 cerr << "unknown?" << endl;
371         }
372         */
373 }
374           
375