# HG changeset patch # User Chris Cannam # Date 1561566320 -3600 # Node ID fe3f7f8df3a36bfbde638839935c51dfd3fe4e8b # Parent 5655754637528fa940fce27f06dca07b1964d2c2 More work on transformers diff -r 565575463752 -r fe3f7f8df3a3 transform/FeatureExtractionModelTransformer.cpp --- a/transform/FeatureExtractionModelTransformer.cpp Wed Jun 26 14:59:09 2019 +0100 +++ b/transform/FeatureExtractionModelTransformer.cpp Wed Jun 26 17:25:20 2019 +0100 @@ -650,7 +650,6 @@ auto input = ModelById::getAs(getInputModel()); if (!input) { - SVCERR << "FeatureExtractionModelTransformer::run: Input model not (no longer?) available, abandoning" << endl; abandon(); return; } @@ -794,22 +793,22 @@ if (m_abandoned) break; - Vamp::Plugin::FeatureSet features = m_plugin->process + auto features = m_plugin->process (buffers, RealTime::frame2RealTime(blockFrame, sampleRate) .toVampRealTime()); if (m_abandoned) break; - for (int j = 0; j < (int)m_outputNos.size(); ++j) { - for (int fi = 0; fi < (int)features[m_outputNos[j]].size(); ++fi) { - Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi]; + for (int j = 0; in_range_for(m_outputNos, j); ++j) { + for (int fi = 0; in_range_for(features[m_outputNos[j]], fi); ++fi) { + auto feature = features[m_outputNos[j]][fi]; addFeature(j, blockFrame, feature); } } if (blockFrame == contextStart || completion > prevCompletion) { - for (int j = 0; j < (int)m_outputNos.size(); ++j) { + for (int j = 0; in_range_for(m_outputNos, j); ++j) { setCompletion(j, completion); } prevCompletion = completion; @@ -820,11 +819,11 @@ } if (!m_abandoned) { - Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures(); + auto features = m_plugin->getRemainingFeatures(); - for (int j = 0; j < (int)m_outputNos.size(); ++j) { - for (int fi = 0; fi < (int)features[m_outputNos[j]].size(); ++fi) { - Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi]; + for (int j = 0; in_range_for(m_outputNos, j); ++j) { + for (int fi = 0; in_range_for(features[m_outputNos[j]], fi); ++fi) { + auto feature = features[m_outputNos[j]][fi]; addFeature(j, blockFrame, feature); } } @@ -990,129 +989,116 @@ // to create. ModelId outputId = m_outputs[n]; - bool found = false; + + if (isOutputType(n)) { + + auto model = ModelById::getAs(outputId); + if (!model) return; + model->add(Event(frame, feature.label.c_str())); + + } else if (isOutputType(n)) { + + auto model = ModelById::getAs(outputId); + if (!model) return; + + for (int i = 0; in_range_for(feature.values, i); ++i) { + + float value = feature.values[i]; + + QString label = feature.label.c_str(); + if (feature.values.size() > 1) { + label = QString("[%1] %2").arg(i+1).arg(label); + } + + auto targetModel = model; + + if (m_needAdditionalModels[n] && i > 0) { + targetModel = ModelById::getAs + (getAdditionalModel(n, i)); + if (!targetModel) targetModel = model; + } + + targetModel->add(Event(frame, value, label)); + } + + } else if (isOutputType(n) || isOutputType(n)) { - if (!found) { - auto model = ModelById::getAs(outputId); - if (model) { - found = true; - model->add(Event(frame, feature.label.c_str())); + int index = 0; + + float value = 0.0; + if ((int)feature.values.size() > index) { + value = feature.values[index++]; } - } - - if (!found) { - auto model = ModelById::getAs(outputId); - if (model) { - found = true; - for (int i = 0; in_range_for(feature.values, i); ++i) { - - float value = feature.values[i]; - - QString label = feature.label.c_str(); - if (feature.values.size() > 1) { - label = QString("[%1] %2").arg(i+1).arg(label); - } - - auto targetModel = model; - - if (m_needAdditionalModels[n] && i > 0) { - targetModel = ModelById::getAs - (getAdditionalModel(n, i)); - if (!targetModel) targetModel = model; - } - - targetModel->add(Event(frame, value, label)); + sv_frame_t duration = 1; + if (feature.hasDuration) { + duration = RealTime::realTime2Frame(feature.duration, inputRate); + } else { + if (in_range_for(feature.values, index)) { + duration = lrintf(feature.values[index++]); } } - } - - if (!found) { - if (ModelById::getAs(outputId) || - ModelById::getAs(outputId)) { - found = true; - int index = 0; + auto noteModel = ModelById::getAs(outputId); + if (noteModel) { - float value = 0.0; + float velocity = 100; if ((int)feature.values.size() > index) { - value = feature.values[index++]; + velocity = feature.values[index++]; } + if (velocity < 0) velocity = 127; + if (velocity > 127) velocity = 127; + + noteModel->add(Event(frame, value, // value is pitch + duration, + velocity / 127.f, + feature.label.c_str())); + } - sv_frame_t duration = 1; - if (feature.hasDuration) { - duration = RealTime::realTime2Frame(feature.duration, inputRate); - } else { - if (in_range_for(feature.values, index)) { - duration = lrintf(feature.values[index++]); - } - } - - auto noteModel = ModelById::getAs(outputId); - if (noteModel) { - - float velocity = 100; - if ((int)feature.values.size() > index) { - velocity = feature.values[index++]; - } - if (velocity < 0) velocity = 127; - if (velocity > 127) velocity = 127; - - noteModel->add(Event(frame, value, // value is pitch - duration, - velocity / 127.f, - feature.label.c_str())); - } - - auto regionModel = ModelById::getAs(outputId); - if (regionModel) { - - if (feature.hasDuration && !feature.values.empty()) { - - for (int i = 0; in_range_for(feature.values, i); ++i) { - - float value = feature.values[i]; - - QString label = feature.label.c_str(); - if (feature.values.size() > 1) { - label = QString("[%1] %2").arg(i+1).arg(label); - } - - regionModel->add(Event(frame, - value, - duration, - label)); + auto regionModel = ModelById::getAs(outputId); + if (regionModel) { + + if (feature.hasDuration && !feature.values.empty()) { + + for (int i = 0; in_range_for(feature.values, i); ++i) { + + float value = feature.values[i]; + + QString label = feature.label.c_str(); + if (feature.values.size() > 1) { + label = QString("[%1] %2").arg(i+1).arg(label); } - } else { - + regionModel->add(Event(frame, value, duration, - feature.label.c_str())); + label)); } + } else { + + regionModel->add(Event(frame, + value, + duration, + feature.label.c_str())); } } + + } else if (isOutputType(n)) { + + auto model = ModelById::getAs + (outputId); + if (!model) return; + + DenseThreeDimensionalModel::Column values = feature.values; + + if (!feature.hasTimestamp && m_fixedRateFeatureNos[n] >= 0) { + model->setColumn(m_fixedRateFeatureNos[n], values); + } else { + model->setColumn(int(frame / model->getResolution()), values); + } } - if (!found) { - auto model = ModelById::getAs - (outputId); - if (model) { - found = true; - - DenseThreeDimensionalModel::Column values = feature.values; - - if (!feature.hasTimestamp && m_fixedRateFeatureNos[n] >= 0) { - model->setColumn(m_fixedRateFeatureNos[n], values); - } else { - model->setColumn(int(frame / model->getResolution()), values); - } - } - } - - if (!found) { - SVDEBUG << "FeatureExtractionModelTransformer::addFeature: Unknown output model type!" << endl; - } + SVDEBUG << "FeatureExtractionModelTransformer::addFeature: Unknown output model type!" << endl; } void @@ -1123,47 +1109,11 @@ << completion << ")" << endl; #endif - ModelId outputId = m_outputs[n]; - bool found = false; - - if (!found) { - auto model = ModelById::getAs(outputId); - if (model) { - found = true; - model->setCompletion(completion, true); - } - } - - if (!found) { - auto model = ModelById::getAs(outputId); - if (model) { - found = true; - model->setCompletion(completion, true); - } - } - - if (!found) { - auto model = ModelById::getAs(outputId); - if (model) { - found = true; - model->setCompletion(completion, true); - } - } - - if (!found) { - auto model = ModelById::getAs(outputId); - if (model) { - found = true; - model->setCompletion(completion, true); - } - } - - if (!found) { - auto model = ModelById::getAs(outputId); - if (model) { - found = true; - model->setCompletion(completion, true); - } - } + (void) + (setOutputCompletion(n, completion) || + setOutputCompletion(n, completion) || + setOutputCompletion(n, completion) || + setOutputCompletion(n, completion) || + setOutputCompletion(n, completion)); } diff -r 565575463752 -r fe3f7f8df3a3 transform/FeatureExtractionModelTransformer.h --- a/transform/FeatureExtractionModelTransformer.h Wed Jun 26 14:59:09 2019 +0100 +++ b/transform/FeatureExtractionModelTransformer.h Wed Jun 26 17:25:20 2019 +0100 @@ -95,27 +95,23 @@ QWaitCondition m_outputsCondition; void awaitOutputModels() override; - // just casts: -/*!!! - DenseTimeValueModel *getConformingInput(); - - template bool isOutput(int n) { - return dynamic_cast(m_outputs[n]) != 0; + template bool isOutputType(int n) { + if (!ModelById::getAs(m_outputs[n])) { + return false; + } else { + return true; + } } - template ModelClass *getConformingOutput(int n) { - if ((int)m_outputs.size() > n) { - ModelClass *mc = dynamic_cast(m_outputs[n]); - if (!mc) { - std::cerr << "FeatureExtractionModelTransformer::getOutput: Output model not conformable" << std::endl; - } - return mc; + template bool setOutputCompletion(int n, int completion) { + auto model = ModelById::getAs(m_outputs[n]); + if (!model) { + return false; } else { - std::cerr << "FeatureExtractionModelTransformer::getOutput: No such output number " << n << std::endl; - return 0; + model->setCompletion(completion, true); + return true; } } -*/ }; #endif diff -r 565575463752 -r fe3f7f8df3a3 transform/ModelTransformer.cpp --- a/transform/ModelTransformer.cpp Wed Jun 26 14:59:09 2019 +0100 +++ b/transform/ModelTransformer.cpp Wed Jun 26 17:25:20 2019 +0100 @@ -17,8 +17,6 @@ ModelTransformer::ModelTransformer(Input input, const Transform &transform) : m_input(input), - m_detached(false), - m_detachedAdd(false), m_abandoned(false) { m_transforms.push_back(transform); @@ -27,8 +25,6 @@ ModelTransformer::ModelTransformer(Input input, const Transforms &transforms) : m_transforms(transforms), m_input(input), - m_detached(false), - m_detachedAdd(false), m_abandoned(false) { } @@ -37,13 +33,5 @@ { m_abandoned = true; wait(); - if (!m_detached) { - Models mine = getOutputModels(); - foreach (Model *m, mine) delete m; - } - if (!m_detachedAdd) { - Models mine = getAdditionalOutputModels(); - foreach (Model *m, mine) delete m; - } } diff -r 565575463752 -r fe3f7f8df3a3 transform/ModelTransformerFactory.cpp --- a/transform/ModelTransformerFactory.cpp Wed Jun 26 14:59:09 2019 +0100 +++ b/transform/ModelTransformerFactory.cpp Wed Jun 26 17:25:20 2019 +0100 @@ -56,8 +56,8 @@ ModelTransformer::Input ModelTransformerFactory::getConfigurationForTransform(Transform &transform, - const vector &candidateInputModels, - Model *defaultInputModel, + vector candidateInputModels, + ModelId defaultInputModel, AudioPlaySource *source, sv_frame_t startFrame, sv_frame_t duration, @@ -65,26 +65,39 @@ { QMutexLocker locker(&m_mutex); - ModelTransformer::Input input(nullptr); + ModelTransformer::Input input({}); if (candidateInputModels.empty()) return input; //!!! This will need revision -- we'll have to have a callback //from the dialog for when the candidate input model is changed, //as we'll need to reinitialise the channel settings in the dialog - Model *inputModel = candidateInputModels[0]; + ModelId inputModel = candidateInputModels[0]; QStringList candidateModelNames; QString defaultModelName; - QMap modelMap; - for (int i = 0; i < (int)candidateInputModels.size(); ++i) { - QString modelName = candidateInputModels[i]->objectName(); + QMap modelMap; + + sv_samplerate_t defaultSampleRate; + { auto im = ModelById::get(inputModel); + if (!im) return input; + defaultSampleRate = im->getSampleRate(); + } + + for (int i = 0; in_range_for(candidateInputModels, i); ++i) { + + auto model = ModelById::get(candidateInputModels[i]); + if (!model) return input; + + QString modelName = model->objectName(); QString origModelName = modelName; int dupcount = 1; while (modelMap.contains(modelName)) { modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount); } + modelMap[modelName] = candidateInputModels[i]; candidateModelNames.push_back(modelName); + if (candidateInputModels[i] == defaultInputModel) { defaultModelName = modelName; } @@ -105,7 +118,7 @@ RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id); - sv_samplerate_t sampleRate = inputModel->getSampleRate(); + sv_samplerate_t sampleRate = defaultSampleRate; int blockSize = 1024; int channels = 1; if (source) { @@ -125,7 +138,7 @@ Vamp::Plugin *vp = FeatureExtractionPluginFactory::instance()->instantiatePlugin - (id, float(inputModel->getSampleRate())); + (id, float(defaultSampleRate)); plugin = vp; } @@ -196,7 +209,7 @@ return transformer; } -Model * +ModelId ModelTransformerFactory::transform(const Transform &transform, const ModelTransformer::Input &input, QString &message, @@ -206,12 +219,12 @@ Transforms transforms; transforms.push_back(transform); - vector mm = transformMultiple(transforms, input, message, handler); - if (mm.empty()) return nullptr; + vector mm = transformMultiple(transforms, input, message, handler); + if (mm.empty()) return {}; else return mm[0]; } -vector +vector ModelTransformerFactory::transformMultiple(const Transforms &transforms, const ModelTransformer::Input &input, QString &message, @@ -220,9 +233,12 @@ SVDEBUG << "ModelTransformerFactory::transformMultiple: Constructing transformer with input model " << input.getModel() << endl; QMutexLocker locker(&m_mutex); + + auto inputModel = ModelById::get(input.getModel()); + if (!inputModel) return {}; ModelTransformer *t = createTransformer(transforms, input); - if (!t) return vector(); + if (!t) return {}; if (handler) { m_handlers[t] = handler; @@ -233,22 +249,24 @@ connect(t, SIGNAL(finished()), this, SLOT(transformerFinished())); t->start(); - vector models = t->detachOutputModels(); - + vector models = t->getOutputModels(); + if (!models.empty()) { - QString imn = input.getModel()->objectName(); + QString imn = inputModel->objectName(); QString trn = TransformFactory::getInstance()->getTransformFriendlyName (transforms[0].getIdentifier()); - for (int i = 0; i < (int)models.size(); ++i) { + for (int i = 0; in_range_for(models, i); ++i) { + auto model = ModelById::get(models[i]); + if (!model) continue; if (imn != "") { if (trn != "") { - models[i]->setObjectName(tr("%1: %2").arg(imn).arg(trn)); + model->setObjectName(tr("%1: %2").arg(imn).arg(trn)); } else { - models[i]->setObjectName(imn); + model->setObjectName(imn); } } else if (trn != "") { - models[i]->setObjectName(trn); + model->setObjectName(trn); } } } else { @@ -284,12 +302,12 @@ m_runningTransformers.erase(transformer); - map> toNotifyOfMore; + map> toNotifyOfMore; vector toNotifyOfNoMore; if (m_handlers.find(transformer) != m_handlers.end()) { if (transformer->willHaveAdditionalOutputModels()) { - vector mm = transformer->detachAdditionalOutputModels(); + vector mm = transformer->getAdditionalOutputModels(); toNotifyOfMore[m_handlers[transformer]] = mm; } else { toNotifyOfNoMore.push_back(m_handlers[transformer]); @@ -299,11 +317,9 @@ m_mutex.unlock(); - // These calls have to be made without the mutex held, as they may - // ultimately call back on us (e.g. we have one baroque situation - // where this could trigger a command to create a layer, which - // triggers the command history to clip the stack, which deletes a - // spare old model, which calls back on our modelAboutToBeDeleted) + // We make these calls without the mutex held, in case they + // ultimately call back on us - not such a concern as in the old + // model lifecycle but just in case for (const auto &i: toNotifyOfMore) { i.first->moreModelsAvailable(i.second); @@ -322,43 +338,6 @@ delete transformer; } -void -ModelTransformerFactory::modelAboutToBeDeleted(Model *m) -{ - TransformerSet affected; - - { - QMutexLocker locker(&m_mutex); - - for (TransformerSet::iterator i = m_runningTransformers.begin(); - i != m_runningTransformers.end(); ++i) { - - ModelTransformer *t = *i; - - if (t->getInputModel() == m) { - affected.insert(t); - } else { - vector mm = t->getOutputModels(); - for (int i = 0; i < (int)mm.size(); ++i) { - if (mm[i] == m) affected.insert(t); - } - } - } - } - - for (TransformerSet::iterator i = affected.begin(); - i != affected.end(); ++i) { - - ModelTransformer *t = *i; - - t->abandon(); - - t->wait(); // this should eventually call back on - // transformerFinished, which will remove from - // m_runningTransformers and delete. - } -} - bool ModelTransformerFactory::haveRunningTransformers() const { diff -r 565575463752 -r fe3f7f8df3a3 transform/ModelTransformerFactory.h --- a/transform/ModelTransformerFactory.h Wed Jun 26 14:59:09 2019 +0100 +++ b/transform/ModelTransformerFactory.h Wed Jun 26 17:25:20 2019 +0100 @@ -46,11 +46,11 @@ virtual bool configure(ModelTransformer::Input &input, Transform &transform, Vamp::PluginBase *plugin, - Model *&inputModel, + ModelId &inputModel, AudioPlaySource *source, sv_frame_t startFrame, sv_frame_t duration, - const QMap &modelMap, + const QMap &modelMap, QStringList candidateModelNames, QString defaultModelName) = 0; }; @@ -59,14 +59,14 @@ * Fill out the configuration for the given transform (may include * asking the user by calling back on the UserConfigurator). * Returns the selected input model and channel if the transform - * is acceptable, or an input with a null model if the operation + * is acceptable, or an input with no model if the operation * should be cancelled. Audio play source may be used to audition * effects plugins, if provided. */ ModelTransformer::Input getConfigurationForTransform(Transform &transform, - const std::vector &candidateInputModels, - Model *defaultInputModel, + std::vector candidateInputModels, + ModelId defaultInputModel, AudioPlaySource *source = 0, sv_frame_t startFrame = 0, sv_frame_t duration = 0, @@ -77,7 +77,7 @@ virtual ~AdditionalModelHandler() { } // Exactly one of these functions will be called - virtual void moreModelsAvailable(std::vector models) = 0; + virtual void moreModelsAvailable(std::vector models) = 0; virtual void noMoreModelsAvailable() = 0; }; @@ -104,10 +104,10 @@ * The returned model is owned by the caller and must be deleted * when no longer needed. */ - Model *transform(const Transform &transform, - const ModelTransformer::Input &input, - QString &message, - AdditionalModelHandler *handler = 0); + ModelId transform(const Transform &transform, + const ModelTransformer::Input &input, + QString &message, + AdditionalModelHandler *handler = 0); /** * Return the multiple output models resulting from applying the @@ -141,7 +141,7 @@ * The returned models are owned by the caller and must be deleted * when no longer needed. */ - std::vector transformMultiple(const Transforms &transform, + std::vector transformMultiple(const Transforms &transform, const ModelTransformer::Input &input, QString &message, AdditionalModelHandler *handler = 0); @@ -154,8 +154,6 @@ protected slots: void transformerFinished(); - void modelAboutToBeDeleted(Model *); - protected: ModelTransformer *createTransformer(const Transforms &transforms, const ModelTransformer::Input &input); diff -r 565575463752 -r fe3f7f8df3a3 transform/RealTimeEffectModelTransformer.cpp --- a/transform/RealTimeEffectModelTransformer.cpp Wed Jun 26 14:59:09 2019 +0100 +++ b/transform/RealTimeEffectModelTransformer.cpp Wed Jun 26 17:25:20 2019 +0100 @@ -58,8 +58,11 @@ return; } - DenseTimeValueModel *input = getConformingInput(); - if (!input) return; + auto input = ModelById::getAs(getInputModel()); + if (!input) { + SVCERR << "RealTimeEffectModelTransformer: Input is absent or of wrong type" << endl; + return; + } m_plugin = factory->instantiatePlugin(pluginId, 0, 0, input->getSampleRate(), @@ -87,19 +90,21 @@ outputChannels = input->getChannelCount(); } - WritableWaveFileModel *model = new WritableWaveFileModel + auto model = std::make_shared (input->getSampleRate(), outputChannels); - m_outputs.push_back(model); + ModelById::add(model); + m_outputs.push_back(model->getId()); } else { - SparseTimeValueModel *model = new SparseTimeValueModel - (input->getSampleRate(), transform.getBlockSize(), 0.0, 0.0, false); - + auto model = std::make_shared + (input->getSampleRate(), transform.getBlockSize(), + 0.0, 0.0, false); if (m_units != "") model->setScaleUnits(m_units); - m_outputs.push_back(model); + ModelById::add(model); + m_outputs.push_back(model->getId()); } } @@ -108,38 +113,39 @@ delete m_plugin; } -DenseTimeValueModel * -RealTimeEffectModelTransformer::getConformingInput() -{ - DenseTimeValueModel *dtvm = - dynamic_cast(getInputModel()); - if (!dtvm) { - SVDEBUG << "RealTimeEffectModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << endl; - } - return dtvm; -} - void RealTimeEffectModelTransformer::run() { - DenseTimeValueModel *input = getConformingInput(); - if (!input) return; - - while (!input->isReady() && !m_abandoned) { - SVDEBUG << "RealTimeEffectModelTransformer::run: Waiting for input model to be ready..." << endl; - usleep(500000); - } - if (m_abandoned) { + if (m_outputs.empty()) { + abandon(); return; } - if (m_outputs.empty()) { + + bool ready = false; + while (!ready && !m_abandoned) { + { // scope so as to release input shared_ptr before sleeping + auto input = ModelById::getAs(getInputModel()); + if (!input) { + abandon(); + return; + } + ready = input->isReady(); + } + if (!ready) { + SVDEBUG << "RealTimeEffectModelTransformer::run: Waiting for input model to be ready..." << endl; + usleep(500000); + } + } + if (m_abandoned) return; + + auto input = ModelById::getAs(getInputModel()); + if (!input) { + abandon(); return; } - - SparseTimeValueModel *stvm = - dynamic_cast(m_outputs[0]); - WritableWaveFileModel *wwfm = - dynamic_cast(m_outputs[0]); + + auto stvm = ModelById::getAs(m_outputs[0]); + auto wwfm = ModelById::getAs(m_outputs[0]); if (!stvm && !wwfm) { return; @@ -157,8 +163,8 @@ float **inbufs = m_plugin->getAudioInputBuffers(); - sv_frame_t startFrame = m_input.getModel()->getStartFrame(); - sv_frame_t endFrame = m_input.getModel()->getEndFrame(); + sv_frame_t startFrame = input->getStartFrame(); + sv_frame_t endFrame = input->getEndFrame(); Transform transform = m_transforms[0]; @@ -242,20 +248,6 @@ } } -/* - cerr << "Input for plugin: " << m_plugin->getAudioInputCount() << " channels "<< endl; - - for (int ch = 0; ch < m_plugin->getAudioInputCount(); ++ch) { - cerr << "Input channel " << ch << endl; - for (int i = 0; i < 100; ++i) { - cerr << inbufs[ch][i] << " "; - if (isnan(inbufs[ch][i])) { - cerr << "\n\nWARNING: NaN in audio input" << endl; - } - } - } -*/ - m_plugin->run(RealTime::frame2RealTime(blockFrame, sampleRate)); if (stvm) { diff -r 565575463752 -r fe3f7f8df3a3 transform/RealTimeEffectModelTransformer.h --- a/transform/RealTimeEffectModelTransformer.h Wed Jun 26 14:59:09 2019 +0100 +++ b/transform/RealTimeEffectModelTransformer.h Wed Jun 26 17:25:20 2019 +0100 @@ -36,9 +36,6 @@ QString m_units; RealTimePluginInstance *m_plugin; int m_outputNo; - - // just casts - DenseTimeValueModel *getConformingInput(); }; #endif