lv2.cpp 8.82 KB
Newer Older
1
2
3
4
5
/**
 * @file Implementation file for the LV2 Format. The needed API functions are
 * defined here. Also the access to the Plugin is implemented here.
 */

6
7
#include "GlobalData.hpp"
#include <interfaces/IPlugin.hpp>
Benjamin Heisch's avatar
Benjamin Heisch committed
8
9
#include <lv2/atom/atom.h>
#include <lv2/atom/util.h>
10
11
12
#include <lv2/core/lv2.h>
#include <lv2/midi/midi.h>
#include <lv2/port-groups/port-groups.h>
Benjamin Heisch's avatar
Benjamin Heisch committed
13
14
#include <lv2/urid/urid.h>
#include <tools/PortHandling.hpp>
15
#include <unordered_map>
16
#include <vector>
17
using namespace APAL;
18

19
20
21
22
/**
 * @brief Handle, wich is used, to create an possibility to write and read  Midi
 * more easily.
 */
23
24
25
26
27
struct MidiHandle
{
  LV2_Atom_Sequence* midiDataLocation;
  IMidiPort* connectedMidiPort;
  LV2_URID midi_MidiEventID;
Benjamin Heisch's avatar
Benjamin Heisch committed
28
29
30
};
/**
 * @brief Treats the MidiHandle, at it would be an input. (Put MidiMsg in Pipe)
31
 * @param in Input MidiHandle.
32
33
34
35
36
37
38
39
40
41
42
 */
inline void
handleInput(MidiHandle* in)
{
  if (in->midiDataLocation != nullptr) {
    LV2_ATOM_SEQUENCE_FOREACH(in->midiDataLocation, ev)
    {
      if (ev->body.type == in->midi_MidiEventID) {
        const uint8_t* const msg = (const uint8_t*)(ev + 1);
        in->connectedMidiPort->feed({ msg[0], msg[1], msg[2] });
      }
Benjamin Heisch's avatar
Benjamin Heisch committed
43
    }
44
  }
Benjamin Heisch's avatar
Benjamin Heisch committed
45
46
}

47
48
49
/**
 * @brief Struct for a 3 byte MIDI event, used for writing notes
 */
50
51
52
53
typedef struct
{
  LV2_Atom_Event event;
  uint8_t msg[3];
Benjamin Heisch's avatar
Benjamin Heisch committed
54
} MIDINoteEvent;
55

Benjamin Heisch's avatar
Benjamin Heisch committed
56
/**
57
58
 * @brief Treats the MidiHandle, at it would be an output. (fetches things from
 * the Pipe to the output)
59
 * @param out Output Midihandle.
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
 */
inline void
handleOutput(MidiHandle* out)
{
  if (out->midiDataLocation != nullptr) {
    const uint32_t out_capacity = out->midiDataLocation->atom.size;
    // Write an empty Sequence header to the output
    lv2_atom_sequence_clear(out->midiDataLocation);
    // out->midiDataLocation->atom.type = out->midi_MidiEventID;
    while (!out->connectedMidiPort->empty()) {
      auto msg = out->connectedMidiPort->get();
      MIDINoteEvent ev{
        { 0,
          { sizeof(MIDINoteEvent) - sizeof(LV2_Atom),
            out->midi_MidiEventID } }, // LV2_Atom_Event
        { msg[0], msg[1], msg[2] }     // new MidiMsg
      };

      lv2_atom_sequence_append_event(
        out->midiDataLocation, out_capacity, &ev.event);
Benjamin Heisch's avatar
Benjamin Heisch committed
80
    }
81
  }
Benjamin Heisch's avatar
Benjamin Heisch committed
82
83
}

84
85
86
87
/**
 * @brief LV2Handle, wich is used to pass Data arount, between LV2-API function
 * calls.
 */
88
89
struct LV2HandleDataType
{
90
91
92
93
94
  IPlugin* plug;                 // Reference to current Plugin
  const LV2_Descriptor* lv2Desc; // Reference to LV2 Descripor
  LV2_URID_Map* map;             // URID Map to map ids.
  std::vector<MidiHandle>
    midiHandles; // Midihandles, to use when writing to output.
95
96
};

97
98
99
100
101
102
103
inline bool
supportsMidi(IPlugin* plug, IPort* port)
{
  return ((plug->getFeatureComponent()->supportsFeature(Feature::MidiInput) &&
           port->getDirection() == PortDirection::Input) ||
          (plug->getFeatureComponent()->supportsFeature(Feature::MidiOutput) &&
           port->getDirection() == PortDirection::Output));
104
}
105
106

/**
107
108
109
 * @brief Map, which maps URIs form plugins to ids, to match the internal API
 * structure.
 */
110
111
112
static std::unordered_map<std::string, uint32_t> URI_INDEX_MAP;
extern "C"
{
113
114
115
116
117
118
  /**
   * @brief Implementation of the lv2_descriptor entrypoint.
   * @param index index of a Plugin to load(compatible with lv2)
   * @return nullptr if index is invalid or an Pointer to the LV2_Descriptor for
   * the Plugin.
   */
119
120
  const LV2_Descriptor* lv2_descriptor(uint32_t index)
  {
121
    // return nullptr, if out of range.
122
    if (index >= GlobalData().getNumberOfRegisteredPlugins())
123
      return nullptr;
124
125
126
127
    PluginPtr plug = GlobalData().getPlugin(index);

    auto desc = new LV2_Descriptor();

128
    // get the identifaction URI for the Plugin.
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
    desc->URI = plug->getInfoComponent()->getPluginURI().data();
    URI_INDEX_MAP[std::string(plug->getInfoComponent()->getPluginURI())] =
      index;

    desc->activate = [](LV2_Handle instance) {
      auto data = static_cast<LV2HandleDataType*>(instance);
      data->plug->activate();
    };
    desc->deactivate = [](LV2_Handle instance) {
      auto data = static_cast<LV2HandleDataType*>(instance);
      data->plug->deactivate();
    };
    desc->connect_port = [](LV2_Handle instance,
                            uint32_t IPort,
                            void* DataLocation) {
      auto data = static_cast<LV2HandleDataType*>(instance);
      size_t midiPortIndex = 0;
      iteratePortsFlat(
        data->plug,
148
        [IPort, DataLocation, &data, &midiPortIndex](APAL::IPort* p,
149
150
151
152
153
154
                                                     size_t ind) {
          if (IPort == ind) {
            auto midiPort = dynamic_cast<IMidiPort*>(p);
            if (midiPort != nullptr) {
              if (supportsMidi(data->plug, midiPort)) {
                if (data->midiHandles.capacity() <
155
156
                    getNumberOfPorts<IMidiPort>(data->plug, PortDirection::All))
                  // Resize if vector is not big enough
157
158
                  data->midiHandles.resize(getNumberOfPorts<IMidiPort>(
                    data->plug, PortDirection::All));
159
                // Allocate for every MIDI Port an Midihandle
160
161
162
163
164
165
166
167
168
                data->midiHandles[midiPortIndex] =
                  MidiHandle{ (LV2_Atom_Sequence*)DataLocation,
                              midiPort,
                              data->map->map(data->map->handle,
                                             LV2_MIDI__MidiEvent) };
              }
            } else {
              auto aPort = dynamic_cast<IAudioPort*>(p);
              aPort->at(ind - IPort)->feed((float*)DataLocation);
Benjamin Heisch's avatar
Benjamin Heisch committed
169
            }
170
171
172
173
174
175
176
177
178
179
180
          }
          if (dynamic_cast<IMidiPort*>(p) != nullptr)
            midiPortIndex++;
          return false;
        });
    };

    desc->instantiate = [](const LV2_Descriptor* descriptor,
                           double,
                           const char*,
                           const LV2_Feature* const* features) -> LV2_Handle {
181
      // map an uri to an id, to use it  with the internal API
182
183
184
185
186
      auto descriptorIndex = URI_INDEX_MAP[std::string(descriptor->URI)];
      GlobalData().getPlugin(descriptorIndex)->init();
      auto lv2Handle = new LV2HandleDataType{
        GlobalData().getPlugin(descriptorIndex).get(), descriptor, nullptr, {}
      };
187
      // Get The URID_Map feature, which is needed to use with midi.
188
189
190
191
192
193
      for (int i = 0; features[i]; ++i) {
        if (!strcmp(features[i]->URI, LV2_URID__map)) {
          lv2Handle->map = (LV2_URID_Map*)features[i]->data;
          break;
        }
      }
194
195
      // When the map feature is not present, we cant instantiate... Maybe skip
      // this, if its not absolutly neccessary.
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
      if (!lv2Handle->map) {
        return NULL;
      }
      return lv2Handle;
    };

    desc->cleanup = [](LV2_Handle instance) {
      auto data = static_cast<LV2HandleDataType*>(instance);
      URI_INDEX_MAP.erase(
        std::string(data->plug->getInfoComponent()->getPluginURI()));
      data->plug->deinit();
      delete data->lv2Desc;
      delete data;
    };

    desc->run = [](LV2_Handle instance, uint32_t SampleCount) {
      auto data = static_cast<LV2HandleDataType*>(instance);
      // TODO not nice. Maybe write a function, which does this, but is RT
      // Capable (non mem allocation and blocking ist allowed.

      iteratePorts<IAudioPort>(data->plug,
                               [SampleCount](IAudioPort* p, size_t) {
218
                                 p->setSampleCount(SampleCount);
219
220
                                 return false;
                               });
221
      // Handle Input Midi
222
223
224
      for (auto mHandle : data->midiHandles)
        if (mHandle.connectedMidiPort->getDirection() == PortDirection::Input)
          handleInput(&mHandle);
225
      // process (and also write output midi inside here, to the internal API)
226
      data->plug->processAudio();
227
      // Handle Output Midi, which was written while processing.
228
229
230
231
232
      for (auto mHandle : data->midiHandles)
        if (mHandle.connectedMidiPort->getDirection() == PortDirection::Output)
          handleOutput(&mHandle);
    };

233
    // Currently is realy no need to support extension data.
234
235
236
237
238
    desc->extension_data = [](const char*) -> const void* { return nullptr; };

    return desc;
  }

239
240
241
242
243
244
245
246
  /**
   * @brief Implementation of the lv2_lib_descriptor. Its pretty straight
   * forward and uses lv2_descriptor mainly.
   * @param  bundle_path is the path to a bundle.
   * @param  features An array of supported Features by the host.
   * @return new LV2_Lib_Descriptor wich can be used to fetch Plugins, which are
   * created for the internal API.
   */
247
248
249
250
251
252
253
254
255
256
257
258
  const LV2_Lib_Descriptor* lv2_lib_descriptor(const char*,
                                               const LV2_Feature* const*)
  {
    auto lDesc = new LV2_Lib_Descriptor;
    lDesc->cleanup = [](LV2_Lib_Handle) {};
    lDesc->get_plugin = [](LV2_Lib_Handle, uint32_t index) {
      return lv2_descriptor(index);
    };
    lDesc->size = sizeof(LV2_Lib_Descriptor);
    lDesc->handle = nullptr;
    return lDesc;
  }
259
}