annotate transform/RealTimeEffectModelTransformer.cpp @ 1496:fde8c497373f

Avoid crashing if an effects plugin can't be instantiated and so the output vector is empty in the transformer's run() method
author Chris Cannam
date Mon, 13 Aug 2018 15:25:32 +0100
parents 48e9f538e6e9
children 70e172e6cc59
rev   line source
Chris@320 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@320 2
Chris@320 3 /*
Chris@320 4 Sonic Visualiser
Chris@320 5 An audio file viewer and annotation editor.
Chris@320 6 Centre for Digital Music, Queen Mary, University of London.
Chris@320 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@320 8
Chris@320 9 This program is free software; you can redistribute it and/or
Chris@320 10 modify it under the terms of the GNU General Public License as
Chris@320 11 published by the Free Software Foundation; either version 2 of the
Chris@320 12 License, or (at your option) any later version. See the file
Chris@320 13 COPYING included with this distribution for more information.
Chris@320 14 */
Chris@320 15
Chris@331 16 #include "RealTimeEffectModelTransformer.h"
Chris@320 17
Chris@320 18 #include "plugin/RealTimePluginFactory.h"
Chris@320 19 #include "plugin/RealTimePluginInstance.h"
Chris@320 20 #include "plugin/PluginXml.h"
Chris@320 21
Chris@320 22 #include "data/model/Model.h"
Chris@320 23 #include "data/model/SparseTimeValueModel.h"
Chris@320 24 #include "data/model/DenseTimeValueModel.h"
Chris@320 25 #include "data/model/WritableWaveFileModel.h"
Chris@320 26 #include "data/model/WaveFileModel.h"
Chris@320 27
Chris@350 28 #include "TransformFactory.h"
Chris@350 29
Chris@320 30 #include <iostream>
Chris@320 31
Chris@350 32 RealTimeEffectModelTransformer::RealTimeEffectModelTransformer(Input in,
Chris@850 33 const Transform &t) :
Chris@850 34 ModelTransformer(in, t),
Chris@350 35 m_plugin(0)
Chris@320 36 {
Chris@850 37 Transform transform(t);
Chris@850 38 if (!transform.getBlockSize()) {
Chris@850 39 transform.setBlockSize(1024);
Chris@850 40 m_transforms[0] = transform;
Chris@850 41 }
Chris@850 42
Chris@350 43 m_units = TransformFactory::getInstance()->getTransformUnits
Chris@350 44 (transform.getIdentifier());
Chris@350 45 m_outputNo =
Chris@350 46 (transform.getOutput() == "A") ? -1 : transform.getOutput().toInt();
Chris@350 47
Chris@350 48 QString pluginId = transform.getPluginIdentifier();
Chris@350 49
Chris@1496 50 SVDEBUG << "RealTimeEffectModelTransformer::RealTimeEffectModelTransformer: plugin " << pluginId << ", output " << transform.getOutput() << endl;
Chris@320 51
Chris@320 52 RealTimePluginFactory *factory =
Chris@1429 53 RealTimePluginFactory::instanceFor(pluginId);
Chris@320 54
Chris@320 55 if (!factory) {
Chris@1496 56 SVCERR << "RealTimeEffectModelTransformer: No factory available for plugin id \""
Chris@1496 57 << pluginId << "\"" << endl;
Chris@1429 58 return;
Chris@320 59 }
Chris@320 60
Chris@350 61 DenseTimeValueModel *input = getConformingInput();
Chris@320 62 if (!input) return;
Chris@320 63
Chris@320 64 m_plugin = factory->instantiatePlugin(pluginId, 0, 0,
Chris@350 65 input->getSampleRate(),
Chris@850 66 transform.getBlockSize(),
Chris@320 67 input->getChannelCount());
Chris@320 68
Chris@320 69 if (!m_plugin) {
Chris@1496 70 SVCERR << "RealTimeEffectModelTransformer: Failed to instantiate plugin \""
Chris@1496 71 << pluginId << "\"" << endl;
Chris@1429 72 return;
Chris@320 73 }
Chris@320 74
Chris@850 75 TransformFactory::getInstance()->setPluginParameters(transform, m_plugin);
Chris@320 76
Chris@320 77 if (m_outputNo >= 0 &&
Chris@320 78 m_outputNo >= int(m_plugin->getControlOutputCount())) {
Chris@843 79 cerr << "RealTimeEffectModelTransformer: Plugin has fewer than desired " << m_outputNo << " control outputs" << endl;
Chris@320 80 return;
Chris@320 81 }
Chris@320 82
Chris@320 83 if (m_outputNo == -1) {
Chris@320 84
Chris@1039 85 int outputChannels = (int)m_plugin->getAudioOutputCount();
Chris@320 86 if (outputChannels > input->getChannelCount()) {
Chris@320 87 outputChannels = input->getChannelCount();
Chris@320 88 }
Chris@320 89
Chris@320 90 WritableWaveFileModel *model = new WritableWaveFileModel
Chris@320 91 (input->getSampleRate(), outputChannels);
Chris@320 92
Chris@849 93 m_outputs.push_back(model);
Chris@320 94
Chris@320 95 } else {
Chris@1429 96
Chris@320 97 SparseTimeValueModel *model = new SparseTimeValueModel
Chris@850 98 (input->getSampleRate(), transform.getBlockSize(), 0.0, 0.0, false);
Chris@320 99
Chris@350 100 if (m_units != "") model->setScaleUnits(m_units);
Chris@320 101
Chris@849 102 m_outputs.push_back(model);
Chris@320 103 }
Chris@320 104 }
Chris@320 105
Chris@331 106 RealTimeEffectModelTransformer::~RealTimeEffectModelTransformer()
Chris@320 107 {
Chris@320 108 delete m_plugin;
Chris@320 109 }
Chris@320 110
Chris@320 111 DenseTimeValueModel *
Chris@350 112 RealTimeEffectModelTransformer::getConformingInput()
Chris@320 113 {
Chris@320 114 DenseTimeValueModel *dtvm =
Chris@1429 115 dynamic_cast<DenseTimeValueModel *>(getInputModel());
Chris@320 116 if (!dtvm) {
Chris@1429 117 SVDEBUG << "RealTimeEffectModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << endl;
Chris@320 118 }
Chris@320 119 return dtvm;
Chris@320 120 }
Chris@320 121
Chris@320 122 void
Chris@331 123 RealTimeEffectModelTransformer::run()
Chris@320 124 {
Chris@350 125 DenseTimeValueModel *input = getConformingInput();
Chris@320 126 if (!input) return;
Chris@320 127
Chris@497 128 while (!input->isReady() && !m_abandoned) {
Chris@690 129 SVDEBUG << "RealTimeEffectModelTransformer::run: Waiting for input model to be ready..." << endl;
Chris@497 130 usleep(500000);
Chris@320 131 }
Chris@1496 132 if (m_abandoned) {
Chris@1496 133 return;
Chris@1496 134 }
Chris@1496 135 if (m_outputs.empty()) {
Chris@1496 136 return;
Chris@1496 137 }
Chris@1496 138
Chris@1496 139 SparseTimeValueModel *stvm =
Chris@1496 140 dynamic_cast<SparseTimeValueModel *>(m_outputs[0]);
Chris@1496 141 WritableWaveFileModel *wwfm =
Chris@1496 142 dynamic_cast<WritableWaveFileModel *>(m_outputs[0]);
Chris@320 143
Chris@1496 144 if (!stvm && !wwfm) {
Chris@1496 145 return;
Chris@1496 146 }
Chris@320 147
Chris@1496 148 if (stvm && (m_outputNo >= int(m_plugin->getControlOutputCount()))) {
Chris@1496 149 return;
Chris@1496 150 }
Chris@320 151
Chris@1040 152 sv_samplerate_t sampleRate = input->getSampleRate();
Chris@930 153 int channelCount = input->getChannelCount();
Chris@350 154 if (!wwfm && m_input.getChannel() != -1) channelCount = 1;
Chris@320 155
Chris@1039 156 sv_frame_t blockSize = m_plugin->getBufferSize();
Chris@320 157
Chris@320 158 float **inbufs = m_plugin->getAudioInputBuffers();
Chris@320 159
Chris@1039 160 sv_frame_t startFrame = m_input.getModel()->getStartFrame();
Chris@1039 161 sv_frame_t endFrame = m_input.getModel()->getEndFrame();
Chris@850 162
Chris@850 163 Transform transform = m_transforms[0];
Chris@320 164
Chris@850 165 RealTime contextStartRT = transform.getStartTime();
Chris@850 166 RealTime contextDurationRT = transform.getDuration();
Chris@350 167
Chris@1039 168 sv_frame_t contextStart =
Chris@350 169 RealTime::realTime2Frame(contextStartRT, sampleRate);
Chris@350 170
Chris@1039 171 sv_frame_t contextDuration =
Chris@350 172 RealTime::realTime2Frame(contextDurationRT, sampleRate);
Chris@320 173
Chris@320 174 if (contextStart == 0 || contextStart < startFrame) {
Chris@320 175 contextStart = startFrame;
Chris@320 176 }
Chris@320 177
Chris@320 178 if (contextDuration == 0) {
Chris@320 179 contextDuration = endFrame - contextStart;
Chris@320 180 }
Chris@320 181 if (contextStart + contextDuration > endFrame) {
Chris@320 182 contextDuration = endFrame - contextStart;
Chris@320 183 }
Chris@320 184
Chris@414 185 if (wwfm) {
Chris@414 186 wwfm->setStartFrame(contextStart);
Chris@414 187 }
Chris@320 188
Chris@1039 189 sv_frame_t blockFrame = contextStart;
Chris@320 190
Chris@1039 191 int prevCompletion = 0;
Chris@320 192
Chris@1039 193 sv_frame_t latency = m_plugin->getLatency();
Chris@320 194
Chris@320 195 while (blockFrame < contextStart + contextDuration + latency &&
Chris@320 196 !m_abandoned) {
Chris@320 197
Chris@1429 198 int completion = int
Chris@1429 199 ((((blockFrame - contextStart) / blockSize) * 99) /
Chris@1039 200 (1 + ((contextDuration) / blockSize)));
Chris@320 201
Chris@1429 202 sv_frame_t got = 0;
Chris@320 203
Chris@1429 204 if (channelCount == 1) {
Chris@320 205 if (inbufs && inbufs[0]) {
Chris@1096 206 auto data = input->getData
Chris@1096 207 (m_input.getChannel(), blockFrame, blockSize);
Chris@1096 208 got = data.size();
Chris@1096 209 for (sv_frame_t i = 0; i < got; ++i) {
Chris@1096 210 inbufs[0][i] = data[i];
Chris@1096 211 }
Chris@320 212 while (got < blockSize) {
Chris@1096 213 inbufs[0][got++] = 0.f;
Chris@320 214 }
Chris@975 215 for (int ch = 1; ch < (int)m_plugin->getAudioInputCount(); ++ch) {
Chris@1039 216 for (sv_frame_t i = 0; i < blockSize; ++i) {
Chris@975 217 inbufs[ch][i] = inbufs[0][i];
Chris@975 218 }
Chris@320 219 }
Chris@320 220 }
Chris@1429 221 } else {
Chris@429 222 if (inbufs && inbufs[0]) {
Chris@1096 223 auto data = input->getMultiChannelData
Chris@1096 224 (0, channelCount - 1, blockFrame, blockSize);
Chris@1096 225 if (!data.empty()) got = data[0].size();
Chris@1096 226 for (int ch = 0; ch < channelCount; ++ch) {
Chris@1096 227 for (sv_frame_t i = 0; i < got; ++i) {
Chris@1096 228 inbufs[ch][i] = data[ch][i];
Chris@1096 229 }
Chris@1096 230 }
Chris@429 231 while (got < blockSize) {
Chris@930 232 for (int ch = 0; ch < channelCount; ++ch) {
Chris@429 233 inbufs[ch][got] = 0.0;
Chris@429 234 }
Chris@429 235 ++got;
Chris@320 236 }
Chris@975 237 for (int ch = channelCount; ch < (int)m_plugin->getAudioInputCount(); ++ch) {
Chris@1039 238 for (sv_frame_t i = 0; i < blockSize; ++i) {
Chris@975 239 inbufs[ch][i] = inbufs[ch % channelCount][i];
Chris@975 240 }
Chris@320 241 }
Chris@320 242 }
Chris@1429 243 }
Chris@320 244
Chris@320 245 /*
Chris@843 246 cerr << "Input for plugin: " << m_plugin->getAudioInputCount() << " channels "<< endl;
Chris@320 247
Chris@930 248 for (int ch = 0; ch < m_plugin->getAudioInputCount(); ++ch) {
Chris@843 249 cerr << "Input channel " << ch << endl;
Chris@930 250 for (int i = 0; i < 100; ++i) {
Chris@843 251 cerr << inbufs[ch][i] << " ";
Chris@320 252 if (isnan(inbufs[ch][i])) {
Chris@843 253 cerr << "\n\nWARNING: NaN in audio input" << endl;
Chris@320 254 }
Chris@320 255 }
Chris@320 256 }
Chris@320 257 */
Chris@320 258
Chris@1040 259 m_plugin->run(RealTime::frame2RealTime(blockFrame, sampleRate));
Chris@320 260
Chris@320 261 if (stvm) {
Chris@320 262
Chris@320 263 float value = m_plugin->getControlOutputValue(m_outputNo);
Chris@320 264
Chris@1039 265 sv_frame_t pointFrame = blockFrame;
Chris@320 266 if (pointFrame > latency) pointFrame -= latency;
Chris@320 267 else pointFrame = 0;
Chris@320 268
Chris@320 269 stvm->addPoint(SparseTimeValueModel::Point
Chris@320 270 (pointFrame, value, ""));
Chris@320 271
Chris@320 272 } else if (wwfm) {
Chris@320 273
Chris@320 274 float **outbufs = m_plugin->getAudioOutputBuffers();
Chris@320 275
Chris@320 276 if (outbufs) {
Chris@320 277
Chris@320 278 if (blockFrame >= latency) {
Chris@1039 279 sv_frame_t writeSize = std::min
Chris@320 280 (blockSize,
Chris@320 281 contextStart + contextDuration + latency - blockFrame);
Chris@320 282 wwfm->addSamples(outbufs, writeSize);
Chris@320 283 } else if (blockFrame + blockSize >= latency) {
Chris@1039 284 sv_frame_t offset = latency - blockFrame;
Chris@1039 285 sv_frame_t count = blockSize - offset;
Chris@320 286 float **tmp = new float *[channelCount];
Chris@930 287 for (int c = 0; c < channelCount; ++c) {
Chris@320 288 tmp[c] = outbufs[c] + offset;
Chris@320 289 }
Chris@320 290 wwfm->addSamples(tmp, count);
Chris@320 291 delete[] tmp;
Chris@320 292 }
Chris@320 293 }
Chris@320 294 }
Chris@320 295
Chris@1429 296 if (blockFrame == contextStart || completion > prevCompletion) {
Chris@1133 297 // This setCompletion is probably misusing the completion
Chris@1133 298 // terminology, just as it was for WritableWaveFileModel
Chris@1429 299 if (stvm) stvm->setCompletion(completion);
Chris@1429 300 if (wwfm) wwfm->setWriteProportion(completion);
Chris@1429 301 prevCompletion = completion;
Chris@1429 302 }
Chris@320 303
Chris@1429 304 blockFrame += blockSize;
Chris@320 305 }
Chris@320 306
Chris@320 307 if (m_abandoned) return;
Chris@320 308
Chris@320 309 if (stvm) stvm->setCompletion(100);
Chris@1133 310 if (wwfm) wwfm->writeComplete();
Chris@320 311 }
Chris@320 312