Chris@0
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 Sonic Visualiser
|
Chris@0
|
5 An audio file viewer and annotation editor.
|
Chris@0
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@77
|
7 This file copyright 2006 Chris Cannam and QMUL.
|
Chris@0
|
8
|
Chris@0
|
9 This program is free software; you can redistribute it and/or
|
Chris@0
|
10 modify it under the terms of the GNU General Public License as
|
Chris@0
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@0
|
12 License, or (at your option) any later version. See the file
|
Chris@0
|
13 COPYING included with this distribution for more information.
|
Chris@0
|
14 */
|
Chris@0
|
15
|
Chris@0
|
16 #include "MainWindow.h"
|
Chris@0
|
17
|
Chris@1
|
18 #include "system/System.h"
|
Chris@1
|
19 #include "system/Init.h"
|
Chris@0
|
20 #include "base/TempDirectory.h"
|
Chris@0
|
21 #include "base/PropertyContainer.h"
|
Chris@0
|
22 #include "base/Preferences.h"
|
Chris@120
|
23 #include "widgets/TipDialog.h"
|
Chris@315
|
24 #include "transform/TransformFactory.h"
|
Chris@0
|
25
|
Chris@0
|
26 #include <QMetaType>
|
Chris@0
|
27 #include <QApplication>
|
Chris@0
|
28 #include <QDesktopWidget>
|
Chris@0
|
29 #include <QMessageBox>
|
Chris@0
|
30 #include <QTranslator>
|
Chris@0
|
31 #include <QLocale>
|
Chris@5
|
32 #include <QSettings>
|
Chris@7
|
33 #include <QIcon>
|
Chris@11
|
34 #include <QSessionManager>
|
Chris@165
|
35 #include <QDir>
|
Chris@231
|
36 #include <QSplashScreen>
|
Chris@252
|
37 #include <QTimer>
|
Chris@331
|
38 #include <QPainter>
|
Chris@331
|
39
|
Chris@331
|
40 #include "../version.h"
|
Chris@0
|
41
|
Chris@0
|
42 #include <iostream>
|
Chris@0
|
43 #include <signal.h>
|
Chris@0
|
44
|
Chris@215
|
45 #ifdef HAVE_FFTW3F
|
Chris@215
|
46 #include <fftw3.h>
|
Chris@215
|
47 #endif
|
Chris@215
|
48
|
Chris@127
|
49 /*! \mainpage Sonic Visualiser
|
Chris@127
|
50
|
Chris@127
|
51 \section interesting Summary of interesting classes
|
Chris@127
|
52
|
Chris@127
|
53 - Data models: Model and subclasses, e.g. WaveFileModel
|
Chris@127
|
54
|
Chris@127
|
55 - Graphical layers: Layer and subclasses, displayed on View and its
|
Chris@127
|
56 subclass widgets.
|
Chris@127
|
57
|
Chris@127
|
58 - Main window class, document class, and file parser: MainWindow,
|
Chris@127
|
59 Document, SVFileReader
|
Chris@127
|
60
|
Chris@127
|
61 - Turning one model (e.g. audio) into another (e.g. more audio, or a
|
Chris@244
|
62 curve extracted from it): Transform, encapsulating the data that need
|
Chris@244
|
63 to be stored to be able to reproduce a given transformation;
|
Chris@244
|
64 TransformFactory, for discovering the available types of transform;
|
Chris@244
|
65 ModelTransformerFactory, ModelTransformer and subclasses, providing
|
Chris@244
|
66 the mechanisms for applying transforms to data models
|
Chris@127
|
67
|
Chris@127
|
68 - Creating the plugins used by transforms: RealTimePluginFactory,
|
Chris@129
|
69 FeatureExtractionPluginFactory. See also the API documentation for
|
Chris@129
|
70 Vamp feature extraction plugins at
|
Chris@129
|
71 http://www.vamp-plugins.org/code-doc/.
|
Chris@127
|
72
|
Chris@127
|
73 - File reading and writing code: AudioFileReader and subclasses,
|
Chris@127
|
74 WavFileWriter, DataFileReader, SVFileReader
|
Chris@127
|
75
|
Chris@127
|
76 - FFT calculation and cacheing: FFTModel, FFTDataServer
|
Chris@127
|
77
|
Chris@127
|
78 - Widgets that show groups of editable properties: PropertyBox for
|
Chris@127
|
79 layer properties (contained in a PropertyStack), PluginParameterBox
|
Chris@127
|
80 for plugins (contained in a PluginParameterDialog)
|
Chris@127
|
81
|
Chris@127
|
82 - Audio playback: AudioCallbackPlaySource and subclasses,
|
Chris@127
|
83 AudioCallbackPlayTarget and subclasses, AudioGenerator
|
Chris@127
|
84
|
Chris@127
|
85 \section model Data sources: the Model hierarchy
|
Chris@127
|
86
|
Chris@127
|
87 A Model is something containing, or knowing how to obtain, data.
|
Chris@127
|
88
|
Chris@127
|
89 For example, WaveFileModel is a model that knows how to get data
|
Chris@127
|
90 from an audio file; SparseTimeValueModel is a model containing
|
Chris@127
|
91 editable "curve" data.
|
Chris@127
|
92
|
Chris@127
|
93 Models typically subclass one of a number of abstract subclasses of
|
Chris@127
|
94 Model. For example, WaveFileModel subclasses DenseTimeValueModel,
|
Chris@127
|
95 which describes an interface for models that have a value at each
|
Chris@127
|
96 time point for a given sampling resolution. (Note that
|
Chris@127
|
97 WaveFileModel does not actually read the files itself: it uses
|
Chris@127
|
98 AudioFileReader classes for that. It just makes data from the
|
Chris@127
|
99 files available in a Model.) SparseTimeValueModel uses the
|
Chris@127
|
100 SparseModel template class, which provides most of the
|
Chris@127
|
101 implementation for models that contain a series of points of some
|
Chris@127
|
102 sort -- also used by NoteModel, TextModel, and
|
Chris@127
|
103 SparseOneDimensionalModel.
|
Chris@127
|
104
|
Chris@127
|
105 Everything that goes on the screen originates from a model, via a
|
Chris@127
|
106 layer (see below). The models are contained in a Document object.
|
Chris@127
|
107 There is no containment hierarchy or ordering of models in the
|
Chris@127
|
108 document. One model is the main model, which defines the sample
|
Chris@127
|
109 rate for playback.
|
Chris@127
|
110
|
Chris@127
|
111 A model may also be marked as a "derived" model, which means it was
|
Chris@127
|
112 generated from another model using some transform (feature
|
Chris@127
|
113 extraction or effect plugin, etc) -- the idea being that they can
|
Chris@127
|
114 be re-generated using the same transform if a new source model is
|
Chris@127
|
115 loaded.
|
Chris@127
|
116
|
Chris@127
|
117 \section layer Things that can display data: the Layer hierarchy
|
Chris@127
|
118
|
Chris@127
|
119 A Layer is something that knows how to draw parts of a model onto a
|
Chris@127
|
120 timeline.
|
Chris@127
|
121
|
Chris@127
|
122 For example, WaveformLayer is a layer which draws waveforms, based
|
Chris@127
|
123 on WaveFileModel; TimeValueLayer draws curves, based on
|
Chris@127
|
124 SparseTimeValueModel; SpectrogramLayer draws spectrograms, based on
|
Chris@127
|
125 WaveFileModel (via FFTModel).
|
Chris@127
|
126
|
Chris@127
|
127 The most basic functions of a layer are: to draw itself onto a
|
Chris@127
|
128 Pane, against a timeline on the x axis; and to permit user
|
Chris@127
|
129 interaction. If you were thinking of adding the capability to
|
Chris@127
|
130 display a new sort of something, then you would want to add a new
|
Chris@127
|
131 layer type. (You may also need a new model type, depending on
|
Chris@127
|
132 whether any existing model can capture the data you need.)
|
Chris@127
|
133 Depending on the sort of data in question, there are various
|
Chris@127
|
134 existing layers that might be appropriate to start from -- for
|
Chris@127
|
135 example, a layer that displays images that the user has imported
|
Chris@127
|
136 and associated with particular times might have something in common
|
Chris@127
|
137 with the existing TextLayer which displays pieces of text that are
|
Chris@127
|
138 associated with particular times.
|
Chris@127
|
139
|
Chris@127
|
140 Although layers are visual objects, they are contained in the
|
Chris@127
|
141 Document in Sonic Visualiser rather than being managed together
|
Chris@127
|
142 with display widgets. The Sonic Visualiser file format has
|
Chris@127
|
143 separate data and layout sections, and the layers are defined in
|
Chris@127
|
144 the data section and then referred to in the layout section which
|
Chris@127
|
145 determines which layers may go on which panes (see Pane below).
|
Chris@127
|
146
|
Chris@127
|
147 Once a layer class is defined, some basic data about it needs to be
|
Chris@127
|
148 set up in the LayerFactory class, and then it will appear in the
|
Chris@127
|
149 menus and so on on the main window.
|
Chris@127
|
150
|
Chris@127
|
151 \section view Widgets that are used to show layers: The View hierarchy
|
Chris@127
|
152
|
Chris@127
|
153 A View is a widget that displays a stack of layers. The most
|
Chris@127
|
154 important subclass is Pane, the widget that is used to show most of
|
Chris@127
|
155 the data in the main window of Sonic Visualiser.
|
Chris@127
|
156
|
Chris@127
|
157 All a pane really does is contain a set of layers and get them to
|
Chris@127
|
158 render themselves (one on top of the other, with the topmost layer
|
Chris@127
|
159 being the one that is currently interacted with), cache the
|
Chris@127
|
160 results, negotiate user interaction with them, and so on. This is
|
Chris@127
|
161 generally fiddly, if not especially interesting. Panes are
|
Chris@127
|
162 strictly layout objects and are not stored in the Document class;
|
Chris@127
|
163 instead the MainWindow contains a PaneStack widget (the widget that
|
Chris@127
|
164 takes up most of Sonic Visualiser's main window) which contains a
|
Chris@127
|
165 set of panes stacked vertically.
|
Chris@127
|
166
|
Chris@127
|
167 Another View subclass is Overview, which is the widget that
|
Chris@127
|
168 contains that green waveform showing the entire file at the bottom
|
Chris@127
|
169 of the window.
|
Chris@127
|
170
|
Chris@127
|
171 */
|
Chris@127
|
172
|
Chris@0
|
173 static QMutex cleanupMutex;
|
Chris@0
|
174
|
Chris@0
|
175 static void
|
Chris@0
|
176 signalHandler(int /* signal */)
|
Chris@0
|
177 {
|
Chris@0
|
178 // Avoid this happening more than once across threads
|
Chris@0
|
179
|
Chris@0
|
180 cleanupMutex.lock();
|
Chris@0
|
181 std::cerr << "signalHandler: cleaning up and exiting" << std::endl;
|
Chris@0
|
182 TempDirectory::getInstance()->cleanup();
|
Chris@0
|
183 exit(0); // without releasing mutex
|
Chris@0
|
184 }
|
Chris@0
|
185
|
Chris@11
|
186 class SVApplication : public QApplication
|
Chris@11
|
187 {
|
Chris@11
|
188 public:
|
Chris@296
|
189 SVApplication(int &argc, char **argv) :
|
Chris@11
|
190 QApplication(argc, argv),
|
Chris@11
|
191 m_mainWindow(0) { }
|
Chris@11
|
192 virtual ~SVApplication() { }
|
Chris@11
|
193
|
Chris@11
|
194 void setMainWindow(MainWindow *mw) { m_mainWindow = mw; }
|
Chris@11
|
195 void releaseMainWindow() { m_mainWindow = 0; }
|
Chris@11
|
196
|
Chris@11
|
197 virtual void commitData(QSessionManager &manager) {
|
Chris@11
|
198 if (!m_mainWindow) return;
|
Chris@11
|
199 bool mayAskUser = manager.allowsInteraction();
|
Chris@11
|
200 bool success = m_mainWindow->commitData(mayAskUser);
|
Chris@11
|
201 manager.release();
|
Chris@11
|
202 if (!success) manager.cancel();
|
Chris@11
|
203 }
|
Chris@11
|
204
|
Chris@11
|
205 protected:
|
Chris@11
|
206 MainWindow *m_mainWindow;
|
Chris@11
|
207 };
|
Chris@11
|
208
|
Chris@0
|
209 int
|
Chris@0
|
210 main(int argc, char **argv)
|
Chris@0
|
211 {
|
Chris@376
|
212 svSystemSpecificInitialisation();
|
Chris@376
|
213
|
Chris@316
|
214 #ifdef Q_WS_X11
|
Chris@317
|
215 #if QT_VERSION >= 0x040500
|
Chris@342
|
216 // QApplication::setGraphicsSystem("raster");
|
Chris@316
|
217 #endif
|
Chris@316
|
218 #endif
|
Chris@316
|
219
|
Chris@11
|
220 SVApplication application(argc, argv);
|
Chris@0
|
221
|
Chris@46
|
222 QStringList args = application.arguments();
|
Chris@46
|
223
|
Chris@0
|
224 signal(SIGINT, signalHandler);
|
Chris@0
|
225 signal(SIGTERM, signalHandler);
|
Chris@0
|
226
|
Chris@0
|
227 #ifndef Q_WS_WIN32
|
Chris@0
|
228 signal(SIGHUP, signalHandler);
|
Chris@0
|
229 signal(SIGQUIT, signalHandler);
|
Chris@0
|
230 #endif
|
Chris@0
|
231
|
Chris@46
|
232 bool audioOutput = true;
|
Chris@70
|
233 bool oscSupport = true;
|
Chris@70
|
234
|
Chris@133
|
235 if (args.contains("--help") || args.contains("-h") || args.contains("-?")) {
|
Chris@70
|
236 std::cerr << QApplication::tr(
|
mathieu@406
|
237 "\nSonic Visualiser is a program for viewing and exploring audio data\nfor semantic music analysis and annotation. The Kiosk edition provides all of the features of the full Sonic Visualiser, except that audio files cannot be saved from it.\n\nUsage:\n\n %1 [--no-audio] [--no-osc] [<file> ...]\n\n --no-audio: Do not attempt to open an audio output device\n --no-osc: Do not provide an Open Sound Control port for remote control\n <file>: One or more Sonic Visualiser (.sv) and audio files may be provided.\n").arg(argv[0]).toStdString() << std::endl;
|
Chris@70
|
238 exit(2);
|
Chris@70
|
239 }
|
Chris@70
|
240
|
Chris@46
|
241 if (args.contains("--no-audio")) audioOutput = false;
|
Chris@70
|
242 if (args.contains("--no-osc")) oscSupport = false;
|
Chris@46
|
243
|
Chris@6
|
244 QApplication::setOrganizationName("sonic-visualiser");
|
Chris@5
|
245 QApplication::setOrganizationDomain("sonicvisualiser.org");
|
mathieu@406
|
246 QApplication::setApplicationName(QApplication::tr("Sonic Visualiser (Kiosk edition)"));
|
Chris@141
|
247
|
Chris@283
|
248 QSplashScreen *splash = 0;
|
Chris@231
|
249
|
Chris@231
|
250 QSettings settings;
|
Chris@237
|
251
|
Chris@237
|
252 settings.beginGroup("Preferences");
|
Chris@237
|
253 if (settings.value("show-splash", true).toBool()) {
|
Chris@331
|
254 QPixmap pixmap(":/icons/sv-splash.png");
|
Chris@331
|
255 QPainter painter;
|
Chris@331
|
256 painter.begin(&pixmap);
|
Chris@331
|
257 QString text = QString("v%1").arg(SV_VERSION);
|
Chris@331
|
258 painter.drawText
|
Chris@331
|
259 (pixmap.width() - painter.fontMetrics().width(text) - 10,
|
Chris@331
|
260 10 + painter.fontMetrics().ascent(),
|
Chris@331
|
261 text);
|
Chris@331
|
262 painter.end();
|
Chris@283
|
263 splash = new QSplashScreen(pixmap);
|
Chris@283
|
264 splash->show();
|
Chris@283
|
265 QTimer::singleShot(5000, splash, SLOT(hide()));
|
Chris@231
|
266 application.processEvents();
|
Chris@231
|
267 }
|
Chris@237
|
268 settings.endGroup();
|
Chris@231
|
269
|
Chris@278
|
270 settings.beginGroup("RDF");
|
Chris@278
|
271 if (!settings.contains("rdf-indices")) {
|
Chris@278
|
272 QStringList list;
|
Chris@278
|
273 list << "http://www.vamp-plugins.org/rdf/plugins/index.txt";
|
Chris@278
|
274 settings.setValue("rdf-indices", list);
|
Chris@278
|
275 }
|
Chris@278
|
276 settings.endGroup();
|
Chris@278
|
277
|
Chris@141
|
278 QIcon icon;
|
Chris@141
|
279 int sizes[] = { 16, 22, 24, 32, 48, 64, 128 };
|
Chris@141
|
280 for (int i = 0; i < sizeof(sizes)/sizeof(sizes[0]); ++i) {
|
Chris@141
|
281 icon.addFile(QString(":icons/sv-%1x%2.png").arg(sizes[i]).arg(sizes[i]));
|
Chris@141
|
282 }
|
Chris@141
|
283 QApplication::setWindowIcon(icon);
|
Chris@7
|
284
|
Chris@0
|
285 QString language = QLocale::system().name();
|
Chris@0
|
286
|
Chris@0
|
287 QTranslator qtTranslator;
|
Chris@0
|
288 QString qtTrName = QString("qt_%1").arg(language);
|
Chris@253
|
289 std::cerr << "Loading " << qtTrName.toStdString() << "... ";
|
Chris@165
|
290 bool success = false;
|
Chris@165
|
291 if (!(success = qtTranslator.load(qtTrName))) {
|
Chris@165
|
292 QString qtDir = getenv("QTDIR");
|
Chris@165
|
293 if (qtDir != "") {
|
Chris@165
|
294 success = qtTranslator.load
|
Chris@165
|
295 (qtTrName, QDir(qtDir).filePath("translations"));
|
Chris@165
|
296 }
|
Chris@165
|
297 }
|
Chris@165
|
298 if (!success) {
|
Chris@253
|
299 std::cerr << "Failed\nFailed to load Qt translation for locale" << std::endl;
|
Chris@253
|
300 } else {
|
Chris@253
|
301 std::cerr << "Done" << std::endl;
|
Chris@165
|
302 }
|
Chris@0
|
303 application.installTranslator(&qtTranslator);
|
Chris@0
|
304
|
Chris@0
|
305 QTranslator svTranslator;
|
Chris@0
|
306 QString svTrName = QString("sonic-visualiser_%1").arg(language);
|
Chris@253
|
307 std::cerr << "Loading " << svTrName.toStdString() << "... ";
|
Chris@0
|
308 svTranslator.load(svTrName, ":i18n");
|
Chris@253
|
309 std::cerr << "Done" << std::endl;
|
Chris@0
|
310 application.installTranslator(&svTranslator);
|
Chris@0
|
311
|
Chris@187
|
312 StoreStartupLocale();
|
Chris@187
|
313
|
Chris@0
|
314 // Permit size_t and PropertyName to be used as args in queued signal calls
|
Chris@0
|
315 qRegisterMetaType<size_t>("size_t");
|
Chris@0
|
316 qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName");
|
Chris@0
|
317
|
Chris@222
|
318 MainWindow *gui = new MainWindow(audioOutput, oscSupport);
|
Chris@222
|
319 application.setMainWindow(gui);
|
Chris@283
|
320 if (splash) {
|
Chris@283
|
321 QObject::connect(gui, SIGNAL(hideSplash()), splash, SLOT(hide()));
|
Chris@283
|
322 }
|
Chris@0
|
323
|
Chris@0
|
324 QDesktopWidget *desktop = QApplication::desktop();
|
Chris@0
|
325 QRect available = desktop->availableGeometry();
|
Chris@0
|
326
|
Chris@378
|
327 int width = (available.width() * 2) / 3;
|
Chris@0
|
328 int height = available.height() / 2;
|
Chris@378
|
329 if (height < 450) height = (available.height() * 2) / 3;
|
Chris@0
|
330 if (width > height * 2) width = height * 2;
|
Chris@0
|
331
|
Chris@237
|
332 settings.beginGroup("MainWindow");
|
Chris@5
|
333 QSize size = settings.value("size", QSize(width, height)).toSize();
|
Chris@319
|
334 gui->resizeConstrained(size);
|
Chris@5
|
335 if (settings.contains("position")) {
|
Chris@297
|
336 QRect prevrect(settings.value("position").toPoint(), size);
|
Chris@297
|
337 if (!(available & prevrect).isEmpty()) {
|
Chris@297
|
338 gui->move(prevrect.topLeft());
|
Chris@297
|
339 }
|
Chris@5
|
340 }
|
Chris@5
|
341 settings.endGroup();
|
Chris@5
|
342
|
Chris@222
|
343 gui->show();
|
Chris@64
|
344
|
Chris@118
|
345 // The MainWindow class seems to have trouble dealing with this if
|
Chris@118
|
346 // it tries to adapt to this preference before the constructor is
|
Chris@118
|
347 // complete. As a lazy hack, apply it explicitly from here
|
Chris@222
|
348 gui->preferenceChanged("Property Box Layout");
|
Chris@118
|
349
|
Chris@54
|
350 bool haveSession = false;
|
Chris@54
|
351 bool haveMainModel = false;
|
Chris@145
|
352 bool havePriorCommandLineModel = false;
|
Chris@46
|
353
|
Chris@54
|
354 for (QStringList::iterator i = args.begin(); i != args.end(); ++i) {
|
Chris@54
|
355
|
Chris@83
|
356 MainWindow::FileOpenStatus status = MainWindow::FileOpenFailed;
|
Chris@54
|
357
|
Chris@54
|
358 if (i == args.begin()) continue;
|
Chris@54
|
359 if (i->startsWith('-')) continue;
|
Chris@54
|
360
|
Chris@54
|
361 QString path = *i;
|
Chris@54
|
362
|
Chris@54
|
363 if (path.endsWith("sv")) {
|
Chris@54
|
364 if (!haveSession) {
|
Chris@222
|
365 status = gui->openSessionFile(path);
|
Chris@82
|
366 if (status == MainWindow::FileOpenSucceeded) {
|
Chris@54
|
367 haveSession = true;
|
Chris@54
|
368 haveMainModel = true;
|
Chris@54
|
369 }
|
Chris@54
|
370 } else {
|
Chris@54
|
371 std::cerr << "WARNING: Ignoring additional session file argument \"" << path.toStdString() << "\"" << std::endl;
|
Chris@82
|
372 status = MainWindow::FileOpenSucceeded;
|
Chris@54
|
373 }
|
Chris@54
|
374 }
|
Chris@82
|
375 if (status != MainWindow::FileOpenSucceeded) {
|
Chris@54
|
376 if (!haveMainModel) {
|
Chris@222
|
377 status = gui->open(path, MainWindow::ReplaceMainModel);
|
Chris@145
|
378 if (status == MainWindow::FileOpenSucceeded) {
|
Chris@145
|
379 haveMainModel = true;
|
Chris@145
|
380 }
|
Chris@54
|
381 } else {
|
Chris@145
|
382 if (haveSession && !havePriorCommandLineModel) {
|
Chris@222
|
383 status = gui->open(path, MainWindow::AskUser);
|
Chris@145
|
384 if (status == MainWindow::FileOpenSucceeded) {
|
Chris@145
|
385 havePriorCommandLineModel = true;
|
Chris@145
|
386 }
|
Chris@145
|
387 } else {
|
Chris@222
|
388 status = gui->open(path, MainWindow::CreateAdditionalModel);
|
Chris@145
|
389 }
|
Chris@54
|
390 }
|
Chris@54
|
391 }
|
Chris@82
|
392 if (status == MainWindow::FileOpenFailed) {
|
Chris@283
|
393 if (splash) splash->hide();
|
Chris@54
|
394 QMessageBox::critical
|
Chris@222
|
395 (gui, QMessageBox::tr("Failed to open file"),
|
Chris@197
|
396 QMessageBox::tr("File or URL \"%1\" could not be opened").arg(path));
|
Chris@270
|
397 } else if (status == MainWindow::FileOpenWrongMode) {
|
Chris@283
|
398 if (splash) splash->hide();
|
Chris@270
|
399 QMessageBox::critical
|
Chris@270
|
400 (gui, QMessageBox::tr("Failed to open file"),
|
Chris@270
|
401 QMessageBox::tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
|
Chris@54
|
402 }
|
Chris@180
|
403 }
|
Chris@180
|
404
|
Chris@215
|
405 #ifdef HAVE_FFTW3F
|
Chris@215
|
406 settings.beginGroup("FFTWisdom");
|
Chris@215
|
407 QString wisdom = settings.value("wisdom").toString();
|
Chris@215
|
408 if (wisdom != "") {
|
Chris@215
|
409 fftwf_import_wisdom_from_string(wisdom.toLocal8Bit().data());
|
Chris@215
|
410 }
|
Chris@267
|
411 #ifdef HAVE_FFTW3
|
Chris@267
|
412 wisdom = settings.value("wisdom_d").toString();
|
Chris@267
|
413 if (wisdom != "") {
|
Chris@267
|
414 fftw_import_wisdom_from_string(wisdom.toLocal8Bit().data());
|
Chris@267
|
415 }
|
Chris@267
|
416 #endif
|
Chris@215
|
417 settings.endGroup();
|
Chris@215
|
418 #endif
|
Chris@180
|
419
|
Chris@283
|
420 if (splash) splash->finish(gui);
|
Chris@283
|
421 delete splash;
|
Chris@180
|
422
|
Chris@123
|
423 /*
|
Chris@120
|
424 TipDialog tipDialog;
|
Chris@120
|
425 if (tipDialog.isOK()) {
|
Chris@120
|
426 tipDialog.exec();
|
Chris@120
|
427 }
|
Chris@123
|
428 */
|
Chris@0
|
429 int rv = application.exec();
|
Chris@0
|
430
|
Chris@298
|
431 gui->hide();
|
Chris@298
|
432
|
Chris@0
|
433 cleanupMutex.lock();
|
Chris@332
|
434
|
Chris@315
|
435 TransformFactory::deleteInstance();
|
Chris@0
|
436 TempDirectory::getInstance()->cleanup();
|
Chris@11
|
437 application.releaseMainWindow();
|
Chris@5
|
438
|
Chris@215
|
439 #ifdef HAVE_FFTW3F
|
Chris@267
|
440 settings.beginGroup("FFTWisdom");
|
Chris@215
|
441 char *cwisdom = fftwf_export_wisdom_to_string();
|
Chris@215
|
442 if (cwisdom) {
|
Chris@215
|
443 settings.setValue("wisdom", cwisdom);
|
Chris@332
|
444 free(cwisdom);
|
Chris@215
|
445 }
|
Chris@267
|
446 #ifdef HAVE_FFTW3
|
Chris@267
|
447 cwisdom = fftw_export_wisdom_to_string();
|
Chris@267
|
448 if (cwisdom) {
|
Chris@267
|
449 settings.setValue("wisdom_d", cwisdom);
|
Chris@332
|
450 free(cwisdom);
|
Chris@267
|
451 }
|
Chris@267
|
452 #endif
|
Chris@267
|
453 settings.endGroup();
|
Chris@215
|
454 #endif
|
Chris@215
|
455
|
Chris@222
|
456 delete gui;
|
Chris@222
|
457
|
Chris@0
|
458 return rv;
|
Chris@0
|
459 }
|