I2S 16Bit 8kHz Mono - too fast

For other supported Arduino products from Adafruit: Shields, accessories, etc.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
smerrett79
 
Posts: 16
Joined: Sun Aug 30, 2015 9:46 pm

I2S 16Bit 8kHz Mono - too fast

Post by smerrett79 »

Hi,

I am using the UDA1334 I2S DAC with Metro M4 and the Zero_I2S library. I am able to get the basic Sine/Cosine tone generator example working (tip for others - keep those clock and data lines short!).

I made an array of mono 8kHz 16bit sampled audio and can play it. However, I have to introduce delays to play it at the correct speed. I realise that this is perhaps because I am reading mono data but if I change

Code: Select all

#define I2S_NUM_SLOTS 1
then no output happens (with or without the introduced delay) - probably because I2S requires stereo.

I have played around with the begin() function, adding in different I2SSlotSize, fs_freq and MCLK multipliers to no avail. Could it be something to do with the clock setup registers for low sampling speeds?

I realise that I am stepping through the array of bytes +=2 each time but that is because two bytes have to be combined into a 16bit value to feed to the DAC. I used Audacity to resample at 8kHz 16bit PCM and HxD to remove the .wav header and export as C array.

Please shout if you can suggest where I should investigate next. Here is the bare minimum test (I use a timer to check how long the delayMicroseconds() needs to be to result in a total duration that matches the source audio:

Code: Select all

 #include <Arduino.h>

#include <Adafruit_ZeroI2S.h>
#include "lookdave8k.h"

/* max volume for 32 bit data */
#define VOLUME ( (1UL << 31) - 1)

// Use default pins in board variant
Adafruit_ZeroI2S i2s = Adafruit_ZeroI2S();

void setup()
{
  while (!Serial) delay(10);

  Serial.println("I2S demo");

  /* begin I2S on the default pins. 16 bit depth at
     8000 samples per second
  */
  i2s.begin(I2S_16_BIT, 8000);
  i2s.enableTx();
}

void loop()
{
  /* write the output buffers
     note that i2s.write() will block until both channels are written.
  */
  long time = millis();
  for (unsigned int i = 0; i < datalen; i += 2) {
    int sound = (rawData[i + 1] & 0xFF ) << 8 | ((rawData[i] ) & 0xFF);
    i2s.write(sound, sound);
    delayMicroseconds(120);
  }
  Serial.println(millis() - time);
  delay(2000);
}
lookdave8k.h is too large to include here so will attach.
Attachments
lookdave8k.h
lookdave8k contains the rawData array used in the main loop.
(305.34 KiB) Downloaded 11 times

User avatar
smerrett79
 
Posts: 16
Joined: Sun Aug 30, 2015 9:46 pm

Re: I2S 16Bit 8kHz Mono - too fast

Post by smerrett79 »

To clarify a little, changing the begin() function with different mclk multipliers does have an effect if you reduce it enough from the default value of 256.

The audio sample should take around 3200 milliseconds to run. With the settings above, commenting the 120 us delay out, it runs in around a third of that, at 1050 ms. If I change the mclk multiplier to 128, there is no effect on the run time to play the clip. If I reduce it to 64, the run time increase to 4200 ms (which might be expected).

However, after changing a few register settings in the SAMD51 I2S peripheral initialisation, I am still only able to get 1050 or 4200 ms run times. Nothing comes close to the 3200 ms that is needed. I'm sure more familiarity with I2S would help and I am accruing it but if any of the observed behaviours signal something in the config that I am getting wrong, please shout.

User avatar
smerrett79
 
Posts: 16
Joined: Sun Aug 30, 2015 9:46 pm

Re: I2S 16Bit 8kHz Mono - too fast

Post by smerrett79 »

I have found one setting which works - the playback lasts 3100 ms. It involves a change to Adafruit_ZeroI2S.cpp and running as 24bit instead of 16bit (so hacky).

Firstly, this section in the library's begin() function seems to be out of order:

Code: Select all

  uint32_t mckFreq = (fs_freq * mck_mult);
  uint32_t sckFreq = fs_freq * I2S_NUM_SLOTS * ((width + 1) << 3);

  uint32_t gclkval = GCLK_PCHCTRL_GEN_GCLK1_Val;
  uint32_t gclkFreq = VARIANT_GCLK1_FREQ;
  uint8_t mckoutdiv = min((gclkFreq / mckFreq) - 1, 63);
  uint8_t mckdiv = min((gclkFreq / sckFreq) - 1, 63);

  if (((VARIANT_GCLK1_FREQ / mckFreq) - 1) > 63) {
    gclkval = GCLK_PCHCTRL_GEN_GCLK4_Val;
    gclkFreq = 12000000;
  }
As mckoutdiv and mckdiv are dependent on gclkFreq, which can be changed after they are calculated (gclkFreq isn't used anywhere else in the .cpp so there is no point having the conditional after the initialisation of mckoutdiv and mckdiv.

Changing this to

Code: Select all

  uint32_t mckFreq = (fs_freq * mck_mult);
  uint32_t sckFreq = fs_freq  * I2S_NUM_SLOTS * ((width + 1) << 3);
  uint32_t gclkval = GCLK_PCHCTRL_GEN_GCLK1_Val;
  uint32_t gclkFreq = VARIANT_GCLK1_FREQ;
  
  if (((VARIANT_GCLK1_FREQ / mckFreq) - 1) > 63) {
    gclkval = GCLK_PCHCTRL_GEN_GCLK4_Val;
    gclkFreq = 12000000;
  }
  
  uint8_t mckoutdiv = min((gclkFreq / mckFreq) - 1, 63);
  uint8_t mckdiv = min((gclkFreq / sckFreq) - 1, 63);
Allows correct duration with the following sketch:

Code: Select all

#include <Arduino.h>

#include <Adafruit_ZeroI2S.h>
#include "lookdave8k.h"

/* max volume for 32 bit data */
#define VOLUME ( (1UL << 31) - 1)

// Use default pins in board variant
Adafruit_ZeroI2S i2s = Adafruit_ZeroI2S();

void setup()
{
  while (!Serial) delay(10);

  Serial.print("I2S demo. clock frequency = ");

  /* begin I2S on the default pins. 16 bit depth at
     8000 samples per second
  */
  Serial.println(i2s.begin(I2S_24_BIT, 8000, 64));
  i2s.enableTx();
}

void loop()
{
  /* write the output buffers
     note that i2s.write() will block until both channels are written.
  */
  long time = millis();
  for (unsigned int i = 0; i < datalen; i += 2) {
    int sound = ((rawData[i + 1] & 0xFF ) << 8 | ((rawData[i] ) & 0xFF) ) << 8;
    i2s.write(sound, sound);
   // delayMicroseconds(120);
  }
  Serial.println(millis() - time);
  delay(20000);
}
I still do not know how to properly configure this for mono 8kHz samples at 16bit resolution (I don't mind if the mono channel is sent to both left and right). Note that I had to set a fixed mclk multiplier - without the change to the .cpp library file this does not result in the correct speed at any multiplier. You also have to bitshift the 16bit values 8 places to the left to produce 24bit values, which isn't ideal. But at least you can comment out the delayMicroseconds() and get correct playback speed.

I may head over to Github and raise there, as something is clearly up with the library. Please could Adafruit help work out how to properly configure for these settings? My code, otherwise, would be not far off an example sketch to play real audio for the library and Adafruit would be welcome to use - if we can get the config right.

User avatar
smerrett79
 
Posts: 16
Joined: Sun Aug 30, 2015 9:46 pm

Re: I2S 16Bit 8kHz Mono - too fast

Post by smerrett79 »

So the deterrent text on the Github repo
- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use
the forums at http://forums.adafruit.com to ask questions and troubleshoot why
something isn't working as expected. In many cases the problem is a common issue
that you will more quickly receive help from the forum community. GitHub issues
are meant for known defects in the code. If you don't know if there is a defect
in the code then start with troubleshooting on the forum first.
makes me come back here to "be sure". Please could @adafruit_support_bill or @adafruit let me know?

EDIT:
Also, please could someone move this to the right support category, if required? It no longer seems linked as tightly to the DAC breakout as it does to the Metro M4.

User avatar
smerrett79
 
Posts: 16
Joined: Sun Aug 30, 2015 9:46 pm

Re: I2S 16Bit 8kHz Mono - too fast

Post by smerrett79 »

Hi @adafruit please could you let me know if there are things I should be trying/fixing before raising a Github issue?

Locked
Please be positive and constructive with your questions and comments.

Return to “Other Arduino products from Adafruit”