[PD] bit crusher abstraction

Matt Barber brbrofsvl at gmail.com
Sat May 1 07:21:52 CEST 2010


Attached is a file with the three different formulas I gave last time
for two-bit quantization, just for illustration -- any of these could
work as a general bit-crusher with a little extra effort.  Number one
has a variant you can toggle on or off.  Also notice how positive
max-amp is an exception in numbers one and three -- it can be clipped
out though.

Matt

On Thu, Apr 29, 2010 at 9:56 PM, Matt Barber <brbrofsvl at gmail.com> wrote:
>>> Reduced bit depth (which is what I think 'bit-crushing' means)  can be
>>> achieved by dividing the signal by x, pass it through something like
>>> [int~], multiply it by x again. An [int~] can be implemented by using
>>> [wrap~] and [-~], which are both vanilla.
>>
>> It is also worth considering adding a DC offset. Your [wrap~] solution
>> implements a floor-function, that is, rounding downwards. You can make it
>> round to closest, by adding x/2 before rounding downwards.
>>
>> [expr int($v1)] is rounding zerowards (thus output zero corresponds to a
>> twice bigger input range as any other input). Thus it will behave in a
>> weird unequal way.
>>
>> When the volume gets low, the _effective_ bits-per-sample gets very low,
>> because the relative precision of integers is proportional to amplitude
>> (which is not the case with floats). Therefore, even with 16-bit audio, if
>> your amplitude is 0.0001 times the max, it will feel as if it were 3-bit
>> audio. In such circumstances, the differences between the possible
>> roundings will become quite audible.
>
>
> Bit-depth transitions are really complicated things, especially when
> going from normalized float to int (and among different utitlities
> which do this, e.g. libsndfile or ecasound, I've found several
> different standards).  The biggest problem is what to do with the
> asymmetry about zero with int representations -- there are more
> negative numbers than positive in a two's complement int system.
>
> Here are three formulas I've used for noise-shaping dither in csound
> (the first one is more compatible with the bit-depth transitions that
> occur when csound writes a file).
>
> let X = the input signal and N = target bits:
>
> 1)  Y=(floor([2^(N-1)]*X)+0.5)/[2^(N-1)]
> 2)  Y=floor([2^(N-1)]*X+0.5)/[2^(N-1)]
> 3)  Y=floor([2^(N-1)]*X)/[2^(N-1)]
>
> The only difference is whether the 0.5 is added, and whether it's
> added before or after the floor function.
>
> So let's say you're moving from floats (-1 to +1) to 8-bits:
>
> In the first, one multiplies the signal by 2^7, getting a signal which
> varies from -128 to +128 (floats).  Flooring that will result in ints
> in the same range (+128 after the floor will be a relative rarity in
> real signals, though, so the effective range is -128 to 127).  Adding
> 0.5 to all of that gives a range of -127.5 to 127.5 (with the very
> rarely encountered 128.5).  Then division by 128 gives a signal which
> varies between two values equally spaced from 0, but a constant 0 in
> will result in a DC-offset out (it's a mid-rise function with 256
> possible values).
>
> In the second, one multiplies the signal by 2^7, getting a signal
> which varies from -128 to +128 (floats).  Adding 0.5 to this will
> result in a signal that varies between -127.5 to +128.5, and flooring
> this gives -128 to +128 (ints).  This time, +128 will not be as rare.
> This also is really just a rounding function, with x.5 values rounded
> up.  It's a mid-tread function, so zeros in will result in zeros out.
> At first glance it may seem as though this has one more than 2^8
> values (128 negative values, 128 positive values, and 0 = 257 total
> possible values).  However, if one were to graph the rounding
> function, one would find that anything that started out between -127.5
> and +127.5 will be rounded to -127 to +127 (255 values), while
> anything lower than -127.5 will round to -128, and anything higher
> than 127.5 will round to +128 -- the extremes of the staircase
> function have half-quantization-step mappings, so the total effect is
> still 8-bit.  This one has the advantage that both zero in and maxamp
> (positive or negative) give "correct" output values as well, but the
> disadvantage that one of the quantized steps is divided between the
> top and the bottom of the total range.  Also, its graph is a
> reflection of that of the first function about a diagonal.
>
> In the third, one starts with the same math as the first, up to the
> floor function, which gives an effective range of -128 to 127.  Then
> one simply divides by the original scalar, which just gives the
> mid-tread version of the function from the first formula (the first
> formula minus half the quantization step), with zeros-in-zeros out but
> one extra negative value than positive.
>
>
> Maybe sometime later I could post some pictures for two-bit audio.
> I'm sure there are plenty on the web.  There's also the question of
> whether, even for a bit-crushing effect, one would add dither.
>
>
> Matt
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: bitcrushers.pd
Type: application/octet-stream
Size: 5409 bytes
Desc: not available
URL: <http://lists.puredata.info/pipermail/pd-list/attachments/20100501/8eeacc57/attachment.obj>


More information about the Pd-list mailing list