Audio System Design Tool++ update

Latest update includes:

* Settings tab categories can have individual colors
(hardcoded but easy to manipulate in code)
ColoredSettings.png

* Project Tree View is now functional (except it don't update when adding/moving a node to a group)
* double click now opens the node correctly without having to uncheck the "Auto switch to info-tab when selecting/deselecting node(s)." setting
* have icons

ProjectTreeViewer.jpg
project tree can for example be used to quickly edit nodes without having to switch tabs.

* Palette categories can now have individual colors
(defined in index.html)
have also rainbow-color mode so that each cat. get it's own color automatically.

* UI_TextBox item now functional

here is a functional calculator made by
using the new textbox and some scriptbuttons
ProofOfConceptCalculator.png

* Workspace tabs have now a min width setting:
settings-Workspace-Other-"Min Workspace Tab Size"
 
Here is the calculator code if anyone is interested.
It can be imported by Import-JSON
(don't forget to uncheck the Replace Flow checkbox if you just want to import it into a existing design)

Code:
[{"id":"TextBox1","type":"UI_TextBox","name":"TextBox","comment":1928,"w":227,"h":42,"textSize":14,"x":180,"y":240,"z":"","bgColor":"#6BF8A2","wires":[]},{"id":"scriptBtn1","type":"UI_ScriptButton","name":"1","comment":"var txtBox=RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment+=d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":80,"y":290,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn2","type":"UI_ScriptButton","name":"2","comment":"var txtBox=RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment+=d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":120,"y":290,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn3","type":"UI_ScriptButton","name":"3","comment":"var txtBox=RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment+=d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":160,"y":290,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn4","type":"UI_ScriptButton","name":"4","comment":"var txtBox=RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment+=d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":80,"y":340,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn5","type":"UI_ScriptButton","name":"5","comment":"var txtBox=RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment+=d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":120,"y":340,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn6","type":"UI_ScriptButton","name":"6","comment":"var txtBox=RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment+=d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":160,"y":340,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn7","type":"UI_ScriptButton","name":"7","comment":"var txtBox=RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment+=d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":80,"y":390,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn8","type":"UI_ScriptButton","name":"8","comment":"var txtBox=RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment+=d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":120,"y":390,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn9","type":"UI_ScriptButton","name":"9","comment":"var txtBox=RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment+=d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":160,"y":390,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn10","type":"UI_ScriptButton","name":"0","comment":"var txtBox=RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment+=d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":72,"h":37,"textSize":14,"nodes":[],"x":100,"y":440,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn11","type":"UI_ScriptButton","name":".","comment":"var txtBox=RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment+=d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":160,"y":440,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn12","type":"UI_ScriptButton","name":"+","comment":"var txtBox=RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment+=d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":200,"y":290,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn13","type":"UI_ScriptButton","name":"-","comment":"var txtBox= RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment += d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":200,"y":340,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn14","type":"UI_ScriptButton","name":"/","comment":"var txtBox= RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment += d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":200,"y":390,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn15","type":"UI_ScriptButton","name":"*","comment":"var txtBox= RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment += d.name;\ntxtBox.dirty=true;\nRED.view.redraw();","w":30,"h":37,"textSize":14,"nodes":[],"x":200,"y":440,"z":"","bgColor":"#DDFFBB","wires":[]},{"id":"scriptBtn16","type":"UI_ScriptButton","name":"=","comment":"var txtBox= RED.nodes.namedNode(\"TextBox\");\ntxtBox.comment=eval(txtBox.comment);\ntxtBox.dirty=true;\nRED.view.redraw();","w":63,"h":178,"textSize":14,"nodes":[],"x":260,"y":365,"z":"","bgColor":"#DDFFBB","wires":[]}
]
 
Latest update includes:

* hardcoded default settings that is used when restoring the settings.

* the default settings is used to get and store only the changed settings in the JSON
(to minimize the size)

* each setting category have it's own restore button
SettingsCatMenu.png

* Each tab can now have individual Workspace settings (only the settings in Workspace/View category is used)
that mean each tab can have it's own background color, size, grid settings, and so on
it's good when having some tabs that are only GUI then they can have the GUI-run always active for example.

note. when changing a setting it's automatically saved in the current tab, and then saved to disk

* "Min Workspace Tab Size" have it's own category Workspaces (because of the above)
(workspaces.js the workspace tab functionality will be moved here later, just like in latest Node-Red)

* fixed settings issues that affected:
drawing of the vertical grid
useCenterBasedPositions which is a boolean, I used parseInt(value) on that
 
Oh dear. I just went to your design tool to make some edits to my project and started as I always do by trying to upload the .json from the last .zip file I downloaded and the design tools just sits infinitely showing "...." as you can see here:

designtool.png

This is pretty fatal if you ever have a situation where an "old version" JSON file cannot be reloaded.

There is a copy of the JSON that won't load here: https://raw.githubusercontent.com/wrightflyer/Synth/master/PhatBass/GUI_TOOL.json

Hopefully you can use that to work out why it sticks in an infinite load loop?
 
It was the setting called useCenterBasedPositions
Because of the bug in #28 caused it to be stored as null
An then when the setting is compared against the def value I use to string() and that cannot take null as input. It fixed so that when a project is loaded with that setting set to null its using the default value that is true.

I allways use my template project to check that everything is ok, but this time it slipped through.
 
I think your design was using non center based locations so either you have to reallign all nodes, or uncheck useCenterBasedPositions on all tabs.

I think I need to add a setting menu item to copy current tab settings to all other.
 
I have to admit that I have no idea what "useCenterBasedPositions" actually means. I guess I probably need to find out!...

EDIT: OK so I have read this...
center.jpg
and I still have no idea what it means. I guess I shall remain blissfully ignorant :)
 
first a note. the location system used only matter if snap to grid is used.
And the choice is mostly esthetic.

the popup only describes the problem when switching between modes.

Maybe I should include these two images in that popup
and also describe what non center based location means.

Center Based Location:
CenterBasedLocations.png

Top Left Based Location:
TopLeftBasedLocation.png





Center based location is the best choice when drawing the audio flow as every wire connected is then aligned to the grid
and a wire can then be completely straight when going from a output to an input.


Top Left location is to make it easier to Left align nodes that have no I/O
for example code nodes.

Here is some code nodes with different lengths, with "Center Based Locations"
as you can see it's hard to to proper left alignment
CenterAllignedNodes.png


Here is the same nodes but with "Top Left Locations"
LeftAllignedNodes.png


edit. when switching between modes the node location is not changed
 
update:
implemented indexedDB projects

so that now every time the project name is changed
there is a new "file" with that name saved to the indexedDB project "dir"

which can then be opened in the new projects menu:
ProjectsMenu.png
 
@wrightflyer
I had no idea that you could have a src sub folder in the sketch,
I have tried with using a "instruments" sub folder but without sucess, that's why I invented the DontRemoveCodeFiles node type.
This means DontRemoveCodeFiles is not needed anymore in my project, as platform io already support sub folders.

Here is a thread that verify the src sub folder.
https://forum.arduino.cc/index.php?topic=587970.0

I plan to use the indexedDB to store additional files like the instrument files, so that when exporting to zip or via the IDE extensions the project is more "complete" from the start.

Recently I also got the idea that the board settings could also be done in the Tool and also be included in the export as either "preferences.txt" (in combination with my prefsaver extension)
Or as a make file for the hardcore coders.

And maybe in the future there could be a small daemon program that links the Tool direct with the compiler and teensy loader.
 
Yup, when I first wrote my code I just had everything in the INO directory but when it came to adding 128 GM instruments (128 .cpp and 128 .h) things got a bit busy so I looked for a way to divide and modularize things. At first I thought I could just put directories alongside the directory with the INO but then I read an explanation that said that when Arduino builds it basically copies all sources to a temp directory first then builds there. It used to be limited to "all files in the INO directory" but then people pushed for an expansion of this so they agree to recursively scan a "src" sub-dir and copy all that stuff over too. So this allowed me to break things up.
 
Something has gone AWOL! To clear all tabs in order to start a new project it used to be possible to go to Examples and load "Empty Workflow" which simply removed everything but that option seems to have disappeared. So how are you supposed to clear everything to start from scratch?

EDIT: I found a workaround. Simply use "Import JSON" and have the "replace" box ticked but then press OK without having entered any text into to JSON text edit. That seems to quickly wipe everything.

(To make sure my polyphony note routing works I simply wanted to create a quick, multi-voice wavetable based piano project so I could implement the same ideas in my main synth).
 
I did some restructuring of the example menu
so that it's much easier to add new examples
(by having the menu structure json in index.html)
and to make the menu easier to structure.
For example it now have dividers.

I have now updated the Tool so that
the "Empty New Project" is back in the menu (at the top).

I have been working with a new feature that makes
it possible to select Platform, Board and options directly in the tool
and then they will be added to the exported zip
both for arduino ide pref.file and platformio.ini

and because there is a example of a make file
in the teensyduino folder I will have that exported as well.

I have implemented it by importing the different boards.txt files
they can be imported in the tool by a button in Settings-Arduino-Export-"Board settings"-"Import Boards File"
It works this way:
1. the platform is selected
2. click Import Boards File - browse and select the boards.txt file for the current platform
3. then the file is imported and saved to a IndexedDB table called otherFiles as a record "file" with the name [platform].boards.txt
4. this "file" is then reloaded and parsed to a json tree

this means there is needed one "file" for each platform in this datatable.
There will be support for boards.local.txt (to load user custom menu choices)
after the export part is completed.

It's all done except the export part which I'm working with
right "now".

There will also be a function to remove saved projects
but that will require a popup window of some sorts
and it's required that it looks nice and works correctly,
so it will take some time, maybe.

Also a function to export all saved projects and files to a zip will be added.
and functionality to regularly "save" (download) the projects and files to a zip.
(if for example the browser have some hickup and deletes everything)
 
I have now implemented the export to arduino IDE pref.file

But when doing it for the platformio is a little harder as that cannot use the boards.txt to straight generate the platformio.ini
I can do the -D [usbtype] just straight of
and -D TEENSY_OPT_[optimizationlevel] this needs some "lockup-table"

but f_cpu is not documented
how can I implement this flag?

?keyboard layout? what is this used for?
 
can the following be used in platformio.ini???
-O2
-O1
-O3
-Og
-Os --specs=nano.specs


I can now generate:
(platformio.ini)
Code:
[env:teensy]
platform = teensy
framework = arduino
board = teensy40
build_flags =  -D USB_MIDI_AUDIO_SERIAL -D TEENSY_OPT_FASTER

(arduino ide preferences.txt) by using my prefsaver extension
Code:
board=teensy40
target_package=teensy
target_platform=avr
custom_usb=teensy40_serialmidiaudio
custom_speed=teensy40_600
custom_opt=teensy40_o2std
custom_keys=teensy40_en-us
 
I have discovered some bugs that will soon be fixed

* cannot import a "old" Arduino design (i.e. designs made by the official Audio Design Tool)
* when exporting as "class based" design there is some extra stuff that should not be there
Code:
board=
custom_length=_0
[env:teensy]
platform = 
framework = arduino
board = 
build_flags =


Also I'm writing the "manual" about the new Node Addons functionality
(that is now in a working beta state).

A post will be done when the "manual" is complete.
 
Quick question... I looked at the Node Red website and it appears the only place ports can be placed on a node are left (in) and right(out). However, it also says you can only have one input port, and the Tool clearly allows more than one. So ... would it be possible to have ports along the bottom or top of a node? I'm thinking this for "control" inputs and "status" outputs, which would have one value per update cycle rather than 128 values, but otherwise behave similarly to the existing audio flow scheme. For example, the DAHDSR Envelope would have six controls for the envelope values, and two status outputs for isActive and isSustain.

If that is possible, the designs might get a bit tangled, so having multiple layers where you could turn off the visibility would probably be a good thing!

Cheers

Jonathan
 
Yes the node-red have that, I think that is mostly because it keeps the design small and tidy but you can connect many wires to one input, how they then know the order I don't know.
But they can have multiple outputs, but that is only used for subflow nodes I think.

Back to the Tool.
It's possible to place the ports anywhere, as I have done with the JunctionRL where the input and output is reversed.
When placing ports on the top/bottom the only thing that is little problematic is the wire drawing code, but the node-blue have ports on the top so I can take the drawing code from there.

By using a system similar to node-blue, the control inputs could be possible, if you just think of the wire as a variable then values can be set using a analog input or be taken from a midi parsing object, then that variable is just used as the parameter for the audio object function that represent for example setting a envelope value.

Layers should not be any problem I think,
It could also be solved by using different wire colors.
 
Yes the node-red have that, I think that is mostly because it keeps the design small and tidy but you can connect many wires to one input, how they then know the order I don't know.
But they can have multiple outputs, but that is only used for subflow nodes I think.

Back to the Tool.
It's possible to place the ports anywhere, as I have done with the JunctionRL where the input and output is reversed.
When placing ports on the top/bottom the only thing that is little problematic is the wire drawing code, but the node-blue have ports on the top so I can take the drawing code from there.

By using a system similar to node-blue, the control inputs could be possible, if you just think of the wire as a variable then values can be set using a analog input or be taken from a midi parsing object, then that variable is just used as the parameter for the audio object function that represent for example setting a envelope value.

Layers should not be any problem I think,
It could also be solved by using different wire colors.
Excellent. Not essential for Dynamic Updates to the audio routing, but I'm thinking it would be really handy for a hard/soft synth for routing physical front panel controls or MIDI control data to audio blocks.

Agree, different wire colours would help. Though if it gets complex it may not be enough:
Nord Modular Editor - [4synthsWithDist] 17042021 161413.jpg
Cheers

Jonathan
 
Node Definitions Manager

Hi,
This post will be in three parts:
1. background information.
2. how the "Node definitions manager" works + technical info.
3. future plans.

I have now made it possible to create/import custom Audio object types,
without touching the original set of objects provided by the tool.
Node-addons is saved to the "project".
Audio Object "documentation" if available is saved to browser indexedDB,
(documentaion is only possible using the html format).

It all started when Bob Larkin contacted me and asked about
how to use custom Audio Objects in the tool.
Specially the https://github.com/chipaudette/OpenAudio_ArduinoLibrary
as he was working on to implement for teensy 4+ as well.

And since I think it's better to just have one tool instead
of many spawns each containing their own set of objects
I started together with Bob to create the structure changes needed.

First I thought to do the node def. list myself, but then I created the
https://github.com/manicken/manicken.github.io/blob/master/DesignToolNodes.html

which is a kind of template for new Audio object types
that i asked Bod to fill in while I did the required programming.

Meanwhile I did also notice that chipaudette had done some
special tagging of the audio class files.
//GUI: inputs:2, outputs:1 //this line used for automatic generation of GUI node
//GUI: shortName: AnalyzePhase

which I find to be a very nice addon to the audio class files,
because it make it easier to understand how the objects are meant to be used.


from that I did think of a "easier to parse" format (in JSON):
/*GUI_NODE_DEF "AudioConvert_I16toF32":
{
"shortName":"I16toF32",
"category":"converters",
"inputTypes":{"0":"i16"},
"outputTypes":{"0":"f32"},
"color":"#E6E0F8",
"boards":["teensy36", "teensy40", "teensy41"]
}
GUI_NODE_DEF*/

where both inputTypes and outputTypes
can used as follows (to make it easier to write for multi-input/output objects):
{"x1":"i16","x2":"f32","x2":"i16"}
which will result in the following
{"0":"i16","1":"f32","2":"f32","3":"i16","4":"i16" }

there is also the n type variant:
{"n":"i16"}
which means that it's i16 for every (inputs and/or outputs) pins

note. when using the n type it wont matter if there is others defined:
{"n":"i16", "x4":"f32"}
will result in
{"n":"i16"}

I have also planned a audio object file doc tag:
/*GUI_NODE_DOC "AudioConvert_I16toF32"
<h3>Summary</h3>
<div class=tooltipinfo>
<p>(summary text)</p>
</div>
<h3>Audio Connections</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Port</th><th>Purpose</th></tr>
<tr class=odd><td align=center>In n</td><td>Input signal #n</td></tr>
<tr class=odd><td align=center>Out 0</td><td>Sum of all inputs</td></tr>
</table>
<h3>Functions</h3>
<p class=func><span class=keyword>gain</span>(channel, level);</p>
<p class=desc>function description goes here</p>
<h3>Notes</h3>
GUI_NODE_DOC*/

that can be included and automatically parsed by the tool import.

The inputTypes and outputTypes are used in the tool to match the wires when they are connected
so that non matching IO types cannot be connected.
If the data type are not defined then the default i16 is used, which is the case for all official Audio Objects.


To make it easier to import and use the nodeAddons saved with the project-JSON
I had to change the "project" structure from:

ProjectStructure_Old.png

to:

ProjectStructure_New.png

which is a thing I have been wanted to do for a long time anyway,
because it makes it easier/faster to export to cpp code/classes. (specially for extremely huge and complex designs)
Also the internal structure/usage could benefit from this as it would make it little faster.
Even if it's allready optimized to only render objects that are selected/moved.

OK that was all the background information.


Node definitions manager "manual"
Is opened from the tool "main menu" found in the (top right) side of the save button.
NodeDefMgr_overview.png

It consist of three parts

1. Left panel:
Tree view of the nodetypes grouped into groups.
The first two are readonly (embedded into the tool)
the 3:rd is autogenerated readonly and contains the "tab"-classes

2. Right panel:
contains either the selected group or node def in json format,
this can be edited and saved for "non readonly" items.

3. Menu Toolbar
add:
single add new group/node def.
only add new group is having a form to fill in,
the add new node def. is using json to edit the data.

new group dialog:
NodeDefMgr_newGroup.png

new node def. type dialog:
NodeDefMgr_newType.png

import:
from url: downloads the node def. from a internet url
(server need to allow CORS, best is to use Github)
from file: not implemented yet (should work just like the above)
refresh: not implemented yet (should update the current addon from the url given)

remove: removes current selected group/node def. (only for "non readonly" items)
note. using a confirm dialog

export: exports all current groups and node def. to a json file
(can only be imported by uploading it to Github in a html file using the DesignToolNodes.html format above)
this is only intented for debugging but can be used to export node def. when creating own libraries

apply: saves and applying the current edited item ("non readonly" can only be saved)

How to use import from URL dialog (and how it works)
NodeDefMgr_importFromUrl.png
the URL:s can be in the following formats:

if it ends with html
and starts with "https://github.com" then the url is changed into the raw file url format,
("https://raw.githubusercontent.com")
else the url is not changed
note. the NodeDefinitions format can either be "classic" format
or the new group based format.

else if it starts with either
"https://github.com" or "https://api.github.com"
and is a url to a folder
then all the .h files in the folder is downloaded and
scanned for the chipaudette //GUI: tags described above.
this is shown in a new form dialog

after the correct supported url is given
it shows what format it's in
and enables the download button

if the old NodeDefinitions html format is used
then both the Mode and UID fields are shown so
that the user can input the UID needed.

When everyting is filled in and the download is completed the OK button is enabled,
which will when pressed, import the downloaded node defs.



Maybe future features:

* download required files required by the node addons
usable when exporting as class based zip file,
which will then include all files required for the "project"

* import from local html file (maybe not used anyway)

* import from local zip file
which will also contain all required lib code files

Please feel free to post if there is something that you don't understand or if something is missing.

note.
the above formats is not set in stone, and suggestions are welcome .
 
I forgot the html file url which can be used together with
https://github.com/chipaudette/OpenAudio_ArduinoLibrary

It's a updated version of the html file from
http://www.janbob.com/electron/OpenAudio_Design_Tool/index.html

It's using the new format with inputTypes and outputTypes,
so that i16 ad f32 objects cannot directly be connected together.
(only by using the convert objects).

here is the url to the modified version
https://github.com/manicken/justSomeFiles/blob/main/OpenAudio_F32.html

it's imported like this:
top right menu - node def. manager

then in node def. mgr.
import - from url

copy paste the html url into url field and press enter
then click download and finally ok to import the Open Audio F32 library Audio Objects.
 
Hi manicksan

OK, so having posted my first effort at a dynamically-configurable audio engine DCAE (https://forum.pjrc.com/threads/66840-Roadmap-quot-Dynamic-Updates-quot-any-effort-going-on, as you know, but others might not have seen..), I'm taking a better look at your GUI++. For my revised demo I hand-coded some voice classes, but I can see that your GUI++ will very nearly do everything for me that I required. Possibly it does it all, but I've missed some stuff... Also, I'm a novice at C++ so there may be ways to do things I've not spotted yet. Some of the following points only apply when using the DCAE.
  • I can't see how to specify an array of voices to drive your variable-inputs mixer
  • I can't see how to define a static class member's value: I believe it has to be done at file scope, not within the class, defined after the class declaration, and qualified with the class name
  • It would be good to have a destructor (optionally) generated automatically, to delete the AudioConnection objects created in the constructor. Or construct them with initialisers. More below...
  • ...and/or have a destructorCode "special object" - but it would still be good (if necessary) to have some part of its body automatically generated, if needed
  • I found it useful to derive every voice class from a base class, as it means for the DCAE I can just delete voices without needing to know their exact class. Again, more below.
  • I can't see how to save a project and load it later, apart from "downloading" a JSON file for later import
You've chose to construct your AudioConnection objects in the voice's constructor code, using new, which would leave them consuming memory after the voice containing the pointers is destroyed. That's academic for the static audio engine (SAE), which can't destroy anything anyway. I found that this method works OK, is compatible with the SAE, and requires no effort in the voice destructor to delete the AudioConnections:
Code:
private:
    AudioConnection cord1;
    AudioConnection cord2;
  public:
    WaveAndEnvVoice() : cord1(wave,env), cord2(env,amp)
    {
    };

On the use of a base class, consider the following:
Code:
#include <Audio.h>

/*********************************************************************************/
class SynthVoice
{
  AudioConnection outputCord;
  virtual AudioStream& getOutputStream(void) = 0;
  public:
    virtual ~SynthVoice(){};
    virtual void noteOn(float freq, float vel, int chan=-1) = 0;
    virtual void noteOn(int MIDInote, int MIDIvel, int chan=-1) = 0;
    virtual void noteOff(void) = 0;
    virtual bool isPlaying(void) = 0;
    int connect(AudioStream& str) { return connect(str,0);}
};


    
/*********************************************************************************/
class WaveAndEnvVoice final : public SynthVoice
{
  // the actual synthesis engine and its connections
public:
    AudioSynthWaveform wave;
    AudioEffectExpEnvelope env;
    AudioAmplifier amp;
private:
 // connections need to be defined after the objects they connect, 
 // in order for initialisation to work properly
    AudioConnection cord1;
    AudioConnection cord2;
    bool isNew;
  
    static short wave_type[4];
    
    AudioStream& getOutputStream(void) {AudioStream& result {amp}; return result;};
  public:
    WaveAndEnvVoice() : cord1(wave,env), cord2(env,amp), isNew(true)
    {
      env.attack(129.2);
// ... etc.
    };
    ~WaveAndEnvVoice(){};
    void noteOn(int MIDInote, int MIDIvel, int chan=-1){}; // don't support this
    void noteOn(float freq, float vel, int chan=-1)
    {
// ... do some stuff
    }

    void noteOff(void){env.noteOff();};
    bool isPlaying(void) {return env.isActive();};
    
};

short WaveAndEnvVoice::wave_type[] = {
    WAVEFORM_PULSE,
    WAVEFORM_SQUARE,
    WAVEFORM_SAWTOOTH,
    WAVEFORM_TRIANGLE}; 


/*********************************************************************************/
class SomeOtherTopologyVoice final : public SynthVoice
{
}


/*********************************************************************************/
#define POLYPHONY 12
SynthVoice* voices[POLYPHONY] = {NULL};

makerFunction(void)
{
   voices[0] = new SomeOtherTopologyVoice;
   voices[1] = new WaveAndEnvVoice;
}


void breakerFunction(int v)
{
  if (NULL != voices[v])
  {
    delete voices[v];
    voices[v] = NULL;
  }
}
[I may have used virtual unnecessarily here, as I say, I'm a beginner at C++. But it works.] By using the SynthVoice base class, I can have voices[] be an array of active (sounding) voices of any class derived from SynthVoice, and when they stop sounding (or whatever criterion is used to decide destruction is needed), breakerFunction() can be used to destroy them, whatever derived class they happen to be. For your GUI++ tool, it may be better to let the user code the base class and the additional class definition syntax (in my case " final : public SynthVoice"), for full flexibility. Oh, and note the definition of short WaveAndEnvVoice::wave_type[], which I can't find a way to do in GUI++.

Cheers

Jonathan
 
Back
Top