Forum Rule: Always post complete source code & details to reproduce any issue!
Page 2 of 2 FirstFirst 1 2
Results 26 to 43 of 43

Thread: Teensy 4 SPDIF input + ASRC

  1. #26
    Junior Member
    Join Date
    Jul 2019
    Posts
    5
    Quote Originally Posted by alex6679 View Post
    Ah, ok. I think I understand. I also think that only using the Teensy is probably the simplest solution. The funny thing is, I think that I use a setup that is very similar to the one you first planned: I use 4 Teensys. All are connected via I2S (I only use LRCLK, BCLK and the data line and don't connect MCLK) and one of the Teensys is the master. The other 3 are i2s slaves and use the incoming bitclock to also clock their spdif output. Hence the complete audio piplines of the slave Teensys are clocked by the master Teensy. Of course, I needed to make some minor changes to the audio library. Anyway, you are right, if a single Teensy can do all the work, it's easier to not add the additional dsp.
    Yeah, that's pretty cool. I started with the ADAU1401, I realized after a while that I needed SPDIF input. So I ordered a larger DSP that has an integrated transciever, a bunch of ASRCs, and a massive number of i2s lines. The board shipped, but with everything going on, it's stuck and I have no idea when it's going to come. So, I've been exploring other options. When I started, I actually looked at the Teensy, but at that time, the 4.0 had just come out, and the audio library wasn't ready yet. So anyways, that's my story. Thanks for the help!

  2. #27
    Junior Member
    Join Date
    Jun 2020
    Location
    Hereford, UK
    Posts
    7
    Hi alex6679, regarding the AsyncAudioInputSPDIF3 example that crashes the Teensy 4.1, you asked to post here, from another thread: I really stripped down my code, and the following seems to crash (or at least severely confuse) the Teensy, but if you swap out the commented lines, it works perfectly well as a SPDIF through. I'm new to Teensy, though not to Arduinos in general, so forgive me if I'm doing something daft!

    Code:
    #include <SPI.h>
    #include <Audio.h>
    #include <Wire.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    AsyncAudioInputSPDIF3    spdif_async1;   //xy=200,294
    AudioOutputSPDIF3        spdif3_1;       //xy=633,316
    AudioConnection          patchCord1(spdif_async1, 0, spdif3_1, 0);
    AudioConnection          patchCord2(spdif_async1, 1, spdif3_1, 1);
    
    //AudioInputSPDIF3         spdif3_2;       //xy=193,232
    //AudioOutputSPDIF3        spdif3_1;       //xy=633,316
    //AudioConnection          patchCord1(spdif3_2, 0, spdif3_1, 0);
    //AudioConnection          patchCord2(spdif3_2, 1, spdif3_1, 1);
    
    void setup() 
    {
      AudioMemory(12);
    }
    
    void loop() 
    {
    
    }

  3. #28
    Junior Member
    Join Date
    Apr 2020
    Location
    Colorado
    Posts
    17
    Tom and I are having similar issues, but a basic example of my problem looks like:

    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    // GUItool: begin automatically generated code
    AsyncAudioInputSPDIF3    spdif_async1;   //xy=177,236
    AudioMixer4              mixer1;         //xy=404,401
    AudioRecordQueue         queue1;         //xy=616,366
    AudioConnection          patchCord1(spdif_async1, 0, mixer1, 0);
    AudioConnection          patchCord2(spdif_async1, 1, mixer1, 1);
    AudioConnection          patchCord5(mixer1, queue1);
    // GUItool: end automatically generated code
    
    void setup(){
      AudioMemory(12);
    }
    
    void loop(){}

  4. #29
    Junior Member
    Join Date
    Apr 2020
    Location
    Colorado
    Posts
    17
    Surprisingly, this is very similar to the Examples>Audio>HardwareTesting>PassThroughAsyncSpd if example, which works for me

  5. #30
    Junior Member
    Join Date
    Jun 2020
    Location
    Hereford, UK
    Posts
    7
    Quote Originally Posted by barley View Post
    Surprisingly, this is very similar to the Examples>Audio>HardwareTesting>PassThroughAsyncSpd if example, which works for me
    Hmm, I've not tried that one. But, looking at it, the obvious difference is it passes some parameters in the AsyncAudioInputSPDIF3 declaration. Is the Audio System Design Tool just neglecting to add those to its code?

    Code:
    AsyncAudioInputSPDIF3     spdifIn(true, true, 100, 20);

  6. #31
    The main problem that caused the crash is a bug in the async spdif input. The spdif receiver is configured after the dma transfer is already started, but the receiver should be configured first. I can't remember at which point I switched that accidentally. I didn't noticed it because if the spdif output constructor is called first (as in the example), the spdif interface is configured correctly before the dma of the input starts. Anyway I fixed that. But I need to do some tests before I commit the fix to my Github repository.
    There might be another problem at the example of barley. I think the AudioRecordQueue will cause a problem, since only 12 audio blocks are allocated. I assume AudioRecordQueue object will buffer all of them and the spdif input will run out of blocks. But I am not sure about that, since I never used that object.

  7. #32
    Junior Member
    Join Date
    Jun 2020
    Location
    Hereford, UK
    Posts
    7
    Quote Originally Posted by alex6679 View Post
    if the spdif output constructor is called first (as in the example), the spdif interface is configured correctly before the dma of the input starts.
    Yep, just swapping the order seems to have sorted it for me - thanks! I was just using the order that the audio design tool code used.

  8. #33
    As far as I know, it's better to first initialize the input object, so that they are updated first during the audio processing. If the outputs are initialized first, some additional latency is introduced. But I am not 100% sure about that. But for now you could of course use that workaround.

  9. #34
    Junior Member
    Join Date
    Apr 2020
    Location
    Colorado
    Posts
    17
    There might be another problem at the example of barley. I think the AudioRecordQueue will cause a problem, since only 12 audio blocks are allocated. I assume AudioRecordQueue object will buffer all of them and the spdif input will run out of blocks. But I am not sure about that, since I never used that object.

    Switching the order also prevents the crash for me, but you're right, something is odd about the output: I use a AudioRecordQueue and AudioOutputSPDIF3 together, and there's about a 500ms(-ish) difference between the queue output and the spdif output, which causes significant latency in the wireless link.

    The same output combination used with the normal AudioInputSPDIF3 input has a latency of about 10ms. I get that the processing would add a little time, but that would affect both outputs the same, I would think.

  10. #35
    Ok, I made now some changes at the asynchronous input. It is now also a friend class of the AudioOutputSPDIF3 and uses (as the other spdif input) also config_spdif3() of the output class to configure the spdif input. I tested it again at different samping rates and, of course, also changed the order of in- and output. I commited it to my fork of the Audio library: https://github.com/alex6679/Audio.

    The files that changed are:
    async_input_spdif3.h
    async_input_spdif3.cpp
    Resampler.h
    Resampler.cpp
    output_spdif3.h

    PassThroughAsyncSpdif.ino

    Regarding your problem with the very high delay of about 500ms. I don't know if you are Ok with manually changing files of your audio library. If yes, then you could update the 6 files above. The new serial output in 'PassThroughAsyncSpdif.ino' will maybe provide a hint. The 'buffered time', the input frequency and the the group delay of the resampling filter would be interesting.
    At the moment I have no clue what could cause that large delay. Typically the the latency of the input is about 1ms and the group delay is in the same range.
    Could you also provide a sketch at which you have the problem?

  11. #36
    Junior Member
    Join Date
    Apr 2020
    Location
    Colorado
    Posts
    17
    I updated my files and have it working now! The latency issue exists if either or both the dither or noise shaping attributes are set to true. It actually starts out in sync and slowly gets further and further out of sync (up to about 500ms) with either/both attribute(s) enabled. If they are both false, it plays in real time with no issues.

  12. #37
    I am glad that it is working, but I am still wondering about that high latency in combination with dithering/noise-shaping. Is your setup quite performance demanding? Did you have a look at the processor usage? Are you sending lots of (debug?) information to the PC via serial connection? I encounter once problem, when I plotted too much debug output in the Arduino IDE serial plotter. However, I encountered noise and distortion and not a delay.

  13. #38
    Junior Member
    Join Date
    Apr 2020
    Location
    Colorado
    Posts
    17
    Alright, I had it output the info every second:

    EDIT: This is with both attributes set to false and 15 audio blocks designated. Setting both to true ups the processor usage to 8%, but the other numbers look similar.

    Click image for larger version. 

Name:	Screen Shot 2020-06-04 at 2.36.20 PM.png 
Views:	11 
Size:	166.0 KB 
ID:	20496

    There is not really any serial output, the only processing is just SPI for sending the data from the T4.0 to the NRF24L01+. I discovered that the more audio memory blocks I add, the longer the latency becomes, so that explains the multi-millisecond delay, I had 200 blocks set for audio.

    Here is a somewhat abbreviated version of the code:
    Code:
    #include <Audio.h>
    #include <SPI.h>
    #include <nRF24L01.h>
    #include <RF24.h>
    #include <EEPROM.h>
    
    #define audioBufferSize 128
    
    #define addrEEPROM 0
    #define LED_PIN 3
    #define CE_PIN 5
    #define CSN_PIN 9
    
    RF24 radio(CE_PIN, CSN_PIN);
    
    const byte pipeaddress[][6] = {"1Ad", "2Ad", "3Ad"};
    
    // GUItool: begin automatically generated code
    AsyncAudioInputSPDIF3    spdif1(false, false, 50, 20);
    AudioEffectDelay         delay2;         //xy=444,196
    AudioEffectDelay         delay1;         //xy=446,65
    AudioMixer4              mixer1;         //xy=447,333
    AudioRecordQueue         queue1;         //xy=659,328
    AudioOutputSPDIF3        spdif2;
    AudioConnection          patchCord1(spdif1, 0, mixer1, 0);
    AudioConnection          patchCord2(spdif1, 0, delay1, 0);
    AudioConnection          patchCord3(spdif1, 1, mixer1, 1);
    AudioConnection          patchCord4(spdif1, 1, delay2, 0);
    AudioConnection          patchCord5(delay2, 0, spdif2, 1);
    AudioConnection          patchCord6(delay1, 0, spdif2, 0);
    AudioConnection          patchCord7(mixer1, queue1);
    // GUItool: end automatically generated code
    
    uint8_t bufr[audioBufferSize * 2];
    
    // The last saved delay value is stored in EEPROM memory address 0.
    int del = EEPROM.read(addrEEPROM);
    
    void setup(){
      Serial.begin(115200);
      delay(100);
      
      AudioMemory(15);
      
      mixer1.gain(0,0.5);
      mixer1.gain(1,0.5);
      
      delay1.delay(0, del);
      delay2.delay(0, del);
      
      radio.begin();
      radio.setPayloadSize(32);
      radio.setAutoAck(false);
      radio.disableCRC();
      radio.setPALevel(RF24_PA_MAX);
      radio.setDataRate(RF24_2MBPS);
      radio.setRetries(0,1);
      radio.setChannel(122);
      delay(50);
      
      radio.openWritingPipe(pipeaddress[0]);
      radio.stopListening();
      delay(100);
        
      queue1.begin();
    }
    
    
    void loop(){
      if (queue1.available()){
        memcpy(bufr, queue1.readBuffer(), audioBufferSize*2);
        queue1.freeBuffer();
        for (int i=0; i<8; i++){
          radio.writeFast(&bufr[i * 32],32);
        }
      }
    }

  14. #39
    Thank you for the code and the information. The buffered time, input frequency,... are totally normal. The processor usage of the audio library is quite low, but we don't know how many recources are needed by the other tasks. I had another look at the dither and noiseshaping algorithms and tried to reproduce your problem, but I had no luck.
    You mentioned that the delay also occurs if only the dither function is activated. Here is the only difference at the algorithm between active and non-active dithering:
    Code:
    if(_dither){
        const uint32_t r0=random(1000000);
        const uint32_t r1=random(1000000);
        xnD=xn + (r0 + r1)*f2-1.f;
    }
    else {
        xnD=xn;
    }
    So I don't think that the delay is caused by the dithering alone. What do you think? Is there the chance that the delay is caused by the additional processor usage caused by the dithering + e.g. the SPI communication? If you are still motivated to find out what's going on, you could for example deactivate the dithering + add some dummy audio filter that increases the processor usage again. Maybe the delay appears also in this scenario.

  15. #40
    Junior Member
    Join Date
    Apr 2020
    Location
    Colorado
    Posts
    17
    I appreciate all of the guidance and effort you're putting into this!

    I may not have been perfectly clear about the delay, so let me re-describe my setup and the issue, for clarity's sake:

    I'm using two teensy 4.0s, each with an NRF24L01+ RF transceiver.
    - Teensy #1 is configured as the transmitter, with S/PDIF-ASYNC in, an S/PDIF out passthrough, and an AudioRecordQueue used to send the data over the RF module.
    - Teensy #2 is configured as the receiver, with an AudioPlayQueue in and S/PDIF and I2S out.

    Teensy #1 works flawlessly in and out. There is never lag in the S/PDIF out here. Teensy #2 plays perfectly with dithering/noise shaping set to false, but is the one that comes out of sync when using these, so the issue must lie somewhere in the queues. I'm not sure how you tried to reproduce the problem, but I just wanted to make it extra clear where the issue occurs. I'm honestly not worried about needing to set those attributes, as it sounds fine without them, and it is currently working as desired.

    But if it would help improve this module, I can definitely keep testing things. The dithering doesn't look like it should affect the audio output unless random() is extremely slow or something, but I don't know much about how the teensy generates pseudorandom numbers.

  16. #41
    Ok, now I understand your setup. I didn't try to reproduce exactly your setup, but had closer look at delays and compared some waveforms with and without dithering. My reasoning was that maybe I also had the delay but just never noticed it. Especially since it only builds up slowly.
    The processor load of the pseudorandom generator is also captured by the processor usage output and is part of that 3% increase that you noticed. Hence this task does not take too much time.
    You are right the dithering/noise-shaping barely makes a difference and you can just turn it off.
    I just would be interessed in the last test that I mentioned above: Does the delay also occur if dithering/noise-shaping is turned off, but processor usage of the audio pipline is increased in another way, for example by increasing the 'minHalfFilterLength' of the spdif input to 80.

  17. #42
    Junior Member
    Join Date
    Apr 2020
    Location
    Colorado
    Posts
    17
    Can confirm that setting the minHalfFilterLength to 80 caused a similar delay. Setting attenuation >100 also causes it.

  18. #43
    Thank you for testing that. I am really sure now that the delay is not caused by a bug of the spdif input. I think the following happens:
    Your program slightly exceeds the capabilities of the teensy if dither is active. The audio blocks accumulate in the queue object until all available audio blocks are allocated. The spdif input only resamples new audio data if two audio blocks are available. Therefore the processor load decreases when all audio blocks are occupied. An equilibrium is set up at which some audio data is lost from time to time. I think that would explain everything: the longer delay when more audio blocks are available and also why the delay doesn't occur at the spdif output of teensy #1. I saw that the AudioRecordQueue class has a function called 'available()', that returns the number of blocks that are currently buffered. You could use that to check if some delay is building up, but from my side no further tests are required.
    If you encounter some other problems there would be another way to debug the async spdif input, that I nearly forgot: In the async_input_spdif3.h header there is the define 'DEBUG_SPDIF_IN', which is currently commented out. You can use it to receive more debug information. Most of the information is difficult to interpret, but it will also inform you if no audio blocks are available.
    There is one thing I need to add the input. The user should be able to restrict the maximum length of the resampling filter. Currently you probably would run into problems if the spdif input frequency is higher than 48kHz, since the filter length/ processor usage would increase automatically.

Tags for this Thread

Posting Permissions

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