Pitch change compensation by samplerate variations SGTL5000

jopstie

New member
Dear all,


This audio library astonished me, so much functionality which is so easy to use.

Last couple of days I have been trying to use the library for my specific purpose. I want to use cheap and lightweight hardware to record sound from a tape. The choices for hardware result in velocity variations up to 400Hz. By using an 800 ppr encoder, I are able to measure velocity and compensate these variations. I made a post-processing implementation and it works, now I would like to implement a real-time using the Teensy 3.6. I want to compensate the velocity variations by changing the samplerate of the Teensy 3.6 + SGTL5000 accordingly. I would like to use the Teensy as an USB audio device with a fixed sample rate.

It's is perfect if the nominal sample rate is constant, thus the sample rate of the audio device is fixed, say the default 44117 Hz. However , the momentarily sample rate of the SGTL5000 (i2c) will change accordingly with the measured variations. So if the tape is going to fast, we increase the sample rate and vice versa. I will need to buffer the incoming samples and pass them to the USB at the desired nominal rate.

I am pretty sure the audio library implements all required functionalities already, including a buffer using the audio memory pool. However, when I change the sample rate of the i2c, I get distorted sound. For a lower i2c sample rate, I clearly see zeros in the recording in Audacity, telling me there were no samples available when the USB requested them.

I must be overlooking something or I must have lack in some understanding. I have searched around the forum and I've even been on page 2 of google. I am stuck, who has some suggestions? The USB request interrupt is not based on the i2c clock, is it?


I included some example code. I am forwarding the i2c to USB with the queue objects in between. My basic test would be to first fill the audio memory pool by adding a delay and the lower the sample rate, which should result in a draining buffer.

Code:
#include <Audio.h>
#include <Wire.h>
#include <AudioTiming.h>

// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=497.00000381469727,367.0000057220459
AudioRecordQueue         rqueue2;         //xy=653.0000076293945,386.0000057220459
AudioRecordQueue         rqueue1;         //xy=655.0000076293945,348.0000057220459
AudioPlayQueue           pqueue1;         //xy=769,348
AudioPlayQueue           pqueue2;         //xy=769.0000114440918,385.0000057220459
AudioOutputUSB           usb1;           //xy=902.0001106262207,364.5000057220459
AudioConnection          patchCord1(i2s1, 0, rqueue1, 0);
AudioConnection          patchCord2(i2s1, 1, rqueue2, 0);
AudioConnection          patchCord3(pqueue1, 0, usb1, 0);
AudioConnection          patchCord4(pqueue2, 0, usb1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=710.0000076293945,465.00000953674316
// GUItool: end automatically generated code

short array1[AUDIO_BLOCK_SAMPLES];
short array2[AUDIO_BLOCK_SAMPLES];

AudioTiming audiotiming;

int ret=0;

void setup() {    
  audiotiming.init();
  audiotiming.begin();
              
  AudioMemory(900);
  sgtl5000_1.enable();
  sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
  sgtl5000_1.volume(0.6);

  audiotiming.setI2S_freq(AUDIO_SAMPLE_RATE_EXACT);
  
  Serial.begin(9600);
  
  rqueue1.begin();
  rqueue2.begin();
  Serial.println("Begin Teensy with SGTL5000");

  Serial.print(" Audio block samples:");
  Serial.println(AUDIO_BLOCK_SAMPLES);
  Serial.print("Exact samplerate:");
  Serial.println(AUDIO_SAMPLE_RATE_EXACT);

  // Let audio pool fill up a little to account for lower sampling rates
  delay(1000);

  // Set sample rate lower, now the buffer should drain (but its not)
  audiotiming.setI2S_freq(AUDIO_SAMPLE_RATE_EXACT - 1000);
}

void loop() {  
  ret=rqueue1.available();

  if(ret >= 1)
  {
    // Get input channel 1 and store in array 1
    memcpy(&array1[0], rqueue1.readBuffer(), 2*AUDIO_BLOCK_SAMPLES);
    rqueue1.freeBuffer();
    
    // Get input channel 2 and store in array 2
    memcpy(&array2[0], rqueue2.readBuffer(), 2*AUDIO_BLOCK_SAMPLES);
    rqueue2.freeBuffer();
    
    memcpy(pqueue1.getBuffer(), &array1[0], 2*AUDIO_BLOCK_SAMPLES);
    pqueue1.playBuffer();
    memcpy(pqueue2.getBuffer(), &array2[0], 2*AUDIO_BLOCK_SAMPLES);
    pqueue2.playBuffer();
    
    Serial.println(AudioMemoryUsage());
  }
}

Justin
 
yes USB audio needs a fixed samplerate, it is not possible to vary it. It is just not supported by the protocol. Your computer inserts the zeros. So with USB it is not possible what you are trying to do.
Recording to SD will work better.
I don't see an easy "just works" way to do it live.
 
Let us ignore that you mean I2S and not I2C.
I will also assume that the Audiotiming library does what you expect it does.
The AudioOutputUSB module tells the PC it is using 44100Hz (fixed value), so if you vary the InputI2S sampling frequency then, obviously, you will have missing samples or empty samples.
So what do you wanted to do?
Resample the InputI2S to a fixed OutputUSB sampling frequency?
Then you need an additional module that resamples the data.
 
FrankB, thanks for your reply.
I am aware that the USB audio needs a fixed samplerate, I do not want to vary the sample rate with which the device is registered on the PC.
The nominal sample rate of my system will be fixed, however I want to momentarily vary the samplerate of the i2c. These samples are then buffered in a FIFO and the USB can fetch these samples at a fixed samplerate from this buffer. The FIFO will always be half filled to account for the variations.
 
WMXZ, indeed I mean I2S, I hadn't noticed, thanks.

The data I recorded which was not zero, had a pitch change when it was played at a higher sampling rate. Which tells me the audiotiming does what it should do.

I can indeed implement a resampler which I have done in post, it works. Introducing some acceptable distortions.

The tape is of course analog audio. I you would digitize this, you want to take spatially equidistant samples. To account for nominal+delta velocities you should always oversample and downsample to the required spatial sample rate. For this a interpolation algorithm is needed.
However, in theory, it would be best to actually vary the sample rate according to the tape velocity. Then I have more processing time on my Teensy for other tasks.

If a make a configuration as follows, I am pretty sure it's feasible:

I2S ----------------------------------------------------------------> FIFO Buffer -------------------------------> USB
sample rate+-delta, mean = sample rate.......................half-filled buffer....................................fixed sample rate
 
The Audiotiming-lib changes the frequency of the whole library -not I2S only. It influences USB, too.

between recordQueue and playQueue one could resample. It is as easy as bare metal DSP programming allows.
:) Yes. So, why we don't have a good-quality resampler in the audio-library? It's one of the most requested features.
I could write a simple resampler, too, but I doubt that it had a good quality and would be useful.
 
Last edited:
The Audiotiming-lib changes the frequency of the whole library -not I2S only. It influences USB, too.

I thought that modifying I2S0_MDR would result in only a change of the I2S port frequency? So what I understand is that the rest of the library, including the USB, is based on this clock frequency?

Could you help me pointing at some documentation about this? I have a hard time getting a good overview of how the library is implemented. How did you guys figure that out?
 
I thought that modifying I2S0_MDR would result in only a change of the I2S port frequency? So what I understand is that the rest of the library, including the USB, is based on this clock frequency?
Yes and yes. The library has only one clock. If you don't use I2S, it uses the the ADC or DAC clock, for example. This is also the reason why it uses 44.18kHz and not 44100Hz - the timer used for the ADCs/DACs does not support 44100, so the whole lib uses these 18 Hz more (which is not audible).

Could you help me pointing at some documentation about this? I have a hard time getting a good overview of how the library is implemented.
The most important part is in the core, in audiostream.h which glues all the parts together.
 
Last edited:
Now,
if you really wanted to have two sampling rates, one variable for input, another one (say fixed) for output you obviously cannot use the audio library as it is,
BUT
you can take the audiostream code and create your own dual sampling frequency system.

After all, the code is free to be duplicated, modified etc. and IMO without experimenting new ideas....
 
Is there currently a solution in the Audio Library for two different sample rates? One for input and one for output? I wish to change the pitch of live audio. Or does anyone have perhaps a piece of sample code of how to achieve this on a Teensy 4.0 w/ Audio Board? Thanx!
 
Back
Top