annotate transform/RealTimeEffectModelTransformer.cpp @ 1752:6d09d68165a4 by-id

Further review of ById: make IDs only available when adding a model to the ById store, not by querying the item directly. This means any id encountered in the wild must have been added to the store at some point (even if later released), which simplifies reasoning about lifecycles
author Chris Cannam
date Fri, 05 Jul 2019 15:28:07 +0100
parents fe3f7f8df3a3
children fd7f127ecd89
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@1582 35 m_plugin(nullptr)
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@1740 61 auto input = ModelById::getAs<DenseTimeValueModel>(getInputModel());
Chris@1740 62 if (!input) {
Chris@1740 63 SVCERR << "RealTimeEffectModelTransformer: Input is absent or of wrong type" << endl;
Chris@1740 64 return;
Chris@1740 65 }
Chris@320 66
Chris@320 67 m_plugin = factory->instantiatePlugin(pluginId, 0, 0,
Chris@350 68 input->getSampleRate(),
Chris@850 69 transform.getBlockSize(),
Chris@320 70 input->getChannelCount());
Chris@320 71
Chris@320 72 if (!m_plugin) {
Chris@1496 73 SVCERR << "RealTimeEffectModelTransformer: Failed to instantiate plugin \""
Chris@1496 74 << pluginId << "\"" << endl;
Chris@1429 75 return;
Chris@320 76 }
Chris@320 77
Chris@850 78 TransformFactory::getInstance()->setPluginParameters(transform, m_plugin);
Chris@320 79
Chris@320 80 if (m_outputNo >= 0 &&
Chris@320 81 m_outputNo >= int(m_plugin->getControlOutputCount())) {
Chris@843 82 cerr << "RealTimeEffectModelTransformer: Plugin has fewer than desired " << m_outputNo << " control outputs" << endl;
Chris@320 83 return;
Chris@320 84 }
Chris@320 85
Chris@320 86 if (m_outputNo == -1) {
Chris@320 87
Chris@1039 88 int outputChannels = (int)m_plugin->getAudioOutputCount();
Chris@320 89 if (outputChannels > input->getChannelCount()) {
Chris@320 90 outputChannels = input->getChannelCount();
Chris@320 91 }
Chris@320 92
Chris@1740 93 auto model = std::make_shared<WritableWaveFileModel>
Chris@320 94 (input->getSampleRate(), outputChannels);
Chris@320 95
Chris@1752 96 m_outputs.push_back(ModelById::add(model));
Chris@320 97
Chris@320 98 } else {
Chris@1429 99
Chris@1740 100 auto model = std::make_shared<SparseTimeValueModel>
Chris@1740 101 (input->getSampleRate(), transform.getBlockSize(),
Chris@1740 102 0.0, 0.0, false);
Chris@350 103 if (m_units != "") model->setScaleUnits(m_units);
Chris@320 104
Chris@1752 105 m_outputs.push_back(ModelById::add(model));
Chris@320 106 }
Chris@320 107 }
Chris@320 108
Chris@331 109 RealTimeEffectModelTransformer::~RealTimeEffectModelTransformer()
Chris@320 110 {
Chris@320 111 delete m_plugin;
Chris@320 112 }
Chris@320 113
Chris@320 114 void
Chris@331 115 RealTimeEffectModelTransformer::run()
Chris@320 116 {
Chris@1740 117 if (m_outputs.empty()) {
Chris@1740 118 abandon();
Chris@1496 119 return;
Chris@1496 120 }
Chris@1740 121
Chris@1740 122 bool ready = false;
Chris@1740 123 while (!ready && !m_abandoned) {
Chris@1740 124 { // scope so as to release input shared_ptr before sleeping
Chris@1740 125 auto input = ModelById::getAs<DenseTimeValueModel>(getInputModel());
Chris@1740 126 if (!input) {
Chris@1740 127 abandon();
Chris@1740 128 return;
Chris@1740 129 }
Chris@1740 130 ready = input->isReady();
Chris@1740 131 }
Chris@1740 132 if (!ready) {
Chris@1740 133 SVDEBUG << "RealTimeEffectModelTransformer::run: Waiting for input model to be ready..." << endl;
Chris@1740 134 usleep(500000);
Chris@1740 135 }
Chris@1740 136 }
Chris@1740 137 if (m_abandoned) return;
Chris@1740 138
Chris@1740 139 auto input = ModelById::getAs<DenseTimeValueModel>(getInputModel());
Chris@1740 140 if (!input) {
Chris@1740 141 abandon();
Chris@1496 142 return;
Chris@1496 143 }
Chris@1740 144
Chris@1740 145 auto stvm = ModelById::getAs<SparseTimeValueModel>(m_outputs[0]);
Chris@1740 146 auto wwfm = ModelById::getAs<WritableWaveFileModel>(m_outputs[0]);
Chris@320 147
Chris@1496 148 if (!stvm && !wwfm) {
Chris@1496 149 return;
Chris@1496 150 }
Chris@320 151
Chris@1496 152 if (stvm && (m_outputNo >= int(m_plugin->getControlOutputCount()))) {
Chris@1496 153 return;
Chris@1496 154 }
Chris@320 155
Chris@1040 156 sv_samplerate_t sampleRate = input->getSampleRate();
Chris@930 157 int channelCount = input->getChannelCount();
Chris@350 158 if (!wwfm && m_input.getChannel() != -1) channelCount = 1;
Chris@320 159
Chris@1039 160 sv_frame_t blockSize = m_plugin->getBufferSize();
Chris@320 161
Chris@320 162 float **inbufs = m_plugin->getAudioInputBuffers();
Chris@320 163
Chris@1740 164 sv_frame_t startFrame = input->getStartFrame();
Chris@1740 165 sv_frame_t endFrame = input->getEndFrame();
Chris@850 166
Chris@850 167 Transform transform = m_transforms[0];
Chris@320 168
Chris@850 169 RealTime contextStartRT = transform.getStartTime();
Chris@850 170 RealTime contextDurationRT = transform.getDuration();
Chris@350 171
Chris@1039 172 sv_frame_t contextStart =
Chris@350 173 RealTime::realTime2Frame(contextStartRT, sampleRate);
Chris@350 174
Chris@1039 175 sv_frame_t contextDuration =
Chris@350 176 RealTime::realTime2Frame(contextDurationRT, sampleRate);
Chris@320 177
Chris@320 178 if (contextStart == 0 || contextStart < startFrame) {
Chris@320 179 contextStart = startFrame;
Chris@320 180 }
Chris@320 181
Chris@320 182 if (contextDuration == 0) {
Chris@320 183 contextDuration = endFrame - contextStart;
Chris@320 184 }
Chris@320 185 if (contextStart + contextDuration > endFrame) {
Chris@320 186 contextDuration = endFrame - contextStart;
Chris@320 187 }
Chris@320 188
Chris@414 189 if (wwfm) {
Chris@414 190 wwfm->setStartFrame(contextStart);
Chris@414 191 }
Chris@320 192
Chris@1039 193 sv_frame_t blockFrame = contextStart;
Chris@320 194
Chris@1039 195 int prevCompletion = 0;
Chris@320 196
Chris@1039 197 sv_frame_t latency = m_plugin->getLatency();
Chris@320 198
Chris@320 199 while (blockFrame < contextStart + contextDuration + latency &&
Chris@320 200 !m_abandoned) {
Chris@320 201
Chris@1429 202 int completion = int
Chris@1429 203 ((((blockFrame - contextStart) / blockSize) * 99) /
Chris@1039 204 (1 + ((contextDuration) / blockSize)));
Chris@320 205
Chris@1429 206 sv_frame_t got = 0;
Chris@320 207
Chris@1429 208 if (channelCount == 1) {
Chris@320 209 if (inbufs && inbufs[0]) {
Chris@1096 210 auto data = input->getData
Chris@1096 211 (m_input.getChannel(), blockFrame, blockSize);
Chris@1096 212 got = data.size();
Chris@1096 213 for (sv_frame_t i = 0; i < got; ++i) {
Chris@1096 214 inbufs[0][i] = data[i];
Chris@1096 215 }
Chris@320 216 while (got < blockSize) {
Chris@1096 217 inbufs[0][got++] = 0.f;
Chris@320 218 }
Chris@975 219 for (int ch = 1; ch < (int)m_plugin->getAudioInputCount(); ++ch) {
Chris@1039 220 for (sv_frame_t i = 0; i < blockSize; ++i) {
Chris@975 221 inbufs[ch][i] = inbufs[0][i];
Chris@975 222 }
Chris@320 223 }
Chris@320 224 }
Chris@1429 225 } else {
Chris@429 226 if (inbufs && inbufs[0]) {
Chris@1096 227 auto data = input->getMultiChannelData
Chris@1096 228 (0, channelCount - 1, blockFrame, blockSize);
Chris@1096 229 if (!data.empty()) got = data[0].size();
Chris@1096 230 for (int ch = 0; ch < channelCount; ++ch) {
Chris@1096 231 for (sv_frame_t i = 0; i < got; ++i) {
Chris@1096 232 inbufs[ch][i] = data[ch][i];
Chris@1096 233 }
Chris@1096 234 }
Chris@429 235 while (got < blockSize) {
Chris@930 236 for (int ch = 0; ch < channelCount; ++ch) {
Chris@429 237 inbufs[ch][got] = 0.0;
Chris@429 238 }
Chris@429 239 ++got;
Chris@320 240 }
Chris@975 241 for (int ch = channelCount; ch < (int)m_plugin->getAudioInputCount(); ++ch) {
Chris@1039 242 for (sv_frame_t i = 0; i < blockSize; ++i) {
Chris@975 243 inbufs[ch][i] = inbufs[ch % channelCount][i];
Chris@975 244 }
Chris@320 245 }
Chris@320 246 }
Chris@1429 247 }
Chris@320 248
Chris@1040 249 m_plugin->run(RealTime::frame2RealTime(blockFrame, sampleRate));
Chris@320 250
Chris@320 251 if (stvm) {
Chris@320 252
Chris@320 253 float value = m_plugin->getControlOutputValue(m_outputNo);
Chris@320 254
Chris@1039 255 sv_frame_t pointFrame = blockFrame;
Chris@320 256 if (pointFrame > latency) pointFrame -= latency;
Chris@320 257 else pointFrame = 0;
Chris@320 258
Chris@1651 259 stvm->add(Event(pointFrame, value, ""));
Chris@320 260
Chris@320 261 } else if (wwfm) {
Chris@320 262
Chris@320 263 float **outbufs = m_plugin->getAudioOutputBuffers();
Chris@320 264
Chris@320 265 if (outbufs) {
Chris@320 266
Chris@320 267 if (blockFrame >= latency) {
Chris@1039 268 sv_frame_t writeSize = std::min
Chris@320 269 (blockSize,
Chris@320 270 contextStart + contextDuration + latency - blockFrame);
Chris@320 271 wwfm->addSamples(outbufs, writeSize);
Chris@320 272 } else if (blockFrame + blockSize >= latency) {
Chris@1039 273 sv_frame_t offset = latency - blockFrame;
Chris@1039 274 sv_frame_t count = blockSize - offset;
Chris@320 275 float **tmp = new float *[channelCount];
Chris@930 276 for (int c = 0; c < channelCount; ++c) {
Chris@320 277 tmp[c] = outbufs[c] + offset;
Chris@320 278 }
Chris@320 279 wwfm->addSamples(tmp, count);
Chris@320 280 delete[] tmp;
Chris@320 281 }
Chris@320 282 }
Chris@320 283 }
Chris@320 284
Chris@1429 285 if (blockFrame == contextStart || completion > prevCompletion) {
Chris@1133 286 // This setCompletion is probably misusing the completion
Chris@1133 287 // terminology, just as it was for WritableWaveFileModel
Chris@1429 288 if (stvm) stvm->setCompletion(completion);
Chris@1429 289 if (wwfm) wwfm->setWriteProportion(completion);
Chris@1429 290 prevCompletion = completion;
Chris@1429 291 }
Chris@320 292
Chris@1429 293 blockFrame += blockSize;
Chris@320 294 }
Chris@320 295
Chris@320 296 if (m_abandoned) return;
Chris@320 297
Chris@320 298 if (stvm) stvm->setCompletion(100);
Chris@1133 299 if (wwfm) wwfm->writeComplete();
Chris@320 300 }
Chris@320 301