chuckkh
Posts: 55
Joined: Fri Apr 01, 2016 8:03 am

Please help! I2S wrong speed

Mon May 09, 2016 12:23 pm

Please help!

I will include lots of detail so you can see that I've done my work, but what I'm really asking about is how to manually force ALSA SoC to play particular settings over I2S, without having it automatically detected by the soundcard driver! Down with automatic setup! We want manual! :)


I attached a 12.288 MHz external crystal oscillator to my Pi and changed my codec's settings accordingly, and the audio was still playing fast. And it apparently used enough current that my Wifi stopped working. I removed it and went back to driving the codec's master clock with the 19.2 MHz Pi oscillator/2 = 9.6 MHz.

My latest attempt was to restrict bcm2835-i2s.c to only allow 16 bits at 44100 SPS. I suspected that, since I'm manually setting up the codec and ALSA doesn't know what the codec is actually expecting, it was resampling or changing something. Here is an A440 tone generated by Audacity on the Pi, recorded being played by Pi with the codec (it is looped, so you can't tell, but it does indeed play 10 seconds of theoretical sound in 3-4 seconds). Also note the periodic skipping sounds, like dropouts, but periodic, which make me wonder if samples are being delayed or skipped or something. I could only record the left side; it is in fact playing stereo, but the periodic skips seem to not be the same on left and right.

I'm using Carla's version of Plugh's famous simple card loader, with filenames to match my system. See below.
See below for the relevant section of my edited version of bcm2835-i2s.c, where you can see that I've instructed ALSA to only use 44100/16 bits.

The codec is an SGTL5000, in the form of a Teensy Audio Adapter. The only thing I'm doing differently than the Teensy default driver is that I'm using the SGTL5000's internal PLL to divide this 9.6 MHz clock; the Teensy default is for all clocks to be provided by Teensy, i.e. slave mode for the sgtl5000. Since the other circuitry is the same as the Teensy audio library default, I'm using its default settings to configure the SGTL5000 EXCEPT for the PLL. The datasheet says to first set the frequency of the PLL, then power it and its VCO up, then instruct SGTL5000 to use the PLL output as clock source. So my setup file is that of the Teensy Audio library except:
0x0032 = PLL frequency, set correctly according to datasheet
0x0030 = 0x45FF analog power setting, the 5 means to start the PLL and its VCO
0x0004 = 0x0007 meaning to use the PLL as clock source and that SR is 44100
0x0006 = 0x01B0 meaning 1. bit clock is 32x FS; 2. sgtl5000 is clock master; 3. 16 bits per sample; 4. I2S mode with normal bit and frame polarity

In summary, the codec is expecting I2S at 44100 SPS, 16 bits, stereo, normal bit and frame polarity, and 32 bit clocks per LR clock. BUT note that I have tried all of the possible combinations of these settings and never with the correct result. If I double the bit clock to 64xFS, it plays even faster!

I have complete control over what the codec expects from the SoC; but, as I'm using this simple card loader, with dummy status, I don't know where to look to know what ALSA is actually sending. The uncertainty all lies in ALSA SoC's I2S. I thought I could resolve it by restricting the dummy card to 16 bits at 44.1 K, but it makes no difference.

COULD IT BE that, as the platform is the timing slave, I don't need to use a bit clock ratio of 50, but only 32? Still, the audio is playing 2.6667 times too fast, and 50 is not 2.6667 times 32. What else could be wrong?

Please help! I'm out of ideas!

If no one can help me get this, maybe someone could offer some info regarding getting ASoC to use the SGTL5000 codec driver provided by the manufacturer; unfortunately, my attempts to get Carla/Plugh's loader to use that driver instead of a dummy failed. None of Florian's examples in linux/sound/soc/bcm seem to use a separate codec driver, and it's a bit unclear how to use them as models.

A 440 played fast on RPi codec:
http://irinasicharlie.com/rpi-audio/audio-from-pi.wav

bcm2835-i2s.c changed parts:

Code: Select all

static struct snd_soc_dai_driver bcm2835_i2s_dai = {
	.name	= "bcm2835-i2s",
	.probe	= bcm2835_i2s_dai_probe,
	.playback = {
		.channels_min = 2,
		.channels_max = 2,
		.rates =	SNDRV_PCM_RATE_44100,
		.formats =	SNDRV_PCM_FMTBIT_S16_LE
		},
	.capture = {
		.channels_min = 2,
		.channels_max = 2,
		.rates =	SNDRV_PCM_RATE_44100,
		.formats =	SNDRV_PCM_FMTBIT_S16_LE
		},
	.ops = &bcm2835_i2s_dai_ops,
	.symmetric_rates = 1
};
.................................
static struct snd_pcm_hardware bcm2835_pcm_hardware = {
	.info			= SNDRV_PCM_INFO_INTERLEAVED |
				  SNDRV_PCM_INFO_JOINT_DUPLEX,
	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
	.period_bytes_min	= 32,
	.period_bytes_max	= 64 * PAGE_SIZE,
	.periods_min		= 2,
	.periods_max		= 255,
	.buffer_bytes_max	= 128 * PAGE_SIZE,
};
Loader:

Code: Select all

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/platform_device.h>
#include <sound/simple_card.h>

void device_release_callback(struct device *dev) { /* do nothing */ };

static struct asoc_simple_card_info snd_rpi_simple_card_info = {
.card = "snd_rpi_simple_card", // -> snd_soc_card.name
.name = "simple-card_codec_link", // -> snd_soc_dai_link.name
.codec = "snd-soc-dummy", // "dmic-codec", // -> snd_soc_dai_link.codec_name
// .platform = "bcm2708-i2s.0", // -> snd_soc_dai_link.platform_name
.platform = "3f203000.i2s",
.daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |SND_SOC_DAIFMT_CBM_CFM,
.cpu_dai = {
//	 .name = "bcm2708-i2s.0", // -> snd_soc_dai_link.cpu_dai_name
.name = "3f203000.i2s", // -> snd_soc_dai_link.cpu_dai_name
//.fmt = 
.sysclk = 0 },
.codec_dai = {
.name = "snd-soc-dummy-dai", //"dmic-codec", // -> snd_soc_dai_link.codec_dai_name
//.fmt = 0,
.sysclk = 0 },
};
static struct platform_device snd_rpi_simple_card_device = {
.name = "asoc-simple-card", //module alias
.id = 0,
.num_resources = 0,
.dev = { .release = &device_release_callback,
.platform_data = &snd_rpi_simple_card_info, // *HACK ALERT*
},
};
static struct platform_device snd_rpi_codec_device = {
.name = "sgtl5000-codec", // "dmic-codec", //module alias
.id = -1,
.num_resources = 0,
.dev = { .release = &device_release_callback,
},
};

int hello_init(void)
{
const char *dmaengine = "bcm2708-dmaengine"; //module name
int ret;

ret = request_module(dmaengine);
pr_alert("request module load '%s': %d\n",dmaengine, ret);

//	ret = platform_device_register(&snd_rpi_codec_device);
//	pr_alert("register platform device '%s': %d\n",snd_rpi_codec_device.name, ret);

ret = platform_device_register(&snd_rpi_simple_card_device);
pr_alert("register platform device '%s': %d\n",snd_rpi_simple_card_device.name, ret);

pr_alert("Hello World :)\n");
return 0;
}
void hello_exit(void)
{// you'll have to sudo modprobe -r the card & codec drivers manually (first?)
platform_device_unregister(&snd_rpi_simple_card_device);
platform_device_unregister(&snd_rpi_codec_device);
pr_alert("Goodbye World!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_DESCRIPTION("ASoC simple-card I2S setup");
MODULE_AUTHOR("Plugh Plover");
MODULE_LICENSE("GPL v2");
[/CODE
Some system info:
[CODE]
[email protected]:~ $ uname -r
4.4.7-rt16-v7+


[email protected]:~ $ cat /proc/asound/card0/pcm0p/sub0/hw_params 
access: RW_INTERLEAVED
format: S16_LE
subformat: STD
channels: 2
rate: 44100 (44100/1)
period_size: 1102
buffer_size: 5510
[email protected]:~ $ cat /proc/asound/card0/pcm0p/sub0/info
card: 0
device: 0
subdevice: 0
stream: PLAYBACK
id: simple-card_codec_link snd-soc-dummy-dai-0
name: 
subname: subdevice #0
class: 0
subclass: 0
subdevices_count: 1
subdevices_avail: 0
[email protected]:~ $ 
Please help me!

Thanks!

-Chuckk

chuckkh
Posts: 55
Joined: Fri Apr 01, 2016 8:03 am

Re: Please help! I2S wrong speed

Mon May 09, 2016 1:03 pm

NB I play the same A440 wave file using internal audio and it plays at the correct speed and frequency! It is only wrong using I2S.

chuckkh
Posts: 55
Joined: Fri Apr 01, 2016 8:03 am

Re: Please help! I2S wrong speed

Mon May 09, 2016 3:16 pm

I see that the Raspberry Pi 3 has some timing bugs, it does not do what it's supposed to do.
I don't know, could this be related to I2S playing at the wrong speed?
viewtopic.php?f=44&t=147142
https://github.com/RPi-Distro/repo/issues/22
viewtopic.php?f=63&t=138162

Whether it is or not, it doesn't fill me with confidence. But has anyone successfully used I2S audio on Raspberry Pi 3? Any bugs?

Something I don't get, if the codec is clock master and RPi I2S is supposedly obeying the bit clock and LR clock sent by the codec, would any of the RPi's internal timing matter? Isn't it supposed to just send the data when it's requested? I mean, if the LR clock changes, shouldn't it jump to the next sample regardless of whether it's finished sending the current one? I think the whole point of using I2S instead of PCM is that it's constantly being synchronized twice per sample, no?

I can try timing the GPIO input of the bit clock and LR clock. I also thought of testing with a simple sine wave on Arduino. I'll post if I manage to pull off either of those...

Another thought: At least 3 of the sound programs I want to use have APIs that I can use to manually manipulate their audio output: Csound, Pure Data, and Fluidsynth. Would it be feasible to use GPIO input in my program to read bit and LR clocks from the codec and just manually send the bits from the buffers? I saw some stuff about I2S in the ARM peripherals datasheet; seems it has hardware support that I might be able to access and forego ALSA altogether.


-Chuckk

tonsmets
Posts: 6
Joined: Mon May 09, 2016 8:11 am

Re: Please help! I2S wrong speed

Tue May 17, 2016 6:29 am

Hi chuckkh,

After reading your posts again I think you are headed in the right direction. The 16 bits per sample and the FS (LRCLOCK) of 44.1kHz should be fine, but the clock speed (BITCLOCK) that should be fed into the Raspberry Pi should be lower. When I make a quick calculation I get to: 16 bits * 2 channels * 44.1kHz clock = 1.411.200Hz. I don't know if your DAC is able to provide this.

You also stated that you didn't know if the internal clock mattered. As a matter of fact: it doens't. At least, that's what I found out. If the Pi is a slave (FSM =1 and CLKM = 1) it will simply lock onto the incoming clock signals. The datasheet of the BCM2835 (page 121) states two different methods of synchronization. Set all the channel settings according to the settings the DAC uses, or let the BCM2835 decide for itself by setting the frame length to 0. Note that this will cost you a clock cycle.

I hope this information will help you and that I'm not stating the obvious things you already know.

Good luck!

chuckkh
Posts: 55
Joined: Fri Apr 01, 2016 8:03 am

Re: Please help! I2S wrong speed

Tue May 17, 2016 6:25 pm

Hi. Thanks for looking it over.
I'm sending a 9.6 MHz signal from the Pi to the codec, as an internal timer for the codec, and it is using its PLL to change that 9.6 MHz to something like 180 MHz, from which it derives 32x44100. The 9.6 MHz is not the bit clock and is not being sent to the Pi. I bought a crystal oscillator tuned to work perfectly for 48k, but it uses a lot of current and it didn't change the speed issue, so I removed it for now.

With my setup, ALSA doesn't even know that there's actually an audio chip, it just knows to send its data according to the clocks. I wonder if somehow the programs I'm using to play audio (aplay and Audacity) don't know about these clocks. But they should still obey ALSA, right? I'm really lost.

I did successfully use an Arduino as a logic analyzer and read that the clock signals from the audio codec are correct and proportionate to each other, so I know 100% that the problem is coming from the Pi. Next I will use a test program I found to send "0xa0a0a0a0" repeatedly without using ALSA, and see if that reads correctly. I tried earlier and it didn't run correctly. I believe I'm pretty close. I will be able to set up I2S on both the codec and the Pi *manually*, the way Linux used to work, and the audio programs only need to write to the TX FIFO with a buffer to keep them from getting too far ahead or behind. But I gotta actually do it before I pat myself on the back.

Chuckk

rufus.x.sarsaparilla
Posts: 4
Joined: Thu May 12, 2016 12:38 am

Re: Please help! I2S wrong speed

Wed May 18, 2016 6:51 am

Hi Chuck. I suspect your problem may have to do with sample rates. The higher tone you are hearing is not the only one in the wav file. It's just the loudest. There is a lower frequency too, about one-third the frequency of the one that you have been hearing (lower even than your 440). So, what is this lower tone? I think that it is the actual fundamental frequency. Why it is so attenuated I don't know, but distortion of the sound could have caused this along with the upper 3rd partial. So, maybe the sound is actually lower, not higher. I did a little bit of arithmetic to test my hypothesis:

Code: Select all

   44100/48000 = 0.91875 
   440 Hz * 0.91875 = 404.25 Hz (perhaps the lower tone)
   404.25 Hz is G "half-sharp" about 147 cents below A 440 Hz
   404.25 Hz * 3 = 1212.75 Hz (likely the tone you are hearing)
   A 880 Hz is an octave, or 1200 cents, above A 440 Hz
   1212.75 Hz is about 555 cents higher than 880 Hz 
   1212.75 Hz amounts to D "half-sharp"
   D is about 1175 Hz, exactly 500 cents above 880 Hz 
Listen for the G "half-sharp" 404.25 Hz. That may be a hint of what may be going wrong. The D "half-sharp" 1212.75 Hz may just be a noisy distraction. Let me know what you think.

chuckkh wrote:Please help!

I will include lots of detail so you can see that I've done my work, but what I'm really asking about is how to manually force ALSA SoC to play particular settings over I2S, without having it automatically detected by the soundcard driver! Down with automatic setup! We want manual! :)


I attached a 12.288 MHz external crystal oscillator to my Pi and changed my codec's settings accordingly, and the audio was still playing fast. And it apparently used enough current that my Wifi stopped working. I removed it and went back to driving the codec's master clock with the 19.2 MHz Pi oscillator/2 = 9.6 MHz.

My latest attempt was to restrict bcm2835-i2s.c to only allow 16 bits at 44100 SPS. I suspected that, since I'm manually setting up the codec and ALSA doesn't know what the codec is actually expecting, it was resampling or changing something. Here is an A440 tone generated by Audacity on the Pi, recorded being played by Pi with the codec (it is looped, so you can't tell, but it does indeed play 10 seconds of theoretical sound in 3-4 seconds). Also note the periodic skipping sounds, like dropouts, but periodic, which make me wonder if samples are being delayed or skipped or something. I could only record the left side; it is in fact playing stereo, but the periodic skips seem to not be the same on left and right.

I'm using Carla's version of Plugh's famous simple card loader, with filenames to match my system. See below.
See below for the relevant section of my edited version of bcm2835-i2s.c, where you can see that I've instructed ALSA to only use 44100/16 bits.

The codec is an SGTL5000, in the form of a Teensy Audio Adapter. The only thing I'm doing differently than the Teensy default driver is that I'm using the SGTL5000's internal PLL to divide this 9.6 MHz clock; the Teensy default is for all clocks to be provided by Teensy, i.e. slave mode for the sgtl5000. Since the other circuitry is the same as the Teensy audio library default, I'm using its default settings to configure the SGTL5000 EXCEPT for the PLL. The datasheet says to first set the frequency of the PLL, then power it and its VCO up, then instruct SGTL5000 to use the PLL output as clock source. So my setup file is that of the Teensy Audio library except:
0x0032 = PLL frequency, set correctly according to datasheet
0x0030 = 0x45FF analog power setting, the 5 means to start the PLL and its VCO
0x0004 = 0x0007 meaning to use the PLL as clock source and that SR is 44100
0x0006 = 0x01B0 meaning 1. bit clock is 32x FS; 2. sgtl5000 is clock master; 3. 16 bits per sample; 4. I2S mode with normal bit and frame polarity

In summary, the codec is expecting I2S at 44100 SPS, 16 bits, stereo, normal bit and frame polarity, and 32 bit clocks per LR clock. BUT note that I have tried all of the possible combinations of these settings and never with the correct result. If I double the bit clock to 64xFS, it plays even faster!

I have complete control over what the codec expects from the SoC; but, as I'm using this simple card loader, with dummy status, I don't know where to look to know what ALSA is actually sending. The uncertainty all lies in ALSA SoC's I2S. I thought I could resolve it by restricting the dummy card to 16 bits at 44.1 K, but it makes no difference.

COULD IT BE that, as the platform is the timing slave, I don't need to use a bit clock ratio of 50, but only 32? Still, the audio is playing 2.6667 times too fast, and 50 is not 2.6667 times 32. What else could be wrong?

Please help! I'm out of ideas!

If no one can help me get this, maybe someone could offer some info regarding getting ASoC to use the SGTL5000 codec driver provided by the manufacturer; unfortunately, my attempts to get Carla/Plugh's loader to use that driver instead of a dummy failed. None of Florian's examples in linux/sound/soc/bcm seem to use a separate codec driver, and it's a bit unclear how to use them as models.

A 440 played fast on RPi codec:
http://irinasicharlie.com/rpi-audio/audio-from-pi.wav

bcm2835-i2s.c changed parts:

Code: Select all

static struct snd_soc_dai_driver bcm2835_i2s_dai = {
	.name	= "bcm2835-i2s",
	.probe	= bcm2835_i2s_dai_probe,
	.playback = {
		.channels_min = 2,
		.channels_max = 2,
		.rates =	SNDRV_PCM_RATE_44100,
		.formats =	SNDRV_PCM_FMTBIT_S16_LE
		},
	.capture = {
		.channels_min = 2,
		.channels_max = 2,
		.rates =	SNDRV_PCM_RATE_44100,
		.formats =	SNDRV_PCM_FMTBIT_S16_LE
		},
	.ops = &bcm2835_i2s_dai_ops,
	.symmetric_rates = 1
};
.................................
static struct snd_pcm_hardware bcm2835_pcm_hardware = {
	.info			= SNDRV_PCM_INFO_INTERLEAVED |
				  SNDRV_PCM_INFO_JOINT_DUPLEX,
	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
	.period_bytes_min	= 32,
	.period_bytes_max	= 64 * PAGE_SIZE,
	.periods_min		= 2,
	.periods_max		= 255,
	.buffer_bytes_max	= 128 * PAGE_SIZE,
};
Loader:

Code: Select all

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/platform_device.h>
#include <sound/simple_card.h>

void device_release_callback(struct device *dev) { /* do nothing */ };

static struct asoc_simple_card_info snd_rpi_simple_card_info = {
.card = "snd_rpi_simple_card", // -> snd_soc_card.name
.name = "simple-card_codec_link", // -> snd_soc_dai_link.name
.codec = "snd-soc-dummy", // "dmic-codec", // -> snd_soc_dai_link.codec_name
// .platform = "bcm2708-i2s.0", // -> snd_soc_dai_link.platform_name
.platform = "3f203000.i2s",
.daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |SND_SOC_DAIFMT_CBM_CFM,
.cpu_dai = {
//	 .name = "bcm2708-i2s.0", // -> snd_soc_dai_link.cpu_dai_name
.name = "3f203000.i2s", // -> snd_soc_dai_link.cpu_dai_name
//.fmt = 
.sysclk = 0 },
.codec_dai = {
.name = "snd-soc-dummy-dai", //"dmic-codec", // -> snd_soc_dai_link.codec_dai_name
//.fmt = 0,
.sysclk = 0 },
};
static struct platform_device snd_rpi_simple_card_device = {
.name = "asoc-simple-card", //module alias
.id = 0,
.num_resources = 0,
.dev = { .release = &device_release_callback,
.platform_data = &snd_rpi_simple_card_info, // *HACK ALERT*
},
};
static struct platform_device snd_rpi_codec_device = {
.name = "sgtl5000-codec", // "dmic-codec", //module alias
.id = -1,
.num_resources = 0,
.dev = { .release = &device_release_callback,
},
};

int hello_init(void)
{
const char *dmaengine = "bcm2708-dmaengine"; //module name
int ret;

ret = request_module(dmaengine);
pr_alert("request module load '%s': %d\n",dmaengine, ret);

//	ret = platform_device_register(&snd_rpi_codec_device);
//	pr_alert("register platform device '%s': %d\n",snd_rpi_codec_device.name, ret);

ret = platform_device_register(&snd_rpi_simple_card_device);
pr_alert("register platform device '%s': %d\n",snd_rpi_simple_card_device.name, ret);

pr_alert("Hello World :)\n");
return 0;
}
void hello_exit(void)
{// you'll have to sudo modprobe -r the card & codec drivers manually (first?)
platform_device_unregister(&snd_rpi_simple_card_device);
platform_device_unregister(&snd_rpi_codec_device);
pr_alert("Goodbye World!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_DESCRIPTION("ASoC simple-card I2S setup");
MODULE_AUTHOR("Plugh Plover");
MODULE_LICENSE("GPL v2");
[/CODE
Some system info:
[CODE]
[email protected]:~ $ uname -r
4.4.7-rt16-v7+


[email protected]:~ $ cat /proc/asound/card0/pcm0p/sub0/hw_params 
access: RW_INTERLEAVED
format: S16_LE
subformat: STD
channels: 2
rate: 44100 (44100/1)
period_size: 1102
buffer_size: 5510
[email protected]:~ $ cat /proc/asound/card0/pcm0p/sub0/info
card: 0
device: 0
subdevice: 0
stream: PLAYBACK
id: simple-card_codec_link snd-soc-dummy-dai-0
name: 
subname: subdevice #0
class: 0
subclass: 0
subdevices_count: 1
subdevices_avail: 0
[email protected]:~ $ 
Please help me!

Thanks!

-Chuckk

Return to “Interfacing (DSI, CSI, I2C, etc.)”