Skip to content
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

How to create a plugin #73

Closed
pierreguillot opened this issue Jan 20, 2018 · 19 comments
Closed

How to create a plugin #73

pierreguillot opened this issue Jan 20, 2018 · 19 comments

Comments

@pierreguillot
Copy link
Owner

pierreguillot commented Jan 20, 2018

Just a quick answer to let you experiment:

Now the plugin can work without any GUI (in the previous versions parameters was created depending on the GUI in the patch). So here are the most importants things to know apart from the GUI.

  • [adc~], [dac~] are used to receive and send audio.
  • [notein], [noteout], [ctrlin] and all the MIDI objects are used to receive and send MIDI.
  • [r param] is used to receive parameters' values with a list index value. The index starts with 1. Look at the abstraction [param.get] in the plugins folders if you want an example to see how to use it. The best approach for example if you want to receive the value of the 1st parameter but to avoid duplicates: [r param] -> [route index] -> [change].
  • [r program] is used to receive program (preset) changes with the index of the program. The index starts with 1.
  • [r bus] is used to receive channels configuration used (when the DSP starts or the configuration is changed). The message selector is the type of channels input or output, followed by the number of channels and a string that describes the channels. For example input 2 stereo. Important: multi-buses and side chain are still experimental, it could change in the future

The text file is used to defined the behavior of the plugin, here are the options:

  • type defines the type of plugin effect or instrument. It is important because the user must know which binary to use to generate the plugin. If the binary used is different from the type defined in the text, you will receive a warning in the console.
  • code is a 4 character string with at least 1 upper case used by the DAW to identify the plugin. It must be unique per plugin.
  • compatibility must be the version of the plugin with which the patch has been created (for example v1.0.2). The plugin uses this to ensure that its compatibility with the patch. If the version of the plugin is inferior to the compatibility version then plugin posts a warning.
  • midiin defines if the plugin accepts MIDI events, it must be true or false. It can be used for effect and instrument plugin.
  • midiout defines if the plugin generates MIDI events, it must be true or false. It can be used for effect and instrument plugin.
  • midionly defines if the plugin generates and receives only MIDI events without audio, it must be true or false. It can be used for effect and instrument plugin.
  • playhead defines if the plugin wants the play head informations, it must be true or false. It can be used for effect and instrument plugin.
  • key defines if the plugin wants to receive key event via the objects key, keyup and keyname, it must be true or false.
  • latency defines the latency of the plugin is samples (can also be changed dynamically).
  • taillength defines the tail length of the plugin is seconds.
  • bus defines a new bus for the plugin and must be followed by the number of inputs and the number of output. For example bus 2 2 if you want to add the support for 2 channels in and 2 channels out and bus 4 8 if you want to add the support for 4 channels in and 8 channels out. Important: multi-buses and side chain are still experimental, it could change in the future
  • program defines a new program (preset) for the plugin and must be followed the name of the program. For example program zozo. The order of the programs corresponds to their order in the text file from top to bottom.
  • param defines a new parameter for the plugin and can be followed by a set of optional arguments. The arguments can be the name -name, the label -label, the minimum value -min, the maximum value -max, the default value -default, the number of steps -nsteps, if the parameter is automatable -auto (it's true by default), if the parameter is a meta parameter (if it controls other parameters, false by default) -meta. If you want to create a parameter that displays a list of possible values for a waveform for example you can use the argument -list where all possible values are escaped with /. In this case, the minimum is 0, the maximum is the number of elements in the list minus one and the number of steps is the number of elements in the list (if you set one of these options with the list option you will receive a warning in the console). So here are two examples:param -name Frequency -label Hz -min 0 -max 5000 -default 500; for a "numeric parameter" or param -name Waveform -list Triangular/Sawtooth/Square; for a "list parameter".
  • image can be the name of an image file (for example myimage.png). The plugin displays the image as the background of the graphical interface.
  • description can be a short sentence (for example A chorus effect) or the name of a text file (for example Infos.txt). The plugin displays the text in the second tab of the auxiliary window. The text can be used to describe what the plugin does and how to use it but also the author, the credits and the version.
@alfonso73
Copy link

thanks pierre! as soon i finish a huge mixing/arranging work (deadline moved to mid february due to executive production delays) i'll be back helping on Camomile!

@pierreguillot
Copy link
Owner Author

pierreguillot commented Feb 15, 2018

How to use openpanel and savepanel:

From the patch, send the message openpanel or savepanel to the plugin via the symbol camomile. You can use an optional argument for the default path (for example openpanel ~/Desktop/). The plugin will show a dialog window and once a file has been selected, it will send the path of the file via the symbols openpanel or savepanel.

Here is a patch for the example.
Panel

Another example that shows how to route the result of the openpanel or savepanel functionalities and use it for different purposes:

Double

Further information on this issue #71.

Important: Most of the time, you will use this feature to load or to save a file from/to your hard drive. If the files are large, Pure Data takes time to perform the operation. At best, it generates glitches and crackles but several DAW simply crash because audio doesn't wait! To avoid such problem, you should use the flag -s that suspends the processing while you receive the results.

LargeFile

@pierreguillot
Copy link
Owner Author

pierreguillot commented Feb 19, 2018

How to save and load extra data with the state information of the plugin:

When saving its state information, the plugin sends a bang to the patch via the symbol save. The patch can use this notification to send back data that will be saved by the plugin using the message save followed by arguments via the symbol camomile. The plugin can send back several messages if needed.

Here a simple example that demonstrate how the plugin can save the content of two arrays with the plugin state information:

Save

When loading a state, the plugin sends back the messages via the symbol load. If several messages have been saved with the state, all these messages will be sent back in the same order.

Here a simple example that demonstrate how the plugin can set the content of two arrays with the plugin state information:

Load

Important: Some DAW don't load the state information of the plugin if the plugin doesn't have any parameter. To ensure that the method will be called, you can create a dummy parameter (with the property -auto false if you don't want it to be automatable). Most of the time, the DAW will save the state information just after loading them, so you should avoid using mechanism that implies a delay.

Here another example with a text object:

Text

@alfonso73
Copy link

Great advancements! Thanks!

@pierreguillot
Copy link
Owner Author

How to display and change the content an array:

The future version 1.0.4 allows to use graphical interface of the array in Camomile to visualize and to change its content:

array-gui

But the version also offers a mechanism to display the content of the non-graphical array define object in a floating window like when the user clicks on it. From the patch, send the message array followed by the name of the array via the symbol camomile to open a window that displays the content of the array:

array-window

If the user changes the content of the array by clicking on it, the plugin sends back to the patch the name of the array that has been modified via the symbol array. Thus, the patch can use the information to retrieve the content of the plugin via the object array get:

array-mec

@Reinissance
Copy link

First of all let me thank you for this awesome work!
While working on arrays you probably have to deal with Graph-on-parent, dont you? As I‘m missing the Feature to display guis placed in abstractions...
Also, would it be possible to have the ability to redraw the gui after dynamically changing the surface of the patch with messages to pd like „ ; pd-dynamicPatch.pd obj 100 100 myAbstraction with arguments;“ ?
Regards, Reini

@pierreguillot
Copy link
Owner Author

For the array/GOP, I think you missed the 1st line ;)

The future version 1.0.4 ....
You can compile the version or wait few weeks for the release

For the moment dynamic patching is insecure but we can investigate the subject. Feel free to create a new specific issue.

@pierreguillot
Copy link
Owner Author

pierreguillot commented Mar 16, 2018

How to change the dynamically change the graphical interface:

The future version 1.0.4 will allow to dynamically change the graphical interface.

After changing the size of the visible area the patch, you can notify the plugin to resize the graphical interface by sending the messages [gui resize( via the symbol camomile. The operation will also redraw all the content of the graphical interface. If you just want to redraw the graphical interface because the margins of the patch changes, the objects changed or whatever, you can use the [gui redraw( via the symbol camomile.

Here is an example:

ezgif com-optimizebam

@pierreguillot
Copy link
Owner Author

How to change dynamically the audio options (latency)

The future version 1.0.4 will allow to dynamically change the audio options.

To change the audio specifications from the patch, you can use the audio method of the plugin by sending the message audio followed by arguments via the symbol camomile. For the moment, the only option that can be dynamically changed is the latency of the plugin in number of samples. So to change the latency - if the sample rate changed, the size of an FFT changed or for any other reason - you have to send the message [audio latency value( to the object [send camomile].

screen shot 2018-03-17 at 2 50 47 pm

One would like to change the tail length dynamically but it's not supported by most of the DAW. So it's more suitable to use a very long tail length that will suit all the possibilities (see this thread).

@pierreguillot
Copy link
Owner Author

Reload the patch dynamically

The future version 1.0.4 will allow to dynamically reload the patch.

As explained previously, some characteristics of the plugin can be changed after the the loading like the audio configurations supported, the parameters, etc. But as all these static characteristics are defined in the text file, nothing prevent to dynamically reload the patch. To do so, you only have to click on the fourth icon of the console (and now the console uses the icons set created by Gregor Cresnar because they are simple and pretty while mine were complicated and ugly).

reloadicon2

The patch can also be reloaded automatically each time you save it - each time the operating system assumes that the patch has been modified - by adding the entry autoreload to true in the text file.

autoreload true;

This feature can slow a bit the plugin so don't forget to set the value to false (or to remove the line) when you don't need it and especially when the plugin is ready to be released.

@alfonso73
Copy link

Hi @pierreguillot, can you make an example of using the "playhead" feature?
thanks!

@Reinissance
Copy link

I thing the patch attached will be a start...
also have a look here
playhead.zip

@alfonso73
Copy link

Thanks @Reinissance !

@pierreguillot pierreguillot added this to To do in v1.0.5 Apr 6, 2018
@pierreguillot pierreguillot moved this from To do to In progress in v1.0.5 Apr 7, 2018
@pierreguillot
Copy link
Owner Author

I have updated the documentation on how to create plugins, it's still a rough draft but I integrate all the information provided by this issue and I changed a lot of things. Now I prefer to close this issue :)

v1.0.5 automation moved this from In progress to Done Apr 16, 2018
@J-Ginkgo
Copy link

Hi @pierreguillot, I am trying to figure out a way to use several tables or arrays. But Camomile seems to always overwrite every array in the patch, no matter the name. Do I miss something in the documentation? I use it like that
arrays.zip
Thank you in advance!

@pierreguillot
Copy link
Owner Author

@J-Ginkgo Yes, the [openpanel] and [savepanel] methods are global. You can use the small trick that allows to route the message, as explained in the documentation (there is also a patch): https://github.com/pierreguillot/Camomile/wiki/How-to-create-new-plugins#openpanel-and-savepanel-support.

@J-Ginkgo
Copy link

@pierreguillot thats it, thank you very much! Yet I have another question concerning multichannels. I tried to have 6 outputs in my patch. Inputs are not needed. In my properties file I put the entry: bus 0 6; In Reaper I routed it to the channels but I only get channel 1+2. Is that feature still in experimental work so it doesnt work yet?

@pierreguillot
Copy link
Owner Author

@J-Ginkgo Multichannel should work. Please open a new issue.

@J-Ginkgo
Copy link

J-Ginkgo commented Nov 6, 2018

@pierreguillot sorry nevermind. It works now. I didnt find out why it didnt work in the first place though

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
v1.0.5
  
Done
Development

No branches or pull requests

4 participants