Mercurial > hg > easaier-soundaccess
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sv/main/main.cpp Fri May 11 09:08:14 2007 +0000 @@ -0,0 +1,330 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "MainWindow.h" + +#include "system/System.h" +#include "system/Init.h" +#include "base/TempDirectory.h" +#include "base/PropertyContainer.h" +#include "base/Preferences.h" +#include "widgets/TipDialog.h" + +#include <QMetaType> +#include <QApplication> +#include <QDesktopWidget> +#include <QMessageBox> +#include <QTranslator> +#include <QLocale> +#include <QSettings> +#include <QIcon> +#include <QSessionManager> + +#include <iostream> +#include <signal.h> + +/*! \mainpage Sonic Visualiser + +\section interesting Summary of interesting classes + + - Data models: Model and subclasses, e.g. WaveFileModel + + - Graphical layers: Layer and subclasses, displayed on View and its + subclass widgets. + + - Main window class, document class, and file parser: MainWindow, + Document, SVFileReader + + - Turning one model (e.g. audio) into another (e.g. more audio, or a + curve extracted from it): Transform and subclasses + + - Creating the plugins used by transforms: RealTimePluginFactory, + FeatureExtractionPluginFactory. See also the API documentation for + Vamp feature extraction plugins at + http://www.vamp-plugins.org/code-doc/. + + - File reading and writing code: AudioFileReader and subclasses, + WavFileWriter, DataFileReader, SVFileReader + + - FFT calculation and cacheing: FFTModel, FFTDataServer + + - Widgets that show groups of editable properties: PropertyBox for + layer properties (contained in a PropertyStack), PluginParameterBox + for plugins (contained in a PluginParameterDialog) + + - Audio playback: AudioCallbackPlaySource and subclasses, + AudioCallbackPlayTarget and subclasses, AudioGenerator + +\section model Data sources: the Model hierarchy + + A Model is something containing, or knowing how to obtain, data. + + For example, WaveFileModel is a model that knows how to get data + from an audio file; SparseTimeValueModel is a model containing + editable "curve" data. + + Models typically subclass one of a number of abstract subclasses of + Model. For example, WaveFileModel subclasses DenseTimeValueModel, + which describes an interface for models that have a value at each + time point for a given sampling resolution. (Note that + WaveFileModel does not actually read the files itself: it uses + AudioFileReader classes for that. It just makes data from the + files available in a Model.) SparseTimeValueModel uses the + SparseModel template class, which provides most of the + implementation for models that contain a series of points of some + sort -- also used by NoteModel, TextModel, and + SparseOneDimensionalModel. + + Everything that goes on the screen originates from a model, via a + layer (see below). The models are contained in a Document object. + There is no containment hierarchy or ordering of models in the + document. One model is the main model, which defines the sample + rate for playback. + + A model may also be marked as a "derived" model, which means it was + generated from another model using some transform (feature + extraction or effect plugin, etc) -- the idea being that they can + be re-generated using the same transform if a new source model is + loaded. + +\section layer Things that can display data: the Layer hierarchy + + A Layer is something that knows how to draw parts of a model onto a + timeline. + + For example, WaveformLayer is a layer which draws waveforms, based + on WaveFileModel; TimeValueLayer draws curves, based on + SparseTimeValueModel; SpectrogramLayer draws spectrograms, based on + WaveFileModel (via FFTModel). + + The most basic functions of a layer are: to draw itself onto a + Pane, against a timeline on the x axis; and to permit user + interaction. If you were thinking of adding the capability to + display a new sort of something, then you would want to add a new + layer type. (You may also need a new model type, depending on + whether any existing model can capture the data you need.) + Depending on the sort of data in question, there are various + existing layers that might be appropriate to start from -- for + example, a layer that displays images that the user has imported + and associated with particular times might have something in common + with the existing TextLayer which displays pieces of text that are + associated with particular times. + + Although layers are visual objects, they are contained in the + Document in Sonic Visualiser rather than being managed together + with display widgets. The Sonic Visualiser file format has + separate data and layout sections, and the layers are defined in + the data section and then referred to in the layout section which + determines which layers may go on which panes (see Pane below). + + Once a layer class is defined, some basic data about it needs to be + set up in the LayerFactory class, and then it will appear in the + menus and so on on the main window. + +\section view Widgets that are used to show layers: The View hierarchy + + A View is a widget that displays a stack of layers. The most + important subclass is Pane, the widget that is used to show most of + the data in the main window of Sonic Visualiser. + + All a pane really does is contain a set of layers and get them to + render themselves (one on top of the other, with the topmost layer + being the one that is currently interacted with), cache the + results, negotiate user interaction with them, and so on. This is + generally fiddly, if not especially interesting. Panes are + strictly layout objects and are not stored in the Document class; + instead the MainWindow contains a PaneStack widget (the widget that + takes up most of Sonic Visualiser's main window) which contains a + set of panes stacked vertically. + + Another View subclass is Overview, which is the widget that + contains that green waveform showing the entire file at the bottom + of the window. + +*/ + +static QMutex cleanupMutex; + +static void +signalHandler(int /* signal */) +{ + // Avoid this happening more than once across threads + + cleanupMutex.lock(); + std::cerr << "signalHandler: cleaning up and exiting" << std::endl; + TempDirectory::getInstance()->cleanup(); + exit(0); // without releasing mutex +} + +class SVApplication : public QApplication +{ +public: + SVApplication(int argc, char **argv) : + QApplication(argc, argv), + m_mainWindow(0) { } + virtual ~SVApplication() { } + + void setMainWindow(MainWindow *mw) { m_mainWindow = mw; } + void releaseMainWindow() { m_mainWindow = 0; } + + virtual void commitData(QSessionManager &manager) { + if (!m_mainWindow) return; + bool mayAskUser = manager.allowsInteraction(); + bool success = m_mainWindow->commitData(mayAskUser); + manager.release(); + if (!success) manager.cancel(); + } + +protected: + MainWindow *m_mainWindow; +}; + +int +main(int argc, char **argv) +{ + SVApplication application(argc, argv); + + QStringList args = application.arguments(); + + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); + +#ifndef Q_WS_WIN32 + signal(SIGHUP, signalHandler); + signal(SIGQUIT, signalHandler); +#endif + + svSystemSpecificInitialisation(); + + bool audioOutput = true; + bool oscSupport = true; + + if (args.contains("--help") || args.contains("-h") || args.contains("-?")) { + std::cerr << QApplication::tr( + "\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; + exit(2); + } + + if (args.contains("--no-audio")) audioOutput = false; + if (args.contains("--no-osc")) oscSupport = false; + + QApplication::setOrganizationName("sonic-visualiser"); + QApplication::setOrganizationDomain("sonicvisualiser.org"); + QApplication::setApplicationName("sonic-visualiser"); + QApplication::setWindowIcon(QIcon(":icons/svicon16.png")); + + QString language = QLocale::system().name(); + + QTranslator qtTranslator; + QString qtTrName = QString("qt_%1").arg(language); + std::cerr << "Loading " << qtTrName.toStdString() << "..." << std::endl; + qtTranslator.load(qtTrName); + application.installTranslator(&qtTranslator); + + QTranslator svTranslator; + QString svTrName = QString("sonic-visualiser_%1").arg(language); + std::cerr << "Loading " << svTrName.toStdString() << "..." << std::endl; + svTranslator.load(svTrName, ":i18n"); + application.installTranslator(&svTranslator); + + // Permit size_t and PropertyName to be used as args in queued signal calls + qRegisterMetaType<size_t>("size_t"); + qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName"); + + MainWindow gui(audioOutput, oscSupport); + application.setMainWindow(&gui); + + QDesktopWidget *desktop = QApplication::desktop(); + QRect available = desktop->availableGeometry(); + + int width = available.width() * 2 / 3; + int height = available.height() / 2; + if (height < 450) height = available.height() * 2 / 3; + if (width > height * 2) width = height * 2; + + QSettings settings; + settings.beginGroup("MainWindow"); + QSize size = settings.value("size", QSize(width, height)).toSize(); + gui.resize(size); + if (settings.contains("position")) { + gui.move(settings.value("position").toPoint()); + } + settings.endGroup(); + + gui.show(); + + // The MainWindow class seems to have trouble dealing with this if + // it tries to adapt to this preference before the constructor is + // complete. As a lazy hack, apply it explicitly from here + gui.preferenceChanged("Property Box Layout"); + + bool haveSession = false; + bool haveMainModel = false; + + for (QStringList::iterator i = args.begin(); i != args.end(); ++i) { + + MainWindow::FileOpenStatus status = MainWindow::FileOpenFailed; + + if (i == args.begin()) continue; + if (i->startsWith('-')) continue; + + if (i->startsWith("http:") || i->startsWith("ftp:")) { + status = gui.openURL(QUrl(*i)); + continue; + } + + QString path = *i; + + if (path.endsWith("sv")) { + if (!haveSession) { + status = gui.openSessionFile(path); + if (status == MainWindow::FileOpenSucceeded) { + haveSession = true; + haveMainModel = true; + } + } else { + std::cerr << "WARNING: Ignoring additional session file argument \"" << path.toStdString() << "\"" << std::endl; + status = MainWindow::FileOpenSucceeded; + } + } + if (status != MainWindow::FileOpenSucceeded) { + if (!haveMainModel) { + status = gui.openSomeFile(path, MainWindow::ReplaceMainModel); + if (status == MainWindow::FileOpenSucceeded) haveMainModel = true; + } else { + status = gui.openSomeFile(path, MainWindow::CreateAdditionalModel); + } + } + if (status == MainWindow::FileOpenFailed) { + QMessageBox::critical + (&gui, QMessageBox::tr("Failed to open file"), + QMessageBox::tr("File \"%1\" could not be opened").arg(path)); + } + } +/* + TipDialog tipDialog; + if (tipDialog.isOK()) { + tipDialog.exec(); + } +*/ + int rv = application.exec(); +// std::cerr << "application.exec() returned " << rv << std::endl; + + cleanupMutex.lock(); + TempDirectory::getInstance()->cleanup(); + application.releaseMainWindow(); + + return rv; +}