Anklang-0.3.0.dev956+gd75ac925 anklang-0.3.0.dev956+gd75ac925
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
liquidsfzplugin.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "liquidsfzplugin.hh"
3
4namespace Ase
5{
6
7/*
8 * do not use a std::mutex here because it may not be hard RT safe to
9 * try_lock() / unlock() it (depending on how the mutex is implemented)
10 */
11bool
12LiquidSFZPlugin::RTMutex::try_lock()
13{
14 return !locked_flag.test_and_set();
15}
16
17/* TODO: either get gid of RTMutex or use RAII */
18void
19LiquidSFZPlugin::RTMutex::wait_for_lock()
20{
21 while (!try_lock())
22 {
23 // this doesn't happen very often and we are in a non-RT thread, so we
24 // can block it for some time
25 // => wait for less than one frame drawing time until trying again
26 constexpr int fps = 240;
28 }
29}
30
31void
32LiquidSFZPlugin::RTMutex::unlock()
33{
34 locked_flag.clear();
35}
36
37LiquidSFZPlugin::LiquidSFZPlugin (tracktion::PluginCreationInfo info) : Plugin (info)
38{
39}
40
41/* TODO: should load in worker thread */
42bool
43LiquidSFZPlugin::load (const String& filename)
44{
45 rt_mutex_.wait_for_lock();
46
47 bool result;
48 if (synth_.is_bank (filename))
49 result = synth_.load_bank (filename) && synth_.select_program (0);
50 else
51 result = synth_.load (filename);
52
53 /* TODO:
54 * - synth_.live_mode() needs to be set for offline rendering
55 * - synth_.set_gain() could be mapped to a property
56 * - synth_.list_programs(), select_program() needs UI and save/restore support
57 * - synth_.list_ccs() needs synthesis and UI support
58 * - synth_.list_keys() needs UI support
59 * - synth_.set_log_function() needs engine support
60 */
61
62 rt_mutex_.unlock();
63
64 return result;
65}
66
67void
68LiquidSFZPlugin::initialise (const tracktion::PluginInitialisationInfo& info)
69{
70 rt_mutex_.wait_for_lock();
71 synth_.set_sample_rate (info.sampleRate);
72 synth_.all_sound_off();
73 rt_mutex_.unlock();
74}
75
76void
77LiquidSFZPlugin::deinitialise()
78{
79 rt_mutex_.wait_for_lock();
80 synth_.all_sound_off();
81 rt_mutex_.unlock();
82}
83
84void
85LiquidSFZPlugin::applyToBuffer (const tracktion::PluginRenderContext& fc)
86{
87 if (fc.destBuffer != nullptr)
88 {
89 /* try_lock is non-blocking and RT safe
90 *
91 * - if the mutex cannot be locked this means we're currently loading new SFZ data,
92 * so in this case we fill the output buffers with zeros
93 */
94 if (!rt_mutex_.try_lock())
95 {
96 tracktion::clearChannels (*fc.destBuffer, 0, -1, fc.bufferStartSample, fc.bufferNumSamples);
97 return;
98 }
99
100 tracktion::clearChannels (*fc.destBuffer, 2, -1, fc.bufferStartSample, fc.bufferNumSamples);
101 if (fc.bufferForMidiMessages != nullptr)
102 {
103 for (auto& m : *fc.bufferForMidiMessages)
104 {
105 int channel = m.getChannel();
106 /* juce::MidiMessage::getChannel starts at 1, LiquidSFZ channels are in range [0:15] */
107 if (channel > 0)
108 channel--;
109
110 if (m.isNoteOn())
111 {
112 const int note = m.getNoteNumber();
113 const int noteTimeSample = std::clamp (juce::roundToInt (m.getTimeStamp() * sampleRate), 0, fc.bufferNumSamples);
114
115 synth_.add_event_note_on (noteTimeSample, channel, note, m.getVelocity());
116 }
117 else if (m.isNoteOff())
118 {
119 const int note = m.getNoteNumber();
120 const int noteTimeSample = std::clamp (juce::roundToInt (m.getTimeStamp() * sampleRate), 0, fc.bufferNumSamples);
121
122 synth_.add_event_note_off (noteTimeSample, channel, note);
123 }
124 else if (m.isAllNotesOff() || m.isAllSoundOff())
125 {
126 synth_.all_sound_off(); // NOTE: there is no extra "all notes off" in liquidsfz
127 }
128 /* TODO: synth_.add_event_pitch_bend */
129 }
130 }
131 /* TODO: make a decision here how UI parameters (which ought to be automatable) map to CC values
132 *
133 * - should the liquidsfz synth maintain different CC state for different midi channels?
134 * - should automating a parameter affect all notes (or only these on specific channels)?
135 * - should CC events affect all notes (or only those on specific channels)?
136 * - should we support per note modulation?
137 *
138 * synth_.add_event_cc (frame, channel, cc, cc_value);
139 *
140 * What proprietary sforzando SFZ player vst3 seems to do:
141 *
142 * - it does not maintain different CC state for different midi channels (changing
143 * CC on midi channel 1 affects notes on midi channel 2)
144 * - automating a parameter affects all notes, regardless of midi channel
145 * - CC events affect all notes, regardless of midi channel
146 * - no support for per-note modulation
147 */
148
149 float *left = fc.destBuffer->getWritePointer (0, fc.bufferStartSample);
150 float *right = fc.destBuffer->getWritePointer (1, fc.bufferStartSample);
151 float *output[2] = { left, right };
152
153 synth_.process (output, fc.bufferNumSamples);
154
155 rt_mutex_.unlock();
156 }
157}
158
159const char* LiquidSFZPlugin::xmlTypeName = "liquidsfz";
160
161}
T clamp(T... args)
T left(T... args)
The Anklang C++ API namespace.
Definition api.hh:8
int roundToInt(const FloatType value) noexcept
T sleep_for(T... args)
T try_lock(T... args)