Chris@246: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@246: Chris@246: /* Chris@246: Sonic Visualiser Chris@246: An audio file viewer and annotation editor. Chris@246: Centre for Digital Music, Queen Mary, University of London. Chris@246: This file copyright 2006-2007 Chris Cannam and QMUL. Chris@246: Chris@246: This program is free software; you can redistribute it and/or Chris@246: modify it under the terms of the GNU General Public License as Chris@246: published by the Free Software Foundation; either version 2 of the Chris@246: License, or (at your option) any later version. See the file Chris@246: COPYING included with this distribution for more information. Chris@246: */ Chris@246: Chris@246: #include "MainWindow.h" Chris@246: #include "data/osc/OSCQueue.h" Chris@246: Chris@246: #include "layer/WaveformLayer.h" Chris@246: #include "view/ViewManager.h" Chris@246: #include "view/Pane.h" Chris@246: #include "view/PaneStack.h" Chris@246: #include "data/model/WaveFileModel.h" Chris@246: #include "base/CommandHistory.h" Chris@246: #include "audioio/AudioCallbackPlaySource.h" Chris@246: #include "audioio/AudioCallbackPlayTarget.h" Chris@246: #include "framework/Document.h" Chris@246: #include "data/fileio/WavFileWriter.h" Chris@246: #include "plugin/transform/TransformFactory.h" Chris@246: #include "widgets/Fader.h" Chris@246: #include "widgets/AudioDial.h" Chris@246: Chris@246: #include Chris@246: Chris@246: void Chris@246: MainWindow::handleOSCMessage(const OSCMessage &message) Chris@246: { Chris@246: std::cerr << "MainWindow::handleOSCMessage: thread id = " Chris@246: << QThread::currentThreadId() << std::endl; Chris@246: Chris@246: // This large function should really be abstracted out. Chris@246: Chris@246: if (message.getMethod() == "open") { Chris@246: Chris@246: if (message.getArgCount() == 1 && Chris@246: message.getArg(0).canConvert(QVariant::String)) { Chris@246: QString path = message.getArg(0).toString(); Chris@246: if (open(path, ReplaceMainModel) != FileOpenSucceeded) { Chris@246: std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" Chris@246: << path.toStdString() << "\"" << std::endl; Chris@246: } Chris@246: //!!! we really need to spin here and not return until the Chris@246: // file has been completely decoded... Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "openadditional") { Chris@246: Chris@246: if (message.getArgCount() == 1 && Chris@246: message.getArg(0).canConvert(QVariant::String)) { Chris@246: QString path = message.getArg(0).toString(); Chris@246: if (open(path, CreateAdditionalModel) != FileOpenSucceeded) { Chris@246: std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" Chris@246: << path.toStdString() << "\"" << std::endl; Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "recent" || Chris@246: message.getMethod() == "last") { Chris@246: Chris@246: int n = 0; Chris@246: if (message.getMethod() == "recent" && Chris@246: message.getArgCount() == 1 && Chris@246: message.getArg(0).canConvert(QVariant::Int)) { Chris@246: n = message.getArg(0).toInt() - 1; Chris@246: } Chris@246: std::vector recent = m_recentFiles.getRecent(); Chris@246: if (n >= 0 && n < int(recent.size())) { Chris@246: if (open(recent[n], ReplaceMainModel) != FileOpenSucceeded) { Chris@246: std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" Chris@246: << recent[n].toStdString() << "\"" << std::endl; Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "save") { Chris@246: Chris@246: QString path; Chris@246: if (message.getArgCount() == 1 && Chris@246: message.getArg(0).canConvert(QVariant::String)) { Chris@246: path = message.getArg(0).toString(); Chris@246: if (QFileInfo(path).exists()) { Chris@246: std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in save" << std::endl; Chris@246: } else { Chris@246: saveSessionFile(path); Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "export") { Chris@246: Chris@246: QString path; Chris@246: if (getMainModel()) { Chris@246: if (message.getArgCount() == 1 && Chris@246: message.getArg(0).canConvert(QVariant::String)) { Chris@246: path = message.getArg(0).toString(); Chris@246: if (QFileInfo(path).exists()) { Chris@246: std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in export" << std::endl; Chris@246: } else { Chris@246: WavFileWriter writer(path, Chris@246: getMainModel()->getSampleRate(), Chris@246: getMainModel()->getChannelCount()); Chris@246: MultiSelection ms = m_viewManager->getSelection(); Chris@246: if (!ms.getSelections().empty()) { Chris@246: writer.writeModel(getMainModel(), &ms); Chris@246: } else { Chris@246: writer.writeModel(getMainModel()); Chris@246: } Chris@246: } Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "jump" || Chris@246: message.getMethod() == "play") { Chris@246: Chris@246: if (getMainModel()) { Chris@246: Chris@246: unsigned long frame = m_viewManager->getPlaybackFrame(); Chris@246: bool selection = false; Chris@246: bool play = (message.getMethod() == "play"); Chris@246: Chris@246: if (message.getArgCount() == 1) { Chris@246: Chris@246: if (message.getArg(0).canConvert(QVariant::String) && Chris@246: message.getArg(0).toString() == "selection") { Chris@246: Chris@246: selection = true; Chris@246: Chris@246: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@246: message.getArg(0).toString() == "end") { Chris@246: Chris@246: frame = getMainModel()->getEndFrame(); Chris@246: Chris@246: } else if (message.getArg(0).canConvert(QVariant::Double)) { Chris@246: Chris@246: double time = message.getArg(0).toDouble(); Chris@246: if (time < 0.0) time = 0.0; Chris@246: Chris@246: frame = lrint(time * getMainModel()->getSampleRate()); Chris@246: } Chris@246: } Chris@246: Chris@246: if (frame > getMainModel()->getEndFrame()) { Chris@246: frame = getMainModel()->getEndFrame(); Chris@246: } Chris@246: Chris@246: if (play) { Chris@246: m_viewManager->setPlaySelectionMode(selection); Chris@246: } Chris@246: Chris@246: if (selection) { Chris@246: MultiSelection::SelectionList sl = m_viewManager->getSelections(); Chris@246: if (!sl.empty()) { Chris@246: frame = sl.begin()->getStartFrame(); Chris@246: } Chris@246: } Chris@246: Chris@246: m_viewManager->setPlaybackFrame(frame); Chris@246: Chris@246: if (play && !m_playSource->isPlaying()) { Chris@246: m_playSource->play(frame); Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "stop") { Chris@246: Chris@246: if (m_playSource->isPlaying()) m_playSource->stop(); Chris@246: Chris@246: } else if (message.getMethod() == "loop") { Chris@246: Chris@246: if (message.getArgCount() == 1 && Chris@246: message.getArg(0).canConvert(QVariant::String)) { Chris@246: Chris@246: QString str = message.getArg(0).toString(); Chris@246: if (str == "on") { Chris@246: m_viewManager->setPlayLoopMode(true); Chris@246: } else if (str == "off") { Chris@246: m_viewManager->setPlayLoopMode(false); Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "solo") { Chris@246: Chris@246: if (message.getArgCount() == 1 && Chris@246: message.getArg(0).canConvert(QVariant::String)) { Chris@246: Chris@246: QString str = message.getArg(0).toString(); Chris@246: if (str == "on") { Chris@246: m_viewManager->setPlaySoloMode(true); Chris@246: } else if (str == "off") { Chris@246: m_viewManager->setPlaySoloMode(false); Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "select" || Chris@246: message.getMethod() == "addselect") { Chris@246: Chris@246: if (getMainModel()) { Chris@246: Chris@246: int f0 = getMainModel()->getStartFrame(); Chris@246: int f1 = getMainModel()->getEndFrame(); Chris@246: Chris@246: bool done = false; Chris@246: Chris@246: if (message.getArgCount() == 2 && Chris@246: message.getArg(0).canConvert(QVariant::Double) && Chris@246: message.getArg(1).canConvert(QVariant::Double)) { Chris@246: Chris@246: double t0 = message.getArg(0).toDouble(); Chris@246: double t1 = message.getArg(1).toDouble(); Chris@246: if (t1 < t0) { double temp = t0; t0 = t1; t1 = temp; } Chris@246: if (t0 < 0.0) t0 = 0.0; Chris@246: if (t1 < 0.0) t1 = 0.0; Chris@246: Chris@246: f0 = lrint(t0 * getMainModel()->getSampleRate()); Chris@246: f1 = lrint(t1 * getMainModel()->getSampleRate()); Chris@246: Chris@246: Pane *pane = m_paneStack->getCurrentPane(); Chris@246: Layer *layer = 0; Chris@246: if (pane) layer = pane->getSelectedLayer(); Chris@246: if (layer) { Chris@246: size_t resolution; Chris@246: layer->snapToFeatureFrame(pane, f0, resolution, Chris@246: Layer::SnapLeft); Chris@246: layer->snapToFeatureFrame(pane, f1, resolution, Chris@246: Layer::SnapRight); Chris@246: } Chris@246: Chris@246: } else if (message.getArgCount() == 1 && Chris@246: message.getArg(0).canConvert(QVariant::String)) { Chris@246: Chris@246: QString str = message.getArg(0).toString(); Chris@246: if (str == "none") { Chris@246: m_viewManager->clearSelections(); Chris@246: done = true; Chris@246: } Chris@246: } Chris@246: Chris@246: if (!done) { Chris@246: if (message.getMethod() == "select") { Chris@246: m_viewManager->setSelection(Selection(f0, f1)); Chris@246: } else { Chris@246: m_viewManager->addSelection(Selection(f0, f1)); Chris@246: } Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "add") { Chris@246: Chris@246: if (getMainModel()) { Chris@246: Chris@246: if (message.getArgCount() >= 1 && Chris@246: message.getArg(0).canConvert(QVariant::String)) { Chris@246: Chris@246: int channel = -1; Chris@246: if (message.getArgCount() == 2 && Chris@246: message.getArg(0).canConvert(QVariant::Int)) { Chris@246: channel = message.getArg(0).toInt(); Chris@246: if (channel < -1 || Chris@246: channel > int(getMainModel()->getChannelCount())) { Chris@246: std::cerr << "WARNING: MainWindow::handleOSCMessage: channel " Chris@246: << channel << " out of range" << std::endl; Chris@246: channel = -1; Chris@246: } Chris@246: } Chris@246: Chris@246: QString str = message.getArg(0).toString(); Chris@246: Chris@246: LayerFactory::LayerType type = Chris@246: LayerFactory::getInstance()->getLayerTypeForName(str); Chris@246: Chris@246: if (type == LayerFactory::UnknownLayer) { Chris@246: std::cerr << "WARNING: MainWindow::handleOSCMessage: unknown layer " Chris@246: << "type " << str.toStdString() << std::endl; Chris@246: } else { Chris@246: Chris@246: LayerConfiguration configuration(type, Chris@246: getMainModel(), Chris@246: channel); Chris@246: Chris@246: addPane(configuration, Chris@246: tr("Add %1 Pane") Chris@246: .arg(LayerFactory::getInstance()-> Chris@246: getLayerPresentationName(type))); Chris@246: } Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "undo") { Chris@246: Chris@246: CommandHistory::getInstance()->undo(); Chris@246: Chris@246: } else if (message.getMethod() == "redo") { Chris@246: Chris@246: CommandHistory::getInstance()->redo(); Chris@246: Chris@246: } else if (message.getMethod() == "set") { Chris@246: Chris@246: if (message.getArgCount() == 2 && Chris@246: message.getArg(0).canConvert(QVariant::String) && Chris@246: message.getArg(1).canConvert(QVariant::Double)) { Chris@246: Chris@246: QString property = message.getArg(0).toString(); Chris@246: float value = (float)message.getArg(1).toDouble(); Chris@246: Chris@246: if (property == "gain") { Chris@246: if (value < 0.0) value = 0.0; Chris@246: m_fader->setValue(value); Chris@246: if (m_playTarget) m_playTarget->setOutputGain(value); Chris@246: } else if (property == "speedup") { Chris@246: m_playSpeed->setMappedValue(value); Chris@246: } else if (property == "overlays") { Chris@246: if (value < 0.5) { Chris@246: m_viewManager->setOverlayMode(ViewManager::NoOverlays); Chris@246: } else if (value < 1.5) { Chris@246: m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); Chris@246: } else if (value < 2.5) { Chris@246: m_viewManager->setOverlayMode(ViewManager::StandardOverlays); Chris@246: } else { Chris@246: m_viewManager->setOverlayMode(ViewManager::AllOverlays); Chris@246: } Chris@246: } else if (property == "zoomwheels") { Chris@246: m_viewManager->setZoomWheelsEnabled(value > 0.5); Chris@246: } else if (property == "propertyboxes") { Chris@246: bool toggle = ((value < 0.5) != Chris@246: (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks)); Chris@246: if (toggle) togglePropertyBoxes(); Chris@246: } Chris@246: Chris@246: } else { Chris@246: PropertyContainer *container = 0; Chris@246: Pane *pane = m_paneStack->getCurrentPane(); Chris@246: if (pane && Chris@246: message.getArgCount() == 3 && Chris@246: message.getArg(0).canConvert(QVariant::String) && Chris@246: message.getArg(1).canConvert(QVariant::String) && Chris@246: message.getArg(2).canConvert(QVariant::String)) { Chris@246: if (message.getArg(0).toString() == "pane") { Chris@246: container = pane->getPropertyContainer(0); Chris@246: } else if (message.getArg(0).toString() == "layer") { Chris@246: container = pane->getSelectedLayer(); Chris@246: } Chris@246: } Chris@246: if (container) { Chris@246: QString nameString = message.getArg(1).toString(); Chris@246: QString valueString = message.getArg(2).toString(); Chris@246: container->setPropertyWithCommand(nameString, valueString); Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "setcurrent") { Chris@246: Chris@246: int paneIndex = -1, layerIndex = -1; Chris@246: bool wantLayer = false; Chris@246: Chris@246: if (message.getArgCount() >= 1 && Chris@246: message.getArg(0).canConvert(QVariant::Int)) { Chris@246: Chris@246: paneIndex = message.getArg(0).toInt() - 1; Chris@246: Chris@246: if (message.getArgCount() >= 2 && Chris@246: message.getArg(1).canConvert(QVariant::Int)) { Chris@246: wantLayer = true; Chris@246: layerIndex = message.getArg(1).toInt() - 1; Chris@246: } Chris@246: } Chris@246: Chris@246: if (paneIndex >= 0 && paneIndex < m_paneStack->getPaneCount()) { Chris@246: Pane *pane = m_paneStack->getPane(paneIndex); Chris@246: m_paneStack->setCurrentPane(pane); Chris@246: if (layerIndex >= 0 && layerIndex < pane->getLayerCount()) { Chris@246: Layer *layer = pane->getLayer(layerIndex); Chris@246: m_paneStack->setCurrentLayer(pane, layer); Chris@246: } else if (wantLayer && layerIndex == -1) { Chris@246: m_paneStack->setCurrentLayer(pane, 0); Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "delete") { Chris@246: Chris@246: if (message.getArgCount() == 1 && Chris@246: message.getArg(0).canConvert(QVariant::String)) { Chris@246: Chris@246: QString target = message.getArg(0).toString(); Chris@246: Chris@246: if (target == "pane") { Chris@246: Chris@246: deleteCurrentPane(); Chris@246: Chris@246: } else if (target == "layer") { Chris@246: Chris@246: deleteCurrentLayer(); Chris@246: Chris@246: } else { Chris@246: Chris@246: std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown delete target " << target.toStdString() << std::endl; Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "zoom") { Chris@246: Chris@246: if (message.getArgCount() == 1) { Chris@246: if (message.getArg(0).canConvert(QVariant::String) && Chris@246: message.getArg(0).toString() == "in") { Chris@246: zoomIn(); Chris@246: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@246: message.getArg(0).toString() == "out") { Chris@246: zoomOut(); Chris@246: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@246: message.getArg(0).toString() == "default") { Chris@246: zoomDefault(); Chris@246: } else if (message.getArg(0).canConvert(QVariant::Double)) { Chris@246: double level = message.getArg(0).toDouble(); Chris@246: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@246: if (level < 1.0) level = 1.0; Chris@246: if (currentPane) currentPane->setZoomLevel(lrint(level)); Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "zoomvertical") { Chris@246: Chris@246: Pane *pane = m_paneStack->getCurrentPane(); Chris@246: Layer *layer = 0; Chris@246: if (pane && pane->getLayerCount() > 0) { Chris@246: layer = pane->getLayer(pane->getLayerCount() - 1); Chris@246: } Chris@246: int defaultStep = 0; Chris@246: int steps = 0; Chris@246: if (layer) { Chris@246: steps = layer->getVerticalZoomSteps(defaultStep); Chris@246: if (message.getArgCount() == 1 && steps > 0) { Chris@246: if (message.getArg(0).canConvert(QVariant::String) && Chris@246: message.getArg(0).toString() == "in") { Chris@246: int step = layer->getCurrentVerticalZoomStep() + 1; Chris@246: if (step < steps) layer->setVerticalZoomStep(step); Chris@246: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@246: message.getArg(0).toString() == "out") { Chris@246: int step = layer->getCurrentVerticalZoomStep() - 1; Chris@246: if (step >= 0) layer->setVerticalZoomStep(step); Chris@246: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@246: message.getArg(0).toString() == "default") { Chris@246: layer->setVerticalZoomStep(defaultStep); Chris@246: } Chris@246: } else if (message.getArgCount() == 2) { Chris@246: if (message.getArg(0).canConvert(QVariant::Double) && Chris@246: message.getArg(1).canConvert(QVariant::Double)) { Chris@246: double min = message.getArg(0).toDouble(); Chris@246: double max = message.getArg(1).toDouble(); Chris@246: layer->setDisplayExtents(min, max); Chris@246: } Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "quit") { Chris@246: Chris@246: m_abandoning = true; Chris@246: close(); Chris@246: Chris@246: } else if (message.getMethod() == "resize") { Chris@246: Chris@246: if (message.getArgCount() == 2) { Chris@246: Chris@246: int width = 0, height = 0; Chris@246: Chris@246: if (message.getArg(1).canConvert(QVariant::Int)) { Chris@246: Chris@246: height = message.getArg(1).toInt(); Chris@246: Chris@246: if (message.getArg(0).canConvert(QVariant::String) && Chris@246: message.getArg(0).toString() == "pane") { Chris@246: Chris@246: Pane *pane = m_paneStack->getCurrentPane(); Chris@246: if (pane) pane->resize(pane->width(), height); Chris@246: Chris@246: } else if (message.getArg(0).canConvert(QVariant::Int)) { Chris@246: Chris@246: width = message.getArg(0).toInt(); Chris@246: resize(width, height); Chris@246: } Chris@246: } Chris@246: } Chris@246: Chris@246: } else if (message.getMethod() == "transform") { Chris@246: Chris@246: Pane *pane = m_paneStack->getCurrentPane(); Chris@246: Chris@246: if (getMainModel() && Chris@246: pane && Chris@246: message.getArgCount() == 1 && Chris@246: message.getArg(0).canConvert(QVariant::String)) { Chris@246: Chris@246: TransformId transformId = message.getArg(0).toString(); Chris@246: Chris@246: Transform transform = TransformFactory::getInstance()-> Chris@246: getDefaultTransformFor(transformId); Chris@246: Chris@246: Layer *newLayer = m_document->createDerivedLayer Chris@246: (transform, getMainModel()); Chris@246: Chris@246: if (newLayer) { Chris@246: m_document->addLayerToView(pane, newLayer); Chris@246: m_recentTransforms.add(transformId); Chris@246: m_paneStack->setCurrentLayer(pane, newLayer); Chris@246: } Chris@246: } Chris@246: Chris@246: } else { Chris@246: std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown or unsupported " Chris@246: << "method \"" << message.getMethod().toStdString() Chris@246: << "\"" << std::endl; Chris@246: } Chris@246: Chris@246: }