OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
mixer.cpp
Go to the documentation of this file.
1#include "mixer.h"
2
3#include <Tempest/Application>
4#include <Tempest/SoundEffect>
5#include <Tempest/Sound>
6#include <Tempest/Log>
7#include <cmath>
8#include <set>
9
10#include "soundfont.h"
11#include "wave.h"
12
13using namespace Dx8;
14using namespace Tempest;
15
16static int64_t toSamples(uint64_t time) {
17 return int64_t(time*SoundFont::SampleRate)/1000;
18 }
19
21 const size_t reserve=2048;
22 pcm.reserve(reserve*2);
23 pcmMix.reserve(reserve*2);
24 vol.reserve(reserve);
25 // uniqInstr.reserve(32);
26 }
27
29 for(auto& i:active)
30 SoundFont::noteOff(i.ticket);
31 }
32
34 if(current==m.impl)
35 return;
36
37 nextMus = m.impl;
38 embellishment.store(e);
39 }
40
41void Mixer::setMusicVolume(float v) {
42 if(current)
43 current->volume.store(v);
44 }
45
46int64_t Mixer::currentPlayTime() const {
47 return (sampleCursor*1000/SoundFont::SampleRate);
48 }
49
50int64_t Mixer::nextNoteOn(PatternList::PatternInternal& part,int64_t b,int64_t e) {
51 int64_t nextDt = std::numeric_limits<int64_t>::max();
52 int64_t timeTotal = toSamples(part.timeTotal);
53 bool inv = (e<b);
54
55 b-=patStart;
56 e-=patStart;
57
58 for(auto& i:part.waves) {
59 int64_t at = toSamples(i.at);
60 if((b<at && at<=e)^inv) {
61 int64_t t = at-b;
62 if(inv)
63 t += timeTotal;
64 if(t<nextDt && i.duration>0)
65 nextDt = t;
66 }
67 }
68
69 return nextDt;
70 }
71
72int64_t Mixer::nextNoteOff(int64_t b, int64_t /*e*/) {
73 int64_t actT = std::numeric_limits<int64_t>::max();
74
75 for(auto& i:active) {
76 int64_t at = i.at;
77 int64_t dt = at>b ? at-b : 0;
78 if(dt<actT)
79 actT = dt;
80 }
81 return actT;
82 }
83
84void Mixer::noteOn(std::shared_ptr<PatternInternal>& pattern, PatternList::Note *r) {
85 if(!checkVariation(*r))
86 return;
87
88 Active a;
89 a.at = sampleCursor + toSamples(r->duration);
90 a.ticket = r->inst->font.noteOn(r->note,r->velosity);
91 if(a.ticket==nullptr)
92 return;
93
94 for(auto& i:uniqInstr)
95 if(i.ptr==r->inst) {
96 a.parent = &i;
97 a.parent->counter++;
98 active.push_back(a);
99 return;
100 }
101 Instr u;
102 u.ptr = r->inst;
103 u.pattern = pattern;
104 uniqInstr.push_back(u);
105
106 a.parent = &uniqInstr.back();
107 a.parent->counter++;
108
109 active.push_back(a);
110 }
111
112void Mixer::noteOn(std::shared_ptr<PatternInternal>& pattern, int64_t time) {
113 time-=patStart;
114
115 size_t n=0;
116 for(auto& i:pattern->waves) {
117 int64_t at = toSamples(i.at);
118 if(at==time) {
119 noteOn(pattern,&i);
120 ++n;
121 }
122 }
123 if(n==0)
124 throw std::runtime_error("mixer critical error");
125 }
126
127void Mixer::noteOff(int64_t time) {
128 size_t sz=0;
129 for(size_t i=0;i<active.size();++i){
130 if(active[i].at>time) {
131 active[sz]=active[i];
132 sz++;
133 } else {
134 SoundFont::noteOff(active[i].ticket);
135 active[i].parent->counter--;
136 }
137 }
138 active.resize(sz);
139 }
140
141void Mixer::nextPattern() {
142 auto mus = current;
143 if(mus->pptn.size()==0) {
144 // no active music
145 pattern = nullptr;
146 return;
147 }
148
149 auto prev = pattern;
150 for(size_t i=0;i<mus->pptn.size();++i) {
151 if(mus->pptn[i]->timeTotal==0)
152 continue;
153 pattern = std::shared_ptr<PatternInternal>(mus,mus->pptn[i].get());
154 break;
155 }
156 size_t nextOff=0;
157 for(size_t i=0;i<mus->pptn.size();++i){
158 if(mus->pptn[i].get()==prev.get()) {
159 nextOff = (i+1)%mus->pptn.size();
160 break;
161 }
162 }
163
164 const DMUS_EMBELLISHT_TYPES em = embellishment.exchange(DMUS_EMBELLISHT_NORMAL);
165 const int groove = getGroove();
166 if(nextMus!=nullptr) {
167 current = nextMus;
168 nextMus = nullptr;
169 grooveCounter.store(0);
170 //variationCounter.store(0);
171 nextOff=0;
172 }
173
174 for(size_t i=0;i<mus->pptn.size();++i) {
175 auto& ptr = mus->pptn[(i+nextOff)%mus->pptn.size()];
176 if(ptr->timeTotal==0)
177 continue;
178 if(ptr->ptnh.wEmbellishment!=em)
179 continue;
180
181 if(mus->groove.size()==0 || (ptr->ptnh.bGrooveBottom<=groove && groove<=ptr->ptnh.bGrooveTop)) {
182 pattern = std::shared_ptr<PatternList::PatternInternal>(mus,ptr.get());
183 break;
184 }
185 }
186
187 if(pattern->timeTotal>0) {
188 grooveCounter.fetch_add(1);
189 }
190
191 if(em!=DMUS_EMBELLISHT_NORMAL) {
192 while(active.size()>0)
193 noteOff(active[0].at);
194 }
195
196 patStart = sampleCursor;
197 patEnd = patStart+toSamples(pattern->timeTotal);
198 for(auto& i:pattern->waves)
199 if(i.at==0) {
200 noteOn(pattern,&i);
201 }
202 variationCounter.fetch_add(1);
203 }
204
205Mixer::Step Mixer::stepInc(PatternInternal& pptn, int64_t b, int64_t e, int64_t samplesRemain) {
206 int64_t nextT = nextNoteOn (pptn,b,e);
207 int64_t offT = nextNoteOff(b,e);
208 int64_t samples = std::min(offT,nextT);
209
210 Step s;
211 if(samples>samplesRemain) {
212 s.samples=samplesRemain;
213 return s;
214 }
215
216 s.nextOn = nextT;//nextNoteOn (pptn,b,e);
217 s.nextOff = offT;//nextNoteOff(b,e);
218 s.samples = samples;
219 return s;
220 }
221
222void Mixer::stepApply(std::shared_ptr<PatternInternal> &pptn, const Mixer::Step &s,int64_t b) {
223 if(s.nextOff<s.nextOn) {
224 noteOff(s.nextOff+b);
225 } else
226 if(s.nextOff>s.nextOn) {
227 noteOn (pptn,s.nextOn+b);
228 } else
229 if(s.nextOff==s.nextOn) {
230 noteOff(s.nextOff+b);
231 noteOn (pptn,s.nextOn+b);
232 }
233 }
234
235std::shared_ptr<Mixer::PatternInternal> Mixer::checkPattern(std::shared_ptr<PatternInternal> p) {
236 auto cur = current;
237 if(cur==nullptr)
238 return nullptr;
239
240 if(p==nullptr) {
241 grooveCounter.store(0);
242 } else {
243 for(auto& i:cur->pptn)
244 if(i.get()==p.get()) {
245 return p;
246 }
247 }
248 // null or foreign
249 nextPattern();
250 return pattern;
251 }
252
253void Mixer::mix(int16_t *out, size_t samples) {
254 std::memset(out,0,2*samples*sizeof(int16_t));
255
256 auto cur = current;
257 if(cur==nullptr) {
258 current = nextMus;
259 return;
260 }
261
262 const int64_t samplesTotal = toSamples(cur->timeTotal);
263 if(samplesTotal==0) {
264 current = nextMus;
265 return;
266 }
267
268 auto pat = checkPattern(pattern);
269
270 size_t samplesRemain = samples;
271 while(samplesRemain>0) {
272 if(pat==nullptr)
273 break;
274 const int64_t remain = std::min(patEnd-sampleCursor,int64_t(samplesRemain));
275 const int64_t b = (sampleCursor );
276 const int64_t e = (sampleCursor+remain);
277
278 auto& pptn = *pat;
279 const float volume = cur->volume.load()*this->volume.load();
280
281 const Step stp = stepInc(pptn,b,e,remain);
282 implMix(pptn,volume,out,size_t(stp.samples));
283
284 if(remain!=stp.samples)
285 stepApply(pat,stp,sampleCursor);
286
287 sampleCursor += stp.samples;
288 out += stp.samples*2;
289 samplesRemain-= size_t(stp.samples);
290
291 // HACK: some music in addonworld.zen has odd paddings in the end of each track
292 if(stp.nextOn==std::numeric_limits<int64_t>::max() && uniqInstr.size()==0)
293 sampleCursor = patEnd;
294
295 if(sampleCursor==patEnd || nextMus!=nullptr) {
296 nextPattern();
297 if(!stp.isValid())
298 break;
299 }
300 }
301
302 uniqInstr.remove_if([](Instr& i){
303 return i.counter==0 && !i.ptr->font.hasNotes();
304 });
305 }
306
307void Mixer::setVolume(float v) {
308 volume.store(v);
309 }
310
311void Mixer::implMix(PatternInternal &pptn, float volume, int16_t *out, size_t cnt) {
312 const size_t cnt2=cnt*2;
313 pcm .resize(cnt2);
314 pcmMix.resize(cnt2);
315 vol .resize(cnt);
316
317 std::memset(pcmMix.data(),0,cnt2*sizeof(pcmMix[0]));
318
319 for(auto& i:uniqInstr) {
320 auto& ins = *i.ptr;
321 if(!ins.font.hasNotes())
322 continue;
323
324 std::memset(pcm.data(),0,cnt2*sizeof(pcm[0]));
325 ins.font.mix(pcm.data(),cnt);
326
327 float insVolume = std::pow(ins.volume,2.f);
328 if(ins.key==5 || ins.key==6) {
329 // HACK
330 // insVolume*=0.10f;
331 }
332 const bool hasVol = hasVolumeCurves(pptn,i);
333 if(hasVol) {
334 volFromCurve(pptn,i,vol);
335 for(size_t r=0;r<cnt2;++r) {
336 float v = vol[r/2];
337 pcmMix[r] += pcm[r]*insVolume*(v*v);
338 }
339 } else {
340 for(size_t r=0;r<cnt2;++r) {
341 float v = i.volLast;
342 pcmMix[r] += pcm[r]*insVolume*(v*v);
343 }
344 }
345 }
346
347 for(size_t i=0;i<cnt2;++i) {
348 float v = pcmMix[i]*volume;
349 out[i] = (v < -1.00004566f ? int16_t(-32768) : (v > 1.00001514f ? int16_t(32767) : int16_t(v * 32767.5f)));
350 }
351 }
352
353void Mixer::volFromCurve(PatternInternal &part,Instr& inst,std::vector<float> &v) {
354 float& base = inst.volLast;
355 for(auto& i:v)
356 i=base;
357
358 const int64_t shift = sampleCursor-patStart;
359 //const int64_t e = s+v.size();
360
361 for(auto& i:part.volume) {
362 if(i.inst!=inst.ptr)
363 continue;
364 if(!checkVariation(i))
365 continue;
366
367 int64_t s = toSamples(i.at)-shift;
368 int64_t e = toSamples(i.at+i.duration)-shift;
369 if((s>=0 && size_t(s)>v.size()) || e<0)
370 continue;
371
372 const size_t begin = size_t(std::max<int64_t>(s,0));
373 const size_t size = std::min(size_t(e),v.size());
374 const float range = float(e-s);
375 const float diffV = i.endV-i.startV;
376 const float shift = i.startV;
377 const float endV = i.endV;
378
379 switch(i.shape) {
380 case DMUS_CURVES_LINEAR: {
381 for(size_t i=begin;i<size;++i) {
382 float val = (float(i)-float(s))/range;
383 v[i] = val*diffV+shift;
384 }
385 break;
386 }
387 case DMUS_CURVES_INSTANT: {
388 for(size_t i=begin;i<size;++i) {
389 v[i] = endV;
390 }
391 break;
392 }
393 case DMUS_CURVES_EXP: {
394 for(size_t i=begin;i<size;++i) {
395 float val = (float(i)-float(s))/range;
396 v[i] = std::pow(val,2.f)*diffV+shift;
397 }
398 break;
399 }
400 case DMUS_CURVES_LOG: {
401 for(size_t i=begin;i<size;++i) {
402 float val = (float(i)-float(s))/range;
403 v[i] = std::sqrt(val)*diffV+shift;
404 }
405 break;
406 }
407 case DMUS_CURVES_SINE: {
408 for(size_t i=begin;i<size;++i) {
409 float linear = (float(i)-float(s))/range;
410 float val = std::sin(float(M_PI)*linear*0.5f);
411 v[i] = val*diffV+shift;
412 }
413 break;
414 }
415 }
416 if(size>begin)
417 base = v[size-1];
418 }
419 }
420
421int Mixer::getGroove() const {
422 auto pmus = current;
423 auto& mus = *pmus;
424 if(mus.groove.size()==0)
425 return 0;
426 auto& g = mus.groove[grooveCounter.load()%mus.groove.size()];
427 return g.bGrooveLevel;
428 }
429
430bool Mixer::hasVolumeCurves(Mixer::PatternInternal& part, Mixer::Instr& inst) const {
431 for(auto& i:part.volume) {
432 if(i.inst!=inst.ptr)
433 continue;
434 if(!checkVariation(i))
435 continue;
436 return true;
437 }
438 return false;
439 }
440
441template<class T>
442bool Mixer::checkVariation(const T& item) const {
443 if(item.inst->dwVarCount==0)
444 return false;
445 uint32_t vbit = variationCounter.load()%item.inst->dwVarCount;
446 if((item.dwVariation & (1<<vbit))==0)
447 return false;
448 return true;
449 }
void setVolume(float v)
Definition mixer.cpp:307
void setMusic(const Music &m, DMUS_EMBELLISHT_TYPES embellishment=DMUS_EMBELLISHT_NORMAL)
Definition mixer.cpp:33
void setMusicVolume(float v)
Definition mixer.cpp:41
int64_t currentPlayTime() const
Definition mixer.cpp:46
void mix(int16_t *out, size_t samples)
Definition mixer.cpp:253
static void noteOff(Ticket &t)
static int64_t toSamples(uint64_t time)
Definition mixer.cpp:16
Definition band.h:10
@ DMUS_CURVES_EXP
Definition structs.h:222
@ DMUS_CURVES_INSTANT
Definition structs.h:221
@ DMUS_CURVES_LINEAR
Definition structs.h:220
@ DMUS_CURVES_SINE
Definition structs.h:224
@ DMUS_CURVES_LOG
Definition structs.h:223
DMUS_EMBELLISHT_TYPES
Definition structs.h:93
@ DMUS_EMBELLISHT_NORMAL
Definition structs.h:94