A simple bitcrusher and sample rate reducer in C++ for a Windows Store App

Pete Brown - 13January2013

I'm working on a Windows 8 synthesizer app using XAudio2 and a
C++ + DirectX/XAML Windows Store app for Windows 8. As part of
this, I thought it would be fun to add a simple bit
crusher effect with included sample rate reducer. The point of
this effect is to make samples sound like they came from older
machines with lower bitrates and sample depth. To do that, I had to
do two things to the samples:

1. Reduce the bit depth of the samples. This would control
vertical stepping as with a traditional bitcrusher.

2. Reduce the bit rate of the samples. By default, this is 44100
or 48000 samples per second. Older systems did quite a bit less,
often 8192 samples, sometimes fewer like 4096 or 8192. I want to
get that old low-fi vibe as simply as possible.

I'm not plugging into the XAudio2 effects pipeline or otherwise
using any XAudio2 plumbing for the bit crusher effect here. The
algorithm would be given a buffer of stereo samples to process. The
buffer size will eventually be based on performance, but right now,
I just have a looping sample buffer. If you want to learn how to
create your own audio using XAudio2, please refer to my older post on this topic (take
care to notice the comment at the bottom where I pointed out that I
didn't initialize one of the values).

I'm not presenting a complete project here, but will post enough
source for you to have context for what I'm doing.

The StereoSample Structure

Each sample is actually a stereo pair. Stereo oscillators? How
cool :)

struct StereoSample
{
public:
SAMPLE_t Left;
SAMPLE_t Right;
};

SAMPLE_t is defined as float.

The Voice Class

The voice class represents a single voice in the synthesizer.
Among other things, it includes a collection of oscillators. Right
now, all three oscillators are configured to output exactly the
same thing. The Render function handles that output as well as
plugging in the bit crusher effect. Note that I apply the effect to
the output from all three oscillators, but in the real synth, this
is decoupled so I could apply it to a single oscillator in a single
voice.

Volume is per-oscillator. Panning etc. is not implemented in
this listing. I have different render functions for each type of
waveform. They are switched using a function template to point to
the current render function. Thanks to everyone on Twitter last
night (especially Jeremiah Morrill) for helping me sort out how to
use the std::function type.

For reducing the bit rate, the current algorithm is like a
sample and hold. It takes the first sample and holds it for however
many steps it needs to. In fact, on my modular synth, if I patch an
oscillator into the sample and hold unit, I can get pretty much the
same result.

Note that this happens per-oscillator. Each voice has multiple
oscillators each of which may or may not be crushed, so I don't
simply submit a low bitrate buffer to XAudio2 and let it do the
expansion. I'm also working on a couple other algorithms in
addition to the current.

The bit depth reduction is handled by the round statements.
Floating point samples are in the range of -1.0 to +1.0. I first
calculate the maximum number for the specified bit depth (first
statement with pow). Then, I add 1.0 to the sample to get it into
the range of 0..2.0. I then map that 0..2.0 to the "max" value
(which is 0..max"). Then, dividing by "max" I get back a now
rounded value in the range or 0..2.0, with at most "max" unique
possible values. Finally, I subtract 1.0 to get back in the range
of -1.0 to +1.0.

UPDATE: @c64_gio on twitter sent me some really great
tips for how I can improve this code. I especially like the
iteration approach and use of vectors instead of raw bytes. You can
see some of his comments here: http://pastebin.com/Q8HqHRDd.

This algorithm appears to work, so let's take a look at the
results.

Results

I hooked up my Rigol to the main output of my sound card (a MOTU
828mk3) to take a look at the generated waveforms.

What follows are the configurations set on the BitCruncher class
and a photo of the resulting wave forms.

BitDepth 24, BitRate, 44100. This is about as good as it gets in
terms of the scope's resolution.

BitDepth 24, BitRate 8192 (very slight stepping)

(note that the frequency is warbling a bit between 110 and 220.
I think my Rigol is confused, possibly due to me not completing
cycles in the buffer)

BitDepth 24, BitRate 4096 (more stepping)

BitDepth 24, BitRate 2048 (a whole lot of stepping). This sounds
like a sine wave with whistling harmonics over it. It reminds me
(only louder) of the overtones from some 8 bit synth chips from
80's computers.

I believe this one was BitDepth 4 and BitRate 44100. Notice the
flats at the peaks. This is due to rounding and contributes a
square-wave overtone to the sound.

BitDepth 2, BitRate 2048 (this sounds much more like a square
wave). This one had a fair bit of jitter to it, presumably because
44100 is not evenly divisible by 2048, and for the final output,
I'm simply looping a buffer of 44100 * 5 seconds.

The bitrate reduction is especially interesting when applied to
white noise. You totally get the Atari/C64 vibe from it.

As I do other interesting things with this synthesizer project,
which will hopefully be in the Windows Store when I complete it,
I'll continue to post about them here.

Cool post man! Forgot about bind<>, which is much cleaner than the alternative of static methods!

I was actually looking for algo's to do this very same thing for a Win8 app I am working on, but for MediaFoundation. In my case MF was nice enough to (internally) down/up sample for me and allowed me to be lazy. :)

If you get into doing any channel mixing, I'd be interested in algos to help with audio artifacts, like clipping *nudge, nudge* ;)

I think those ultra modern and sleek C64 machines weren't as classy as their predecessor, the VIC-20

Comment on this Post

Name (required)

Email address (will not be published) (required)

Website url

Remember me

Your comment is being submitted, please wait...

Your comment has been posted, thank you. (If your comment does not appear shortly, it was marked as spam.)

Pete Brown is a XAML and Blinky lights guy at Microsoft who focuses on Windows XAML (WinRT), WPF, Silverlight, .NET Micro Framework and other "code on the client" and "code on a device" technologies. This is his personal blog.
About Pete/Full Bio | Contact | About 10rem.net