WVR backing track player

49 views
Skip to first unread message

Dan Kadera

unread,
Oct 24, 2023, 4:33:39 PM10/24/23
to WVR Audio
Hi folks,

I've asked a couple of questions around here before, but it's been a while and now I finally got around to actually plugging my WVR in and starting to play around a bit. I'm trying to use WVR as the brains of a simple backing track player - I'd mostly like to stick to the default firmware with just a couple of modifications. The "tracks" would basically be notes 0-99 in the first voice - that's more than enough for the scope of the project. I'll probably roll my own UI for this project, but from browsing the code in the repositories I'm pretty clear on how to do that, so this message will be about the firmware side of things instead.

What I need to ask is this: is there a way to configure the WVR pins using code, rather than the UI? I'd like to "hardwire" certain pins to specific predefined functions, but I can't, for the life of me, figure out where the pins are being configured in the firmware code.. Also: is it possible to define a custom pin action in the firmware code? I'd like to have a button which essentially behaves like a regular WVR pin set to "note-on / velocity 127 / touch / falling edge / 60ms debounce", but with the note number being a variable - that would be the "trigger" for the backing track.

The variable will be set using an encoder; I've already found the encoder code example, so I have a starting point for the "reading the encoder" part (although I'm unsure if I should include some kind of hardware filtering for the encoder signal - the code looks like the kind of state machine which has inherent debouncing baked in, but I'm not entirely sure..?). But I'd also like to include some kind of an user interface - most probably a small SSD1306 OLED via u8g2 in the 8x8 buffer-less mode over hardware I2C. The display won't be too busy - it will just show the name and number of the currently selected note and maybe a "wifi on" indicator (in particular, it won't display any real-time information like playback progress), so I'm hoping I'll be able to get away with running it directly from the WVR. But to that end, I need to figure out two things: first, is it possible to get the name of a note given the voice and note number? I know I've asked about this before and it wasn't trivial, but not impossible, either, iirc. And second: is the HW I2C buffer-less u8x8 driver a sensible choice in this case, or should I look into something else? My programming background is in PHP and TypeScript - I know next to nothing about C/C++, and even less about FreeRTOS / real-time programming. For example, from reading the rotary_encoder.c library, it looks as though it would be possible to simply render the display inside the encoder callback, because it looks like it's being run inside a RTOS task scheduled from the encoder ISR.. right?

And for my last question(s) - I'd like to have a single LED driven by one of the pins which would light up while the WVR is playing (any note / audio, not necessarily the currently selected one). Is there an event I could hook into for that? In fact, what I'd love most would be having a callback at the start and end of any note's playback, with the note number passed in as an argument - then I'd not only be able to use that to drive the LED, but also to automatically advance the currently selected note when it starts / finishes playing (if it's still selected).

And as an addendum: what does the "lut" in e.g. "get_channel_lut()" mean? I can't figure it out and it's driving me crazy 😂😂😂

Thanks a lot for any input!
Have a great day everyone,
Dan

WVR Audio

unread,
Oct 24, 2023, 6:46:23 PM10/24/23
to WVR Audio
Hi Dan!

I still havn't heard of anyone writing their own firmware for WVR, so this is super exciting! I am always here to help!
I have some responses here to get you started, but keep in touch, I am super happy to pitch in.

For most of the things your asking about, the example file wvr_thames.ino will demonstrate solutions for. Including how to have pin events trigger a sound, how to code for the encoder, and how to control LEDs (with free PWM fading).

I am also working with some friends on a new device which uses WVR and an OLED, so you can see that code here https://github.com/ultrapalace/sample_banker/blob/main/src/main.cpp

You'll notice I often call WVR.resetPin() in these firmwares, this is what tells the WVR to forget everything it knows about this pin.

get_channel_lut() gets a pointer to the look-up-table (LUT) which stores data that tells WVR how to route from a midi channel to a voice. When WVR gets a CC Program Change command, it writes that information to this LUT, so that future midi events can be routed to the correct voice.

The encoder code works pretty well without hardware debouncing. I often put the debouncing RC circuits in anyway, but its not always needed. So proceed without hardware debouncing at your own risk, if you like. wvr.onEncoder() is the function that responds to events on the encoder. I am also a JS dev, so I have named a lot of stuff using  the event-oriented lingo from that language.

To get the name of a note, it's pretty hard, because it has to be loaded from the eMMC memory, that info isn't needed in WVR firmware, so I don't bother to load all that data and waste ram. You'll see in the new project I linked, I want to display the name, but have just hard-coded the names for now. I will be writing that code properly very soon, so check back in the next few weeks! Once I do write it, I will add it to the WVR firmware, so you should be able to just say WVR.getName(int voice, int note, char* buffer);

I highly recommend getting VSCode, or another IDE, which can get you to the function definitions by cmd + clicking on them. I would literally not be able to do any work without that enabled. It would drive me crazy. This takes a bit of configuration in VSCode to get it working, but it is worth it!

Also note that it is absolutely necessary to have an FTDI -> USB module setup, if you are going to hack on WVR code. If you don't, it is VERY easy to brick the WVR. Details on this are in the docs.

WVR Audio

unread,
Oct 24, 2023, 6:49:47 PM10/24/23
to WVR Audio
Oh and all the pin actions are setup in button.cpp, and the actual execution is in gpio.cpp
You'll see I attach interrupts, and everything else in those 2 files.

Dan Kadera

unread,
Oct 25, 2023, 1:49:36 PM10/25/23
to WVR Audio
Hi Andrew,

thanks so much for your replies! I seem to have a pretty decent start: I've built a custom firmware which has:
  • one touch button for "play selected" (the onPress handler basically just calls "wvr.play(0, currentTrack, 127)", so it acts as a "play/stop" thanks to the note itself being configured to stop on retrigger)
  • digital "previous" and "next" buttons, which decrement / increment the "currentTrack" variable (with limits at 0 and 99)
  • a digital "wifi toggle" button
  • and all of this works as expected
But: one thing which currently doesn't work as expected is the UI - I can connect to the soft AP, but the server doesn't return any response to requests (as in, not even headers - the TCP connection is apparently accepted, but I don't receive even a single byte back) - not sure what that's about, since my code is basically just the "wvr_basic.ino" with a couple of Buttons at this point. When I boot, I can connect to the soft AP and ping the device on the usual address, but as soon as I try sending it a HTTP request, it stops responding even to ping. Weird.

Side note - I almost soft-bricked my WVR because of this lol - I have an FTDI module, but I left it at home and went to fiddle with the WVR at my favourite café, only to find out that the UI isn't accessible with my firmware 😂 fortunately I was able to boot into the recovery firmware and from there back into the stock wvr_basic.ino.bin from the Github Releases page, but it took a bit of a dive into the code to figure out how to do it - the readme mentions the recovery mode, but it doesn't mention that it's enabled by default and what the default recovery pin is, so I had to find that out from the source..

Also I have an issue with DHCP - I'm not sure if WVR is supposed to provide a DHCP server, but I'm pretty consistently not getting an address when I connect - and that's both with my custom firmware and with the stock wvr_basic.ino.bin. Any idea why that may be? I'm on a Macbook with macOS Ventura 13.4.1. If I configure an appropriate IP manually I can use the basic firmware's UI just fine, but if I don't, it sometimes works and sometimes doesn't and it keeps disconnecting..

Anyway, I think a lot of my current issues are due to the fact that I didn't even solder the pin headers onto my module and I'm just sticking jumper cables into the holes for the pin headers and hoping they'll stick for a bit 😂 I don't want to install the pin headers because I'm planning on surface-mounting the module using the crenellations and I don't want to have to desolder the headers later, because knowing myself, I'd cook my WVR in the process. But I'm definitely going to have to figure out a better approach to doing this if I want to get anywhere..

Here's my current code, if you want to take a look - just the .ino, I haven't touched anything else yet, so this should include everything pertinent..

Cheers everybody,
Dan

WVR Audio

unread,
Oct 25, 2023, 3:50:47 PM10/25/23
to WVR Audio
Not sure why the UI would be failing.
I updated the UI repo to include bundle.h https://github.com/marchingband/wvr_ui/tree/main/output
Copy bundle.h into wvr/src (and overwrite whats in there now)

I have huge problems with my new macbook trying to connect to WVR, it makes me crazy.
Unfortunately I know nothing about DHCP. If you have any tips I would deeply appreciate your help.

WVR Audio

unread,
Oct 25, 2023, 3:55:20 PM10/25/23
to WVR Audio
Also, thats so awesome that you are making headway! 🎂
If copy/pasting bundle.h doesn't work, then see if there is a crash or any errors printed to the terminal, I am not sure what the issue would be.
To get logs from WVR via FTDI you must set wvr.useFTDI = true, and make sure that you don't call wvr.resetPin on any of the UART pins which are D0 and D1, you can see that I check this in wvr_thames.ino.

Dan Kadera

unread,
Oct 26, 2023, 12:58:15 PM10/26/23
to WVR Audio
Hi, so I've managed to hook up the FTDI and I have two interesting findings to report:

My custom firmware reports the following error, just after "AP IP address: ...":

[   916][E][AsyncTCP.cpp:1270] begin(): failed to start task

This looks to me like the AsyncTCP RTOS task being the problem. The internal function which returns the very "false" responsible for the error message can be found here, although I for one have no idea what to look for in there..

And my second find: when I remove all IP related configuration from WVR's server.cpp (namely, comment out this and this), the soft AP network config reverts to the default 192.168.4.x range - and it works correctly - meaning I get an almost instant Wifi connection and IP address. This leads me to believe something is amiss with the network configuration.. not sure what, but I'll keep experimenting with this.

That's all for now, thanks for watching! ;-)
Dan

--
You received this message because you are subscribed to the Google Groups "WVR Audio" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wvr-audio+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wvr-audio/9617f113-7efb-48e4-9952-d0fea293719fn%40googlegroups.com.

Dan Kadera

unread,
Oct 26, 2023, 2:35:52 PM10/26/23
to WVR Audio
So the AsyncTCP issue is down to insufficient memory. There's an xTaskCreateUniversal() call on line 221 and its third argument is set to 8192 * 2. If I set it to just 8192, it works properly. Well, it doesn't, because now my SoftAP is configured for the default IP range, and the UI HTML code contains addresses with the regular WVR IP hardcoded, so the UI doesn't actually load.. but that I can solve. Of course, I have absolutely no idea what that 8192 * 2 value was for (except it's something to do with memory allocation), and maybe the AsyncTCP thingy will break miserably when I deprive it of bytes to munch on.. Oh well 😂

WVR Audio

unread,
Oct 26, 2023, 6:55:47 PM10/26/23
to WVR Audio
ok interesting. WVR needs a fixed IP, but it's possible I picked a bad one ... maybe out of range?
Is your code using a lot of memory? Do you declare large arrays or something? Real RAM is very tight in the WVR firmware. But you can always allocate some PSRAM using ps_malloc. There it tons of that.
Starving your TCP task is risky ... it may crash when doing big file transfers for example, but worth trying out if it's the easiest fix.

Dan Kadera

unread,
Oct 26, 2023, 7:52:11 PM10/26/23
to WVR Audio
No, the wifi settings you're doing look okay. Actually, with the stock 3.8.3 firmware I also get a log at boot about the gateway address being inside the DHCP range, which it shouldn't be - I don't remember exactly atm, but I think it was trying to set the gateway to 192.168.5.20 or something like that - but looking at the v3.8.3 tag on Github, the code is the same as now, which is that the gateway should be .17, and that should be perfectly acceptable. So I suspect there might be a bug in the ESP32 wifi stack, possibly triggered by the fact that you're starting your whole address range at a pretty non-standard offset (not .1, but .17/.18 - which is perfectly legitimate and it should work, it just might explain why nobody else has run into this before).

In any case, the default settings already give you a fixed, static IP address - the soft AP always has 192.168.4.1 as the default IP on all ESP platforms as far as I know. So that may be a backup solution if we can't find a way to make your existing address scheme work. And then of course you could use mDNS to avoid exposing the user to raw IP addresses entirely - they'd just use e.g. http://wvr.local/ to access the UI instead of http://192.168.x.y/ - but it would consume some more resources, so perhaps not.

Actually, the code which was already triggering the AsyncTCP error was just a vanilla wvr_basic.ino with maybe a couple of buttons set up. I'll check again tomorrow with the wvr_basic.ino verbatim from the examples directory, and also all libraries in their default state without my modifications - exactly as if I were starting from scratch with everything fresh from its respective source. But I do know I couldn't access the UI even in the first build of my custom firmware, and that really didn't do much - one or two uint8_t variables and a couple of buttons, that was it.. Could you maybe check how much memory your local version of the AsyncTCP library allocates at that place? The library didn't receive any updates in some years, but still, would be nice to know for sure..



You received this message because you are subscribed to a topic in the Google Groups "WVR Audio" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/wvr-audio/HsDZJ8qa9XE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to wvr-audio+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wvr-audio/cc7eb1ce-f77f-4b9a-a9ec-76368c74f1c6n%40googlegroups.com.

Dan Kadera

unread,
Oct 27, 2023, 10:32:57 AM10/27/23
to WVR Audio
Good morning! So tl;dr, I resolved the AsyncTCP bug by deleting all of my Arduino libraries (not just WVR-related - there was a lot of old unused junk as well), then recreating the WVR environment from scratch. Now all my builds have a working UI. I threw together a "fake display" driver implementing a subset of the u8x8 API which just outputs stuff over the serial port (I'm still waiting for the actual display to be delivered), and so now I think I'll be moving on to implementing a custom UI, because everything else seems to be in place.

I do have another question though: I'm thinking of using the Arduino-ESP32 built-in Preferences.h facility to store some data, this is supposed to use the on-board non-volatile memory to persist across reboots - will this conflict with WVR in some way?

I'm thinking that in the future, it would be great to have a separate "libwvr" library which would be less tightly interwoven, so that individual parts of it could be reused more selectively - as it is now, initialising the WVR object will basically start everything, and there's no way to opt out of some features - this makes sense within WVR, but if I wanted to just use e.g. the wav storage + wav player without things like pins, (web)midi, neopixel etc, I basically have to copy & paste the code into my own project, which I don't want to do for multiple reasons.. Would you be open to trying to figure something out? I'd be happy to help of course.

Cheers!
Dan

WVR Audio

unread,
Oct 27, 2023, 11:58:45 AM10/27/23
to WVR Audio
Awesome news!
Preferences should not bother WVR. I don't use the flash at all actually.
I have been using Platformio to manage my Arduino projects lately, I highly recommend it, it keeps your projects isolated.
I really appreciate your insight with the IP address. I think I can actually change that for the next version release, which will be coming soon. My reasoning for not using mDNS was that I heard it only works sometimes, and I didn't like that idea.
A modular libwvr would be great. If I were starting the project from scratch today I would try to build it that way for sure, even though it's not the idiomatic Arduino style. Unfortunately everything is so interwoven it would be a challenge to do at this point, I wrote this library at the beginning of my coding journey, and that shows. If more folks start writing WVR code then I will definitely take a closer look at that task. Hopefully that's not a chicken-or-egg situation.

WVR Audio

unread,
Oct 27, 2023, 12:02:57 PM10/27/23
to WVR Audio
One approach that could work for customizing the WVR library would be "$ git clone https://github.com/marchingband/wvr.git WVR_Dan" into your Arduino/libraries folder, and go to town hacking it to bits.

WVR Audio

unread,
Oct 27, 2023, 12:04:53 PM10/27/23
to WVR Audio
The approach I used for the new project that I linked was to fork the WVR repo, and then using Platformio, you can set the URL for the library, so I just set it to my fork.

Dan Kadera

unread,
Oct 27, 2023, 6:03:33 PM10/27/23
to WVR Audio
I've actually already forked the main WVR repo ;-) figured it would be easier to keep my code up to date with any changes you might introduce later that way..

Anyway - the modular design would be awesome, but really, the most important part of that for me would be having just a library to manage WAV files and playback - and since the original WVR use-case needs to be able to handle more than just raw playback (ASR + loops, racks, did I hear about pitch interpolation?), it would still be pretty interwoven with what WVR was originally designed to do - maybe it would just have an API decoupled from MIDI (but at that point that would mostly just be a question of what things are called). So it's probably too much work just to make it more "generic" just to fit my purpose.

I have done some work on the UI today, and I have a couple of new topics to share in that regard:
  • I'm not sure if I understand what's actually happening, but I built a custom firmware with my own HTML and JS bundles, and I've run into a couple issues there - the UI runs as expected, but when I reboot into recovery mode, I don't get back the default WVR UI - I get my custom one. I thought the recovery mode boots an entirely separate firmware..? I had my custom firmware in slot #1 or above, with the stock wvr_basic in slot #0, and I uploaded my custom firmware using the web UI (not FTDI).
  • I'm not entirely sure, but I think that when I delete a firmware from a slot and upload a new binary into that same slot without rebooting in between and then try to boot the new firmware, I still get the previous firmware which was in that slot.. It's not too big of a deal, but if it's actually consistently happening that way, then I think it should either be documented, or changed to allow this kind of workflow - it left me confused the first couple of times (before it occurred to me to try uploading into an empty slot, booting from that and only afterwards deleting the old slot).
  • One other weird thing that happens to me consistently is that when I boot my custom firmware, then sending a request to reboot from a different slot immediately triggers a Guru Meditation Error: Core 1 panic'ed (Load Prohibited). Since rebooting into recovery appears to still start the same firmware, this actually forces me to flash back the stock firmware using FTDI.. Any idea why that might be? My edits so far haven't touched the HTTP server or firmware handling, so this codepath should be the same between the stock firmware and my own..
  • I needed to be able to read and change note names, neither of which was currently possible - there was no function to get the wav_file_t structs similarly to how add_voice_json does it - so I added one, but I had to actually patch it into the file_system.c file because it uses some macros which are only defined in that file. I'm not sure if there was an easier way..? And as for writing note names, I noticed that the "name" property in the voice JSON is being ignored when updating the voice settings - the name is only being taken from the WAV upload. So I copied & adapted some lines which deal with rack names in a similar context, and it seems to work as expected. Is either of these two operations considered ill-advised? They're simple enough edits, so I'm happy to keep them if they don't break anything important elsewhere.
All else looks well! My custom UI can already upload new files and rename existing ones, that's almost all I need :-)
Have a great day / night :-)


WVR Audio

unread,
Oct 27, 2023, 6:58:48 PM10/27/23
to WVR Audio
#1 There is not a recovery firmware, there was at first but i changed how that works fairly early on, it was a logistical nightmare, and not necissary.
Instead, code execution stops before returning from wvr_init(), accomplished with vTaskDelete(NULL). This means any user code, which must be triggering the crash, is never called.
Then the server is started via recovery_server_begin() which has some modified handlers.

#2 It seems unlikely, but possible, that there is a bug in the firmware code causing #2. Let me know if it persists and I'll create a GitHub issue and fix that.

#3 I am not sure what that would be. You should be able to get more information from that crash. Did you install the stack decoder plugin for Arduino?

#4 a) that seems right to me. The names are on the eMMC, so you have to read it somehow, this is a task for file_system.c I think.

#4 b) gah yah that "name" property looks like dead code, embarrassing, haha. It sounds like you are saying you repurposed that property, and that sounds fine I think? I certainly can't see any way it would get you into trouble, if its working. It sounds like something I would do anyhow ... for what it's worth.

Its super awesome to see you progress through all this! Amazing work!

WVR Audio

unread,
Oct 27, 2023, 7:07:33 PM10/27/23
to WVR Audio
Maybe the best approach to libwvr would be to fork the whole library, remove everything about midi and pins, and present a more common-sense API ... probably a 1D array of memory slots, and ex. volume expressed as a percentage (rather then a silly 7-bit value).

You mentioned pitch interpolation ... the stock UI can accomplish this, there is a section in the docs on it ... but I also have a beta firmware that can pitch shift live. Up to 2 semitones up, and an octave down. It is in the wvr_binaries repo if you want to try it out, and I can push that branch to the repo if you want to fork it, its currently only living on my machine.

Dan Kadera

unread,
Oct 28, 2023, 11:25:45 PM10/28/23
to WVR Audio
Okay, lots of new stuff.. I apologise in advance for the amount of bytes in your mailbox that this message will occupy...

Regarding previous issues:
  1. Recovery firmware - understood.
  2. Overwriting non-empty firmware slot: I think that must've been just the network connection dropping out or something, it's working as expected now.
  3. Request to boot from a different slot causing a crash on custom firmware: hehe, that one was fun - turns out that I was passing 'index=1' as an argument to curl on the command line to set the header....... and the correct value would've been 'index:1'. A colon instead of an equals sign. It was actually ESPAsyncWebServer's AsyncWebHeader or whatever it's called which got just too sad that I would make such a stupid mistake. Oh well.
  4. Note "name" property: awesome, works for me :-)
  5. LibWVR: yeah, that's the direction I had in mind as well, but once you factor in all the features that such a "generic" library would need to have to be still useful for WVR itself, you'd probably be almost back to where you started, just with a different naming convention and maybe things like volume in dB / as a gain factor instead of a MIDI value, which, to be frank, isn't that big of a difference at the end of the day.. But if you have an idea which looks good to you, let me know, I'd be really interested in seeing it happen and like I said, I'd certainly be happy to help in any way :-)
Now onto the new stuff: I've resolved many issues with my custom build. I have a working UI (well, both a web UI and a hardware UI using a display + encoder, at least in theory as I still don't have the real hardware, so I'm simulating it for now). I have a "playlist" feature, which is just the order of the first 99 notes stored using the Preferences facility - so from the user's perspective, the note numbers are no longer relevant - you just upload audio files into any of the 99 "tracks" and then reorder them any way you want. I've managed to (tentatively) persuade the wav player to send me an event whenever it starts or stops playing audio, so I can light up a LED whenever any audio is playing. So far so good. But there's a couple of things that came up:
  1. The upload speed. It's just a little over 2x the realtime speed of the audio :-D though from reading the ESP32 docs it seems as though that's simply as good as it gets with this chip. There was a weird error once or twice, when the upload failed at the very end and there was a message in the console from AsyncTCP about a RX timeout - I'm assuming that's possibly due to the low speed (and that, in turn, might be in part due to a pretty noisy environment, not just ESP32 Wifi being slow).. but anyway - thought I'd ask - there's an entire section in the ESP-IDF docs about improving Wifi throughput, but it goes far over my head - does any of it look like something that could be done in WVR? Tuning up a couple of buffers here or there..? Although it doesn't really look like it would do much anyway..
  2. I've been looking for a way to implement a "panic" button - "stop everything now" - and it looks like it can be done by sending a MIDI Mute CC event to the wav player queue. That's pretty neat, and it Just Works with my current mechanism of emitting events at playback start & end.. but there are two things I'd like to change: first, this mechanism actually produces tiny "pops", because the audio is just rudely cut off wherever it currently is, by means of flagging the buffer as "done". Would it break anything if I instead set the "fade" flag, similarly to what a NoteOff event does? And while I'm poking about in wav player internals..
  3. What's DAMPEN_BITS? It's used to right-shift each sample before it's summed into the output buffer by one bit, but why? Doesn't that mean that the engine is losing resolution?
  4. And back to GPIO again: I've only just now noticed that the touch button implementation has no concept of a "touchend" event - basically the onPress callback is being called repeatedly in (I suppose) an interval set by the debounce time, correct? I've looked into the code which provides this functionality, but I'm still not entirely sure.. is the issue in the fact that touch_pad_get_status() just returns a bitmask of which touch pads have changed state, but there's no way to check which pins are being touched now? This is starting to get important because I feel like I'm approaching a time where I'll want to start to think about building a box for the thing, and I'm gonna need a couple of big buttons on the box, and making even binary (non-velocity/pressure-sensitive) rubber pads is apparently not a piece of cake, I was thinking I'd stick with just touch pads.. but the accidental retriggers with short debounce times are making me slightly wary.
Anyway, sorry again for the insane TL;DR, gotta go catch some sleep. Toodles!
Dan



WVR Audio

unread,
Oct 30, 2023, 12:16:02 PM10/30/23
to WVR Audio
Great bug hunting!

1) a) RX Timeout is a quirk/bug in that AsyncTCP stack that has haunted me before. It has been a while but I believe the best defence against it is to send a response early, even if it is a CONTINUE. I can't imagine that any of your code is to blame here, but I', also not sure what would be to blame, as I don't think that library has changed in a long time, and I havn't received that error in a long time. Keep in touch about it please.

1) b) I havn't seen those docs can you link me to them? I feel similarly that there is little chance of significantly speeding things up that way. Having the TX power up all the way (adjustable in the WEB GUI) and having the devices physically close, and with a clear path with minimal metal in the way, helps a lot.

2) That sounds very reasonable and I should make that change as well.

3) Summing audio would typically be done with a divide to average the inputs, the right-shift is a faster alternative. You can play around with it if you like, but be on the lookout for distortion.

4) Yah, that could definitely be improved. line 38 is what causes the re-triggers I believe. The correct implementation would wait for an interrupt and then run something like this. I believe all these esp-idf functions are available in Arduino still? I can help on this if you need, I could make time later in the week.

Dan Kadera

unread,
Nov 13, 2023, 12:26:15 PM11/13/23
to WVR Audio
Hi, sorry for the late reply, a couple of days ago I finally received the I2C display, so I was able to do some more work on the project. Quick reaction to previous discussion:

1) b) increasing wifi throughput - the pertinent section of the ESP-IDF docs is this, but like I said, I don't expect this to have a significant impact.. There's a table listing a couple of different configuration presets (they call them "ranks"), and as can be seen there, if you were to max out everything as the "iperf" preset does, you could get to a TCP RX rate of some 63 Mbps, but it would eat up a huge amount of memory; the second best TCP RX result was using the "RX priority" preset, which can achieve around 48 Mbps, whereas the default preset yields 40 Mbps - so there is a 20% improvement in theory (although I'm pretty sure you would actually only get those numbers in lab conditions, if then). And apparently the "RX priority" preset actually uses a tiny little bit less memory than the default. So it might not hurt to try it, but it might also help only a little, if at all..

4) touchend - I actually ended up designing and coding for a mechanical switch, because I want the main play button to have tactile response, but yeah, I think this is still worth looking into..

Allright, and now for the new stuff:

α) I got everything to work! I've ordered a PCB for my project, and meanwhile I put everything together on a breadboard and after some tweaking it actually works. Amazing.
β) I ran into an issue where a fast-enough button press with a long enough debounce time didn't end up calling the onRelease() callback, which I'm using to cancel a long-press timer I have on one of the buttons. If I'm not mistaken, it's a limitation of the current implementation; it probably doesn't matter too much, but it may be worth looking into if you were to consider implementing the "touchend" event.
γ) I've tried switching to PlatformIO, both as an IDE for writing code and as a build pipeline - and it turns out that something in the PlatformIO build pipeline is causing my firmware to eat like 30kB of extra RAM from the get-go. My firmware starts with wifi off, and long-pressing one of the buttons is supposed to toggle it; this worked well previously, but suddenly it started failing (there was an error being logged about failing to allocate the expected number of RX buffers, or something like that). I didn't suspect the build pipeline at first, but after eliminating all the other changes I've made since the last successful build it was the only thing that could cause it.. and sure enough, rebuilding again using Arduino CLI got me a working firmware. I've no idea why that is.. Maybe PlatformIO includes some more debugging facilities even in release builds, or maybe it got a slightly different version of some library or something like that.. but yeah. I don't mind terribly, but it was an interesting thing to observe.
δ) Oh, yeah, ESP-IDF stuff is definitely available, that's what I'm using for the long-press timer - and just now, browsing the docs randomly, I discovered that it includes a pretty capable HTTP server implementation - maybe WVR could drop the (seemingly unmaintained) AsyncTCP and ESPAsyncWebServer dependencies.. I'll definitely look into this, as it would be relevant for other projects I have in mind. It supports async requests and responses, as well as websockets, so it looks promising.

Regarding the "librarification" of WVR, I'm starting to see the outlines of a couple of things. At least I think the parts dealing with audio management might have sensible solutions.. There would need to be three parts to this, I think:
  • A wav storage layer - a generic API allowing the storage and retrieval of PCM data to and from the eMMC - probably as a single flat indexed list; ideally it would allow storing application-defined metadata along with the wav files (so the WVR firmware could store e.g. loop start and end points here). From the playback point of view, this would need to provide a method which would read PCM data into a buffer given a file index, an offset, and a buffer length. It could also abstract away the handling of mono files - just pass it a separate L and R output buffer, and it would either fill each with data from the respective PCM channels, or fill both with the same data when the file is mono.
  • An audio playback layer - which would use one or more input buffers for each audio channel, and it would handle summing them and sending them to the DAC. It'd be awesome if the number of output channels could be configurable - as in, if you could use it with e.g. multiple DACs to handle multi-channel output. It could handle global volume & mute (ideally on a per-channel basis). This would encapsulate the RTOS task; the user would just provide a callback to fill the input buffers.
  • An application-specific "glue" layer, which would implement the callback for the playback layer using the storage layer. In the default WVR firmware, this would be where MIDI notes are converted to storage layer indices, pruning is resolved, ASR loops are applied etc - basically most of what the current wav player task does, except for the stuff abstracted away into the other two layers.
In my ideal world, all of this would be implemented as classes & wouldn't use any global state.. ;-)

By the way, have you heard of the ESP Audio Development Framework, a.k.a. ESP-ADF? I haven't, until recently. It looks like it might be pretty powerful..

Cheers!
Dan


WVR Audio

unread,
Nov 13, 2023, 1:07:16 PM11/13/23
to WVR Audio
α) aaawwwwesome! amazing work!
β) yah ... the debounce time needs to be correct. It might be possible to design the driver to handle this, but off the top of my head I am not sure how.
γ) oh wow. Another user has been finding the PIO builds crash his (homemade) WVR, and I am not sure what's causing it. I think I will be sticking to arduino CLI for now.
δ) That webserver has come a very long way since WVR was created. I have been meaning to try it out, I would love to remove the current server, it made my life very hard for a very long time.

1) b) let me know if you try this out! 20% is not nothing.

"Regarding the "librarification" of WVR" : That architecture sounds like it makes a lot of sense. I am not sure how it would be be beneficial to co-locate the metadata and the pcm data, I can imagine a route to achieving it, but it would be a fairly large change. What I want to avoid is a 2 step process: 1) loadAudio 2) playAudio, so I load all the metadata at boot.

I used the ADF for the first time last month on another project. It is very powerful, and has some amazing example code. The architecture doesn't make a lot of sense for WVR, it abstracts away things that I needed to fine tune, but for any application that doesn't require that fine tuning, and the abstractions are useful, I would definitely use it again.

WVR Audio

unread,
Nov 14, 2023, 12:09:21 AM11/14/23
to WVR Audio

Now that I think it through, I don't believe that the esp-idf server code was (or maybe still is?) NOT available in Arduino ... I think those parts are not open source?
Which leads me to a question for you: would you have been as keen to hack on WVR code if it was written in esp-idf rather then Arduino? There has been a bug in Arduino's ESP32-S3 code, blocking me from working on WVR2, for the last 13 months. It is the eMMC driver. I am considering writing WVR2 in esp-idf using PlatformIO.

Dan Kadera

unread,
Nov 14, 2023, 7:07:32 AM11/14/23
to WVR Audio
Huh, that possibility didn't occur to me - I don't think parts of ESP-IDF are closed-source, but the ESP32 Arduino core might not be setting some flags and so some optional parts of ESP-IDF might not be baked in by default. But well - in my WVR project in PlatformIO it is available for autocomplete (at least the header file is), so it looks like it's available now, whatever the status might've been in the past. Maybe I could try rewriting the WVR HTTP server on top of the ESP-IDF implementation, as a way to contribute to the project..?

In any case, I haven't really done many Arduino projects before getting into WVR - in fact, I don't even own an Arduino, I just used the ESP8266 Arduino core for a couple of small projects, and that was my first time writing C++ code. So I'm not really all that strongly invested in the Arduino ecosystem and I'd say I'd be just as eager for WVR were it written entirely on top of ESP-IDF. So as far as I'm concerned, go for it! The ESP32-S3 looks pretty good for WVR. I'm excited to see what you can come up with for the second incarnation :-)

I just talked about my project with a friend yesterday and he asked how hard it would be to add an audio input and basically turn the thing into a live looper.. hehe :-D that's well out of the scope of what I'm trying to do now, but perhaps in the future...

Regarding WVR-as-a-library - what I meant is that the persistence layer could incorporate metadata persistence, not necessarily that the metadata would have to be physically collocated with the PCM data - it's perfectly possible to have the behaviour you're describing, so that you're able to load the map of an entire section of the metadata into memory in one go.

Got the PCB today. If all goes well, tomorrow I should have a working circuit, and then it's just a matter of printing the enclosure. I'll probably record a short demo video or something so that I can show off to people - I'm definitely posting it here ;-)



Dan Kadera

unread,
Nov 14, 2023, 11:50:51 AM11/14/23
to WVR Audio
Aha, I see why you probably couldn't have implemented the web server using the ESP-IDF HTTPD component: the async functionality in the component has only been implemented recently, as far as I can tell it hasn't even been included in any release yet, it's just in the repo master branch.. Well, perhaps eventually, then. Too bad the ESP-IDF docs default to showing the master branch, as opposed to latest stable..

WVR Audio

unread,
Nov 14, 2023, 5:37:52 PM11/14/23
to WVR Audio
My first draft of WVR firmware was in esp-idf, there didn't really appear to be any need for async functionality, since there is at most one client, and it's easy to write synchronous JS. My understanding at the time was that some of the idf server was inaccessible, either I was mistaken or things have since changed. There appears to be several newer, feature rich Arduino servers, built on the idf httpd lib
Reply all
Reply to author
Forward
0 new messages