-
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathSF2Engine.hpp
More file actions
210 lines (166 loc) · 8.4 KB
/
SF2Engine.hpp
File metadata and controls
210 lines (166 loc) · 8.4 KB
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
// Copyright © 2023, 2025 Brad Howes. All rights reserved.
#pragma once
#include <array>
#include <memory>
#include <string>
#include <vector>
#include <Foundation/Foundation.h>
#include <CoreAudioKit/CoreAudioKit.h>
#include <SF2File/Entity/Generator/Index.hpp>
#include <SF2Lib/MIDI/GeneratorOverride.hpp>
#include <SF2Lib/Render/Engine/ParameterAddress.hpp>
#include <swift/bridging>
/**
Umbrella header for the Engine module to use with Swift via interoperability mode.
*/
namespace SF2 {
namespace IO { class File; }
namespace Render { namespace Engine { class Engine; } }
}
/**
Wrapper class for the SF2::Render::Engine that exposes a minimal API for Swift/C++ bridging. Note that with this
wrapper, there is no direct way to manipulate the held SF2::Render::Engine::Engin entity. The expectation is that
an instance of SF2Engine will be held within an AUv3 plugin component, which an receive changes via its AUParameterTree
or via MIDI commands. This class exposes various `create` methods that return proper MIDI v1 commands. In particular,
the `createLoadFileUsePresetPayload` returns a MIDI SysEx sequence that indicates an SF2 file to load and a preset in
that file to use.
*/
struct SWIFT_ESCAPABLE SF2Engine
{
/**
Default constructor. Does not allocate anything.
*/
SF2Engine() noexcept;
/**
Destructor. Only for logging of reference count of any allocated Engine.
*/
~SF2Engine() noexcept;
/**
Default copy constructor.
*/
SF2Engine(const SF2Engine&) = default;
/**
Default move constructor.
*/
SF2Engine(SF2Engine&&) = default;
/**
Constructs a new Engine.
@param sampleRate the sample rate to use when rendering. Note that this is not fixed and may change in the
call to `setRenderingFormat`.
@param voiceCount the max number of voices to allow to simultaneously render
*/
void create(double sampleRate, NSUInteger voiceCount);
/**
Set the rendering format to be when rendering in CoreAudio infrastructure. After returning from this call,
the engine will be ready to process and render samples -- though no sound will be emitted until a SF2 file
is installed and a preset is chosen.
@param busCount the number of busses to support. This will be at least one, and each bus will be stereo.
@param format the format to use for rendering
@param maxFramesToRender the max number of frames to be seen in a `processAndRender` call. A frame consists of one
sample per channel in a bus. For stereo, N frames = 2N audio samples.
@returns `true` if engine can start rendering
*/
bool setRenderingFormat(NSInteger busCount, AVAudioFormat* format, AUAudioFrameCount maxFramesToRender) const noexcept;
/**
Obtain a render block to be used to invoke the `processAndRender` method below.
*/
AUInternalRenderBlock getRenderBlock() const noexcept;
/**
Request to render samples. May be called on a real-time thread by the CoreAudio framework. Note that
`setRenderingFormat` will be called before the first call to this method.`
@param timestamp the point in time for the rendering to take place
@param frameCount the number of frames to render
@param outputBusNumber the bus that is being rendered to
@param output the audio buffer to write to. Note that this might not contain the actual samples buffers to use
@param realtimeEventListHead the list of events to process in order of timestamps when they should take place
@param pullInputBlock for an effect, this will be called to obtain samples to operate on. For the SF2 engine, this
is ignored.
@returns result of the call. Should be `noErr` unless there is an issue with one of the parameters and the rendering
could not take place.
*/
AUAudioUnitStatus processAndRender(const AudioTimeStamp* timestamp, UInt32 frameCount, NSInteger outputBusNumber,
AudioBufferList* output, const AURenderEvent* realtimeEventListHead,
AURenderPullInputBlock pullInputBlock) const noexcept;
/**
Obtain the name of the active preset being used by the engine. This will be an empty string ("") for the case where
there is no active preset.
@returns preset name or ""
*/
std::string activePresetName() const noexcept;
/// @returns current number of active voices
size_t activeVoiceCount() const noexcept;
AUParameterTree* getParameterTree() const noexcept;
/**
Obtain MIDI payload containing a MIDI SYSEX command that can be sent to load an SF2 file and use a given
preset. This should be sent to the engine via a MIDI control connection; this method only creates the bytes to send.
@param filePath the full path of the SF2 file to load
@param presetIndex the index of the preset in the file to use
@returns MIDI SYSEX command as a byte sequence
*/
static std::vector<uint8_t> createLoadFileUsePresetPayload(std::string filePath, size_t presetIndex) noexcept;
/**
Load a soundfont file at the given path. If successful, make active the preset at the given index.
NOTE: this should only be invoked when there is no rendering activity. Otherwise, this change should be done using a
SysEx MIDI command created by ``createLoadFileUsePresetPayload`` sent to the MIDI event scheduling block.
@param path the location of the file to load
@param presetIndex the index of the preset to make active after loading
*/
void loadFileAndPreset(std::string path, size_t presetIndex) noexcept;
/**
Obtain MIDI payload containing a MIDI SYSEX command that can be sent to load an SF2 file and use a given
preset. This should be sent to the engine via a MIDI control connection; this method only creates the bytes to send.
@param bookmark the bookmark data for the URL to load
@param presetIndex the index of the preset in the file to use
@returns MIDI SYSEX command as a byte sequence
*/
static std::vector<uint8_t> createLoadBookmarkUsePresetPayload(NSData* bookmark, size_t presetIndex) noexcept;
// SF2::MIDI::GeneratorOverrideVector overrides) noexcept;
/**
Load a soundfont file using a bookmark. If successful, make active the preset at the given index.
NOTE: this should only be invoked when there is no rendering activity. Otherwise, this change should be done using a
SysEx MIDI command created by ``createLoadBookmarkUsePresetPayload`` sent to the MIDI event scheduling block.
@param bookmark the encoded location of the file to load
@param presetIndex the index of the preset to make active after loading
*/
void loadBookmarkAndPreset(NSData* bookmark, size_t presetIndex) noexcept;
/**
Obtain MIDI payload containing MIDI command to reset the engine. This will stop playing any notes and reset
the MIDI controllers to a known state.
@returns MIDI command as a byte sequence
*/
static std::array<uint8_t, 1> createResetCommandPayload() noexcept;
/**
Obtain MIDI payload containing MIDI commands to set the desired bank and program to use.
@returns array of MIDI commands to be sent to engine
*/
static std::array<uint8_t, 9> createUseBankProgramPayload(uint16_t bank, uint8_t program) noexcept;
/**
Obtain MIDI payload containing MIDI command to send a channel message to the engine.
@param channelMessage indicates the channel message to send
@param value the value to send along with the command
@returns MIDI command as a byte sequence
*/
static std::array<uint8_t, 3> createChannelMessagePayload(uint8_t channelMessage, uint8_t value) noexcept;
/**
Obtain MIDI payload containing MIDI command to stop any and all active notes.
@returns MIDI command as a byte sequence
*/
static std::array<uint8_t, 3> createAllNotesOffPayload() noexcept;
/**
Obtain MIDI payload containing MIDI command to stop all sound rendering.
@returns MIDI command as a byte sequence
*/
static std::array<uint8_t, 3> createAllSoundOffPayload() noexcept;
/// @returns true if the monophonic mode is enabled
bool monophonicModeEnabled() const noexcept;
/// @returns true if the polyphonic mode is enabled
bool polyphonicModeEnabled() const noexcept;
/// @returns true if portamento mode is enabled
bool portamentoModeEnabled() const noexcept;
/// @returns true if "one-voice-per-key" mode is enabled.
bool oneVoicePerKeyModeEnabled() const noexcept;
/// @returns true if `retrigger` mode is enabled.
bool retriggerModeEnabled() const noexcept;
private:
std::shared_ptr<SF2::Render::Engine::Engine> impl_;
} SWIFT_SELF_CONTAINED;