egra: even more comments
[iv.d.git] / follin / synth / sfxr.d
blob9b5a1d4f5934651a21e1df158acf6bdba523167c
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module iv.follin.synth.sfxr /*is aliced*/;
19 import iv.alice;
20 import iv.follin.engine : TflChannel;
22 static if (__traits(compiles, () { import iv.stream; })) {
23 import iv.stream;
26 // ////////////////////////////////////////////////////////////////////////// //
27 public struct Sfxr {
28 // seed must not be 0
30 static ulong nextrand64 (ref ulong seed) nothrow @trusted @nogc {
31 static if (__VERSION__ > 2067) pragma(inline, true);
32 if (seed == 0) seed = 0x29a; // arbitrary number
33 seed ^= seed>>12; // a
34 seed ^= seed<<25; // b
35 seed ^= seed>>27; // c
36 return seed*0x2545f4914f6cdd1dUL;
39 static uint nextrand32 (ref ulong seed) nothrow @trusted @nogc {
40 static if (__VERSION__ > 2067) pragma(inline, true);
41 if (seed == 0) seed = 0x29a; // arbitrary number
42 seed ^= seed>>12; // a
43 seed ^= seed<<25; // b
44 seed ^= seed>>27; // c
45 return (seed*0x2545f4914f6cdd1dUL)&0xffff_ffffu;
49 static uint nextrand32 (ref int seed) nothrow @trusted @nogc {
50 static if (__VERSION__ > 2067) pragma(inline, true);
51 if (!seed) seed = 0x29a; // arbitrary number
52 seed *= 16807;
53 return cast(uint)seed;
56 // fast floating point rand, suitable for noise
57 align(1) static union FITrick {
58 align(1):
59 float fres;
60 uint ires;
63 // gives [0..1] result (wow!)
64 static float nextfrand (ref int seed) nothrow @trusted @nogc {
65 static if (__VERSION__ > 2067) pragma(inline, true);
66 if (!seed) seed = 0x29a; // arbitrary number
67 seed *= 16807;
68 FITrick fi = void;
69 fi.ires = (((cast(uint)seed)>>9)|0x3f800000);
70 return fi.fres-1.0f;
73 // gives [-1..1] result (wow!)
74 static float nextfrandneg (ref int seed) nothrow @trusted @nogc {
75 static if (__VERSION__ > 2067) pragma(inline, true);
76 if (!seed) seed = 0x29a; // arbitrary number
77 seed *= 16807;
78 FITrick fi = void;
79 fi.ires = (((cast(uint)seed)>>9)|0x3f800000);
80 fi.fres -= 1.0f;
81 fi.ires ^= cast(uint)seed&0x8000_0000u;
82 return fi.fres;
85 enum Type : int {
86 Square, // 0
87 Sawtooth, // 1
88 Sine, // 2
89 Noise, // 3
92 int wave_type = Type.Square;
94 float p_base_freq = 0.3f;
95 float p_freq_limit = 0.0f;
96 float p_freq_ramp = 0.0f;
97 float p_freq_dramp = 0.0f;
98 float p_duty = 0.0f;
99 float p_duty_ramp = 0.0f;
101 float p_vib_strength = 0.0f;
102 float p_vib_speed = 0.0f;
103 float p_vib_delay = 0.0f;
105 float p_env_attack = 0.0f;
106 float p_env_sustain = 0.3f;
107 float p_env_decay = 0.4f;
108 float p_env_punch = 0.0f;
110 ubyte filter_on = false; // bool
111 float p_lpf_resonance = 0.0f;
112 float p_lpf_freq = 1.0f;
113 float p_lpf_ramp = 0.0f;
114 float p_hpf_freq = 0.0f;
115 float p_hpf_ramp = 0.0f;
117 float p_pha_offset = 0.0f;
118 float p_pha_ramp = 0.0f;
120 float p_repeat_speed = 0.0f;
122 float p_arp_speed = 0.0f;
123 float p_arp_mod = 0.0f;
125 float sound_vol = 0.5f;
127 int origSeed = 0x29a, curseed = 0x29a;
130 void setSeed (int seed) nothrow @trusted @nogc { origSeed = curseed = seed; }
132 void reset () nothrow @safe @nogc {
133 uint sd = origSeed;
134 this = this.init;
135 origSeed = curseed = sd;
138 void load (const(void)[] buf) {
139 if (buf.length != 105) throw new NamedException!"SFXR"("invalid sfxr init buffer");
140 auto ms = MemoryStreamRO(buf);
141 load(ms);
144 void load(ST) (auto ref ST st) if (isReadableStream!ST) {
145 scope(failure) reset();
147 auto ver = st.readNum!int();
148 if (ver != 100 && ver != 101 && ver != 102) throw new NamedException!"SFXR"("invalid stream version");
150 wave_type = st.readNum!int();
151 if (wave_type < 0 || wave_type > Type.max) throw new NamedException!"SFXR"("invalid stream wave type");
153 sound_vol = (ver >= 102 ? st.readNum!float() : 0.5f);
155 p_base_freq = st.readNum!float();
156 p_freq_limit = st.readNum!float();
157 p_freq_ramp = st.readNum!float();
158 p_freq_dramp = (ver >= 101 ? st.readNum!float() : 0.0f);
159 p_duty = st.readNum!float();
160 p_duty_ramp = st.readNum!float();
162 p_vib_strength = st.readNum!float();
163 p_vib_speed = st.readNum!float();
164 p_vib_delay = st.readNum!float();
166 p_env_attack = st.readNum!float();
167 p_env_sustain = st.readNum!float();
168 p_env_decay = st.readNum!float();
169 p_env_punch = st.readNum!float();
171 filter_on = st.readNum!ubyte();
172 p_lpf_resonance = st.readNum!float();
173 p_lpf_freq = st.readNum!float();
174 p_lpf_ramp = st.readNum!float();
175 p_hpf_freq = st.readNum!float();
176 p_hpf_ramp = st.readNum!float();
178 p_pha_offset = st.readNum!float();
179 p_pha_ramp = st.readNum!float();
181 p_repeat_speed = st.readNum!float();
183 if (ver >= 101) {
184 p_arp_speed = st.readNum!float();
185 p_arp_mod = st.readNum!float();
186 } else {
187 p_arp_speed = 0.0f;
188 p_arp_mod = 0.0f;
192 void save(ST) (auto ref ST st) if (isWriteableStream!ST) {
193 st.writeNum!int(102); // version
195 st.writeNum!int(wave_type);
197 st.writeNum!float(sound_vol);
199 st.writeNum!float(p_base_freq);
200 st.writeNum!float(p_freq_limit);
201 st.writeNum!float(p_freq_ramp);
202 st.writeNum!float(p_freq_dramp);
203 st.writeNum!float(p_duty);
204 st.writeNum!float(p_duty_ramp);
206 st.writeNum!float(p_vib_strength);
207 st.writeNum!float(p_vib_speed);
208 st.writeNum!float(p_vib_delay);
210 st.writeNum!float(p_env_attack);
211 st.writeNum!float(p_env_sustain);
212 st.writeNum!float(p_env_decay);
213 st.writeNum!float(p_env_punch);
215 st.writeNum!ubyte(filter_on);
216 st.writeNum!float(p_lpf_resonance);
217 st.writeNum!float(p_lpf_freq);
218 st.writeNum!float(p_lpf_ramp);
219 st.writeNum!float(p_hpf_freq);
220 st.writeNum!float(p_hpf_ramp);
222 st.writeNum!float(p_pha_offset);
223 st.writeNum!float(p_pha_ramp);
225 st.writeNum!float(p_repeat_speed);
227 st.writeNum!float(p_arp_speed);
228 st.writeNum!float(p_arp_mod);
231 void exportWAV(string mode, BT, ST) (auto ref ST st)
232 if ((is(BT == byte) || is(BT == short)) && (mode == "mono" || mode == "stereo") && isWriteableStream!ST && isSeekableStream!ST)
234 static if (is(BT == byte)) {
235 enum bitsPerSample = 8;
236 } else {
237 enum bitsPerSample = 16;
239 static if (mode == "mono") {
240 enum numChans = 1;
241 } else {
242 enum numChans = 2;
244 enum sampleRate = 44100;
246 st.rawWrite("RIFF");
247 auto fszpos = st.tell;
248 st.writeNum!uint(0); // remaining file size
249 st.rawWrite("WAVE");
251 st.rawWrite("fmt ");
252 st.writeNum!uint(16); // chunk size
253 st.writeNum!ushort(1); // compression code
254 st.writeNum!ushort(numChans); // channels
255 st.writeNum!uint(sampleRate); // sample rate
256 st.writeNum!uint(sampleRate*(bitsPerSample*numChans)/8); // bytes per second
257 st.writeNum!ushort((bitsPerSample*numChans)/8); // block align
258 st.writeNum!ushort(bitsPerSample); // bits per sample
260 st.rawWrite("data");
261 auto dszpos = st.tell;
262 st.writeNum!uint(0); // chunk size
264 BT[256*numChans] asmp;
265 auto smp = SfxrSample(this);
266 uint dataSize = 0;
267 do {
268 smp.fillBuffer!(mode, BT)(asmp[]);
269 st.rawWrite(asmp[]);
270 dataSize += cast(uint)(asmp.length*asmp[0].sizeof);
271 } while (smp.playing);
273 auto epos = st.tell;
274 st.seek(dszpos);
275 st.writeNum!uint(dataSize);
276 st.seek(fszpos);
277 st.writeNum!uint(epos-8);
279 st.seek(epos);
282 void exportRaw(string mode, BT, ST) (auto ref ST st)
283 if ((is(BT == byte) || is(BT == short)) && (mode == "mono" || mode == "stereo") && isWriteableStream!ST)
285 static if (is(BT == byte)) {
286 enum bitsPerSample = 8;
287 } else {
288 enum bitsPerSample = 16;
290 static if (mode == "mono") {
291 enum numChans = 1;
292 } else {
293 enum numChans = 2;
295 enum sampleRate = 44100;
297 BT[256*numChans] asmp;
298 auto smp = SfxrSample(this);
299 uint dataSize = 0;
300 do {
301 smp.fillBuffer!(mode, BT)(asmp[]);
302 st.rawWrite(asmp[]);
303 dataSize += cast(uint)(asmp.length*asmp[0].sizeof);
304 } while (smp.playing);
307 uint rnd () nothrow @trusted @nogc { static if (__VERSION__ > 2067) pragma(inline, true); return nextrand32(curseed); }
308 float frnd (float range) nothrow @trusted @nogc { static if (__VERSION__ > 2067) pragma(inline, true); return nextfrand(curseed)*range; }
310 void rndPickup () nothrow @safe @nogc {
311 reset();
312 p_base_freq = 0.4f+frnd(0.5f);
313 p_env_attack = 0.0f;
314 p_env_sustain = frnd(0.1f);
315 p_env_decay = 0.1f+frnd(0.4f);
316 p_env_punch = 0.3f+frnd(0.3f);
317 if (rnd()%2) {
318 p_arp_speed = 0.5f+frnd(0.2f);
319 p_arp_mod = 0.2f+frnd(0.4f);
323 void rndLaser () nothrow @safe @nogc {
324 reset();
325 wave_type = rnd()%3;
326 if (wave_type==2 && rnd()%2) wave_type = rnd()%2;
327 p_base_freq = 0.5f+frnd(0.5f);
328 p_freq_limit = p_base_freq-0.2f-frnd(0.6f);
329 if (p_freq_limit < 0.2f) p_freq_limit = 0.2f;
330 p_freq_ramp = -0.15f-frnd(0.2f);
331 if (rnd()%3 == 0) {
332 p_base_freq = 0.3f+frnd(0.6f);
333 p_freq_limit = frnd(0.1f);
334 p_freq_ramp = -0.35f-frnd(0.3f);
336 if (rnd()%2) {
337 p_duty = frnd(0.5f);
338 p_duty_ramp = frnd(0.2f);
339 } else {
340 p_duty = 0.4f+frnd(0.5f);
341 p_duty_ramp = -frnd(0.7f);
343 p_env_attack = 0.0f;
344 p_env_sustain = 0.1f+frnd(0.2f);
345 p_env_decay = frnd(0.4f);
346 if (rnd()%2) p_env_punch = frnd(0.3f);
347 if (rnd()%3 == 0) {
348 p_pha_offset = frnd(0.2f);
349 p_pha_ramp = -frnd(0.2f);
351 if (rnd()%2) p_hpf_freq = frnd(0.3f);
354 void rndExplosion () nothrow @safe @nogc {
355 reset();
356 wave_type = Type.Noise;
357 if (rnd()%2) {
358 p_base_freq = 0.1f+frnd(0.4f);
359 p_freq_ramp = -0.1f+frnd(0.4f);
360 } else {
361 p_base_freq = 0.2f+frnd(0.7f);
362 p_freq_ramp = -0.2f-frnd(0.2f);
364 p_base_freq *= p_base_freq;
365 if (rnd()%5 == 0) p_freq_ramp = 0.0f;
366 if (rnd()%3 == 0) p_repeat_speed = 0.3f+frnd(0.5f);
367 p_env_attack = 0.0f;
368 p_env_sustain = 0.1f+frnd(0.3f);
369 p_env_decay = frnd(0.5f);
370 if (rnd()%2 == 0) {
371 p_pha_offset = -0.3f+frnd(0.9f);
372 p_pha_ramp = -frnd(0.3f);
374 p_env_punch = 0.2f+frnd(0.6f);
375 if (rnd()%2) {
376 p_vib_strength = frnd(0.7f);
377 p_vib_speed = frnd(0.6f);
379 if (rnd()%3 == 0) {
380 p_arp_speed = 0.6f+frnd(0.3f);
381 p_arp_mod = 0.8f-frnd(1.6f);
385 void rndPowerup () nothrow @safe @nogc {
386 reset();
387 if (rnd()%2) wave_type = Type.Sawtooth; else p_duty = frnd(0.6f);
388 if (rnd()%2) {
389 p_base_freq = 0.2f+frnd(0.3f);
390 p_freq_ramp = 0.1f+frnd(0.4f);
391 p_repeat_speed = 0.4f+frnd(0.4f);
392 } else {
393 p_base_freq = 0.2f+frnd(0.3f);
394 p_freq_ramp = 0.05f+frnd(0.2f);
395 if (rnd()%2) {
396 p_vib_strength = frnd(0.7f);
397 p_vib_speed = frnd(0.6f);
400 p_env_attack = 0.0f;
401 p_env_sustain = frnd(0.4f);
402 p_env_decay = 0.1f+frnd(0.4f);
405 void rndHurt () nothrow @safe @nogc {
406 reset();
407 wave_type = rnd()%3;
408 if (wave_type == Type.Sine) wave_type = Type.Noise;
409 if (wave_type == Type.Square) p_duty = frnd(0.6f);
410 p_base_freq = 0.2f+frnd(0.6f);
411 p_freq_ramp = -0.3f-frnd(0.4f);
412 p_env_attack = 0.0f;
413 p_env_sustain = frnd(0.1f);
414 p_env_decay = 0.1f+frnd(0.2f);
415 if (rnd()%2) p_hpf_freq = frnd(0.3f);
418 void rndJump () nothrow @safe @nogc {
419 reset();
420 wave_type = Type.Square;
421 p_duty = frnd(0.6f);
422 p_base_freq = 0.3f+frnd(0.3f);
423 p_freq_ramp = 0.1f+frnd(0.2f);
424 p_env_attack = 0.0f;
425 p_env_sustain = 0.1f+frnd(0.3f);
426 p_env_decay = 0.1f+frnd(0.2f);
427 if (rnd()%2) p_hpf_freq = frnd(0.3f);
428 if (rnd()%2) p_lpf_freq = 1.0f-frnd(0.6f);
431 void rndBlip () nothrow @safe @nogc {
432 reset();
433 wave_type = rnd()%2;
434 if (wave_type == Type.Square) p_duty = frnd(0.6f);
435 p_base_freq = 0.2f+frnd(0.4f);
436 p_env_attack = 0.0f;
437 p_env_sustain = 0.1f+frnd(0.1f);
438 p_env_decay = frnd(0.2f);
439 p_hpf_freq = 0.1f;
444 // ////////////////////////////////////////////////////////////////////////// //
445 public struct SfxrSample {
446 bool playing_sample = false;
448 float master_vol = 0.5f;
450 Sfxr fx;
451 int phase;
452 double fperiod;
453 double fmaxperiod;
454 double fslide;
455 double fdslide;
456 int period;
457 float square_duty;
458 float square_slide;
459 int env_stage;
460 int env_time;
461 int[3] env_length;
462 float env_vol;
463 float fphase;
464 float fdphase;
465 int iphase;
466 float[1024] phaser_buffer;
467 int ipp;
468 float[32] noise_buffer;
469 float fltp;
470 float fltdp;
471 float fltw;
472 float fltw_d;
473 float fltdmp;
474 float fltphp;
475 float flthp;
476 float flthp_d;
477 float vib_phase;
478 float vib_speed;
479 float vib_amp;
480 int rep_time;
481 int rep_limit;
482 int arp_time;
483 int arp_limit;
484 double arp_mod;
485 int curseed;
487 this() (in auto ref Sfxr sfx) { reset(sfx); curseed = sfx.origSeed; }
489 void setSeed (int seed) nothrow @trusted @nogc { curseed = seed; }
491 //float frnd (float range) nothrow @trusted @nogc { static if (__VERSION__ > 2067) pragma(inline, true); return (1.0f/16384.0f)*range*(Sfxr.nextrand32(curseed)%16384); }
493 @property bool playing () const pure nothrow @safe @nogc { return playing_sample; }
495 void resetLoop () nothrow @safe @nogc {
496 import std.math : abs, pow;
498 fperiod = 100.0/(fx.p_base_freq*fx.p_base_freq+0.001);
499 period = cast(int)fperiod;
500 fmaxperiod = 100.0/(fx.p_freq_limit*fx.p_freq_limit+0.001);
501 fslide = 1.0-pow(cast(double)fx.p_freq_ramp, 3.0)*0.01;
502 fdslide = -pow(cast(double)fx.p_freq_dramp, 3.0)*0.000001;
503 square_duty = 0.5f-fx.p_duty*0.5f;
504 square_slide = -fx.p_duty_ramp*0.00005f;
505 if (fx.p_arp_mod >= 0.0f) {
506 arp_mod = 1.0-pow(cast(double)fx.p_arp_mod, 2.0)*0.9;
507 } else {
508 arp_mod = 1.0+pow(cast(double)fx.p_arp_mod, 2.0)*10.0;
510 arp_time = 0;
511 arp_limit = cast(int)(pow(1.0f-fx.p_arp_speed, 2.0f)*20000+32);
512 if (fx.p_arp_speed == 1.0f) arp_limit = 0;
515 void reset() (in auto ref Sfxr sfx) nothrow @trusted @nogc {
516 import std.math : abs, pow;
518 fx = sfx;
519 phase = 0;
520 resetLoop();
521 // reset filter
522 fltp = 0.0f;
523 fltdp = 0.0f;
524 fltw = pow(fx.p_lpf_freq, 3.0f)*0.1f;
525 fltw_d = 1.0f+fx.p_lpf_ramp*0.0001f;
526 fltdmp = 5.0f/(1.0f+pow(fx.p_lpf_resonance, 2.0f)*20.0f)*(0.01f+fltw);
527 if (fltdmp > 0.8f) fltdmp = 0.8f;
528 fltphp = 0.0f;
529 flthp = pow(fx.p_hpf_freq, 2.0f)*0.1f;
530 flthp_d = 1.0+fx.p_hpf_ramp*0.0003f;
531 // reset vibrato
532 vib_phase = 0.0f;
533 vib_speed = pow(fx.p_vib_speed, 2.0f)*0.01f;
534 vib_amp = fx.p_vib_strength*0.5f;
535 // reset envelope
536 env_vol = 0.0f;
537 env_stage = 0;
538 env_time = 0;
539 env_length[0] = cast(int)(fx.p_env_attack*fx.p_env_attack*100000.0f);
540 env_length[1] = cast(int)(fx.p_env_sustain*fx.p_env_sustain*100000.0f);
541 env_length[2] = cast(int)(fx.p_env_decay*fx.p_env_decay*100000.0f);
543 fphase = pow(fx.p_pha_offset, 2.0f)*1020.0f;
544 if (fx.p_pha_offset < 0.0f) fphase = -fphase;
545 fdphase = pow(fx.p_pha_ramp, 2.0f)*1.0f;
546 if (fx.p_pha_ramp < 0.0f) fdphase = -fdphase;
547 iphase = abs(cast(int)fphase);
548 ipp = 0;
549 phaser_buffer[] = 0.0f;
551 auto nb = noise_buffer.ptr;
552 foreach (immutable _; 0..noise_buffer.length) *nb++ = /*frnd(2.0f)-1.0f*/Sfxr.nextfrandneg(curseed);
555 rep_time = 0;
556 rep_limit = cast(int)(pow(1.0f-fx.p_repeat_speed, 2.0f)*20000+32);
557 if (fx.p_repeat_speed == 0.0f) rep_limit = 0;
559 playing_sample = true;
562 // return `true` if the whole frame was silent
563 void fillBuffer(string mode, BT) (BT[] buffer) nothrow @trusted @nogc
564 if ((is(BT == byte) || is(BT == short) || is(BT == float)) && (mode == "mono" || mode == "stereo"))
566 import std.math : abs, pow, sin, PI;
567 //static assert(is(BT == float));
568 //static assert(mode == "mono");
570 static if (mode == "stereo") {
571 float smp;
572 bool smpdup;
575 foreach (immutable bidx; 0..buffer.length) {
576 auto bufel = buffer.ptr+bidx;
577 static if (mode == "stereo") {
578 if (smpdup) {
579 smpdup = false;
580 static if (is(BT == float)) *bufel = smp;
581 else static if (is(BT == byte)) *bufel = cast(byte)(smp*127);
582 else static if (is(BT == short)) *bufel = cast(short)(smp*32767);
583 else static assert(0, "wtf?!");
584 continue;
587 //if (!playing_sample) break; //FIXME: silence buffer?
588 if (!playing_sample) {
589 //static if (is(BT == ubyte)) bufel = 0.0f;
590 *bufel = 0;
591 continue;
594 ++rep_time;
595 if (rep_limit != 0 && rep_time >= rep_limit) {
596 rep_time = 0;
597 resetLoop();
600 // frequency envelopes/arpeggios
601 ++arp_time;
602 if (arp_limit != 0 && arp_time >= arp_limit) {
603 arp_limit = 0;
604 fperiod *= arp_mod;
606 fslide += fdslide;
607 fperiod *= fslide;
608 if (fperiod > fmaxperiod) {
609 fperiod = fmaxperiod;
610 if (fx.p_freq_limit > 0.0f) playing_sample = false;
612 float rfperiod = fperiod;
613 if (vib_amp > 0.0f) {
614 vib_phase += vib_speed;
615 rfperiod = fperiod*(1.0+sin(vib_phase)*vib_amp);
617 period = cast(int)rfperiod;
618 if (period < 8) period = 8;
619 square_duty += square_slide;
620 if (square_duty < 0.0f) square_duty = 0.0f;
621 if (square_duty > 0.5f) square_duty = 0.5f;
622 // volume envelope
623 ++env_time;
624 if (env_time > env_length[env_stage]) {
625 env_time = 0;
626 ++env_stage;
627 if (env_stage == 3) playing_sample = false;
629 if (env_stage == 0) env_vol = cast(float)env_time/env_length[0];
630 if (env_stage == 1) env_vol = 1.0f+pow(1.0f-cast(float)env_time/env_length[1], 1.0f)*2.0f*fx.p_env_punch;
631 if (env_stage == 2) env_vol = 1.0f-cast(float)env_time/env_length[2];
633 // phaser step
634 fphase += fdphase;
635 iphase = abs(cast(int)fphase);
636 if (iphase > 1023) iphase = 1023;
638 if (flthp_d != 0.0f) {
639 flthp *= flthp_d;
640 if (flthp < 0.00001f) flthp = 0.00001f;
641 if (flthp > 0.1f) flthp = 0.1f;
644 float ssample = 0.0f;
645 // 8x supersampling
646 foreach (immutable _; 0..8) {
647 float sample = 0.0f;
648 ++phase;
649 if (phase >= period) {
650 //phase = 0;
651 phase %= period;
652 if (fx.wave_type == Sfxr.Type.Noise) {
653 auto nb = noise_buffer.ptr;
654 foreach (immutable _0; 0..noise_buffer.length) *nb++ = /*frnd(2.0f)-1.0f*/Sfxr.nextfrandneg(curseed);
657 // base waveform
658 float fp = cast(float)phase/period;
659 final switch (fx.wave_type) with (Sfxr.Type) {
660 case Square: sample = (fp < square_duty ? 0.5f : -0.5f); break;
661 case Sawtooth: sample = 1.0f-fp*2; break;
662 case Sine: sample = cast(float)sin(fp*2*PI); break;
663 case Noise: sample = noise_buffer[phase*32/period]; break;
665 // lp filter
666 float pp = fltp;
667 fltw *= fltw_d;
668 if (fltw < 0.0f) fltw = 0.0f;
669 if (fltw > 0.1f) fltw = 0.1f;
670 if (fx.p_lpf_freq != 1.0f) {
671 fltdp += (sample-fltp)*fltw;
672 fltdp -= fltdp*fltdmp;
673 } else {
674 fltp = sample;
675 fltdp = 0.0f;
677 fltp += fltdp;
678 // hp filter
679 fltphp += fltp-pp;
680 fltphp -= fltphp*flthp;
681 sample = fltphp;
682 // phaser
683 phaser_buffer[ipp&1023] = sample;
684 sample += phaser_buffer[(ipp-iphase+1024)&1023];
685 ipp = (ipp+1)&1023;
686 // final accumulation and envelope application
687 ssample += sample*env_vol;
689 ssample = ssample/8*master_vol;
690 ssample *= 2.0f*fx.sound_vol;
691 // to buffer
693 static if (mode == "mono") float smp = ssample; else smp = ssample;
694 if (smp > 1.0f) smp = 1.0f;
695 if (smp < -1.0f) smp = -1.0f;
696 static if (is(BT == float)) *bufel = smp;
697 else static if (is(BT == byte)) *bufel = cast(byte)(smp*127);
698 else static if (is(BT == short)) *bufel = cast(short)(smp*32767);
699 else static assert(0, "wtf?!");
700 static if (mode == "stereo") smpdup = true;
707 // ////////////////////////////////////////////////////////////////////////// //
708 public class SfxChannel : TflChannel {
709 SfxrSample smp;
710 //shared bool sfxdone = false;
712 this() (in auto ref Sfxr sfxr) { smp.reset(sfxr); }
714 override uint fillFrames (float[] buf) nothrow {
715 if (!smp.playing) return 0; // no more
716 //{ import core.stdc.stdio; printf("filling sfx buffer... (%u)\n", (smp.playing ? 1 : 0)); }
717 smp.fillBuffer!"stereo"(buf[]);
718 return buf.length/2; // return number of frames
719 //{ import core.stdc.stdio; printf("filled sfx buffer... (%u)\n", (smp.playing ? 1 : 0)); }
723 override void discarded () nothrow {
724 import core.stdc.stdio;
725 printf("sfx complete\n");
726 atomicStore(sfxdone, true);