Mercurial > hg > easaier-soundaccess
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 } |