Problem using AudioInputI2Sslave and AudioOutputUSB together

Status
Not open for further replies.

faboo

Member
Hi there,

Following my previous post, I did some tests and found a minimal non-working example which I provide here.
The setup is as follow:

  • Rpi3 (which provides I2S signal to the teensy)
  • Teensy 4.0
  • USB cable between the Teensy and my PC
  • Audacity on PC to record audio
I run the following code:
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>

AudioInputI2S            i2s1;     // GOOD output
//AudioInputI2Sslave       i2s1;    // BAD output

AudioSynthWaveformSine   sine1;          
AudioOutputUSB           usb1;           
AudioConnection          patchCord1(sine1, 0, usb1, 0);
AudioConnection          patchCord2(sine1, 0, usb1, 1);

void setup() {
  AudioMemory(20);
  sine1.frequency(1000);
  sine1.amplitude(0.7);
}

void loop(){  
}

I record the sound on Audacity, and get what is expected:

sine1.jpg

Now, I switch from AudioInputI2S to AudioInputI2Sslave.
I play some mp3 on the Rpi3 in order to provide I2S master signal.
I record the sound on Audacity, and this is what I get: :confused:

sine2.jpg

(Note that I don't even use the sound coming from the pi, by just changing the type of i2s1 the usb tranfer gets messed up.)

Can someone help me with that?

Thanks!
 
Hi,
This is just a suggestion - I haven't tried it. The AudioOutputUSB object doesn't cause the library to update, but adding the AudioInputI2S does so that's why that version works. Perhaps the AudioInputI2Sslave is not causing the library to update as it should. Try adding another dummy output object - e.g. an AudioOutputI2S?
All the best,
Alan
 
Hi Alank,

Thanks for your suggestion!
I just tried (and also with other types of output) but no... :(

I'm not an expert in audio stuff, but intuitively, I feel like some sort of buffer is needed to accommodate between both input and output streams...

Some ideas?
 
At this point you must check if the RPi is providing proper I2S data stream (64 data bits / frame-sync) I cannot tell you how you can get this info apart from using a oscilloscope or logic analyser. maybe there is RPI documentation?
 
OK,

Thank you WMXZ, it works. I mean yes, if I play a file which I first converted to 32bits and playback with specific parameters, then yes, the sinewave goes through perfectly. Great, thank you !!

Now, since most of audio files are in CD format (16 bits), I have to find a way on the Pi to play by default in 32bits...
This should be possible, but if someone knows how to do that on pi with raspbian, I take it!

The other solution would be, I guess, to modify the I2SInput so that it takes 32 data bits / frame-sync, but this involves modifying the library I suppose...?

Anyway, I'll try to figure out something, thanks!

[edit] Actually it does not work completely: the sinewave generated on the teensy is fine (1000Hz), but the sound from the raspberry is half the speed!

[edit] it really much depends on the format of the file played on the raspberry. I'll come back and share my solution when if I get one! The idea is to redirect every sound played on the RPi so that it is spit out as stereo 32bits 44100hz over I2S. Any suggestions are much welcome!
 
Last edited:
The first AudioStream object to be initialized in the code that is capable of driving the update mechanism will be the one
selected to do it, so make sure the AudioOutputI2S dummy object occurs before the the other objects.

The update mechanism drives the entire audio lib workflow and should be activated every 2.9ms - those I/O stream types which
are interrupt driven are marked as able to update, and the constructor will install such as the updater if there's currently
no updater. This means all the interrupt driven stream types that can update must trigger in lock-step, it sounds like the
USB stream doesn't do this, it just buffers samples as they arrive, in the expectation the rates match.

Not entirely sure though.

The RPi is sending 32bits for each LRCLK period. Is that the cause of the problem you think?
I2S in general supports 32/48/64 bitclocks per LRCLK IIRC - but check in the datasheet that the SGTL5000 supports
32bit (16 per sample) and that the RPi is running at 44100 SPS.

The audio lib can be a bit confusing if you don't know how the workflow is driven behind the scenes, in particular
the requirement that there be at least one AudioStream object that is interrupt-driven can catch you out - forget this
and nothing ever hapens. The other thing to beware of is that as soon as the constructor runs for the first such
stream object the whole library is activated (typically before AudioMemory is initialized), although typically
nothing interesting happens till the AudioMemory is initialised as no blocks are allocated till then.

When writing your own audio lib classes you have to handle the case where no blocks exist, allocate() returns NULL
and the incoming streams are all NULL blocks too - forget this and the class may crash, but not for all uses, depending
on when AudioMemory is called.
 
Hi MarkT,

Thanks for your detailed answer. I've tried with an AudioOutputI2S dummy object declared in first position, but the problem is then that it forces the Teensy in Master mode, and then the sound I get from the Pi through I2S is distorted (although the sinewave generated on the teensy is fine).

When writing your own audio lib classes you have to handle the case where no blocks exist, allocate() returns NULL
and the incoming streams are all NULL blocks too - forget this and the class may crash, but not for all uses, depending
on when AudioMemory is called.
Well, I'm far from being able to write my own lib classes :D, but i'll try to dig further in it. Maybe my problem is not that difficult to solve. But in my opinion, the I2S input interface should be able to handle different formats: if it can take in 32 bits, it should be able to take in 24, 16, and 8, filling the rest with zeros (at least that's how I see things in my simple world :) )

cheers
 
Well, I'm far from being able to write my own lib classes :D, but i'll try to dig further in it. Maybe my problem is not that difficult to solve. But in my opinion, the I2S input interface should be able to handle different formats: if it can take in 32 bits, it should be able to take in 24, 16, and 8, filling the rest with zeros (at least that's how I see things in my simple world :) )

cheers
writing your own lib is easy:
So take, say I2S input interface, you will see that there are two parts, one that is configuring the I2S interface, the other is a DMA.
Now, you have two options: understand the the two parts, that are really low level hardware interfaces. OR get hold of an early TD (maybe asking Paul on for a link to TD 1.37 (the last one with 16 bit stereo I2S). maybe someone else on forum knows how to construct link?) Not to use it but to see the differences and modify code.
 
Ok, thank you for the hint!
I will first try to output everything in 32bits on the Pi side (I found that it is possible to define "audio plugins" with ALSA), in this way, I should be able to play both 32 and 64 bits / frame with the current version of AudioInputI2S.

cheers
 
First, a caveat. All my Audio Library experience is with T3.x devices, never used a T4.x.

That being said, the AudioInputI2Sslave is indeed capable of having responsibility for triggering the update of all audio objects every ~2.9ms. This is easily confirmed by looking at the library source code.

Next, the "modern" implementation of the Audio Library uses a I2S BCK rate of 64 * Fs. Meaning there are 32 samples each for the Left and Right channel, or a total of 64 data bits for every cycle of LRCK.

However, the library only uses the upper 16 data bits of each Left / Right sample. The lower 16 bits of each sample are ignored on input and set to zero on output. By the way, CD sound is indeed 16 data bits per Left / Right sample. So, the technique works just fine by placing those 16-bit samples in the upper half of each 32-bit I2S sample.

I know less about Rpi than I do T4.x, but it seems to me the easiest thing to do is configure the Rpi (if you must have it as the I2S master) for I2S with BCK = 64 * Fs and MCK = 256 * Fs. That's the exact format used by the Teensy.

Speaking of MCK, the Teensy doesn't need it for I2S slave input or output. But, the SGTL5000 does require an MCK input to perform its function. So, make sure you supply it and make sure its frequency is 256 * Fs.

BCK, LRCK, and Data must, of course, be phase synchronous. But, there is no required phase relationship between these signals and MCK as long as they are in exactly the required frequency relationship.
 
Thanks gfvalvo for this useful details.
And thanks to all for your help, my problem is now solved.
Generally speaking, I find people on this forum very nice and ready to share and help :)

In case someone is interested in doing something similar to what I'm doing, I post here the ALSA/PulseAudio configuration I'm using on the Raspberry side. This way, whatever format are the files I play on the Pi (using Python Pygame in my case), they will automatically be converted at the default Playback device to a format which is compatible with teensy-I2S input.

.config/pulse/daemon.conf
Code:
default-sample-format = float32le
default-sample-rate = 44100
alternate-sample-rate = 44100
default-sample-channels = 2
default-channel-map = front-left,front-right
default-fragments = 4
default-fragment-size-msec = 25
resample-method = ffmpeg
enable-lfe-remixing = yes
high-priority = yes
nice-level = -11
realtime-scheduling = no
realtime-priority = 9
rlimit-rtprio = 9
daemonize = no

/etc/asound.conf
Code:
pcm.!default  {
 type plug
 slave.pcm hw
}

.asoundrc
Code:
pcm.!default {
        type plug
        slave.pcm hw
}

ctl.!default {
        type hw
        card 0
}


Best
 
Good that you have persevered and succeeded - I have been watching your posts on the Raspberry Pi forum as well - although as you have now discovered the Teensy forum is a much better place to post a query such as yours.
 
Status
Not open for further replies.
Back
Top