Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 8 of 8

Thread: queue and i2s dac it even working?

  1. #1

    queue and i2s dac it even working?

    Send audio from ADC to i2s output using queue play and dac UDA1334A.
    I don't know if it even is able to work, I tried couple of methods and Im disappointed, the sound is distorted or no sound. Im pretty sure that is my fault somewhere. Maybe someone could help me get it work?
    The UDA1334A module is working, i tested it with another example Simple Drum and quality of sound is amazing. Why queue doesn't work?
    Here is my simple initial code
    Code:
    IntervalTimer timer;
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    #include <ADC.h>
    #include <ADC_util.h>
    ADC *adc = new ADC();
    
    // GUItool: begin automatically generated code
    AudioPlayQueue           play;         //xy=1137,316
    AudioOutputI2S           i2s2Output;           //xy=1344,317
    AudioConnection          patchCord1(play, 0, i2s2Output, 0);
    AudioConnection          patchCord2(play, 0, i2s2Output, 1);
    // GUItool: end automatically generated code
    
    int16_t output = 0;
    
    
    void setup() {
      AudioMemory(20);
      pinMode(16, INPUT_DISABLE);
    
      play.setBehaviour(AudioPlayQueue::NON_STALLING);
      play.setMaxBuffers(16);
    
      timer.begin(effect, 80);
      timer.priority(128);
      //ADC0
      adc->adc0->setAveraging(16);
      adc->adc0->setResolution(12); //resolution
      adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED);
      adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED);
      adc->adc0->setReference(ADC_REFERENCE::REF_3V3);
      //ADC1
      adc->adc0->setAveraging(16);
      adc->adc1->setResolution(10); //resolution
      adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED); // change the conversion speed
      adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED); // change the sampling speed
      adc->adc1->setReference(ADC_REFERENCE::REF_3V3);
    }
    
    void dac() { //i2S output
      int16_t *dacBuffer = play.getBuffer();
      //memcpy(dacBuffer, output, 256);
      //queue1.playBuffer();
      for (int i = 0; i < 128; i++) {
        dacBuffer[i] = output;
        play.playBuffer();
      }
    }
    
    void loop() {
     dac();
    }
    
    void effect() {
     
      output = adc->adc1->analogRead(A2);
    }

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    27,705
    The ADC input is supported by the latest audio library.

    https://www.pjrc.com/teensy/gui/?info=AudioInputAnalog

    Reading it this way with the ADC library is pretty much impossible to get the sample rate to closely match the audio library sample rate. When you copy data between buffers with mismatched sample rates, all sorts of terrible things happen to the sound quality.

    But it could at least be closer. Looks like you're running the timer with 80 us interval. But the library uses 44100 Hz sample rate. So you could at least get close by changing the timer to interval of 22.676 us.

    Your function writes the latest sample to "output". Looks like the dac() function just reads the 1 sample 128 times, but doesn't do anything to wait for it to actually change. So each buffer is probably getting filled with 128 copies of the same sample. You probably need the interrupt to set a flag or counter, so the main program can detect when "output" has new data. Usually that data variable and the flag need to be declared "volatile", otherwise the compiler will optimize your code with assumptions the data stored in memory can't spontaneously change. But your interrupt does change the data, so volatile tells the compiler not to make those assumptions.

    If you do all that, you can probably get much better quality. But it probably won't be as good as AudioInputAnalog from the library, which reads the ADC at 4X the sample rate and does FIR filtering before downsampling to 44100 Hz.

  3. #3
    Quote Originally Posted by PaulStoffregen View Post
    The ADC input is supported by the latest audio library.

    https://www.pjrc.com/teensy/gui/?info=AudioInputAnalog

    Reading it this way with the ADC library is pretty much impossible to get the sample rate to closely match the audio library sample rate. When you copy data between buffers with mismatched sample rates, all sorts of terrible things happen to the sound quality.

    But it could at least be closer. Looks like you're running the timer with 80 us interval. But the library uses 44100 Hz sample rate. So you could at least get close by changing the timer to interval of 22.676 us.

    Your function writes the latest sample to "output". Looks like the dac() function just reads the 1 sample 128 times, but doesn't do anything to wait for it to actually change. So each buffer is probably getting filled with 128 copies of the same sample. You probably need the interrupt to set a flag or counter, so the main program can detect when "output" has new data. Usually that data variable and the flag need to be declared "volatile", otherwise the compiler will optimize your code with assumptions the data stored in memory can't spontaneously change. But your interrupt does change the data, so volatile tells the compiler not to make those assumptions.

    If you do all that, you can probably get much better quality. But it probably won't be as good as AudioInputAnalog from the library, which reads the ADC at 4X the sample rate and does FIR filtering before downsampling to 44100 Hz.
    Paul thank you for suggestions, I got it to work almost everything working like I except, good quality of sound.
    I have still small problem with my code.
    1. Latency between input and output is around half second, is any trick or optimisation for resolve latency problem?
    2. Clicks in sound, they are not horrible but exist.
    How I can better optimise my code?
    Here is what I got.
    clean(); is just forward input to the output.
    ring_mod(); is an pseudo octaver/modulator.
    Code:
    IntervalTimer timer;
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    // GUItool: begin automatically generated code
    AudioInputAnalog         adc1;           //xy=205.00000381469727,195.00000286102295
    AudioMixer4              mixer1;         //xy=366.25,208.25
    AudioPlayQueue           queuePlay;      //xy=488.25,435.25
    AudioRecordQueue         queueRecord;    //xy=509.25,209.25
    AudioMixer4              mixer2;         //xy=644.25,454.25
    AudioOutputI2S           i2s2Play;       //xy=828.25,454.25
    AudioConnection          patchCord1(adc1, 0, mixer1, 0);
    //AudioConnection          patchCord2(adc1, 0, mixer1, 1);
    AudioConnection          patchCord3(mixer1, queueRecord);
    AudioConnection          patchCord4(mixer1, 0, mixer2, 1);
    AudioConnection          patchCord5(queuePlay, 0, mixer2, 0);
    AudioConnection          patchCord6(mixer2, 0, i2s2Play, 0);
    AudioConnection          patchCord7(mixer2, 0, i2s2Play, 1);
    // GUItool: end automatically generated code
    
    #define     MAX_SAMPLES  128
    #define     MAX_QUEUE_SIZE 16
    
    short DMAMEM bufferIn[MAX_SAMPLES * MAX_QUEUE_SIZE];
    short DMAMEM bufferOut[MAX_SAMPLES * MAX_QUEUE_SIZE];
    int32_t record_offset = 0;
    int32_t play_offset = 0;
    int16_t audioDataInput = 0, audioDataOutput = 0;
    unsigned int pointer = 0, speed = 0;
    unsigned int time = 0;
    
    void setup() {
      Serial.begin(115200);
      AudioMemory(30);
      timer.begin(effect, 22.676);
      timer.priority(128);
      queuePlay.setBehaviour(AudioPlayQueue::NON_STALLING);
      queuePlay.setMaxBuffers(16);
    
      mixer1.gain(0, 1.0);
      mixer1.gain(1, 0);
      mixer1.gain(2, 0);
      mixer1.gain(3, 0);
    
      mixer2.gain(0, 1.0); // Fx signal
      mixer2.gain(1, 0);   // Dry signal
      mixer2.gain(2, 0);
      mixer2.gain(3, 0);
    
      queueRecord.begin();
    }
    
    void ADC_to_buffer() {
      memcpy(bufferIn + record_offset, queueRecord.readBuffer(), MAX_SAMPLES * 2);
      queueRecord.freeBuffer();
      record_offset += MAX_SAMPLES;
      if (record_offset >= (MAX_SAMPLES * MAX_QUEUE_SIZE))  record_offset = 0;
    }
    
    void buffer_to_i2s() {
      memcpy(queuePlay.getBuffer(), bufferOut + play_offset , MAX_SAMPLES * 2);
      queuePlay.playBuffer();
      play_offset += MAX_SAMPLES;
      if (play_offset >= (MAX_SAMPLES * MAX_QUEUE_SIZE)) play_offset = 0;
    }
    
    void loop() {}
    
    void effect() {
      if (queueRecord.available() >= 2) ADC_to_buffer(), buffer_to_i2s();
      clean();
      //ring_mod();
    }
    
    void clean() {
      pointer++;
      audioDataInput = bufferIn[pointer];
      bufferOut[pointer] = audioDataInput;
      if (pointer >= (MAX_SAMPLES * MAX_QUEUE_SIZE)) pointer = 0;
    }
    
    void ring_mod() {
      pointer++;
      audioDataInput = bufferIn[pointer];
      bufferOut[pointer] = audioDataOutput;
      if (pointer >= (MAX_SAMPLES * MAX_QUEUE_SIZE)) pointer = 0;
      time = 15;//map(analogRead(A15), 0, 1023, 0, 60);
      speed++;
      if (speed >= time) {
        speed = 0;
        audioDataOutput = audioDataInput;
      }
    }

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    27,705
    Quote Originally Posted by Diodac View Post
    1. Latency between input and output is around half second, is any trick or optimisation for resolve latency problem?
    Best way is to build your code as audio library classes.

    https://www.pjrc.com/teensy/td_libs_...ewObjects.html

    Using the queues is possible, but they are designed to automatically buffer (queue) audio blocks if your code doesn't run at the correct rate. By creating your own objects in the audio library way, they will get their update() function called for each block of 128 samples without the possibility of building up a queue of buffered data.

  5. #5
    Quote Originally Posted by PaulStoffregen View Post
    Best way is to build your code as audio library classes.

    https://www.pjrc.com/teensy/td_libs_...ewObjects.html

    Using the queues is possible, but they are designed to automatically buffer (queue) audio blocks if your code doesn't run at the correct rate. By creating your own objects in the audio library way, they will get their update() function called for each block of 128 samples without the possibility of building up a queue of buffered data.
    Yeah it is perfect solution, i tried it but so far without success and I used queue option. Still I didn’t understand exactly some things with Audio library.
    I have some other effects pitch shifters, reverbs, modulators, chorus and so on, it will be nice if I get it as an audio objects.
    If I get to work my ring_mod as audio object then other will be easy.

  6. #6
    I tried this code also with Teensy audio shield and get much better sound quality, clicks go away, and latency between input and output is smaller.
    Tomorrow I will share it with another effect code.

  7. #7
    Paul I created separately thread for my project.
    It is here https://forum.pjrc.com/threads/72019...LTI-FX-Project

  8. #8
    Quote Originally Posted by PaulStoffregen View Post
    Best way is to build your code as audio library classes.

    https://www.pjrc.com/teensy/td_libs_...ewObjects.html

    Using the queues is possible, but they are designed to automatically buffer (queue) audio blocks if your code doesn't run at the correct rate. By creating your own objects in the audio library way, they will get their update() function called for each block of 128 samples without the possibility of building up a queue of buffered data.
    Paul is any better solution for pick up audio from record queue and send audio to play queue than this
    Code:
    void ADC_to_buffer() {
      memcpy(bufferIn + record_offset, queueRecord.readBuffer(), MAX_SAMPLES * 2);
      queueRecord.freeBuffer();
      record_offset += MAX_SAMPLES;
      if (record_offset >= (MAX_SAMPLES * MAX_QUEUE_SIZE))  record_offset = 0;
    }
    
    void buffer_to_i2s() {
      memcpy(queuePlay.getBuffer(), bufferOut + play_offset , MAX_SAMPLES * 2);
      queuePlay.playBuffer();
      play_offset += MAX_SAMPLES;
      if (play_offset >= (MAX_SAMPLES * MAX_QUEUE_SIZE)) play_offset = 0;
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •