static void set_current_preset(struct SoundPlugin *plugin, int num){ #if JUCE_LINUX const MessageManagerLock mmLock; #endif Data *data = (Data*)plugin->data; AudioPluginInstance *instance = data->audio_instance; instance->setCurrentProgram(num); }
bool handlePluginRequest(const PluginRequestParameters ¶ms, OutputStream &ostream, ThreadSafePlugin *plugin = nullptr) { if (!plugin) { // It's very possible that all of this was a premature optimization. // For VSTs at least, code loading and caching is handled by ModuleHandle::findOrCreateModule, // and each instantiation only requires a couple of disc hits for working directory setting. // On the other hand, we want to make sure that each audio request has a "fresh" instance. // The easiest way to do this is by bypassing the instance pool and instantiating on demand. #if PLUGIN_POOL_SIZE // Recurse with a plugin from the pool, locking on it. // Keep trying with a delay until a timeout occurs. const int TIMEOUT = 5000, WAIT = 200; int64 startTime = Time::currentTimeMillis(); while (Time::currentTimeMillis() < startTime + TIMEOUT) { int i = 0; while ((plugin = pluginPool[i++])) { const ScopedTryLock pluginTryLock(plugin->crit); if (pluginTryLock.isLocked()) { DBG << "Handling with plugin " << i << endl; return handlePluginRequest(params, ostream, plugin); } } DBG << "Trying again in " << WAIT << endl; Thread::sleep(WAIT); } // If we were unable to obtain a lock, return failure. DBG << "Timeout" << endl; return false; #else ThreadSafePlugin temporaryPlugin(createSynthInstance()); return handlePluginRequest(params, ostream, &temporaryPlugin); #endif } else { // Re-acquire or acquire the lock. const ScopedLock pluginLock(plugin->crit); AudioPluginInstance *instance = plugin->instance; // unmanaged, for simplicity // Attempt to reset the plugin in all ways possible. instance->reset(); // Setting default parameters here causes miniTERA to become unresponsive to parameter settings. // It's possible that it's effectively pressing some interface buttons that change the editor mode entirely. // It's not necessary anyways if the plugin instance has been freshly created (see above). // pluginParametersOldNewFallback(instance, nullptr, &pluginDefaults); // note that the defaults may be empty instance->setCurrentProgram(0); // Load preset if specified, before listing or modifying parameters! if (params.presetNumber >= 0 && params.presetNumber < instance->getNumPrograms()) { DBG << "Setting program/preset: " << params.presetNumber << endl; instance->setCurrentProgram(params.presetNumber); } int currentProgram = instance->getCurrentProgram(); DBG << "Current program/preset: " << currentProgram << " - " << instance->getProgramName(currentProgram) << endl; // Set parameters, starting with named, then indexed pluginParametersSet(instance, params.parameters); pluginParametersSetIndexed(instance, params.indexedParameters); // If parameters requested, output them and return if (params.listParameters) { DBG << "Rendering parameter list: # parameters " << instance->getNumPrograms() << endl; // Output each parameter setting in two places: // an indexed array and a dictionary by name // All DynamicObjects created will be freed when their var's leave scope. DynamicObject *outer = new DynamicObject(); DynamicObject *innerParams = new DynamicObject(); var indexedParamVar; { for (int i = 0, n = instance->getNumParameters(); i < n; ++i) { String name = instance->getParameterName(i); float val = instance->getParameter(i); innerParams->setProperty(name, val); DynamicObject *indexedInnerObj = new DynamicObject(); indexedInnerObj->setProperty("index", i); indexedInnerObj->setProperty("name", name); indexedInnerObj->setProperty("value", val); indexedParamVar.append(var(indexedInnerObj)); // frees indexedInnerObj when this scope ends } } outer->setProperty(Identifier("parameters"), var(innerParams)); outer->setProperty(Identifier("indexedParameters"), indexedParamVar); // List presets/programs. var progVar; { for (int i = 0, n = instance->getNumPrograms(); i < n; ++i) { progVar.append(var(instance->getProgramName(i))); } } outer->setProperty(Identifier("presets"), progVar); var outerVar(outer); JSON::writeToStream(ostream, outerVar); // DBG << JSON::toString(outerVar, true /* allOnOneLine */) << endl; return true; } // Now attempt to render audio. AudioFormatManager formatManager; formatManager.registerBasicFormats(); OptionalScopedPointer<AudioFormat> outputFormat(formatManager.findFormatForFileExtension(params.getFormatName()), false); if (!outputFormat) return false; instance->setNonRealtime(true); instance->prepareToPlay(params.sampleRate, params.blockSize); instance->setNonRealtime(true); // The writer takes ownership of the output stream; the writer will delete it when the writer leaves scope. // Therefore, we pass a special pointer class that does not allow the writer to delete it. OutputStream *ostreamNonDeleting = new NonDeletingOutputStream(&ostream); ScopedPointer<AudioFormatWriter> writer(outputFormat->createWriterFor(ostreamNonDeleting, params.sampleRate, params.nChannels, params.bitDepth, StringPairArray(), 0)); // Create a MIDI buffer MidiBuffer midiBuffer; midiBuffer.addEvent(MidiMessage::noteOn(params.midiChannel, (uint8)params.midiPitch, (uint8)params.midiVelocity), 0 /* time */); midiBuffer.addEvent(MidiMessage::allNotesOff(params.midiChannel), params.noteSeconds * params.sampleRate); AudioSampleBuffer buffer(params.nChannels, params.blockSize); int numBuffers = (int)(params.renderSeconds * params.sampleRate / params.blockSize); for (int i = 0; i < numBuffers; ++i) { // DBG << "Processing block " << i << "..." << flush; instance->processBlock(buffer, midiBuffer); // DBG << " left RMS level " << buffer.getRMSLevel(0, 0, params.blockSize) << endl; writer->writeFromAudioSampleBuffer(buffer, 0 /* offset into buffer */, params.blockSize); } instance->reset(); return true; } }