# HG changeset patch # User Chris Cannam # Date 1562247128 -3600 # Node ID eb7f4579e5cc005a6cf981d25998a3f03171ca53 # Parent bb0f5a8f93fe22b2857f74d726a4f1f0098b29ac Updates throughout for ModelById logic diff -r bb0f5a8f93fe -r eb7f4579e5cc main/MainWindow.cpp --- a/main/MainWindow.cpp Mon Jun 24 16:58:10 2019 +0100 +++ b/main/MainWindow.cpp Thu Jul 04 14:32:08 2019 +0100 @@ -1294,11 +1294,11 @@ int backgroundTypeCount = int(sizeof(backgroundTypes) / sizeof(backgroundTypes[0])); - std::vector models; + std::vector models; if (m_document) models = m_document->getTransformInputModels(); bool plural = (models.size() > 1); if (models.empty()) { - models.push_back(getMainModel()); // probably 0 + models.push_back(getMainModelId()); // probably None at this point } for (int i = 0; i < backgroundTypeCount; ++i) { @@ -1382,19 +1382,21 @@ default: break; } - std::vector candidateModels = models; + std::vector candidateModels = models; + if (candidateModels.empty()) { + throw std::logic_error("candidateModels should not be empty"); + } - for (std::vector::iterator mi = - candidateModels.begin(); - mi != candidateModels.end(); ++mi) { + for (auto modelId: candidateModels) { + + auto model = ModelById::get(modelId); - Model *model = *mi; - int channels = 0; if (model) { - DenseTimeValueModel *dtvm = - dynamic_cast(model); - if (dtvm) channels = dtvm->getChannelCount(); + if (auto dtvm = ModelById::getAs + (modelId)) { + channels = dtvm->getChannelCount(); + } } if (channels < 1 && getMainModel()) { channels = getMainModel()->getChannelCount(); @@ -1419,14 +1421,14 @@ connect(this, SIGNAL(canAddPane(bool)), action, SLOT(setEnabled(bool))); m_paneActions.push_back - ({ action, LayerConfiguration(type, model) }); + ({ action, LayerConfiguration(type, modelId) }); } else { connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); m_layerActions.push_back - ({ action, LayerConfiguration(type, model) }); + ({ action, LayerConfiguration(type, modelId) }); } if (shortcutText != "") { m_keyReference->registerShortcut(action); @@ -1461,7 +1463,7 @@ if (isDefault) { action = new QAction(icon, actionText, this); - if (!model || model == getMainModel()) { + if (!model || modelId == getMainModelId()) { // Default for the shortcut is to // attach to an action that uses the // main model as input. But this may @@ -1482,21 +1484,21 @@ connect(this, SIGNAL(canAddPane(bool)), action, SLOT(setEnabled(bool))); m_paneActions.push_back - ({ action, LayerConfiguration(type, model, c - 1) }); + ({ action, LayerConfiguration(type, modelId, c - 1) }); } else { connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); m_layerActions.push_back - ({ action, LayerConfiguration(type, model, c - 1) }); + ({ action, LayerConfiguration(type, modelId, c - 1) }); } submenu->addAction(action); } if (isDefault && menuType == layerMenuType && - mi == candidateModels.begin()) { + modelId == *candidateModels.begin()) { // only add for one model, one channel, one menu on // right button -- the action itself will discover // which model is the correct one (based on pane) @@ -1507,7 +1509,7 @@ connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); m_layerActions.push_back - ({ action, LayerConfiguration(type, nullptr, 0) }); + ({ action, LayerConfiguration(type, ModelId(), 0) }); m_rightButtonLayerMenu->addAction(action); } } @@ -1620,7 +1622,7 @@ } void -MainWindow::updateLayerShortcutsFor(Model *model) +MainWindow::updateLayerShortcutsFor(ModelId modelId) { // Called when e.g. the current pane has changed, to ensure the // various layer shortcuts select an action whose input model is @@ -1629,9 +1631,11 @@ set seen; for (auto &a : m_paneActions) { - if (!a.second.sourceModel) continue; // empty pane/layer shortcut + if (a.second.sourceModel.isNone()) { + continue; // empty pane/layer shortcut + } auto type = a.second.layer; - if (a.second.sourceModel == model && seen.find(type) == seen.end()) { + if (a.second.sourceModel == modelId && seen.find(type) == seen.end()) { a.first->setShortcut(shortcutFor(type, true)); seen.insert(type); } else { @@ -1642,9 +1646,11 @@ seen.clear(); for (auto &a : m_layerActions) { - if (!a.second.sourceModel) continue; // empty pane/layer shortcut + if (a.second.sourceModel.isNone()) { + continue; // empty pane/layer shortcut + } auto type = a.second.layer; - if (a.second.sourceModel == model && seen.find(type) == seen.end()) { + if (a.second.sourceModel == modelId && seen.find(type) == seen.end()) { a.first->setShortcut(shortcutFor(type, false)); seen.insert(type); } else { @@ -2690,11 +2696,12 @@ void MainWindow::exportAudio(bool asData) { - if (!getMainModel()) return; - - RangeSummarisableTimeValueModel *model = getMainModel(); - std::set otherModels; - RangeSummarisableTimeValueModel *current = model; + auto modelId = getMainModelId(); + if (modelId.isNone()) return; + + std::set otherModelIds; + ModelId current = modelId; + if (m_paneStack) { for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { Pane *pane = m_paneStack->getPane(i); @@ -2703,37 +2710,39 @@ Layer *layer = pane->getLayer(j); if (!layer) continue; cerr << "layer = " << layer->objectName() << endl; - Model *m = layer->getModel(); - RangeSummarisableTimeValueModel *wm = - dynamic_cast(m); - if (wm) { - cerr << "found: " << wm->objectName() << endl; - otherModels.insert(wm); + ModelId m = layer->getModel(); + if (ModelById::isa(m)) { + otherModelIds.insert(m); if (pane == m_paneStack->getCurrentPane()) { - current = wm; + current = m; } } } } } - if (!otherModels.empty()) { - std::map m; - m[tr("1. %2").arg(model->objectName())] = model; + if (!otherModelIds.empty()) { + std::map m; + QString unnamed = tr(""); + QString oname = unnamed; + if (auto mp = ModelById::get(modelId)) { + oname = mp->objectName(); + } + m[tr("1. %2").arg(oname)] = modelId; int n = 2; int c = 0; - for (std::set::const_iterator i - = otherModels.begin(); - i != otherModels.end(); ++i) { - if (*i == model) continue; - m[tr("%1. %2").arg(n).arg((*i)->objectName())] = *i; + for (auto otherModelId: otherModelIds) { + if (otherModelId == modelId) continue; + oname = unnamed; + if (auto mp = ModelById::get(otherModelId)) { + oname = mp->objectName(); + } + m[tr("%1. %2").arg(n).arg(oname)] = otherModelId; ++n; - if (*i == current) c = n-1; + if (otherModelId == current) c = n-1; } QStringList items; - for (std::map - ::const_iterator i = m.begin(); - i != m.end(); ++i) { - items << i->first; + for (auto i: m) { + items << i.first; } if (items.size() > 1) { bool ok = false; @@ -2743,14 +2752,20 @@ items, c, false, &ok); if (!ok || item.isEmpty()) return; if (m.find(item) == m.end()) { - cerr << "WARNING: Model " << item - << " not found in list!" << endl; + SVCERR << "WARNING: Model " << item + << " not found in list!" << endl; } else { - model = m[item]; + modelId = m[item]; } } } + auto model = ModelById::getAs(modelId); + if (!model) { + SVCERR << "ERROR: Chosen model is not a DenseTimeValueModel!" << endl; + return; + } + QString path; if (asData) { path = getSaveFileName(FileFinder::CSVFile); @@ -2835,7 +2850,7 @@ model->getSampleRate(), model->getChannelCount(), WavFileWriter::WriteToTemporary); - subwriter.writeModel(model, &subms); + subwriter.writeModel(model.get(), &subms); ok = subwriter.isOK(); if (!ok) { @@ -2856,7 +2871,7 @@ this, Qt::ApplicationModal }; - CSVFileWriter writer(path, model, &dialog, + CSVFileWriter writer(path, model.get(), &dialog, ((QFileInfo(path).suffix() == "csv") ? "," : "\t")); if (selectionToWrite) { @@ -2871,7 +2886,7 @@ model->getSampleRate(), model->getChannelCount(), WavFileWriter::WriteToTemporary); - writer.writeModel(model, selectionToWrite); + writer.writeModel(model.get(), selectionToWrite); ok = writer.isOK(); error = writer.getError(); } @@ -2936,8 +2951,10 @@ } else { + auto modelId = ModelById::add(std::shared_ptr(model)); + status = addOpenedAudioModel(path, - model, + modelId, CreateAdditionalModel, getDefaultSessionTemplate(), false); @@ -2997,11 +3014,11 @@ Layer *layer = pane->getSelectedLayer(); if (!layer) return; - Model *model = layer->getModel(); - if (!model) return; + ModelId modelId = layer->getModel(); + if (modelId.isNone()) return; FileFinder::FileType type = FileFinder::LayerFileNoMidi; - if (dynamic_cast(model)) type = FileFinder::LayerFile; + if (ModelById::isa(modelId)) type = FileFinder::LayerFile; QString path = getSaveFileName(type); if (path == "") return; @@ -3387,7 +3404,7 @@ } QString mainModelLocation; - WaveFileModel *mm = getMainModel(); + auto mm = getMainModel(); if (mm) mainModelLocation = mm->getLocation(); if (mainModelLocation != "") { openAudio(mainModelLocation, ReplaceSession, n); @@ -3837,32 +3854,31 @@ Layer *newLayer = m_document->createLayer(configuration.layer); - Model *suggestedModel = configuration.sourceModel; - Model *model = nullptr; - - if (suggestedModel) { + ModelId suggestedModelId = configuration.sourceModel; + ModelId modelId; + + if (!suggestedModelId.isNone()) { // check its validity - std::vector inputModels = m_document->getTransformInputModels(); - for (size_t j = 0; j < inputModels.size(); ++j) { - if (inputModels[j] == suggestedModel) { - model = suggestedModel; - break; + std::vector inputModels = m_document->getTransformInputModels(); + for (auto im: inputModels) { + if (im == suggestedModelId) { + modelId = suggestedModelId; } } - if (!model) { - cerr << "WARNING: Model " << (void *)suggestedModel - << " appears in pane action map, but is not reported " - << "by document as a valid transform source" << endl; + if (modelId.isNone()) { + cerr << "WARNING: Model " << modelId + << " appears in pane action map, but is not reported " + << "by document as a valid transform source" << endl; } } - if (!model) { - model = m_document->getMainModel(); + if (modelId.isNone()) { + modelId = m_document->getMainModel(); } - m_document->setModel(newLayer, model); + m_document->setModel(newLayer, modelId); m_document->setChannel(newLayer, configuration.channel); m_document->addLayerToView(pane, newLayer); @@ -3976,11 +3992,9 @@ } else { - Model *model = i->second.sourceModel; - - cerr << "model = "<< model << endl; - - if (!model) { + ModelId modelId = i->second.sourceModel; + + if (modelId.isNone()) { if (type == LayerFactory::TimeRuler) { newLayer = m_document->createMainModelLayer(type); } else { @@ -3989,27 +4003,25 @@ // the current pane -- this is the case for // right-button menu layer additions Pane::ModelSet ms = pane->getModels(); - foreach (Model *m, ms) { - RangeSummarisableTimeValueModel *r = - dynamic_cast(m); - if (r) model = m; + for (ModelId m: ms) { + if (ModelById::isa(m)) { + modelId = m; + } } - if (!model) model = getMainModel(); + if (modelId.isNone()) { + modelId = getMainModelId(); + } } } - if (model) { + if (!modelId.isNone()) { newLayer = m_document->createLayer(type); - if (m_document->isKnownModel(model)) { + if (m_document->isKnownModel(modelId)) { m_document->setChannel(newLayer, i->second.channel); - m_document->setModel(newLayer, model); + m_document->setModel(newLayer, modelId); } else { - cerr << "WARNING: MainWindow::addLayer: unknown model " - << model - << " (\"" - << model->objectName() - << "\") in layer action map" - << endl; + SVCERR << "WARNING: MainWindow::addLayer: unknown model " + << modelId << " in layer action map" << endl; } } } @@ -4066,10 +4078,10 @@ return; } - std::vector candidateInputModels = + std::vector candidateInputModels = m_document->getTransformInputModels(); - Model *defaultInputModel = nullptr; + ModelId defaultInputModelId; for (int j = 0; j < pane->getLayerCount(); ++j) { @@ -4078,37 +4090,37 @@ if (LayerFactory::getInstance()->getLayerType(layer) != LayerFactory::Waveform && - !layer->isLayerOpaque()) continue; - - Model *model = layer->getModel(); - if (!model) continue; - - for (size_t k = 0; k < candidateInputModels.size(); ++k) { - if (candidateInputModels[k] == model) { - defaultInputModel = model; + !layer->isLayerOpaque()) { + continue; + } + + ModelId modelId = layer->getModel(); + if (modelId.isNone()) continue; + + for (ModelId candidateId: candidateInputModels) { + if (candidateId == modelId) { + defaultInputModelId = modelId; break; } } - if (defaultInputModel) break; + if (!defaultInputModelId.isNone()) break; } - AggregateWaveModel *aggregate = nullptr; + ModelId aggregate; if (candidateInputModels.size() > 1) { // Add an aggregate model as another option AggregateWaveModel::ChannelSpecList sl; - foreach (Model *m, candidateInputModels) { - RangeSummarisableTimeValueModel *r = - qobject_cast(m); - if (r) { - sl.push_back(AggregateWaveModel::ModelChannelSpec(r->getId(), -1)); + for (ModelId mid: candidateInputModels) { + if (ModelById::isa(mid)) { + sl.push_back(AggregateWaveModel::ModelChannelSpec(mid, -1)); } } if (!sl.empty()) { - aggregate = new AggregateWaveModel(sl); + auto aggregate = std::make_shared(sl); aggregate->setObjectName(tr("Multiplex all of the above")); - candidateInputModels.push_back(aggregate); + candidateInputModels.push_back(ModelById::add(aggregate)); } } @@ -4124,23 +4136,24 @@ getConfigurationForTransform (transform, candidateInputModels, - defaultInputModel, + defaultInputModelId, m_playSource, startFrame, duration, &configurator); - if (aggregate) { + if (!aggregate.isNone()) { if (input.getModel() == aggregate) { - aggregate->setObjectName(tr("Multiplexed audio")); + if (auto aggregateModel = ModelById::get(aggregate)) { + aggregateModel->setObjectName(tr("Multiplexed audio")); + } m_document->addAggregateModel(aggregate); } else { - aggregate->aboutToDelete(); - delete aggregate; + ModelById::release(aggregate); } } - if (!input.getModel()) return; + if (input.getModel().isNone()) return; // SVDEBUG << "MainWindow::addLayer: Input model is " << input.getModel() << " \"" << input.getModel()->objectName() << "\"" << endl << "transform:" << endl << transform.toXmlString() << endl; @@ -4330,7 +4343,7 @@ if (layer && LayerFactory::getInstance()->getLayerType(layer) == LayerFactory::Waveform && - layer->getModel() == getMainModel()) { + layer->getModel() == getMainModelId()) { containsMainModel = true; break; } @@ -4341,15 +4354,14 @@ for (int i = pane->getLayerCount(); i > 0; ) { --i; Layer *layer = pane->getLayer(i); - RangeSummarisableTimeValueModel *tvm = - qobject_cast(layer->getModel()); - if (tvm) { + ModelId modelId = layer->getModel(); + if (ModelById::isa(modelId)) { auto type = LayerFactory::getInstance()->getLayerType(layer); if (type != LayerFactory::TimeRuler) { - updateLayerShortcutsFor(tvm); + updateLayerShortcutsFor(modelId); } if (type == LayerFactory::Waveform) { - m_panLayer->setModel(tvm); + m_panLayer->setModel(modelId); panLayerSet = true; break; } @@ -4357,14 +4369,20 @@ } if (containsMainModel && !panLayerSet) { - m_panLayer->setModel(getMainModel()); + m_panLayer->setModel(getMainModelId()); } } void MainWindow::updateVisibleRangeDisplay(Pane *p) const { - if (!getMainModel() || !p) { + sv_samplerate_t sampleRate = 0; + if (auto mm = getMainModel()) { + sampleRate = mm->getSampleRate(); + } else { + return; + } + if (!p) { return; } @@ -4388,12 +4406,8 @@ endFrame = p->getLastVisibleFrame(); } - RealTime start = RealTime::frame2RealTime - (startFrame, getMainModel()->getSampleRate()); - - RealTime end = RealTime::frame2RealTime - (endFrame, getMainModel()->getSampleRate()); - + RealTime start = RealTime::frame2RealTime(startFrame, sampleRate); + RealTime end = RealTime::frame2RealTime(endFrame, sampleRate); RealTime duration = end - start; QString startStr, endStr, durationStr; @@ -4586,13 +4600,11 @@ if (!m_playSource || !m_playSource->isPlaying()) continue; - Model *model = static_cast(currentTimeValueLayer)->getModel(); - SparseTimeValueModel *tvm = - dynamic_cast(model); - if (tvm) { + ModelId modelId = currentTimeValueLayer->getModel(); + if (ModelById::isa(modelId)) { Event point(frame, float(ev.getPitch() % 12), ""); AddEventCommand *command = new AddEventCommand - (tvm, point, tr("Add Point")); + (modelId.untyped, point, tr("Add Point")); CommandHistory::getInstance()->addCommand(command); } @@ -4639,20 +4651,20 @@ } void -MainWindow::modelAdded(Model *model) +MainWindow::modelAdded(ModelId modelId) { - MainWindowBase::modelAdded(model); - if (dynamic_cast(model)) { + MainWindowBase::modelAdded(modelId); + if (ModelById::isa(modelId)) { setupPaneAndLayerMenus(); } } void -MainWindow::mainModelChanged(WaveFileModel *model) +MainWindow::mainModelChanged(ModelId modelId) { - m_panLayer->setModel(model); - - MainWindowBase::mainModelChanged(model); + m_panLayer->setModel(modelId); + + MainWindowBase::mainModelChanged(modelId); if (m_playTarget || m_audioIO) { connect(m_mainLevelPan, SIGNAL(levelChanged(float)), @@ -4684,19 +4696,6 @@ } void -MainWindow::modelAboutToBeDeleted(Model *model) -{ - if (model == m_panLayer->getModel()) { - if (model == getMainModel()) { - m_panLayer->setModel(nullptr); - } else { - m_panLayer->setModel(getMainModel()); - } - } - MainWindowBase::modelAboutToBeDeleted(model); -} - -void MainWindow::setInstantsNumbering() { QAction *a = dynamic_cast(sender()); diff -r bb0f5a8f93fe -r eb7f4579e5cc main/MainWindow.h --- a/main/MainWindow.h Mon Jun 24 16:58:10 2019 +0100 +++ b/main/MainWindow.h Thu Jul 04 14:32:08 2019 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _MAIN_WINDOW_H_ -#define _MAIN_WINDOW_H_ +#ifndef SV_MAIN_WINDOW_H +#define SV_MAIN_WINDOW_H #include "framework/MainWindowBase.h" @@ -137,11 +137,10 @@ void layerRemoved(Layer *) override; void layerInAView(Layer *, bool) override; - void mainModelChanged(WaveFileModel *) override; + void mainModelChanged(ModelId) override; virtual void mainModelGainChanged(float); virtual void mainModelPanChanged(float); - void modelAdded(Model *) override; - void modelAboutToBeDeleted(Model *) override; + void modelAdded(ModelId) override; virtual void showLayerTree(); virtual void showActivityLog(); @@ -237,16 +236,16 @@ struct LayerConfiguration { LayerConfiguration(LayerFactory::LayerType _layer = LayerFactory::TimeRuler, - Model *_source = 0, + ModelId _source = ModelId(), int _channel = -1) : layer(_layer), sourceModel(_source), channel(_channel) { } LayerFactory::LayerType layer; - Model *sourceModel; + ModelId sourceModel; int channel; }; QString shortcutFor(LayerFactory::LayerType, bool isPaneMenu); - void updateLayerShortcutsFor(Model *); + void updateLayerShortcutsFor(ModelId); // Map from menu action to the resulting layer configurations // etc. These all used to be std::maps, but we sometimes want to diff -r bb0f5a8f93fe -r eb7f4579e5cc main/OSCHandler.cpp --- a/main/OSCHandler.cpp Mon Jun 24 16:58:10 2019 +0100 +++ b/main/OSCHandler.cpp Thu Jul 04 14:32:08 2019 +0100 @@ -110,9 +110,10 @@ WavFileWriter::WriteToTemporary); MultiSelection ms = m_viewManager->getSelection(); if (!ms.getSelections().empty()) { - writer.writeModel(getMainModel(), &ms); + //!!! todo: update WavFileWriter! + writer.writeModel(getMainModel().get(), &ms); } else { - writer.writeModel(getMainModel()); + writer.writeModel(getMainModel().get()); } } } @@ -352,7 +353,7 @@ } else { LayerConfiguration configuration(type, - getMainModel(), + getMainModelId(), channel); addPane(configuration, @@ -611,7 +612,7 @@ getDefaultTransformFor(transformId); Layer *newLayer = m_document->createDerivedLayer - (transform, getMainModel()); + (transform, getMainModelId()); if (newLayer) { m_document->addLayerToView(pane, newLayer); diff -r bb0f5a8f93fe -r eb7f4579e5cc main/SVSplash.cpp --- a/main/SVSplash.cpp Mon Jun 24 16:58:10 2019 +0100 +++ b/main/SVSplash.cpp Thu Jul 04 14:32:08 2019 +0100 @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include @@ -33,7 +33,8 @@ QPixmap *p1 = new QPixmap(":icons/scalable/sv-splash.png"); int w = p1->width(), h = p1->height(); - QRect desk = QApplication::desktop()->availableGeometry(); + QScreen *screen = QApplication::primaryScreen(); + QRect desk = screen->availableGeometry(); double dpratio = devicePixelRatio(); double widthMultiple = double(desk.width()) / double(w); @@ -89,6 +90,11 @@ void SVSplash::drawContents(QPainter *painter) { + // Qt 5.13 deprecates QFontMetrics::width(), but its suggested + // replacement (horizontalAdvance) was only added in Qt 5.11 + // which is too new for us +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + painter->drawPixmap(rect(), *m_pixmap, m_pixmap->rect()); QString text = QString("v%1").arg(SV_VERSION); painter->setPen(Qt::black); diff -r bb0f5a8f93fe -r eb7f4579e5cc main/main.cpp --- a/main/main.cpp Mon Jun 24 16:58:10 2019 +0100 +++ b/main/main.cpp Thu Jul 04 14:32:08 2019 +0100 @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include #include @@ -404,8 +404,8 @@ splash, SLOT(finishSplash(QWidget *))); } - QDesktopWidget *desktop = QApplication::desktop(); - QRect available = desktop->availableGeometry(); + QScreen *screen = QApplication::primaryScreen(); + QRect available = screen->availableGeometry(); int width = (available.width() * 2) / 3; int height = available.height() / 2; diff -r bb0f5a8f93fe -r eb7f4579e5cc repoint-lock.json --- a/repoint-lock.json Mon Jun 24 16:58:10 2019 +0100 +++ b/repoint-lock.json Thu Jul 04 14:32:08 2019 +0100 @@ -4,13 +4,13 @@ "pin": "62987b6d6a3b" }, "svcore": { - "pin": "c3b5564cfb78" + "pin": "d0ef65d8dd89" }, "svgui": { - "pin": "de41a11cabc2" + "pin": "9bf8aa2916e9" }, "svapp": { - "pin": "c7406ebcd51c" + "pin": "7540733f5480" }, "checker": { "pin": "c8c17e51aab0" diff -r bb0f5a8f93fe -r eb7f4579e5cc repoint-project.json --- a/repoint-project.json Mon Jun 24 16:58:10 2019 +0100 +++ b/repoint-project.json Thu Jul 04 14:32:08 2019 +0100 @@ -21,7 +21,8 @@ }, "svgui": { "vcs": "hg", - "service": "soundsoftware" + "service": "soundsoftware", + "branch": "by-id" }, "svapp": { "vcs": "hg",