Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

supporting of different bits per sample #5244

Closed
tedhsieh1966 opened this issue Oct 14, 2018 · 20 comments · Fixed by #7835
Closed

supporting of different bits per sample #5244

tedhsieh1966 opened this issue Oct 14, 2018 · 20 comments · Fixed by #7835

Comments

@tedhsieh1966
Copy link

In https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/i2s.h , there are different type of bit width per sample supported as following.
/**

  • @brief I2S bit width per sample.

/
typedef enum {
I2S_BITS_PER_SAMPLE_8BIT = 8, /
!< I2S bits per sample: 8-bits*/
I2S_BITS_PER_SAMPLE_16BIT = 16, /!< I2S bits per sample: 16-bits/
I2S_BITS_PER_SAMPLE_24BIT = 24, /!< I2S bits per sample: 24-bits/
I2S_BITS_PER_SAMPLE_32BIT = 32, /!< I2S bits per sample: 32-bits/
} i2s_bits_per_sample_t;

However, in Arduino/cores/esp8266/i2s.h there is no description on how to use these.

Is there supporting of these bits_per_sample from cores for esp8266?
If yes, where can I find the usage documentation or examples?
If no, will there be a schedule to support them?

Thanks for any replying.

@earlephilhower
Copy link
Collaborator

Not supported now, with no expectation of adding them in. Never seen an 8-bit-only I2S DAC (thankfully!).

All the API and calls now are assuming packed 16-bits, as well as assumptions about clocking setup (reverse-engineered by multiple people) and other parts (DMA clocking/etc).

What's the use case? Never had a request from the folks using my I2S decoder library, so I'm curious...

@tedhsieh1966
Copy link
Author

Thanks for replying. There are hifi audio with 96k, 192k and 384k sampling rate and 24bis or 32 bits per sample.

@devyte
Copy link
Collaborator

devyte commented Nov 20, 2018

@earlephilhower is this request valid? Is it something you'd consider as an enhancement for your audio lib?

@devyte devyte added the waiting for feedback Waiting on additional info. If it's not received, the issue may be closed. label Nov 20, 2018
@earlephilhower
Copy link
Collaborator

It's definitely possible, but would require a significant change to the userspace API. The actual DMA/etc. doesn't care as long as you set the I2S config appropriately. Given that the I2S clocking is also only approximate (check the code that tries to figure out the closest divisor combination for the clock), hifi DACs and the 8266 seem like a waste.

24- and 32-bit samples would be handled the same, as 4-bytes per sample, left-aligned signed fractional.

Fine for leaving it open as a 3.0 or something, but probably not 2.6.x.

@devyte
Copy link
Collaborator

devyte commented Nov 20, 2018

I'll leave it without a milestone for now.

@devyte devyte added type: enhancement component: libraries and removed waiting for feedback Waiting on additional info. If it's not received, the issue may be closed. labels Nov 20, 2018
@tedhsieh1966
Copy link
Author

Hi
I had modified your code and try to run it on ESP8266 seeming without problem. I am waiting for my hardware with DAC built to test whether it really works.

if you got time, I can send you the modified code for your review.

The modified i2s.h is as following:

#define I2S_DUAL_CHANNELS (0)
#define I2S_RIGHT_CHANNEL (1)
#define I2S_LEFT_CHANNEL (2)
#define I2S_RIGHT_CHANNEL_ONLY (3)
#define I2S_LEFT_CHANNEL_ONLY (4)

#define I2STX_16BITS_FULL (0)
#define I2STX_16BITS_HALF (1)
#define I2STX_24BITS_FULL_BREAK (2)
#define I2STX_24BITS_HALF_BREAK (3)
#define I2STX_24BITS_FULL (4)
#define I2STX_24BITS_HALF (5)
#define I2SRX_16BITS_FULL (0)
#define I2SRX_16BITS_HALF (1)
#define I2SRX_24BITS_FULL_BREAK (2)
#define I2SRX_24BITS_HALF_BREAK (3)

#ifdef __cplusplus
extern "C" {
#endif

void i2s_begin(); // Enable TX only, for compatibility
bool i2s_rxtx_begin(bool enableRx, bool enableTx); // Allow TX and/or RX, returns false on OOM error
void i2s_end();
void i2s_set_rate(uint32_t rate);//Sample Rate in Hz (ex 44100, 48000)
void i2s_set_dividers(uint8_t div1, uint8_t div2);//Direct control over output rate
float i2s_get_real_rate();//The actual Sample Rate on output

/*
bool i2s_write_sample(uint32_t sample);//32bit sample with channels being upper and lower 16 bits (blocking when DMA is full)
bool i2s_write_sample_nb(uint32_t sample);//same as above but does not block when DMA is full and returns false instead
bool i2s_write_lr(int16_t left, int16_t right);//combines both channels and calls i2s_write_sample with the result
bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking); // RX data returned in both 16-bit outputs.
*/
bool i2s_write_sample_lr(uint8_t *left, uint8_t *right);
bool i2s_write_frame(uint8_t *frame);
bool i2s_read_sample(int8_t *left, int8_t *right, bool blocking);
void i2s_set_sampling_rate(int sampling_rate);
void i2s_set_tx_bits_per_sample(uint8_t bits_per_sample);
void i2s_set_rx_bits_per_sample(uint8_t bits_per_sample);
void i2s_set_tx_channels(uint8_t channels);
void i2s_set_rx_channels(uint8_t channels);

bool i2s_is_full();//returns true if DMA is full and can not take more bytes (overflow)
bool i2s_is_empty();//returns true if DMA is empty (underflow)
bool i2s_rx_is_full();
bool i2s_rx_is_empty();
int16_t i2s_available();// returns the number of samples than can be written before blocking
int16_t i2s_rx_available();// returns the number of samples than can be written before blocking
void i2s_set_callback(void (*callback) (void));
void i2s_rx_set_callback(void (*callback) (void));

#ifdef __cplusplus
}
#endif

@maxmeli123
Copy link

maxmeli123 commented Apr 12, 2020

Hi @earlephilhower I am very interested to this too, I developed an ESP8266 and ESP32 audio library like your good ESP8266Audio library, but simpler and a bit different, ESP32 have this by filling i2s configuration structures but I cannot figure how to set highter bit rates on ESP8266, without these ESP8266 cannot be used as Hi Fidelity audio player, I only can play with 16Bit audio and not with professional 24Bit and 32Bit audio for studio recording. Please, tell me if there are plains to add it or maybe point me on the right direction. Hi @tedhsieh1966 do you you tried your modified code? Many thanks to both

earlephilhower added a commit that referenced this issue Jan 23, 2021
Add basic 24 bit mode to the I2S API with a i2s_set_bits() call.
By default 16b mode is still used, but if i2s_set_bits(24) is run
before i2s_begin() then the HW will drive 24-bits of data.  This
data must be left-aligned (i.e. bits 31..8) in 4-byte samples.

Fixes #5244 (the HW doesn't support 8 or 32 bits, only 16 or 24).
@maxmeli123
Copy link

maxmeli123 commented Feb 5, 2021

Many thanks for this great feature I've awaited for a long time 😉 So now I can play 96Khz 24 bit audio on ESP8266.
In my latest tests with external DAC PCM5102 I've found some that seem to be a bug. I can set max 78Khz samplerate, if I set greater I"ve no errors but no sound at all, but it play at 108Khz and 152Khz too, not sure for these last two numbers I do not remember, need to search the test sketch, but sure for 78Khz limit. I'm sure on old cores I've played up 192Khz 16bit Stereo audio so maybe something was changed in the core. Next when I've some time I will open an issue here.

Many thanks for your great work !!!

@tedhsieh1966
Copy link
Author

Thanks a million. Can't wait to try it.

@earlephilhower
Copy link
Collaborator

In my latest tests with external DAC PCM5102 I've found some that seem to be a bug. I can set max 78Khz samplerate, if I set greater I"ve no errors but no sound at all, but it play at 108Khz and 152Khz too, not sure for these last two numbers I do not remember, need to search the test sketch, but sure for 78Khz limit. I'm sure on old cores I've played up 192Khz 16bit Stereo audio so maybe something was changed in the core. Next when I've some time I will open an issue here.

The math used shouldn't care whether it's 8khz or 96klhz (it does floating point math to avoid overflow/etc, see the core_esp8266_i2s.c file). However the 8266 itself does not have an audio PLL and therefore the generated bit clock is simply a divided version of the 80mhz core clock. It is most likely the case that at 48 bits/L+R sample at 96khz the actual closest clock is too far from 96khz to work properly w/the DAC. DACs often have separate filter banks depending on the bitrate, and if you fall outside its window may not do anything at all. :(

Back of the envelope gives ~4.6MHz clock for 96K x 24b x 2, which is also very high for the GPIOs on-chip and any kind of breadboarding, so be careful with signal integrity, too.

@maxmeli123
Copy link

maxmeli123 commented Feb 5, 2021

Many thanks for your reply earlephilhower,

I had the similar issue using the PCM1808 I2S 24 bit Stereo line in with ESP32 that need an external clock (quartz or PLL chip like PLL17xx), but I've tried to use ESP32 itself to generate the clock with LEDC pwm (teorically by set the divider to 1 the resolution decrease to 1 bit and the max speed is 80 Mhz. It worked well at 36Khz samplerate but won't work with 44100, 48000, 96000 samplerate due to clock dividers aproximation, I know that the clock cannot be precise. I use it at 256FS so i.e. at 48Khz it need 48000 x 256 x 2 = 24.576.000 Hz, using same calculation at 44100 it must be 22.579.200 and at 96Khz must be exact the double of 48Khz, so 49.512.000 Hz. For PCM1808 specific case the clock must be max 6 frames +- outgoing the real clock needed, after this it won't work.

But the problem is that I'm sure that on old cores the same external DAC I will use now worked well up 192 Khz on breadboard too with short wires, so this is software related too, not only hardware, maybe I can search i2s.h and core_esp8266_i2s.c from old cores and test again.

Many thanks for your great support

@earlephilhower
Copy link
Collaborator

The sample rate 192k/96k doesn't matter, really, just the bitclock. The sample rate is the period of the LRCLOCK and triggered off the bitclock mod (16,24). Remember you're sending more bits at 24b per sample.

192k * 16 * 2 != 96k * 24 * 2, so your examples are using a different frequency. ~6.1M vs ~4.6M.

There is a fcn which reports the real I2S rate. I'd recommend dumping it on your 96K setup and see how far off it is.

@maxmeli123
Copy link

maxmeli123 commented Feb 5, 2021

Yes I already used it, the i2s_get_real_rate() function.

Note that I only used 16bit with ESP8266, never used the new 24bit API, I will try it these days when I've some time, for 24bit I refer to ESP32. I've used 24bit on ESP8266 only for input (PCM1808 Dual I2S line in and INMP441 I2S microphone), but to send it's signal to external DAC I downsampled to 16bit.

Anyway, next days I will test and see the real rate if is out the PCM5102 threshold by reading the datasheet and eventually I report it here.

Many thanks

@tedhsieh1966
Copy link
Author

Hi earlephilhower ,
I am a little bit confused on how to play 24-bit sample for each left and right channels with latest 24-bit mode. Is the following function the right one to call? If so, how should I spread 24 bits into two int16_t for each channel? Thanks for your great effort.

uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count);

@earlephilhower
Copy link
Collaborator

The core unfortunately exported the I2S functions as plain C so we couldn't add overloads for 32b quantities.

Easiest thing is to use i2s_write_sample(uint32) with the data MSB aligned. Lower 8 bits will be ignored by the HW. You can also use the write_buffer and double the frame_count (since you need 2x the data per sample).

@dirtmover
Copy link

I'm trying to use this with an INMP441 mic. I'm a bit confused how 24-bit read is supposed to work given that i2s_read_sample defines a 16-bit interface?

@NoNamedCat
Copy link

NoNamedCat commented Apr 28, 2022

Can we have the 8 bit -mono support? I write this question because the i2s port of this MCU is used not only for using sound DAC's, and i think that using 8bit shift registers with this hardware would be awesome!
Or anyone can point me in the direction I need to take to make the i2s port write an 8-bit mono sample using the IDF functions?
I appreciate any hint. Tks a lot!

@maxmeli123
Copy link

Maybe you can use 2 shift registers to get 16 bits, or just use one and then use 8 bit values masking the value with 8 bit. (value & 0xFF).

@MOJNICK
Copy link

MOJNICK commented Sep 23, 2023

@dirtmover Have you succeded?
Bumping up, in my case I'm reading from INMP441 and I get random data.. around 200 bytes of noise and 500 bytes of zeroes in 24bit mode. 16-bit mode works without a problem.

Here is a screenshot of raw data decoded as 32kHz sample rate and 8bit per sample. Those noise parts are around 200bytes in length, so it is 50 samples@4bytes per sample. (I'm using i2s_read_sample(left, right) and left-right variables are put in buffer unmodified):
image

@mousenapkin
Copy link

mousenapkin commented Sep 28, 2024

IM trying to solve this also.
INMP441.pdf Says must supply 32-bits/channel ( 64 in total per L/R Pair).
I also note esp8266-technical_reference_en.pdf states the I2SRX_24BITS_FULL_BREAK is "24bits_per_channel half data discontinue (single channel, FIFO data organisation, 24 bits data, 8 bits invalid, 24 bits data, 8 bits empty)"
So its not clear to me if its truly supplying a 64-bit long clock cycle. ( as master) , or only 24 Bit-long.
PUtting on a DSO should reveal whats going on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants