Pulse Density Modulation

Mon, 11 May 2009

This is a bit of experimental code for working with sounds in the PDM domain. The simple explaination of what that is would probably be these pictures:

Input signal:

A sine wave

Converted to PDM (the three lines are independent, I wrapped it to avoid horizontal scrolling), oversampling 32 times:

A sine wave in PDM form

So as the waveform goes up, the PDM version gets darker, and when the waveform goes down, the PDM gets lighter. This is not quite the same as PWM, as we don't have a specific time window which we try to encode, and so we generally have more transitions from on to off in a time period.

People generally seem to pass the PDM signal to an amplifier, which then spits out a bigger version of the signal, which is then low pass filtered to get back to something resembling the original. This is a simple way to build a D/A converter, and they can be quite cheap, efficient and I believe also sound good.

I thought it would be interesting to do some processing on the signal while it is still in the PDM domain. So, I built a quick test application for taking a wave-file, putting it into PDM domain, doing something to it, and then returning it to PCM and spitting out another wave file.

Things which are trivial in PCM domain (like adjusting the volume) are a bit more involved for a PDM signal. This is (a simplified form of) the code I used:

for (int i = 0; i < get_length(this_channel); i++) {
  this_bit = get_bit(i);

  if (this_bit) error += VOLUME_FACTOR;
  else          error -= VOLUME_FACTOR;

  if (error > 0)   this_value = 0;
  else (error < 0) this_value = 1;

  if (this_value) error += 1;
  else            error -= 1;

  set_bit(this_channel, i, this_value);
}

Whereas for PCM, you would write something like:

for (int i = 0; i < get_length(this_channel); i++) {
  this_value = get_value(i);
  set_value(this_channel, i, this_value * VOLUME_FACTOR);
}

However, this also means that it isn't as obvious when we are going to clip. In PCM, you know you have clipped if the value ends up out of the range you are working in. But here, we are always working with 0 and 1, and there is no way we can get a -1 or a 2. If you drive it too hard, the output will still clip, but it just won't be as obvious that it will clip until you do the conversion back to PCM.

So what the included code does is:

  1. Split the input signal into high frequencies (> 2KHz) and low frequencies.
  2. Put the low frequency signal into PDM domain.
  3. Drive the low frequency signal using code like the code above, where VOLUME_FACTOR is big enough to make it clip frequently.
  4. Mix together the high frequencies, some clean lows and some distorted lows.

And it can transform something like this into something like this. Even though it destroys the bass drum (in a cool way), the filtering makes it one of the subtler things I've put up here :)

The code isn't written for speed. On the other hand, you should look at some of the hacks used to get the filters right. I use a stack of 1st order IIR filters to do the low pass filter. Then for the high pass, I delay it by a magic number and subtract this from the original. From what I understand, this shouldn't work very well thanks to the phase response of the IIR... but in practice it seems to work (just don't change the frequency without retuning the cutoff).

The C, GPL3 code.

Name & email are optional. Email will not be obfuscated.
HTML tags will be removed except hyperlinks.
 

About

I'm a nerd living in Sydney. This is a place where I can write stuff about my interests and not care that no one else is reading.

I like music, maths, programming, pretty pictures, filters and other good things.

(more info)

It should be fairly obvious that this isn't connected to my employer at all.

Email me (not a catchpa)

Email policy

Subscribe

RSS Feed RSS

Get an aggregator

Liferea (Linux)

Vienna (OSX)

Feedreader (Windows)

Google Reader (Web based)

I've only used Liferea, so I can't vouch for the other ones.

About this site

This site runs a (modified) version of blosxom.

The host is GeekISP, and they seem to do an excellent job.