Running Audio Library @ 8000 Samples per Second

Status
Not open for further replies.

chillmf

Member
I have enjoyed using the audio library on my Teensy to build a software defined radio (SDR). I have added to the audio library code to include a TI3204 codec which has features which are benificial to SDR projects. And, I have successfully added a 512 bin FFT module.

I have been able to modify the library I2S input and output modules to operate at 48000 sample rate.

I would like to modify the sample rate to 8000 samples per second.

The I2S input and output modules are happy with this sample rate. I am able to feed in audio signals and get them out undamaged my doing a simple connection between input and output modules.

However, when I attempt to run the audio streams thru an FIR filter I get some huge noise bands over the entire 4 kHz audio spectrum.

I suspect the problem revolves around the requirement discussed in a 01-07-2014 post about the audio library which states that the update function must complete within 2.9 ms or 278528 cpu cycles.

Given a sample rate of 8000 hz and 128 block size, the update time would be 0.016 msec or 153600 cpu cycles.

I made a thorough review of the audio library code and I can not find where the update time rules are enforced so that I can modify the update time for the 8000 hz.

The most germane code that I have found is the function "void software_isr(void) // AudioStream::update_all()" which is in the AudioStream.cpp file.


Will you please help me by pointing me where or how I can get the audio streams to play nicely at 8000 hz?

Thanks for your patience.

Regards,

Charley
 
Perhaps you've already modified AudioStream.h for 8000 Hz?

In a perfect world, every other piece of code would automatically adapt itself based on the sample rate definition. The current code is *far* from such perfection!

At a bare minimum, you must also edit whatever input & output objects you're using. For the I2S ones, you'd start by editing config_i2s(), here:

https://github.com/PaulStoffregen/Audio/blob/master/output_i2s.cpp#L225

As you can see in the code, various values for MCLK_MULT and MCLK_DIV are defined, depending on the PLL clock frequency. All of those result in 44100 Hz (or something very close to 44100).

The sample rate is MCLK divided by 256, and the update rate is the sample rate divided by 128. So at least for master-mode I2S, everything ultimately comes from MCLK. As you fiddle with the code, if you have a frequency counter, it might be useful to have it monitor the frequency on pin 11.
 
Paul,

Thanks for your message.

I have already altered the i2s input and i2s output modules as you suggested and I am quite certain that they are working properly since I get good audio at 8000 sample rate in a straight pass thru mode that I set up by using an audio library connector to make direct data connections between the i2s input and i2s output module. I think this is working since there is no requirement for a scheduled update all call in this configuration.

However, when I put an FIR filter between the i2s input and i2s output I get wideband noise out to 22050 Hz which I suspect is generated since the audio stream is still updating at the 44100 Hz rate.

And, I did make your suggested changes to the audiostream module to set the sample rate at 8000 hz:

#define AUDIO_SAMPLE_RATE 8000.0
#define AUDIO_SAMPLE_RATE_EXACT 8000.0

I note that the AudioStream::update_all() function is controlled by the IRQ_SOFTWARE:

static void update_all(void) { NVIC_SET_PENDING(IRQ_SOFTWARE);

I have reviewed the reference manual for the processor which mentions the IRQ_SOFTWARE but does not really explain how this IRQ is defined or generated.

I have also reviewed the kenetis.h file which lists the IRQ_SOFTWARE as interrupt 95.

I expect that somewhere in this haystack a register is set so that the IRQ_SOFTWARE is triggered every 278528 cpu cycles.

However, I have not found any obvious register configuration to this value.

The closest reference that I found so far is in the kenetis.h file which is shown below:

#define ARM_DEMCR (*(volatile uint32_t *)0xE000EDFC) // Debug Exception and Monitor Control
#define ARM_DEMCR_TRCENA (1 << 24) // Enable debugging & monitoring blocks
#define ARM_DWT_CTRL (*(volatile uint32_t *)0xE0001000) // DWT control register
#define ARM_DWT_CTRL_CYCCNTENA (1 << 0) // Enable cycle count
#define ARM_DWT_CYCCNT (*(volatile uint32_t *)0xE0001004) // Cycle count register

I have not found and direct reference to these registers in the processor reference document.

Hopefully this review will jog your or one of your colleagues' memory on how the audiostream update rate is set to 44100 Hz by the IRQ_SOFTWARE.

Thanks for the help!

Charley
 
Yes, I have verified LRCLK at 8 kHz and BCLK at 256 khz using a direct measurement with digital oscilloscope.

Regards,

Charley
 
Maybe I am stupid but my question is how you can get 22kHz noise from an 8Khz sampled output ?

That might result from large steps at 8kHz showing up as overtones at higher frequencies, usually you place a lowpass filter that cuts frequences between fsample and fsample/2 , or am I totally in the dark?
 
Your point is well taken. I misstated the there is noise out to 22 khz. I have been doing a lot of testing and I was thinking of a different test setup.

I am attaching two screen shots of what I am seeing.

The 8k_pass_thru file shows the output of the codec when the output I2S module is directly connected to the I2S input module. I am feeding in a 3 khz tone into both left and right channels.

The 8k_FIR file shows the output of the codec when I placed FIR filters between the I2S input modules and the I2S modules. Each channel path has it's own FIR fliter. The FIR filters are Hilbert filters with a flat band pass between 0 and 4 KHZ. So I would expect the output spectrum from this configuration to be the same as the pass thru configuration.

I have no explanation for only one channel showing the a 3 khz tone.

You will note that each channel has a very significant noise pedastel.

My interpretation is that since the audio stream is updating every 2.9 msec, the data going to the I2S output module is being corrupted since at a 8khz sample rate the audio stream should be updating every 16 msec.

Also, I now realize a made a mistake in my original post.

I said "Given a sample rate of 8000 hz and 128 block size, the update time would be 0.016 msec or 153600 cpu cycles."

I should have said "Given a sample rate of 8000 hz and 128 block size, the update time would be 16 msec or 1536000 cpu cycles."

Thanks for keeping me honest!

Regards,

Charley 8k_pass_thru.JPG8k_withFIR.JPG



Given a sample rate of 8000 hz and 128 block size, the update time would be 0.016 msec or 153600 cpu cycles.
 
What happens if you run the FIR filter with the exact same impulse response, absolutely the same config, but with the library at the normal 44.1 kHz rate?

A FIR filter doesn't "know" the sample rate. Its corner frequency is some ratio of the sample rate, not an absolute number. So it should do the same at the normal sample rate, right?

If so, that would be something you could post here and we could try running on the normal audio boards at the normal sample rate.
 
Paul,

Thanks for your reply.

I have executed your suggestion.

The teensy project source code is at the bottom of this email. I wanted to send you the complete project file set with filter files but I can find no way to attach a zipped file with the forum tools. If you can suggest how I can send the complete file set I will be happy to do so.

I am attaching four image files that show the response of the codec / teensy system running at 44.1 khz and 8 khz.

First, there is a image which shows the system running at 44.1 khz as a straight pass thru connection between I2S input and I2S output. Then, there is an image which shows the system running at 44.1 with the Hilbert filters connected between the I2S input and I2S output. As you can see, there is no distortion generate at the 44.1 khz sample rate.

There are another two images which show the same scenarios but at 8 khz sample rate. This illustrates that along as there is no modules in the loop which does a scheduled update that all works fine. BUT, when modules with a scheduled update such as FIR filters are in the loop there are very significant signals out to the nyquist frequency for 44.1 khz. Please note that these high frequency signals are not present when the FIR filters are not in the loop.

I hope this update will motivate you to address my initial question (assertion) that since the audio steam is updated at the rate of 2.9 msec for 128 samples that this causes distortion of the 8 khz data which should be updated instead at a rate of 16 msec for 128 samples.

In particular my question is really about how the audio stream is forced to update at the 2.9 msec rate. I note that the update routines are tied to a generic software IRQ which is not well documented anywhere. I am actually surprised to see the update routines being triggered by a software IRA rather than a DMA IRQ.

Thank you for your patience!

Regards,

Charley


Here is the ino file contents:

#include "TI3204Codec.h"
#include <Audio.h>
#include <SD.h>
#include <Wire.h>
#include <SPI.h>
#include "filters.h"
#include "AudioControl.h"

#define Codec_RST 4

AudioInputI2S audioinput;

AudioFilterFIR Hilbert45_I;
AudioFilterFIR Hilbert45_Q;

AudioOutputI2S audioOutput;

TI3204Codec codec;

AudioConnection c1(audioinput, 0, Hilbert45_I, 0);
AudioConnection c2(audioinput, 1, Hilbert45_Q, 0);

AudioConnection fir_I(Hilbert45_I,0,audioOutput,0);
AudioConnection fir_Q(Hilbert45_Q,0,audioOutput,1);

//AudioConnection test_I(audioinput,0,audioOutput,0);
//AudioConnection test_Q(audioinput,1,audioOutput,1);

void setup() {
// put your setup code here, to run once:
AudioNoInterrupts();
AudioMemory(16);

pinMode(Codec_RST, OUTPUT);
digitalWrite(Codec_RST,0); // turn off codec reset
delay(3);
digitalWrite(Codec_RST,1); // turn on codec reset
delay(10);
codec.enable();



Hilbert45_I.begin(RX_hilbertm45,HILBERT_COEFFS);
Hilbert45_Q.begin(RX_hilbert45,HILBERT_COEFFS);

AudioInterrupts();

}

void loop() {


}

44k_with_FIR.JPG8k_pass_thru.JPG8k_withFIR.JPG44k_pass_thru.JPG
 
Last edited:
From what I know the update, software interrupt, is generated from the I2S input dma IRQ after every 128 sample period. So all audio updates, filters and connections will be updated at the same rate as 128 innput samples. If the I2S is running at 8kHz this should be every 16ms.

Would be interesting to have the filter coefficients and run some tests in Matlab.

One question, in the 44.1 kHz plots the signal peak is at 9kHz and for the 8kHz the peak seems to be at 2kHz, will this make any difference?
 
MLU,

I would expect that the audio library update to be triggered by a DMA IRQ associated with an I2S input or output.

I did find such a reference in the output_I2S.cpp file which is pasted below:

under "void AudioOutputI2S::isr(void)" there is a line that reads:

if (AudioOutputI2S::update_responsibility) AudioStream::update_all();

There is a similar reference in the input_I2S.cpp file.

AudioStream::update_all() is defined in the audiostream.cpp and audiostream.h files.

In audiostream.h there is a section which reads:

static void update_all(void) { NVIC_SET_PENDING(IRQ_SOFTWARE); }
friend void software_isr(void);
friend class AudioConnection;

And, in the audiostream.cpp file there is a definition of of the software_isr(void) which starts out as:

void software_isr(void) // AudioStream::update_all()
{
AudioStream *p;

ARM_DEMCR |= ARM_DEMCR_TRCENA;

Further there are calls in audiostream.cpp file which start and stop the software

such as " NVIC_ENABLE_IRQ(IRQ_SOFTWARE);" and " NVIC_DISABLE_IRQ(IRQ_SOFTWARE);".

The IRQ_SOFTWARE is enumerated in the kinetis.h file as IRQ_SOFTWARE = 94

So, clearly the audiostream update_all() function is being controlled by IRQ_SOFTWARE.

However, I have not been able to find how the IRQ_SOFTWARE is defined either by searching further into the Teensy code or by reviewing the processor documentation.

The peaks that you see in the graphics are the spectral response to the audio tones that I injected into the audio configuration. For the 44.1k sample rate configuration I set the tone to a frequency of 9 khz while for the 8k sample rate I set the tone to 2 khz. I do not think the tone frequency has any bearing on the distortion that I am see with an 8k sample rate.

Thanks for the interest and comment.

Regards,

Charley
 
The IRQ_SOFTWARE is only a index in the vectortable. The entry in this table points to software_isr() in Arduino/hardware/teensy/avr/cores/teensy3/AudioStream.cpp.
Then again, software_isr() calls the update() functions for the used audio-objects
 
Last edited:
Frank,

Thanks ever so much for your explanation of the software_isr.

Once I realized that the software_isr is really being triggered by a DMA isr in the I2Soutput module I started looking at other suspects.

I finally found that in my setup of the TI 3204 codec I had chosen a left justified i2S format. When I changed he codec to operate with an "I2S" format all plays well at 8 khz sample rate.

Regards,

Charley
 
Status
Not open for further replies.
Back
Top