-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Convert audiosamples to use micropython "protocols" (safely) #2353
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
Conversation
Protocols are nice, but there is no way for C code to verify whether a type's "protocol" structure actually implements some particular protocol. As a result, you can pass an object that implements the "vfs" protocol to one that expects the "stream" protocol, and the opposite of awesomeness ensues. This patch adds an OPTIONAL (but enabled by default) protocol identifier as the first member of any protocol structure. This identifier is simply a unique QSTR chosen by the protocol designer and used by each protocol implementer. When checking for protocol support, instead of just checking whether the object's type has a non-NULL protocol field, use `mp_proto_get` which implements the protocol check when possible. The existing protocols are now named: protocol_framebuf protocol_i2c protocol_pin protocol_stream protocol_spi protocol_vfs (most of these are unused in CP and are just inherited from MP; vfs and stream are definitely used though) I did not find any crashing examples, but here's one to give a flavor of what is improved, using `micropython_coverage`. Before the change, the vfs "ioctl" protocol is invoked, and the result is not intelligible as json (but it could have resulted in a hard fault, potentially): >>> import uos, ujson >>> u = uos.VfsPosix('/tmp') >>> ujson.load(u) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: syntax error in JSON After the change, the vfs object is correctly detected as not supporting the stream protocol: >>> ujson.load(p) Traceback (most recent call last): File "<stdin>", line 1, in <module> OSError: stream operation not supported
This eases addition of new sample sources, since the manual virtual function dispatch functions are just calls via a protocol
I'd mildly like this for the mp3 PR but that's no reason to hurry it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this looks really good. Using a QSTR for the protocol enum and error message is clever.
Two minor things and then this should be good to go.
shared-module/audiocore/__init__.h
Outdated
typedef uint32_t (*audiosample_sample_rate_fun)(void* sample_obj); | ||
typedef uint8_t (*audiosample_bits_per_sample_fun)(void* sample_obj); | ||
typedef uint8_t (*audiosample_channel_count_fun)(void* sample_obj); | ||
typedef void (*audiosample_reset_buffer_fun)(void* sample_obj); | ||
typedef audioio_get_buffer_result_t (*audiosample_get_buffer_fun)(void* sample_obj, | ||
bool single_channel, uint8_t channel, uint8_t** buffer, | ||
uint32_t* buffer_length); | ||
typedef void (*audiosample_get_buffer_structure_fun)(void* sample_obj, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These sample_objs can also be mp_obj_t right? That'd make it clearer that the first field is the Python type pointer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Thank you!
Audio sample sources are essentially objects with virtual methods. However, the existing approach is to have dispatch functions that just check for each statically known type of object in turn. Using the underlying "protocol" facility of Micropython enables use of function pointers instead, so that the dispatch function need not know about all the sample types. This will make it slightly easier to add mp3 playback as a new audio sample source.
Protocols are nice, but before now there was no way for C code to verify whether a type's "protocol" structure actually implements some particular protocol. As a result, you could pass an object that implements the "vfs" protocol to one that expects the "stream" protocol, and the opposite of awesomeness ensues.
This patch adds an OPTIONAL (but enabled by default) protocol identifier as the first member of any protocol structure. This identifier is simply a unique QSTR chosen by the protocol designer and used by each protocol implementer. When checking for protocol support, instead of just checking whether the object's type has a non-NULL protocol field, use
mp_proto_get
which implements the protocol check when possible.The existing protocols are now named:
protocol_framebuf
protocol_i2c
protocol_pin
protocol_stream
protocol_spi
protocol_vfs
(most of these are unused in CP and are just inherited from MP; vfs and stream are definitely used though)
I did not find any crashing examples, but here's one to give a flavor of what is improved, using
micropython_coverage
. Before the change, the vfs "ioctl" protocol is invoked, and the result is not intelligible as json (but it could have resulted in a hard fault, potentially):After the change, the vfs object is correctly detected as not supporting the stream protocol:
Testing performed: UNIX testsuite; played a rawsample on an nRF PWMAudioOut