Synthesis Theory I: Programming Lab 5




This lab demonstrates how to write a program which will filter out unwanted DC components in a soundfile caused by a cheap microphone input or by processing of a soundfile which adds a DC signal to the soundfile.

The Problem

Many cheaper computer microphone inputs add a constant voltage offset to a recorded sound as demonstrated in the figure below:

When processing sound, it is more desirable to have the waveform centered about the zero point; otherwise the sound has a greater chance of clipping, especially if you add several sounds together. Besides, you cannot here 0 Hz, so you do not need to record it. Here is what we actually want, a signal (in this case a sinewave) with no DC component:

The Solution

To get rid of 0 Hz frequencies in the soundfile, it must be filtered. It is possible to subtract a constant amount from every sample in the soundfile, but a more generalized solution which will work in more situations, and work automatically is to filter out the low frequencies.

The simplest and most effect way of filtering out the low frequencies is with the following filter, shown as a flow-graph:

Below is a picture of a soundfile on the left which has a noticeable DC offset. The picture of the same sound on the right has been passed through the DC-blocking filter given in the flow-graph above, and it is now centered around rather than above the zero line.

How to convert the flow-graph to an actual program implementation? The flow-graph describes graphically what happens to the input signal as it is filtered. There are three paths the input (and output) to the DC blocking filter make when passing through the filter. The three separate paths of the signal through the flow-graph are highlighted in the picture below:

After identifying the independent signal paths in the filter, it is easy to write down the difference equation:

y[n] = x[n] - x[n-1] + a * y[n-1]
Where y[n] is the output at the current time n, and x[n] is the input at the current time n.

Remember in class the steps to figure out how the frequencies are affected by the filter:

Now, with the last equation for the frequency response, the following picture can be generated from the absolute value (or magnitude) of the frequency response:

This picture shows how the frequencies are effected below 400 Hz in a sound sampled at 44.1 kHz. The different plots are related to the scale value a in the flow-graph. When a is close to 1.0, then the filter will only filter out very low frequencies. As a goes toward 0.0, more and more frequencies are noticeable affected by the filter. At a=0.95, the attenuation (lowering of volume) of the sound at 400 Hz would be noticeable since there is a drop of 3dB for sounds in that region of the spectrum.

For musical purposes a is usually set to 0.995 because only frequencies below 30 Hz or so will be significantly reduced, while higher frequencies will remain mostly unaffected by the filter.

A useful analysis of the filter can be done by examining the locations of the poles and zeros on the z-plane. We haven't covered this in class yet, but you can contemplate what the following picture means for next week:

Programming the Filter

The following program implements the DC-blocking filter. Save it to a file and compile (as explained in Lab 1). You can do download the file dcblock1.cpp with the following command:
   wget http://peabody.sapp.org/class/st1/lab/prog5/dcblock1.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include "soundfile.h"

int main(int argc, char** argv) {
   Options options;
   options.process(argc, argv);

   SoundFileRead  insound(options.getArg(1));
   SoundHeader    header = insound;
   header.setChannels(1);          // write only mono file
   SoundFileWrite outsound(options.getArg(2), header);

   int sampleCount = insound.getSamples();
   double a          = 0.9;        // the feedback gain          a
   double insample   = 0.0;        // the current input sample   x[n]
   double insample2  = 0.0;        // the last input sample      x[n-1]
   double outsample  = 0.0;        // the current output sample  y[n]
   double outsample2 = 0.0;        // the last output sample     y[n-1]
   int i;

   // implementing the difference equation: y[n] = x[n] - x[n-1] + a * y[n-1]
   for (i=0; i<sampleCount; i++) {
      insound.incrementSample();
      outsample2 = outsample;
      insample2  = insample;
      insample   = insound.getCurrentSampleDouble(0);
      outsample  = insample - insample2 + a * outsample2;
      outsound.writeSampleDouble(outsample);
   }

   return 0;
}

Exercises

  1. Here is a soundfile with a DC offset: 440hzoffset.wav. Filter it with the program you just compiled. Listen to the soundfile and then to the output soundfile. Do they sound the same or different? Look at the input and output soundfiles in Peak. How do the soundfiles compare to each other visually inside of Peak? If you are using the computer lab next to the cafeteria, you can use SoundHack instead of Peak.
  2. Make the feedback filter gain, a, an input parameter from the command line (see Lab 2 for how to do this).
  3. Try different amounts of feedback gain and see how it affects a whitenoise soundfile (try whitenoise.wav or make your own from your Lab 1 program) used as input to the filter. Use values in the range from [-1.0 .. +1.0] for the feedback gain.
  4. Optional: Write a program which adds its own DC offset to a soundfile. Try adding an offset to a soundfile to be used with granular synthesis. Examine the output of granular synthesis when the input soundfile has a large DC offset. Alternatively, your offset program could be used to fix a constant DC offset in a soundfile recorded from a cheap microphone audio input by adding a negative offset to center the sound at zero.