cannam@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ cannam@0: cannam@0: #include "Processor.h" cannam@0: Chris@16: #include cannam@0: cannam@4: //#define DEBUG_RUN_PROCESSOR 1 cannam@4: Chris@16: using Vamp::HostExt::PluginLoader; Chris@16: cannam@0: using std::cout; cannam@0: using std::cerr; cannam@0: using std::endl; cannam@0: cannam@1: void printFeatures(int, int, int, int, Vamp::Plugin::FeatureSet &); cannam@0: void transformInput(float *, size_t); cannam@0: void fft(unsigned int, bool, double *, double *, double *, double *); cannam@0: cannam@0: Processor::Processor(BufferingAudioCallbackRecordTarget *audioRecordTarget) : cannam@0: m_exiting(false), cannam@0: m_audioRecordTarget(audioRecordTarget), cannam@0: m_nextNumber(1), cannam@1: m_havePlugins(false), cannam@1: m_minBlockSize(0), cannam@1: m_maxBlockSize(0) cannam@0: { cannam@5: for (int i = 0; i < MAX_DISTINCT_STEP_SIZES; ++i) { cannam@5: m_unusedReaders.insert(i); cannam@5: } cannam@0: } cannam@0: cannam@0: Processor::~Processor() cannam@0: { cannam@0: m_exiting = true; cannam@0: wait(); Chris@15: Chris@15: for (RuleSet::const_iterator i = m_rules.begin(); i != m_rules.end(); ++i) { Chris@15: delete *i; cannam@7: } cannam@0: } cannam@0: cannam@0: void cannam@0: Processor::run() cannam@0: { cannam@0: while (!m_exiting) { cannam@0: cannam@1: if (!m_havePlugins) { cannam@0: msleep(100); cannam@0: continue; cannam@0: } cannam@0: cannam@1: size_t nframes = 0; cannam@0: cannam@1: for (StepSizeReaderMap::iterator i = m_stepSizeReaderMap.begin(); cannam@1: i != m_stepSizeReaderMap.end(); ++i) { cannam@1: size_t here = m_audioRecordTarget->samplesReady(i->second); cannam@1: if (here > nframes) { cannam@1: nframes = here; cannam@1: } cannam@1: } cannam@0: cannam@1: if (nframes > m_minBlockSize) { cannam@1: if (!runPlugins()) { cannam@1: msleep(10); cannam@1: } cannam@0: } else { cannam@0: msleep(50); cannam@0: } cannam@5: cannam@5: m_mutex.lock(); cannam@5: for (ReaderSet::iterator i = m_unusedReaders.begin(); cannam@5: i != m_unusedReaders.end(); ++i) { cannam@5: size_t ch = m_audioRecordTarget->getChannelCount(); cannam@5: for (size_t c = 0; c < ch; ++c) { cannam@5: m_audioRecordTarget->skipAllSamples(c, *i); cannam@5: } cannam@5: } cannam@5: m_mutex.unlock(); cannam@0: } cannam@0: } cannam@0: cannam@1: bool cannam@0: Processor::runPlugins() cannam@0: { cannam@0: QMutexLocker locker(&m_mutex); cannam@0: cannam@1: static std::map frame; // reader -> frame cannam@1: if (frame.empty()) { cannam@1: for (int i = 0; i < MAX_DISTINCT_STEP_SIZES; ++i) { cannam@1: frame[i] = 0; cannam@1: } cannam@1: } cannam@0: cannam@0: size_t sr = m_audioRecordTarget->getSourceSampleRate(); cannam@0: cannam@1: // cerr << "max block size " << m_maxBlockSize << endl; cannam@1: cannam@1: //!!! cannam@0: size_t ch = m_audioRecordTarget->getChannelCount(); cannam@0: float **buffers = new float *[ch]; cannam@0: for (size_t c = 0; c < ch; ++c) { cannam@1: buffers[c] = new float[m_maxBlockSize]; cannam@1: } cannam@1: cannam@1: bool doneWork = false; cannam@1: cannam@1: for (StepSizePluginMap::iterator i = m_processingMap.begin(); cannam@1: i != m_processingMap.end(); ++i) { cannam@1: cannam@1: size_t step = i->first; cannam@1: if (m_stepSizeReaderMap.find(step) == m_stepSizeReaderMap.end()) { cannam@1: cerr << "ERROR: Unrecorded step size " << step << endl; cannam@1: continue; cannam@0: } cannam@0: cannam@1: int reader = m_stepSizeReaderMap[step]; cannam@1: size_t maxBlock = 0; cannam@0: cannam@1: for (BlockSizePluginMap::iterator j = i->second.begin(); cannam@1: j != i->second.end(); ++j) { cannam@1: cannam@1: if (j == i->second.begin() || j->first > maxBlock) { cannam@1: maxBlock = j->first; cannam@0: } cannam@0: } cannam@0: cannam@1: if (maxBlock == 0) { cannam@1: cerr << "ERROR: maxBlock == 0 for step " << step << endl; cannam@1: continue; cannam@1: } cannam@0: cannam@1: // int counter = 0; cannam@1: cannam@1: while (m_audioRecordTarget->samplesReady(reader) >= maxBlock) { cannam@1: // && counter < 10) { cannam@1: cannam@1: // ++counter; cannam@1: cannam@4: #ifdef DEBUG_RUN_PROCESSOR cannam@4: cerr << "enough samples for max block " << maxBlock cannam@4: << " for step " << step << endl; cannam@4: #endif cannam@1: cannam@1: for (size_t c = 0; c < ch; ++c) { cannam@5: size_t got = m_audioRecordTarget->peekSamples(c, maxBlock, buffers[c], reader); cannam@5: if (got < maxBlock) { cannam@5: cerr << "ERROR: Requested " << maxBlock << " samples on channel " << c << " reader " << reader << ", only got " << got << endl; cannam@5: } cannam@5: size_t skipped = m_audioRecordTarget->skipSamples(c, step, reader); cannam@5: if (skipped < step) { cannam@5: cerr << "ERROR: Requested skip of " << step << " samples on channel " << c << " reader " << reader << ", only skipped " << skipped << endl; cannam@5: } cannam@1: } cannam@1: cannam@1: for (BlockSizePluginMap::iterator j = i->second.begin(); cannam@1: j != i->second.end(); ++j) { cannam@1: cannam@1: size_t block = j->first; cannam@1: size_t toUse = maxBlock; cannam@1: size_t off = 0; cannam@1: cannam@1: while (toUse >= block) { cannam@1: cannam@14: bool silentInput = true; cannam@14: for (size_t s = 0; s < block; ++s) { cannam@14: for (size_t c = 0; c < ch; ++c) { cannam@14: if (fabsf(buffers[c][s + off]) > 0.00001) { cannam@14: silentInput = false; cannam@14: break; cannam@14: } cannam@14: } cannam@14: if (!silentInput) break; cannam@14: } cannam@14: cannam@1: for (PluginSet::iterator k = j->second.begin(); cannam@1: k != j->second.end(); ++k) { cannam@1: cannam@4: #ifdef DEBUG_RUN_PROCESSOR cannam@4: cerr << "block inner loop for block " << block cannam@4: << " (toUse = " << toUse << ")" << endl; cannam@4: #endif cannam@1: cannam@1: Vamp::Plugin *plugin = *k; cannam@1: Vamp::Plugin::FeatureSet fs; cannam@1: Vamp::RealTime timestamp = cannam@1: Vamp::RealTime::frame2RealTime(frame[reader], sr); cannam@1: cannam@4: #ifdef DEBUG_RUN_PROCESSOR Chris@16: cerr << "running " << plugin->getName() << " frame = " << frame[reader] << endl; cannam@4: #endif Chris@16: float *tmp[10]; Chris@16: for (size_t c = 0; c < ch; ++c) { Chris@16: tmp[c] = buffers[c] + off; cannam@5: } Chris@16: fs = plugin->process(tmp, timestamp); cannam@5: cannam@5: int pluginIndex = m_pluginRMap[plugin]; cannam@5: cannam@5: if (fs.empty()) { cannam@5: for (OutputStateMap::iterator oi = cannam@5: m_pluginStates[pluginIndex].begin(); cannam@5: oi != m_pluginStates[pluginIndex].end(); ++oi) { cannam@5: int output = oi->first; cannam@5: OutputState currentState = oi->second; cannam@5: cannam@5: Vamp::RealTime rt = cannam@5: Vamp::RealTime::frame2RealTime(frame[reader], sr); cannam@5: cannam@13: // bool changed = currentState.present; cannam@13: bool changed = false; cannam@14: if (currentState.silentInput && !silentInput) { cannam@14: changed = true; cannam@14: } cannam@5: Vamp::RealTime gap = rt - currentState.laststamp; cannam@5: m_pluginStates[pluginIndex][output] = cannam@14: OutputState(false, silentInput, changed, currentState.value, currentState.laststamp, currentState.gap); cannam@1: } cannam@1: } cannam@5: cannam@1: for (Vamp::Plugin::FeatureSet::iterator fi = fs.begin(); cannam@1: fi != fs.end(); ++fi) { cannam@4: cannam@4: int output = fi->first; cannam@4: cannam@4: //!!! For each output, we need to step cannam@4: //through the returned features one by one cannam@4: //and update the plugin state for each, cannam@4: //then run the rules each time a timestamp cannam@4: //changes. But it has to do this with the cannam@4: //features in the order of their cannam@4: //timestamps, not in order of cannam@4: //plugin/output enumeration. cannam@4: // cannam@4: // But we don't do that yet, we just use cannam@4: // the first feature in the block (if any) cannam@4: cannam@4: Vamp::RealTime rt = cannam@4: Vamp::RealTime::frame2RealTime(frame[reader], sr); cannam@4: cannam@4: Vamp::Plugin::FeatureList fl(fi->second); cannam@4: cannam@4: OutputState currentState = cannam@4: m_pluginStates[pluginIndex][output]; cannam@5: cannam@4: if (fl.empty()) { cannam@13: // bool changed = currentState.present; cannam@13: bool changed = false; cannam@14: if (currentState.silentInput && !silentInput) { cannam@14: changed = true; cannam@14: } cannam@4: Vamp::RealTime gap = rt - currentState.laststamp; cannam@4: m_pluginStates[pluginIndex][output] = cannam@14: OutputState(false, silentInput, changed, currentState.value, currentState.laststamp, currentState.gap); cannam@4: } else { cannam@4: cannam@4: if (fl[0].hasTimestamp) { cannam@4: rt = fl[0].timestamp; cannam@4: } cannam@4: float value = 0.f; cannam@4: if (!fl[0].values.empty()) { cannam@4: value = fl[0].values[0]; cannam@4: } cannam@5: bool changed = cannam@13: // (!currentState.present || cannam@5: (fabsf(currentState.value - value) > cannam@13: 0.000001) cannam@13: // ) cannam@13: ; cannam@14: if (currentState.silentInput && !silentInput) { cannam@14: changed = true; cannam@14: } cannam@13: if (changed) { cannam@13: std::cerr << "changed: " < " << value << std::endl; cannam@13: } cannam@4: Vamp::RealTime gap = rt - currentState.laststamp; cannam@9: // std::cerr << "gap = " << gap << std::endl; cannam@4: m_pluginStates[pluginIndex][output] = cannam@14: OutputState(true, silentInput, changed, value, rt, gap); cannam@4: } cannam@4: Chris@15: printFeatures(pluginIndex, frame[reader], sr, output, fs); cannam@1: } cannam@1: cannam@4: processRules(); cannam@4: cannam@1: doneWork = true; cannam@1: } cannam@1: cannam@1: toUse -= block; cannam@1: off += block; cannam@1: } cannam@1: } cannam@1: cannam@1: frame[reader] += step; cannam@0: } cannam@1: cannam@0: } cannam@0: cannam@0: for (size_t c = 0; c < ch; ++c) { cannam@0: delete[] buffers[c]; cannam@0: } cannam@0: delete[] buffers; cannam@1: cannam@1: return doneWork; cannam@0: } cannam@0: cannam@0: int cannam@0: Processor::addPlugin(QString pluginId) cannam@0: { cannam@0: QMutexLocker locker(&m_mutex); cannam@0: cannam@0: size_t sr = m_audioRecordTarget->getSourceSampleRate(); cannam@0: cannam@0: if (!sr) { cannam@0: cerr << "ERROR: Processor::addPlugin: Source sample rate is not defined" << endl; cannam@0: return 0; cannam@0: } cannam@0: Chris@16: if (pluginId.startsWith("vamp:")) { Chris@16: pluginId = pluginId.right(pluginId.size() - 5); cannam@0: } cannam@0: Chris@16: PluginLoader *loader = PluginLoader::getInstance(); Chris@16: Vamp::Plugin *plugin = loader->loadPlugin Chris@16: (pluginId.toStdString(), sr, PluginLoader::ADAPT_ALL_SAFE); cannam@0: cannam@0: if (!plugin) { cannam@0: cerr << "ERROR: Processor::addPlugin: Failed to instantiate plugin \"" << pluginId.toStdString() << "\"" << endl; cannam@0: return 0; cannam@0: } cannam@0: cannam@1: size_t block = plugin->getPreferredBlockSize(); cannam@1: if (block == 0) block = m_audioRecordTarget->getSourceBlockSize(); cannam@0: cannam@1: size_t step = plugin->getPreferredStepSize(); Chris@16: if (step == 0) step = block; cannam@0: cannam@1: if (m_stepSizeReaderMap.find(step) == m_stepSizeReaderMap.end()) { cannam@5: if (m_unusedReaders.empty()) { cannam@1: cerr << "ERROR: Processor::addPlugin: Run out of distinct step size slots: increase MAX_DISTINCT_STEP_SIZES and recompile" << endl; cannam@1: delete plugin; cannam@1: return 0; cannam@1: } cannam@5: int reader = *m_unusedReaders.begin(); cannam@5: m_unusedReaders.erase(reader); cannam@5: m_stepSizeReaderMap[step] = reader; cannam@1: } cannam@0: cannam@1: size_t ch = m_audioRecordTarget->getChannelCount(); cannam@0: cannam@1: if (!plugin->initialise(ch, step, block)) { cannam@1: cerr << "ERROR: Processor::addPlugin: Initialisation failed with step size " << step << ", block size " << block << ", channels " << ch << endl; cannam@0: delete plugin; cannam@0: return 0; cannam@0: } cannam@0: cannam@1: if (!m_havePlugins || block < m_minBlockSize) m_minBlockSize = block; cannam@1: if (!m_havePlugins || block > m_maxBlockSize) m_maxBlockSize = block; cannam@0: cannam@5: std::cerr << "initialised plugin \"" << pluginId.toStdString() cannam@5: << "\" with step \"" << step << "\" block \"" << block << "\"" cannam@5: << std::endl; cannam@5: cannam@0: int number = m_nextNumber++; cannam@1: m_plugins[number] = plugin; cannam@4: m_pluginRMap[plugin] = number; cannam@1: m_processingMap[step][block].insert(plugin); cannam@0: cannam@1: m_havePlugins = true; cannam@0: return number; cannam@0: } cannam@0: cannam@0: void cannam@0: Processor::removePlugin(int number) cannam@0: { cannam@0: QMutexLocker locker(&m_mutex); cannam@0: cannam@0: if (m_plugins.find(number) == m_plugins.end()) { cannam@0: cerr << "ERROR: Processor::removePlugin: No such plugin number " cannam@0: << number << endl; cannam@0: return; cannam@0: } cannam@0: cannam@0: Vamp::Plugin *plugin = m_plugins[number]; cannam@1: bool done = false; cannam@1: cannam@1: for (StepSizePluginMap::iterator i = m_processingMap.begin(); cannam@1: i != m_processingMap.end(); ++i) { cannam@1: cannam@1: BlockSizePluginMap::iterator j; cannam@1: cannam@1: for (j = i->second.begin(); j != i->second.end(); ++j) { cannam@1: cannam@1: if (j->second.find(plugin) != j->second.end()) { cannam@1: cannam@1: j->second.erase(plugin); cannam@1: cannam@1: if (j->second.empty()) { // no plugins with this step & block cannam@1: cannam@1: i->second.erase(j->first); cannam@1: cannam@1: if (i->second.empty()) { // no plugins with this step cannam@1: cannam@1: m_processingMap.erase(i->first); cannam@1: m_stepSizeReaderMap.erase(i->first); cannam@5: m_unusedReaders.insert(i->first); cannam@1: } cannam@1: } cannam@1: cannam@1: done = true; cannam@1: break; cannam@1: } cannam@1: } cannam@1: cannam@1: if (done) break; cannam@1: } cannam@1: cannam@0: m_plugins.erase(number); cannam@4: m_pluginRMap.erase(plugin); cannam@1: if (m_plugins.empty()) m_havePlugins = false; cannam@0: delete plugin; cannam@0: } cannam@0: cannam@4: void cannam@7: Processor::addRule(Rule *rule) cannam@4: { Chris@15: cerr << "adding rule " << rule << endl; cannam@7: m_rules.insert(rule); cannam@4: } cannam@4: cannam@4: bool cannam@4: Processor::processRules() cannam@4: { cannam@7: for (RuleSet::iterator i = m_rules.begin(); i != m_rules.end(); ++i) { cannam@4: cannam@7: Rule *rule(*i); cannam@4: cannam@7: bool passed = false; cannam@13: bool oneChanged = false; cannam@4: cannam@7: for (Rule::ConditionList::const_iterator j = rule->getConditions().begin(); cannam@7: j != rule->getConditions().end(); ++j) { cannam@7: cannam@7: Condition condition(*j); cannam@7: cannam@7: int pluginIndex = condition.getPluginIndex(); cannam@7: int outputNumber = condition.getOutputNumber(); cannam@7: Condition::Type type = condition.getType(); cannam@7: float argument = condition.getArgument(); cannam@7: cannam@7: OutputState state = m_pluginStates[pluginIndex][outputNumber]; cannam@4: cannam@5: #ifdef DEBUG_RUN_PROCESSOR cannam@7: std::cerr << "Present = " << state.present << ", changed = " << state.changed << ", value = " << state.value << std::endl; cannam@5: #endif cannam@5: cannam@7: passed = false; cannam@5: cannam@14: if (state.silentInput) { cannam@14: break; cannam@14: } cannam@14: cannam@13: if (state.changed) { cannam@13: // std::cerr << "State changed with present = " << state.present << ", value = " << state.value << std::endl; cannam@13: oneChanged = true; cannam@13: } cannam@5: cannam@7: switch (type) { cannam@5: cannam@7: case Condition::GreaterThan: cannam@7: passed = (state.value > argument); cannam@7: break; cannam@7: cannam@7: case Condition::LessThan: cannam@7: passed = (state.value < argument); cannam@7: break; cannam@7: cannam@7: case Condition::EqualTo: cannam@7: passed = fabsf(state.value - argument) < 0.000001; cannam@7: break; cannam@7: cannam@7: case Condition::NotEqualTo: cannam@7: passed = fabsf(state.value - argument) > 0.000001; cannam@7: break; cannam@7: cannam@7: case Condition::Present: cannam@7: passed = state.present; cannam@13: if (passed) oneChanged = true; cannam@7: break; cannam@7: cannam@7: case Condition::Changed: cannam@13: passed = state.changed; cannam@7: break; cannam@7: cannam@7: case Condition::GapGreaterThan: cannam@7: passed = (state.gap > Vamp::RealTime::fromSeconds(argument)); cannam@7: break; cannam@7: cannam@7: case Condition::GapLessThan: cannam@9: std::cout << "GapLessThan: gap is " << state.gap << ", argument is " << argument << std::endl; cannam@7: passed = (state.gap < Vamp::RealTime::fromSeconds(argument)); cannam@7: break; cannam@7: } cannam@5: cannam@7: if (!passed) break; cannam@5: } cannam@5: cannam@13: if (passed && oneChanged) { cannam@9: for (Rule::ActionList::const_iterator ai = rule->getActions().begin(); cannam@9: ai != rule->getActions().end(); ++ai) { cannam@9: Action *action = *ai; cannam@9: if (action) { cannam@9: // std::cerr << "FIRING RULE: " << action->getName().toStdString() << "!" << std::endl; cannam@9: action->fire(); cannam@9: } cannam@7: } cannam@4: } cannam@4: } cannam@4: cannam@4: return true; cannam@4: } cannam@0: cannam@0: void cannam@1: printFeatures(int plugno, int frame, int sr, int output, Vamp::Plugin::FeatureSet &features) cannam@0: { cannam@9: return;//!!! cannam@5: cannam@5: if (output > 0) return;//!!! cannam@5: cannam@0: for (unsigned int i = 0; i < features[output].size(); ++i) { cannam@0: Vamp::RealTime rt = Vamp::RealTime::frame2RealTime(frame, sr); cannam@0: if (features[output][i].hasTimestamp) { cannam@0: rt = features[output][i].timestamp; cannam@0: } cannam@1: cout << plugno << ":" << output << " -" << rt.toString() << ":"; cannam@0: for (unsigned int j = 0; j < features[output][i].values.size(); ++j) { cannam@0: cout << " " << features[output][i].values[j]; cannam@0: } cannam@0: cout << endl; cannam@0: } cannam@0: } cannam@0: