Teensy 4 SPDIF input + ASRC

@Bill Glass: I was also pleasantly surprised about the low distortion and I am curious about your results. Two things that you need to take into account: When the resampler is initialized, it uses the input frequency provided by one of the spidf registers (SPDIF_SRFM). This frequency is not very accurate and within the first few seconds the ratio from input to output frequency is adjusted. So, you would need to discard the first few seconds at your evaluation. The second point is: I use a 32bit floating point version of the audio library. If you use the standard 16bit version, your noise level will be higher.

Yes, as soon as I get that connector for the spdif optical cable, I will test it and publish the results. I am a little bit pessimistic about what may happen between my 24-bit sine wav file on my USB key and the optical output of my TV but it will definitely be better than what I'm getting on my audio cable with a horrible ground loop noise. (I ordered a ground loop eliminator from China somewhere in transit).
Or were you interested in my floating point study on an asrc? It's described in a pdf file that I can try and mail to you. Here's that horrible ground loop noise (seems to be 3rd harmonic of the 50Hz ac power).

lineinSD.jpg
 
If you count the peaks there are 29 per 5kHz, or 172Hz, being the period of 2 audio blocks, nothing to do with power line
frequency (you'd expect to see mainly odd harmonics of mains, not just 3rd, 6th, 9th...)
 
If you count the peaks there are 29 per 5kHz, or 172Hz, being the period of 2 audio blocks, nothing to do with power line
frequency (you'd expect to see mainly odd harmonics of mains, not just 3rd, 6th, 9th...)

Ah, that corresponds precisely with what I'm doing. Recording every 2 audio blocks to the SDCard. Sounds like a bug? Here's my code:
Code:
#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#define RECORD_PIN 0

#define TESTLINEIN
#define DEBUG

AudioOutputI2S            i2s2;
AudioRecordQueue          queue1;
#ifdef TESTLINEIN
AudioInputI2S             i2s1;
AudioConnection           patchCord1(i2s1, 0, i2s2, 0);
AudioConnection           patchCord2(i2s1, 0, queue1, 0);
AudioConnection           patchCord3(i2s1, 1, i2s2, 1);
#else
AsyncAudioInputSPDIF3     spdifIn(false, false, 100, 20, 80);	//dither = false, noiseshaping = false, anti-aliasing attenuation=100dB, minimum half resampling filter length=20, maximum half resampling filter length=80
AudioConnection           patchCord1(spdifIn, 0, i2s2, 0);
AudioConnection           patchCord2(spdifIn, 0, queue1, 0);
AudioConnection           patchCord3(spdifIn, 1, i2s2, 1);
#endif
AudioControlSGTL5000      sgtl5000_1;     

// Bounce objects to easily and reliably read the buttons
Bounce buttonRecord = Bounce(RECORD_PIN, 8);

#ifdef TESTLINEIN
// which input on the audio shield will be used?
const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;
#endif

// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14

// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording, 2=stopRecording

// The file where data is recorded
File frec;
int nbiter=0;

#ifdef DEBUG
unsigned long last_time = millis();
#endif

void setup() {
  // Configure the pushbutton pins
  pinMode(RECORD_PIN, INPUT_PULLUP);
  
  AudioMemory(12);

  // Enable the audio shield, enable output
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
#ifdef TESTLINEIN
  sgtl5000_1.inputSelect(myInput);       //choose line-in or mic-in
#endif
    
#ifdef DEBUG
  Serial.begin(115200);
#endif
  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
#ifdef DEBUG
      Serial.println("Unable to access the SD card");
#endif
      delay(500);
    }
  }
  
}

void loop() {

#if !defined TESTLINEIN && defined DEBUG 
  double bufferedTime=spdifIn.getBufferedTime();
#endif

  // First, read the button
  buttonRecord.update();
  
  // Respond to button presses
  if (buttonRecord.fallingEdge()) 
  {
#ifdef DEBUG
    Serial.println("-------------------");
    Serial.println("Record Button Press");
    Serial.println("-------------------");
#endif
    if (mode == 0) startRecording();
  }
  else
  { 
    if (mode == 1) 
    {
      // If we'rerecording, carry on...
      continueRecording();
    }
    else
    {
      if (mode==2)
      {
        // Stop Recording
        stopRecording();
      }
    }
  }
  
#if !defined TESTLINEIN && defined DEBUG
  if (millis() - last_time >= 2500) 
  {
    Serial.print("buffered time [micro seconds]: ");
    Serial.println(bufferedTime*1e6,2);
    Serial.print("locked: ");
    Serial.println(spdifIn.isLocked());
    Serial.print("input frequency: ");
    double inputFrequency=spdifIn.getInputFrequency();
    Serial.println(inputFrequency);
    Serial.print("anti-aliasing attenuation: ");
    Serial.println(spdifIn.getAttenuation());
    Serial.print("resampling goup delay [milli seconds]: ");
    Serial.println(spdifIn.getHalfFilterLength()/inputFrequency*1e3,2);
    Serial.print("half filter length: ");
    Serial.println(spdifIn.getHalfFilterLength()); 
    double pUsageIn=spdifIn.processorUsage(); 
    Serial.print("processor usage [%]: ");
    Serial.println(pUsageIn);
    Serial.print("max number of used blocks: ");
    Serial.println(AudioMemoryUsageMax()); 
    last_time=millis();
  }
#endif

}

void startRecording() 
{
#ifdef DEBUG
  Serial.println("--------------");
  Serial.println("startRecording");
  Serial.println("--------------");
#endif
  // The SD library writes new data to the end of the
  // file, so to start a new recording, the old file
  // must be deleted before new data is written.
#ifdef TESTLINEIN
  if (SD.exists("linein.raw")) 
  {
    SD.remove("linein.raw");
  }
  frec = SD.open("linein.raw", FILE_WRITE);
#else
  if (SD.exists("spdif.raw")) 
  {
    SD.remove("spdif.raw");
  }
  frec = SD.open("spdif.raw", FILE_WRITE);
#endif
  if (frec) 
  {
    queue1.begin();
    mode = 1;
  }
}

void continueRecording() 
{
  if (queue1.available() >= 2) 
  {
    byte buffer[512];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer.  The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer+256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // write all 512 bytes to the SD card
#ifdef DEBUG1
    elapsedMicros usec = 0;
#endif
    frec.write(buffer, 512);
    // Uncomment these lines to see how long SD writes
    // are taking.  A pair of audio blocks arrives every
    // 5802 microseconds, so hopefully most of the writes
    // take well under 5802 us.  Some will take more, as
    // the SD library also must write to the FAT tables
    // and the SD card controller manages media erase and
    // wear leveling.  The queue object can buffer
    // approximately 301700 us of audio, to allow time
    // for occasional high SD card latency, as long as
    // the average write time is under 5802 us.
#ifdef DEBUG1
    Serial.print("SD write, us=");
    Serial.println(usec);
#endif
    if (nbiter<1722) nbiter++; //440832 samples (approx. 10sec) stored in SDCARD before stopRecording
    else mode=2; //stopRecording
  }
}

void stopRecording() 
{
#ifdef DEBUG
  Serial.println("-------------");
  Serial.println("stopRecording");
  Serial.println("-------------");
#endif
  queue1.end();  
  while (queue1.available() > 0) 
  {
    frec.write((byte*)queue1.readBuffer(), 256);
    queue1.freeBuffer();
  }
  frec.close();
  nbiter=0;
  mode = 0;
}
 
Nothing obviously wrong with the code to my eyes.

I suspect this is due to the SD card pausing on occasional writes - many cards will stutter occasionally for internal housekeeping,
although I'd have hoped the recordqueue buffer would be long enough to handle this, but I have seen cards take 50ms or more
for dozens of consecutive writes, which would be a problem. Perhaps you could monitor the high-water-mark of the calls to
available() to see if climbs significantly above 2.
 
Nothing obviously wrong with the code to my eyes.

I suspect this is due to the SD card pausing on occasional writes - many cards will stutter occasionally for internal housekeeping,
although I'd have hoped the recordqueue buffer would be long enough to handle this, but I have seen cards take 50ms or more
for dozens of consecutive writes, which would be a problem. Perhaps you could monitor the high-water-mark of the calls to
available() to see if climbs significantly above 2.

And the winner is 22msec but occasionally with normal values of 1.7msec.
I recorded a linein signal at 172.2256Hz (period of 256 samples at 44.1kHz) and it looks pretty ratty. I didn't have this problem when I was recording myTeensyBiq filter response last wk. but the filter input signal was stored in Teensy memory. Here's a coupla images:

linein172HzSD.jpg SDCardDelays.jpg
 
I powered up myTeensy and audio adapter with 5V on Vin instead of via the USB cable. Better but not perfect. I'm curious to find out how it will look when I get that spdif input interface.



lineinextpwr.jpg
 
And my next day discovery concerning the previous spectrum reveals occasional hiccups in that 128-sample recording queue.

lineinextpwr1.jpg
 
Observations:
1. After studying the forum, there seems to be a general problem concerning reliable recording of audio on the SD card even if reasonable average SD write latency is obtained (<2ms).
2. If I analyze the spectrum of a signal recorded between "hiccups", I observe similar results as those obtained using the USB audio (provided I ignore the higher noise floor due to USB resampling).
3. I have not yet found a reliable method to digitally analyze a signal after A/D conversion on the audio adapter board.
 
Observations:
1. After studying the forum, there seems to be a general problem concerning reliable recording of audio on the SD card even if reasonable average SD write latency is obtained (<2ms).

The problem is, sometimes the latency is *much* higher, when the card decides to do some internal operations.

Some hints:

- use a large buffer
- write in Blocks of 4096 Bytes
- use a fast card: https://forum.pjrc.com/threads/68418-Which-SD-is-Best-for-Audio-Projects

And perhaps, for SPDIF, change the audio library to the freq of SPDIF. I think, for TV it's 48kHz?
 
The problem is, sometimes the latency is *much* higher, when the card decides to do some internal operations.

Some hints:

- use a large buffer
- write in Blocks of 4096 Bytes
- use a fast card: https://forum.pjrc.com/threads/68418-Which-SD-is-Best-for-Audio-Projects

And perhaps, for SPDIF, change the audio library to the freq of SPDIF. I think, for TV it's 48kHz?

Yes, but there were, worst case, 22msec even with those last hiccups I showed. According to the comment in the record example, the buffer object can tolerate 301.7 msec. from time to time.
 
There is a problem with the length of the recording queue. If (queue1.available() >=12) it never happens. 12 corresponds to 35msec of 44100Hz samples. According to the Recorder example sketch, the queue can tolerate 301.7 msec (from time to time). I try to let it fill for 12 times and ONLY ONCE. My sketch is here and can be observed with #define BUG.

Code:
 #include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#define RECORD_PIN 0

#define TESTLINEIN
#define DEBUG
#define BUG

AudioOutputI2S            i2s2;
AudioRecordQueue          queue1;
#ifdef TESTLINEIN
AudioInputI2S             i2s1;
AudioConnection           patchCord1(i2s1, 0, i2s2, 0);
AudioConnection           patchCord2(i2s1, 0, queue1, 0);
AudioConnection           patchCord3(i2s1, 1, i2s2, 1);
#else
AsyncAudioInputSPDIF3     spdifIn(false, false, 100, 20, 80);	//dither = false, noiseshaping = false, anti-aliasing attenuation=100dB, minimum half resampling filter length=20, maximum half resampling filter length=80
AudioConnection           patchCord1(spdifIn, 0, i2s2, 0);
AudioConnection           patchCord2(spdifIn, 0, queue1, 0);
AudioConnection           patchCord3(spdifIn, 1, i2s2, 1);
#endif
AudioControlSGTL5000      sgtl5000_1;     

// Bounce objects to easily and reliably read the buttons
Bounce buttonRecord = Bounce(RECORD_PIN, 8);

#ifdef TESTLINEIN
// which input on the audio shield will be used?
const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;
#endif

// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14

// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording, 2=stopRecording

// The file where data is recorded
File frec;
int nbiter=0;

#ifdef DEBUG
unsigned long last_time = millis();
#endif

void setup() {
  // Configure the pushbutton pins
  pinMode(RECORD_PIN, INPUT_PULLUP);
  
  AudioMemory(12);

  // Enable the audio shield, enable output
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
#ifdef TESTLINEIN
  sgtl5000_1.inputSelect(myInput);       //choose line-in or mic-in
#endif
    
#ifdef DEBUG
  Serial.begin(115200);
#endif
  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
#ifdef DEBUG
      Serial.println("Unable to access the SD card");
#endif
      delay(500);
    }
  }
  
}

void loop() {

#if !defined TESTLINEIN && defined DEBUG 
  double bufferedTime=spdifIn.getBufferedTime();
#endif

  // First, read the button
  buttonRecord.update();
  
  // Respond to button presses
  if (buttonRecord.fallingEdge()) 
  {
#ifdef DEBUG
    Serial.println("-------------------");
    Serial.println("Record Button Press");
    Serial.println("-------------------");
#endif
    if (mode == 0) startRecording();
  }
  else
  { 
    if (mode == 1) 
    {
      // If we'rerecording, carry on...
#ifdef BUG
      continueRecording1();
#else
      continueRecording();
#endif
    }
    else
    {
      if (mode==2)
      {
        // Stop Recording
        stopRecording();
      }
    }
  }
  
#if !defined TESTLINEIN && defined DEBUG
  if (millis() - last_time >= 2500) 
  {
    Serial.print("buffered time [micro seconds]: ");
    Serial.println(bufferedTime*1e6,2);
    Serial.print("locked: ");
    Serial.println(spdifIn.isLocked());
    Serial.print("input frequency: ");
    double inputFrequency=spdifIn.getInputFrequency();
    Serial.println(inputFrequency);
    Serial.print("anti-aliasing attenuation: ");
    Serial.println(spdifIn.getAttenuation());
    Serial.print("resampling goup delay [milli seconds]: ");
    Serial.println(spdifIn.getHalfFilterLength()/inputFrequency*1e3,2);
    Serial.print("half filter length: ");
    Serial.println(spdifIn.getHalfFilterLength()); 
    double pUsageIn=spdifIn.processorUsage(); 
    Serial.print("processor usage [%]: ");
    Serial.println(pUsageIn);
    Serial.print("max number of used blocks: ");
    Serial.println(AudioMemoryUsageMax()); 
    last_time=millis();
  }
#endif

}

void startRecording() 
{
#ifdef DEBUG
  Serial.println("--------------");
  Serial.println("startRecording");
  Serial.println("--------------");
#endif
  // The SD library writes new data to the end of the
  // file, so to start a new recording, the old file
  // must be deleted before new data is written.
#ifdef TESTLINEIN
  if (SD.exists("linein.raw")) 
  {
    SD.remove("linein.raw");
  }
  frec = SD.open("linein.raw", FILE_WRITE);
#else
  if (SD.exists("spdif.raw")) 
  {
    SD.remove("spdif.raw");
  }
  frec = SD.open("spdif.raw", FILE_WRITE);
#endif
  if (frec) 
  {
    queue1.begin();
    mode = 1;
  }
}

void continueRecording() 
{
  if (queue1.available() >= 2) 
  {
    byte buffer[512];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer.  The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer+256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // write all 512 bytes to the SD card
#ifdef DEBUG
    elapsedMicros usec = 0;
#endif
    frec.write(buffer, 512);
    // Uncomment these lines to see how long SD writes
    // are taking.  A pair of audio blocks arrives every
    // 5802 microseconds, so hopefully most of the writes
    // take well under 5802 us.  Some will take more, as
    // the SD library also must write to the FAT tables
    // and the SD card controller manages media erase and
    // wear leveling.  The queue object can buffer
    // approximately 301700 us of audio, to allow time
    // for occasional high SD card latency, as long as
    // the average write time is under 5802 us.
#ifdef DEBUG
    Serial.print("SD write, us=");
    Serial.println(usec);
#endif
    if (nbiter<1722) nbiter++; //440832 samples (approx. 10sec) stored in SDCARD before stopRecording
    else mode=2; //stopRecording
  }
}

void continueRecording1() 
{
  int i,m=12; //12 corresponds to 35 ms of 44100Hz samples, not 301.7 ms as announced
  if (queue1.available() >= m) 
  {
    for (i=0;i<m;i++)
    {
      queue1.freeBuffer();
    }
    elapsedMicros usec = 0;
    Serial.print("SD write, us=");
    Serial.println(usec);
    mode=2; //stopRecording
  }
}

void stopRecording() 
{
#ifdef DEBUG
  Serial.println("-------------");
  Serial.println("stopRecording");
  Serial.println("-------------");
#endif
  queue1.end();  
  while (queue1.available() > 0) 
  {
    frec.write((byte*)queue1.readBuffer(), 256);
    queue1.freeBuffer();
  }
  frec.close();
  nbiter=0;
  mode = 0;
}
 
Code:
  AudioMemory(12);
BTW there are only 12 blocks available to any audio class due to this call.
 
Code:
  AudioMemory(12);
BTW there are only 12 blocks available to any audio class due to this call.

BTW, I'm the BUG. Yes, indeed. Quick test with same program, I set m to 100 blocks (301.7ms means 103.7 blocks) and I set AudioMemory(128). It happens now. I'll go look for hiccups tomorrow.

Thanx!
 
Code:
  AudioMemory(12);
BTW there are only 12 blocks available to any audio class due to this call.

OK, no more hiccups. Got me what seems to be a good method for analyzing Teensy digital audio after ADC. Thanks for your help! Attached are the spectrums of input to Audacity player sig.jpg, and SD recorded signal, linein.jpg. I am waiting for that optical spdif connecter now to see the difference. If my TV uses 48kHz, how do I configure the Teensy audio?




sig.jpg linein.jpg
 
@Bill Glass: I was also pleasantly surprised about the low distortion and I am curious about your results. Two things that you need to take into account: When the resampler is initialized, it uses the input frequency provided by one of the spidf registers (SPDIF_SRFM). This frequency is not very accurate and within the first few seconds the ratio from input to output frequency is adjusted. So, you would need to discard the first few seconds at your evaluation. The second point is: I use a 32bit floating point version of the audio library. If you use the standard 16bit version, your noise level will be higher.

Hello Alex

I am still waiting for that optical spdif connector to evaluate the quality of the received signal from my TV thru the Teensy spdif asrc input. I now have a reliable method for recording it onto the audio adapter's SD card. My question is about sample frequency. My TV may use 48kHz. Do I have to change the audio library sample frequency to 48, or does the async algorithm tolerate that large delta_fs? If I do need to change it, how? I would also be very interested in any documentation you may have concerning your asrc algorithm. It is a very interesting subject to me. Thanks!

p.s.
My address is: billglass38@gmail.com.
 
Hi,

you don't need to change the sample freuqency of the library. From 48kHz to 44.1kHz is no problem.
The the resampling algorithm, that I implemented, is quite simple. It is described here:
http://ccrma.stanford.edu/~jos/resample/resample.pdf

Thank you. I received the optical connecter today. I tried to hook it up but there seems to be a compatability issue between the spdfin pin 15 of the Teensy 4.0 and the audio adapter. I need the audio adapter for the SD card so I'll probably have to do some modifications to it. I'll let you know.
 
Thank you. I received the optical connecter today. I tried to hook it up but there seems to be a compatability issue between the spdfin pin 15 of the Teensy 4.0 and the audio adapter. I need the audio adapter for the SD card so I'll probably have to do some modifications to it. I'll let you know.

Yes, there is a capacitor on pin 15 on the shield.
You can remove it, it's for the optional potentiometer only.

See here:
https://github.com/TeensyUser/doc/wiki/Teensy-40-SPDIF-in-and-Audioshield
 
Hi,

you don't need to change the sample freuqency of the library. From 48kHz to 44.1kHz is no problem.
The the resampling algorithm, that I implemented, is quite simple. It is described here:
http://ccrma.stanford.edu/~jos/resample/resample.pdf

My results using optical spdif connector recording a 10 kHz sine wav coming out of my TV audio player OR my PC via an optical cable.

Code:
#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#define RECORD_PIN 0

//#define TESTLINEIN
#define DEBUG

AudioOutputI2S            i2s2;
AudioRecordQueue          queue1;
#ifdef TESTLINEIN
AudioInputI2S             i2s1;
AudioConnection           patchCord1(i2s1, 0, i2s2, 0);
AudioConnection           patchCord2(i2s1, 0, queue1, 0);
AudioConnection           patchCord3(i2s1, 1, i2s2, 1);
#else
AsyncAudioInputSPDIF3     spdifIn(false, false, 100, 20, 80);	//dither = false, noiseshaping = false, anti-aliasing attenuation=100dB, minimum half resampling filter length=20, maximum half resampling filter length=80
AudioConnection           patchCord1(spdifIn, 0, i2s2, 0);
AudioConnection           patchCord2(spdifIn, 0, queue1, 0);
AudioConnection           patchCord3(spdifIn, 1, i2s2, 1);
#endif
AudioControlSGTL5000      sgtl5000_1;     

// Bounce objects to easily and reliably read the buttons
Bounce buttonRecord = Bounce(RECORD_PIN, 8);

#ifdef TESTLINEIN
// which input on the audio shield will be used?
const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;
#endif

// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14

// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording, 2=stopRecording

// The file where data is recorded
File frec;
int nbiter=0;

#ifdef DEBUG
unsigned long last_time = millis();
#endif

void setup() {
  // Configure the pushbutton pins
  pinMode(RECORD_PIN, INPUT_PULLUP);
  AudioMemory(128);

  // Enable the audio shield, enable output
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
#ifdef TESTLINEIN
  sgtl5000_1.inputSelect(myInput);       //choose line-in or mic-in
#endif
    
#ifdef DEBUG
  Serial.begin(115200);
#endif
  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
#ifdef DEBUG
      Serial.println("Unable to access the SD card");
#endif
      delay(500);
    }
  }
  
}

void loop() {

#if !defined TESTLINEIN && defined DEBUG 
  double bufferedTime=spdifIn.getBufferedTime();
#endif

  // First, read the button
  buttonRecord.update();
  
  // Respond to button presses
  if (buttonRecord.fallingEdge()) 
  {
#ifdef DEBUG
    Serial.println("-------------------");
    Serial.println("Record Button Press");
    Serial.println("-------------------");
#endif
    if (mode == 0) startRecording();
  }
  else
  { 
    if (mode == 1) 
    {
      // If we'rerecording, carry on...
      continueRecording();
    }
    else
    {
      if (mode==2)
      {
        // Stop Recording
        stopRecording();
      }
    }
  }
  
#if !defined TESTLINEIN && defined DEBUG
  if (millis() - last_time >= 2500) 
  {
    Serial.print("buffered time [micro seconds]: ");
    Serial.println(bufferedTime*1e6,2);
    Serial.print("locked: ");
    Serial.println(spdifIn.isLocked());
    Serial.print("input frequency: ");
    double inputFrequency=spdifIn.getInputFrequency();
    Serial.println(inputFrequency);
    Serial.print("anti-aliasing attenuation: ");
    Serial.println(spdifIn.getAttenuation());
    Serial.print("resampling goup delay [milli seconds]: ");
    Serial.println(spdifIn.getHalfFilterLength()/inputFrequency*1e3,2);
    Serial.print("half filter length: ");
    Serial.println(spdifIn.getHalfFilterLength()); 
    double pUsageIn=spdifIn.processorUsage(); 
    Serial.print("processor usage [%]: ");
    Serial.println(pUsageIn);
    Serial.print("max number of used blocks: ");
    Serial.println(AudioMemoryUsageMax()); 
    last_time=millis();
  }
#endif

}

void startRecording() 
{
#ifdef DEBUG
  Serial.println("--------------");
  Serial.println("startRecording");
  Serial.println("--------------");
#endif
  // The SD library writes new data to the end of the
  // file, so to start a new recording, the old file
  // must be deleted before new data is written.
#ifdef TESTLINEIN
  if (SD.exists("linein.raw")) 
  {
    SD.remove("linein.raw");
  }
  frec = SD.open("linein.raw", FILE_WRITE);
#else
  if (SD.exists("spdif.raw")) 
  {
    SD.remove("spdif.raw");
  }
  frec = SD.open("spdif.raw", FILE_WRITE);
#endif
  if (frec) 
  {
    queue1.begin();
    mode = 1;
  }
}

void continueRecording() 
{
  if (queue1.available() >= 2) 
  {
    byte buffer[512];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer.  The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer+256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // write all 512 bytes to the SD card
#ifdef DEBUG1
    elapsedMicros usec = 0;
#endif
    frec.write(buffer, 512);
    // Uncomment these lines to see how long SD writes
    // are taking.  A pair of audio blocks arrives every
    // 5802 microseconds, so hopefully most of the writes
    // take well under 5802 us.  Some will take more, as
    // the SD library also must write to the FAT tables
    // and the SD card controller manages media erase and
    // wear leveling.  The queue object can buffer
    // approximately 301700 us of audio, to allow time
    // for occasional high SD card latency, as long as
    // the average write time is under 5802 us.
#ifdef DEBUG1
    Serial.print("SD write, us=");
    Serial.println(usec);
#endif
    if (nbiter<1722) nbiter++; //440832 samples (approx. 10sec) stored in SDCARD before stopRecording
    else mode=2; //stopRecording
  }
}

void stopRecording() 
{
#ifdef DEBUG
  Serial.println("-------------");
  Serial.println("stopRecording");
  Serial.println("-------------");
#endif
  queue1.end();  
  while (queue1.available() > 0) 
  {
    frec.write((byte*)queue1.readBuffer(), 256);
    queue1.freeBuffer();
  }
  frec.close();
  nbiter=0;
  mode = 0;
}

TeensySetup.jpg siginspdif.jpg spdif.jpg
 
That does really not look good. Can you have a look at the signal on the Teensy without resampling?
I only had a brief look at you code, but I think replacing the AsyncAudioInputSPDIF3 object with an AudioInputSPDIF3 and removing AudioOutputI2S should do the job.
 
What spectrum do you get for a 1kHz tone?

For the 10kHz without recording to the SD but to the USB, same " really not look good" spectrum.
But, with 10kHz tone on the audio input to the adapter and output to the USB, result was OK.

spdif1kHz.jpg
 
That does really not look good. Can you have a look at the signal on the Teensy without resampling?
I only had a brief look at you code, but I think replacing the AsyncAudioInputSPDIF3 object with an AudioInputSPDIF3 and removing AudioOutputI2S should do the job.

I had too much volume on my TV music player and I switched to external 5V supply on Teensy Vin instead of power from the USB. It looks good now. Here's the input wav file, and the SD card recorded wav file.

siginspdif.jpg spdif.jpg
 
Good, that you were able to fix the problem.

Is the results what you expected?

I hope so.

They sure look good to me.

I will probably get a transmitter optical connector for my box as well as the same company makes an FCR684205(R&T). My TV sound bar has an optical input.

I glanced on your document concerning the ASRC. If I understand, you do a linear interpolation(1st order) between 2 output samples from 2 of the closest neighboring coefficient phases? What is the L/M ratio? How many total coefficients do you have?

The one I did had a ratio of 15. I need 15x89x4 polynomial values. I calculated 89 coefficients every sample using a 3rd order polynomial as a function of a -.5T < group delay < .5T. (equivalent of 4 convolutions per sample for a FIR of 89 coefficients). With only a 1st order interpolation, I imagine you need many more coefficients but you perform only 2 convolutions per sample?
 
Back
Top