# HG changeset patch # User Chris Cannam # Date 1190987798 0 # Node ID 98ba77e0d89757fb415cee14c6b2eb5a31e4a808 # Parent dab257bd9d2d52d1c7f82c173f13a7bc4e4a5e82 * Merge from sv-match-alignment branch (excluding alignment-specific document). - add aggregate wave model (not yet complete enough to be added as a true model in a layer, but there's potential) - add play solo mode - add alignment model -- unused in plain SV - fix two plugin leaks - add m3u playlist support (opens all files at once, potentially hazardous) - fix retrieval of pre-encoded URLs - add ability to resample audio files on import, so as to match rates with other files previously loaded; add preference for same - add preliminary support in transform code for range and rate of transform input - reorganise preferences dialog, move dark-background option to preferences, add option for temporary directory location diff -r dab257bd9d2d -r 98ba77e0d897 audioio/AudioCallbackPlaySource.cpp --- a/audioio/AudioCallbackPlaySource.cpp Fri Sep 21 09:13:11 2007 +0000 +++ b/audioio/AudioCallbackPlaySource.cpp Fri Sep 28 13:56:38 2007 +0000 @@ -697,6 +697,20 @@ if (formerPlugin) m_pluginScavenger.claim(formerPlugin); } +void +AudioCallbackPlaySource::setSoloModelSet(std::set s) +{ + m_audioGenerator->setSoloModelSet(s); + clearRingBuffers(); +} + +void +AudioCallbackPlaySource::clearSoloModelSet() +{ + m_audioGenerator->clearSoloModelSet(); + clearRingBuffers(); +} + size_t AudioCallbackPlaySource::getTargetSampleRate() const { @@ -1108,9 +1122,9 @@ #ifdef DEBUG_AUDIO_PLAY_SOURCE std::cout << "Using crappy converter" << std::endl; #endif - src_process(m_crapConverter, &data); + err = src_process(m_crapConverter, &data); } else { - src_process(m_converter, &data); + err = src_process(m_converter, &data); } size_t toCopy = size_t(got * ratio + 0.1); diff -r dab257bd9d2d -r 98ba77e0d897 audioio/AudioCallbackPlaySource.h --- a/audioio/AudioCallbackPlaySource.h Fri Sep 21 09:13:11 2007 +0000 +++ b/audioio/AudioCallbackPlaySource.h Fri Sep 28 13:56:38 2007 +0000 @@ -215,6 +215,17 @@ */ void setAuditioningPlugin(RealTimePluginInstance *plugin); + /** + * Specify that only the given set of models should be played. + */ + void setSoloModelSet(std::sets); + + /** + * Specify that all models should be played as normal (if not + * muted). + */ + void clearSoloModelSet(); + signals: void modelReplaced(); diff -r dab257bd9d2d -r 98ba77e0d897 audioio/AudioGenerator.cpp --- a/audioio/AudioGenerator.cpp Fri Sep 21 09:13:11 2007 +0000 +++ b/audioio/AudioGenerator.cpp Fri Sep 28 13:56:38 2007 +0000 @@ -47,7 +47,8 @@ AudioGenerator::AudioGenerator() : m_sourceSampleRate(0), - m_targetChannelCount(1) + m_targetChannelCount(1), + m_soloing(false) { connect(PlayParameterRepository::getInstance(), SIGNAL(playPluginIdChanged(const Model *, QString)), @@ -352,6 +353,26 @@ return m_pluginBlockSize; } +void +AudioGenerator::setSoloModelSet(std::set s) +{ + QMutexLocker locker(&m_mutex); + + std::cerr << "setting solo set" << std::endl; + + m_soloModelSet = s; + m_soloing = true; +} + +void +AudioGenerator::clearSoloModelSet() +{ + QMutexLocker locker(&m_mutex); + + m_soloModelSet.clear(); + m_soloing = false; +} + size_t AudioGenerator::mixModel(Model *model, size_t startFrame, size_t frameCount, float **buffer, size_t fadeIn, size_t fadeOut) @@ -375,6 +396,15 @@ return frameCount; } + if (m_soloing) { + if (m_soloModelSet.find(model) == m_soloModelSet.end()) { +#ifdef DEBUG_AUDIO_GENERATOR + std::cout << "AudioGenerator::mixModel(" << model << "): not one of the solo'd models" << std::endl; +#endif + return frameCount; + } + } + float gain = parameters->getPlayGain(); float pan = parameters->getPlayPan(); diff -r dab257bd9d2d -r 98ba77e0d897 audioio/AudioGenerator.h --- a/audioio/AudioGenerator.h Fri Sep 21 09:13:11 2007 +0000 +++ b/audioio/AudioGenerator.h Fri Sep 28 13:56:38 2007 +0000 @@ -90,6 +90,17 @@ virtual size_t mixModel(Model *model, size_t startFrame, size_t frameCount, float **buffer, size_t fadeIn = 0, size_t fadeOut = 0); + /** + * Specify that only the given set of models should be played. + */ + virtual void setSoloModelSet(std::sets); + + /** + * Specify that all models should be played as normal (if not + * muted). + */ + virtual void clearSoloModelSet(); + protected slots: void playPluginIdChanged(const Model *, QString); void playPluginConfigurationChanged(const Model *, QString); @@ -98,6 +109,9 @@ size_t m_sourceSampleRate; size_t m_targetChannelCount; + bool m_soloing; + std::set m_soloModelSet; + struct NoteOff { int pitch; diff -r dab257bd9d2d -r 98ba77e0d897 document/Document.cpp --- a/document/Document.cpp Fri Sep 21 09:13:11 2007 +0000 +++ b/document/Document.cpp Fri Sep 28 13:56:38 2007 +0000 @@ -334,6 +334,8 @@ rec.configurationXml = configurationXml; rec.refcount = 0; + outputModelToAdd->setSourceModel(inputModel); + m_models[outputModelToAdd] = rec; emit modelAdded(outputModelToAdd); diff -r dab257bd9d2d -r 98ba77e0d897 document/Document.h --- a/document/Document.h Fri Sep 21 09:13:11 2007 +0000 +++ b/document/Document.h Fri Sep 28 13:56:38 2007 +0000 @@ -221,9 +221,8 @@ /* * Every model that is in use by a layer in the document must be - * found in either m_mainModel, m_derivedModels or - * m_importedModels. We own and control the lifespan of all of - * these models. + * found in either m_mainModel or m_models. We own and control + * the lifespan of all of these models. */ /** diff -r dab257bd9d2d -r 98ba77e0d897 main/MainWindow.cpp --- a/main/MainWindow.cpp Fri Sep 21 09:13:11 2007 +0000 +++ b/main/MainWindow.cpp Fri Sep 28 13:56:38 2007 +0000 @@ -47,8 +47,8 @@ #include "audioio/AudioCallbackPlayTarget.h" #include "audioio/AudioTargetFactory.h" #include "audioio/PlaySpeedRangeMapper.h" -#include "data/fileio/AudioFileReaderFactory.h" #include "data/fileio/DataFileReaderFactory.h" +#include "data/fileio/PlaylistFileReader.h" #include "data/fileio/WavFileWriter.h" #include "data/fileio/CSVFileWriter.h" #include "data/fileio/BZipFileDevice.h" @@ -65,6 +65,9 @@ #include "base/ColourDatabase.h" #include "osc/OSCQueue.h" +//!!! +#include "data/model/AggregateWaveModel.h" + // For version information #include "vamp/vamp.h" #include "vamp-sdk/PluginBase.h" @@ -177,6 +180,14 @@ connect(m_viewManager, SIGNAL(inProgressSelectionChanged()), this, SLOT(inProgressSelectionChanged())); + Preferences::BackgroundMode mode = + Preferences::getInstance()->getBackgroundMode(); + m_initialDarkBackground = m_viewManager->getGlobalDarkBackground(); + if (mode != Preferences::BackgroundFromTheme) { + m_viewManager->setGlobalDarkBackground + (mode == Preferences::DarkBackground); + } + m_descriptionLabel = new QLabel; QScrollArea *scroll = new QScrollArea(frame); @@ -850,15 +861,6 @@ menu->addSeparator(); - action = new QAction(tr("Use Dar&k Background"), this); - action->setStatusTip(tr("Switch between light and dark background colour schemes")); - connect(action, SIGNAL(triggered()), this, SLOT(toggleDarkBackground())); - action->setCheckable(true); - action->setChecked(m_viewManager->getGlobalDarkBackground()); - menu->addAction(action); - - menu->addSeparator(); - action = new QAction(tr("Show &Zoom Wheels"), this); action->setShortcut(tr("Z")); action->setStatusTip(tr("Show thumbwheels for zooming horizontally and vertically")); @@ -1684,9 +1686,21 @@ connect(plAction, SIGNAL(triggered()), this, SLOT(playLoopToggled())); connect(this, SIGNAL(canPlay(bool)), plAction, SLOT(setEnabled(bool))); + QAction *soAction = toolbar->addAction(il.load("solo"), + tr("Solo Current Pane")); + soAction->setCheckable(true); + soAction->setChecked(m_viewManager->getPlaySoloMode()); + soAction->setShortcut(tr("o")); + soAction->setStatusTip(tr("Solo the current pane during playback")); + connect(m_viewManager, SIGNAL(playSoloModeChanged(bool)), + soAction, SLOT(setChecked(bool))); + connect(soAction, SIGNAL(triggered()), this, SLOT(playSoloToggled())); + connect(this, SIGNAL(canPlay(bool)), soAction, SLOT(setEnabled(bool))); + m_keyReference->registerShortcut(playAction); m_keyReference->registerShortcut(psAction); m_keyReference->registerShortcut(plAction); + m_keyReference->registerShortcut(soAction); m_keyReference->registerShortcut(m_rwdAction); m_keyReference->registerShortcut(m_ffwdAction); m_keyReference->registerShortcut(rwdStartAction); @@ -1695,6 +1709,7 @@ menu->addAction(playAction); menu->addAction(psAction); menu->addAction(plAction); + menu->addAction(soAction); menu->addSeparator(); menu->addAction(m_rwdAction); menu->addAction(m_ffwdAction); @@ -1706,6 +1721,7 @@ m_rightButtonPlaybackMenu->addAction(playAction); m_rightButtonPlaybackMenu->addAction(psAction); m_rightButtonPlaybackMenu->addAction(plAction); + m_rightButtonPlaybackMenu->addAction(soAction); m_rightButtonPlaybackMenu->addSeparator(); m_rightButtonPlaybackMenu->addAction(m_rwdAction); m_rightButtonPlaybackMenu->addAction(m_ffwdAction); @@ -1996,10 +2012,72 @@ } void +MainWindow::playSoloToggled() +{ + QAction *action = dynamic_cast(sender()); + + if (action) { + m_viewManager->setPlaySoloMode(action->isChecked()); + } else { + m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode()); + } + + if (!m_viewManager->getPlaySoloMode()) { + m_viewManager->setPlaybackModel(0); + if (m_playSource) { + m_playSource->clearSoloModelSet(); + } + } +} + +void MainWindow::currentPaneChanged(Pane *p) { updateMenuStates(); updateVisibleRangeDisplay(p); + + if (!p) return; + + if (!(m_viewManager && + m_playSource && + m_viewManager->getPlaySoloMode())) { + if (m_viewManager) m_viewManager->setPlaybackModel(0); + return; + } + + Model *prevPlaybackModel = m_viewManager->getPlaybackModel(); + + std::set soloModels; + + for (int i = 0; i < p->getLayerCount(); ++i) { + Layer *layer = p->getLayer(i); + if (dynamic_cast(layer)) { + continue; + } + if (layer && layer->getModel()) { + Model *model = layer->getModel(); + if (dynamic_cast(model)) { + m_viewManager->setPlaybackModel(model); + } + soloModels.insert(model); + } + } + + RangeSummarisableTimeValueModel *a = + dynamic_cast(prevPlaybackModel); + RangeSummarisableTimeValueModel *b = + dynamic_cast(m_viewManager-> + getPlaybackModel()); + + m_playSource->setSoloModelSet(soloModels); + + if (a && b && (a != b)) { + int frame = m_playSource->getCurrentPlayingFrame(); + //!!! I don't really believe that these functions are the right way around + int rframe = a->alignFromReference(frame); + int bframe = b->alignToReference(rframe); + if (m_playSource->isPlaying()) m_playSource->play(bframe); + } } void @@ -2680,7 +2758,13 @@ m_openingAudioFile = true; - WaveFileModel *newModel = new WaveFileModel(path, location); + size_t rate = 0; + + if (Preferences::getInstance()->getResampleOnLoad()) { + rate = m_playSource->getSourceSampleRate(); + } + + WaveFileModel *newModel = new WaveFileModel(path, location, rate); if (!newModel->isOK()) { delete newModel; @@ -2784,9 +2868,57 @@ } m_openingAudioFile = false; + currentPaneChanged(m_paneStack->getCurrentPane()); + return FileOpenSucceeded; } +MainWindow::FileOpenStatus +MainWindow::openPlaylistFile(QString path, AudioFileOpenMode mode) +{ + return openPlaylistFile(path, path, mode); +} + +MainWindow::FileOpenStatus +MainWindow::openPlaylistFile(QString path, QString location, AudioFileOpenMode mode) +{ + if (!(QFileInfo(path).exists() && + QFileInfo(path).isFile() && + QFileInfo(path).isReadable())) { + return FileOpenFailed; + } + + std::set extensions; + PlaylistFileReader::getSupportedExtensions(extensions); + QString extension = QFileInfo(path).suffix(); + if (extensions.find(extension) == extensions.end()) return FileOpenFailed; + + PlaylistFileReader reader(path); + if (!reader.isOK()) return FileOpenFailed; + + PlaylistFileReader::Playlist playlist = reader.load(); + + bool someSuccess = false; + + for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin(); + i != playlist.end(); ++i) { + + FileOpenStatus status = openURL(*i, mode); + + if (status == FileOpenCancelled) { + return FileOpenCancelled; + } + + if (status == FileOpenSucceeded) { + someSuccess = true; + mode = CreateAdditionalModel; + } + } + + if (someSuccess) return FileOpenSucceeded; + else return FileOpenFailed; +} + void MainWindow::createPlayTarget() { @@ -2965,12 +3097,15 @@ } else { - if (openAudioFile(path, AskUser) == FileOpenFailed) { - - if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { - - QMessageBox::critical(this, tr("Failed to open file"), - tr("File \"%1\" could not be opened").arg(path)); + if (openPlaylistFile(path, AskUser) == FileOpenFailed) { + + if (openAudioFile(path, AskUser) == FileOpenFailed) { + + if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { + + QMessageBox::critical(this, tr("Failed to open file"), + tr("File \"%1\" could not be opened").arg(path)); + } } } } @@ -3033,31 +3168,38 @@ } else { - if (openAudioFile(path, AskUser) == FileOpenFailed) { - - bool canImportLayer = (getMainModel() != 0 && - m_paneStack != 0 && - m_paneStack->getCurrentPane() != 0); - - if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { - - QMessageBox::critical(this, tr("Failed to open file"), - tr("File \"%1\" could not be opened").arg(path)); + if (openPlaylistFile(path, AskUser) == FileOpenFailed) { + + if (openAudioFile(path, AskUser) == FileOpenFailed) { + + bool canImportLayer = (getMainModel() != 0 && + m_paneStack != 0 && + m_paneStack->getCurrentPane() != 0); + + if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { + + QMessageBox::critical(this, tr("Failed to open file"), + tr("File \"%1\" could not be opened").arg(path)); + } } } } } MainWindow::FileOpenStatus -MainWindow::openURL(QUrl url) +MainWindow::openURL(QUrl url, AudioFileOpenMode mode) { if (url.scheme().toLower() == "file") { - return openSomeFile(url.toLocalFile()); + + return openSomeFile(url.toLocalFile(), mode); + } else if (!RemoteFile::canHandleScheme(url)) { + QMessageBox::critical(this, tr("Unsupported scheme in URL"), tr("The URL scheme \"%1\" is not supported") .arg(url.scheme())); return FileOpenFailed; + } else { RemoteFile rf(url); rf.wait(); @@ -3068,7 +3210,8 @@ return FileOpenFailed; } FileOpenStatus status; - if ((status = openSomeFile(rf.getLocalFilename(), url.toString())) != + if ((status = openSomeFile(rf.getLocalFilename(), url.toString(), + mode)) != FileOpenSucceeded) { rf.deleteLocalFile(); } @@ -3077,6 +3220,49 @@ } MainWindow::FileOpenStatus +MainWindow::openURL(QString ustr, AudioFileOpenMode mode) +{ + // This function is used when we don't know whether the string is + // an encoded or human-readable url + + QUrl url(ustr); + + if (url.scheme().toLower() == "file") { + + return openSomeFile(url.toLocalFile(), mode); + + } else if (!RemoteFile::canHandleScheme(url)) { + + QMessageBox::critical(this, tr("Unsupported scheme in URL"), + tr("The URL scheme \"%1\" is not supported") + .arg(url.scheme())); + return FileOpenFailed; + + } else { + RemoteFile rf(url); + rf.wait(); + if (!rf.isOK()) { + // rf was created on the assumption that ustr was + // human-readable. Let's try again, this time assuming it + // was already encoded. + std::cerr << "MainWindow::openURL: Failed to retrieve URL \"" + << ustr.toStdString() << "\" as human-readable URL; " + << "trying again treating it as encoded URL" + << std::endl; + url.setEncodedUrl(ustr.toAscii()); + return openURL(url, mode); + } + + FileOpenStatus status; + if ((status = openSomeFile(rf.getLocalFilename(), ustr, mode)) != + FileOpenSucceeded) { + rf.deleteLocalFile(); + } + return status; + } +} + +MainWindow::FileOpenStatus MainWindow::openSomeFile(QString path, AudioFileOpenMode mode) { return openSomeFile(path, path, mode); @@ -3092,7 +3278,9 @@ m_paneStack != 0 && m_paneStack->getCurrentPane() != 0); - if ((status = openAudioFile(path, location, mode)) != FileOpenFailed) { + if ((status = openPlaylistFile(path, location, mode)) != FileOpenFailed) { + return status; + } else if ((status = openAudioFile(path, location, mode)) != FileOpenFailed) { return status; } else if ((status = openSessionFile(path, location)) != FileOpenFailed) { return status; @@ -3546,23 +3734,6 @@ } void -MainWindow::toggleDarkBackground() -{ - if (!m_viewManager) return; - - m_viewManager->setGlobalDarkBackground - (!m_viewManager->getGlobalDarkBackground()); - - if (m_viewManager->getGlobalDarkBackground()) { - m_panLayer->setBaseColour - (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green"))); - } else { - m_panLayer->setBaseColour - (ColourDatabase::getInstance()->getColourIndex(tr("Green"))); - } -} - -void MainWindow::preferenceChanged(PropertyContainer::PropertyName name) { if (name == "Property Box Layout") { @@ -3574,7 +3745,24 @@ m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); } } - } + } else if (name == "Background Mode" && m_viewManager) { + Preferences::BackgroundMode mode = + Preferences::getInstance()->getBackgroundMode(); + if (mode == Preferences::BackgroundFromTheme) { + m_viewManager->setGlobalDarkBackground(m_initialDarkBackground); + } else if (mode == Preferences::DarkBackground) { + m_viewManager->setGlobalDarkBackground(true); + } else { + m_viewManager->setGlobalDarkBackground(false); + } + if (m_viewManager->getGlobalDarkBackground()) { + m_panLayer->setBaseColour + (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green"))); + } else { + m_panLayer->setBaseColour + (ColourDatabase::getInstance()->getColourIndex(tr("Green"))); + } + } } void @@ -4413,6 +4601,9 @@ MainWindow::modelAboutToBeDeleted(Model *model) { // std::cerr << "MainWindow::modelAboutToBeDeleted(" << model << ")" << std::endl; + if (model == m_viewManager->getPlaybackModel()) { + m_viewManager->setPlaybackModel(0); + } m_playSource->removeModel(model); FFTDataServer::modelAboutToBeDeleted(model); } @@ -4650,6 +4841,19 @@ } } + } else if (message.getMethod() == "solo") { + + if (message.getArgCount() == 1 && + message.getArg(0).canConvert(QVariant::String)) { + + QString str = message.getArg(0).toString(); + if (str == "on") { + m_viewManager->setPlaySoloMode(true); + } else if (str == "off") { + m_viewManager->setPlaySoloMode(false); + } + } + } else if (message.getMethod() == "select" || message.getMethod() == "addselect") { diff -r dab257bd9d2d -r 98ba77e0d897 main/MainWindow.h --- a/main/MainWindow.h Fri Sep 21 09:13:11 2007 +0000 +++ b/main/MainWindow.h Fri Sep 28 13:56:38 2007 +0000 @@ -79,9 +79,11 @@ FileOpenStatus openSomeFile(QString path, AudioFileOpenMode = AskUser); FileOpenStatus openAudioFile(QString path, AudioFileOpenMode = AskUser); + FileOpenStatus openPlaylistFile(QString path, AudioFileOpenMode = AskUser); FileOpenStatus openLayerFile(QString path); FileOpenStatus openSessionFile(QString path); - FileOpenStatus openURL(QUrl url); + FileOpenStatus openURL(QUrl url, AudioFileOpenMode = AskUser); + FileOpenStatus openURL(QString url, AudioFileOpenMode = AskUser); bool saveSessionFile(QString path); bool commitData(bool mayAskUser); // on session shutdown @@ -155,7 +157,6 @@ void toggleZoomWheels(); void togglePropertyBoxes(); void toggleStatusBar(); - void toggleDarkBackground(); void play(); void ffwd(); @@ -172,6 +173,7 @@ void playLoopToggled(); void playSelectionToggled(); + void playSoloToggled(); void playSpeedChanged(int); void playSharpenToggled(); void playMonoToggled(); @@ -302,6 +304,8 @@ QPointer m_preferencesDialog; QPointer m_layerTreeView; + bool m_initialDarkBackground; + KeyReference *m_keyReference; WaveFileModel *getMainModel(); @@ -412,6 +416,8 @@ AudioFileOpenMode = AskUser); FileOpenStatus openAudioFile(QString path, QString location, AudioFileOpenMode = AskUser); + FileOpenStatus openPlaylistFile(QString path, QString location, + AudioFileOpenMode = AskUser); FileOpenStatus openLayerFile(QString path, QString location); FileOpenStatus openSessionFile(QString path, QString location); diff -r dab257bd9d2d -r 98ba77e0d897 main/PreferencesDialog.cpp --- a/main/PreferencesDialog.cpp Fri Sep 21 09:13:11 2007 +0000 +++ b/main/PreferencesDialog.cpp Fri Sep 28 13:56:38 2007 +0000 @@ -26,12 +26,18 @@ #include #include #include +#include +#include +#include +#include #include "widgets/WindowTypeSelector.h" +#include "widgets/IconLoader.h" #include "base/Preferences.h" PreferencesDialog::PreferencesDialog(QWidget *parent, Qt::WFlags flags) : - QDialog(parent, flags) + QDialog(parent, flags), + m_changesOnRestart(false) { setWindowTitle(tr("Sonic Visualiser: Application Preferences")); @@ -39,18 +45,20 @@ QGridLayout *grid = new QGridLayout; setLayout(grid); + + QTabWidget *tab = new QTabWidget; + grid->addWidget(tab, 0, 0); - QGroupBox *groupBox = new QGroupBox; - groupBox->setTitle(tr("Application Preferences")); - grid->addWidget(groupBox, 0, 0); - - QGridLayout *subgrid = new QGridLayout; - groupBox->setLayout(subgrid); + tab->setTabPosition(QTabWidget::North); // Create this first, as slots that get called from the ctor will // refer to it m_applyButton = new QPushButton(tr("Apply")); + // Create all the preference widgets first, then create the + // individual tab widgets and place the preferences in their + // appropriate places in one go afterwards + int min, max, deflt, i; m_windowType = WindowType(prefs->getPropertyRangeAndValue @@ -117,6 +125,44 @@ connect(resampleQuality, SIGNAL(currentIndexChanged(int)), this, SLOT(resampleQualityChanged(int))); + QCheckBox *resampleOnLoad = new QCheckBox; + m_resampleOnLoad = prefs->getResampleOnLoad(); + resampleOnLoad->setCheckState(m_resampleOnLoad ? Qt::Checked : + Qt::Unchecked); + connect(resampleOnLoad, SIGNAL(stateChanged(int)), + this, SLOT(resampleOnLoadChanged(int))); + + m_tempDirRootEdit = new QLineEdit; + QString dir = prefs->getTemporaryDirectoryRoot(); + m_tempDirRoot = dir; + dir.replace("$HOME", tr("")); + m_tempDirRootEdit->setText(dir); + m_tempDirRootEdit->setReadOnly(true); + QPushButton *tempDirButton = new QPushButton; + tempDirButton->setIcon(IconLoader().load("fileopen")); + connect(tempDirButton, SIGNAL(clicked()), + this, SLOT(tempDirButtonClicked())); + tempDirButton->setFixedSize(QSize(24, 24)); + + QComboBox *bgMode = new QComboBox; + int bg = prefs->getPropertyRangeAndValue("Background Mode", &min, &max, + &deflt); + m_backgroundMode = bg; + for (i = min; i <= max; ++i) { + bgMode->addItem(prefs->getPropertyValueLabel("Background Mode", i)); + } + bgMode->setCurrentIndex(bg); + + connect(bgMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(backgroundModeChanged(int))); + + // General tab + + QFrame *frame = new QFrame; + + QGridLayout *subgrid = new QGridLayout; + frame->setLayout(subgrid); + int row = 0; subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel @@ -125,15 +171,43 @@ subgrid->addWidget(propertyLayout, row++, 1, 1, 2); subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel - ("Tuning Frequency"))), + ("Background Mode"))), row, 0); - subgrid->addWidget(frequency, row++, 1, 1, 2); + subgrid->addWidget(bgMode, row++, 1, 1, 2); + + subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel + ("Resample On Load"))), + row, 0); + subgrid->addWidget(resampleOnLoad, row++, 1, 1, 1); subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel ("Resample Quality"))), row, 0); subgrid->addWidget(resampleQuality, row++, 1, 1, 2); + subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel + ("Temporary Directory Root"))), + row, 0); + subgrid->addWidget(m_tempDirRootEdit, row, 1, 1, 1); + subgrid->addWidget(tempDirButton, row, 2, 1, 1); + row++; + + subgrid->setRowStretch(row, 10); + + tab->addTab(frame, tr("&General")); + + // Analysis tab + + frame = new QFrame; + subgrid = new QGridLayout; + frame->setLayout(subgrid); + row = 0; + + subgrid->addWidget(new QLabel(tr("%1:").arg(prefs->getPropertyLabel + ("Tuning Frequency"))), + row, 0); + subgrid->addWidget(frequency, row++, 1, 1, 2); + subgrid->addWidget(new QLabel(prefs->getPropertyLabel ("Spectrogram Smoothing")), row, 0); @@ -146,6 +220,10 @@ subgrid->setRowStretch(row, 10); row++; + subgrid->setRowStretch(row, 10); + + tab->addTab(frame, tr("&Analysis")); + QDialogButtonBox *bb = new QDialogButtonBox(Qt::Horizontal); grid->addWidget(bb, 1, 0); @@ -202,6 +280,41 @@ } void +PreferencesDialog::resampleOnLoadChanged(int state) +{ + m_resampleOnLoad = (state == Qt::Checked); + m_applyButton->setEnabled(true); + m_changesOnRestart = true; +} + +void +PreferencesDialog::tempDirRootChanged(QString r) +{ + m_tempDirRoot = r; + m_applyButton->setEnabled(true); +} + +void +PreferencesDialog::tempDirButtonClicked() +{ + QString dir = QFileDialog::getExistingDirectory + (this, tr("Select a directory to create cache subdirectory in"), + m_tempDirRoot); + if (dir == "") return; + m_tempDirRootEdit->setText(dir); + tempDirRootChanged(dir); + m_changesOnRestart = true; +} + +void +PreferencesDialog::backgroundModeChanged(int mode) +{ + m_backgroundMode = mode; + m_applyButton->setEnabled(true); + m_changesOnRestart = true; +} + +void PreferencesDialog::okClicked() { applyClicked(); @@ -219,7 +332,17 @@ (m_propertyLayout)); prefs->setTuningFrequency(m_tuningFrequency); prefs->setResampleQuality(m_resampleQuality); + prefs->setResampleOnLoad(m_resampleOnLoad); + prefs->setTemporaryDirectoryRoot(m_tempDirRoot); + prefs->setBackgroundMode(Preferences::BackgroundMode(m_backgroundMode)); + m_applyButton->setEnabled(false); + + if (m_changesOnRestart) { + QMessageBox::information(this, tr("Preferences"), + tr("One or more of the application preferences you have changed may not take full effect until Sonic Visualiser is restarted.\nPlease exit and restart the application now if you want these changes to take effect immediately.")); + m_changesOnRestart = false; + } } void diff -r dab257bd9d2d -r 98ba77e0d897 main/PreferencesDialog.h --- a/main/PreferencesDialog.h Fri Sep 21 09:13:11 2007 +0000 +++ b/main/PreferencesDialog.h Fri Sep 28 13:56:38 2007 +0000 @@ -22,6 +22,7 @@ class WindowTypeSelector; class QPushButton; +class QLineEdit; class PreferencesDialog : public QDialog { @@ -40,6 +41,11 @@ void propertyLayoutChanged(int layout); void tuningFrequencyChanged(double freq); void resampleQualityChanged(int quality); + void resampleOnLoadChanged(int state); + void tempDirRootChanged(QString root); + void backgroundModeChanged(int mode); + + void tempDirButtonClicked(); void okClicked(); void applyClicked(); @@ -48,12 +54,19 @@ protected: WindowTypeSelector *m_windowTypeSelector; QPushButton *m_applyButton; + + QLineEdit *m_tempDirRootEdit; WindowType m_windowType; - int m_spectrogramSmoothing; - int m_propertyLayout; + int m_spectrogramSmoothing; + int m_propertyLayout; float m_tuningFrequency; - int m_resampleQuality; + int m_resampleQuality; + bool m_resampleOnLoad; + QString m_tempDirRoot; + int m_backgroundMode; + + bool m_changesOnRestart; }; #endif diff -r dab257bd9d2d -r 98ba77e0d897 main/main.cpp --- a/main/main.cpp Fri Sep 21 09:13:11 2007 +0000 +++ b/main/main.cpp Fri Sep 28 13:56:38 2007 +0000 @@ -300,7 +300,7 @@ if (i->startsWith("http:") || i->startsWith("ftp:")) { std::cerr << "opening URL: \"" << i->toStdString() << "\"..." << std::endl; - status = gui.openURL(QUrl(*i)); + status = gui.openURL(*i); continue; } @@ -340,7 +340,10 @@ (&gui, QMessageBox::tr("Failed to open file"), QMessageBox::tr("File \"%1\" could not be opened").arg(path)); } - } + } + + + /* TipDialog tipDialog; if (tipDialog.isOK()) { diff -r dab257bd9d2d -r 98ba77e0d897 sonic-visualiser.qrc --- a/sonic-visualiser.qrc Fri Sep 21 09:13:11 2007 +0000 +++ b/sonic-visualiser.qrc Fri Sep 28 13:56:38 2007 +0000 @@ -16,6 +16,7 @@ icons/rewind-start.png icons/playselection.png icons/playloop.png + icons/solo.png icons/fader_background.png icons/fader_knob.png icons/fader_knob_red.png