1 // Blip_Buffer 0.4.0. http://www.slack.net/~ant/
2 // Band-limited sound synthesis and buffering
3 /* Copyright (C) 2003-2006 Shay Green. This module is free software; you
4 * can redistribute it and/or modify it under the terms of the GNU Lesser
5 * General Public License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version. This
7 * module is distributed in the hope that it will be useful, but WITHOUT ANY
8 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10 * more details. You should have received a copy of the GNU Lesser General
11 * Public License along with this module; if not, write to the Free Software
12 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 // assumptions code makes about implementation-defined features
15 // right shift of negative value preserves sign
16 // casting to smaller signed type truncates bits and extends sign
20 // ////////////////////////////////////////////////////////////////////////// //
22 final class BlipBuffer
{
24 alias Time
= int; /// Time unit at source clock rate
28 /// Output samples are 16-bit signed, with a range of -32767 to 32767
31 /// Number of bits in resample ratio fraction. Higher values give a more accurate ratio but reduce maximum buffer size.
32 enum BufferAccuracy
= 16;
34 /** Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in
35 * noticeable broadband noise when synthesizing high frequency square waves.
36 * Affects size of Blip_Synth objects since they store the waveform directly.
44 /// Quality level. Start with blip_good_quality.
59 alias ResampledTime
= UInt
;
63 ResampledTime mOffset
;
64 BufType
[] mBufferDArr
;
69 enum BufferExtra
= blip_widest_impulse_
+2;
81 /** Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults
82 * to 1/4 second), then clear buffer. Returns Result.OK on success, otherwise if there
83 * isn't enough memory, returns error without affecting current buffer setup.
85 Result
setSampleRate(bool dofail
=true) (Int samples_per_sec
, uint msec_length
=1000/4) nothrow @trusted {
86 // start with maximum length that resampled time can represent
87 Int new_size
= (UInt
.max
>>BufferAccuracy
)-BufferExtra
-64;
88 if (msec_length
!= blip_max_length
) {
89 long s
= (cast(long)samples_per_sec
*(msec_length
+1)+999)/1000;
92 return Result
.BadArguments
; //assert(0, "requested buffer size too big");
97 if (s
< new_size
) new_size
= cast(Int
)s
;
99 mMSLength
= msec_length
; //UNRELIABLE!
101 if (mBufferSize
!= new_size
) {
102 mBufferDArr
.assumeSafeAppend
;
103 mBufferDArr
.length
= new_size
+BufferExtra
;
106 mBufferSize
= new_size
;
108 // update things based on the sample rate
109 mSampleRate
= samples_per_sec
;
110 mLength
= new_size
*1000/samples_per_sec
-1;
111 assert(msec_length
== 0 || mLength
== msec_length
); // ensure length is same as that passed in
112 if (mClockRate
) clockRate(mClockRate
);
120 /// Set number of source time units per second
121 @property void clockRate (Int cps
) nothrow @trusted @nogc {
123 mFactor
= clockRateFactor(cps
);
126 /** End current time frame of specified duration and make its samples available
127 * (along with any still-unread samples) for reading with read_samples(). Begins
128 * a new time frame at the end of the current frame.
130 void endFrame (Time time
) nothrow @trusted @nogc {
131 mOffset
+= time
*mFactor
;
132 assert(samplesAvail
<= cast(Int
)mBufferSize
); // time outside buffer length
135 /** Read at most 'max_samples' out of buffer into 'aout', removing them from from
136 * the buffer. Returns number of samples actually read and removed. If stereo is
137 * true, increments 'aout' one extra time after writing each sample, to allow
138 * easy interleving of two channels into a stereo output buffer.
139 * Fill both left and right channels if `fake_stereo` is set.
141 Int
readSamples(bool stereo
, bool fake_stereo
=false) (Sample
* aout
, Int max_samples
) nothrow @trusted @nogc {
142 Int count
= samplesAvail
;
143 if (count
> max_samples
) count
= max_samples
;
145 enum sample_shift
= SampleBits
-16;
146 immutable ubyte bass_shift
= this.mBassShift
;
147 Int accum
= mReaderAccum
;
148 const(BufType
)* ain
= mBufferDArr
.ptr
;
149 for (Int n
= count
; n
--; ) {
150 Int s
= accum
>>sample_shift
;
151 accum
-= accum
>>bass_shift
;
153 *aout
= cast(Sample
)s
;
155 if (cast(Sample
)s
!= s
) *aout
= cast(Sample
)(0x7FFF-(s
>>24));
157 static if (fake_stereo
) { aout
[1] = aout
[0]; }
163 mReaderAccum
= accum
;
164 removeSamples(count
);
169 // Additional optional features
171 /// Current output sample rate
172 @property Int
sampleRate () const nothrow @trusted @nogc { return mSampleRate
; }
174 /// Current output sample rate
175 @property void sampleRate (Int rate
) nothrow @trusted { setSampleRate
!false(rate
, (mMSLength ? mMSLength
: 1000/4)); }
177 /// Length of buffer, in milliseconds
178 @property int length () const nothrow @trusted @nogc { return mLength
; }
180 /// Number of source time units per second
181 @property Int
clockRate () const nothrow @trusted @nogc { return mClockRate
; }
183 /// Set frequency high-pass filter frequency, where higher values reduce bass more
184 void bassFreq (int freq
) nothrow @trusted @nogc {
189 Int f
= (freq
<<16)/mSampleRate
;
190 while ((f
>>= 1) != 0 && --shift
) {}
195 /// Number of samples delay from synthesis to samples read out
196 int outputLatency () const nothrow @trusted @nogc { return blip_widest_impulse_
/2; }
198 /** Remove all available samples and clear buffer to silence. If 'entire_buffer' is
199 * false, just clears out any samples waiting rather than the entire buffer.
201 void clear(bool entire_buffer
=true) () nothrow @trusted @nogc {
204 if (mBufferDArr
.length
) {
205 static if (entire_buffer
) {
206 Int count
= mBufferSize
;
208 Int count
= samples_avail
;
210 mBufferDArr
[0..count
+BufferExtra
] = 0;
214 /// Number of samples available for reading with read_samples()
215 Int
samplesAvail () const nothrow @trusted @nogc { return cast(Int
)(mOffset
>>BufferAccuracy
); }
217 /// Remove 'count' samples from those waiting to be read
218 void removeSamples (Int count
) nothrow @trusted @nogc {
219 import core
.stdc
.string
: memmove
, memset
;
221 removeSilence(count
);
222 // copy remaining samples to beginning and clear old samples
223 Int remain
= samplesAvail
+BufferExtra
;
224 if (remain
) memmove(mBufferDArr
.ptr
, mBufferDArr
.ptr
+count
, remain
*mBufferDArr
[0].sizeof
);
225 memset(mBufferDArr
.ptr
+remain
, 0, count
*mBufferDArr
[0].sizeof
);
229 // Experimental features
231 /// Number of raw samples that can be mixed within frame of specified duration.
232 Int
countSamples (Time duration
) const nothrow @trusted @nogc {
233 UInt last_sample
= resampledTime(duration
)>>BufferAccuracy
;
234 UInt first_sample
= mOffset
>>BufferAccuracy
;
235 return cast(Int
)(last_sample
-first_sample
);
238 /// Mix 'count' samples from 'ain' into buffer.
239 void mixSamples (const(Sample
)* ain
, Int count
) nothrow @trusted @nogc {
240 BufType
* aout
= mBufferDArr
.ptr
+(mOffset
>>BufferAccuracy
)+blip_widest_impulse_
/2;
241 enum sample_shift
= SampleBits
-16;
244 Int s
= (cast(Int
)(*ain
++))<<sample_shift
;
252 /** Count number of clocks needed until 'count' samples will be available.
253 * If buffer can't even hold 'count' samples, returns number of clocks until
254 * buffer becomes full.
256 Time
countClocks (Int count
) const nothrow @trusted @nogc {
257 if (count
> mBufferSize
) count
= mBufferSize
;
258 ResampledTime time
= (cast(ResampledTime
)count
)<<BufferAccuracy
;
259 return cast(Time
)((time
-mOffset
+mFactor
-1)/mFactor
);
262 /// not documented yet
263 void removeSilence (Int count
) nothrow @trusted @nogc {
264 assert(count
<= samplesAvail
); // tried to remove more samples than available
265 mOffset
-= (cast(ResampledTime
)count
)<<BufferAccuracy
;
269 ResampledTime
resampledDuration (int t
) const nothrow @trusted @nogc { return t
*mFactor
; }
272 ResampledTime
resampledTime (Time t
) const nothrow @trusted @nogc { return t
*mFactor
+mOffset
; }
275 ResampledTime
clockRateFactor (Int clock_rate
) const nothrow @trusted @nogc {
276 import std
.math
: floor
;
277 double ratio
= cast(double)mSampleRate
/clock_rate
;
278 Int factor
= cast(Int
)floor(ratio
*((cast(Int
)1)<<BufferAccuracy
)+0.5);
279 assert(factor
> 0 ||
!mSampleRate
); // fails if clock/output ratio is too large
280 return cast(ResampledTime
)factor
;
284 this () nothrow @trusted { kill(); } ///
286 /// free memory and such
287 void kill () nothrow @trusted {
288 mFactor
= Int
.max
; //???
290 delete mBufferDArr
; mBufferDArr
= null; // double-safety! ;-)
302 // ////////////////////////////////////////////////////////////////////////// //
304 //alias Blip_Synth = Blip_Synth_Parm!(blip_good_quality, 65535);
307 // ////////////////////////////////////////////////////////////////////////// //
308 /** Range specifies the greatest expected change in amplitude. Calculate it
309 * by finding the difference between the maximum and minimum expected
310 * amplitudes (max - min).
312 final class BlipSynthBase(int quality
, int range
) {
313 static assert(quality
> 0 && quality
<= BlipBuffer
.High
, "invalid blip synth quality");
314 static assert(range
> 0 && range
<= 65535, "invalid blip synth range");
317 imp_t
[blip_res
*(quality
/2)+1+4] impulses
;
318 BlipSynth_
!(imp_t
, quality
) impl
;
320 final double calcVolUnit (double v
) {
321 pragma(inline
, true);
322 //return v*(1.0/(range < 0 ? -(range) : range));
323 return v
*(1.0/range
);
327 /// Set overall volume of waveform
328 void volume (double v
) nothrow @trusted @nogc { impl
.last_amp
= 0; impl
.volume_unit(calcVolUnit(v
)); }
330 /// Configure low-pass filter (see notes.txt)
331 void trebleEq() (in auto ref BlipEq eq
) nothrow @trusted @nogc { impl
.last_amp
= 0; impl
.treble_eq(eq
); }
333 /// Configure low-pass filter (see notes.txt)
334 void setVolumeTreble (double vol
, double treb
) nothrow @trusted @nogc { impl
.last_amp
= 0; impl
.set_volume_treble(calcVolUnit(vol
), treb
); }
336 /// Configure low-pass filter (see notes.txt)
337 void setOutputVolumeTreble (BlipBuffer b
, double vol
, double treb
) nothrow @trusted @nogc { impl
.buf
= b
; impl
.last_amp
= 0; impl
.set_volume_treble(calcVolUnit(vol
), treb
); }
339 /// Get/set Blip_Buffer used for output
340 BlipBuffer
output () nothrow @trusted @nogc { return impl
.buf
; }
341 void output (BlipBuffer b
) nothrow @trusted @nogc { impl
.set_buffer(b
); impl
.last_amp
= 0; }
343 /// Update amplitude of waveform at given time. Using this requires a separate
344 /// Blip_Synth for each waveform.
345 void update (BlipBuffer
.Time t
, int amp
) nothrow @trusted @nogc {
346 if (amp
< -range
) amp
= -range
; else if (amp
> range
) amp
= range
;
347 int delta
= amp
-impl
.last_amp
;
349 offsetResampled(t
*impl
.buf
.mFactor
+impl
.buf
.mOffset
, delta
, impl
.buf
);
352 // Low-level interface
354 // Add an amplitude transition of specified delta, optionally into specified buffer
355 // rather than the one set with output(). Delta can be positive or negative.
356 // The actual change in amplitude is delta * (volume / range)
357 void offset (BlipBuffer
.Time t
, int delta
, BlipBuffer buf
) nothrow @trusted @nogc { offsetResampled(t
*buf
.mFactor
+buf
.mOffset
, delta
, buf
); }
358 void offset (BlipBuffer
.Time t
, int delta
) nothrow @trusted @nogc { offset(t
, delta
, impl
.buf
); }
360 // Works directly in terms of fractional output samples. Contact author for more.
361 void offsetResampled (BlipBuffer
.ResampledTime time
, int delta
, BlipBuffer blip_buf
) nothrow @trusted @nogc {
362 enum BLIP_FWD (string i
) = "
363 t0 = i0*delta+buf[fwd+"~i
~"];
364 t1 = imp[blip_res*("~i
~"+1)]*delta+buf[fwd+1+"~i
~"];
365 i0 = imp[blip_res*("~i
~"+2)];
367 buf[fwd+1+"~i
~"] = t1;
370 enum BLIP_REV(string r
) = "
371 t0 = i0*delta+buf[rev-"~r
~"];
372 t1 = imp[blip_res*"~r
~"]*delta+buf[rev+1-"~r
~"];
373 i0 = imp[blip_res*("~r
~"-1)];
375 buf[rev+1-"~r
~"] = t1;
378 // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the
379 // need for a longer buffer as set by set_sample_rate().
380 assert(cast(BlipBuffer
.Int
)(time
>>BlipBuffer
.BufferAccuracy
) < blip_buf
.mBufferSize
);
381 delta
*= impl
.delta_factor
;
382 int phase
= cast(int)(time
>>(BlipBuffer
.BufferAccuracy
-BlipBuffer
.PhaseBits
)&(blip_res
-1));
383 imp_t
* imp
= impulses
.ptr
+blip_res
-phase
;
384 BlipBuffer
.Int
* buf
= blip_buf
.mBufferDArr
.ptr
+(time
>>BlipBuffer
.BufferAccuracy
);
385 BlipBuffer
.Int i0
= *imp
;
386 BlipBuffer
.Int t0
, t1
;
388 enum fwd
= (blip_widest_impulse_
-quality
)/2;
389 enum rev
= fwd
+quality
-2;
392 static if (quality
> 8) { mixin(BLIP_FWD
!"2"); }
393 static if (quality
> 12) { mixin(BLIP_FWD
!"4"); }
395 enum mid
= quality
/2-1;
396 t0
= i0
*delta
+buf
[fwd
+mid
-1];
397 t1
= imp
[blip_res
*mid
]*delta
+buf
[fwd
+mid
];
398 imp
= impulses
.ptr
+phase
;
399 i0
= imp
[blip_res
*mid
];
403 static if (quality
> 12) { mixin(BLIP_REV
!"6"); }
404 static if (quality
> 8) { mixin(BLIP_REV
!"4"); }
407 t0
= i0
*delta
+buf
[rev
];
408 t1
= (*imp
)*delta
+buf
[rev
+1];
414 this () nothrow @trusted { impl
.impulses
= impulses
.ptr
; }
418 // ////////////////////////////////////////////////////////////////////////// //
419 /// Low-pass equalization parameters
423 BlipBuffer
.Int rolloff_freq
= 0;
424 BlipBuffer
.Int cutoff_freq
= 0;
427 /// Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce treble, small positive values (0 to 5.0) increase treble.
429 this (double treble_db
, BlipBuffer
.Int arolloff_freq
=0, BlipBuffer
.Int acutoff_freq
=0) nothrow @trusted @nogc {
431 rolloff_freq
= arolloff_freq
;
432 cutoff_freq
= acutoff_freq
;
436 void generate (float* aout
, int count
, BlipBuffer
.Int sample_rate
) const nothrow @trusted @nogc {
437 import std
.math
: PI
, cos
;
438 // lower cutoff freq for narrow kernels with their wider transition band
439 // (8 points->1.49, 16 points->1.15)
440 immutable double half_rate
= sample_rate
*0.5;
441 double oversample
= blip_res
*2.25/count
+0.85;
442 if (cutoff_freq
) oversample
= half_rate
/cutoff_freq
;
443 immutable double cutoff
= rolloff_freq
*oversample
/half_rate
;
444 gen_sinc(aout
, count
, blip_res
*oversample
, treble
, cutoff
);
445 // apply (half of) hamming window
446 immutable double to_fraction
= PI
/(count
-1);
447 for (int i
= count
; i
--; ) aout
[i
] *= 0.54-0.46*cos(i
*to_fraction
);
452 // ////////////////////////////////////////////////////////////////////////// //
453 /// Sample reader for custom sample formats and mixing of BlipBuffer samples
456 const(BlipBuffer
.BufType
)* buf
;
457 BlipBuffer
.Int accum
;
460 /// Begin reading samples from buffer. Returns value to pass to next() (can be ignored if default bass_freq is acceptable).
461 int begin (BlipBuffer blip_buf
) nothrow @trusted @nogc {
462 pragma(inline
, true);
463 buf
= blip_buf
.mBufferDArr
.ptr
;
464 accum
= blip_buf
.mReaderAccum
;
465 return blip_buf
.mBassShift
;
469 BlipBuffer
.Int
read () const pure nothrow @trusted @nogc { pragma(inline
, true); return accum
>>(BlipBuffer
.SampleBits
-16); }
471 /// Current raw sample in full internal resolution
472 BlipBuffer
.Int
read_raw () const pure nothrow @trusted @nogc { pragma(inline
, true); return accum
; }
474 /// Advance to next sample
475 void next (int bass_shift
=9) nothrow @trusted @nogc { pragma(inline
, true); accum
+= (*buf
++)-(accum
>>bass_shift
); }
477 /// End reading samples from buffer. The number of samples read must now be removed using `Blip_Buffer.remove_samples()`.
478 void end (BlipBuffer b
) nothrow @trusted @nogc { pragma(inline
, true); b
.mReaderAccum
= accum
; }
482 // ////////////////////////////////////////////////////////////////////////// //
483 // End of public interface
486 enum blip_max_length
= 0;
487 enum blip_default_length
= 250;
489 enum blip_widest_impulse_
= 16;
490 enum blip_res
= 1<<BlipBuffer
.PhaseBits
;
493 // ////////////////////////////////////////////////////////////////////////// //
494 struct BlipSynth_(imp_t
, int width
) {
496 BlipEq cur_eq
= BlipEq(-8.0);
497 double mVolumeUnit
= 0;
498 BlipBuffer
.Int kernel_unit
= 0;
505 int delta_factor
= 0;
509 this (imp_t* aimpulses/*, int awidth*/) nothrow @trusted @nogc {
510 impulses = aimpulses;
515 @disable this (this); // no copies
517 void set_buffer (BlipBuffer abuf
) nothrow @trusted @nogc {
522 void set_volume_treble (double vol
, double treb
) nothrow @trusted @nogc {
523 cur_eq
.treble
= treb
;
525 treble_eq(cur_eq
); // recalculate
528 void treble_eq() (in auto ref BlipEq eq
) nothrow @trusted @nogc {
529 float[blip_res
/2*(blip_widest_impulse_
-1)+blip_res
*2] fimpulse
= void;
530 enum half_size
= blip_res
/2*(width
-1);
532 BlipBuffer
.Int sample_rate
= (buf
!is null ? buf
.sampleRate
: 48000);
533 eq
.generate(&fimpulse
[blip_res
], half_size
, sample_rate
);
535 //FIXME! don't do copypasta here
536 cur_eq
.treble
= eq
.treble
;
537 cur_eq
.rolloff_freq
= eq
.rolloff_freq
;
538 cur_eq
.cutoff_freq
= eq
.cutoff_freq
;
540 // need mirror slightly past center for calculation
541 for (int i
= blip_res
; i
--; ) fimpulse
[blip_res
+half_size
+i
] = fimpulse
[blip_res
+half_size
-1-i
];
544 //for (int i = 0; i < blip_res; ++i) fimpulse[i] = 0.0f;
545 fimpulse
[0..blip_res
] = 0;
547 // find rescale factor
549 foreach (float v
; fimpulse
[blip_res
..blip_res
+half_size
]) total
+= v
;
551 //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB
552 //double const base_unit = 37888.0; // allows treble to +5 dB
553 enum base_unit
= cast(double)32768.0; // necessary for blip_unscaled to work
554 immutable double rescale
= base_unit
/2/total
;
555 kernel_unit
= cast(BlipBuffer
.Int
)base_unit
;
557 // integrate, first difference, rescale, convert to int
560 foreach (immutable int i
; 0..impulses_size
) {
561 import std
.math
: floor
;
562 impulses
[i
] = cast(imp_t
)floor((next
-sum
)*rescale
+0.5);
563 sum
+= fimpulse
.ptr
[i
];
564 next
+= fimpulse
.ptr
[i
+blip_res
];
568 // volume might require rescaling
569 double vol
= mVolumeUnit
;
576 void volume_unit (double new_unit
) nothrow @trusted @nogc {
577 import std
.math
: floor
;
578 if (new_unit
!= mVolumeUnit
) {
579 // use default eq if it hasn't been set yet
580 if (!kernel_unit
) treble_eq(cur_eq
);
581 mVolumeUnit
= new_unit
;
582 double factor
= new_unit
*((cast(BlipBuffer
.Int
)1)<<BlipBuffer
.SampleBits
)/kernel_unit
;
585 // if unit is really small, might need to attenuate kernel
586 while (factor
< 2.0) { ++shift
; factor
*= 2.0; }
588 kernel_unit
>>= shift
;
589 assert(kernel_unit
> 0); // fails if volume unit is too low
590 // keep values positive to avoid round-towards-zero of sign-preserving
591 // right shift for negative values
592 BlipBuffer
.Int offset
= 0x8000+(1<<(shift
-1));
593 BlipBuffer
.Int offset2
= 0x8000>>shift
;
594 for (int i
= impulses_size
; i
--; ) impulses
[i
] = cast(imp_t
)(((impulses
[i
]+offset
)>>shift
)-offset2
);
598 delta_factor
= cast(int)floor(factor
+0.5);
603 //int impulses_size () const nothrow @trusted @nogc { pragma(inline, true); return blip_res/2*width+1; }
604 //enum impulses_size = blip_res/2*width+1;
605 enum impulses_size
= blip_res
*(width
/2)+1;
607 void adjust_impulse () nothrow @trusted @nogc {
608 // sum pairs for each phase and add error correction to end of first half
609 enum size
= impulses_size
;
610 for (int p
= blip_res
; p
-- >= blip_res
/2; ) {
611 int p2
= blip_res
-2-p
;
612 BlipBuffer
.Int error
= kernel_unit
;
613 for (int i
= 1; i
< size
; i
+= blip_res
) {
614 error
-= impulses
[i
+p
];
615 error
-= impulses
[i
+p2
];
617 if (p
== p2
) error
/= 2; // phase = 0.5 impulse uses same half for both sides
618 impulses
[size
-blip_res
+p
] += error
;
624 // ////////////////////////////////////////////////////////////////////////// //
625 void gen_sinc (float* aout
, int count
, double oversample
, double treble
, double cutoff
) nothrow @trusted @nogc {
626 import std
.math
: PI
, cos
, pow
;
628 if (cutoff
> 0.999) cutoff
= 0.999;
629 if (treble
< -300.0) treble
= -300.0; else if (treble
> 5.0) treble
= 5.0;
631 enum maxh
= cast(double)4096.0;
632 immutable double rolloff
= pow(10.0, 1.0/(maxh
*20.0)*treble
/(1.0-cutoff
));
633 immutable double pow_a_n
= pow(rolloff
, maxh
-maxh
*cutoff
);
634 immutable double to_angle
= PI
/2/maxh
/oversample
;
635 foreach (immutable int i
; 0..count
) {
636 immutable double angle
= ((i
-count
)*2+1)*to_angle
;
637 double c
= rolloff
* cos( (maxh
- 1.0) * angle
) - cos( maxh
* angle
);
638 immutable double cos_nc_angle
= cos(maxh
*cutoff
*angle
);
639 immutable double cos_nc1_angle
= cos((maxh
*cutoff
-1.0)*angle
);
640 immutable double cos_angle
= cos(angle
);
642 c
= c
*pow_a_n
-rolloff
*cos_nc1_angle
+cos_nc_angle
;
643 immutable double d
= 1.0+rolloff
*(rolloff
-cos_angle
-cos_angle
);
644 immutable double b
= 2.0-cos_angle
-cos_angle
;
645 immutable double a
= 1.0-cos_angle
-cos_nc_angle
+cos_nc1_angle
;
647 aout
[i
] = cast(float)((a
*d
+c
*b
)/(b
*d
)); // a / b + c / d
652 // ////////////////////////////////////////////////////////////////////////// //
654 Blip_Buffer 0.4.0 Notes
655 -----------------------
656 Author : Shay Green <hotpop.com@blargg>
657 Website: http://www.slack.net/~ant/
658 Forum : http://groups.google.com/group/blargg-sound-libs
662 Blip_Buffer buffers samples at the current sampling rate until they are read
663 out. Blip_Synth adds waveforms into a Blip_Buffer, specified by amplitude
664 changes at times given in the source clock rate. To generate sound, setup one
665 or more Blip_Buffers and Blip_Synths, add sound waves, then read samples as
668 Waveform amplitude changes are specified to Blip_Synth in time units at the
669 source clock rate, relative to the beginning of the current time frame. When a
670 time frame is ended at time T, what was time T becomes time 0, and all samples
671 before that are made available for reading from the Blip_Buffer using
672 read_samples(). Time frames can be whatever length is convenient, and can
673 differ in length from one frame to the next. Also, samples don't need to be
674 read immediately after each time frame; unread samples accumulate in the buffer
675 until they're read (but also reduce the available free space for new
678 This sets up a Blip_Buffer at a 1MHz input clock rate, 44.1kHz output sample
682 buf.clock_rate( 1000000 );
683 if ( buf.set_sample_rate( 44100 ) )
686 This sets up a Blip_Synth with good sound quality, an amplitude range of 20
687 (for a waveform that goes from -10 to 10), at 50% volume, outputting to buf:
689 Blip_Synth<blip_good_quality,20> synth;
690 synth.volume( 0.50 );
691 synth.output( &buf );
693 See the demos for examples of adding a waveform and reading samples.
696 Treble and Bass Equalization
697 ----------------------------
698 Treble and bass frequency equalization can be adjusted. Blip_Synth::treble_eq(
699 treble_dB ) sets the treble level (in dB), where 0.0 dB gives normal treble;
700 -200.0 dB is quite muffled, and 5.0 dB emphasizes treble for an extra crisp
701 sound. Blip_Buffer::bass_freq( freq_hz ) sets the frequency where bass response
702 starts to diminish; 15 Hz is normal, 0 Hz gives maximum bass, and 15000 Hz
706 - - - - - - - - - - - - - - - - - - - - - - - -
707 1 Hz 0.0 dB Flat equalization
708 1 Hz +5.0 dB Extra crisp sound
709 16 Hz -8.0 dB Default equalization
710 180 Hz -8.0 dB TV Speaker
711 2000 Hz -47.0 dB Handheld game speaker
713 For example, to simulate a TV speaker, call buf.bass_freq( 180 ) and
714 synth.treble_eq( -8.0 ). The best way to find parameters is to write a program
715 which allows interactive control over bass and treble.
717 For more information about blip_eq_t, which allows several parameters for
718 low-pass equalization, post to the forum.
723 The length passed to Blip_Buffer::set_sample_rate() specifies the length of the
724 buffer in milliseconds. At the default time resolution, the resulting buffer
725 currently can't be more than about 65000 samples, which works out to almost
726 1500 milliseconds at the common 44.1kHz sample rate. This is much more than
727 necessary for most uses.
729 The output sample rate should be around 44-48kHz for best results. Since
730 synthesis is band-limited, there's almost no reason to use a higher sample
733 The ratio of input clock rate to output sample rate has limited precision (at
734 the default time resolution, rounded to nearest 1/65536th), so you won't get
735 the exact sample rate you requested. However, it is *exact*, so whatever the
736 input/output ratio is internally rounded to, it will generate exactly that many
737 output samples for each second of input. For example if you set the clock rate
738 to 768000 Hz and the sample rate to 48000 Hz (a ratio it can represent
739 exactly), there will always be exactly one sample generated for every 16 clocks
742 For an example of rounding, setting a clock rate of 1000000Hz (1MHz) and sample
743 rate of 44100 Hz results in an actual sample rate of 44097.9 Hz, causing an
744 unnoticeable shift in frequency. If you're running 60 frames of sound per
745 second and expecting exactly 735 samples each frame (to keep synchronized with
746 graphics), your code will require some changes. This isn't a problem in
747 practice because the computer's sound output itself probably doesn't run at
748 *exactly* the claimed sample rate, and it's better to synchronize graphics with
749 sound rather than the other way around. Put another way, even if this library
750 could generate exactly 735 samples per frame, every frame, you would still have
751 audio problems (try generating a sine wave manually and see). Post to the forum
752 if you'd like to discuss this issue further.
757 There are more advanced topics not covered here, some of which aren't fully
758 worked out. Some of these are: using multiple clock rates, more efficient
759 synthesis, higher resampling ratio accuracy, an mini-version in the C language,
760 sound quality issues, mixing samples directly into a Blip_Buffer. I lack
761 feedback from people using the library so I haven't been able to complete
762 design of these features. Post to the forum and we can work on adding these
768 If you're having problems, try the following:
770 - Enable debugging support in your environment. This enables assertions and
771 other run-time checks.
773 - Turn the compiler's optimizer is off. Sometimes an optimizer generates bad
776 - If multiple threads are being used, ensure that only one at a time is
777 accessing objects from the library. This library is not in general thread-safe,
778 though independent objects can be used in separate threads.
780 - If all else fails, see if the demos work.
785 Understanding the basic internal operation might help in proper use of
786 Blip_Synth. There are two main aspects: what Blip_Synth does, and how samples
787 are stored internally to Blip_Buffer. A description of the core algorithm and
788 example code showing the essentials is available on the web:
790 http://www.slack.net/~ant/bl-synth/
792 When adding a band-limited amplitude transition, the waveform differs based on
793 the relative position of the transition with respect to output samples. Adding
794 a transition between two samples requires a different waveform than one
795 centered on one sample. Blip_Synth stores the waveforms at several small
796 increments of differing alignment and selects the proper one based on the
799 Blip_Synth adds step waveforms, which start at zero and end up at the final
800 amplitude, extending infinitely into the future. This would be hard to handle
801 given a finite buffer size, so the sample buffer holds the *differences*
802 between each sample, rather than the absolute sample values. For example, the
803 waveform 0, 0, 1, 1, 1, 0, 0 is stored in a Blip_Buffer as 0, 0, +1, 0, 0, -1,
804 0. With this scheme, adding a change in amplitude at any position in the buffer
805 simply requires adding the proper values around that position; no other samples
806 in the buffer need to be modified. The extension of the final amplitude occurs
807 when reading samples out, by keeping a running sum of all samples up to
810 The above should make it clearer how Blip_Synth::offset() gets its flexibility,
811 and that there is no penalty for making full use of this by adding amplitude
812 transitions in whatever order is convenient. Blip_Synth::update() simply keeps
813 track of the current amplitude and calls offset() with the change, so it's no
814 worse except being limited to a single waveform.
819 Thanks to Jsr (FamiTracker author), the Mednafen team (multi-system emulator),
820 and ShizZie (Nhes GMB author) for using and giving feedback for the library.
821 Thanks to Disch for his interest and discussions about the synthesis algorithm
822 itself, and for writing his own implementation of it (Schpune). Thanks to
823 Xodnizel for Festalon, whose sound quality got me interested in video game
824 sound emulation in the first place, and where I first came up with the
825 algorithm while optimizing its brute-force filter.