Way to Interface PD to Display

Hi,
using plugdata with daisy seed. I really love the graphical environment of pd or plugdata!
But I would like to make some menu interface using a rot encoder and a display, so you can select and edit parameters and store a set of parameters. I have done such things with Arduino and with Micropython. But how can I bring in some additional code into the heavy system and set some floats of heavy? Or read them to display them?
Thank you!

You will have to export the C/C++ code with Daisy wrapper/generator and manually change it to create such a custom integration.

Thank you for your reply.
Well, if you have to export the C code, modify it and compile/load externally every time, you make changes in the patch, then using Plugdata does not make things more easy anymore but much more complicated!
I don’t know about synthesisers but for Guitar multi effects, potentiometers are no more used these days, because you want to store and retrieve sets of parameter values. They use rot encoders and displays, because you have more parameters than encoders. I have seen, that I am not the first, who needed the same things.
I have spend several more hours now to find ways too hook some code into Plugdata+heavy. No easy task, if you have only a vague idea, how things work… So I would be very thankful for some hints!

I would need:

  1. A place for some setup code. (In void DaisySeed::Init(bool boost) in daisy_seed.cpp ???)
  2. A place for some code in the main loop. ???
  3. Would [tabread] be a good way to get the floats into the patch? I have seen, that there should be a possibility to write into the table. hvcc/docs/05.c.md at develop · Wasted-Audio/hvcc · GitHub
    /** Returns a pointer to the raw buffer backing this table. DO NOT free it. */
    float *hv_table_getBuffer(HeavyContextInterface *c, unsigned int tableHash);

Thanks again! Christof

1 Like

Having the Compiled Mode + entire toolchain for exporting the code right there makes things a lot easier than doing it all with pd-vanilla and by hand.

I’m not sure how else you expect to get a 100% custom implementation going without diving into tho code yourself?

And yes we have done a prototype to send a specially named table onto an oled display. This actually works quite well. But you can’t do a custom menu and such in this way.

Example display code that uses an audio buffer (values -1 to 1) of 128 values. The table has the name __hv_table:

void Display() {
  hardware.display.Fill(0);

  float* table_buffer = hv.getBufferForTable(0x3D43B694);

  for (int i=0; i<128; i++)
  {
    int pixel = (int) ((table_buffer[i] * -1.0f + 1.0f) * 32.0f);
    hardware.display.DrawPixel(i, pixel, true);
  }

  hardware.display.Update();
}

Here the table is scaled from [-1, 1] to [0, 64], and inverted because the display code draws top-left to bottom-right.
It assumes the display is 64x128 pixels.

Oh wait, you want to do it the other way around and send data into the patch?

Then just use the normal hv.sendFloatToReceiver() function. However at the moment this all gets set up automatically if you have any receivers with @hv_param in place.

The setup that you want to do is really 100% custom from how the Daisy integration currently works, I’m afraid this will be a lot of manual work to implement/adjust.

Ok, for some time I hoped, that I could use this Display() as THE HOOK to abuse, as a routine with this name is compiled into the program. But it is just empty.

Well, perhaps you can in future provide some userCode() hook in the main loop and a user.cpp file, that could be modified for such cases. As far as I can see, a table would be a simple means to communicate parameters to the patch.

I must say, that this environment of Daisy Seed in combination with Plugdata and Heavy is the most complicated, that I have ever seen for a microcontroller. (((The blink example in Arduino produces 32kBytes of code… Has bloat reached the controllers?)))
I had hoped, that I could somehow make some virtual Knobs. But to find the source code, which actually does something, is not easy. It seems to work with DMA somehow…
Sorry, just lost too much time…

sorry but I can’t follow what you are trying to say.

If you want to send information from your controllers into the patch, then these are created by the board configuration and the compiler.

But what it seems like what you want is completely circumventing the Daisy integration entirely. in which case you are probably better off using the plain c/c++ output of Heavy and creating your own custom integration.

Sorry, my English is perhaps too bad.
I want to control the patch with rotary encoder(s) and a display. And to be able to store sets of parameters. Like it is done in each and every Multi Effect for Guitar since ?? years. Potentiometer Knobs are completely outdated for this application. The problem is, when you switch to a new parameter-set, then all the potentiometers are at a wrong position. Unfortunately “board configuration and compiler” are stuck with that ancient technology.
So my question was, if it would be possible to integrate a SIMPLE but universal method to integrate other means to change parameters (or do whatever). As we have seen, a table can carry several values, this is good. What would be needed, is a file user.cpp with 3 empty functions: usr_init(); user_main_loop() and usr_callback(); The compiler/linker would always bring the functions into the system at those 3 places. If someone needs some features, they can edit the user.cpp file and just put their code into these functions in that file. For example, your Display() would be called from the user_main_loop() function, which is always called in the for(;;) loop in main().

Hi,
astonishingly I was able to get the toolchain of Plugdata+Heavy to compile my C++ code into the system. At the moment only some sort of proof of concept.

There is a template file “HeavyDaisy.cpp” which is used by the heavy compiler in C:\Users\ChWEb\Documents\plugdata\Toolchain\usr\bin\Heavy_internal\hvcc\generators\c2daisy\templates which can be modified to include a user file and to call some routine userMainLoop() in the for(;:wink: loop in main().

There seems to be something wrong though in hardware.LoopProcess(); Only if this line is commented out, the for(;:wink: loop in main() continues to work??? This also prevents [print] to work.

At the moment my own code is very simple:

// ctestA.cpp


static uint32_t oldTime= 0;

void userMainLoop()
{

      uint32_t now      = 1 & (System::GetNow() / 500); // ms
      // hv.sendFloatToReceiver(hv_stringToHash("ledValue"),1.0f*now);
      // oldTime++;
      
      


   if(now != oldTime)
   {
      oldTime= now;
      // hardware.som.PrintLine("Hello from cTestA.cpp");
      // https://github.com/Wasted-Audio/hvcc/blob/develop/docs/06.cpp.md
      // bool sendFloatToReceiver(unsigned int receiverHash, float f);
      hv.sendFloatToReceiver(hv_stringToHash("ledValue"),now);
      // hardware.som.SetLed( now );
   }


}

and:

#N canvas 827 239 527 327 12;
#X obj 290 343 s led @hv_param;
#X obj 290 242 receive ledValue;
#X obj 298 435 osc~ 200;
#X obj 308 493 dac~;
#X obj 336 277 print;
#X connect 1 0 0 0;
#X connect 1 0 4 0;
#X connect 2 0 3 0;
#X connect 2 0 3 1;

After a few minutes the daisy stops working. Unfortunately…

Hm,
I now deleted all switches and the rot encoder from the json file. This made it possible to reintroduce hardware.LoopProcess(); without blocking the system. When I use [print] the patch gets stuck after some minutes. Before the print outputs are visible in Teraterm.
Is it a known issue, that [print] does only work for some time?
Thanks, Christof

[print] should only be used for quick debugging, it’s expected that over time the print buffer will fill up and the device might crash as a result. Especially if you are printing on every audio cycle this will go very fast.

It is not for production.

Thank you for your info!
Unfortunately if I don’t use [print] but use
hv.sendFloatToReceiver(hv_stringToHash(“ledValue”),now);
the crash happens too after some time.
Does this fill up something too?

No idea. With such a custom setup it’s not possible to give any specific guidance.

I’m afraid you’re in very unfamiliar territory and kind of on your own with this one.

At least you have found the Heavy C documentation, and now you’ll have to figure out your own custom integration with libDaisy.

Hi,
after some hours I am now able to use [tabwrite] and to read the result and let a led blink from CPP side according to the contents of table_buffer[0].
On the PC in plugdata [tabread] is also working as expected.
But I cannot get [tabread] working on the daisy seed. Do you know if this should work?
Thank you very much!

Without seeing your patch it’s impossible to say what is or isn’t working.
Can you perhaps share a minimal patch that localizes the problem?

I just tried this on my Field and I can change the LED value by reading from the table/array:

read value from table

This example requires saving the table data to the patch (this graphical array is set from range 0 to 1 and 128 values).

If you are using [table] object you will need to set both the name and the size, otherwise it will default to 0.

1 Like

Thank you very very much!
The missing size was the problem. I had read that there is a default size and plugdata on the PC seems not to need it.
Now I can write into the table from c++ side too and see the result.

Hi again,
I had not yet seen the “Tips and Tricks” in hvcc/docs/02.getting_started.md at develop · Wasted-Audio/hvcc · GitHub

Do you want to add that speciality of heavy to need the length of a table for tabread there? (Funnily tabwrite worked for the first element)

Also I have the impression, that to give the serial debug line for [print] some time to establish, seems to make the [print] statement stable. (???) Windows with Teraterm needs some time to reconnect after download.

One other thing seems to be, that switches or rot encoders, which are not connected and not used in the patch must be deleted from the json file. (???)

Because a length of 0 in computer code means 1 element :wink:

This would be odd. I rarely use all the features of the Field and can use the board definition just fine.
Do you have an example that others can reproduce?