Discussion about a simple way to change the sample-rate

Frank B

Senior Member
Hi,

i guess, there is a - more or less - simple way to change the samplerate of the audiolibrary at runtime.
For example, if you use the I2S-Output, i think, it would be sufficient to patch the "timing"-registers (multipliers/dividers) to archive 48KHz.
- without any changes to the library code -

Of course, this has several unwanted side-effects, and some things will NOT work.
But which ?

For example,
- the sine-object will work, but with a wrong frequeny. The would be easy to "repair" (call frequency(freq*0.85))
- mixers will work

So, which parts will work, which not ? Where are problems ?

Edit: My reason to ask:
I re-activated an old project, my webradio, and some stations are using 48KHz. Therey are not usable, at the moment.
A "hack" like this would allow to play them - without having to patch audio-library sourcecode.
 
Last edited:
Hi,

i guess, there is a - more or less - simple way to change the samplerate of the audiolibrary at runtime.
For example, if you use the I2S-Output, i think, it would be sufficient to patch the "timing"-registers (multipliers/dividers) to archive 48KHz.

Yes, that is the way to do it.
Consequences:
I only see: different sampling-> different block duration -> different frequency.
So all instances that internally use 44.1 kHz must be converted to 48 kHz, as you suggested.
 
Not sure I understand the question, but I think you're asking what will happen if you play audio at a different rate than it was orignially sampled at?
E.g. audio recorded at 44.1kHz played out at 48kHz.
If the audio is recorded and you have it all in local storage somewhere, chances are it will work, but you will hear it at a slightly higher pitch (or lower if the other way around).
If the audio is being streamed live, you might hear audio breaks as you run out of data (clocking it out faster than clocking it in) or you will have to handle buffer overruns (clocking it in faster than clocking out) in your software which will probably sound like short skips in the audio (when you dump a chunk of data).
 
Not sure I understand the question, but I think you're asking what will happen if you play audio at a different rate than it was orignially sampled at?

No. :cool:
I want to know the side-effects of changing the sample-freq at runtime - and if there are workarounds for the different objects.
And *which* parts make problems.
Because i want to be able to switch to 48KHz or other freqs and runtime.
(An other way would be to create a "up/down" sampler that re-samples anything to 44.1KHz - but this is more work and less quality)

But *without* changing anything in the core- or audiolibrary.

Ok, I guess, I have to read all the sourcecode..
 
Last edited:
...if someone wants to try it:

Code:
void setI2SFreq(int freq) {
  typedef struct {
    uint8_t mult;
    uint16_t div;
  } tmclk;

  const int numfreqs = 14;
  const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, 44117.64706 , 48000, 88200, 44117.64706 * 2, 96000, 176400, 44117.64706 * 4, 192000};

#if (F_PLL==16000000)
  const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
#elif (F_PLL==72000000)
  const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
#elif (F_PLL==96000000)
  const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
#elif (F_PLL==120000000)
  const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
#elif (F_PLL==144000000)
  const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
#elif (F_PLL==180000000)
  const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
#elif (F_PLL==192000000)
  const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
#elif (F_PLL==216000000)
  const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
#elif (F_PLL==240000000)
  const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#endif

  for (int f = 0; f < numfreqs; f++) {
    if ( freq == samplefreqs[f] ) {
      while (I2S0_MCR & I2S_MCR_DUF) ;
      I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
      return;
    }
  }
}

(Tested with PT8211 only)
 
Last edited:
OOooooohh Frank,
If you can get the SGTL5000 running at 11025kHz and/or 8kHz sampling rate, life would be so much easier for some things I'd like to play with - assuming that the audio library doesn't require major tweaks to accomodate this.

Pete
 
OOooooohh Frank,
If you can get the SGTL5000 running at 11025kHz and/or 8kHz sampling rate, life would be so much easier for some things I'd like to play with - assuming that the audio library doesn't require major tweaks to accomodate this.

Pete

Can you try the code above ? Maybe it works ?
setI2SFreq(11025);

I don't know if the SGTL accepts that, and i did not look at the datasheet, but it is worth a try :)
 
Hi Frank,
Just tried the code in a sketch which routes microphone input straight to output on T3.6 at 180MHz. At 8000 and 11025, neither of them work but it works at 44100.
This is calling your function immediately after the SGTL enable() function.


Pete
 
Hi Frank,
Just tried the code in a sketch which routes microphone input straight to output on T3.6 at 180MHz. At 8000 and 11025, neither of them work but it works at 44100.
This is calling your function immediately after the SGTL enable() function.


Pete

Ok, maybe it needs some additional settings (PLL?)
I'll try it in a few days :)
 
Here's the same for the internal DACs :

Code:
void setDACFreq(int freq) {
const unsigned config = PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_CONT | PDB_SC_PDBIE | PDB_SC_DMAEN;
    PDB0_SC = 0;
    PDB0_IDLY = 1;
    PDB0_MOD = round((float)F_BUS / freq ) - 1;    
    PDB0_SC = config | PDB_SC_LDOK;
    PDB0_SC = config | PDB_SC_SWTRIG;
    PDB0_CH0C1 = 0x0101;    
}

edit: Added one line
 
Last edited:
@el_supremo,

Well, the SGTL datasheet says, that it supports 8 and 11KHz.I guess, with some additional I2C-commands, it will work.
I'll look tomorrow.
 
Hm, is there was a way to trigger the ADCs from I2S, we could use them, too...
Anyone knows if the pin-interrupts work with pins set to output?

Edit:
Or, even better, is it possible to trigger DMA with a pin-change ?
 
Last edited:
el_supremeo,
i don't understand..
please, can you provide a test-sketch that shows the problem ?

Output works flawlessly:

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

void setI2SFreq(int freq) {
  typedef struct {
    uint8_t mult;
    uint16_t div;
  } __attribute__((__packed__)) tmclk;

  const int numfreqs = 14;
  const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, 44117.64706 , 48000, 88200, 44117.64706 * 2, 96000, 176400, 44117.64706 * 4, 192000};

#if (F_PLL==16000000)
  const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
#elif (F_PLL==72000000)
  const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
#elif (F_PLL==96000000)
  const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
#elif (F_PLL==120000000)
  const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
#elif (F_PLL==144000000)
  const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
#elif (F_PLL==180000000)
  const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
#elif (F_PLL==192000000)
  const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
#elif (F_PLL==216000000)
  const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
#elif (F_PLL==240000000)
  const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#endif

  for (int f = 0; f < numfreqs; f++) {
    if ( freq == samplefreqs[f] ) {
      while (I2S0_MCR & I2S_MCR_DUF) ;
      I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
      return;
    }
  }
}

// GUItool: begin automatically generated code
AudioSynthWaveform       waveform1;      //xy=110,75
AudioOutputI2S           output;          //xy=303,78
AudioConnection          patchCord1(waveform1, 0, output, 0);
AudioConnection          patchCord2(waveform1, 0, output, 1);
// GUItool: end automatically generated code
AudioControlSGTL5000 codec;

void setup() {
  const int freq = 1000;
  const int samplefreq = 8000;
  AudioMemory(15);
  codec.enable();
  codec.volume(0.35);  
  waveform1.begin(WAVEFORM_SINE);
  waveform1.frequency(AUDIO_SAMPLE_RATE_EXACT/samplefreq*freq);
  waveform1.amplitude(0.99); 
  setI2SFreq(samplefreq);
}

void loop() {
  /* 
  float vol = analogRead(15);
  vol = vol / 1024;
  codec.volume(vol);
  */
}
 
Last edited:
Hi Frank,
I used a simple microphone input to headphone output sketch and just got silence unless I chose the 44100 rate.

What/where is audioutil.h?

Pete
 
audioutil. sorry, delete it, it's not needed.
Can you post the sketch ? i've never used micropohone. Just to make sure, that the sketch i will use is correct.
 
Just tried your sketch and it looks like it is working. I'd like to try an audio filter at the sampling rate of 8000 - shouldn't take long to put together.

Pete
 
works! (????)
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>


void setI2SFreq(int freq) {
  typedef struct {
    uint8_t mult;
    uint16_t div;
  } __attribute__((__packed__)) tmclk;
  const int numfreqs = 14;
  const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, 44117.64706 , 48000, 88200, 44117.64706 * 2, 96000, 176400, 44117.64706 * 4, 192000};

#if (F_PLL==16000000)
  const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
#elif (F_PLL==72000000)
  const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
#elif (F_PLL==96000000)
  const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
#elif (F_PLL==120000000)
  const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
#elif (F_PLL==144000000)
  const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
#elif (F_PLL==180000000)
  const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
#elif (F_PLL==192000000)
  const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
#elif (F_PLL==216000000)
  const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
#elif (F_PLL==240000000)
  const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#endif

  for (int f = 0; f < numfreqs; f++) {
    if ( freq == samplefreqs[f] ) {
      while (I2S0_MCR & I2S_MCR_DUF) ;
      I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
      return;
    }
  }
}


// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=250,232
AudioOutputI2S           i2s2;           //xy=455,243
AudioConnection          patchCord1(i2s1, 0, i2s2, 0);
AudioConnection          patchCord2(i2s1, 1, i2s2, 1);
// GUItool: end automatically generated code

AudioControlSGTL5000 sgtl5000_1;

const int samplefreq = 8000;
  
void setup() {
  Serial.begin(9600);
  AudioMemory(8);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
  sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
  sgtl5000_1.micGain(36);
  setI2SFreq(samplefreq);
}

void loop() {}
 
Last edited:
I've got a simple bandpass filter working at a sampling rate of 8000Hz but the audio quality is awful. When the audio is passed through unfiltered it sounds fine. When I switch in the filter, the filtering itself is obviously working but the audio quality is not good at all. I'll dig into what's going on with the filter. Maybe I haven't initialized it correctly or something like that.

Pete
 
But not a filter on the SGTL, right ?
I guess, this will not work good.. we're lieing about the freq, so any filters on the SGTL can not work, with this simple patch. The SGTL still thinks, we're @ 44100 kHz

For parts in the audio-lib, you' need to lie too (like in my sine-example above) :

waveform1.frequency(AUDIO_SAMPLE_RATE_EXACT/samplefreq*freq);
 
Your sketch in #19 works at 8000, 11025, 22050, 44100.

For the filter, I don't think I have to lie to anything (and BTW this uses the line input). I am just getting the samples from the queue, filtering them and then sending them back to the queue. As long as the audio sampling rate on input and output really is 8000, things should work. In fact, as I mentioned, the filter is functioning as intended (about 200Hz wide, centred at 774Hz). It's just that when the filter is switched in, a lot of out-of-band noise gets through the filter too. I'll keep digging.

Pete
 
Got it. As I thought. A bug. I hadn't specified the correct buffer size at one point. With the correct buffer size, the filter now works as expected.
Fabulous. Well done Frank.

Pete
 
Ok, i've checked it. My Multimeter shows exactly 8000Hz (on LRCLK).

Good night. It's 00:30 here.
I hope you get it working !
 
Has anyone tried faster sample rates? There are several people who might be interested in doing ultrasound experiments (bat listening!) if they can sample at, say, 96 kHz. The Teensy 3.6 should have enough horsepower, and the STGL5000 itself is rated up to that speed...but can the Audio Library be convinced to run the STGL5000 that fast?

Chip
 
Back
Top