Audio Library

Status
Not open for further replies.
Just joined the forum. Got my Teensy and audio board the other day and I'm really impressed. Paul and the rest of you have done a great job on the audio library.

My interest is in making a Software Defined Radio. I hacked away at it yesterday and a crude version of it is working.

From what I can see almost everything I need is in the library with one exception - an audio multiplier ("mixer" if you are speaking RF). I whipped one up and its working but not optimized for the M4 DSP instructions. I'm not familiar with the procedure for submitting code via github. Should I post it here ? I think its a useful addition for doing frequency shifting, ring modulator effects etc.

Demo of the radio: https://www.youtube.com/watch?v=9pZnqMBpSdc

Rich
 
Hi Rich,
That's really interesting. I'd be interested in seeing your code. Can you attach a zip of it here?

Pete
 
I believe I found the bug. It's memory leaks.

I just pushed a fix to github. Please give it a try and let me know how this works for you?

https://github.com/PaulStoffregen/Audio


I know this is old (was on page 7 or this thread), but I'm actually seeing the same problems with the latest & greatest. Seems to lock up the board after playing a sound sample several times. Not sure when it's going to fail, but it does always fail. Here's my code:

Code:
// +++++++++++++++++++++ libraries +++++++++++++++++++++ //
#include <Audio.h>
#include <Wire.h>
#include <SD.h>
#include <SPI.h>

// +++++++++++++++++++++ audio components +++++++++++++++++++++ //
// Create the Audio components.  These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioPlaySdWav     im_vo;
AudioPlaySdWav     im_servo;
AudioMixer4        mainMix; 

AudioOutputI2S     headphones;

// Create Audio connections between the components
//
AudioConnection c1(im_vo, 0, mainMix, 0);
AudioConnection c2(im_servo, 0, mainMix, 1);
AudioConnection c3(mainMix, 0, headphones, 0);
AudioConnection c4(mainMix, 1, headphones, 0);
AudioConnection c5(mainMix, 0, headphones, 1);
AudioConnection c6(mainMix, 1, headphones, 1);

// Create an object to control the audio shield.
// 
AudioControlSGTL5000 audioShield;

// +++++++++++++++++++++ variables +++++++++++++++++++++ //
char* wavToPlay;
char* servoToPlay;
char* voxToPlay;
boolean VOState;
boolean servoState;
int whatToPlay[2];
int serialIn;
byte whatToPlayIndx = 0;



void setup() {
  
  // +++++++++++++++++++++ init serial ports +++++++++++++++++++++ //
  Serial.begin(921600); // USB
  Serial1.begin(115200); // incoming from gatekeeper micro
  
  // +++++++++++++++++++++ audio setup +++++++++++++++++++++ //
  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(5);
  
  float mixVol = 0.50;
  mainMix.gain(0, mixVol); // vox
  mainMix.gain(1, mixVol); // servo

  audioShield.enable();
  audioShield.volume(100); // <<<<< set volume here

  // +++++++++++++++++++++ SD card inits +++++++++++++++++++++ //
  SPI.setMOSI(7);
  SPI.setSCK(14);
  wavToPlay = "00000vIM.WAV"; // start up sound
  if (SD.begin(10)) {
    delay(1000); // so there's no pop at the beginning (not sure this works)
    im_vo.play(wavToPlay); // init sound
  }
  
  
}

void loop() {
  
  // +++++++++++++++++++++ service USB for messages +++++++++++++++++++++ //
  while (Serial.available() > 0) { 
    // mail call
    serialIn = Serial.parseInt(); 
    whatToPlay[whatToPlayIndx] = serialIn;
    Serial.print(whatToPlay[whatToPlayIndx]);
    Serial.write(10);
    whatToPlayIndx++;
    if (Serial.read() == '\n') { // end of message
       servoState = im_servo.isPlaying();
       if (servoState == 0) {
         playServoSound();
       }
       VOState = im_vo.isPlaying();
        if (VOState == 0) {
          playVOXSound();
       }
       whatToPlayIndx = 0;
    }
 
  } // end USB check
  

} // end main loop

void playServoSound()
{
  if (whatToPlay[0] == 0) return;
  Serial.print("play Servo");
  Serial.write(10);
  if (whatToPlay[0] == 21) servoToPlay = "00173vIM.WAV";
  Serial.print(servoToPlay);
  Serial.write(10);
  im_servo.play(servoToPlay);
  Serial.print("playing servo");
  Serial.write(10);
  
  
} // end playServoSound

void playVOXSound()
{
  if (whatToPlay[1] == 0) return;
  Serial.print("play VOX");
  Serial.write(10);
  if (whatToPlay[1] == 21) voxToPlay = "01234vIM.WAV";
  Serial.print(voxToPlay);
  Serial.write(10);
  im_vo.play(voxToPlay);
  Serial.print("playing VOX");
  Serial.write(10);
} // end playVOXsound

Happy to share some sample sound files, but curious if I was simply doing something wrong above, or are there still memory issues here.

Thanks,
David
 
Hi Rich,
That's really interesting. I'd be interested in seeing your code. Can you attach a zip of it here?

Pete

Attached. Still pretty hacky. I included multiplier.cpp, multiplier.h and audio.h for the audio library. Note that when I scaled the multiplier output there is an extra factor of 2. It will clip like this with full range inputs but I found the output level to be more useful for most of the operations I tried. I added the codec AVC to the code but didn't realize it has to be in the audio chain as well - another thing to fix.

A bit of a description of how the radio works and some of the things I intend to try: http://rheslip.blogspot.ca/2014/04/software-defined-radio-with-teensy-31.html

Rich
 

Attachments

  • SDR_test.zip
    6.1 KB · Views: 163
I know this is old (was on page 7 or this thread), but I'm actually seeing the same problems with the latest & greatest. Seems to lock up the board after playing a sound sample several times. Not sure when it's going to fail, but it does always fail.

Have you tried printing the memory consumption at regular intervals, or at least after each time playing?

If it really is a memory leak, you'll see the memory consumption grow until all 5 buffers are permanently used up.
 
Have you tried printing the memory consumption at regular intervals, or at least after each time playing?

If it really is a memory leak, you'll see the memory consumption grow until all 5 buffers are permanently used up.

Hi Paul,
Never gets above 2. Shall I email the sound files? They are short little snippets. Craps out after about 5 playbacks (but not consistent). Sometimes one mixer input will freeze followed by the other one a few samples later. Once both freeze the t3.1 locks up. Occasionally it will lock up on a sample at an obnoxiously loud volume (glad I'm not wearing my headphones for this test).

Thanks,
David
 
@Rich:
I think your scaling needs to shift down by 15. When you multiply two signed 16-bit numbers (i.e. 1.15 format) the result is a 2.30 number - i.e. the top two bits of the 32-bit number will be the same and will both be the sign bit. If you shift this down by only 13 bits, you haven't shifted either of the top two sign bits down to the sign bit in the lower 16-bits. At least, that's what I think happens. I will have to play with it sometime.

Pete
 
@Rich:
I think your scaling needs to shift down by 15. When you multiply two signed 16-bit numbers (i.e. 1.15 format) the result is a 2.30 number - i.e. the top two bits of the 32-bit number will be the same and will both be the sign bit. If you shift this down by only 13 bits, you haven't shifted either of the top two sign bits down to the sign bit in the lower 16-bits. At least, that's what I think happens. I will have to play with it sometime.

Pete

Yeah - so actually it should be >>14. I used a 13 bit shift to scale the output 2X and I didn't see any problems with it. It will clip if you multiply a square wave by a sine but sin**2 for example yields a fairly small output as does sin * audio which is what I'm doing. You can scale the inputs appropriately to avoid clipping or I suppose the output scale factor could be made settable.

What I have not investigated is clipping due to DC effects. e.g. as I recall sin**2 yields a doubled frequency plus an offset. Those crazy trig identities we had to memorize in school come up often in the RF world.

Rich
 
I think the problem with scaling is because there is a bug in my AudioSynthWaveform code when it produces sin waves. I have a pull request pending to fix that but it isn't in yet.
You can patch your local copy by editing libraries/Audiomaster/synth_waveform.cpp. At about line 96 there is a case statement for TONE_TYPE_SINE. Replace that entire case statement with this code:
Code:
    case TONE_TYPE_SINE:
      for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
        // The value of ramp_up is always initialized to RAMP_LENGTH and then is
        // decremented each time through here until it reaches zero.
        // The value of ramp_up is used to generate a Q15 fraction which varies
        // from [0 - 1), and multiplies this by the current sample
        if(ramp_up) {
          // ramp up to the new magnitude
          // ramp_mag is the Q15 representation of the fraction
          // Since ramp_up can't be zero, this cannot generate +1
          ramp_mag = ((ramp_length-ramp_up)<<15)/ramp_length;
          ramp_up--;
          // adjust tone_phase to Q15 format and then adjust the result
          // of the multiplication
      	// calculate the sample
          tmp_amp = (short)((arm_sin_q15(tone_phase>>16) * tone_amp) >> 15);
          *bp++ = (tmp_amp * ramp_mag)>>15;
        } 
        else if(ramp_down) {
          // ramp down to zero from the last magnitude
          // The value of ramp_down is always initialized to RAMP_LENGTH and then is
          // decremented each time through here until it reaches zero.
          // The value of ramp_down is used to generate a Q15 fraction which varies
          // from [0 - 1), and multiplies this by the current sample
          // avoid RAMP_LENGTH/RAMP_LENGTH because Q15 format
          // cannot represent +1
          ramp_mag = ((ramp_down - 1)<<15)/ramp_length;
          ramp_down--;
          // adjust tone_phase to Q15 format and then adjust the result
          // of the multiplication
          tmp_amp = (short)((arm_sin_q15(tone_phase>>16) * last_tone_amp) >> 15);
          *bp++ = (tmp_amp * ramp_mag)>>15;
        } else {
          // adjust tone_phase to Q15 format and then adjust the result
          // of the multiplication
          tmp_amp = (short)((arm_sin_q15(tone_phase>>16) * tone_amp) >> 15);
          *bp++ = tmp_amp;
        } 
        
        // phase and incr are both unsigned 32-bit fractions
        tone_phase += tone_incr;
        // If tone_phase has overflowed, truncate the top bit 
        if(tone_phase & 0x80000000)tone_phase &= 0x7fffffff;
      }
      break;

I think you will then find that you should shift 15 places in your multiplier.

Pete
 
Pete

That definitely helped. When I scale using >> 15 output level is much better. If I multiply two sines I have to scale them to about 0.8 or the result clips which seems a bit odd. Above about 0.7 the sine output of AudioSynthWaveform clips regardless of the volume setting. I should be able to set the level of a sinewave to 1.0 without clipping - no ?


Rich
 
@Rich:
I (heavily) modified your sketch so that it produces the two sine waves, multiplies them and then outputs the resulting samples to a WAV file on the uSD card - I haven't yet released the code which records the audio to a WAV file.
The routing and tone generation look like this:
Code:
AudioConnection c3(toneHigh, 0, mult, 0);
AudioConnection c3a(toneLow, 0, mult, 1);
AudioConnection c13(mult, 0, recorder, 0);
.
.
.
  toneLow.begin(1.0,1000,TONE_TYPE_SINE);
  toneHigh.begin(0.99,vfo,TONE_TYPE_SINE);
where vfo = 5000;
I played the resulting file with GoldWave and the spectrum shows two spikes, one at 4kHz and one at 6kHz. I think that is the correct result of that multiplication, right? The oddity is that there is a DC offset that I can't explain. I've attached a screenshot of GoldWave playing that file. You can see that the audio does not clip.
This was with my fixed version of AudioSynthWaveform and I also changed your multiplier to scale down by 15.

How are you detecting clipping? I was testing my original changes to AudioSynthWaveform by playing the output into GoldWave on the PC and although I was sure the scaling after a multiplication should be 15, GoldWave showed massive clipping, so I shifted by 17 to stop the clipping. The problem, of course, is that the audio was being amplified before GoldWave saw it. That's why I wrote the code which records audio output to a uSD card. I could see for certain what the Audio library was producing.

Pete

SDR_mix_screencap.jpg
 
Pete.

The spectrum is correct - double sideband suppressed carrier.

I saw clipping with my scope at the line out pins even with the volume set to 20. There are multiple places in the codec when clipping could occur tho so its probably due to some internal gain. Your test is right at the limit numerically so it wouldn't take much.

The DC offset is curious. Is it possible there is a bit of numerical bias in AudioSynthWaveform ?

A .wav recorder will be a nice addition!
Rich
 
I also recorded the two tones separately and they are both full scale waves so whatever is going on is after AudioSynthWaveform.

Is it OK if I send you a private message?

Pete
 
I think the DC offset has something to do with the relationship between the two frequencies, 1000 and 5000, and the sampling frequency of 44100.
I changed the code to use (arbitrarily chosen) 1367 and 5111 and output the result of multiplying them to the uSD. The result is a full-scale waveform.

Pete
 
I have soldered a W25Q128FV flash chip to my audio board and written/modified Audio library code to access the chip and play a WAV file plus some sketches to store wav files in the chip and then play one.
After I had got it working I ran into a weird problem. I can run any of the flash sketches repeatedly with no problem. But if I run the PlayFromSdCard sketch, none of the sketches using flash will work after that.

I've attached a zip of the audio library changes and the example sketches so if someone has one of these flash chips, could you give this a try?

Back up your existing Audiomaster directory - just to be safe.

Here's what is in the zip file.

Audiomaster
- Audio.h has the extra references for the two .h files below.
- flash_spi.cpp and .h
This is the driver for the flash chip.

- play_flash_wav.cpp and .h
This is a modified version of play_sd_wav and uses flash_spi to read a WAV
file and play it. I may eventually combine this and play_sd_wav into one
module which lets you choose the source of the wav file.

Audiomaster/examples
- flash_wav_write_file_list_a
This is used to copy a list of WAV files from the uSD card into flash.
There is a list of filenames in the file_list array. Change this to a list
of names of your WAV files. Then upload the code and start the serial monitor.
It will first make sure that there is a uSD card and then it will do a
BULK ERASE of the chip - so be SURE you want to do this before you start
the serial monitor!
It will then write the files to the flash chip and list their starting
page number and their length in pages. When it is done it will also print
the next free page number.

- flash_wav_dump_flash_header_a
This just dumps the content of the flash. At the moment the filename is not
stored but I may add that later.
This sketch is used just to be sure there's still WAV files there.

- flash_wav_play_file_a
This plays one of the files in the flash. It is specified by its starting page
number and by default the code will play the first wav file (at page zero).


Once you have successfully loaded some wav files into flash and played the first one.
You should see this on the serial monitor:
Status = 0
ID = EF, 40, 18, 0,

Playing
Done
It prints the status register and 4 bytes of the chip ID and then starts playing
the file.

Now run the PlayWavFromSdCard example and play a WAV from the uSD card.
Then use flash_wav_play_file_a again. This time I get:
Status = FF
ID = FF, FF, FF, FF,
ERROR: incorrect ID
Trying to fix it
SPI.end()
Set chip select pin 6
flash_init()
Flash hard reset
Status = 0
ID = EF, 40, 18, 0,

Playing
Done

This time it sees 0xFF for the status and ID bytes. The sketch detects this
and runs some code which tries to reset the chip. It apparently succeeds
because it prints "Status = 0" and the correct ID. But, although it then says
"Playing", there is only silence. It does seem to be shovelling samples at
the audio board because it doesn't say "Done" until the original playing
time is over.

The only way to fix this is to power off the Teensy and then power it up again.
Grounding the Reset pin does not help.
My guess is that it is something to do with SPI although that doesn't explain how it can partly recover and apparently be reading samples from the flash but not making a sound.

Pete
 

Attachments

  • AudioMaster.zip
    20.4 KB · Views: 153
>> But if I run the PlayFromSdCard sketch, none of the sketches using flash will work after that.

Perhaps the flash chip requires a different SPI mode ? Wrong mode can actually work but be right on the edge timing wise.

Rich
 
The datasheet says that mode 0 or mode 3 will work with the flash. My driver for the flash sets the SPI mode and I've tried both and the PlayFromSdWav sketch clobbers them both.

Pete
 
The datasheet says that mode 0 or mode 3 will work with the flash. My driver for the flash sets the SPI mode and I've tried both and the PlayFromSdWav sketch clobbers them both.

Pete

Presumably you've modified the library and the old sketches to assert/deassert all the SPI chip selects correctly before anything gets initialized. Fiddling with SPI modes has to be done very carefully when there are multiple devices.
I used soft SPI with the TFT display I was using because the graphics library hammered the SPI setup which caused the codec not to work. Unfortunately you can't easily try that.

Rich
 
I've just tried setting pin 10 as a high output before opening the SPI for the flash, just in case it was causing trouble, but no go. Looks like I may have to dig into the datasheet for the Cortex M4 to see if there's any other fancy dancing that needs to be done to initialize SPI. If that doesn't pan out I'll have to dig into the audio chip and the flash chip (again).

Pete
 
It looks like setting pin 10 has partially fixed it after all. The first I run the flash after I run the uSD sketch, it plays silence. But if I play the flash again it works. Before I put the pin 10 fix in, trying several times in row made no difference.
Now to see if I can get it to work without having to do a dummy run first.

Pete
 
Hi

I'm using a Teensy 3.1 with an Audio Adaptor... it's great by the way ;-)

A couple of things... they may have been mentioned previously... but this thread is very long and difficult to search... if you could reference me back to specific page that would be really useful.

I want to play back from and record files to an SD card...

By installing the latest cores and audio library at Github I'm able to play back a mono 44.1K wav file on the card...

I can also hear my mic input using the PassThrough example sketch... but there's no example I can see for recording the input and saving it to the card. Any advice appreciated.

Also I'm getting a consistent error with examples that use the AudioSynthWaveform - I get an "'AudioWaveformSine' was not declared in this scope" message in the console. Any ideas why?

Best

Prodical
 
Status
Not open for further replies.
Back
Top