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 <vamp-hostsdk/PluginHostAdapter.h>
Chris@320: 
Chris@320: #include <iostream>
Chris@320: #include <set>
Chris@320: 
Chris@320: #include <QRegExp>
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<Model *> &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<QString, Model *> 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@686:     std::cerr << "last configuration: " << configurationXml << std::endl;
Chris@320: 
Chris@320:     Vamp::PluginBase *plugin = 0;
Chris@320: 
Chris@320:     if (FeatureExtractionPluginFactory::instanceFor(id)) {
Chris@320: 
Chris@350:         std::cerr << "getConfigurationForTransform: instantiating Vamp plugin" << std::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@350:             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@361:                                    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<ModelTransformer *>(s);
Chris@320:     
Chris@690: //    SVDEBUG << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << endl;
Chris@320: 
Chris@331:     if (!transformer) {
Chris@331: 	std::cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << std::endl;
Chris@320: 	return;
Chris@320:     }
Chris@320: 
Chris@331:     if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) {
Chris@331:         std::cerr << "WARNING: ModelTransformerFactory::transformerFinished(" 
Chris@331:                   << transformer
Chris@331:                   << "): I have no record of this transformer running!"
Chris@320:                   << std::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: