comparison sv/main/main.cpp @ 0:fc9323a41f5a

start base : Sonic Visualiser sv1-1.0rc1
author lbajardsilogic
date Fri, 11 May 2007 09:08:14 +0000
parents
children ba54bc09cd62
comparison
equal deleted inserted replaced
-1:000000000000 0:fc9323a41f5a
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Sonic Visualiser
5 An audio file viewer and annotation editor.
6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2006 Chris Cannam and QMUL.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
14 */
15
16 #include "MainWindow.h"
17
18 #include "system/System.h"
19 #include "system/Init.h"
20 #include "base/TempDirectory.h"
21 #include "base/PropertyContainer.h"
22 #include "base/Preferences.h"
23 #include "widgets/TipDialog.h"
24
25 #include <QMetaType>
26 #include <QApplication>
27 #include <QDesktopWidget>
28 #include <QMessageBox>
29 #include <QTranslator>
30 #include <QLocale>
31 #include <QSettings>
32 #include <QIcon>
33 #include <QSessionManager>
34
35 #include <iostream>
36 #include <signal.h>
37
38 /*! \mainpage Sonic Visualiser
39
40 \section interesting Summary of interesting classes
41
42 - Data models: Model and subclasses, e.g. WaveFileModel
43
44 - Graphical layers: Layer and subclasses, displayed on View and its
45 subclass widgets.
46
47 - Main window class, document class, and file parser: MainWindow,
48 Document, SVFileReader
49
50 - Turning one model (e.g. audio) into another (e.g. more audio, or a
51 curve extracted from it): Transform and subclasses
52
53 - Creating the plugins used by transforms: RealTimePluginFactory,
54 FeatureExtractionPluginFactory. See also the API documentation for
55 Vamp feature extraction plugins at
56 http://www.vamp-plugins.org/code-doc/.
57
58 - File reading and writing code: AudioFileReader and subclasses,
59 WavFileWriter, DataFileReader, SVFileReader
60
61 - FFT calculation and cacheing: FFTModel, FFTDataServer
62
63 - Widgets that show groups of editable properties: PropertyBox for
64 layer properties (contained in a PropertyStack), PluginParameterBox
65 for plugins (contained in a PluginParameterDialog)
66
67 - Audio playback: AudioCallbackPlaySource and subclasses,
68 AudioCallbackPlayTarget and subclasses, AudioGenerator
69
70 \section model Data sources: the Model hierarchy
71
72 A Model is something containing, or knowing how to obtain, data.
73
74 For example, WaveFileModel is a model that knows how to get data
75 from an audio file; SparseTimeValueModel is a model containing
76 editable "curve" data.
77
78 Models typically subclass one of a number of abstract subclasses of
79 Model. For example, WaveFileModel subclasses DenseTimeValueModel,
80 which describes an interface for models that have a value at each
81 time point for a given sampling resolution. (Note that
82 WaveFileModel does not actually read the files itself: it uses
83 AudioFileReader classes for that. It just makes data from the
84 files available in a Model.) SparseTimeValueModel uses the
85 SparseModel template class, which provides most of the
86 implementation for models that contain a series of points of some
87 sort -- also used by NoteModel, TextModel, and
88 SparseOneDimensionalModel.
89
90 Everything that goes on the screen originates from a model, via a
91 layer (see below). The models are contained in a Document object.
92 There is no containment hierarchy or ordering of models in the
93 document. One model is the main model, which defines the sample
94 rate for playback.
95
96 A model may also be marked as a "derived" model, which means it was
97 generated from another model using some transform (feature
98 extraction or effect plugin, etc) -- the idea being that they can
99 be re-generated using the same transform if a new source model is
100 loaded.
101
102 \section layer Things that can display data: the Layer hierarchy
103
104 A Layer is something that knows how to draw parts of a model onto a
105 timeline.
106
107 For example, WaveformLayer is a layer which draws waveforms, based
108 on WaveFileModel; TimeValueLayer draws curves, based on
109 SparseTimeValueModel; SpectrogramLayer draws spectrograms, based on
110 WaveFileModel (via FFTModel).
111
112 The most basic functions of a layer are: to draw itself onto a
113 Pane, against a timeline on the x axis; and to permit user
114 interaction. If you were thinking of adding the capability to
115 display a new sort of something, then you would want to add a new
116 layer type. (You may also need a new model type, depending on
117 whether any existing model can capture the data you need.)
118 Depending on the sort of data in question, there are various
119 existing layers that might be appropriate to start from -- for
120 example, a layer that displays images that the user has imported
121 and associated with particular times might have something in common
122 with the existing TextLayer which displays pieces of text that are
123 associated with particular times.
124
125 Although layers are visual objects, they are contained in the
126 Document in Sonic Visualiser rather than being managed together
127 with display widgets. The Sonic Visualiser file format has
128 separate data and layout sections, and the layers are defined in
129 the data section and then referred to in the layout section which
130 determines which layers may go on which panes (see Pane below).
131
132 Once a layer class is defined, some basic data about it needs to be
133 set up in the LayerFactory class, and then it will appear in the
134 menus and so on on the main window.
135
136 \section view Widgets that are used to show layers: The View hierarchy
137
138 A View is a widget that displays a stack of layers. The most
139 important subclass is Pane, the widget that is used to show most of
140 the data in the main window of Sonic Visualiser.
141
142 All a pane really does is contain a set of layers and get them to
143 render themselves (one on top of the other, with the topmost layer
144 being the one that is currently interacted with), cache the
145 results, negotiate user interaction with them, and so on. This is
146 generally fiddly, if not especially interesting. Panes are
147 strictly layout objects and are not stored in the Document class;
148 instead the MainWindow contains a PaneStack widget (the widget that
149 takes up most of Sonic Visualiser's main window) which contains a
150 set of panes stacked vertically.
151
152 Another View subclass is Overview, which is the widget that
153 contains that green waveform showing the entire file at the bottom
154 of the window.
155
156 */
157
158 static QMutex cleanupMutex;
159
160 static void
161 signalHandler(int /* signal */)
162 {
163 // Avoid this happening more than once across threads
164
165 cleanupMutex.lock();
166 std::cerr << "signalHandler: cleaning up and exiting" << std::endl;
167 TempDirectory::getInstance()->cleanup();
168 exit(0); // without releasing mutex
169 }
170
171 class SVApplication : public QApplication
172 {
173 public:
174 SVApplication(int argc, char **argv) :
175 QApplication(argc, argv),
176 m_mainWindow(0) { }
177 virtual ~SVApplication() { }
178
179 void setMainWindow(MainWindow *mw) { m_mainWindow = mw; }
180 void releaseMainWindow() { m_mainWindow = 0; }
181
182 virtual void commitData(QSessionManager &manager) {
183 if (!m_mainWindow) return;
184 bool mayAskUser = manager.allowsInteraction();
185 bool success = m_mainWindow->commitData(mayAskUser);
186 manager.release();
187 if (!success) manager.cancel();
188 }
189
190 protected:
191 MainWindow *m_mainWindow;
192 };
193
194 int
195 main(int argc, char **argv)
196 {
197 SVApplication application(argc, argv);
198
199 QStringList args = application.arguments();
200
201 signal(SIGINT, signalHandler);
202 signal(SIGTERM, signalHandler);
203
204 #ifndef Q_WS_WIN32
205 signal(SIGHUP, signalHandler);
206 signal(SIGQUIT, signalHandler);
207 #endif
208
209 svSystemSpecificInitialisation();
210
211 bool audioOutput = true;
212 bool oscSupport = true;
213
214 if (args.contains("--help") || args.contains("-h") || args.contains("-?")) {
215 std::cerr << QApplication::tr(
216 "\nSonic Visualiser is a program for viewing and exploring audio data\nfor semantic music analysis and annotation.\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;
217 exit(2);
218 }
219
220 if (args.contains("--no-audio")) audioOutput = false;
221 if (args.contains("--no-osc")) oscSupport = false;
222
223 QApplication::setOrganizationName("sonic-visualiser");
224 QApplication::setOrganizationDomain("sonicvisualiser.org");
225 QApplication::setApplicationName("sonic-visualiser");
226 QApplication::setWindowIcon(QIcon(":icons/svicon16.png"));
227
228 QString language = QLocale::system().name();
229
230 QTranslator qtTranslator;
231 QString qtTrName = QString("qt_%1").arg(language);
232 std::cerr << "Loading " << qtTrName.toStdString() << "..." << std::endl;
233 qtTranslator.load(qtTrName);
234 application.installTranslator(&qtTranslator);
235
236 QTranslator svTranslator;
237 QString svTrName = QString("sonic-visualiser_%1").arg(language);
238 std::cerr << "Loading " << svTrName.toStdString() << "..." << std::endl;
239 svTranslator.load(svTrName, ":i18n");
240 application.installTranslator(&svTranslator);
241
242 // Permit size_t and PropertyName to be used as args in queued signal calls
243 qRegisterMetaType<size_t>("size_t");
244 qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName");
245
246 MainWindow gui(audioOutput, oscSupport);
247 application.setMainWindow(&gui);
248
249 QDesktopWidget *desktop = QApplication::desktop();
250 QRect available = desktop->availableGeometry();
251
252 int width = available.width() * 2 / 3;
253 int height = available.height() / 2;
254 if (height < 450) height = available.height() * 2 / 3;
255 if (width > height * 2) width = height * 2;
256
257 QSettings settings;
258 settings.beginGroup("MainWindow");
259 QSize size = settings.value("size", QSize(width, height)).toSize();
260 gui.resize(size);
261 if (settings.contains("position")) {
262 gui.move(settings.value("position").toPoint());
263 }
264 settings.endGroup();
265
266 gui.show();
267
268 // The MainWindow class seems to have trouble dealing with this if
269 // it tries to adapt to this preference before the constructor is
270 // complete. As a lazy hack, apply it explicitly from here
271 gui.preferenceChanged("Property Box Layout");
272
273 bool haveSession = false;
274 bool haveMainModel = false;
275
276 for (QStringList::iterator i = args.begin(); i != args.end(); ++i) {
277
278 MainWindow::FileOpenStatus status = MainWindow::FileOpenFailed;
279
280 if (i == args.begin()) continue;
281 if (i->startsWith('-')) continue;
282
283 if (i->startsWith("http:") || i->startsWith("ftp:")) {
284 status = gui.openURL(QUrl(*i));
285 continue;
286 }
287
288 QString path = *i;
289
290 if (path.endsWith("sv")) {
291 if (!haveSession) {
292 status = gui.openSessionFile(path);
293 if (status == MainWindow::FileOpenSucceeded) {
294 haveSession = true;
295 haveMainModel = true;
296 }
297 } else {
298 std::cerr << "WARNING: Ignoring additional session file argument \"" << path.toStdString() << "\"" << std::endl;
299 status = MainWindow::FileOpenSucceeded;
300 }
301 }
302 if (status != MainWindow::FileOpenSucceeded) {
303 if (!haveMainModel) {
304 status = gui.openSomeFile(path, MainWindow::ReplaceMainModel);
305 if (status == MainWindow::FileOpenSucceeded) haveMainModel = true;
306 } else {
307 status = gui.openSomeFile(path, MainWindow::CreateAdditionalModel);
308 }
309 }
310 if (status == MainWindow::FileOpenFailed) {
311 QMessageBox::critical
312 (&gui, QMessageBox::tr("Failed to open file"),
313 QMessageBox::tr("File \"%1\" could not be opened").arg(path));
314 }
315 }
316 /*
317 TipDialog tipDialog;
318 if (tipDialog.isOK()) {
319 tipDialog.exec();
320 }
321 */
322 int rv = application.exec();
323 // std::cerr << "application.exec() returned " << rv << std::endl;
324
325 cleanupMutex.lock();
326 TempDirectory::getInstance()->cleanup();
327 application.releaseMainWindow();
328
329 return rv;
330 }