Chris@320: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@320: Chris@320: /* Chris@320: Sonic Visualiser Chris@320: An audio file viewer and annotation editor. Chris@320: Centre for Digital Music, Queen Mary, University of London. Chris@320: This file copyright 2006 Chris Cannam and QMUL. Chris@320: Chris@320: This program is free software; you can redistribute it and/or Chris@320: modify it under the terms of the GNU General Public License as Chris@320: published by the Free Software Foundation; either version 2 of the Chris@320: License, or (at your option) any later version. See the file Chris@320: COPYING included with this distribution for more information. Chris@320: */ Chris@320: Chris@331: #include "ModelTransformerFactory.h" Chris@320: Chris@331: #include "FeatureExtractionModelTransformer.h" Chris@331: #include "RealTimeEffectModelTransformer.h" Chris@320: Chris@332: #include "TransformFactory.h" Chris@332: Chris@389: #include "base/AudioPlaySource.h" Chris@389: Chris@320: #include "plugin/FeatureExtractionPluginFactory.h" Chris@320: #include "plugin/RealTimePluginFactory.h" Chris@320: #include "plugin/PluginXml.h" Chris@320: Chris@320: #include "data/model/DenseTimeValueModel.h" Chris@320: Chris@475: #include Chris@320: Chris@320: #include Chris@320: #include Chris@1727: #include Chris@320: Chris@320: #include Chris@1703: #include Chris@320: Chris@850: using std::vector; Chris@1727: using std::set; Chris@1727: using std::map; Chris@1830: using std::shared_ptr; Chris@1830: using std::make_shared; Chris@850: Chris@331: ModelTransformerFactory * Chris@331: ModelTransformerFactory::m_instance = new ModelTransformerFactory; Chris@320: Chris@331: ModelTransformerFactory * Chris@331: ModelTransformerFactory::getInstance() Chris@320: { Chris@320: return m_instance; Chris@320: } Chris@320: Chris@331: ModelTransformerFactory::~ModelTransformerFactory() Chris@320: { Chris@320: } Chris@320: Chris@350: ModelTransformer::Input Chris@350: ModelTransformerFactory::getConfigurationForTransform(Transform &transform, Chris@1740: vector candidateInputModels, Chris@1740: ModelId defaultInputModel, Chris@389: AudioPlaySource *source, Chris@1048: sv_frame_t startFrame, Chris@1048: sv_frame_t duration, Chris@653: UserConfigurator *configurator) Chris@320: { Chris@1703: QMutexLocker locker(&m_mutex); Chris@1703: Chris@1740: ModelTransformer::Input input({}); Chris@350: Chris@350: if (candidateInputModels.empty()) return input; Chris@320: Chris@320: //!!! This will need revision -- we'll have to have a callback Chris@320: //from the dialog for when the candidate input model is changed, Chris@320: //as we'll need to reinitialise the channel settings in the dialog Chris@1740: ModelId inputModel = candidateInputModels[0]; Chris@320: QStringList candidateModelNames; Chris@345: QString defaultModelName; Chris@1740: QMap modelMap; Chris@1740: Chris@1740: sv_samplerate_t defaultSampleRate; Chris@1740: { auto im = ModelById::get(inputModel); Chris@1740: if (!im) return input; Chris@1740: defaultSampleRate = im->getSampleRate(); Chris@1740: } Chris@1740: Chris@1740: for (int i = 0; in_range_for(candidateInputModels, i); ++i) { Chris@1740: Chris@1740: auto model = ModelById::get(candidateInputModels[i]); Chris@1740: if (!model) return input; Chris@1740: Chris@1740: QString modelName = model->objectName(); Chris@320: QString origModelName = modelName; Chris@320: int dupcount = 1; Chris@653: while (modelMap.contains(modelName)) { Chris@320: modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount); Chris@320: } Chris@1740: Chris@320: modelMap[modelName] = candidateInputModels[i]; Chris@320: candidateModelNames.push_back(modelName); Chris@1740: Chris@345: if (candidateInputModels[i] == defaultInputModel) { Chris@345: defaultModelName = modelName; Chris@345: } Chris@320: } Chris@320: Chris@350: QString id = transform.getPluginIdentifier(); Chris@320: Chris@653: bool ok = true; Chris@350: QString configurationXml = m_lastConfigurations[transform.getIdentifier()]; Chris@320: Chris@1264: SVDEBUG << "ModelTransformer: last configuration for identifier " << transform.getIdentifier() << ": " << configurationXml << endl; Chris@320: Chris@1830: shared_ptr plugin; Chris@320: Chris@1225: if (RealTimePluginFactory::instanceFor(id)) { Chris@320: Chris@1264: SVDEBUG << "ModelTransformerFactory::getConfigurationForTransform: instantiating real-time plugin" << endl; Chris@1264: Chris@320: RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id); Chris@320: Chris@1740: sv_samplerate_t sampleRate = defaultSampleRate; Chris@930: int blockSize = 1024; Chris@930: int channels = 1; Chris@653: if (source) { Chris@1321: sampleRate = source->getSourceSampleRate(); Chris@320: blockSize = source->getTargetBlockSize(); Chris@320: channels = source->getTargetChannelCount(); Chris@320: } Chris@320: Chris@1830: plugin = factory->instantiatePlugin Chris@320: (id, 0, 0, sampleRate, blockSize, channels); Chris@320: Chris@1225: } else { Chris@1225: Chris@1264: SVDEBUG << "ModelTransformerFactory::getConfigurationForTransform: instantiating Vamp plugin" << endl; Chris@1225: Chris@1830: plugin = Chris@1225: FeatureExtractionPluginFactory::instance()->instantiatePlugin Chris@1740: (id, float(defaultSampleRate)); Chris@320: } Chris@320: Chris@320: if (plugin) { Chris@320: Chris@350: // Ensure block size etc are valid Chris@350: TransformFactory::getInstance()-> Chris@350: makeContextConsistentWithPlugin(transform, plugin); Chris@320: Chris@350: // Prepare the plugin with any existing parameters already Chris@350: // found in the transform Chris@350: TransformFactory::getInstance()-> Chris@350: setPluginParameters(transform, plugin); Chris@350: Chris@350: // For this interactive usage, we want to override those with Chris@350: // whatever the user chose last time around Chris@350: PluginXml(plugin).setParametersFromXml(configurationXml); Chris@320: Chris@653: if (configurator) { Chris@653: ok = configurator->configure(input, transform, plugin, Chris@653: inputModel, source, Chris@653: startFrame, duration, Chris@653: modelMap, Chris@653: candidateModelNames, Chris@653: defaultModelName); Chris@320: } Chris@320: Chris@516: Chris@350: TransformFactory::getInstance()-> Chris@350: makeContextConsistentWithPlugin(transform, plugin); Chris@350: Chris@350: configurationXml = PluginXml(plugin).toXmlString(); Chris@320: } Chris@320: Chris@350: if (ok) { Chris@350: m_lastConfigurations[transform.getIdentifier()] = configurationXml; Chris@350: input.setModel(inputModel); Chris@350: } Chris@320: Chris@350: return input; Chris@320: } Chris@320: Chris@331: ModelTransformer * Chris@850: ModelTransformerFactory::createTransformer(const Transforms &transforms, Chris@350: const ModelTransformer::Input &input) Chris@320: { Chris@1582: ModelTransformer *transformer = nullptr; Chris@320: Chris@850: QString id = transforms[0].getPluginIdentifier(); Chris@320: Chris@1225: if (RealTimePluginFactory::instanceFor(id)) { Chris@350: Chris@350: transformer = Chris@850: new RealTimeEffectModelTransformer(input, transforms[0]); Chris@350: Chris@320: } else { Chris@1225: Chris@1225: transformer = Chris@1225: new FeatureExtractionModelTransformer(input, transforms); Chris@320: } Chris@320: Chris@850: if (transformer) transformer->setObjectName(transforms[0].getIdentifier()); Chris@331: return transformer; Chris@320: } Chris@320: Chris@1740: ModelId Chris@350: ModelTransformerFactory::transform(const Transform &transform, Chris@361: const ModelTransformer::Input &input, Chris@877: QString &message, Chris@877: AdditionalModelHandler *handler) Chris@320: { Chris@690: SVDEBUG << "ModelTransformerFactory::transform: Constructing transformer with input model " << input.getModel() << endl; Chris@408: Chris@850: Transforms transforms; Chris@850: transforms.push_back(transform); Chris@1740: vector mm = transformMultiple(transforms, input, message, handler); Chris@1740: if (mm.empty()) return {}; Chris@850: else return mm[0]; Chris@320: } Chris@320: Chris@1740: vector Chris@849: ModelTransformerFactory::transformMultiple(const Transforms &transforms, Chris@849: const ModelTransformer::Input &input, Chris@877: QString &message, Chris@877: AdditionalModelHandler *handler) Chris@849: { Chris@849: SVDEBUG << "ModelTransformerFactory::transformMultiple: Constructing transformer with input model " << input.getModel() << endl; Chris@849: Chris@1703: QMutexLocker locker(&m_mutex); Chris@1740: Chris@1740: auto inputModel = ModelById::get(input.getModel()); Chris@1740: if (!inputModel) return {}; Chris@1703: Chris@849: ModelTransformer *t = createTransformer(transforms, input); Chris@1740: if (!t) return {}; Chris@849: Chris@877: if (handler) { Chris@877: m_handlers[t] = handler; Chris@877: } Chris@849: Chris@849: m_runningTransformers.insert(t); Chris@849: Chris@877: connect(t, SIGNAL(finished()), this, SLOT(transformerFinished())); Chris@877: Chris@849: t->start(); Chris@1740: vector models = t->getOutputModels(); Chris@1740: Chris@850: if (!models.empty()) { Chris@1740: QString imn = inputModel->objectName(); Chris@849: QString trn = Chris@849: TransformFactory::getInstance()->getTransformFriendlyName Chris@850: (transforms[0].getIdentifier()); Chris@1740: for (int i = 0; in_range_for(models, i); ++i) { Chris@1740: auto model = ModelById::get(models[i]); Chris@1740: if (!model) continue; Chris@850: if (imn != "") { Chris@850: if (trn != "") { Chris@1740: model->setObjectName(tr("%1: %2").arg(imn).arg(trn)); Chris@850: } else { Chris@1740: model->setObjectName(imn); Chris@850: } Chris@850: } else if (trn != "") { Chris@1740: model->setObjectName(trn); Chris@849: } Chris@849: } Chris@849: } else { Chris@849: t->wait(); Chris@849: } Chris@849: Chris@849: message = t->getMessage(); Chris@849: Chris@850: return models; Chris@849: } Chris@849: Chris@320: void Chris@331: ModelTransformerFactory::transformerFinished() Chris@320: { Chris@320: QObject *s = sender(); Chris@331: ModelTransformer *transformer = dynamic_cast(s); Chris@320: Chris@690: // SVDEBUG << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << endl; Chris@320: Chris@331: if (!transformer) { Chris@1429: cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << endl; Chris@1429: return; Chris@320: } Chris@320: Chris@1727: m_mutex.lock(); Chris@1727: Chris@331: if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) { Chris@843: cerr << "WARNING: ModelTransformerFactory::transformerFinished(" Chris@331: << transformer Chris@331: << "): I have no record of this transformer running!" Chris@843: << endl; Chris@320: } Chris@320: Chris@331: m_runningTransformers.erase(transformer); Chris@320: Chris@1740: map> toNotifyOfMore; Chris@1727: vector toNotifyOfNoMore; Chris@1727: Chris@877: if (m_handlers.find(transformer) != m_handlers.end()) { Chris@877: if (transformer->willHaveAdditionalOutputModels()) { Chris@1740: vector mm = transformer->getAdditionalOutputModels(); Chris@1727: toNotifyOfMore[m_handlers[transformer]] = mm; Chris@878: } else { Chris@1727: toNotifyOfNoMore.push_back(m_handlers[transformer]); Chris@877: } Chris@877: m_handlers.erase(transformer); Chris@877: } Chris@877: Chris@1727: m_mutex.unlock(); Chris@1727: Chris@1740: // We make these calls without the mutex held, in case they Chris@1740: // ultimately call back on us - not such a concern as in the old Chris@1740: // model lifecycle but just in case Chris@1727: Chris@1727: for (const auto &i: toNotifyOfMore) { Chris@1727: i.first->moreModelsAvailable(i.second); Chris@1727: } Chris@1727: for (AdditionalModelHandler *handler: toNotifyOfNoMore) { Chris@1727: handler->noMoreModelsAvailable(); Chris@1727: } Chris@1727: Chris@1079: if (transformer->isAbandoned()) { Chris@1079: if (transformer->getMessage() != "") { Chris@1079: emit transformFailed("", transformer->getMessage()); Chris@1079: } Chris@1079: } Chris@1079: Chris@331: transformer->wait(); // unnecessary but reassuring Chris@331: delete transformer; Chris@320: } Chris@320: Chris@1703: bool Chris@1703: ModelTransformerFactory::haveRunningTransformers() const Chris@1703: { Chris@1703: QMutexLocker locker(&m_mutex); Chris@1703: Chris@1703: return (!m_runningTransformers.empty()); Chris@1703: }