OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
soundfont.cpp
Go to the documentation of this file.
1#include "soundfont.h"
2
3#include <Tempest/Log>
4#include <bitset>
5
6#include "dlscollection.h"
7#include "hydra.h"
8
9using namespace Dx8;
10using namespace Tempest;
11
12#ifdef TSF_IMPLEMENTATION
13// For testing
14TSFDEF void __note_on(tsf* f, int preset_index, int key, float vel) {
15 short midiVelocity = short(vel * 127);
16 struct tsf_region *region, *regionEnd;
17
18 if(preset_index < 0 || preset_index >= f->presetNum)
19 return;
20
21 if(vel <= 0.0f) {
22 tsf_note_off(f, preset_index, key);
23 return;
24 }
25
26 // Play all matching regions.
27 int voicePlayIndex = f->voicePlayIndex++;
28 for(region = f->presets[preset_index].regions, regionEnd = region + f->presets[preset_index].regionNum; region != regionEnd; region++) {
29 if(key < region->lokey || key > region->hikey || midiVelocity < region->lovel || midiVelocity > region->hivel)
30 continue;
31
32 tsf_voice *voice = nullptr;
33 tsf_voice *v = f->voices, *vEnd = v + f->voiceNum;
34 if(region->group) {
35 for(; v != vEnd; v++)
36 if(v->playingPreset == preset_index && v->region->group == region->group)
37 tsf_voice_endquick(v, f->outSampleRate);
38 else if (v->playingPreset == -1 && !voice)
39 voice = v;
40 } else {
41 for(; v != vEnd; v++)
42 if(v->playingPreset==-1) {
43 voice = v;
44 break;
45 }
46 }
47
48 if(!voice) {
49 f->voiceNum += 4;
50 f->voices = (struct tsf_voice*)TSF_REALLOC(f->voices, f->voiceNum * sizeof(struct tsf_voice));
51 voice = &f->voices[f->voiceNum - 4];
52 voice[1].playingPreset = voice[2].playingPreset = voice[3].playingPreset = -1;
53 }
54
55 voice->region = region;
56 voice->playingPreset = preset_index;
57 voice->playingKey = key;
58 voice->playIndex = voicePlayIndex;
59 voice->noteGainDB = f->globalGainDB - region->attenuation - tsf_gainToDecibels(1.0f / vel);
60
61 if(f->channels) {
62 f->channels->setupVoice(f, voice);
63 } else {
64 tsf_voice_calcpitchratio(voice, 0, f->outSampleRate);
65 // The SFZ spec is silent about the pan curve, but a 3dB pan law seems common. This sqrt() curve matches what Dimension LE does; Alchemy Free seems closer to sin(adjustedPan * pi/2).
66 voice->panFactorLeft = TSF_SQRTF(0.5f - region->pan);
67 voice->panFactorRight = TSF_SQRTF(0.5f + region->pan);
68 }
69
70 // Offset/end.
71 voice->sourceSamplePosition = region->offset;
72
73 // Loop.
74 bool doLoop = (region->loop_mode != TSF_LOOPMODE_NONE && region->loop_start < region->loop_end);
75 voice->loopStart = (doLoop ? region->loop_start : 0);
76 voice->loopEnd = (doLoop ? region->loop_end : 0);
77
78 // Setup envelopes.
79 tsf_voice_envelope_setup(&voice->ampenv, &region->ampenv, key, midiVelocity, TSF_TRUE, f->outSampleRate);
80 tsf_voice_envelope_setup(&voice->modenv, &region->modenv, key, midiVelocity, TSF_FALSE, f->outSampleRate);
81
82 // Setup lowpass filter.
83 float filterQDB = region->initialFilterQ / 10.0f;
84 voice->lowpass.QInv = 1.0 / TSF_POW(10.0, (filterQDB / 20.0));
85 voice->lowpass.z1 = voice->lowpass.z2 = 0;
86 voice->lowpass.active = (region->initialFilterFc <= 13500);
87 if (voice->lowpass.active) tsf_voice_lowpass_setup(&voice->lowpass, tsf_cents2Hertz((float)region->initialFilterFc) / f->outSampleRate);
88
89 // Setup LFO filters.
90 tsf_voice_lfo_setup(&voice->modlfo, region->delayModLFO, region->freqModLFO, f->outSampleRate);
91 tsf_voice_lfo_setup(&voice->viblfo, region->delayVibLFO, region->freqVibLFO, f->outSampleRate);
92 }
93 }
94
95TSFDEF void __note_off(tsf* f, int preset_index, int key) {
96 tsf_voice *v = f->voices, *vEnd = v + f->voiceNum, *vMatchFirst = nullptr, *vMatchLast = nullptr;
97 for(; v!=vEnd; v++) {
98 //Find the first and last entry in the voices list with matching preset, key and look up the smallest play index
99 if(v->playingPreset != preset_index || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE)
100 continue;
101 if(!vMatchFirst || v->playIndex < vMatchFirst->playIndex)
102 vMatchFirst = vMatchLast = v;
103 else if(v->playIndex == vMatchFirst->playIndex)
104 vMatchLast = v;
105 }
106 if(!vMatchFirst)
107 return;
108 int sz = vMatchLast-vMatchFirst;
109 if(sz>1)
110 Tempest::Log::d();
111 for(v = vMatchFirst; v <= vMatchLast; v++) {
112 //Stop all voices with matching preset, key and the smallest play index which was enumerated above
113 if(v != vMatchFirst && v != vMatchLast &&
114 (v->playIndex != vMatchFirst->playIndex || v->playingPreset != preset_index || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE))
115 continue;
116 tsf_voice_end(v, f->outSampleRate);
117 }
118 }
119
120TSFDEF void __render_float(tsf* f, float* buffer, int samples, int flag_mixing)
121{
122 struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum;
123 if (!flag_mixing) TSF_MEMSET(buffer, 0, (f->outputmode == TSF_MONO ? 1 : 2) * sizeof(float) * samples);
124 for (; v != vEnd; v++)
125 if (v->playingPreset != -1)
126 tsf_voice_render(f, v, buffer, samples);
127}
128#endif
129
131 Data(const DlsCollection &dls,const std::vector<Wave>& wave)
132 :hydra(dls,wave) {
133 }
134
136 };
137
139 Instance(std::shared_ptr<Data> &shData,uint32_t dwPatch){
140 uint8_t bankHi = uint8_t((dwPatch & 0x00FF0000) >> 0x10);
141 uint8_t bankLo = uint8_t((dwPatch & 0x0000FF00) >> 0x8);
142 uint8_t patch = uint8_t(dwPatch & 0x000000FF);
143 int32_t bank = (bankHi << 16) + bankLo;
144
145 fnt = shData->hydra.toTsf();
146 preset = Hydra::presetIndex(fnt, bank, patch);
147 Hydra::setOutput(fnt,44100,0);
148 }
149
153
154 bool hasNotes() {
155 return Hydra::hasNotes(fnt);
156 }
157
158 void setPan(float p){
161 }
162
163 bool noteOn(uint8_t note, uint8_t velosity){
164 if(alloc[note]) {
165 return false;
166 }
167 alloc[note]=true;
168 Hydra::noteOn(fnt,preset,note,(velosity+0.5f)/127.f);
169 return true;
170 }
171
172 bool noteOff(uint8_t note){
173 if(!alloc[note])
174 return false;
175 alloc[note]=false;
177 return true;
178 }
179
180 std::bitset<256> alloc;
181 tsf* fnt=nullptr;
182 int preset=0;
183 };
184
186 Impl(std::shared_ptr<Data> &shData,uint32_t dwPatch)
188 }
189
191 }
192
193 void setPan(float p){
194 for(auto& i:inst)
195 i->setPan(p);
196 pan = p;
197 }
198
199 std::shared_ptr<Instance> noteOn(uint8_t note, uint8_t velosity){
200 for(auto& i:inst){
201 if(i->noteOn(note,velosity))
202 return i;
203 }
204 auto fnt = std::make_shared<Instance>(shData,dwPatch);
205 fnt->setPan(pan);
206 fnt->noteOn(note,velosity);
207 inst.emplace_back(fnt);
208 return inst.back();
209 }
210
211 /*
212 void noteOff(uint8_t note){
213 for(auto& i:inst){
214 if(i->noteOff(note))
215 return;
216 }
217 }*/
218
219 bool hasNotes() {
220 for(auto& i:inst)
221 if(i->hasNotes())
222 return true;
223 return false;
224 }
225
226 void mix(float *samples, size_t count) {
227 for(auto& i:inst)
228 Hydra::renderFloat(i->fnt,samples,int(count),true);
229 }
230
231 std::shared_ptr<Data> shData;
232 uint32_t dwPatch=0;
233 float pan=0.5f;
234 std::vector<std::shared_ptr<Instance>> inst;
235 };
236
239
240SoundFont::SoundFont(std::shared_ptr<Data> &sh, uint32_t dwPatch) {
241 impl = std::shared_ptr<Impl>(new Impl(sh,dwPatch));
242 }
243
246
247std::shared_ptr<SoundFont::Data> SoundFont::shared(const DlsCollection &dls,const std::vector<Wave>& wave) {
248 return std::shared_ptr<Data>(new Data(dls,wave));
249 }
250
252 if(impl==nullptr)
253 return false;
254 return impl->hasNotes();
255 }
256
257void SoundFont::setVolume(float /*v*/) {
258 // handled in mixer
259 }
260
261void SoundFont::setPan(float p) {
262 if(impl==nullptr)
263 return;
264 impl->setPan(p);
265 }
266
267void SoundFont::mix(float *samples, size_t count) {
268 if(impl==nullptr)
269 return;
270 impl->mix(samples,count);
271 }
272
273SoundFont::Ticket SoundFont::noteOn(uint8_t note, uint8_t velosity) {
274 Ticket t;
275 if(impl==nullptr)
276 return t;
277 t.impl = impl->noteOn(note,velosity);
278 t.note = note;
279 return t;
280 }
281
283 if(t.impl==nullptr)
284 return;
285 auto& i = *t.impl;
286 i.noteOff(t.note);
287 }
288
static void renderFloat(tsf *f, float *buffer, int samples, int flag)
Definition hydra.cpp:312
static void setOutput(tsf *f, int samplerate, float gain)
Definition hydra.cpp:320
static int presetIndex(const tsf *f, int bank, int presetNum)
Definition hydra.cpp:308
static bool hasNotes(tsf *tsf)
Definition hydra.cpp:292
static void noteOn(tsf *f, int presetId, int key, float vel)
Definition hydra.cpp:300
static int channelSetPan(tsf *f, int channel, float pan)
Definition hydra.cpp:316
static void finalize(tsf *tsf)
Definition hydra.cpp:287
static void noteOff(tsf *f, int presetId, int key)
Definition hydra.cpp:304
static void noteOff(Ticket &t)
Ticket noteOn(uint8_t note, uint8_t velosity)
bool hasNotes() const
void mix(float *samples, size_t count)
void setPan(float p)
static std::shared_ptr< Data > shared(const DlsCollection &dls, const std::vector< Wave > &wave)
void setVolume(float v)
Definition band.h:10
Data(const DlsCollection &dls, const std::vector< Wave > &wave)
Impl(std::shared_ptr< Data > &shData, uint32_t dwPatch)
std::vector< std::shared_ptr< Instance > > inst
std::shared_ptr< Instance > noteOn(uint8_t note, uint8_t velosity)
void setPan(float p)
void mix(float *samples, size_t count)
std::shared_ptr< Data > shData
bool noteOff(uint8_t note)
bool noteOn(uint8_t note, uint8_t velosity)
Instance(std::shared_ptr< Data > &shData, uint32_t dwPatch)
std::bitset< 256 > alloc