예제 #1
0
bool handlePluginRequest(const PluginRequestParameters &params, 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;
    }
}