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@320: #include "plugin/FeatureExtractionPluginFactory.h" Chris@320: #include "plugin/RealTimePluginFactory.h" Chris@320: #include "plugin/PluginXml.h" Chris@320: Chris@320: #include "widgets/PluginParameterDialog.h" Chris@320: Chris@320: #include "data/model/DenseTimeValueModel.h" Chris@320: Chris@320: #include "vamp-sdk/PluginHostAdapter.h" Chris@320: Chris@320: #include "audioio/AudioCallbackPlaySource.h" //!!! shouldn't include here 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@320: bool Chris@331: ModelTransformerFactory::getChannelRange(TransformId identifier, Vamp::PluginBase *plugin, Chris@332: int &minChannels, int &maxChannels) Chris@320: { Chris@320: Vamp::Plugin *vp = 0; Chris@320: if ((vp = dynamic_cast(plugin)) || Chris@320: (vp = dynamic_cast(plugin))) { Chris@320: minChannels = vp->getMinChannelCount(); Chris@320: maxChannels = vp->getMaxChannelCount(); Chris@320: return true; Chris@320: } else { Chris@332: return TransformFactory::getInstance()-> Chris@332: getTransformChannelRange(identifier, minChannels, maxChannels); Chris@320: } Chris@320: } Chris@320: Chris@320: Model * Chris@331: ModelTransformerFactory::getConfigurationForTransformer(TransformId identifier, Chris@345: const std::vector &candidateInputModels, Chris@345: Model *defaultInputModel, Chris@345: PluginTransformer::ExecutionContext &context, Chris@345: QString &configurationXml, Chris@345: AudioCallbackPlaySource *source, Chris@345: size_t startFrame, Chris@345: size_t duration) Chris@320: { Chris@320: if (candidateInputModels.empty()) return 0; 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@320: std::map 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@320: while (modelMap.find(modelName) != modelMap.end()) { 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@320: QString id = identifier.section(':', 0, 2); Chris@320: QString output = identifier.section(':', 3); Chris@320: QString outputLabel = ""; Chris@320: QString outputDescription = ""; Chris@320: Chris@320: bool ok = false; Chris@320: configurationXml = m_lastConfigurations[identifier]; Chris@320: Chris@320: // std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl; Chris@320: Chris@320: Vamp::PluginBase *plugin = 0; Chris@320: Chris@320: bool frequency = false; Chris@320: bool effect = false; Chris@320: bool generator = false; Chris@320: Chris@320: if (FeatureExtractionPluginFactory::instanceFor(id)) { Chris@320: Chris@328: std::cerr << "getConfigurationForTransformer: 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@320: if (vp) { Chris@320: Chris@320: plugin = vp; Chris@320: frequency = (vp->getInputDomain() == Vamp::Plugin::FrequencyDomain); Chris@320: Chris@320: std::vector od = Chris@320: vp->getOutputDescriptors(); Chris@320: if (od.size() > 1) { Chris@320: for (size_t i = 0; i < od.size(); ++i) { Chris@320: if (od[i].identifier == output.toStdString()) { Chris@320: outputLabel = od[i].name.c_str(); Chris@320: outputDescription = od[i].description.c_str(); Chris@320: break; Chris@320: } Chris@320: } Chris@320: } Chris@320: } 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: if (desc->audioInputPortCount > 0 && Chris@320: desc->audioOutputPortCount > 0 && Chris@320: !desc->isSynth) { Chris@320: effect = true; Chris@320: } Chris@320: Chris@320: if (desc->audioInputPortCount == 0) { Chris@320: generator = true; Chris@320: } Chris@320: Chris@320: if (output != "A") { Chris@320: int outputNo = output.toInt(); Chris@320: if (outputNo >= 0 && outputNo < int(desc->controlOutputPortCount)) { Chris@320: outputLabel = desc->controlOutputPortNames[outputNo].c_str(); Chris@320: } Chris@320: } Chris@320: Chris@320: size_t sampleRate = inputModel->getSampleRate(); Chris@320: size_t blockSize = 1024; Chris@320: size_t channels = 1; Chris@320: if (effect && 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: if (effect && source && rtp) { Chris@320: source->setAuditioningPlugin(rtp); Chris@320: } Chris@320: } Chris@320: Chris@320: if (plugin) { Chris@320: Chris@328: context = PluginTransformer::ExecutionContext(context.channel, plugin); Chris@320: Chris@320: if (configurationXml != "") { Chris@320: PluginXml(plugin).setParametersFromXml(configurationXml); Chris@320: } Chris@320: Chris@320: int sourceChannels = 1; Chris@320: if (dynamic_cast(inputModel)) { Chris@320: sourceChannels = dynamic_cast(inputModel) Chris@320: ->getChannelCount(); Chris@320: } Chris@320: Chris@320: int minChannels = 1, maxChannels = sourceChannels; Chris@320: getChannelRange(identifier, plugin, minChannels, maxChannels); Chris@320: Chris@320: int targetChannels = sourceChannels; Chris@320: if (!effect) { Chris@320: if (sourceChannels < minChannels) targetChannels = minChannels; Chris@320: if (sourceChannels > maxChannels) targetChannels = maxChannels; Chris@320: } Chris@320: Chris@320: int defaultChannel = context.channel; Chris@320: Chris@320: PluginParameterDialog *dialog = new PluginParameterDialog(plugin); Chris@320: Chris@320: if (candidateModelNames.size() > 1 && !generator) { Chris@345: dialog->setCandidateInputModels(candidateModelNames, Chris@345: defaultModelName); Chris@320: } Chris@320: Chris@320: if (startFrame != 0 || duration != 0) { Chris@320: dialog->setShowSelectionOnlyOption(true); Chris@320: } Chris@320: Chris@320: if (targetChannels > 0) { Chris@320: dialog->setChannelArrangement(sourceChannels, targetChannels, Chris@320: defaultChannel); Chris@320: } Chris@320: Chris@320: dialog->setOutputLabel(outputLabel, outputDescription); Chris@320: Chris@320: dialog->setShowProcessingOptions(true, frequency); Chris@320: Chris@320: if (dialog->exec() == QDialog::Accepted) { Chris@320: ok = true; Chris@320: } Chris@320: Chris@320: QString selectedInput = dialog->getInputModel(); Chris@320: if (selectedInput != "") { Chris@320: if (modelMap.find(selectedInput) != modelMap.end()) { Chris@320: inputModel = modelMap[selectedInput]; Chris@320: std::cerr << "Found selected input \"" << selectedInput.toStdString() << "\" in model map, result is " << inputModel << std::endl; Chris@320: } else { Chris@320: std::cerr << "Failed to find selected input \"" << selectedInput.toStdString() << "\" in model map" << std::endl; Chris@320: } Chris@320: } else { Chris@320: std::cerr << "Selected input empty: \"" << selectedInput.toStdString() << "\"" << std::endl; Chris@320: } Chris@320: Chris@320: configurationXml = PluginXml(plugin).toXmlString(); Chris@320: context.channel = dialog->getChannel(); Chris@320: Chris@320: if (startFrame != 0 || duration != 0) { Chris@320: if (dialog->getSelectionOnly()) { Chris@320: context.startFrame = startFrame; Chris@320: context.duration = duration; Chris@320: } Chris@320: } Chris@320: Chris@320: dialog->getProcessingParameters(context.stepSize, Chris@320: context.blockSize, Chris@320: context.windowType); Chris@320: Chris@320: context.makeConsistentWithPlugin(plugin); Chris@320: Chris@320: delete dialog; Chris@320: Chris@320: if (effect && source) { Chris@320: source->setAuditioningPlugin(0); // will delete our plugin Chris@320: } else { Chris@320: delete plugin; Chris@320: } Chris@320: } Chris@320: Chris@320: if (ok) m_lastConfigurations[identifier] = configurationXml; Chris@320: Chris@320: return ok ? inputModel : 0; Chris@320: } Chris@320: Chris@328: PluginTransformer::ExecutionContext Chris@331: ModelTransformerFactory::getDefaultContextForTransformer(TransformId identifier, Chris@320: Model *inputModel) Chris@320: { Chris@328: PluginTransformer::ExecutionContext context(-1); Chris@320: Chris@320: QString id = identifier.section(':', 0, 2); Chris@320: Chris@320: if (FeatureExtractionPluginFactory::instanceFor(id)) { Chris@320: Chris@320: Vamp::Plugin *vp = Chris@320: FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin Chris@320: (id, inputModel ? inputModel->getSampleRate() : 48000); Chris@320: Chris@320: if (vp) { Chris@328: context = PluginTransformer::ExecutionContext(-1, vp); Chris@320: delete vp; Chris@320: } Chris@320: } Chris@320: Chris@320: return context; Chris@320: } Chris@320: Chris@331: ModelTransformer * Chris@331: ModelTransformerFactory::createTransformer(TransformId identifier, Model *inputModel, Chris@328: const PluginTransformer::ExecutionContext &context, Chris@320: QString configurationXml) Chris@320: { Chris@331: ModelTransformer *transformer = 0; Chris@320: Chris@320: QString id = identifier.section(':', 0, 2); Chris@320: QString output = identifier.section(':', 3); Chris@320: Chris@320: if (FeatureExtractionPluginFactory::instanceFor(id)) { Chris@332: transformer = new FeatureExtractionModelTransformer Chris@332: (inputModel, id, context, configurationXml, output); Chris@331: } else if (RealTimePluginFactory::instanceFor(id)) { Chris@332: transformer = new RealTimeEffectModelTransformer Chris@332: (inputModel, id, context, configurationXml, Chris@332: TransformFactory::getInstance()->getTransformUnits(identifier), Chris@332: output == "A" ? -1 : output.toInt()); Chris@320: } else { Chris@331: std::cerr << "ModelTransformerFactory::createTransformer: Unknown transform \"" Chris@320: << identifier.toStdString() << "\"" << std::endl; Chris@331: return transformer; Chris@320: } Chris@320: Chris@331: if (transformer) transformer->setObjectName(identifier); Chris@331: return transformer; Chris@320: } Chris@320: Chris@320: Model * Chris@331: ModelTransformerFactory::transform(TransformId identifier, Model *inputModel, Chris@328: const PluginTransformer::ExecutionContext &context, Chris@320: QString configurationXml) Chris@320: { Chris@331: ModelTransformer *t = createTransformer(identifier, inputModel, context, Chris@331: configurationXml); Chris@320: 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@320: QString imn = inputModel->objectName(); Chris@332: QString trn = Chris@332: TransformFactory::getInstance()->getTransformFriendlyName Chris@332: (identifier); 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@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@331: std::cerr << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << std::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: