24bit-48KHz I2S input on Teensy 4.0

Status
Not open for further replies.

muon

Member
So I'm having a couple of thoughts...

Here I am testing instances of AudioInputI2S and AudioInputI2S2 for capturing samples in a queue and printing them(mostly copy code from recording example). But this is all gonna take place at 16bit @44.1KHz while I want 24bit@48KHz So...
Code:
#include <Audio.h>

AudioInputI2S             i2s1;
AudioInputI2S2            i2s2;
AudioRecordQueue          queue1;
AudioRecordQueue          queue2;
AudioConnection           patchCord1(i2s1, 0, queue1, 0);
AudioConnection           patchCord2(i2s2, 0, queue2, 0);

void setup() {
  Serial.begin(115200);
  AudioMemory(60);
  queue1.begin();
  queue2.begin();
}

void loop() {
  byte buffer1[512];
  byte buffer2[512];

  memcpy(buffer1, queue1.readBuffer(), 256);
  queue1.freeBuffer();
  memcpy(buffer1 + 256, queue1.readBuffer(), 256);
  queue1.freeBuffer();
  memcpy(buffer2, queue2.readBuffer(), 256);
  queue2.freeBuffer();
  memcpy(buffer2 + 256, queue2.readBuffer(), 256);
  queue2.freeBuffer();
  for (int i = 0; i < 512; i++)
  {
    Serial.println(buffer1[i]);
  }
  for (int i = 0; i < 512; i++)
  {
    Serial.println(buffer2[i]);
  }
}

I think what I might do is inside the "audio_block_struct" in AudioStream.h,
View attachment 18260
change the data[AUDIO_BLOCK_SAMPLES] data type from int16_t to int32_t and change the
#define AUDIO_SAMPLE_RATE_EXACT 44100.0f
to
#define AUDIO_SAMPLE_RATE_EXACT 48000.0f

I don't know how deep the implications of this might be.
What do you folks think?

I'm creating this new thread because it can be a topic on its own I think..
Original source is https://forum.pjrc.com/threads/58474-2x-PCM1808-ADCs-recording-LR-ch-each-(I2S-quad-input-)-on-T4-and-also-1-DAC-UDA1334
 
I don't know how deep the implications of this might be.

Changing the sample rate will break some parts of the library, especially playing WAV files which are 44100 Hz sample rate. Many other parts will automatically work.

Changing the data type to int32_t will break almost everything.
 
Bit depth: Your ADC PCM1808 will hardly be able to really represent 24bits: according to the data sheet it has an SNR of 99dBA, which translates into an effective resolution of about 16bit. So, there is no need at all to deal with 24bits. I use the same ADC PCM1808 and it works perfectly with the Teensy 4 using the standard queue object and 16bit resolution.

Sample rate: use this code by FrankB to adjust the I2S sample rate of your teensy 4.0 + ADC PCM1808 to your needs (I managed to achieve up to 256ksps sample rate with the PCM1808 :). But then this is out of specs and could permanently damage your ADC and of course the specs such as SNR and THD are no longer guaranteed, but your desired 48ksps is well inside specs of the PCM1808). As Paul said, you loose some functionality in the audio lib with 48ksps and you have to account for the ratio 44.1/48 when designing filter cutoff frequencies for example

Code:
#include <utility/imxrt_hw.h> // needed for setting I2S freq on the Teensy 4.0

// set samplerate code by Frank Bösing
void setI2SFreq(int freq) {
  // PLL between 27*24 = 648MHz und 54*24=1296MHz
  int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  int n2 = 1 + (24000000 * 27) / (freq * 256 * n1);
  double C = ((double)freq * 256 * n1 * n2) / 24000000;
  int c0 = C;
  int c2 = 10000;
  int c1 = C * c2 - (c0 * c2);
  set_audioClock(c0, c1, c2, true);
  CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
       | CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07
       | CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f
}
 
Thanks for the help! I didn't know about the 6dB / bit SNR relation. I wonder why they put 24 bit when its 16bit effectively anyways? Where are the other 8 bits going? Shouldn't the lost bits cause bad audio in terms of completely incorrect samples? Unless the 24bits is being linearly scaled down to 16bits.

I just happened to try outputting the received samples on I2S2 (similary to the
here is the first version of the "bat detector":
)and the audio seems jittery, probably because SAI2/I2S2 is still processing @44.1KHz.

I believe the CCM_CS1CDR is only updating the clock for SAI1 and SAI3(unused by Teensy 4.0), and NOT SAI2 which I happen to be using for audio out.
Would you happen to have/know a solution to modify the SAI2 freq to 48KHz as well?
I did look at the data/reference sheets and found the CCM_CS2CDR that might do the trick in case a pre-made solution doesn't exist yet.
I still haven't discovered what "set_audioClock()" does. I have a feeling I might need to modify that as well for SAI2 clock configuration.

Thanks!
 
Last edited:
My assumpltions were correct and I was able to improve the audio output quality by making SAI2 run at 48KHz
So I was able to modify both the SAI1 & SAI2 clocks using the following updated setI2SFreq(int freq):

Code:
#include <utility/imxrt_hw.h> // needed for setting I2S freq on the Teensy 4.0

// set samplerate code by Frank Bösing
void setI2SFreq(int freq) {
  // PLL between 27*24 = 648MHz und 54*24=1296MHz
  int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  int n2 = 1 + (24000000 * 27) / (freq * 256 * n1);
  double C = ((double)freq * 256 * n1 * n2) / 24000000;
  int c0 = C;
  int c2 = 10000;
  int c1 = C * c2 - (c0 * c2);
  set_audioClock(c0, c1, c2, true);
  CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
       | CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07
       | CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f

//START//Added afterwards to make the SAI2 function at the desired frequency as well.
  
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
       | CCM_CS2CDR_SAI2_CLK_PRED(n1-1) // &0x07
       | CCM_CS2CDR_SAI2_CLK_PODF(n2-1); // &0x3f)

//END//Added afterwards to make the SAI2 function at the desired frequency as well.
}

I DID NOT need to modify set_audioClock() as it is working on PLL4 which is common(I'm assuming) to all the SAI peripherals.
 
Just because something has a 96dB noise floor doesn't mean that 16 bits is enough - this is the nonsense that led Philips and Sony to think 16 bits was enough for CDs. The ear can pick out a lot of detail of signals buried in noise so a 24 bit convertor still makes sense. From tests we did in the late 1990s those with really good ears can discern detail down to about 22 or 23 bits, depending on the type of signal.
 
Yes, blind audition tests reveal how much of the dynamic range you are able to hear, good young ears (younger than 25 years !) are capable of 120dB of dynamic range, which is about 20 bits.

But an ADC with 96dB SNR will NOT increase in quality if you acquire the data with 24 bit resolution, the resolution stays at about 16 bit, so for that particular ADC PCM1808 it does not make sense to acquire the data from the ADC with 24 bits, because all your lower significant bits will contain noise :).

If you have scientific evidence or publications that deliver blind test data for people hearing details down to 22/23 bits with a 96dB SNR ADC, I would be very happy to digest those!
 
Pretty much all semiconductor companies specify "A weighted" signal to noise ratio. So an ADC with 96 dB SNR will have lots of random changes in its 16th bit, and even a good amount in the 15th bit, and of course nothing but noise in those low 8 bits.

Sometimes it seems pretty much everything about tech specs in the audio world is designed to give people placebo-like specs. It seems to be highly effective at making sales, but the end result is a lot of misinformation and people with utterly unrealistic expectations.
 
So, if the ADC gives SNR of 96 dB, it is integrated over the whole bandwidth (A-weighted) and 15, 16th bit are very likely noisy.
BUT if you go into spectral analysis (say >1024 point FFT) then you can detect features that are well below the 96 dB.
For humans, obviously the critical bandwidth varies with frequency , so the noise bandwidth will vary, but it will always be less the full-scale A-weighted noise bandwidth.
Consequently, having more than 2 noise bits can be very advantageous, as one can detect signals with negative SNR (dB) levels.
I typically suggest to have 2 noise bits in the spectral band of interest, to ensure to not to miss signals of interest.
Obviously you not only need low noise ADC's but also very low noise pre-amplifiers and low noise amplification states.
And in the end it is the application that defines implementation.
 
So, if the ADC gives SNR of 96 dB, it is integrated over the whole bandwidth (A-weighted) and 15, 16th bit are very likely noisy.
BUT if you go into spectral analysis (say >1024 point FFT) then you can detect features that are well below the 96 dB.
For humans, obviously the critical bandwidth varies with frequency , so the noise bandwidth will vary, but it will always be less the full-scale A-weighted noise bandwidth.
Consequently, having more than 2 noise bits can be very advantageous, as one can detect signals with negative SNR (dB) levels.
I typically suggest to have 2 noise bits in the spectral band of interest, to ensure to not to miss signals of interest.
Obviously you not only need low noise ADC's but also very low noise pre-amplifiers and low noise amplification states.
And in the end it is the application that defines implementation.

Agreed. We were using 112dB AKM devices and information about 12dB lower can be discerned. In fact if you use pure tones you can hear about another 6dB but that isn't a measure of anything useful.

And to the other poster, the person with the best ears and is very well known in pro-audio circles was about 50 at the time.
 
Thats interesting. Did those "pro-audio circles" publish anything (possibly peer-reviewed?), so that others can participate in your knowledge?
 
Thats interesting. Did those "pro-audio circles" publish anything (possibly peer-reviewed?), so that others can participate in your knowledge?

This was done in the Soundcraft R&D department where I was Engineering Director at the time. We were far too busy designing new products to compete with Yamaha to have time to publish papers so you can take the information or leave it as you wish. The mixer being used was the Soundcraft 328 and we were testing a concept for extending the dynamic range of the analogue outputs in future digital consoles, over a decade before AKM launched the Velvet Sound range which I suspect uses similar principles.
 
It was YOU who put out the term "pro-audio circles" ;-) --> #10

Come on, I am not interested in fairy tales, give me something where I can learn, is there anything you guys published so we can learn from you, or are you still "far too busy"?
 
It was YOU who put out the term "pro-audio circles" ;-) --> #10

Come on, I am not interested in fairy tales, give me something where I can learn, is there anything you guys published so we can learn from you, or are you still "far too busy"?

Not my job to teach you things. Get yourself a programmable signal generator, a mixer and some decent loudspeakers and try it yourself. You'll learn far more than by reading papers or datasheets. And the conclusions of any paper we didn't bother to write have been given above anyway. I suggest you begin by looking at Figure 19 of the 1808 datasheet and the equivalent for whatever DAC you are using so that you can see the noise floor - then start by finding what is the lowest level of tone you can discern above that noise floor. Now change the generator to shaped noise and slowly widen the bandwidth. You'll find a direct correlation between the bandwidth of that noise and at what level you can discern its presense.
 
change the data[AUDIO_BLOCK_SAMPLES] data type from int16_t to int32_t and change the
#define AUDIO_SAMPLE_RATE_EXACT 44100.0f
to
#define AUDIO_SAMPLE_RATE_EXACT 48000.0f

Do rather something like this:
Code:
typedef enum {
	INT16, INT32, FLOAT
} data_type;

typedef struct audio_block_struct {
	uint8_t  ref_count;
	uint8_t  reserved1:6;
	uint8_t  data_type_tag:2;
	uint16_t memory_pool_index;
	union {
		int16_t data[AUDIO_BLOCK_SAMPLES];
		int32_t data_int32[AUDIO_BLOCK_SAMPLES];
		float   data_float[AUDIO_BLOCK_SAMPLES];
	};
} audio_block_t;
and maybe
Code:
#define AudioMemory(num) ({ \
	static DMAMEM audio_block_t data[num]; \
	AudioStream::initialize_memory(data, num, data_type::INT16); \
})

#define AudioMemory(num, type) ({ \
	static DMAMEM audio_block_t data[num]; \
	AudioStream::initialize_memory(data, num, type); \
})

Problem is how to use the audio memory pool with different type of blocks, cleanest solution might be to separate DMA and DSP pools.

Anyway, you are backwards compatible right away, but then you have to first implement the I2S input and output and the DMA, and then some converters which you then use to connect/patch between AudioInputI2S(INT32) and other yet 16-bit library functions (or possible 32-bit floating point library functions in future) and again between them and AudioOutputI2S(INT32). It's duable.
 
I'd say, if the lib ist not needed - for 24Bit It's not usable at the moment - it makes more sense to omit it, take the i2s input modify it for 24 Bit, and write to an array. Done.
 
Status
Not open for further replies.
Back
Top