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

Design guidance sought - doc suggestions #37

Open
peterhinch opened this issue Nov 11, 2024 · 3 comments
Open

Design guidance sought - doc suggestions #37

peterhinch opened this issue Nov 11, 2024 · 3 comments

Comments

@peterhinch
Copy link

peterhinch commented Nov 11, 2024

I have made a WAV music player based on RP2 and asyncio. Because of the issue with allocations I am using the nonblocking I2S driver with a two phase buffer.

I can calculate the size of these buffers based on measurements of blocking: SD card read time and display refresh. The buffers need to be large enough to keep the I2S driver fed during the blocking period plus a margin. This led to a pair of 16KiB buffers.

Where I am puzzled is how to calculate the size of the I2S buffer. I am currently using 10KiB which works fine, but I would like to understand the basis for defining this. When the IRQ occurs my code switches to the second audio buffer and issues an I2S write in under 1ms. This might indicate a smaller buffer - but what other considerations apply?

[EDIT]

Official docs

It might be good if the docs gave some hints on sizing this buffer - the examples offer a wide range of sizes up to 40KiB.

It is not self-evident that the nonblocking driver continues to access the user buffer after the write has occurred. The naive user might assume that write transfers the contents of the user buffer to the driver's buffer; they might then start refilling the user buffer. In fact it is not safe to modify the user buffer until the IRQ occurs.

@miketeachman
Copy link
Owner

miketeachman commented Jan 14, 2025

Where I am puzzled is how to calculate the size of the I2S buffer. I am currently using 10KiB which works fine, but I would like to understand the basis for defining this. When the IRQ occurs my code switches to the second audio buffer and issues an I2S write in under 1ms. This might indicate a smaller buffer - but what other considerations apply?

There is a FAQ at the bottom of this repo that presents some ideas on how to size both the user and internal buffers. That might give some guidance? There is also an explanation of these buffer using an analogy of a person filling a slowly draining reservoir. I have the feeling that you're seeking a deeper understanding on how to size these things. If so, let me know and I'll try harder to come up with a better explanation.

It might be good if the docs gave some hints on sizing this buffer - the examples offer a wide range of sizes up to 40KiB.

That's a good point - the official docs leave users guessing on how to size these buffers. When I initially wrote the MicroPython I2S docs there seemed to be a pattern of writing quite terse documentation - I followed that lead. That was a prime motivator to write this I2S helper repo. Since then I've seen a trend to writing longer and more descriptive MicroPython documentation. Perhaps I could try to add some guidance on buffer sizing and see if it would be accepted by the maintainers.

It is not self-evident that the nonblocking driver continues to access the user buffer after the write has occurred. The naive user might assume that write transfers the contents of the user buffer to the driver's buffer; they might then start refilling the user buffer. In fact it is not safe to modify the user buffer until the IRQ occurs.

This is a very important point that highlights a gap in the documentation. The documentation should clearly state that the IRQ serves to indicate that the user buffer has been copied into the internal buffer, and can be now filled with more sample data.

@peterhinch
Copy link
Author

That FAQ is a useful reference for beginners. I like the water analogy.

I perhaps didn't explain my personal query too well. I have a two-phase user buffer. The "empty" IRQ causes the driver to be pointed at the full buffer in under 1ms. On the face of it the internal buffer only needs to support 1ms of playback. I wondered if there were constraints which might dictate a larger minimum size. (If so, perhaps the docs should reflect this.)

The application works fine with a larger buffer - and I got diverted onto other things before I could experiment - so my query is rather academic.

@miketeachman
Copy link
Owner

There are minimum size constraints on the internal buffer size, as follows:

  • >128 bytes for blocking and asyncio modes
  • >128*4 bytes for non-blocking mode

Those constraints are the same for the rp2, stm32, and mimxrt implementations of I2S. The ESP32 minimums are impossible to calculate as they are controlled by the ESP-IDF and are opaque to the developer. In fact, it's possible to set an internal buffer = 0 for the ESP32 I2S which suggests a minimum buffer size is being set internally in the I2S implementation.

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

No branches or pull requests

2 participants