Audio Library

Status
Not open for further replies.
I've started looking at the existing stuff, in Paul's audio library, for SGTL5000. This post is primarily to subscribe to this thread but I may as well announce my intentions at least a bit.

I'm interested in fleshing out the AudioControlSGTL5000 object as much as I can, based on having learned a fair deal about the SGTL5000 for my employer's purposes, I think I can offer it a fair bit.

My focus will be on things the codec can do without using I2S, I will post a list of what I propose to add or change in the next couple/few days and see where who points what from there I guess.



A function to change the mic gain would be good, I have been editing the header file which is not convenient.
 
A function to change the mic gain would be good, I have been editing the header file which is not convenient.
If you are in that much a hurry for it you might like my attachment, provided of course that it compiles - I didn't try it yet, please give me feedback (as verbose as you like) if you try it.

Please see 'CHIP_MIC_CTRL' on page 40 of http://www.pjrc.com/teensy/SGTL5000.pdf and note that I have added ::micGain(unsigned int n) to a copy of the library which only manipulates bits 1:0 of that register so actual gain applied should be 10 + (n * 10) dB unless n = 0 in which case it will be 0dB

I am not sure how long it will take me to make the list I was envisaging when I made yesterday's post, I might piece-meal it after all - perhaps my next post to this thread will be to try to start a discussion on how best to add the IIR filter & PEQ stuff to the library.


@Paul: Can I turn the read, write & modify functions public for the AudioControlSGTL5000 without annoying anyone do you think? I worry that in so doing it could cause confusion for some because they are not like .read & .write for other objects like Serial. etc etc but I think they might be terribly useful to even only slightly more advanced users, maybe they could suffer name changes like regWrite, regRead & regModify without being too annoying?

edit: I removed the attachment because it contained an error, I will reattach to a post a couple of posts down without the error, sorry.
 
Last edited:
If you are in that much a hurry for it you might like my attachment, provided of course that it compiles - I didn't try it yet, please give me feedback (as verbose as you like) if you try it.

Please see 'CHIP_MIC_CTRL' on page 40 of http://www.pjrc.com/teensy/SGTL5000.pdf and note that I have added ::micGain(unsigned int n) to a copy of the library which only manipulates bits 1:0 of that register so actual gain applied should be 10 + (n * 10) dB unless n = 0 in which case it will be 0dB

I am not sure how long it will take me to make the list I was envisaging when I made yesterday's post, I might piece-meal it after all - perhaps my next post to this thread will be to try to start a discussion on how best to add the IIR filter & PEQ stuff to the library.


@Paul: Can I turn the read, write & modify functions public for the AudioControlSGTL5000 without annoying anyone do you think? I worry that in so doing it could cause confusion for some because they are not like .read & .write for other objects like Serial. etc etc but I think they might be terribly useful to even only slightly more advanced users, maybe they could suffer name changes like regWrite, regRead & regModify without being too annoying?


Several thing going on, I get the following error:

This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.
Arduino: 1.0.5 (Windows 7), Board: "Teensy 3.1"
In file included from Toneremote.ino:1:0:
C:\Users\Turner\Documents\Arduino\libraries\Audio/Audio.h: In member function 'unsigned int AudioControlSGTL5000::micGain(unsigned int)':
C:\Users\Turner\Documents\Arduino\libraries\Audio/Audio.h:509:55: error: 'CHIP_MIC_CTRL' was not declared in this scope

When I replace CHIP_MIC_CTRL WITH 0X002a the sketch compiles but no change in mic gain.
 
...
C:\Users\Turner\Documents\Arduino\libraries\Audio/Audio.h:509:55: error: 'CHIP_MIC_CTRL' was not declared in this scope

When I replace CHIP_MIC_CTRL WITH 0X002a the sketch compiles but no change in mic gain.

Sorry about that, my mistake thinking the definitions in the .cpp file were available by the time the compiler sees that in the .h file, your fix should have made it OK but the pedant in me wants me to ask you if you can make it '0x002A' instead of '0X...' :)

When you call the function does the return value look reasonable to you? By this I mean that the return value I expect is 0x017n, so 0x0170 for n=0 and 0x0171 for n=1 etc etc. Maybe my modify routine isn't as good as it looks to me ?

I should probably wait till I have the Teensy 3.1 and Audio Adaptor, rather than posting rubbish I haven't verified myself yet :eek:
 
Ok, so, weird that it does not seem to change gain setting for you - I wonder just how arduous a task it will be for me to try to test that part of the library using the awful freescale tower nonsense I have with my favored Teensy++ 2.0

Just in case anybody else wants to try what I managed to give cartere, I removed the one with the *obvious* error in it from the post above and attach the remedied one to this post...
 

Attachments

  • audio-library+micGain-fix1.zip
    24.4 KB · Views: 182
They are already protected. Never mind about those for now.

Please look at http://forum.pjrc.com/threads/24578...using-Teensy-2-0?p=40354&viewfull=1#post40354 for reference to PEQcalc and loadPEQx if interested and you haven't already.

I'm thinking of reworking PEQcalc so that it can quantize for both the SGTL5000 PEQ and the AudioFilterBiquad object, rename it filterCalc and ask for suggestions as to where to stick it - if not for AudioFilterBiquad I would just put it in AudioControlSGTL5000 (practically but not quite as is, sans Serial.prints of course) but I think it merits the question; where would it be best placed?

the loadPEQx routine can almost just become a member of AudioControlSGTL5000 as is but I think I will rework it to blend in a little more.

To make it worth having any form of the loadPEQx routine there has to be opportunity to route audio data via the DAP so I will try to post soon with a few models of functions I propose to add at the same time a more reasonable simile of laodPEQx is added.
 
I'm having trouble with my audio board. I finally got my Teensy hooked up last night and successfully got it to play a wav file through my headphones. However, thus far I have been able to get a peep out of my amplifier, with or without the headphones plugged in.

The amplifier is connected to the Line Out port on the audio module via a 3.5mm plug. I have attached the pins for said plug to the three closest to the Teensy leaving the two near the edge of the board unconnected, just as the stencil indicates. I have tested the continuity of the cable, and the center pin goes to the sleeve, and the outside ones go to the tip and ring. So the issue isn't with the cable.

I have also tested the amplifier using my PC. The amplifier works fine.

The amplifier receives power from a 12v wall adapter. I have two speakers connected to it just in case one channel starts working and the other doesn't. This 12v supply goes to a 3.3v regulator that I use to supply power to the Teensy through its 3.3v port. The grounds are connected. All pins of the Teensy are connected to the Audio module. I have no other boards attached to the Teensy. With this setup, the headphone jack on the Teeensy works fine, I can hear the music. But I hear nothing from my amp. I have tested both with and without headphones attached just in case there is a headphone detect built in that would shut off the line out.

I have even attempted to disconnect the 3.3v regulator from the Teensy and power it from the USB instead while the amplifier is powered by the 12v source. Headphones still work fine, but no audio on the Line Out.

It occurred to me just now that there may be some setting I need to enable in the audio library to get sound on the line out pins, since they're not attached to the same pins on the codec as the headphone jack and so may be independently controlled. Is there?
 
I'm having trouble with my audio board. I finally got my Teensy hooked up last night and successfully got it to play a wav file through my headphones. However, thus far I have been able to get a peep out of my amplifier, with or without the headphones plugged in.

The amplifier is connected to the Line Out port on the audio module via a 3.5mm plug. I have attached the pins for said plug to the three closest to the Teensy leaving the two near the edge of the board unconnected, just as the stencil indicates. I have tested the continuity of the cable, and the center pin goes to the sleeve, and the outside ones go to the tip and ring. So the issue isn't with the cable.

I have also tested the amplifier using my PC. The amplifier works fine.

The amplifier receives power from a 12v wall adapter. I have two speakers connected to it just in case one channel starts working and the other doesn't. This 12v supply goes to a 3.3v regulator that I use to supply power to the Teensy through its 3.3v port. The grounds are connected. All pins of the Teensy are connected to the Audio module. I have no other boards attached to the Teensy. With this setup, the headphone jack on the Teeensy works fine, I can hear the music. But I hear nothing from my amp. I have tested both with and without headphones attached just in case there is a headphone detect built in that would shut off the line out.

I have even attempted to disconnect the 3.3v regulator from the Teensy and power it from the USB instead while the amplifier is powered by the 12v source. Headphones still work fine, but no audio on the Line Out.

It occurred to me just now that there may be some setting I need to enable in the audio library to get sound on the line out pins, since they're not attached to the same pins on the codec as the headphone jack and so may be independently controlled. Is there?



This is what I did:

audioShield.unmuteLineout();
 
That seems to have fixed it. Though I seem to be getting a pop after my audio file ends and I just double checked that the file does not have a pop in it. I hear this pop when using the headphones as well.

I have not changed the example much:
Code:
void setup() {
  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(5);

  audioShield.enable();
  audioShield.volume(50);
  audioShield.unmuteLineout(); 
  
  SPI.setMOSI(7);
  SPI.setSCK(14);
  if (SD.begin(10)) {
    wav.play("001.WAV");
  }
}

void loop() {
  //float vol = analogRead(15);
  //vol = vol / 10.24;
  //audioShield.volume(vol);
  delay(20);
}


Btw, vol = vol / 10.24 looked like a bug to me, but I guess it's not since volume takes 0..100 for input instead of 0..1, which is an odd choice, but anyway, shouldn't that be 10.23? Analogread doesn't go up to 1024.
 
Last edited:
Though I seem to be getting a pop after my audio file ends and I just double checked that the file does not have a pop in it. I hear this pop when using the headphones as well.

I'm pretty sure it's related to this part of the code:

Code:
                // end of file reached
                if (block_offset > 0) {
                        // TODO: fill remainder of last block with zero and transmit
                }
                state = STATE_STOP;
                return false;
 
Paul,

I have made a version of 'filterCalc' which appears to output usable coefficients for both SGTL5000 built in stuff & your AudioFilterBiquad object, I tested it by making a sketch for Teensy++ 2.0 and having it output values to serial monitor and I think its output is OK.
Code:
// void filterCalc(unsigned char filtertype, float fC, float dB_Gain, float Q, unsigned long quantization_unit, unsigned long fS, unsigned long coef[]);

// usage for SGTL5000 built in filtering
unsigned long coefs[5];
filterCalc(PEQ_LPF,800,0,0.707,524288,44100,coefs);


// usage for AudioFilterBiquad
long myFilterParameters[8];
filterCalc(PEQ_LPF,800,0,0.707,2147483648,44100,(unsigned long*)myFilterParameters);
The resultant output of my test was
Code:
SGTL5000 Filter info;
b0=0x00313
b1=0x00626
b2=0x00313
a1=0x75B3C
a2=0xC9878

AudioFilterBiquad Filter info;
a0=3224303
a1=6448607
a2=3224303
b1=1974714496
b2=-913869824
Mind you this was done on Teensy++ 2.0 where sizeof(int)==2 and sizeof(long)==4 - I've the impression that if compiling for Teensy 3.x the sizeof(int)==4 and sizeof(long)==?; I am hoping the ?==4 as well in that case.

I may or may not be quantizing well for AudioFilterBiquad, I expect I am but I see I come out with slightly different figures than derived the way you derived yours - I expect it amounts to very very similar filtering but I haven't means to test it properly yet.


It looks to me like AudioFilterBiquad does not make a copy of 'myFilterParameters' but instead refers to the original array giving me the impression it could be changed on the fly, please correct me if I misinterpreted that.

If I am right about that I expect I am probably right that before changing them it would be best to 'cli()', change them, re-zero the other three elements of the array and finally 'sei()' again - am I very wrong here or not?

If nobody thinks I'm very wrong about those details then I can whip up a test-sketch I would be pleased if somebody would volunteer to try for me, if I am only wrong about sizeof(long) on Teensy 3.x or necessity to stop interrupts or re-zero other elements of myFilterParameters[] and somebody is verbose enough about how wrong exactly then I can nearly as quickly whip up a test sketch anyway...
 
Last edited:
Mind you this was done on Teensy++ 2.0 where sizeof(int)==2 and sizeof(long)==4 - I've the impression that if compiling for Teensy 3.x the sizeof(int)==4 and sizeof(long)==?; I am hoping the ?==4 as well in that case.
On arm chips like the Teensy, sizeof (short) == 2, sizeof (int) == 4, sizeof (long) == 4, sizeof (long long) == 8. The include file limits.h gives you the min/max for each type, and you can use that in #if type statements to adjust types.
 
Last edited:
For programming inside the library, it's best to use the these types: int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t. Even though this library is very specific to the Cortex-M4 processor, it's still good to use those names to specify exactly how many bits you want.

One minor complexity (well, perhaps not so minor) is using the M4's packed format. Two int16_t's can be packed into a single 32 bit variable, which is usually specified as uint32_t type. Generally it's best to write code first using ordinary variables and types and get it working and well tested, before piling on this extra complexity. But the packed format really helps speed things up, partly because you can bring 2 int16_t's into the processor as a single uint32_t read from memory, which is twice as fast as reading them separately.


I may or may not be quantizing well for AudioFilterBiquad, I expect I am but I see I come out with slightly different figures than derived the way you derived yours - I expect it amounts to very very similar filtering but I haven't means to test it properly yet.

When your board arrives, it's best to test the quantizing problems using a low pass filter with a low corner frequency.

It looks to me like AudioFilterBiquad does not make a copy of 'myFilterParameters' but instead refers to the original array giving me the impression it could be changed on the fly, please correct me if I misinterpreted that.

Yes, the filter coefficients can be changed on the fly.

If I am right about that I expect I am probably right that before changing them it would be best to 'cli()', change them, re-zero the other three elements of the array and finally 'sei()' again - am I very wrong here or not?

Yes. If interrupts are enabled, and your code is running in the main program, it's possible the update() function will run after you've changed some but not all the coefficients. That could produce 128 samples of very wrongly processed audio.

Within the library, use __disable_irq() and __enable_irq().

In the example sketches, normally you should use AudioNoInterrupts() and AudioInterrupts(). Users will copy the examples, so even if you know these aren't truly necessary for your specific example, they should still always be used in the example sketches.

The idea behind AudioNoInterrupts() is to allow the sketch to suspend audio updates, even when it calls to library functions that have __disable_irq() and __enable_irq(). The library's function can guarantee correct operation with __disable_irq() and then use __enable_irq(), but the audio library will remain disabled until the sketch completes a sequence of several such library calls and finally enables the library with AudioInterrupts().
 
Thanks very much Michael & Paul. I will review my example/test sketch a bunch and post it later, my family want some attention from me too so 'later' could be a day or something.
 
Attached should be suspiciously similar to a bass control, it compiles for Teensy 3.1 and I'll be very pleased to hear if someone can try it and it just works but grateful no less to hear how it fails if that is the case. It is probably not well done centered at 220Hz and being the width 'Q=0.2' makes it.
 

Attachments

  • filterCalc_test.ino
    7.4 KB · Views: 141
Attached should be suspiciously similar to a bass control, it compiles for Teensy 3.1 and I'll be very pleased to hear if someone can try it and it just works but grateful no less to hear how it fails if that is the case. It is probably not well done centered at 220Hz and being the width 'Q=0.2' makes it.

Seems to work, the update every 50ms is very audible.
 
Seems to work, the update every 50ms is very audible.
Thanks for trying it cartere.

It must sound awful, I imagine. Here is a replacement loop which should, at minimum, reduce that noise hopefully as much as drastically; There will be a cleaner way to update those coefficients than I am here and if nobody makes an example of how before I get my parcel I will definitely be looking for it.

Code:
void loop() {
  // every 50 ms, check for adjustment the bass
  if (volmsec > 250) { // every quarter of a second instead perhaps.
    float vol = analogRead(15);
    vol=((vol-512)/512)*60;
    vol=(int)vol;
    vol=vol/10;  // only changing for 0.1dB steps
    if(lastvol!=vol) {
      AudioNoInterrupts();
      filterCalc(FILTER_PEAKEQ, 220, vol, 0.2, 2147483648, 44100, (unsigned long*)myFilterParameters);
      myFilterParameters[5]=myFilterParameters[6]=myFilterParameters[7]=0;
      AudioInterrupts();
      lastvol=vol;
    }
/*    vol = vol / 10.24;
    audioShield.volume(vol); */
    volmsec = 0;
  }
}
 
Thanks for trying it cartere.

It must sound awful, I imagine. Here is a replacement loop which should, at minimum, reduce that noise hopefully as much as drastically; There will be a cleaner way to update those coefficients than I am here and if nobody makes an example of how before I get my parcel I will definitely be looking for it.

Code:
void loop() {
  // every 50 ms, check for adjustment the bass
  if (volmsec > 250) { // every quarter of a second instead perhaps.
    float vol = analogRead(15);
    vol=((vol-512)/512)*60;
    vol=(int)vol;
    vol=vol/10;  // only changing for 0.1dB steps
    if(lastvol!=vol) {
      AudioNoInterrupts();
      filterCalc(FILTER_PEAKEQ, 220, vol, 0.2, 2147483648, 44100, (unsigned long*)myFilterParameters);
      myFilterParameters[5]=myFilterParameters[6]=myFilterParameters[7]=0;
      AudioInterrupts();
      lastvol=vol;
    }
/*    vol = vol / 10.24;
    audioShield.volume(vol); */
    volmsec = 0;
  }
}

/code
if(fabs(lastvol-vol)>02.0) {
vol=((vol-512)/512)*6;
//AudioNoInterrupts();
filterCalc(FILTER_PEAKEQ, 220, vol, 0.2, 2147483648, 44100, (unsigned long*)myFilterParameters);
myFilterParameters[5]=myFilterParameters[6]=myFilterParameters[7]=0;
//AudioInterrupts();
lastvol=vol;
/code

This helped in the center range of the pot, still noisy towards both ends. No change with AudioInterrupts enabled or disabled.
 
/code
if(fabs(lastvol-vol)>02.0) {
vol=((vol-512)/512)*6;
//AudioNoInterrupts();
filterCalc(FILTER_PEAKEQ, 220, vol, 0.2, 2147483648, 44100, (unsigned long*)myFilterParameters);
myFilterParameters[5]=myFilterParameters[6]=myFilterParameters[7]=0;
//AudioInterrupts();
lastvol=vol;
/code

This helped in the center range of the pot, still noisy towards both ends. No change with AudioInterrupts enabled or disabled.

If you change that 02.0 to 9 or maybe 10
Code:
 if(fabs(lastvol-vol)>9) {
      vol=((vol-512)/512)*6;
      //AudioNoInterrupts();
      filterCalc(FILTER_PEAKEQ, 220, vol, 0.2, 2147483648, 44100, (unsigned long*)myFilterParameters);
      myFilterParameters[5]=myFilterParameters[6]=myFilterParameters[7]=0;
      //AudioInterrupts();
      lastvol=vol;
yours would be a near enough simile of my proposed fix (0.1dB steps) and it would defeat even more of the jitter/noise coming from the pot (my guess, at least.)
 
So I've gotten to the point where I need to know if a wav file is still playing. I assume I can just do this:

Code:
if (wav::state != STATE_STOP) {}

But how can I find out exactly how long a particular wav file is in case I want to begin doing something a second before it finishes playing? I see that some states list the bit depth and whether the file is stereo or mono, and I assume the sample rate is available somewhere as well, but I don't see were I can get the amount of data after the header to calculate the length, and using the state to calculate it seems like a big kludge anyway because I'd have to wait until it gets into one of those states, seeing as how there are one or two before it gets into that state where it's looking at the header.

Is the only solution here to look at the wav file parsing code and make my own function to parse the wav file? But how should I do that? You said accessing the SD card while wav files are playing isn't supported, so do I have to do all that before I start playing files or do it between playing files? And how does your own code handle starting the playback of a second file anyway if you can't access the SD card while a wav file is playing? Or does the existence of AudioNoInterrupts() now mean I can safely access the card for brief periods of time while playing back audio?
 
...

To make it worth having any form of the loadPEQx routine there has to be opportunity to route audio data via the DAP so I will try to post soon with a few models of functions I propose to add at the same time a more reasonable simile of loadPEQx is added.

I suggest these all as public members of AudioControlSGTL5000, mostly returning the contents of the register they are effecting
Code:
// CHIP_SSS_CTRL
uint16_t dap_mix_lrswap(uint8_t n); // 1=true/enable
uint16_t dap_lrswap(uint8_t n);
uint16_t dac_lrswap(uint8_t n);
uint16_t i2s_lrswap(uint8_t n);
uint16_t dap_mix_select(uint8_t n); // 0=ADC, 1=I2S_IN
uint16_t dap_select(uint8_t n);
uint16_t dac_select(uint8_t n); // 3=DAP
uint16_t i2s_select(uint8_t n);

// CHIP_DAC_VOL
uint16_t dac_vol_right(float n); //  by percentage 0-100
uint16_t dac_vol_left(float n);
uint16_t dac_vol(float n); // set both directly

// DAP_CONTROL
uint16_t dap_mix_enable(uint8_t n);
uint16_t dap_enable(uint8_t n);

// DAP_PEQ
uint16_t dap_peqs(uint8_t n); // valid to n&7, 0 thru 7 filters enabled.

// DAP_AUDIO_EQ
uint16_t dap_audio_eq(uint8_t n); // 0=NONE, 1=PEQ (7 IIR Biquad filters), 2=TONE (tone), 3=GEQ (5 band EQ)

// DAP_AUDIO_EQ_BASS_BAND0 & DAP_AUDIO_EQ_BAND1 & DAP_AUDIO_EQ_BAND2 etc etc
uint8_t dap_audio_eq_band(uint8_t bandNum, float n); // by signed percentage -100/+100; dap_audio_eq(3);
void dap_audio_eq_geq(float bass, float mid_bass, float midrange, float mid_treble, float treble);
uint8_t dap_audio_eq_tone(float bass, float tone); // dap_audio_eq(2);

// SGTL5000 PEQ Coefficient loader
void load_peq(uint8_t filterNum, uint32_t filterParameters[]);

// a route selection routine to simplify a little
void route(uint8_t input, uint8_t output, uint8_t via_i2s, uint8_t via_dap);
Please question/comment/advise :)
 
Status
Not open for further replies.
Back
Top