File: ThreadedPlugin.h

package info (click to toggle)
pd-vstplugin 0.6.1-1~exp1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 2,008 kB
  • sloc: cpp: 22,783; lisp: 2,860; makefile: 37; sh: 26
file content (175 lines) | stat: -rw-r--r-- 5,619 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#pragma once

#include "Interface.h"
#include "Sync.h"
#include "DeferredPlugin.h"
#include "Lockfree.h"
#include "Bus.h"

#include <thread>

namespace vst {

class ThreadedPlugin;

class DSPThreadPool {
 public:
    static DSPThreadPool& instance(){
        static DSPThreadPool inst;
        return inst;
    }

    DSPThreadPool();
    ~DSPThreadPool();

    using Callback = void (*)(ThreadedPlugin *, int);
    bool push(Callback cb, ThreadedPlugin *plugin, int numSamples);

    bool processTask();
 private:
    struct Task {
        Callback cb;
        ThreadedPlugin *plugin;
        int numSamples;
    };
    std::vector<std::thread> threads_;
    // NOTE: Semaphore is the right tool to notify one or more threads in a thread pool.
    // With Event there are certain edge cases where it would fail to notify the correct
    // number of threads. For example, if several worker threads are about to call wait()
    // more or less simultaneously and you call set() several times *before* one of them
    // actually manages to do so, only a single worker thread will continue and the others
    // will wait.
    // The only disadvantage of Semaphore is that all those post() calls will make the worker
    // threads spin a few times, but I think this negligible. Also, the post() call is a bit faster.
    LightSemaphore semaphore_;
    std::atomic<bool> running_;
    LockfreeFifo<Task, 1024> queue_;
    PaddedSpinLock pushLock_;
    PaddedSpinLock popLock_;

    void run(int index);
};

/*//////////////////// ThreadedPlugin ////////////////*/


class ThreadedPlugin final : public DeferredPlugin, public IPluginListener
{
 public:
    ThreadedPlugin(IPlugin::ptr plugin);
    ~ThreadedPlugin();

    const PluginDesc& info() const override {
        return plugin_->info();
    }

    bool isThreaded() const override { return true; }
    bool isBridged() const override { return plugin_->isBridged(); }

    void setupProcessing(double sampleRate, int maxBlockSize,
                         ProcessPrecision precision, ProcessMode mode) override;
    void process(ProcessData& data) override;
    void suspend() override;
    void resume() override;
    void setNumSpeakers(int *input, int numInputs, int *output, int numOutputs) override;
    int getLatencySamples() override {
        return plugin_->getLatencySamples();
    }

    void setListener(IPluginListener* listener) override;

    double getTransportPosition() const override {
        return plugin_->getTransportPosition();
    }

    float getParameter(int index) const override;
    size_t getParameterString(int index, ParamStringBuffer& buffer) const override;

    void setProgram(int index) override;
    int getProgram() const override;

    void setProgramName(std::string_view name) override;
    std::string getProgramName() const override;
    std::string getProgramNameIndexed(int index) const override;

    void readProgramFile(const std::string& path) override;
    void readProgramData(const char *data, size_t size) override;
    void writeProgramFile(const std::string& path) override;
    void writeProgramData(std::string& buffer) override;
    void readBankFile(const std::string& path) override;
    void readBankData(const char *data, size_t size) override;
    void writeBankFile(const std::string& path) override;
    void writeBankData(std::string& buffer) override;

    void openEditor(void *window) override {
        plugin_->openEditor(window);
    }
    void closeEditor() override {
        plugin_->closeEditor();
    }
    bool getEditorRect(Rect& rect) const override {
        return plugin_->getEditorRect(rect);
    }
    void updateEditor() override {
        plugin_->updateEditor();
    }
    void checkEditorSize(int &width, int &height) const override {
        plugin_->checkEditorSize(width, height);
    }
    void resizeEditor(int width, int height) override {
        plugin_->resizeEditor(width, height);
    }
    IWindow *getWindow() const override {
        return plugin_->getWindow();
    }

    // VST2 only
    int canDo(const char *what) const override {
        return plugin_->canDo(what);
    }
    intptr_t vendorSpecific(int index, intptr_t value, void *p, float opt) override;

    // IPluginListener
    void parameterAutomated(int index, float value) override;
    void latencyChanged(int nsamples) override;
    void updateDisplay() override;
    void pluginCrashed() override;
    void midiEvent(const MidiEvent& event) override;
    void sysexEvent(const SysexEvent& event) override;
private:
    void updateBuffer();
    template<typename T>
    void doProcess(ProcessData& data);
    void dispatchCommands();
    void sendEvents();
    template<typename T>
    void threadFunction(int numSamples);
    // data
    DSPThreadPool *threadPool_;
    IPlugin::ptr plugin_;
    IPluginListener* listener_ = nullptr;
    mutable Mutex mutex_; // use spinlock instead?
    Event event_;
    // commands/events
    void pushCommand(const Command& command) override {
        commands_[current_].push_back(command);
    }
    void pushEvent(const Command& event){
        events_[!current_].push_back(event);
    }
    std::vector<Command> commands_[2];
    std::vector<Command> events_[2];
    int current_ = 0;
    int program_ = 0; // current program number
    // buffer
    int blockSize_ = 0;
    ProcessPrecision precision_ = ProcessPrecision::Single;
    ProcessMode mode_ = ProcessMode::Realtime;
    std::unique_ptr<Bus[]> inputs_;
    int numInputs_ = 0;
    std::unique_ptr<Bus[]> outputs_;
    int numOutputs_ = 0;
    std::vector<char> buffer_;
};

} // vst