Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Roman Black's BTc Sound Encoder (mostly working)

Status
Not open for further replies.

3v0

Coop Build Coordinator
Forum Supporter
The sound is good enough that I can actualy tell it is me speaking. But I need help getting rid of the embedded noise.

The attached zip includes the original WAV and a recording of the PIC playing back the sound.

I am unsure what the noise is.

Is it normal BTC noise that the RC should be filtering out?
Could it be a flaw in the playback code I wrote?

The noise seems to cover a wide range of freqs such that I could not get rid of it using an equalizer. (testing to see what cuttoff I needed for the low pass filter)


This is what the BTC software indicates for the RC filter. I need to add an amp which will also require a low pass filter as I understand it.

; Size 62708 bits (7838 bytes)
; Sound encoded at 44100 bits/sec
; using BTc16 1.5bit Algorithm to be decoded on
; the following circuit:
; R = 1596 ohms
; each 2R = 3192 ohms
;
;
; Digital ----------2R-----,
; |
; Digital ----------2R-----*----- Analogue
; | out
; |
; |
; C = 0.22 uF
; |
; |
; |
; Gnd

I used a 220000 pF cap. 2.7K resistors each followed by a 1K pot. None of it seems to be critcial (work?). Changing the cap value does not seem to change the sound. It works about the same without one.


More details.

The PIC is a 16F1330 running on the internal OSC with the PLL with an effective clock of 32MHz. The BTC data is stored in a 24LC512.

The playback code is a modified version of Microchips C18 code used to read I2C EEPROMs. The only real change is that I wait for a bit period to elapse prior to outputing the bit and reading the next from the EEPROM.

If anyone thinks it is my code I will post it.

Any ideas where to go from here ?
 

Attachments

  • testing123.zip
    693.7 KB · Views: 1,170
The original sound is overloading the preamp or recorder and is with too much low frequencies and not enough high frequencies.

The sound from the PIC is the opposite. The low frequencies are weak and the high frequencies (and high frequency noise) are too strong.
 
I tried a few times before to get this BTc of Roman's to work and could not get the quality I hoped for. The .wav files he had on his origional page sounded very good, nothing at all like I managed to get. If you get this going please post your findings. There is similar stuff here :- h??p://mondo-technology.com/ under Talking Frog Box.
 
Gordz said:
I tried a few times before to get this BTc of Roman's to work and could not get the quality I hoped for. The .wav files he had on his origional page sounded very good, nothing at all like I managed to get.
Your lowpass filter must not be loaded down with a 1k pot. It must feed an impedance of at least 16k.
 
Last edited:
It looks like the problem is in the recording and encoding process. My original recording sound about as bad on the BTC built in preview as they do on the PIC.

Roman has several suggestion regarding on how to get the best sound in the apps help file. One is that a cheap mic will not work. Perhaps the fact that mine came out of a Radio Shack suprise box is the problem.

Pre recorded clips that I have found seem to work a bit better. Not too much better.

I am sure there is some magic incontation to make this work but I have yet to find it...
 
Why is your original sound just very distorted boomy bass, and why is the output from the PIC just bass-less sizzling highs?

Then maybe the input to the PIC is overloaded?
 
audioguru said:
Why is your original sound just very distorted boomy bass,


Booth says in his instructions
Sound wave volume

It is critically important that the sound wave be the largest volume possible. Ideally most of the sound wave should be "peak to peak" even with some mild clipping of peaks. Many sound files (like those supplied with Windows) are very low in volume and will not encode very well.

You can download freeware wave editing software from the internet, and most of these programs will let you change formats and increase the volume of the sound. Sometimes they call this "normalising" or "maximising".

Dynamic compression

Dynamic compression refers to the dynamic range of the sound, ie, the difference in volume between the loud bits and the quiet bits. The BEST encoding result will be on sounds that have a "compressed" dynamic range, where the entire sound is loud. Think "radio advertising" for a typical example of sound of this type.

If your wave editor does not support dynamic compression you can get a result almost as good by increasing the volume until most of the peaks start to clip. Save a number of waves with different volume increases, and see which sound the best after encoding.

The sample I posted was a bit too much. I tried several others that ranged from weak to loud. I even used a tool to change the pitch of my voice.

and why is the output from the PIC just bass-less sizzling highs?
Perhaps because my LRC playback circuit is bad.

The PIC plays back through a old headphone speaker. I tried the speaker from an old PC first but the recorded part of the sound was not good. I do not think they were ever meant to do much more then beep.

The original speaker was 8 ohm as suggested by Black. The headphone speaker showed 30 ohms on the multimeter. Since the inductance of the speaker coil is part of the playback circuit that may have made the noise worse, even though the reproduced part was much better.

I feed the recorded pic sound back into audacity and tried to filter out the noise. That may have not been a good idea. The mechanical speaker may have introduced harmonics of the original noise making it present over many frequencies.


I tried recording a pure tone generated by audacity. To a lesser extent it showed the same noise problem as my other recordings. That makes me think that it is time to revisit the analog playback circuit.

I think the first step is to ditch the little speakers and go with an amplified design.

Black said:
If you have an avaliable opamp in your design, you can configure it as a active low pass filter by using a cap to couple high frequencies from the output back to the inverting input. Do a web search for "opamp low pass filter".

What do you think of using a TL072. Use the first opamp as a low pass filter and the second opamp to bring it up to line level.

If I put a pot in the RC circuit and another on the low pass opamp I may be able to tune this thing to where it works.

Then maybe the input to the PIC is overloaded?
Not sure how to respond to this. The input to the PIC is a bitstream stored in the EEPROM. Since it is a digital device I am not sure what overloading it would mean. Maybe more like the bitstream output by the PIC overloads the RC circuit?

EDIT: The tone I recorded was middle C (440Hz).
 
Last edited:
Hi 3v0. I've been trying Black's software recently and I noticed your post yesterday. I was planning to use the BTc32 algorithm (your wave file sounds little better, I listened to the preview with the software, note that you'd need different values of R).
I planned to use an op amp as a pre-amplifier in order to have a high impedance stage following the RC filter. I've considered additional post-filtering too.
 
Roman Black's demo doesn't sound like it was recorded at a level much too loud and doesn't sound bass-less with sizzling highs. It sounds like an AM radio.

Roman Black says his system has an inherent high noise level and sounds best with the treble turned all the way down and the bass turned all the way up.

Instead of turning down the treble which is just a first-order lowpass filter that reduces many of the frequencies you want and doesn't cut high frequencies very well, use a 4th order or 8th order Butterworth lowpass filter IC from Maxim-IC that uses switched-capacitor technology. Its cutoff is very sharp and changed by changing its clock frequency with a pot.

What are you doing with the little pc speaker? It isn't made for good sound, just beeps. Use a proper amplifier and larger speaker in a correctly designed enclosure.
 
audioguru:
I tried the little speaker first because it was a 8 ohm speaker, which is what Black suggests. Needed to verify that I could read/play the digital stream correctly. It was always my intent to use a filter and an amp. One thing at a time.

If you encode a good/normal sounding audio clip with the BTC encoder it will be very weak when played back on the PIC. The wave file needs to be recorded at a high level.

I will order the MAX7480 filter chip from Maxium. While I am there I will check for small audio amps. The project will go on hold till they arrive.

eng1:
I used the BTC16 encoding. I would be quite happy with the sound if I could get rid of the noise. The "Testing one two three" clip used about 0x1F00 on the EEPROM. Could be done in a bit less.
 
Last edited:
3v0 said:
I used the BTC16 encoding. I would be quite happy with the sound if I could get rid of the noise. The "Testing one two three" clip used about 0x1F00 on the EEPROM. Could be done in a bit less.
I've found that BTc32 compression generates less noisy waveforms. However, I think that additional (hardware) filtering is necessary.
Using BTc16 or BTc32 algorithms, you get equal size files. Instead, I will use 22050 Hz sampling rate for my sounds, 44.1 kHz seem to be quite high for voice. Quality will be the same, I'm trying to save space on EEPROM to store a couple of sounds.

I have a MAX292 handy, it's an 8th order low pass filter. I'm not sure you can use a high order filter directly on the output of the PIC. I thought that you must use a 1st order filter, isn't the Black's algorithm modelled specifically for such a filter? so I thought to add an op amp after the passive RC network and then a second stage (it could be the MAX292). Your opinions?
 
Last edited:
As I understand it, and I could be wrong. The BTC encoding requires the first order filter to convert the bits back into audio. I have looked at the signal prior to and after the filter and that would seem to be the case.

Additional filtering is for noise reduction and maybe adjusting the frequency response.

Black shows two additional filter solutions in the help file under Tips for Best Playback in Hardware. For 1.5 bits you would need two. In the text he talks about saving pennies. For the hobby type an extra buck or two to make it sound better is reasonable.

Let me know how the MAX292 works out. In the mean time I have found a opamp based 2ND-ORDER BUTTERWORTH know as a filter that I may try.

I will try BTC32 and 22050Hz to see what sort of results I get. At this point I am I not to concerned with EEPROM space. Once it work "good enough" I can try degrading it to a more reasonable storage requirement.
 
The MAX292 is Bessel, not Butterworth but since it is an 8th order then it is still sharp enough. A 2nd order Butterworth filter is just a little better than an RC single order filter. A 2nd order Butterworth Sallen and Key filter needs a dual pot to tune its frequency.

Tune the cutoff of the lowpass filter to 3kHz to 10kHz to reduce hissssss.
 
audioguru said:
The MAX292 is Bessel, not Butterworth but since it is an 8th order then it is still sharp enough.
Yes.... the MAX291 is Butterworth, Here's the datasheet. I'd like to try diffrent solutions, but it probably won't be until after Christmas Holidays as I'll be away next week.
 
Gordz said:
I tried a few times before to get this BTc of Roman's to work and could not get the quality I hoped for. The .wav files he had on his origional page sounded very good, nothing at all like I managed to get. If you get this going please post your findings. There is similar stuff here :- h??p://mondo-technology.com/ under Talking Frog Box.

I did some reading on Luhan's site. For anyone who has not visited Mondo Tech, it has several neat project including the superprobe.

Page talking EEPROMS. uses a Microchip MCP41010 SPI controlled digital pot as a DAC. The procedure for building the EEPROMs is informative.

Procedure
Collect the speech sounds in a set of WAV or other files you can edit.
Maximize the level in the file.
Filter the file with a 3500hz low pass function.
Resample the rate to 8khz.
Cut out individual words.
Store them to disk in the SND format, 8 bits, unsigned, 8000 samples/second.
Construct a Sound Control File (filename.scf)
Use FTALK to convert the several SND files into one BIN file.
Use HEX (if necessary) to convert the BIN file to a HEX file.

Sampling at 8K with 8 bit words results in 8k bytes per second. Then a 64K byte 24LC512 would hold 8 seconds of speach. The MCP41010 is $1.35 at newark. For less then $16 one can get 4 24FC1025 EEPROMS. That should store about 64 seconds of recorded speach. Thinking $20 to $25 of parts on the board.

I will continue with Roman Black's BTC. I am waiting on the filter chip suggested by audiogru.
 
Few lines of pseudo-code will be fine, I'm going to write my own code later (C or assembly, I haven't decided yet).

I'm going to use a TIMER to send bits out (with a sampling rate of 22050 Hz, each bit will be sent on PORTx every 45.4 us). Bits are stored into 8 bit values, the most significant bit goes out first, is that correct? If that is the case I'll use a 'rlf' instruction to 'serialize' data.
The 1.5bit algorithm requires two pins of the PIC, what do they have to do exactly? (sorry if this is explained somewhere but I haven't found it).
 
Last edited:
eng1 said:
Few lines of pseudo-code will be fine, I'm going to write my own code later (C or assembly, I haven't decided yet).

I'm going to use a TIMER to send bits out (with a sampling rate of 22050 Hz, each bit will be sent on PORTx every 45.4 us). Bits are stored into 8 bit values, the most significant bit goes out first, is that correct? If that is the case I'll use a 'rlf' instruction to 'serialize' data.

You could. But since you are reading a bit at a time and have control of the I2C clock you can alternate between reading EEPROM bits and sending them to the speaker.

Here are the bits needed to do bit bang EEPROM reads and send them to the speaker. Based on Microchips code library for SW_I2C

In short you setup a sequential I2C read. As you read a bit send it out to speaker. Use TIMER0 to set speaker bit times.

Code:
// Delays are clock dependant so we will define them here
#define LONG_DELAY     Delay1TCY();Delay1TCY();Delay1TCY()
// Delay10TCYx(1)
#define SHORT_DELAY   Delay1TCY();Delay1TCY()  
#define return(c)     if(c==0)return(c);while(1)

.............................................................

// modifed for 16 bit address
void sequential_read( int addr )
{
    SWStartI2C();
    var = SWPutcI2C( 0xA0 ); // control byte
    SWAckI2C();
    var = SWPutcI2C( 0x00 ); // address to read from hi byte
    SWAckI2C();
    var = SWPutcI2C( addr ); // address to read from lo byte
    SWAckI2C();
    SWRestartI2C();
    var = SWPutcI2C( 0xA1 );
    SWAckI2C();
    SWGetsI2C_speaker( 0x1F00); // fixed lenght for now
    SWStopI2C();
}

....................................................

/************************************************************ 
*     Function Name:    void SWGetsI2C_speaker(unsigned int length )   
*                                                                            
*     Return Value:     return with error condition                         
*     Parameters:       address of read string storage location and 
*                        length of string bytes to read              
*     Description:      This routine reads a string from the I2C bus.                                        
********************************************************************/
void SWGetsI2C_speaker(unsigned int length )
{
  T0CONbits.TMR0ON = 0; // timer 0 off
  INTCONbits.TMR0IF = 0;	
  while ( length --)              // stay in loop until byte count is zero
  {
    SWReadI2C_Speaker();      // read and send a byte to the speaker
    
    
    if ( !length )                 // initiate NOT ACK
    {
      CLOCK_LOW;               // make clock pin output to drive low
      DATA_HI;                  // release data line to float high 
      LONG_DELAY;              // user may need to modify based on Fosc
      CLOCK_HI;                  // release clock line to float high 
      LONG_DELAY;               // user may need to modify based on Fosc
    }
    else                            // else initiate ACK condition
    {
      CLOCK_LOW;                  // make clock pin output to drive low
      DATA_LAT = 0;               // set data pin latch to 0
      DATA_LOW;                   // make data pin output to drive low
      LONG_DELAY;               // user may need to modify based on Fosc
      CLOCK_HI;                   // release clock line to float high 
      LONG_DELAY;               // user may need to modify based on Fosc
    }
  }   
  T0CONbits.TMR0ON = 0; // timer 0 off
  return( 0 );                    // return with no error
}


...........................................................................................

// #define SEE_BITRATE   // if defined generate a signal that is 1/2 the bitrate
                         // EEPROM data is ignored
                         // you can check the birate with a freq counter
                         
// #define SEE_BYTE      // collect bits into bytes so you to check EEPROM read


#define TEST_TIME        // comment out for production code



#include <p18cxxx.h>
#include <delays.h>
#include "sw_i2c.h"

/********************************************************************
*     Function Name:    unsigned int SWReadI2C_Speaker(void)        *
*     Return Value:     none                                        *
*     Parameters:       void                                        *
*     Description:      Read 8 bits from I2C EEPROM send to speaker *
*                       using bit bang                              *
********************************************************************/
//#pragma inline
void SWReadI2C_Speaker( void )
{
  unsigned char bitCnt, prevBit, curBit;
  // the next two are for debugging only
  unsigned char flip;     // used to generate a signal to check freq
  unsigned char readByte; // value of last read byte
  
  SCLK_LAT = 0;           // set clock pin latch to 0
  T0CONbits.TMR0ON = 1;   // timer 0 on
  
  flip = 0;
  readByte = 0;
  bitCnt = 8;             // set bit count for byte 
  
  do
  { 
    #ifdef SEE_BYTE
    readByte = readByte <<1;
    #endif
    
    // clock in the bit
    CLOCK_LOW;            // set clock pin output to drive low
    DATA_HI;              // release data line to float high
    LONG_DELAY;           // user may need to modify based on Fosc
    CLOCK_HI;             // release clock line to float high
    SHORT_DELAY;          // user may need to modify based on Fosc

    while( !SCLK_PIN )    // test for clock low
    {
      if ( Clock_test() ) // clock wait routine
      {
        #ifdef TEST_TIME
        while(1);         // hang here, ***set breakpoint here***
        #else
        return;            /// need error recovery here, maybe skip to next byte??
        #endif
      }
    }

    // get the bit 
    curBit = ( DATA_PIN );          
        
    #ifdef TEST_TIME    
    if( INTCONbits.TMR0IF)
        while(1);                   // for testing hang here if we late, ***set breakpoint here***
    #endif
    
    // wait for timer to expire, for speakers bit time to start 
    while(!INTCONbits.TMR0IF);	    
    
    // new speaker bit time starts here
    INTCONbits.TMR0IF = 0;  // clear timeout flag   
    TMR0L = 0x45;               // setup timer for bit time, this is for 32Mhz 18F1330  
    
    #ifdef SEE_BITRATE
    SPEAKER0 = flip;                //                        _   _   _
    flip = !flip;                   // change output state  _| |_| |_| |_ ... test freq only
    #else  
    SPEAKER0 = curBit; 
    SPEAKER1 = prevBit;
    TRIS_SPEAKER1 = (curBit != prevBit);  // make 2nd bit (prev) output only if not same as current
    #endif
    
    #ifdef SEE_BYTE
    readByte += DATA_PIN;        
    #endif

    prevBit = curBit;    
  } while ( --bitCnt );      // stay until 8 bits have been acquired

}
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top