-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Proposed changes to machine.DAC #4254
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
Comments
Thanks for bringing this up, it would be good to flesh out the DAC class. At the moment there isn't really a defined Writing a stream of values, either in the foreground or background, is definitely a good use case for a DAC. (There are also other entities like SPI and I2S which would also benefit from being able to specify background activity, something to keep in mind.) The micro:bit has a bit of support for background activity, like playing audio and animating the display. The music.play(tune, pin=None, wait=True, loop=False)
music.stop() And the audio module this: audio.play(source, pin=None, wait=True)
audio.stop()
audio.is_playing() There are no callbacks in this scheme. I think it is important to support IRQs/callbacks (eg when the output queue is half empty) and/or ability to poll the DAC with the |
I strongly favour background operation. It would be great to be able to run a DAC and an ADC concurrently, with nonblocking methods. Background operation also facilitates asynchronous coding. If there were a method returning the current free space in the queue you could write (in Python) ioctl and write methods enabling a DAC to be used as a StreamWriter. Then finite and infinite datasets (e.g. files and generators) could be supported in uasyncio applications. IRQ's and callbacks are needed where speed is critical. |
@dpgeorge Are you okay with the queuing scheme? I can move forward on that, and we can figure out the "need more data" bit later. Was going to implement on the ESP32. |
Support for "double buffering" is a good idea. It's just a question of how it's exposed to the user. From a functionality point of view the following seem necessary (but not necessarily exposed like this): DAC.set_freq(freq) # set Hz, or set using a Timer object
DAC.set_mode_circular() # repeats single buffer over and over
DAC.set_mode_sequential() # consumes buffer after buffer
DAC.enqueue_data(buf)
DAC.get_queue_size()
DAC.start() # start outputting data
DAC.stop() # will pause the output, it can be started again if needed
DAC.set_callback(cb) # to be called when it gets to the end of a buffer The One advantage of separating out the functionality of setting the frequency/timer to a dedicated method is that the pattern generalises to other things like SPI: SPI.set_mode_circular()
SPI.set_mode_sequential()
SPI.enqueue_data()
...
If you can implement the low-level C code for esp32 that would be a place to start. Then connect it up to Python methods when an API is worked out. |
So ... two? I'm okay with the API, with one possible change:
The last item where you may have a large amount of data queued, and need to stop it entirely, and possibly enqueue different data. I'll begin coding soon; I'll keep the buffer enqueuing generic. |
Following how displays work, two buffers (active and hidden) is usually enough to get smooth output. If, due to latency issues, the application needs more buffers then that might be solvable instead by using larger buffers, but just two of them. But I guess a given port could support more than two. Two would be the minimum.
Yes, good point, a "flush buffers" is needed. |
Updated DAC api per: micropython#4254
FYI in CircuitPython we have a separate Just food for thought. Let me know if you have questions. |
Got this mostly working, w/o the callback for low buffers, and ... it sounds terrible. You can hear the audio, but there's lots of other noise and distortion. |
If you have access to an oscilloscope you could examine sinewaves at various frequencies. This would probably demonstrate the cause. |
I have. Leaning towards "the dac is not very good" and/or "timer has jitter." I'll analyze more when I have time. |
This repo has routines for tone generation, even playing WAV files: https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo/wiki/dac |
Rp2040 countio
You can output to the DAC using I2S and DMA. It might be worth combining this into the work that is being done on getting the I2S peripheral working? #4170 |
Been thinking about using the DAC to output audio (on the ESP32, but the proposed changes aren't specific to that platform). I'm wondering how to:
DAC.write_timed
for the pyb will use DMA under the hood, but there's presently no way to know when to queue new data.Proposed:
DAC.write_timed
will now enqueue the data. Doing anotherDAC.write_timed
before the first one finishes enqueues the next bytestream. Enqueuing would only work forDAC.NORMAL
, and only if the last item was alsoDAC.NORMAL
. Calling withDAC.CIRCULAR
while the queue is active with aDAC.NORMAL
will fail.DAC.queue_size
, which returns a quantity of how many items are in the queue, including the currently streamed item.Examples:
The text was updated successfully, but these errors were encountered: