annotate transform/RealTimeEffectModelTransformer.cpp @ 1833:21c792334c2e sensible-delimited-data-strings

Rewrite all the DelimitedDataString stuff so as to return vectors of individual cell strings rather than having the classes add the delimiters themselves. Rename accordingly to names based on StringExport. Take advantage of this in the CSV writer code so as to properly quote cells that contain delimiter characters.
author Chris Cannam
date Fri, 03 Apr 2020 17:11:05 +0100
parents 5f8fbbde08ff
children
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 }
Chris@320 112
Chris@320 113 void
Chris@331 114 RealTimeEffectModelTransformer::run()
Chris@320 115 {
Chris@1740 116 if (m_outputs.empty()) {
Chris@1740 117 abandon();
Chris@1496 118 return;
Chris@1496 119 }
Chris@1740 120
Chris@1740 121 bool ready = false;
Chris@1740 122 while (!ready && !m_abandoned) {
Chris@1740 123 { // scope so as to release input shared_ptr before sleeping
Chris@1740 124 auto input = ModelById::getAs<DenseTimeValueModel>(getInputModel());
Chris@1740 125 if (!input) {
Chris@1740 126 abandon();
Chris@1740 127 return;
Chris@1740 128 }
Chris@1740 129 ready = input->isReady();
Chris@1740 130 }
Chris@1740 131 if (!ready) {
Chris@1740 132 SVDEBUG << "RealTimeEffectModelTransformer::run: Waiting for input model to be ready..." << endl;
Chris@1740 133 usleep(500000);
Chris@1740 134 }
Chris@1740 135 }
Chris@1740 136 if (m_abandoned) return;
Chris@1740 137
Chris@1740 138 auto input = ModelById::getAs<DenseTimeValueModel>(getInputModel());
Chris@1740 139 if (!input) {
Chris@1740 140 abandon();
Chris@1496 141 return;
Chris@1496 142 }
Chris@1740 143
Chris@1755 144 sv_samplerate_t sampleRate;
Chris@1755 145 int channelCount;
Chris@1755 146 sv_frame_t startFrame;
Chris@1755 147 sv_frame_t endFrame;
Chris@1755 148
Chris@1755 149 { // scope so as not to have this borrowed pointer retained around
Chris@1755 150 // the edges of the process loop
Chris@1755 151 auto input = ModelById::getAs<DenseTimeValueModel>(getInputModel());
Chris@1755 152 if (!input) {
Chris@1755 153 abandon();
Chris@1755 154 return;
Chris@1755 155 }
Chris@1755 156
Chris@1755 157 sampleRate = input->getSampleRate();
Chris@1755 158 channelCount = input->getChannelCount();
Chris@1755 159 startFrame = input->getStartFrame();
Chris@1755 160 endFrame = input->getEndFrame();
Chris@1755 161 }
Chris@1755 162
Chris@1740 163 auto stvm = ModelById::getAs<SparseTimeValueModel>(m_outputs[0]);
Chris@1740 164 auto wwfm = ModelById::getAs<WritableWaveFileModel>(m_outputs[0]);
Chris@320 165
Chris@1496 166 if (!stvm && !wwfm) {
Chris@1496 167 return;
Chris@1496 168 }
Chris@320 169
Chris@1496 170 if (stvm && (m_outputNo >= int(m_plugin->getControlOutputCount()))) {
Chris@1496 171 return;
Chris@1496 172 }
Chris@320 173
Chris@350 174 if (!wwfm && m_input.getChannel() != -1) channelCount = 1;
Chris@320 175
Chris@1039 176 sv_frame_t blockSize = m_plugin->getBufferSize();
Chris@320 177
Chris@320 178 float **inbufs = m_plugin->getAudioInputBuffers();
Chris@320 179
Chris@850 180 Transform transform = m_transforms[0];
Chris@320 181
Chris@850 182 RealTime contextStartRT = transform.getStartTime();
Chris@850 183 RealTime contextDurationRT = transform.getDuration();
Chris@350 184
Chris@1039 185 sv_frame_t contextStart =
Chris@350 186 RealTime::realTime2Frame(contextStartRT, sampleRate);
Chris@350 187
Chris@1039 188 sv_frame_t contextDuration =
Chris@350 189 RealTime::realTime2Frame(contextDurationRT, sampleRate);
Chris@320 190
Chris@320 191 if (contextStart == 0 || contextStart < startFrame) {
Chris@320 192 contextStart = startFrame;
Chris@320 193 }
Chris@320 194
Chris@320 195 if (contextDuration == 0) {
Chris@320 196 contextDuration = endFrame - contextStart;
Chris@320 197 }
Chris@320 198 if (contextStart + contextDuration > endFrame) {
Chris@320 199 contextDuration = endFrame - contextStart;
Chris@320 200 }
Chris@320 201
Chris@414 202 if (wwfm) {
Chris@414 203 wwfm->setStartFrame(contextStart);
Chris@414 204 }
Chris@320 205
Chris@1039 206 sv_frame_t blockFrame = contextStart;
Chris@320 207
Chris@1039 208 int prevCompletion = 0;
Chris@320 209
Chris@1039 210 sv_frame_t latency = m_plugin->getLatency();
Chris@320 211
Chris@320 212 while (blockFrame < contextStart + contextDuration + latency &&
Chris@320 213 !m_abandoned) {
Chris@320 214
Chris@1429 215 int completion = int
Chris@1429 216 ((((blockFrame - contextStart) / blockSize) * 99) /
Chris@1039 217 (1 + ((contextDuration) / blockSize)));
Chris@320 218
Chris@1429 219 sv_frame_t got = 0;
Chris@320 220
Chris@1755 221 auto input = ModelById::getAs<DenseTimeValueModel>(getInputModel());
Chris@1755 222 if (!input) {
Chris@1755 223 abandon();
Chris@1755 224 return;
Chris@1755 225 }
Chris@1755 226
Chris@1429 227 if (channelCount == 1) {
Chris@320 228 if (inbufs && inbufs[0]) {
Chris@1096 229 auto data = input->getData
Chris@1096 230 (m_input.getChannel(), blockFrame, blockSize);
Chris@1096 231 got = data.size();
Chris@1096 232 for (sv_frame_t i = 0; i < got; ++i) {
Chris@1096 233 inbufs[0][i] = data[i];
Chris@1096 234 }
Chris@320 235 while (got < blockSize) {
Chris@1096 236 inbufs[0][got++] = 0.f;
Chris@320 237 }
Chris@975 238 for (int ch = 1; ch < (int)m_plugin->getAudioInputCount(); ++ch) {
Chris@1039 239 for (sv_frame_t i = 0; i < blockSize; ++i) {
Chris@975 240 inbufs[ch][i] = inbufs[0][i];
Chris@975 241 }
Chris@320 242 }
Chris@320 243 }
Chris@1429 244 } else {
Chris@429 245 if (inbufs && inbufs[0]) {
Chris@1096 246 auto data = input->getMultiChannelData
Chris@1096 247 (0, channelCount - 1, blockFrame, blockSize);
Chris@1096 248 if (!data.empty()) got = data[0].size();
Chris@1096 249 for (int ch = 0; ch < channelCount; ++ch) {
Chris@1096 250 for (sv_frame_t i = 0; i < got; ++i) {
Chris@1096 251 inbufs[ch][i] = data[ch][i];
Chris@1096 252 }
Chris@1096 253 }
Chris@429 254 while (got < blockSize) {
Chris@930 255 for (int ch = 0; ch < channelCount; ++ch) {
Chris@429 256 inbufs[ch][got] = 0.0;
Chris@429 257 }
Chris@429 258 ++got;
Chris@320 259 }
Chris@975 260 for (int ch = channelCount; ch < (int)m_plugin->getAudioInputCount(); ++ch) {
Chris@1039 261 for (sv_frame_t i = 0; i < blockSize; ++i) {
Chris@975 262 inbufs[ch][i] = inbufs[ch % channelCount][i];
Chris@975 263 }
Chris@320 264 }
Chris@320 265 }
Chris@1429 266 }
Chris@320 267
Chris@1040 268 m_plugin->run(RealTime::frame2RealTime(blockFrame, sampleRate));
Chris@320 269
Chris@320 270 if (stvm) {
Chris@320 271
Chris@320 272 float value = m_plugin->getControlOutputValue(m_outputNo);
Chris@320 273
Chris@1039 274 sv_frame_t pointFrame = blockFrame;
Chris@320 275 if (pointFrame > latency) pointFrame -= latency;
Chris@320 276 else pointFrame = 0;
Chris@320 277
Chris@1651 278 stvm->add(Event(pointFrame, value, ""));
Chris@320 279
Chris@320 280 } else if (wwfm) {
Chris@320 281
Chris@320 282 float **outbufs = m_plugin->getAudioOutputBuffers();
Chris@320 283
Chris@320 284 if (outbufs) {
Chris@320 285
Chris@320 286 if (blockFrame >= latency) {
Chris@1039 287 sv_frame_t writeSize = std::min
Chris@320 288 (blockSize,
Chris@320 289 contextStart + contextDuration + latency - blockFrame);
Chris@320 290 wwfm->addSamples(outbufs, writeSize);
Chris@320 291 } else if (blockFrame + blockSize >= latency) {
Chris@1039 292 sv_frame_t offset = latency - blockFrame;
Chris@1039 293 sv_frame_t count = blockSize - offset;
Chris@320 294 float **tmp = new float *[channelCount];
Chris@930 295 for (int c = 0; c < channelCount; ++c) {
Chris@320 296 tmp[c] = outbufs[c] + offset;
Chris@320 297 }
Chris@320 298 wwfm->addSamples(tmp, count);
Chris@320 299 delete[] tmp;
Chris@320 300 }
Chris@320 301 }
Chris@320 302 }
Chris@320 303
Chris@1429 304 if (blockFrame == contextStart || completion > prevCompletion) {
Chris@1133 305 // This setCompletion is probably misusing the completion
Chris@1133 306 // terminology, just as it was for WritableWaveFileModel
Chris@1429 307 if (stvm) stvm->setCompletion(completion);
Chris@1429 308 if (wwfm) wwfm->setWriteProportion(completion);
Chris@1429 309 prevCompletion = completion;
Chris@1429 310 }
Chris@320 311
Chris@1429 312 blockFrame += blockSize;
Chris@320 313 }
Chris@320 314
Chris@320 315 if (m_abandoned) return;
Chris@320 316
Chris@320 317 if (stvm) stvm->setCompletion(100);
Chris@1133 318 if (wwfm) wwfm->writeComplete();
Chris@320 319 }
Chris@320 320