[Back to BTc Encoder Page]

www.RomanBlack.com

BTc Sound Compression Algorithm
A system to encode sound to 1-bit format to be played on a PIC or other cheap micro.
Roman Black - orig 2001 - rewritten 14 Feb 2008.


Note about my work with BTc

As this page has caused confusion in the past I have simplified it into three points;

1. I did NOT invent playback of 1-bit sound! Even the earliest computers played sound by setting one output pin high and low, which was then filtered into hardware and played on a speaker.

2. My BTc is a sound conversion and compression algorithm. 1-bit sound is always "compressed" ie, 8 times smaller data than normal 8-bit PCM sound. It is also a much lower sound quality. It's usefulness comes from small size and very easy playback (no DAC needed). What my BTc system does is allow this encoding (coversion) to be done very fast, and with very little processor math required. This has a great benefit in that a low-cost PIC can do real-time recording and encoding, and even data transmission of the encoded digital sound.

3. I created windows software called BTc Picsound Encoder. This is what most people see as my BTc 1-bit sound. It is a tool that converts a windows WAVE file using BTc to create a 1-bit sound file that can be played on a cheap PIC micro. If you only need to "play some sounds" on your PIC or other micro then go to my BTc Picsound Encoder Software page.

If you are interested in the actual BTc algorithm then keep reading.


The problem with modelling simple RC playback hardware

A micro plays 1-bit sound from a single digital output pin like this;





The digital output of 1's (+5v) and 0's (0v) is smoothed by an RC filter to make an approximation of the original sound waveform.

As you can see from the waveform below the RC filter responds exponentially to each bit. This is a problem caused by the very simple output hardware. You can see that some 1 bits make the waveform rise more than others, depending on whether the wave voltage is already high or low...





So to encode the sound from a waveform to a string of 1's and 0's it is necessary to mathematically model the performance of an RC filter which is exponential.


The traditional RC math model

Below you can see a typical RC charging curve (shown in RED) representing a capacitor charging from 0 volts to 5 volts through a resistor.





The standard Tc (time constant) used in electronics is 63.2%, and as you can see after one Tc period (at point A) the capacitor has charged 63.2% from where it was (0v) to where it is going (5v). After another Tc time period the capacitor has charged another 63.2% towards the target 5v. For each time period the capactor charges another 63.2%, and after 5 time periods (at point E) it is sufficiently close to the target to be considered as "fully charged".

The problem with this traditional model is that it requires floating point math, to multiply each value by 0.632 (63.2%). A further problem is that we need to do more math to further divide these periods into the much shorter periods needed to somehow model each bit of the 1-bit sound at the desired playback frequency.


My BTc "Binary Time constant" math models...

It occured to me that by simply choosing a different period for the time constant that it would become a BINARY Time constant. Where the original Tc of 63.2% is relevant for electronics calculations requiring ohms and farads, my binary time constants are relevant for allowing modeling of the RC curve with the very simplest of math, in real time suitable for low-cost micros like a PIC.

Below you can see the most obvious example of a binary time constant, which I named "BTc2" because with each BTc2 period the capacitor charges 1/2 of the distance towards the target.

This simplifies the math enormously! Now the PIC only needs to know the remaining distance to the target (a simple subtract) and then divide that number by 2!





Below is another example; BTc4. The capacitor charges 1/4 of the way towards the target in each BTc4 period.





However the BTc encoding algorithm is more than just "choosing" a new time constant. It needed an optimised system for actual encoding of the sound data, and a system to optimise the BTc value for the chosen bitrate.


Implementing the BTc encoding algorithm

The first thing required is scaling the size of the original waveform to half the maximum scale of the BTc model.

See below the original waveform is shown in green and is scaled to fit totally within the centre half of the available range.





This gives 2 important benefits;
  • The BTc waveform uses the more linear mid portion of the curve.
  • It allows room for the BTc waveform to extend past the original wave limits.

    This may be seen more clearly in the waveform window of my BTc Encoder program below;

    The 2 lines marked with purple arrows are the 1.25v and 3.75v lines as in the example above. The yellow waveform is the original 8 bit waveform and fits totally within the lines at all times. The red wave is the BTc encoded waveform. You can see it is free to exceed the voltage limits that always constrain the orig waveform.





    Reactive or Predictive?

    In my 2001 BTc Encoder I tested both encoding methods;

    Reactive is where the encoder produces ONE new value for each sample. It reacts to the change in the orig waveform. If the BTc waveform is less than the new sample it produces a 1 bit, if the BTc wave is currently above the new sample, it produces a 0 bit. Reactive is the system used by most encoding processes in the real world, it is faster and simpler to encode.

    Predictive is a system that generates TWO possible outcomes for each new sample. Then the two outcomes are compared to the desired sample and the BEST outcome (ie the closest to the desired sample) is chosen.

    I settled on the Predictive system for my 2001 BTc Encoder and have used it since. The predictive system generates better sound quality; it produces the closest result to the desired waveform and responds instantly to the new sample. The reactive system lags behind the desired waveform, doing the best it can to stay close to it after it changes. I chose the predictive system because it has slightly better sound quality. If you are using a PIC or other micro to do real-time encoding you may wish to use reactive encoding to sacrifice a little quality but reduce the processing required.

    The following examples of my BTc encoding algorithm are written as simple "psuedo code" so they can be easily adapted to whatever language (or micro) you program with.

    newsample (0-255) is the next sample from the wave file we are encoding
    lastbtc (0-255) is the existing position (last known position) of the BTc waveform
    newbit (0-1) is the result of the BTc encoding!


    Reactive BTc algorithm

    Assuming 8-bit wave file, 8-bit math and BTc4 encoding;
    // Scale the next wave sample and place in centre
    newsample = (newsample / 2) + 64; 
    
    // generate the BTc bit,
    // does the BTc wave need to react UP or DOWN to follow it?
    if(newsample > lastbtc)
    {
       newbit = 1;                 // BTc must move UP
       
       dist = (256 - lastbtc);     // calc total distance to charge
       dist = (dist / 4);          // BTc4, only charge 1/4 distance
       lastbtc = (lastbtc + dist); // where it charges up to
       goto done;
    }
    else
    {
       newbit = 0;                 // BTc must move DOWN
    
       dist = lastbtc;             // calc total distance to charge
       dist = (dist / 4);          // BTc4, only charge 1/4 distance
       lastbtc = (lastbtc - dist); // where it charges down to
       goto done;
    }
    


    Predictive BTc algorithm

    Also using an 8-bit wave, 8-bit math and BTc4. Basically this is how my BTc Encoder software does it;

    // Scale the next wave sample and place in centre
    newsample = (newsample / 2) + 64; 
    
    // generate a "1" (high) outcome
    dist = (256 - lastbtc);     // calc total distance to charge
    dist = (dist / 4);          // BTc4, only charge 1/4 distance
    highbtc = (lastbtc + dist); // where it charges up to
    
    // generate a "0" (low) outcome
    dist = lastbtc;             // calc total distance to charge
    dist = (dist / 4);          // BTc4, only charge 1/4 distance
    lowbtc = (lastbtc - dist);  // where it charges down to
    
    // calc distance from high outcome (up or down) to new sample
    if(highbtc > newsample) disthigh = (highbtc - newsample);
    else                    disthigh = (newsample - highbtc);
    
    // calc distance from low outcome (up or down) to new sample
    if(lowbtc > newsample)  distlow = (lowbtc - newsample);
    else                    distlow = (newsample - lowbtc);
    
    // see which outcome is closest to new sample and generate the bit
    if(disthigh > distlow)    // low is closest
    {
       newbit=0;
       lastbtc = lowbtc;
    }
    else                      // else high is closest
    {
       newbit=1;
       lastbtc = highbtc;
    }
    goto done;
    

    There are some optimisations that can be done to shrink the above code examples but I have left them long for clarity. You can see the predictive algorithm takes roughly twice as long but will still fit in a handful of PIC assembler instructions. Unlike floating point math, the whole algorithm only requires 8-bit add and subtract and /2 /4 etc (right shift). It encodes the waveform VERY quickly!


    1-bit playback code example!

    The playback code is extremely simple. This is the main benefit of 1-bit sound playback, and also the fact that it does not need a DAC for playback, only a digital output pin.

    This next code is in "psuedo assembler" and should be adaptable to most micros.

    interrupt_routine:
    
       clrc               ; clear carry ready to use it
       rlf data           ; left shift data, puts next bit in carry
    
       btfss carry        ; test carry and set output pin to match
       bcf output_pin     ; =carry low, so set pin low
       btfsc carry        ;
       bsf output_pin     ; =carry high, so set pin high
    
       decfsc bit_count   ; count 1 more bit was played
       goto interrupt_end ; more bits remaining   
       
       ; have played all 8 bits so load another byte
    
       call table         ; returns with next byte in W reg
       movwf data         ; put byte in data reg
    
       movlw d'8'         ; also reset count, to play 8 more bits   
       movwf bit_count    ;
    
    interrupt_end:
    



    More to be added here soon!

    Note! I have removed most of the content of this page during the re-write. I need to describe again my predicitive BTc 1.5bit format. Basically it predicts 2 choices for each bit; if the new BTc bit is the same as the last BTc bit and the BTc waveform moves a full BTc distance, or if the new bit is the opposite of the last bit, and the waveform only moves 1/2 the BTc distance selected by the new BTc bit. It's fairly obvious if you look at the blue example below. I will add a better description later and some encoding and playback code when I get around to pulling code samples out of some existing projects. - Roman















    Nostalgia! - A screenshot of the first BTc Encoder I publicly released, (dos version 0.01).


    - end -

    [Back to BTc Encoder Page]