| Chris@0 | 1 | 
| Chris@0 | 2 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@0 | 3 | 
| Chris@0 | 4 /* | 
| Chris@0 | 5     Sonic Visualiser | 
| Chris@0 | 6     An audio file viewer and annotation editor. | 
| Chris@0 | 7     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@0 | 8     This file copyright 2006 Chris Cannam. | 
| Chris@0 | 9 | 
| Chris@0 | 10     This program is free software; you can redistribute it and/or | 
| Chris@0 | 11     modify it under the terms of the GNU General Public License as | 
| Chris@0 | 12     published by the Free Software Foundation; either version 2 of the | 
| Chris@0 | 13     License, or (at your option) any later version.  See the file | 
| Chris@0 | 14     COPYING included with this distribution for more information. | 
| Chris@0 | 15 */ | 
| Chris@0 | 16 | 
| Chris@0 | 17 #include "RealTimePluginTransform.h" | 
| Chris@0 | 18 | 
| Chris@0 | 19 #include "plugin/RealTimePluginFactory.h" | 
| Chris@0 | 20 #include "plugin/RealTimePluginInstance.h" | 
| Chris@0 | 21 #include "plugin/PluginXml.h" | 
| Chris@0 | 22 | 
| Chris@1 | 23 #include "data/model/Model.h" | 
| Chris@1 | 24 #include "data/model/SparseTimeValueModel.h" | 
| Chris@1 | 25 #include "data/model/DenseTimeValueModel.h" | 
| Chris@39 | 26 #include "data/model/WritableWaveFileModel.h" | 
| Chris@0 | 27 | 
| Chris@0 | 28 #include <iostream> | 
| Chris@0 | 29 | 
| Chris@0 | 30 RealTimePluginTransform::RealTimePluginTransform(Model *inputModel, | 
| Chris@0 | 31                                                  QString pluginId, | 
| Chris@27 | 32                                                  const ExecutionContext &context, | 
| Chris@0 | 33                                                  QString configurationXml, | 
| Chris@0 | 34                                                  QString units, | 
| Chris@27 | 35                                                  int output) : | 
| Chris@27 | 36     PluginTransform(inputModel, context), | 
| Chris@0 | 37     m_plugin(0), | 
| Chris@27 | 38     m_outputNo(output) | 
| Chris@0 | 39 { | 
| Chris@27 | 40     if (!m_context.blockSize) m_context.blockSize = 1024; | 
| Chris@26 | 41 | 
| Chris@0 | 42     std::cerr << "RealTimePluginTransform::RealTimePluginTransform: plugin " << pluginId.toStdString() << ", output " << output << std::endl; | 
| Chris@0 | 43 | 
| Chris@0 | 44     RealTimePluginFactory *factory = | 
| Chris@0 | 45 	RealTimePluginFactory::instanceFor(pluginId); | 
| Chris@0 | 46 | 
| Chris@0 | 47     if (!factory) { | 
| Chris@0 | 48 	std::cerr << "RealTimePluginTransform: No factory available for plugin id \"" | 
| Chris@0 | 49 		  << pluginId.toStdString() << "\"" << std::endl; | 
| Chris@0 | 50 	return; | 
| Chris@0 | 51     } | 
| Chris@0 | 52 | 
| Chris@0 | 53     DenseTimeValueModel *input = getInput(); | 
| Chris@0 | 54     if (!input) return; | 
| Chris@0 | 55 | 
| Chris@44 | 56     m_plugin = factory->instantiatePlugin(pluginId, 0, 0, | 
| Chris@44 | 57                                           m_input->getSampleRate(), | 
| Chris@27 | 58                                           m_context.blockSize, | 
| Chris@0 | 59                                           input->getChannelCount()); | 
| Chris@0 | 60 | 
| Chris@0 | 61     if (!m_plugin) { | 
| Chris@0 | 62 	std::cerr << "RealTimePluginTransform: Failed to instantiate plugin \"" | 
| Chris@0 | 63 		  << pluginId.toStdString() << "\"" << std::endl; | 
| Chris@0 | 64 	return; | 
| Chris@0 | 65     } | 
| Chris@0 | 66 | 
| Chris@0 | 67     if (configurationXml != "") { | 
| Chris@0 | 68         PluginXml(m_plugin).setParametersFromXml(configurationXml); | 
| Chris@0 | 69     } | 
| Chris@0 | 70 | 
| Chris@34 | 71     if (m_outputNo >= 0 && m_outputNo >= m_plugin->getControlOutputCount()) { | 
| Chris@0 | 72         std::cerr << "RealTimePluginTransform: Plugin has fewer than desired " << m_outputNo << " control outputs" << std::endl; | 
| Chris@0 | 73         return; | 
| Chris@0 | 74     } | 
| Chris@34 | 75 | 
| Chris@34 | 76     if (m_outputNo == -1) { | 
| Chris@34 | 77 | 
| Chris@52 | 78         size_t outputChannels = m_plugin->getAudioOutputCount(); | 
| Chris@52 | 79         if (outputChannels > input->getChannelCount()) { | 
| Chris@52 | 80             outputChannels = input->getChannelCount(); | 
| Chris@52 | 81         } | 
| Chris@52 | 82 | 
| Chris@39 | 83         WritableWaveFileModel *model = new WritableWaveFileModel | 
| Chris@52 | 84             (input->getSampleRate(), outputChannels); | 
| Chris@39 | 85 | 
| Chris@39 | 86         m_output = model; | 
| Chris@34 | 87 | 
| Chris@34 | 88     } else { | 
| Chris@0 | 89 | 
| Chris@34 | 90         SparseTimeValueModel *model = new SparseTimeValueModel | 
| Chris@34 | 91             (input->getSampleRate(), m_context.blockSize, 0.0, 0.0, false); | 
| Chris@0 | 92 | 
| Chris@34 | 93         if (units != "") model->setScaleUnits(units); | 
| Chris@0 | 94 | 
| Chris@34 | 95         m_output = model; | 
| Chris@34 | 96     } | 
| Chris@0 | 97 } | 
| Chris@0 | 98 | 
| Chris@0 | 99 RealTimePluginTransform::~RealTimePluginTransform() | 
| Chris@0 | 100 { | 
| Chris@0 | 101     delete m_plugin; | 
| Chris@0 | 102 } | 
| Chris@0 | 103 | 
| Chris@0 | 104 DenseTimeValueModel * | 
| Chris@0 | 105 RealTimePluginTransform::getInput() | 
| Chris@0 | 106 { | 
| Chris@0 | 107     DenseTimeValueModel *dtvm = | 
| Chris@0 | 108 	dynamic_cast<DenseTimeValueModel *>(getInputModel()); | 
| Chris@0 | 109     if (!dtvm) { | 
| Chris@0 | 110 	std::cerr << "RealTimePluginTransform::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; | 
| Chris@0 | 111     } | 
| Chris@0 | 112     return dtvm; | 
| Chris@0 | 113 } | 
| Chris@0 | 114 | 
| Chris@0 | 115 void | 
| Chris@0 | 116 RealTimePluginTransform::run() | 
| Chris@0 | 117 { | 
| Chris@0 | 118     DenseTimeValueModel *input = getInput(); | 
| Chris@0 | 119     if (!input) return; | 
| Chris@0 | 120 | 
| Chris@39 | 121     SparseTimeValueModel *stvm = dynamic_cast<SparseTimeValueModel *>(m_output); | 
| Chris@39 | 122     WritableWaveFileModel *wwfm = dynamic_cast<WritableWaveFileModel *>(m_output); | 
| Chris@39 | 123     if (!stvm && !wwfm) return; | 
| Chris@0 | 124 | 
| Chris@39 | 125     if (stvm && (m_outputNo >= m_plugin->getControlOutputCount())) return; | 
| Chris@0 | 126 | 
| Chris@0 | 127     size_t sampleRate = input->getSampleRate(); | 
| Chris@0 | 128     int channelCount = input->getChannelCount(); | 
| Chris@44 | 129     if (!wwfm && m_context.channel != -1) channelCount = 1; | 
| Chris@0 | 130 | 
| Chris@0 | 131     size_t blockSize = m_plugin->getBufferSize(); | 
| Chris@0 | 132 | 
| Chris@0 | 133     float **buffers = m_plugin->getAudioInputBuffers(); | 
| Chris@0 | 134 | 
| Chris@0 | 135     size_t startFrame = m_input->getStartFrame(); | 
| Chris@0 | 136     size_t   endFrame = m_input->getEndFrame(); | 
| Chris@0 | 137     size_t blockFrame = startFrame; | 
| Chris@0 | 138 | 
| Chris@0 | 139     size_t prevCompletion = 0; | 
| Chris@0 | 140 | 
| Chris@39 | 141     size_t latency = m_plugin->getLatency(); | 
| Chris@39 | 142 | 
| Chris@0 | 143     int i = 0; | 
| Chris@0 | 144 | 
| Chris@0 | 145     while (blockFrame < endFrame) { | 
| Chris@0 | 146 | 
| Chris@0 | 147 	size_t completion = | 
| Chris@0 | 148 	    (((blockFrame - startFrame) / blockSize) * 99) / | 
| Chris@0 | 149 	    (   (endFrame - startFrame) / blockSize); | 
| Chris@0 | 150 | 
| Chris@0 | 151 	size_t got = 0; | 
| Chris@0 | 152 | 
| Chris@0 | 153 	if (channelCount == 1) { | 
| Chris@40 | 154             if (buffers && buffers[0]) { | 
| Chris@40 | 155                 got = input->getValues | 
| Chris@40 | 156                     (m_context.channel, blockFrame, blockFrame + blockSize, buffers[0]); | 
| Chris@40 | 157                 while (got < blockSize) { | 
| Chris@40 | 158                     buffers[0][got++] = 0.0; | 
| Chris@0 | 159                 } | 
| Chris@40 | 160                 if (m_context.channel == -1 && channelCount > 1) { | 
| Chris@40 | 161                     // use mean instead of sum, as plugin input | 
| Chris@40 | 162                     for (size_t i = 0; i < got; ++i) { | 
| Chris@40 | 163                         buffers[0][i] /= channelCount; | 
| Chris@40 | 164                     } | 
| Chris@40 | 165                 } | 
| Chris@40 | 166             } | 
| Chris@0 | 167 	} else { | 
| Chris@0 | 168 	    for (size_t ch = 0; ch < channelCount; ++ch) { | 
| Chris@40 | 169                 if (buffers && buffers[ch]) { | 
| Chris@40 | 170                     got = input->getValues | 
| Chris@40 | 171                         (ch, blockFrame, blockFrame + blockSize, buffers[ch]); | 
| Chris@40 | 172                     while (got < blockSize) { | 
| Chris@40 | 173                         buffers[ch][got++] = 0.0; | 
| Chris@40 | 174                     } | 
| Chris@40 | 175                 } | 
| Chris@0 | 176 	    } | 
| Chris@0 | 177 	} | 
| Chris@0 | 178 | 
| Chris@0 | 179         m_plugin->run(Vamp::RealTime::frame2RealTime(blockFrame, sampleRate)); | 
| Chris@0 | 180 | 
| Chris@39 | 181         if (stvm) { | 
| Chris@0 | 182 | 
| Chris@39 | 183             float value = m_plugin->getControlOutputValue(m_outputNo); | 
| Chris@39 | 184 | 
| Chris@39 | 185             size_t pointFrame = blockFrame; | 
| Chris@39 | 186             if (pointFrame > latency) pointFrame -= latency; | 
| Chris@39 | 187             else pointFrame = 0; | 
| Chris@39 | 188 | 
| Chris@39 | 189             stvm->addPoint(SparseTimeValueModel::Point | 
| Chris@39 | 190                            (pointFrame, value, "")); | 
| Chris@39 | 191 | 
| Chris@39 | 192         } else if (wwfm) { | 
| Chris@39 | 193 | 
| Chris@39 | 194             float **buffers = m_plugin->getAudioOutputBuffers(); | 
| Chris@39 | 195 | 
| Chris@40 | 196             if (buffers) { | 
| Chris@40 | 197 | 
| Chris@40 | 198                 //!!! This will fail if any buffers[c] is null or | 
| Chris@40 | 199                 //uninitialised.  The plugin instance should ensure | 
| Chris@40 | 200                 //that that can't happen -- but it doesn't | 
| Chris@40 | 201 | 
| Chris@40 | 202                 if (blockFrame >= latency) { | 
| Chris@40 | 203                     wwfm->addSamples(buffers, blockSize); | 
| Chris@40 | 204                 } else if (blockFrame + blockSize >= latency) { | 
| Chris@40 | 205                     size_t offset = latency - blockFrame; | 
| Chris@40 | 206                     size_t count = blockSize - offset; | 
| Chris@40 | 207                     float **tmp = new float *[channelCount]; | 
| Chris@40 | 208                     for (size_t c = 0; c < channelCount; ++c) { | 
| Chris@40 | 209                         tmp[c] = buffers[c] + offset; | 
| Chris@40 | 210                     } | 
| Chris@40 | 211                     wwfm->addSamples(tmp, count); | 
| Chris@40 | 212                     delete[] tmp; | 
| Chris@39 | 213                 } | 
| Chris@39 | 214             } | 
| Chris@39 | 215         } | 
| Chris@0 | 216 | 
| Chris@0 | 217 	if (blockFrame == startFrame || completion > prevCompletion) { | 
| Chris@39 | 218 	    if (stvm) stvm->setCompletion(completion); | 
| Chris@0 | 219 	    prevCompletion = completion; | 
| Chris@0 | 220 	} | 
| Chris@0 | 221 | 
| Chris@0 | 222 	blockFrame += blockSize; | 
| Chris@0 | 223     } | 
| Chris@0 | 224 | 
| Chris@39 | 225     if (stvm) stvm->setCompletion(100); | 
| Chris@39 | 226     if (wwfm) wwfm->sync(); | 
| Chris@0 | 227 } | 
| Chris@0 | 228 |