-
Notifications
You must be signed in to change notification settings - Fork 94
/
Copy pathsoul_patch_Player.h
334 lines (275 loc) · 12.4 KB
/
soul_patch_Player.h
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/*
_____ _____ _____ __
| __| | | | |
|__ | | | | | |__
|_____|_____|_____|_____|
Copyright (c) 2018 - ROLI Ltd.
*/
#if ! SOUL_PATCH_MAIN_INCLUDE_FILE
#error "This header must not be included directly in your code - include soul_patch.h instead"
#endif
namespace soul
{
namespace patch
{
//==============================================================================
/** The set of properties that are known about a patch before it is compiled.
Most of these are taken directly from the .soulpatch manifest file contents.
*/
struct Description : public RefCountedBase
{
using Ptr = RefCountingPtr<Description>;
/** Provides acces to the .soulpatch manifest file from which this patch was loaded. */
VirtualFile* manifestFile;
const char* UID;
const char* version;
const char* name;
const char* description;
const char* category;
const char* manufacturer;
const char* URL;
bool isInstrument = false;
};
//==============================================================================
/** Gives information about one of the patch's buses.
Currently this is a minimal bus description, just providing the number of
channels. In the longer term, this will be expanded to include details such
as channel layouts and speaker assignments.
*/
struct Bus
{
String::Ptr name;
uint32_t numChannels = 0;
};
//==============================================================================
/** Provides access to a parameter's value and properties. */
struct Parameter : public RefCountedBase
{
using Ptr = RefCountingPtr<Parameter>;
String::Ptr ID, name, unit;
float minValue = 0, maxValue = 1.0f, step = 0, initialValue = 0;
/** Returns the current value of this parameter. */
virtual float getValue() const = 0;
/** Changes the value of this parameter.
The value that is passed-in will be clamped to the valid range, and
if a step vaiue is specified, it will also be quantised.
*/
virtual void setValue (float newValue) = 0;
/** Returns one of the properties from the annotation on the SOUL stream.
If there's no property with this name, it will return a nullptr.
*/
virtual String* getProperty (const char* propertyName) const = 0;
/** Returns the names of all the annotations on the SOUL stream. */
virtual Span<const char*> getPropertyNames() const = 0;
};
//==============================================================================
/** This struct is used to pass a */
struct SerialisedType
{
const void* serialisedTypeData;
uint32_t size;
choc::value::Type get() const
{
if (size == 0)
return {};
auto data = static_cast<const uint8_t*> (serialisedTypeData);
choc::value::InputData source { data, data + size };
return choc::value::Type::deserialise (source);
}
};
struct SerialisedValue
{
const void* serialisedValueData;
uint32_t size;
choc::value::Value get() const
{
if (size == 0)
return {};
auto data = static_cast<const uint8_t*> (serialisedValueData);
choc::value::InputData source { data, data + size };
return choc::value::Value::deserialise (source);
}
};
using EndpointHandle = uint32_t;
struct EndpointDescription
{
EndpointHandle handle;
String::Ptr ID, name;
soul::EndpointType type;
SerialisedValue annotation;
const SerialisedType* valueTypes;
uint32_t numValueTypes;
};
//==============================================================================
/** Holds the settings needed when compiling an instance of a PatchPlayer. */
struct PatchPlayerConfiguration
{
double sampleRate = 0;
uint32_t maxFramesPerBlock = 0;
};
//==============================================================================
/** Description of an error or warning message. */
struct CompilationMessage
{
String::Ptr severity, description, filename, sourceLine;
uint32_t line = 0, column = 0;
bool isError = false;
std::string getFullMessage() const
{
return formatAnnotatedErrorMessage (severity, description, filename, sourceLine, line, column);
}
};
//==============================================================================
/**
A PatchPlayer is created by calling PatchInstance::compileNewPlayer().
Once created, a PatchPlayer provides more detailed information about the
parameters and buses, and can actually render audio. While running, the
only modifications that can be made are to parameters - if anything else
changes, such as the sample rate, the block size, or the soul patch source
code, then a new PatchPlayer must be created to replace the old one.
*/
class PatchPlayer : public RefCountedBase
{
public:
using Ptr = RefCountingPtr<PatchPlayer>;
/** If compilation failed, this will return one or more error messages and
the player can't be used.
If the player compiled successfully, this will return either an empty list,
or a list of just warnings. You should always be careful to check this before
using the player object!
@see isPlayable
*/
virtual Span<CompilationMessage> getCompileMessages() const = 0;
/** Returns true if the compilation succeeded (possibly with warnings) and the
player can be run.
@see getCompileMessages
*/
virtual bool isPlayable() const = 0;
/** Returns a Description object containing all the details about this patch. */
virtual Description* getDescription() const = 0;
/** Checks whether the configuration or other internal factors (such as the source
files of the patch) have changed in a way that means this player is out of date
and should be replaced by a newly compiled player instance.
*/
virtual bool needsRebuilding (const PatchPlayerConfiguration&) = 0;
//==============================================================================
/** Returns a list of the input buses that this patch provides. */
virtual Span<Bus> getInputBuses() const = 0;
/** Returns a list of the output buses that this patch provides. */
virtual Span<Bus> getOutputBuses() const = 0;
/** Returns a list of patch's parameters. */
virtual Span<Parameter::Ptr> getParameters() const = 0;
virtual Span<EndpointDescription> getInputEventEndpoints() const = 0;
virtual Span<EndpointDescription> getOutputEventEndpoints() const = 0;
virtual EndpointDescription getEndpointDetails (const char* endpointID) const = 0;
/** Returns the patch's internal latency. */
virtual uint32_t getLatencySamples() const = 0;
//==============================================================================
/** This will reset the state of the player to its initial state.
Calls to this method must not be made concurrently with the render() method!
*/
virtual void reset() = 0;
/** Posts an event value to be delivered to an endpoint.
The event will be queued and will invoke a callback in the program at some
unspecified time in the future (probably the start of the next block that
gets processed). The available handles for input event endpoinst are obtained
with a call to getInputEventEndpoints().
*/
virtual bool sendInputEvent (EndpointHandle inputEndpointHandle,
const choc::value::ValueView& event) = 0;
//==============================================================================
/** Return value for the PatchPlayer::render() method. */
enum class RenderResult
{
ok,
noProgramLoaded,
wrongNumberOfChannels,
failure
};
/** Contains the info needed for a call to the PatchPlayer::render() method. */
struct RenderContext
{
/** A set of pointers to input channel data for the render() method to read.
See the numInputChannels variable for the number of channels, and numFrames for
the number of samples in each channel.
None of the pointers in this array may be null.
*/
const float* const* inputChannels;
/** A set of pointers to output channel data for the render() method to write.
See the numOutputChannels variable for the number of channels, and numFrames for
the number of samples in each channel.
None of the pointers in this array may be null.
*/
float* const* outputChannels;
/** An array of MIDI messages for the render method to process.
See the numMIDIMessages variable for the number of channels.
*/
const soul::MIDIEvent* incomingMIDI;
/** An array of MIDI messages for the render method to write to.
See the numMIDIMessages variable for the number of channels.
*/
soul::MIDIEvent* outgoingMIDI;
/** Number of audio frames to process */
uint32_t numFrames;
/** Number of channels in the input stream array */
uint32_t numInputChannels;
/** Number of channels in the output stream array */
uint32_t numOutputChannels;
/** Number of messages to process from the incomingMIDI buffer */
uint32_t numMIDIMessagesIn;
/** The maximum number of messages that can be added to the outgoingMIDI buffer */
uint32_t maximumMIDIMessagesOut;
/** On return, this will be set to the number of MIDI messages that could have been
added to the outgoingMIDI buffer. If more messages were available than the
maximumMIDIMessagesOut size, then the buffer will have been filled up to
maximumMIDIMessagesOut, and numMIDIMessagesOut will return a larger number to
indicate how many would have been added if it had been possible. */
uint32_t numMIDIMessagesOut;
};
//==============================================================================
/** Renders the next block of audio.
The RenderContext object provides all the necessary input/output audio and
MIDI data that is needed.
Note that any events or console messages which were dispatched during this call
will have been queued, and the caller must arrange for them to be delivered
by calling handleOutgoingEvents(), either synchronously on this thread, or
periodically on another thread.
*/
virtual RenderResult render (RenderContext&) = 0;
//==============================================================================
/** Used in handleOutgoingEvents. */
using HandleOutgoingEventFn = void(void* userContext, uint64_t frameIndex, const char* endpointName, const choc::value::ValueView&);
/** Used in handleOutgoingEvents. */
using HandleConsoleMessageFn = void(void* userContext, uint64_t frameIndex, const char* message);
/**
Flushes any outgoing event and console messages that are currently queued.
This must be called periodically if the patch is generating events, otherwise
the FIFO will overflow.
*/
virtual void handleOutgoingEvents (void* userContext,
HandleOutgoingEventFn* handleEvent,
HandleConsoleMessageFn* handleConsoleMessage) = 0;
//==============================================================================
/** Sends a time-signature to the patch.
A host must call this on the audio thread, before a call to render().
It should avoid calling it except when the value has actually changed.
*/
virtual void applyNewTimeSignature (TimeSignature) = 0;
/** Sends a new tempo to the patch.
A host must call this on the audio thread, before a call to render().
It should avoid calling it except when the value has actually changed.
*/
virtual void applyNewTempo (float newBPM) = 0;
/** Updates the patch about the playback state changing.
A host must call this on the audio thread, before a call to render().
It should avoid calling it except when the value has actually changed.
*/
virtual void applyNewTransportState (TransportState) = 0;
/** Tells the patch about the current position along a timeline.
A host must call this on the audio thread, before a call to render().
It should avoid calling it except when the value has actually changed.
*/
virtual void applyNewTimelinePosition (TimelinePosition) = 0;
};
} // namespace patch
} // namespace soul