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@320: Chris@320: #include Chris@320: 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@350: const std::vector &candidateInputModels, Chris@350: Model *defaultInputModel, Chris@389: AudioPlaySource *source, Chris@350: size_t startFrame, Chris@653: size_t duration, Chris@653: UserConfigurator *configurator) Chris@320: { Chris@350: ModelTransformer::Input input(0); 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@345: Model *inputModel = candidateInputModels[0]; Chris@320: QStringList candidateModelNames; Chris@345: QString defaultModelName; Chris@653: QMap modelMap; Chris@320: for (size_t i = 0; i < candidateInputModels.size(); ++i) { Chris@320: QString modelName = candidateInputModels[i]->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@320: modelMap[modelName] = candidateInputModels[i]; Chris@320: candidateModelNames.push_back(modelName); 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@843: cerr << "last configuration: " << configurationXml << endl; Chris@320: Chris@320: Vamp::PluginBase *plugin = 0; Chris@320: Chris@320: if (FeatureExtractionPluginFactory::instanceFor(id)) { Chris@320: Chris@843: cerr << "getConfigurationForTransform: instantiating Vamp plugin" << endl; Chris@320: Chris@320: Vamp::Plugin *vp = Chris@320: FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin Chris@320: (id, inputModel->getSampleRate()); Chris@320: Chris@653: plugin = vp; Chris@320: Chris@320: } else if (RealTimePluginFactory::instanceFor(id)) { Chris@320: Chris@320: RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id); Chris@320: const RealTimePluginDescriptor *desc = factory->getPluginDescriptor(id); Chris@320: Chris@320: size_t sampleRate = inputModel->getSampleRate(); Chris@320: size_t blockSize = 1024; Chris@320: size_t channels = 1; Chris@653: if (source) { Chris@320: sampleRate = source->getTargetSampleRate(); Chris@320: blockSize = source->getTargetBlockSize(); Chris@320: channels = source->getTargetChannelCount(); Chris@320: } Chris@320: Chris@320: RealTimePluginInstance *rtp = factory->instantiatePlugin Chris@320: (id, 0, 0, sampleRate, blockSize, channels); Chris@320: Chris@320: plugin = rtp; 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@653: delete plugin; 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@350: ModelTransformerFactory::createTransformer(const Transform &transform, Chris@350: const ModelTransformer::Input &input) Chris@320: { Chris@331: ModelTransformer *transformer = 0; Chris@320: Chris@350: QString id = transform.getPluginIdentifier(); Chris@320: Chris@320: if (FeatureExtractionPluginFactory::instanceFor(id)) { Chris@350: Chris@350: transformer = Chris@848: new FeatureExtractionModelTransformer(input, transform); Chris@350: Chris@331: } else if (RealTimePluginFactory::instanceFor(id)) { Chris@350: Chris@350: transformer = Chris@350: new RealTimeEffectModelTransformer(input, transform); Chris@350: Chris@320: } else { Chris@690: SVDEBUG << "ModelTransformerFactory::createTransformer: Unknown transform \"" Chris@687: << transform.getIdentifier() << "\"" << endl; Chris@331: return transformer; Chris@320: } Chris@320: Chris@350: if (transformer) transformer->setObjectName(transform.getIdentifier()); Chris@331: return transformer; Chris@320: } Chris@320: Chris@320: Model * Chris@350: ModelTransformerFactory::transform(const Transform &transform, Chris@361: const ModelTransformer::Input &input, Chris@848: QString &message) Chris@320: { Chris@690: SVDEBUG << "ModelTransformerFactory::transform: Constructing transformer with input model " << input.getModel() << endl; Chris@408: Chris@350: ModelTransformer *t = createTransformer(transform, input); Chris@320: if (!t) return 0; Chris@320: Chris@331: connect(t, SIGNAL(finished()), this, SLOT(transformerFinished())); Chris@320: Chris@328: m_runningTransformers.insert(t); Chris@320: Chris@320: t->start(); Chris@320: Model *model = t->detachOutputModel(); Chris@320: Chris@320: if (model) { Chris@350: QString imn = input.getModel()->objectName(); Chris@332: QString trn = Chris@332: TransformFactory::getInstance()->getTransformFriendlyName Chris@350: (transform.getIdentifier()); Chris@320: if (imn != "") { Chris@320: if (trn != "") { Chris@320: model->setObjectName(tr("%1: %2").arg(imn).arg(trn)); Chris@320: } else { Chris@320: model->setObjectName(imn); Chris@320: } Chris@320: } else if (trn != "") { Chris@320: model->setObjectName(trn); Chris@320: } Chris@320: } else { Chris@320: t->wait(); Chris@320: } Chris@320: Chris@361: message = t->getMessage(); Chris@361: Chris@320: return model; Chris@320: } Chris@320: 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@843: cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << endl; Chris@320: return; Chris@320: } Chris@320: 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@331: transformer->wait(); // unnecessary but reassuring Chris@331: delete transformer; Chris@320: } Chris@320: Chris@320: void Chris@331: ModelTransformerFactory::modelAboutToBeDeleted(Model *m) Chris@320: { Chris@328: TransformerSet affected; Chris@320: Chris@328: for (TransformerSet::iterator i = m_runningTransformers.begin(); Chris@328: i != m_runningTransformers.end(); ++i) { Chris@320: Chris@331: ModelTransformer *t = *i; Chris@320: Chris@320: if (t->getInputModel() == m || t->getOutputModel() == m) { Chris@320: affected.insert(t); Chris@320: } Chris@320: } Chris@320: Chris@328: for (TransformerSet::iterator i = affected.begin(); Chris@320: i != affected.end(); ++i) { Chris@320: Chris@331: ModelTransformer *t = *i; Chris@320: Chris@320: t->abandon(); Chris@320: Chris@320: t->wait(); // this should eventually call back on Chris@331: // transformerFinished, which will remove from Chris@328: // m_runningTransformers and delete. Chris@320: } Chris@320: } Chris@320: