Resources usage

There’s no doubt that OpenWare is full of hidden gems that @mars is hiding from us. It’s time to shed some light on one such case.

Besides storing patches on flash, it’s possible to use it for any binary data. It would be available separately from uploaded patches and won’t interfere with normal patch running. Currently it’s only used to store device settings, but there are multiple slots available and data can be loaded with MIDI Sysex the same way as normal patches. This makes it possible to support a lot of interesting use cases for OwlProgram patches:

  • loadable wavetables (shared between patches)
  • custom lookup tables (potentially exceeding 128k per patch limit)
  • settings presets (either parameters state or arbitrary user data)
  • custom tuning tables for MIDI (or CV quantizer)
  • Scala files support (maybe this could be ported)
  • even something like DX7 emulator with preset loading is possible

For now I’ve made a script to convert binary data of existing LUTs to resource format in order to fit FW on 128k flash. Also made UI for browsing resources, planning to port this to Magus.

1 Like

One minor issue that I’ve found is that settings are configured to be stored in slot 42 which is element 1 (instead of 0) in registry.resourceblocks array. Pretty sure it was supposed to be 41 to appear in the head of this array.

Flash usage stats added to UI page:

1 Like

General plan for resources:

  1. Move settings resource on position 41 (it would be too confusing to have a gap in resource list otherwise)

  2. Adopt convention for using __UPPER_CASE__ format as system resource name (currently only __SETTINGS__, but I also need it for pow/log/fft resources to minimize flash image size on Daisy)

  3. Non-system resources can be deleted by clicking on them in resource browser page.

  4. Add migration process from old to new settings name/index in owl_setup (very annoying, but I think this should just be made consistent before resources are exposed officially)

  5. Copy UI updates to Magus (flash stats and resource browser)

  6. Add service calls for getting/setting resource contents by name/index

  7. OwlProgram class for resource access using those service calls

  8. Script for converting wavetables from WAV to resource and a simple wavetable oscilator example. There are multiple formats in existence, I will just suppost 2D wavetables as used by WaveEdit

  9. There could be a way to load resource contents to Faust tables, I’ll have to research this. It’s can be used for oscillators like osc(freq) = rdtable(tablesize, os.sinwaveform(tablesize), int(os.phasor(tablesize,freq))), so looks like we only need a function for reading data to be used as second argument.

Any suggestions/feedback?

Faust could support reading float tables from flash if we implement this method: OwlProgram/owl.cpp at master · pingdynasty/OwlProgram · GitHub . Its default implementation

Other relevant files:

Soundfile structure

Memory reader

Soundfile Faust library includes usage examples

Added service calls for access to resource info to user patches. This works:

#ifndef __RES_LIST_PATCH_HPP__
#define __RES_LIST_PATCH_HPP__

#include "Patch.h"
#include "ResourceStorage.h"

/*
 * This patch would output all found resources one by one by ID.
 * After that it would print if settings resource can be found by name.
 * So it tests those 2 service calls plus results of list resource call that is run on startup.
 */

class ResListPatch : public Patch {
public:
    ResListPatch(){
        sr = getSampleRate();
        index = 0;
    }

    void processAudio(AudioBuffer &buffer) {
        time += buffer.getSize();
        if (time >= sr){
            if (storage.getResourceCount()){                
                if (index == storage.getResourceCount()){
                    index = 0;
                    const Resource* res = storage.getResource("__SETTINGS__");                    
                    if (res == NULL){
                        debugMessage("No settings");
                    }
                    else {
                        debugMessage("Settings found");
                        delete res;
                    }
                }
                else {
                    const Resource* res = storage.getResource(index);
                    if (res == NULL){
                        debugMessage("*EMPTY*", index);
                    }
                    else {
                        debugMessage(res->name, index);
                        delete res;
                    }
                    index++;                
                }
            }
            else {
                debugMessage("No resources");
            }
            time %= sr;
        }
    }

private:
    ResourceStorage storage;
    uint32_t time;
    uint32_t sr;
    uint8_t index;
};

#endif

Next stop: converting/loading wavetables.

Wavetable will take some time, because I’m not happy about how current OwlProgram code. How about custom RGB tables, loadable from resources on the fly:

2 tables, each taking up 8kb on flash. This is using data from the “easter eggs” headers in firmware converterted to resources, didn’t bother generating different colors (but found the file that does that).

@antisvin sorry I didn’t see your FAUST documentation PR until now. I need to turn on GH notifications! Will review and merge asap.

Ok, since I have a plugin in IDE that shows my unmerged PRs, here’s the list of what else is pending:

OwlProgram - 2 PRs
OpenWare - 5
Libraries - 1

Some of that got reviewed and is waiting for updates from me, but most didn’t.

Also, I think Faust docs should be linked from OpenWareLab/README.md at master · pingdynasty/OpenWareLab · GitHub

Why not add a few more PRs, really.

Here’s an extract from the resource test patch that reads data from flash to memory, edits it and stores back to flash:

class TestData {
public:
    int value;
};

using TestMemoryResource = MemoryResource<TestData>;
using TestStorageResource = StorageResource<TestData>;

// ....

    ResourceStorage storage;
//...
            const TestStorageResource* res1 = storage.getResource<TestData>(TEST_RESOURCE_INDEX);
            // Copy storage resource to memory resource
            TestMemoryResource res2;
            res2 = *res1;
            // Edit and store updated resource
            res2.setName("test2");
            res2.payload.value = TOKEN2;
            if (storage.storeResource(TEST_RESOURCE_INDEX, res2)){
                debugMessage("Write OK");
            }
            else {
                debugMessage("Write FAIL");
            }

Added draft docs with resource usage info

Looking into ways to add support for using multi-dimensional wavetables with morphing in OwlProgram. General ideas:

  • should be usable for varying number of dimensions (i.e. defined using variadic template)
  • wavetable morphing can be for be enabled for each dimensions with a separate boolean parameter
  • should be usable with different array types, not just FloatArray. typically it would mean ShortArray
  • sample interpolation should be optional (I mean interpolation within a single table, not between multiple tables)

Naturally, resources would be used as WT data source (but there won’t be dependency on this).

So I was reminded that we have an extra SPI NOR chip thanks to Magus kickstarter stretch goal. I’ve found driver for this chip in repo and its datasheet. STM also has a nice header for larger version of this chip.

I’m not sure how much we can achieve without memory mapped mode support. I think this would require a separate storage and different access from user patches. We would have to add a service call for copying data from resource header pointer (using it as address only).

Defrag would also be a bit harder - currently we copy data to memory, but 8Mb is too much. It’s possible to erase data in 4k or 64k chunks, so we’d have to use buffer to erase data gradually. Also, refactoring done in Daisy branch allows us to use arbitrary alignment (as class template parameter). So we could align by sectors or blocks to make defrag code simpler.

Initial support for access to resources has been merged to development branches of OwlProgram/OpenWare. This enables read access to any flash resource by name and converting resources to array data.

There’s also Magus-specific code for overriding RGB tables with custom color schemes and resource browser UI page.

The outline of the process, to add resources to C++ patches, is:

  1. Declare a Resource pointer in your Patch class:
class MyPatch : public Patch {
  Resource* myresource;
  1. In your constructor, call Patch::getResource() and get the data from it that you need
  MyPatch() {
    myresource = getResource("909kick");
    FloatArray samples = myresource->asFloatArray();
  1. In your destructor, call Resource::destroy() to clean up memory allocations
  ~MyPatch(){
    Resource::destroy(myresource);

Of course this needs the latest develop firmware and OwlProgram to actually work, and you need to first store the resource on the device as well (roughly same method as storing patches, but with slot numbers starting at 43). We’ll be publishing more info on all of this shortly as we catch up with docs and releases.

Great work on this @antisvin, thank you again for your amazing PRs!

It may be a little bit earlier to advertise this API, I expect that it would require a few more updates to get everything right in OwlWare repo.

Also, I wonder if anyone except @mcc would dare running code from development branch :wink:

Would a test be helpful? I have use for the resources API (I’m making a small sequencer) and also I was considering doing a firmware update for the reasons I describe here. The thing is tho it is very important to me to retain MIDI HOST functionality so I don’t want to upgrade master if MIDI HOST is going to break like it did in my test with d6c58cf8ec6126

Yeah, it should work for storing sequencer data. There are 2 gotchas that I can think of here:

  1. There’s currently only 12 slots allowed for resource data, one of them is occupied by firmware settings. So using it to store each track in a separate slot would be limiting.

  2. I’ve made a simple script to generate resources from binary files, tables in C headers or WAV file bodies - OwlProgram/makeresource.py at develop · pingdynasty/OwlProgram · GitHub . It’s intended to work with simple arrays, you may have to add zero padding to get correct alignment for storing datastructures.

I might be ok with a small limit of resources, especially if there is some way (in principle) to back them up to a computer. Can resources currently be loaded/stored on the computer side?

One thing that might help with the 11-slot limit is if it were possible to load or save just a part of a resource (memory mapped mode would effectively give this too). If there’s a limit of 12 resources, then my thought is I should just create one resource per patch, and have the resource contain an array of presets within it. However at that point I would be worried about running out of RAM from loading the entire combined-resource into memory, or crashing while writing a single sub-resource and corrupting the entire combined-resource. If I could load or save a range of the named resource I could avert both these problems. I haven’t checked out the header yet to see if there is anything like this.

Supporting resource downloading sounds like a good idea. I can’t find anything like that in current sources, but recently added background tasks support could be used to implement such feature. For now you should just store a copy of your resources before uploading, but it likely would be possible to download them from device later. There were other planned enhancements for this, i.e. web based tool for browsing/managing resources, resource upload in web UI, etc.

Btw, don’t worry too much about number of resource slots - it’s just the beginning. There’s an extra chip with 8MB storage that would be supported in the near future. It would further complicate some things, as we’ll have to deal with 2 separate storages. But at least it would solve the storage space issue. It does have one limitation - you’ll have to load resource from that storage to RAM, while with current flash storage you can read data directly without using an extra memory buffer.