-
Notifications
You must be signed in to change notification settings - Fork 186
Improved WavPlayer and Extra Utilities #677
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
…ibrary (minor whitespace changes to a few aux. files, and vscode config)
|
There is likely some clean up to still do here. I also have not validated this on bootloader apps, or with the CMake build system. In addition, the existing example needs to be simplified, and a few more simple examples should be added. edit: looks like the conditional inclusion of the FatFS |
…loop.wav size to 1s of audio
|
Okay, I think this is ready to merge. |
Summary
The goal of this PR is to provide functional, streaming WAV file playback within libDaisy.
The existing WavPlayer class was not robust enough for most use cases, and was highly susceptible to glitches, streaming artifacts, etc.
In the process of fixing the internal issues with the WavPlayer, I decided some of its built-in behavior was pretty opinionated, and that it would be better to abstract away some of the file selection mechanics (they were unsorted, and only accessible via index, etc.)
So this is a breaking change, but utilities have been added to accomplish the original behavior, and additional quality of life improvements have been added.
Specifically:
WavParseris a new, more involved WAV parser that parses other RIFF file chunks used for metadata, provides useful audio file information, and offset/size information for the PCM data, and other meta data sections.WavParserintroduces a new portable,FileReaderbased on anIReaderabstract interface. This allows this small wrapper around the actual I/O to be replaced to allow for desktop environments, locally stored binary data, etc. instead of being fixed to FatFS).FileTablewas added to allow the collection of file paths into an array, recreating the original multi-file support, but allowing more flexibility, and use beyond WAV files.Previoulsy, you might have had:
With this set of changes you can do:
If you want recreate the previous multi-file support it would look more like:
Changes
Internally, the entire class was reworked to rely on a FIFO of samples to pull from, with request generation being done when the number of elements remaining in the FIFO falls below a certain threshold.
This provides more flexibility with the timing between needing more samples, and them becoming available. It also opens up the opportunity to centralize these requests in a single manager to better allow for simultaneous playback of multiple files.
All functions return
Resulttype status that can be helpful in error detection/recovery.The previous implementation would not function properly when metadata, or additional RIFF file chunks were present within the WAV file. With the new WavParser, these factors no longer cause issues, and it is possible to access, and use metadata more easily.
The class now supports multiple audio channels more easily, and due to this
Streamfunction no longer returns a single mono sample. Instead, it takes a pointer to an array of samples to fill, with the number of samples the user wants.This channel number can be different than that of the file being played back.
The
WavPlayeritself has been extended to automatically convert samples tofloatfromint16_t, and provide a simple API for vari-speed playback.With the combination of multi-channel support, and vari-speed playback, some tighter frame alignment was necessary for avoiding playback discrepancies, especially with files that contain metadata.
The class is now a template class with the buffer size for the internal FIFO as an argument.
The efficiency of the Disk I/O increases with this size, but the latency of
Restart, and the memory usage increases with this size as well.With maximum settings (SD Card set to VERY_FAST, and
workspace_bytes= 32kB) streaming of a file at 8x speed, or 3-octaves up) is possible.Examples
At the time of opening this PR, there is a single
WavPlayerexample within libDaisy's example directory.It is designed to work with the Daisy Pod and USB MIDI.
It will load up to 8 files in alphabetical order, opening the first. MIDI Notes will playback the file at different playback speeds.
The encoder will select from the pool of files, SW 1 will pause/resume playback, SW 2 will enable disable looping, which is indicated by the Seed's Built-in LED.
Prior to merging, I will split this example into a few simpler examples that can run standalone without the Daisy Pod encoder/buttons, and add separate examples for the FileTable, and WavParser.
Future Work and Limitations
There are some limitations to the existing class, and opportunities around libDaisy that can be done in future PRs.
Limitations
At this time the class assumes 16-bit audio data, and will not function properly with files of different bit-depths.
Supporting other bit-depths is large enough in scope, that it warrants a separate update.
The class does not take sample rate into account, but this can be separately inspected with
WavParser.It may make sense to cache this internally, and add an optional flag for adjusting playback speed to match the system sample rate.
Playback in reverse is not implemented, and would be substantial more i/o heavy than forward playback.
For the moment, I have left that out. However, it is probably worth adding at some point.
It is possible to run multiple of these. However, there is no shared system for File I/O Requests.
The work of abstracting the
IoRequestmanagement into a static class that can manage requests, clear pending-flags, etc. is also a bit more involved than makes sense to tackle right now.Library Improvements
The addition of the
FileReader/IReaderabstract interface for File I/O presents the opportunity to extend that, and write some improved testing for classes that rely on disk i/o to function.The new
WavParseris currently only in use within these few objects.The
WavWriter(streaming audio recording), andWaveTableLoader(loading audio data into internal buffers) still use the old fixed,WAV_FormatTypeDefheader. The latter is still susceptible to issues with files containing additional metadata.So it will be worth while to update these classes to use the new parser, and remove the issue-prone fixed-size enum that is currently in use.