Forum Rule: Always post complete source code & details to reproduce any issue!
Page 12 of 12 FirstFirst ... 2 10 11 12
Results 276 to 300 of 300

Thread: Open Sound Control (OSC) Teensy Audio Library Implementation

  1. #276
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    I did just realized that I only allow one destination
    In AudioHalfConnection:
    int connect(AudioStream &audioStream, unsigned char audioStreamPortIndex)

    The connected flag should only be set to true when the "dir" is 1 (input)

  2. #277
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    337
    I've been looking at your AudioHalfConnection concept, and a thought occurred to me that it may be simpler than I realised. Every input can have only one source, and therefore the class it belongs to can (should?) provide an AudioConnection for it. However, the outputs can have more than one destination, and therefore cannot provide AudioConnections; but that's OK, because they will be connected to (class) inputs which do provide a connection. (There's a possible exception at the root level, but that's probably not a major issue).

    I think what confused things is that I said classes should provide both input and output AudioConnections, which in hindsight is clearly nonsense, because even with a simple one-to-one connection topology, you end up with twice as many AudioConnection objects as you need.

    So for class export purposes I think we need (in addition to the internal connections) an extra AudioConnection for every patchcord emerging from a TabInput node, with an associated connectInput(src,port) function; and only a connectOutput(dst,port) function for every patchcord entering a TabOutput node. For multi-port TabInputs or TabOutputs the functions could be connectNode(nodePort,srcORdst,port) with just one connectFunction per node.


    The OSCAudio::callBack() function is intended to scan the "tree" of objects looking for matches, and then execute a callback function whenever it finds a complete match; the scan doesn't have to start at the root, or recurse into groups. It's used to find, count, create, delete and rename objects. Sometimes there's a find within a find: for example, when a "leaf name" is matched during a rename, another callback search is done starting within the current group to check for any matches to the new leaf name, in order to prevent duplicate names. Maybe the name is confusing, and I should call it scanGroups() or something, though it's now marked private so it may matter less than it did...


    In other news, I've tidied up the access specifiers in OSCAudio, so you shouldn't be able to get to anything really dangerous. Because I'm lazy I've left a few members public to aid debugging, e.g. using the listObjects() utility. While I did that, I found there wasn't a proper error code if you tried to create a duplicate group name, so I fixed that. And the OSCAudioMIDIsubsFS.ino example has a /system/listObjects function added, so you can request a dump of the current topology to the serial port.

  3. #278
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    337
    I've done a minor update to the OSCAudio library, to go with v0.9-alpha of the Audio library. This adds a "soft knee" option to the multi-width mixers, so if you get close to or exceed the dynamic range the output is truncated more gracefully.

  4. #279
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    OSC live updating while using dyn. mixers is really hard.

    things that currently don't work while edit live:

    1. changing the size of any connected array source, or changing it to/from array
    if you want to add a array source live, you need to do a complete export

    2. removing any of the 'first' wires of mixer ( only remove/add them from the end)
    otherwise the names get the wrong ones, as they are autogenerated
    could solve it by storing the names somewhere when the links are first created

    3. changing the 'internal' structure of any classes live (have a function that gets the ref. paths, but not still used)

    4. surely still other things that I did not think about

    but otherwise you can:
    1. export the whole design

    2. remove/add links from/to the end of mixer
    that have either array/buswire sources

  5. #280
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    Did some fixes
    now the complete export-data is stored in each link, when doing complete export and creating links live
    that means links can now be removed in any order from the dyn. mixers,
    however it do not change the size of the mixer when removing links

    Consider the following example:
    Name:  DynMixerLiveEdit.png
Views: 185
Size:  4.9 KB

    So if for example
    1. wavesB[2] link is removed
    2. array size is changed to 4 (wavesB[4])
    (note. see note1 in the end)

    3. wavesB[4] link is reconnected to the mixer (if note1 was possible)

    3alt. there was a alternative wavesD[4] that would connect in place of wavesB[2]

    then the mixer would not resize to fit the new wavesB size
    that would mean that the last links from the wavesB[4] would not connect

    but anything that fits the gap i.e. have the same size
    (as wavesB have stereo out that would mean 4 inputs)
    are now free in that gap

    the only current way to resize the mixer is to remove wavesC as well
    and connect wavesB[4] before reconnecting wavesC again


    note1.
    as stated before array sizes cannot still not be edited live
    that would require
    1. the group would need additional items to be added
    (if the items are classes that would mean some wires+additional items as well)
    and if changed to/from array would require removal of the whole group
    2. resizing of the whole mixer if wires are connected,
    resizing the mixer always require all links to first be removed + the mixer
    then a new sized mixer is added + all old + new links

    think I will store all export data in every item
    links (already done)
    nodes
    classes
    then it would be much easier to delete/rename things

  6. #281
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    337
    It's definitely a challenge - not sure how much help I can be, but here are some thoughts.

    As a general principle, I've found it very error-prone to store duplicates of information, unless the duplication is very local and guaranteed to be changed when its "parent data" are changed. For example, it'd be easy to lose track of connection names if they contain port numbers, and those port numbers might change. Much better to have a single function that can create the connection name from the data structure, even if it takes a bit of CPU time and crawling over the structure every time you need to figure the name out.

    I've taken a look at the exported JSON, and I may have spotted some "improvements" which would (I think!) make life easier. The nodes' "wires" properties are arrays of arrays of strings, and they might be better as arrays of arrays of objects. As it is, the strings contain the destination node name and port separated by a colon, so the new object would be something like {"dst": id, "port": number}. The port number must be the logicalport number only, i.e. a number between 0 and N-1 where there are N visible connections on the design. The physical port numbers could well be different, and should be calculated on the fly when live editing or exporting. In a similar way, you currently have the "name" of a node possibly indicating that it is an array, if it has a number in square brackets at the end. I think this should be an "arraySize" property (zero for a non-array), with the name value stored without the array indicator, and the displayed string re-created only for display purposes. This should make it easier to scan the design structure figuring out the physical connection counts for a mixer or tab.

    As far as I can see you have to be prepared scan the entire design structure for almost any change to a bus-capable entity, because connections can go between groups. Similarly, the only way to "resize" anything is actually to destroy the old one and re-create it at the new size: maybe not groups, you can delete the last few members if it's getting smaller, or create just the new ones if it's getting larger. But definitely the variable-width mixers...

  7. #282
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    The structure for the wires internally
    are not the same as the saved JSON
    (that is the way the Node-RED team saves some space for the exported JSON, or actually I don't really know why it's like that, but it could be)

    the internal working structure have all wires saved into a separate array
    var links = [];

    with each wire having the following structure
    {source, sourcePort, target, targetPort}
    where source and target are references to the actual node:s

    also then imagine if that structure was saved to the JSON
    then it would look like this
    {"sourceId",sourcePort,"targetId",tagetPort}
    that would mean the sourceId + sourcePort would be duplicated for every output wire from that specific source, so my guess about space saving is surely correct

    back to the JSON structure, yes the arrays of arrays look a little strange
    but I would not currently prioritize changing that, as it's not actually a problem
    as it's only in the saved JSON,


    Yes I could do the array thing like you propose
    and show the displayed text name[] for arrays
    and only name for non arrays.
    That's really much possible.
    It would however need to convert 'old' style array def. nodes
    but that should not be any problem.

    Good that I use a 'Global editor' for most of the audio objects,
    as I only need to change that to add the array def. property.
    Can also use my autogen feature, as I would need to add the new property to every object anyway.


    Yes I do rescan the whole design for every change,
    what it also do in the background is sorting every object by columns (top-bottom)
    before saving the JSON to local storage, it's crazy how fast JavaScript really is.

    Did you see my post when It first outperformed both python and C# for Serial data receive?

    The other day I did however see a bug for the Serial receive
    it was suddenly unable to receive any data,
    (transmit was ok, could verify that by the debug port)
    and I had to restart my computer to solve that.

  8. #283
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    Quote Originally Posted by manicksan View Post
    Can also use my autogen feature, as I would need to add the new property to every object anyway.
    While I do that, and did just discovered a easy way of joining objects together
    (by using ES6 spread operator)

    I will define two Global node def.

    Code:
    var NodeTypeBase = {
        "defaults":{
            "name":{"type":"c_cpp_name_no_array"},
            "id":{"noEdit":""},
            "comment":{},
            "color":{"type":"color"},
        },
        "editor":"autogen",
        "shortName":"newType",
        "editorhelp":"",
        "inputs":0,
        "outputs":0,
        "category":"",
        "color":"#E6E0F8",
        "icon":"arrow-in.png"
    };
    var arraySize_Help = "selects the array size,<br>a value of 1 mean no array<br>the max value is 255";
    var NodeTypeArrayBase = {
        ...NodeTypeBase,
        "defaults":{
            ...NodeTypeBase.defaults,
            "arraySize":{"value":1,"maxval":255,"minval":1,"type":"int",
                "editor":{
                    "label":"Array Size","rowClass":"form-row-mid",
                    "help":arraySize_Help
                }
            }
        }
    };
    they can then by used on all node def

    some examples:

    Code:
    "AudioInputI2S":{...NodeTypeBase,"shortName":"i2s","outputs":2,"category":"input-i2s1"},
    "AudioOutputI2S":{...NodeTypeBase,"shortName":"i2s","inputs":2,"category":"output-i2s1"},
    "AudioMixer4":{...NodeTypeArrayBase,"shortName":"mixer4","inputs":4,"outputs":1,"category":"mixer"},
    "AudioSynthWaveform":{...NodeTYpeArrayBase,"shortName":"waveform","outputs":1,"category":"synth"},
    that makes it much easier to add/remove properties on all node types
    Last edited by manicksan; 02-26-2022 at 01:12 AM. Reason: removed unnecessary comment after ...NodeTypeBase.defaults,

  9. #284
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    337
    I'm having a play with exporting an OSCAudio-capable design to C++ classes. It's going to be slow because my Javascript is pretty much non-existent and I'm totally unfamiliar with the data structures involved... will do PRs if I think I've come up with anything useful.

  10. #285
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    sounds good

    Because of that, I have now implemented a draft for exporting as OSC style c++,
    It's using the 'Export Mode'-toolbar setting to select if OSC c++ export should be used.
    Name:  ExportModeOSC.png
Views: 155
Size:  10.3 KB

    note. the setting is stored in the RED.arduino namespace,
    but do not have a setting in the settings-tab, as there is no point having it at two places

    I did also do some rework of my code
    specially getTypeName and RED.export.links.getDynInputDynSize is now used directly instead of wrapping it into getDynamicInputCount

    actually RED.export.links.getDynInputDynSize(node) is a wrapper for RED.export.links.getDynInputDynSizePortStartIndex( node, undefined);
    but it is at least at the same namespace/module

    also did a draft to handle autogeneration of AudioMixerStereo code
    but there is no code in mixersCode.js for the mixerStereo

    Then I wonder what would be the downside of only using the dynmixer:s for everything,
    and having them as the standard mixers for the whole audio lib?


    Currently the draft OSC c++ export (as usual ) don't support the following:
    * arrays don't get proper osc names
    * the AudioConnections don't get the OSC names
    * OSC groups
    * busWire (not supported at any c++ export modes either)
    * array source with multiple outputs connecting to the same mixer

  11. #286
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    After some additional thinking

    OSC export/mode maybe need a separate setting/checkbox?
    or should all possible modes exist in the 'combobox'?

    here are the all (in theory) possible export situations

    OSC live whole design export
    OSC live partial edit

    OSC c++ simple export
    OSC c++ class based export
    OSC c++ class to zip export

    c++ simple export
    c++ class based export
    c++ class to zip export


    mostly think that a user only use one export mode for one project
    and therefore there only needs to be one export button?

    also one plan is to use the export mode to verify the design.

    but in the end that would not be needed,
    as there should only be one set of rules that apply for all modes.

    Think the simple/class export modes could be minimized to
    some autodetect functionality,
    i.e. if there is any class/tab-node at the 'Audio'-main then the class export should be used,
    otherwise the simple plain export could be used.

  12. #287
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    Think we need a special case while having array of objects inside a class while using the OSC c++ export

    consider your example:

    Code:
    class OSCVoice1grp : public OSCAudioGroup
    {
    public:
        // the actual voice elements we want to group together
        OSCAudioGroup& wav; // this would be holding the array of wav:s
        OSCAudioSynthWaveform* wavs[4]; // to hold the actual instances
        //OSCAudioSynthWaveform&  wav1;
        //OSCAudioSynthWaveform&  wav2;
        OSCAudioMixer4&         mixer;
        OSCAudioEffectEnvelope& env;
    
        // internal patch cords
        // total patchCordCount:3 including array typed ones.
        OSCAudioConnection*     patchCord[5];
    
        // create as sub-group
        OSCVoice1grp(const char* _name,OSCAudioGroup* parent) : // constructor 
          OSCAudioGroup(_name,parent), // construct our base class instance
          wav(*new OSCAudioGroup{"wav", ((OSCAudioGroup*) this)}), // group is not a pointer argument here????
          //wav1(*new OSCAudioSynthWaveform{"wav1",*((OSCAudioGroup*) this)}),
          //wav2(*new OSCAudioSynthWaveform{"wav2",*((OSCAudioGroup*) this)}),
          mixer(*new OSCAudioMixer4{"mixer",*((OSCAudioGroup*) this)}),
          env(*new OSCAudioEffectEnvelope{"env",*((OSCAudioGroup*) this)})
        {
            int pci = 0; // used only for adding new patchcords
            OSCAudioGroup& grp = *((OSCAudioGroup*) this);
            char arrayItemName[5]; // ixxx'\n'
            char pcName[20];
    
            for(int i=0;i<4;i++) {
                sprintf(arrayItemName, "i%d", i);
                wavs[i] = new OSCAudioSynthWaveform(arrayItemName, wav);
                sprintf(pcName, "wav_i%d_0_mixer_%d", i, i);
                patchCord[pci++] = new OSCAudioConnection(pcName, grp, *wavs[i],   0, mixer, i);
            }
            patchCord[pci++] = new OSCAudioConnection("mixer_env", grp, mixer, 0, env,   0);                
        } 
    
        // create at root
        OSCVoice1grp(const char* _name) : OSCVoice1grp(_name,NULL) {}
    
        // no destructor needed, the base OSCAudioGroup   
        // destroys its members when it's destroyed
    };
    One thing about the current state of the arduino exports
    they still use the exported structure
    which makes that code a bit slower and also ineffective
    because of a lot of unnecessary lockups

    The export simple is mostly done
    only need to replace the RED.nodes.eachWire

    the class export is kind of a mess,
    and in the current state would make implementing OSC export harder than necessary

    if you look at the simple export you can see that I go through all nodes one time
    and utilize different variables to contain the different parts of the exported code
    those parts are then put together in the end

    var cpp = getCppHeader(jsonString, includes, false);

    // here I add the different mixer variants not included in this example

    cpp += "\n" + codeFiles + "\n" + defines + "\n" + cppAPN + "\n" + cppAC + "\n" + cppCN + "\n" + globalVars + "\n" + functions + "\n";
    cpp += getCppFooter();


    that is also needed for the OSC class export

  13. #288
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    337
    Yes, it's not easy to follow! And I'm not 100% sure which parts might be original NodeRED code that's best left untouched. I've made some progress and pushed it up to the development branch on my fork just now. It won't result in working code, but it's getting there. I think I have some of the possible very weird cases covered - the test design is this at the moment:
    Code:
    {"version":1,"settings":{"main":{},"OSC":{},"arduino":{"ExportForOSC":true,"useExportDialog":true,"ProjectName":"MultiTimbralLinEnv","Board":{"Platform":"","Board":"teensy41","Options":""}},"BiDirDataWebSocketBridge":{},"workspaces":{},"sidebar":{},"palette":{},"editor":{},"devTest":{},"IndexedDBfiles":{"testFileNames":"testFile.txt"},"NodeDefGenerator":{},"NodeDefManager":{},"NodeHelpManager":{}},"workspaces":[{"type":"tab","id":"Main","label":"Main","nodes":[{"id":"20220302T113319_446Z_335b","type":"AudioSynthWaveform","isClass":false,"name":"LFO","comment":"","x":90,"y":260,"z":"Main","bgColor":"#E6E0F8","wires":[["20220302T113305_119Z_4b2:0","20220302T113305_119Z_4b2:1","20220227T150834_852Z_11ed:3"]]},{"id":"20220228T194812_823Z_dfa8","type":"SampleAndHold","isClass":true,"name":"SnH[6]","x":265,"y":170,"z":"Main","bgColor":"#CCFFCC","wires":[["20220227T150834_852Z_11ed:0"]]},{"id":"Main_MinHammond1","type":"MinHammond","isClass":true,"name":"HammondVoice[8]","x":235,"y":230,"z":"Main","bgColor":"#CCFFCC","wires":[["20220227T150834_852Z_11ed:2","20220302T170700_396Z_4a3b:0"]]},{"id":"20220302T113305_119Z_4b2","type":"WaveFormVoice","isClass":true,"name":"modWaves[2]","x":240,"y":290,"z":"Main","bgColor":"#CCFFCC","wires":[["20220227T150834_852Z_11ed:1","20220227T150834_852Z_11ed:4","20220302T170700_396Z_4a3b:1"]]},{"id":"20220227T150834_852Z_11ed","type":"AudioMixerStereo","isClass":false,"name":"mixerI2S","comment":"","inputs":5,"ExtraInputs":0,"RealInputs":19,"x":475,"y":245,"z":"Main","bgColor":"#E6E0F8","wires":[["Main_i2s1:0"],["Main_i2s1:1"]]},{"id":"20220302T170700_396Z_4a3b","type":"AudioMixerStereo","isClass":false,"name":"mixerUSB","comment":"","inputs":2,"ExtraInputs":0,"RealInputs":10,"x":470,"y":315,"z":"Main","bgColor":"#E6E0F8","wires":[["Main_usb1:0"],["Main_usb1:1"]]},{"id":"Main_i2s1","type":"AudioOutputI2S","isClass":false,"name":"i2s","comment":"","x":690,"y":225,"z":"Main","bgColor":"#E6E0F8","wires":[]},{"id":"Main_usb1","type":"AudioOutputUSB","isClass":false,"name":"usb","comment":"","x":695,"y":275,"z":"Main","bgColor":"#E6E0F8","wires":[]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":true,"generateCppDestructor":true,"extraClassDeclarations":"","settings":{}},{"type":"tab","id":"a47e9f.1128a16","label":"SampleAndHold","nodes":[{"id":"SampleAndHold_Out1","type":"TabOutput","isClass":false,"name":"Out","comment":"","x":865,"y":195,"z":"a47e9f.1128a16","bgColor":"#cce6ff","wires":[]},{"id":"Sheet_1_waveform1","type":"AudioSynthWaveform","isClass":false,"name":"waveform1","x":195,"y":180,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["20220227T151017_148Z_7e09:0"]]},{"id":"Sheet_1_noise1","type":"AudioSynthNoiseWhite","isClass":false,"name":"noise1","x":155,"y":315,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["Sheet_1_bitcrusher1:0"]]},{"id":"20220227T151017_148Z_7e09","type":"AudioEffectEnvelope","isClass":false,"name":"envelope","comment":"","x":345,"y":180,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["Sheet_1_filter1:0"]]},{"id":"Sheet_1_bitcrusher1","type":"AudioEffectBitcrusher","isClass":false,"name":"bitcrusher1","x":319,"y":314,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["Sheet_1_filter1:1"]]},{"id":"Sheet_1_filter1","type":"AudioFilterStateVariable","isClass":false,"name":"filter1","x":485,"y":190,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["20220227T172000_754Z_e580:0"],["20220227T172000_754Z_e580:1"],["20220227T172000_754Z_e580:2"]]},{"id":"20220227T172000_754Z_e580","type":"AudioMixer","isClass":false,"name":"mixer","comment":"","inputs":3,"ExtraInputs":0,"RealInputs":3,"x":690,"y":190,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["SampleAndHold_Out1:0"]]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":false,"generateCppDestructor":true,"extraClassDeclarations":"","settings":{}},{"type":"tab","id":"6eff372d.78f988","label":"MinHammond","nodes":[{"id":"MinHammond_Out1","type":"TabOutput","isClass":false,"name":"Out1","comment":"","x":827,"y":300,"z":"6eff372d.78f988","bgColor":"#cce6ff","wires":[]},{"id":"MinHammond_waveform7","type":"AudioSynthWaveform","isClass":false,"name":"waveform1","x":263,"y":99,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_1:0"]]},{"id":"MinHammond_waveform8","type":"AudioSynthWaveform","isClass":false,"name":"waveform2","x":264,"y":144,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_1:1"]]},{"id":"MinHammond_waveform9","type":"AudioSynthWaveform","isClass":false,"name":"waveform3","x":265,"y":185,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_1:2"]]},{"id":"MinHammond_waveform5","type":"AudioSynthWaveform","isClass":false,"name":"waveform4","x":257,"y":226,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_1:3"]]},{"id":"MinHammond_waveform6","type":"AudioSynthWaveform","isClass":false,"name":"waveform5","x":257,"y":266,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_2:0"]]},{"id":"MinHammond_waveform4","type":"AudioSynthWaveform","isClass":false,"name":"waveform6","x":256,"y":310,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_2:1"]]},{"id":"MinHammond_waveform1","type":"AudioSynthWaveform","isClass":false,"name":"waveform7","x":255,"y":350,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_2:2"]]},{"id":"MinHammond_waveform2","type":"AudioSynthWaveform","isClass":false,"name":"waveform8","x":255,"y":392,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_2:3"]]},{"id":"MinHammond_waveform3","type":"AudioSynthWaveform","isClass":false,"name":"waveform9","x":255,"y":446,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_3:2"]]},{"id":"MinHammond_noise1","type":"AudioSynthNoiseWhite","isClass":false,"name":"noise1","x":258,"y":535,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_envelope1:0"]]},{"id":"MinHammond_mixer4_1","type":"AudioMixer4","isClass":false,"name":"mixer1","x":477,"y":163,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_3:0"]]},{"id":"MinHammond_mixer4_2","type":"AudioMixer4","isClass":false,"name":"mixer2","x":482,"y":290,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_3:1"]]},{"id":"MinHammond_envelope1","type":"AudioEffectEnvelope","isClass":false,"name":"envelope1","x":493,"y":523,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_3:3"]]},{"id":"MinHammond_mixer4_3","type":"AudioMixer4","isClass":false,"name":"mixer3","x":656,"y":297,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_Out1:0"]]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":false,"generateCppDestructor":false,"extraClassDeclarations":"","settings":{}},{"type":"tab","id":"ea305170.fb2f9","label":"WaveFormVoice","nodes":[{"id":"20220302T113212_611Z_f670","type":"TabInput","isClass":false,"name":"freqMod","comment":"","outputs":1,"x":110,"y":283,"z":"ea305170.fb2f9","bgColor":"#cce6ff","wires":[["20220302T113145_789Z_77c0:0"]]},{"id":"WaveFormVoice_Out1","type":"TabOutput","isClass":false,"name":"Out2","comment":"","x":570,"y":320,"z":"ea305170.fb2f9","bgColor":"#cce6ff","wires":[]},{"id":"20220302T113218_689Z_d58b","type":"TabInput","isClass":false,"name":"shapeMod","comment":"","outputs":1,"x":106,"y":336,"z":"ea305170.fb2f9","bgColor":"#cce6ff","wires":[["20220302T113145_789Z_77c0:1"]]},{"id":"WaveFormVoice_vars1","type":"Variables","isClass":false,"name":"vars","comment":"// Start of variables\nprivate:\n    bool isNew;\n    static short wave_type[4];\npublic:\n// End of variables\n","x":290,"y":105,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"WaveFormVoice_constructor code1","type":"ConstructorCode","isClass":false,"name":"constructor code","comment":"      env.attack(129.2);\r\n      env.hold(2.1);\r\n      env.decay(181.4);\r\n      env.sustain(0.3);\r\n      env.release(284.5);\r\n      amp.gain(0.5);","x":283,"y":151,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"WaveFormVoice_code1","type":"Function","isClass":false,"name":"WaveFormVoice_code","comment":"// Start of voice class manually-entered code\r\n    void noteOn(int MIDInote, int MIDIvel, int chan=-1){};\r\n    void noteOn(float freq, float vel, int chan=-1)\r\n    {\r\n      if (isNew)\r\n        wave.begin(vel,freq,(chan<0)?WAVEFORM_SINE:(wave_type[chan&3]));\r\n      else\r\n      {\r\n        wave.amplitude(vel);\r\n        wave.frequency(freq);\r\n      }\r\n      env.noteOn();\r\n      isNew = false;\r\n    }\r\n\r\n    void noteOff(void){env.noteOff();};\r\n    bool isPlaying(void) {return env.isActive();};\r\n}; // terminates class\r\n\r\nstatic short WaveformVoice::wave_type[] = {\r\n    WAVEFORM_SINE,\r\n    WAVEFORM_SQUARE,\r\n    WAVEFORM_SAWTOOTH,\r\n    WAVEFORM_TRIANGLE // no closing brace, GUI tool puts it in\r\n// End of voice class manually-entered code\r\n","x":290,"y":220,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"20220302T113145_789Z_77c0","type":"AudioSynthWaveformModulated","isClass":false,"name":"waveformMod","comment":"","x":285,"y":320,"z":"ea305170.fb2f9","bgColor":"#E6E0F8","wires":[["WaveFormVoice_amp1:0"]]},{"id":"WaveFormVoice_destructor code1","type":"DestructorCode","isClass":false,"name":"destructor code","comment":"// extra code for destructor","x":480,"y":148,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"WaveFormVoice_amp1","type":"AudioAmplifier","isClass":false,"name":"amp","comment":"","x":460,"y":320,"z":"ea305170.fb2f9","bgColor":"#E6E0F8","wires":[["WaveFormVoice_Out1:0"]]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":false,"generateCppDestructor":false,"extraClassDeclarations":"","settings":{}}],"nodeAddons":{}
    }
    Won't be able to do much for the next couple of days - Real Work and other stuff getting in the way...

  14. #289
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    FYI

    You don't need the
    }; // terminates class
    workaround anymore

    there is the 'eof code' object that can be used instead
    where you can put the whole of
    static short WaveformVoice::wave_type[] = {
    //...
    };

  15. #290
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    337
    Breaking News ... the latest push to my development branch (same as above) compiles and doesn't crash the Teensy, though I've not tested generating audio or destroying the classes as yet. I've only tested the class export via copy and paste. The exported classes are rather inelegant because of the need to generate unique patchcord names: I've not thought through whether that's just a consequence of a poor design structure. If not, I suspect it may be useful to add an "auto name connection" capability to the OSCAudio library.

    The test design is now:
    Code:
    {"version":1,"settings":{"main":{},"OSC":{},"arduino":{"ExportForOSC":true,"useExportDialog":true,"ProjectName":"MultiTimbralLinEnv","StandardIncludeHeader":"#include <Arduino.h>\n#include <Audio.h>\n#include <Wire.h>\n#include <SPI.h>\n#include <SD.h>\n#include <SerialFlash.h>\n\n#include <OSCAudioBase.h>\n","Board":{"Platform":"","Board":"teensy41","Options":""}},"BiDirDataWebSocketBridge":{},"workspaces":{},"sidebar":{},"palette":{},"editor":{},"devTest":{},"IndexedDBfiles":{"testFileNames":"testFile.txt"},"NodeDefGenerator":{},"NodeDefManager":{},"NodeHelpManager":{}},"workspaces":[{"type":"tab","id":"Main","label":"Main","nodes":[{"id":"20220302T113319_446Z_335b","type":"AudioSynthWaveform","isClass":false,"name":"LFO","comment":"","x":90,"y":260,"z":"Main","bgColor":"#E6E0F8","wires":[["20220302T113305_119Z_4b2:0","20220302T113305_119Z_4b2:1","20220227T150834_852Z_11ed:3"]]},{"id":"20220228T194812_823Z_dfa8","type":"SampleAndHold","isClass":true,"name":"SnH[6]","x":265,"y":170,"z":"Main","bgColor":"#CCFFCC","wires":[["20220227T150834_852Z_11ed:0"]]},{"id":"Main_MinHammond1","type":"MinHammond","isClass":true,"name":"HammondVoice[8]","x":235,"y":230,"z":"Main","bgColor":"#CCFFCC","wires":[["20220227T150834_852Z_11ed:2","20220302T170700_396Z_4a3b:0"]]},{"id":"20220302T113305_119Z_4b2","type":"WaveFormVoice","isClass":true,"name":"modWaves[2]","x":250,"y":290,"z":"Main","bgColor":"#CCFFCC","wires":[["20220227T150834_852Z_11ed:4","20220302T170700_396Z_4a3b:2"],["20220302T170700_396Z_4a3b:1","20220227T150834_852Z_11ed:1"]]},{"id":"20220227T150834_852Z_11ed","type":"AudioMixerStereo","isClass":false,"name":"mixerI2S","comment":"","inputs":5,"ExtraInputs":0,"RealInputs":19,"x":475,"y":245,"z":"Main","bgColor":"#E6E0F8","wires":[["Main_i2s1:0"],["Main_i2s1:1"]]},{"id":"20220302T170700_396Z_4a3b","type":"AudioMixerStereo","isClass":false,"name":"mixerUSB","comment":"","inputs":3,"ExtraInputs":0,"RealInputs":12,"x":470,"y":315,"z":"Main","bgColor":"#E6E0F8","wires":[["Main_usb1:0"],["Main_usb1:1"]]},{"id":"Main_i2s1","type":"AudioOutputI2S","isClass":false,"name":"i2s","comment":"","x":600,"y":245,"z":"Main","bgColor":"#E6E0F8","wires":[]},{"id":"Main_usb1","type":"AudioOutputUSB","isClass":false,"name":"usb","comment":"","x":605,"y":315,"z":"Main","bgColor":"#E6E0F8","wires":[]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":true,"generateCppDestructor":true,"extraClassDeclarations":"","settings":{}},{"type":"tab","id":"a47e9f.1128a16","label":"SampleAndHold","nodes":[{"id":"SampleAndHold_Out1","type":"TabOutput","isClass":false,"name":"Out","comment":"","x":865,"y":195,"z":"a47e9f.1128a16","bgColor":"#cce6ff","wires":[]},{"id":"Sheet_1_waveform1","type":"AudioSynthWaveform","isClass":false,"name":"waveform1","x":195,"y":180,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["20220227T151017_148Z_7e09:0"]]},{"id":"Sheet_1_noise1","type":"AudioSynthNoiseWhite","isClass":false,"name":"noise1","x":155,"y":315,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["Sheet_1_bitcrusher1:0"]]},{"id":"20220227T151017_148Z_7e09","type":"AudioEffectEnvelope","isClass":false,"name":"envelope","comment":"","x":345,"y":180,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["Sheet_1_filter1:0"]]},{"id":"Sheet_1_bitcrusher1","type":"AudioEffectBitcrusher","isClass":false,"name":"bitcrusher1","x":319,"y":314,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["Sheet_1_filter1:1"]]},{"id":"Sheet_1_filter1","type":"AudioFilterStateVariable","isClass":false,"name":"filter1","x":485,"y":190,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["20220227T172000_754Z_e580:0"],["20220227T172000_754Z_e580:1"],["20220227T172000_754Z_e580:2"]]},{"id":"20220227T172000_754Z_e580","type":"AudioMixer","isClass":false,"name":"mixer","comment":"","inputs":3,"ExtraInputs":0,"RealInputs":3,"x":690,"y":190,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["SampleAndHold_Out1:0"]]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":false,"generateCppDestructor":true,"extraClassDeclarations":"","settings":{}},{"type":"tab","id":"6eff372d.78f988","label":"MinHammond","nodes":[{"id":"MinHammond_Out1","type":"TabOutput","isClass":false,"name":"Out1","comment":"","x":827,"y":300,"z":"6eff372d.78f988","bgColor":"#cce6ff","wires":[]},{"id":"MinHammond_waveform7","type":"AudioSynthWaveform","isClass":false,"name":"waveform1","x":263,"y":99,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_1:0"]]},{"id":"MinHammond_waveform8","type":"AudioSynthWaveform","isClass":false,"name":"waveform2","x":264,"y":144,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_1:1"]]},{"id":"MinHammond_waveform9","type":"AudioSynthWaveform","isClass":false,"name":"waveform3","x":265,"y":185,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_1:2"]]},{"id":"MinHammond_waveform5","type":"AudioSynthWaveform","isClass":false,"name":"waveform4","x":257,"y":226,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_1:3"]]},{"id":"MinHammond_waveform6","type":"AudioSynthWaveform","isClass":false,"name":"waveform5","x":257,"y":266,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_2:0"]]},{"id":"MinHammond_waveform4","type":"AudioSynthWaveform","isClass":false,"name":"waveform6","x":256,"y":310,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_2:1"]]},{"id":"MinHammond_waveform1","type":"AudioSynthWaveform","isClass":false,"name":"waveform7","x":255,"y":350,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_2:2"]]},{"id":"MinHammond_waveform2","type":"AudioSynthWaveform","isClass":false,"name":"waveform8","x":255,"y":392,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_2:3"]]},{"id":"MinHammond_waveform3","type":"AudioSynthWaveform","isClass":false,"name":"waveform9","x":255,"y":446,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_3:2"]]},{"id":"MinHammond_noise1","type":"AudioSynthNoiseWhite","isClass":false,"name":"noise1","x":258,"y":535,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_envelope1:0"]]},{"id":"MinHammond_mixer4_1","type":"AudioMixer4","isClass":false,"name":"mixer1","x":477,"y":163,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_3:0"]]},{"id":"MinHammond_mixer4_2","type":"AudioMixer4","isClass":false,"name":"mixer2","x":482,"y":290,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_3:1"]]},{"id":"MinHammond_envelope1","type":"AudioEffectEnvelope","isClass":false,"name":"envelope1","x":493,"y":523,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_mixer4_3:3"]]},{"id":"MinHammond_mixer4_3","type":"AudioMixer4","isClass":false,"name":"mixer3","x":656,"y":297,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_Out1:0"]]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":false,"generateCppDestructor":false,"extraClassDeclarations":"","settings":{}},{"type":"tab","id":"ea305170.fb2f9","label":"WaveFormVoice","nodes":[{"id":"20220302T113212_611Z_f670","type":"TabInput","isClass":false,"name":"freqMod","comment":"","outputs":1,"x":110,"y":283,"z":"ea305170.fb2f9","bgColor":"#cce6ff","wires":[["20220302T113145_789Z_77c0:0"]]},{"id":"WaveFormVoice_Out1","type":"TabOutput","isClass":false,"name":"OutA","comment":"","inputs":"","x":570,"y":320,"z":"ea305170.fb2f9","bgColor":"#cce6ff","wires":[]},{"id":"20220302T113218_689Z_d58b","type":"TabInput","isClass":false,"name":"shapeMod","comment":"","outputs":1,"x":106,"y":336,"z":"ea305170.fb2f9","bgColor":"#cce6ff","wires":[["20220302T113145_789Z_77c0:1"]]},{"id":"20220304T200046_288Z_b82f","type":"TabOutput","isClass":false,"name":"OutW","comment":"","inputs":1,"x":570,"y":380,"z":"ea305170.fb2f9","bgColor":"#cce6ff","wires":[]},{"id":"WaveFormVoice_vars1","type":"Variables","isClass":false,"name":"vars","comment":"// Start of variables\nprivate:\n    bool isNew;\n    static short wave_type[4];\npublic:\n// End of variables\n","x":290,"y":105,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"WaveFormVoice_constructor code1","type":"ConstructorCode","isClass":false,"name":"constructor code","comment":"      env.attack(129.2);\r\n      env.hold(2.1);\r\n      env.decay(181.4);\r\n      env.sustain(0.3);\r\n      env.release(284.5);\r\n      amp.gain(0.5);","x":283,"y":151,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"WaveFormVoice_code1","type":"Function","isClass":false,"name":"WaveFormVoice_code","comment":"// Start of voice class manually-entered code\r\n    void noteOn(int MIDInote, int MIDIvel, int chan=-1){};\r\n    void noteOn(float freq, float vel, int chan=-1)\r\n    {\r\n      if (isNew)\r\n        wave.begin(vel,freq,(chan<0)?WAVEFORM_SINE:(wave_type[chan&3]));\r\n      else\r\n      {\r\n        wave.amplitude(vel);\r\n        wave.frequency(freq);\r\n      }\r\n      env.noteOn();\r\n      isNew = false;\r\n    }\r\n\r\n    void noteOff(void){env.noteOff();};\r\n    bool isPlaying(void) {return env.isActive();};\r\n}; // terminates class\r\n\r\n/* static */ short WaveFormVoice::wave_type[] = {\r\n    WAVEFORM_SINE,\r\n    WAVEFORM_SQUARE,\r\n    WAVEFORM_SAWTOOTH,\r\n    WAVEFORM_TRIANGLE // no closing brace, GUI tool puts it in\r\n// End of voice class manually-entered code\r\n","x":290,"y":220,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"20220302T113145_789Z_77c0","type":"AudioSynthWaveformModulated","isClass":false,"name":"wave","comment":"","x":285,"y":320,"z":"ea305170.fb2f9","bgColor":"#E6E0F8","wires":[["WaveFormVoice_amp1:0","20220306T195324_547Z_8d5d:0"]]},{"id":"WaveFormVoice_destructor code1","type":"DestructorCode","isClass":false,"name":"destructor code","comment":"// extra code for destructor","x":480,"y":148,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"WaveFormVoice_amp1","type":"AudioAmplifier","isClass":false,"name":"amp","comment":"","x":460,"y":320,"z":"ea305170.fb2f9","bgColor":"#E6E0F8","wires":[["WaveFormVoice_Out1:0"]]},{"id":"20220306T195324_547Z_8d5d","type":"AudioEffectEnvelope","isClass":false,"name":"env","comment":"","x":460,"y":380,"z":"ea305170.fb2f9","bgColor":"#E6E0F8","wires":[["20220304T200046_288Z_b82f:0"]]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":false,"generateCppDestructor":false,"extraClassDeclarations":"","settings":{}}],"nodeAddons":{}
    }
    (Your FYI comment noted, just haven't got round to fixing it yet).

  16. #291
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    337
    In actual OSCAudio news, I've just pushed an update (a1bff434) which implements the USB I/O objects with OSCAudio capability. Missed them because they're defined in cores, not the Audio library. Not fully tested, and clearly no-one missed them!

  17. #292
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    [QUOTE=h4yn0nnym0u5e;301929]The exported classes are rather inelegant because of the need to generate unique patchcord names: I've not thought through whether that's just a consequence of a poor design structure. If not, I suspect it may be useful to add an "auto name connection" capability to the OSCAudio library.

    The best way would to have no connections at all and each audio object had it own connect function
    could surely be implemented into the AudioStream base class

    and the connections could just be stored into a 'linked list'(to make expansion retraction easier)

    Code:
    struct {
       //sourcePort;
       //*destination; // just a pointer to the address so it can be looked up at disconnect
       //destinationPort;
       //acNext;
    } AudioConnection;
    so that you could just do (just a example using ordinary objects)

    Code:
    AudioSynthWaveform wav1;
    AudioSynthWaveform wav2;
    AudioEffectEnvelope   env1;
    AudioEffectEnvelope   env2;
    AudioMixer4              mix;
    
    // connect all
    void connectEverything() {
        wav1.connect(0,mix,0);
        wav2.connect(0,mix,1);
        wav1.connect(0,env1,0);
        wav2.connect(0,env2,0);
        env1.connect(0,mix,2);
        env2.connect(0,mix,3);
    }
    
    // disconnect all
    void disconnectEverything() {
        wav1.disconnect(0,mix,0);
        wav2.disconnect(0,mix,1);
        wav1.disconnect(0,env1,0);
        wav2.disconnect(0,env2,0);
        env1.disconnect(0,mix,2);
        env2.disconnect(0,mix,3);
    
       // or more elegant
    
       // these can be used when for example the mix object gets destroyed
       // could also be a automatic thing as a input can only have one source
       // and therefore can easily send a message to 
       // that source to disconnect that specific connection 
       wav1.disconnectAll(mix);
       wav2.disconnectAll(mix);
       env1.disconnectAll(mix);
       env2.disconnectAll(mix);
    
       // providing no parameters mean disconnect all connections going out
       wav1.disconnectAll();
       wav2.disconnectAll();
       env1.disconnectAll();
       env2.disconnectAll();
       
    }
    but maybe that structure should just be implemented into the OSC lib for now

    it would make the AudioConnection:s obsolete

  18. #293
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    I have also made classes for the following objects

    REDWorkspace
    REDNode
    REDLink
    REDLinkSvgPaths
    REDLinkInfo

    so that JSDoc info can be utilized much easier
    thus making understanding of the different structures much easier

    it's mostly used in nodes.js but view.js also needs some JSDoc specially where REDNode is used

    plan to implement a class for 'JSONNode' as well but that is not that important
    as it's only used for the exported JSON structure which only is used as the save/load functionality.

    Currently working on a new more structured arduino-export.js (should maybe just be called cpp-export.js)
    could also be called teensy-export.js but as it can export 'ordinary' c++ code as well maybe cpp-export is a better name?

  19. #294
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    337
    I've tried to add OSC extensions in a manner as similar as possible to the existing Audio library, and make essentially no changes to the Audio library API itself (apart from the ability to destroy objects and disconnect connections...). The original Audio library already did connections as a linked list to allow multiple destinations from a given source.

    When I was working through the existing export_classBased() I found it very difficult to follow what data structures were used and what the code was doing - the function is nearly 600 lines long! Assuming it's not too late, I'd encourage you to split it into calls to separate functions (that maybe you can partially re-use for non-class and OSC export?), and in particular not interleave generating intermediate data structures and generating output.

  20. #295
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    I have now 'kind of' 'merged' your osc export code into my 'master' branch
    but instead used a different 'namespace' RED.arduino.export.osc @ arduino-export-osc.js

    The settings that you use are at the original place @ arduino.js
    And the export buttons are at the OSC menu

    So now you could make a fork of that,

    This would make it easier to have 'both' variants available simultaneous,
    and would also make it much easier to merge your changes into my branch,
    as I would not make any changes to arduino-export-osc.js



    I did spot one thing, while exporting as class I get a undefined error:
    Uncaught TypeError: Cannot read properties of undefined (reading 'outputs')
    at getMaxConnName (arduino-export-osc.js:1311:19)
    at export_classBased (arduino-export-osc.js:841:25)

    This is because the bus var in getMaxConnName is undefined

    don't know where the origin of this problem occur as I have not
    had the time to understand your code yet.


    Have not yet completed the new arduino-export2.js as I have been busy with other things.

  21. #296
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    337
    Apologies, been offline with other stuff for a while - it's not that I've lost interest, far from it!

    I've been having a few issues with the editor, too. Here's the design:
    Code:
    {"version":1,"settings":{"main":{},"OSC":{},"arduino":{"ExportForOSC":true,"useExportDialog":true,"ProjectName":"MultiTimbralLinEnv2","StandardIncludeHeader":"#include <Arduino.h>\n#include <Audio.h>\n#include <Wire.h>\n#include <SPI.h>\n#include <SD.h>\n#include <SerialFlash.h>\n\n#include <OSCAudioBase.h>\n","Board":{"Platform":"","Board":"teensy41","Options":""}},"BiDirDataWebSocketBridge":{},"workspaces":{},"sidebar":{},"palette":{},"editor":{},"devTest":{},"IndexedDBfiles":{"testFileNames":"testFile.txt"},"NodeDefGenerator":{},"NodeDefManager":{},"NodeHelpManager":{}},"workspaces":[{"type":"tab","id":"Main","label":"Main","nodes":[{"id":"20220302T113319_446Z_335b","type":"AudioSynthWaveform","name":"LFO","comment":"","x":80,"y":195,"z":"Main","bgColor":"#E6E0F8","wires":[["20220302T113305_119Z_4b2:0","20220302T113305_119Z_4b2:1","20220227T150834_852Z_11ed:4"]]},{"id":"20220228T194812_823Z_dfa8","type":"SampleAndHold","name":"SnH[6]","arraySize":6,"x":265,"y":170,"z":"Main","bgColor":"#CCFFCC","wires":[["20220227T150834_852Z_11ed:0"]]},{"id":"20220316T174639_836Z_37cc","type":"MinHammond1","name":"Hammond[8]","comment":"","arraySize":8,"x":240,"y":240,"z":"Main","bgColor":"#CCFFCC","wires":[["20220227T150834_852Z_11ed:2","20220302T170700_396Z_4a3b:1","20220302T170700_396Z_4a3b:0","20220227T150834_852Z_11ed:3"]]},{"id":"20220302T113305_119Z_4b2","type":"WaveFormVoice","name":"modWaves[2]","arraySize":2,"x":245,"y":295,"z":"Main","bgColor":"#CCFFCC","wires":[["20220227T150834_852Z_11ed:5","20220302T170700_396Z_4a3b:3"],["20220227T150834_852Z_11ed:1","20220302T170700_396Z_4a3b:2"]]},{"id":"20220227T150834_852Z_11ed","type":"AudioMixerStereo","name":"mixerI2S","comment":"","inputs":6,"ExtraInputs":0,"RealInputs":27,"x":475,"y":200,"z":"Main","bgColor":"#E6E0F8","wires":[[],["Main_i2s1:1"]]},{"id":"20220302T170700_396Z_4a3b","type":"AudioMixerStereo","name":"mixerUSB","comment":"","inputs":4,"ExtraInputs":0,"RealInputs":20,"x":475,"y":330,"z":"Main","bgColor":"#E6E0F8","wires":[["Main_usb1:0"],["Main_usb1:1"]]},{"id":"Main_i2s1","type":"AudioOutputI2S","name":"i2s","comment":"","x":610,"y":200,"z":"Main","bgColor":"#E6E0F8","wires":[]},{"id":"Main_usb1","type":"AudioOutputUSB","name":"usb","comment":"","x":605,"y":330,"z":"Main","bgColor":"#E6E0F8","wires":[]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":true,"generateCppDestructor":true,"extraClassDeclarations":"","settings":{}},{"type":"tab","id":"a47e9f.1128a16","label":"SampleAndHold","nodes":[{"id":"SampleAndHold_Out1","type":"TabOutput","name":"Out","comment":"","x":865,"y":195,"z":"a47e9f.1128a16","bgColor":"#cce6ff","wires":[]},{"id":"Sheet_1_waveform1","type":"AudioSynthWaveform","name":"waveform1","x":195,"y":180,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["20220227T151017_148Z_7e09:0"]]},{"id":"Sheet_1_noise1","type":"AudioSynthNoiseWhite","name":"noise1","x":155,"y":315,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["Sheet_1_bitcrusher1:0"]]},{"id":"20220227T151017_148Z_7e09","type":"AudioEffectEnvelope","name":"envelope","comment":"","x":345,"y":180,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["Sheet_1_filter1:0"]]},{"id":"Sheet_1_bitcrusher1","type":"AudioEffectBitcrusher","name":"bitcrusher1","x":319,"y":314,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["Sheet_1_filter1:1"]]},{"id":"Sheet_1_filter1","type":"AudioFilterStateVariable","name":"filter1","x":485,"y":190,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["20220227T172000_754Z_e580:0"],["20220227T172000_754Z_e580:1"],["20220227T172000_754Z_e580:2"]]},{"id":"20220227T172000_754Z_e580","type":"AudioMixer","name":"mixer","comment":"","inputs":3,"ExtraInputs":0,"RealInputs":3,"x":690,"y":190,"z":"a47e9f.1128a16","bgColor":"#E6E0F8","wires":[["SampleAndHold_Out1:0"]]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":false,"generateCppDestructor":true,"extraClassDeclarations":"","settings":{}},{"type":"tab","id":"6eff372d.78f988","label":"MinHammond1","nodes":[{"id":"MinHammond_Out1","type":"TabOutput","name":"Out","comment":"","inputs":"","x":680,"y":280,"z":"6eff372d.78f988","bgColor":"#cce6ff","wires":[]},{"id":"MinHammond_waveform7","type":"AudioSynthWaveform","name":"waveform1","x":263,"y":99,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["20220307T120440_881Z_fb5:0"]]},{"id":"MinHammond_waveform8","type":"AudioSynthWaveform","name":"waveform2","x":264,"y":144,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["20220307T120440_881Z_fb5:1"]]},{"id":"MinHammond_waveform9","type":"AudioSynthWaveform","name":"waveform3","x":265,"y":185,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["20220307T120440_881Z_fb5:2"]]},{"id":"MinHammond_waveform5","type":"AudioSynthWaveform","name":"waveform4","x":257,"y":226,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["20220307T120440_881Z_fb5:3"]]},{"id":"MinHammond_waveform6","type":"AudioSynthWaveform","name":"waveform5","x":257,"y":266,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["20220307T120440_881Z_fb5:4"]]},{"id":"MinHammond_waveform4","type":"AudioSynthWaveform","name":"waveform6","x":256,"y":310,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["20220307T120440_881Z_fb5:5"]]},{"id":"MinHammond_waveform1","type":"AudioSynthWaveform","name":"waveform7","x":255,"y":350,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["20220307T120440_881Z_fb5:6"]]},{"id":"MinHammond_waveform2","type":"AudioSynthWaveform","name":"waveform8","x":255,"y":392,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["20220307T120440_881Z_fb5:7"]]},{"id":"MinHammond_waveform3","type":"AudioSynthWaveform","name":"waveform9","x":255,"y":446,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["20220307T120440_881Z_fb5:8"]]},{"id":"MinHammond_noise1","type":"AudioSynthNoiseWhite","name":"noise1","x":258,"y":535,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_envelope1:0"]]},{"id":"MinHammond_envelope1","type":"AudioEffectEnvelope","name":"env","comment":"","x":410,"y":535,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["20220307T120440_881Z_fb5:9"]]},{"id":"20220307T120440_881Z_fb5","type":"AudioMixer","name":"mixer","comment":"","inputs":10,"ExtraInputs":0,"RealInputs":10,"x":540,"y":280,"z":"6eff372d.78f988","bgColor":"#E6E0F8","wires":[["MinHammond_Out1:0"]]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":false,"generateCppDestructor":false,"extraClassDeclarations":"","settings":{}},{"type":"tab","id":"20220307T120627_405Z_a479","label":"Hammond_x8","nodes":[{"id":"20220307T120721_021Z_29bd","type":"TabOutput","name":"OutL","comment":"","inputs":1,"x":510,"y":195,"z":"20220307T120627_405Z_a479","bgColor":"#cce6ff","wires":[]},{"id":"20220307T122215_132Z_aca6","type":"TabOutput","name":"OutR","comment":"","inputs":1,"x":510,"y":240,"z":"20220307T120627_405Z_a479","bgColor":"#cce6ff","wires":[]},{"id":"20220307T120648_593Z_b118","type":"MinHammond1","name":"minhammond1[8]","arraySize":8,"x":165,"y":215,"z":"20220307T120627_405Z_a479","bgColor":"#CCFFCC","wires":[["20220307T122152_547Z_8a89:0"]]},{"id":"20220307T122152_547Z_8a89","type":"AudioMixerStereo","name":"mixS","comment":"","inputs":1,"ExtraInputs":0,"RealInputs":8,"x":335,"y":215,"z":"20220307T120627_405Z_a479","bgColor":"#E6E0F8","wires":[["20220307T120721_021Z_29bd:0"],["20220307T122215_132Z_aca6:0"]]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":false,"generateCppDestructor":false,"extraClassDeclarations":"","settings":{}},{"type":"tab","id":"ea305170.fb2f9","label":"WaveFormVoice","nodes":[{"id":"20220302T113212_611Z_f670","type":"TabInput","name":"freqMod","comment":"","outputs":1,"x":110,"y":283,"z":"ea305170.fb2f9","bgColor":"#cce6ff","wires":[["20220302T113145_789Z_77c0:0"]]},{"id":"WaveFormVoice_Out1","type":"TabOutput","name":"OutA","comment":"","inputs":"","x":570,"y":320,"z":"ea305170.fb2f9","bgColor":"#cce6ff","wires":[]},{"id":"20220302T113218_689Z_d58b","type":"TabInput","name":"shapeMod","comment":"","outputs":1,"x":106,"y":336,"z":"ea305170.fb2f9","bgColor":"#cce6ff","wires":[["20220302T113145_789Z_77c0:1"]]},{"id":"20220304T200046_288Z_b82f","type":"TabOutput","name":"OutW","comment":"","inputs":1,"x":570,"y":380,"z":"ea305170.fb2f9","bgColor":"#cce6ff","wires":[]},{"id":"WaveFormVoice_vars1","type":"Variables","name":"vars","comment":"// Start of variables\nprivate:\n    bool isNew;\n    static short wave_type[4];\npublic:\n// End of variables\n","x":290,"y":105,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"WaveFormVoice_constructor code1","type":"ConstructorCode","name":"constructor code","comment":"      env.attack(129.2);\r\n      env.hold(2.1);\r\n      env.decay(181.4);\r\n      env.sustain(0.3);\r\n      env.release(284.5);\r\n      amp.gain(0.5);","x":283,"y":151,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"WaveFormVoice_code1","type":"Function","name":"WaveFormVoice_code","comment":"// Start of voice class manually-entered code\r\n    void noteOn(int MIDInote, int MIDIvel, int chan=-1){};\r\n    void noteOn(float freq, float vel, int chan=-1)\r\n    {\r\n      if (isNew)\r\n        wave.begin(vel,freq,(chan<0)?WAVEFORM_SINE:(wave_type[chan&3]));\r\n      else\r\n      {\r\n        wave.amplitude(vel);\r\n        wave.frequency(freq);\r\n      }\r\n      env.noteOn();\r\n      isNew = false;\r\n    }\r\n\r\n    void noteOff(void){env.noteOff();};\r\n    bool isPlaying(void) {return env.isActive();};","x":290,"y":220,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"20220302T113145_789Z_77c0","type":"AudioSynthWaveformModulated","name":"wave","comment":"","x":285,"y":320,"z":"ea305170.fb2f9","bgColor":"#E6E0F8","wires":[["WaveFormVoice_amp1:0","20220306T195324_547Z_8d5d:0"]]},{"id":"20220307T115003_039Z_3cc2","type":"EndOfFileCode","name":"eof code","comment":"/* static */ short WaveFormVoice::wave_type[] = \r\n{\r\n    WAVEFORM_SINE,\r\n    WAVEFORM_SQUARE,\r\n    WAVEFORM_SAWTOOTH,\r\n    WAVEFORM_TRIANGLE \r\n};","x":289,"y":435,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"WaveFormVoice_destructor code1","type":"DestructorCode","name":"destructor code","comment":"// extra code for destructor","x":480,"y":148,"z":"ea305170.fb2f9","bgColor":"#DDFFBB","wires":[]},{"id":"WaveFormVoice_amp1","type":"AudioAmplifier","name":"amp","comment":"","x":460,"y":320,"z":"ea305170.fb2f9","bgColor":"#E6E0F8","wires":[["WaveFormVoice_Out1:0"]]},{"id":"20220306T195324_547Z_8d5d","type":"AudioEffectEnvelope","name":"env","comment":"","x":460,"y":380,"z":"ea305170.fb2f9","bgColor":"#E6E0F8","wires":[["20220304T200046_288Z_b82f:0"]]}],"links":[],"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isAudioMain":false,"generateCppDestructor":false,"extraClassDeclarations":"","settings":{}}],"nodeAddons":{}
    }
    And here are two different errors that show in the console when I try to connect mixerI2S to i2s - one occurs when I try to make the connection immediately after an F5 refresh, the second when I try again.
    Code:
    Uncaught TypeError: Cannot set properties of undefined (setting 'tabOut')
        at setLinkInfo (nodes.js:2602:23)
        at Object.addLink (nodes.js:565:13)
        at portMouseUp (view.js:2131:23)
        at SVGRectElement.<anonymous> (view.js:2818:58)
        at SVGRectElement.i [as __onmouseup] (d3.v3.min.js:515:23)
    setLinkInfo @ nodes.js:2602
    addLink @ nodes.js:565
    portMouseUp @ view.js:2131
    (anonymous) @ view.js:2818
    i @ d3.v3.min.js:515
    
    
    
    view.js:3042 Uncaught TypeError: Cannot read properties of undefined (reading 'isBus')
        at redraw_link_notation (view.js:3042:20)
        at SVGPathElement.redraw_link (view.js:3057:9)
        at SVGPathElement.a (d3.v3.min.js:413:23)
        at d3.v3.min.js:3959:15
        at Tn (d3.v3.min.js:531:81)
        at Array.Ma.each (d3.v3.min.js:3958:16)
        at Array.Ma.attr (d3.v3.min.js:3808:21)
        at redraw_links (view.js:3037:9)
        at Object.setSelectedWorkspace [as onchange] (view.js:725:9)
        at activateTab (tabs.js:357:29)
    redraw_link_notation @ view.js:3042
    redraw_link @ view.js:3057
    a @ d3.v3.min.js:413
    (anonymous) @ d3.v3.min.js:3959
    Tn @ d3.v3.min.js:531
    Ma.each @ d3.v3.min.js:3958
    Ma.attr @ d3.v3.min.js:3808
    redraw_links @ view.js:3037
    setSelectedWorkspace @ view.js:725
    activateTab @ tabs.js:357
    onTabClick @ tabs.js:300
    dispatch @ jquery-1.9.1.js:3074
    elemData.handle @ jquery-1.9.1.js:2750
    This is with your current master branch - my feature branch is working OK.

  22. #297
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    Quote Originally Posted by h4yn0nnym0u5e View Post
    And here are two different errors that show in the console when I try to connect mixerI2S to i2s - one occurs when I try to make the connection immediately after an F5 refresh, the second when I try again.
    This is now fixed
    It was because I forgot to use the REDLink class when creating new links,
    as the REDLink class initializes the info property which both tabOut and isBus belongs to

  23. #298
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    337
    Great, seems to work for me. I've done a PR which restores the ability to export OSC-based classes, seems to be back to where it was before. See PR comment for why the fix was needed...

    Note that the latest version of the export code assumes the use of the latest OSCAudio commit, which can generate connection names semi-automatically - given a "base" name, it appends the source and target objects and port numbers. If you forget to update, it will fail to compile

  24. #299
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    337
    I've just pushed an update to the OSCAudio repository, which allows for easier maintenance of objects which need constructors with parameters. At the moment this applies to the AudioMixer and AudioMixerStereo objects, which were implemented as special cases but are now (slightly) more general, and AudioEffectDelayExternal, which previously was poorly supported. You can now send something like ("/teensy*/dynamic/crOb", "ssif", "AudioEffectDelayExternal", "delayExt", 3, 2000.0) which will create a 2s delay in a PSRAM on the audio adaptor, or ("/teensy*/dynamic/crOb", "sssif", "AudioEffectDelayExternal", "delay", "/looper/i*", 6, 20000.0) which will create N delay objects of 20s in EXTMEM, in the /looper group. Note these examples need my updated AudioEffectDelayExternal, but it should work OK if you only have a 1.5s 23LC1024 on the audio adaptor.

  25. #300
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    337
    I've just realised that when live-editing an AudioEffectDelayExt object, e.g. changing the maxDelay[ms] parameter, GUI++ will need to destroy and re-create it, in a similar manner to what happens when AudioMixer and AudioMixerStereo objects have the number of inputs changed.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •