Forum Rule: Always post complete source code & details to reproduce any issue!
Page 9 of 12 FirstFirst ... 7 8 9 10 11 ... LastLast
Results 201 to 225 of 300

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

  1. #201
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    338
    While I think, and back on topic a bit more (!), do we want to think about some sort of /subscribe capability? Could the GUI++ be set up to generate those (as defined by the user, of course), and then display the results? I suspect so, given your calculator demo. I'm thinking of allowing for e.g. a CPU%, free memory, audio block usage etc. sort of overlay.

  2. #202
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    ok when I tried to add a picture the page froze and whole prev. message got lost

    here is a summary

    Quote Originally Posted by manicksan View Post
    Think I should also have a option that defines the edit dialog structure it in the node def. or when I think about this further there should not be needing any such structure at all, because it's kind of already defined in the def. Structure.
    turns out I already did that
    here is the json for that
    (I did comment it prev. note comments not possible in JSON)
    Code:
    {
        "defaults": { // order of items matter for editor
            "name": {
                "type": "c_cpp_name"
            },
            "id": {
                "noEdit": "" <- this means not edit of value
            },
            "comment": {},
            "color": {
                "editor": { <- customize editor
                    "type": "color" <- editor type (only text & color available)
                },
                "value": "#E6E0F8"
            },
            "inputs": {
                "value": 1, <- this only define the default value, but works for dynamic inputs
                "maxval": 255,
                "minval": 1,
                "type": "int"
            }
        },
        "editor": "autogen", <- this defines that the editor should be autogenerated from the above defaults, note. the index.html editor code is overriden
        "shortName": "mixerStereo",
        "inputs": 1, <- static real def.
        "outputs": 2, <- static real def.
        "category": "mixer",
        "color": "#E6E0F8",
        "icon": "arrow-in.png",
        "help": "this is a <b>Stereo</b> mixer"
    }
    also some proof of concept stuff (not yet implemented)
    Tab I/O can now be adjustable to allow "bus" signals
    also there is suitable bus joiner/splitter objects
    to make Stereo signals easier to manage in the tool:

    Click image for larger version. 

Name:	StereoAndQuadObject.png 
Views:	77 
Size:	32.5 KB 
ID:	27159

    Click image for larger version. 

Name:	StereoAndQuadObject_main.png 
Views:	77 
Size:	43.9 KB 
ID:	27160

    @h4yn0nnym0u5e
    do you have the /subscribe command implemented?

  3. #203
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    this is a response to
    https://forum.pjrc.com/threads/68798...l=1#post297139
    Click image for larger version. 

Name:	WithoutBusLinks.png 
Views:	74 
Size:	7.1 KB 
ID:	27162

    Here is your example using "bus" links
    (the BusJoin and BusSplit is just a demonstration of a future feature)

    Click image for larger version. 

Name:	BusLinksRealUse.png 
Views:	77 
Size:	11.7 KB 
ID:	27161

    By using bus links it's much easier to determine pairs when two lines go from a stereo array source to a stereo mixer destination.

    the result would otherwise be
    Click image for larger version. 

Name:	WithoutBusLinks_expanded.png 
Views:	74 
Size:	12.7 KB 
ID:	27163

    instead of (by using bus links)
    Click image for larger version. 

Name:	BusLinksRealUse_expanded.png 
Views:	77 
Size:	9.3 KB 
ID:	27164

    back to topic little more
    I hope that I can use the same code to generate the link connections
    for the OSC messages
    that I use for the code exports
    Will see tomorrow.

  4. #204
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    338
    No, I don't have /subscribe even started yet! It just occurred to me that it's present in some implementations of OSC, and would probably be useful. However, like /fs it may not directly be part of OSCaudio. There's also the consideration that the audio objects rarely have much support for reading their current status, and it would be a fairly big job to implement it: there are 86 different objects providing about 160 control and 260 status functions...

    Your bus links look good - can you actually colour the connections differently depending on their type, or is that just done for your images? I can see it being very useful.

    I've just pushed updates to OSCAudio and audio which implement the AudioMixerStereo and expose its functionality to OSC. (Note that I've merged the groups feature branch back into trunk for OSCAudio, and I'm now working in trunk unless we come up with a radical change which needs a branch to develop on.) The only real change is the addition of AudioMixerStereo/setPanLaw(float), which varies how the LR levels change as pan() and balance() go from -1.0 to +1.0. I've found values from 0.71 down to 0.01 usable (default is 0.22), with my preference being for lower values, but I think that'll be very user-dependent. It's also quite subtle, but it takes immediate effect so it's fairly easy to play with. The setting is per-mixer.

  5. #205
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    Yes the connections are automatically colored, if they connect to either a class containing a bus tab IO (a tab IO that have it's IO count set to anything larger than 1)
    I have defined two global styles in css for both the wire border and the actual wire, so it can easily be changed.
    (link_line_bus and link_outline_bus)

    However the wires can also be individually changed by code, I did that before creating the styles.

    I'm not really convinced that is how bus links should look like, but
    It will do for the moment, maybe it could be user customized.

    I did a script for a UI_ScriptButton that changes the styles
    Code:
    [{"id":"Main_scriptBtn1","type":"UI_ScriptButton","name":"changeBusStyle","tag":"","comment":"var style = document.createElement('style');\r\nstyle.type = 'text/css';\r\ndocument.getElementsByTagName('head')[0].appendChild(style);\r\n\r\n//here is the magic.\r\nstyle.innerHTML = '.link_line_bus {'+\r\n    'stroke: #7f7f7f;'+\r\n    'stroke-width: 6;'+\r\n    'fill: none;'+\r\n    'pointer-events: none;'+\r\n    'stroke-dasharray: 10, 5;'+\r\n  '}'+\r\n  '.link_outline_bus {'+\r\n    'stroke:#aef2ff;'+\r\n    'stroke-width: 8;'+\r\n    'cursor: crosshair;'+\r\n    'fill: none;'+\r\n    'pointer-events: none;'+\r\n  '}';","w":117,"h":30,"textSize":14,"nodes":[],"x":346.5,"y":369,"z":"Main","bgColor":"#DDFFBB","wires":[]}
    ]
    Last edited by manicksan; 01-10-2022 at 10:01 AM. Reason: add JSON for script button

  6. #206
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    242
    Quote Originally Posted by h4yn0nnym0u5e View Post
    While I think, and back on topic a bit more (!), do we want to think about some sort of /subscribe capability? Could the GUI++ be set up to generate those (as defined by the user, of course), and then display the results? I suspect so, given your calculator demo. I'm thinking of allowing for e.g. a CPU%, free memory, audio block usage etc. sort of overlay.
    I brought up the /subscribe functionality only as one way to receive the data, but I haven't confirmed that is the only or best way yet. I don't know a ton about OSC so I'm still learning.

    I checked Reaper, Ableton, and some other DAWs to see how they are doing it. Here is the configuration window for the Reaper DAW.

    Click image for larger version. 

Name:	reaper_control_surface_settings.png 
Views:	79 
Size:	21.2 KB 
ID:	27197

    In this instance Reaper would be in the same space as the Teensy. It's what is being controlled. Notice the "local IP" is greyed out because that's what the current address is for Reaper (it's on my desktop PC at that IP). But then there is a "Device Port", this port is where the controller is, it's where the GUI is. We don't really have any networking setup for Teensy OSC control yet, only serial. So this explanation is just so I can start to grasp exactly what's going on.

    It seems to me that the most standard way to do this would be to setup the reverse of what you've setup now. Currently the Teensy is Listening (aka server), it accepts commands and responds if needed. For proper implementation within standard practice (undefined in the OSC definition) we would want the teensy to add Send (aka client) functionality; I believe.

    The Teensy would be a client that initiates commands to the GUI (server). It can start spitting out as much data as it wants to the server (GUI or otherwise), but unless the GUI knows what to do with the data, it is speaking to deaf ears. So we could theoretically just start sending all the data we have to a serial port. (IE Send CPU/memory/block data to an address /GUI1, /GUI2, etc) but this would create a lot of traffic. Alternatively, we could have the GUI tell the Teensy what to send, where, at what interval, and for how long. Then the GUI could also tell the Teensy to stop sending. This is somewhat like the subscribe concept, maybe a little more advanced.

    Steps to set it all up (potentially).

    1. Configure the Teensy client by communicating with its server... In this step we tell the Teensy what data it should start sending, where it should send the data, at what interval, and for how long. Doing this could mean that the Teensy may send data to multiple clients too at different ports). Just each client would need to subscribe and tell the Teensy where it is. IE /teensy1/rms1/read/subscribe/<port> <OSC Address> <interval> <Duration>.
    2. The Teensy then begins sending the data to the specified port at the specified interval and duration (or infinitely). /GUI1/rms1/read/<data>... and/or /controlSurface2/rms1/read/<data>...
    3. The GUI then receives the information and uses it. Note, the GUI would be a client (which is not implemented at the moment, if I'm not mistaken).
    4. The timeframe can expire and the data will stop, or the GUI can "unsubscribe" /teensy1/rms/read/unsubscribe or /teensy1/rms/read/subscribe/stop

    Quote Originally Posted by h4yn0nnym0u5e View Post
    No, I don't have /subscribe even started yet! It just occurred to me that it's present in some implementations of OSC, and would probably be useful. However, like /fs it may not directly be part of OSCaudio. There's also the consideration that the audio objects rarely have much support for reading their current status, and it would be a fairly big job to implement it: there are 86 different objects providing about 160 control and 260 status functions...
    I agree, not all functions require a "send from Teensy to GUI" command. Although setting up the scaffold for it in the future might be quite nice. Setup the scaffold, and then in the future other objects can respond to some request. It seems to me that any of the "Analyze" functions would be a prime target for this. (peak, rms, fft256, fft1024, tone, notefreq, print). Then in the future, if the other objects wanted to implement read functionality they can. In the meantime, the /subscribe command would just be ignored. (IE /teensy1/mixer1/subscribe does nothing, but exists maybe still?).

    Does my analysis make sense on this? I hope maybe it's becoming clearer on how this could work. My ultimate goal is simply to get the meter data in the GUI (for monitoring audio levels, and for eye candy).

    It has also dawned on me that maybe making the Teensy an OSC client can help if/when the Teensy wants to become a control surface in itself! Picture two Teensy's. One is the audio processor. The other is the control surface. The control surface would need to be an OSC client so it can control the audio processor. Then the audio processor is also a client, sending audio data to the control surface for displaying a Meter on LEDs... How cool would that be! It sure would be nice if your work would also help move in that direction so that the Teensy can be configured as a client and send control data to another device.
    Last edited by JayShoe; 01-13-2022 at 01:08 PM.

  7. #207
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    338
    I agree it would be possible to make the Teensy an OSC client, and equally possible to use Ethernet as the transport stream. I've not touched Ethernet on the Teensy as yet, though I do have an adaptor kit waiting for me to pay it some attention.

    Having said that, the concept is pretty much the exact opposite of your original post #1! And implementing an OSC client is just a Small Matter Of Programming, which would work equally well with the original static audio library. Using the Audio Design Tool as the OSC server / display is probably not the best choice, though a few well-chosen readouts would undoubtedly be helpful, e.g. being able to show memory use and CPU load as a design grows. Far better to have your concept of control surfaces on tablets, as I think you posted some time back. Or maybe as tiny standalone applications which you can tile together on a PC screen.

    At the moment I'm prioritising any support needed for @manicksan to get the GUI++ working to his / our satisfaction, and a secondary goal of seeing if any of this is useful on a Teensy 3.x. For the latter, I need to implement the dynamic audio library, as it's only on 4.x at the moment.

    Interesting that Reaper supports OSC, even if only via Ethernet. I must take a look at that, once I've got Ethernet going.

  8. #208
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    242
    Quote Originally Posted by h4yn0nnym0u5e View Post
    I agree it would be possible to make the Teensy an OSC client, and equally possible to use Ethernet as the transport stream.
    Yeah, that's the ideal solution in a nutshell. Ethernet or an ESP32... The ESP32 would have to work as an OSC bridge (two ways for server/client). :-)

    Quote Originally Posted by h4yn0nnym0u5e View Post
    Having said that, the concept is pretty much the exact opposite of your original post #1! And implementing an OSC client is just a Small Matter Of Programming, which would work equally well with the original static audio library.
    It's worth mentioning my appreciation. You and manicksan have contributed dearly to the Audio Library with your work on this. Thank you for your hard work.

    Using the Audio Design Tool as the OSC server / display is probably not the best choice
    It does feel funny doesn't it. But I think that's how two way communication works with OSC. To get the meter data the control surface listens to what the device sends out to the controller, usually send to some address. What we discussed previously is more of a request/response functionality. I saw that functionality in the X32 but nowhere else.

    I wonder, CAN a browser be an OSC server? Or would it have to be a Request/Response situation only?

  9. #209
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    Quote Originally Posted by JayShoe View Post
    I wonder, CAN a browser be an OSC server? Or would it have to be a Request/Response situation only?
    only supported by Opera Unite in the Opera browser

    https://en.wikipedia.org/wiki/List_o...mplementations


    The only thing that all modern browsers support is WebSockets but that is client only.

    Updates on the Tool++:
    * bug fixes + improvements
    https://forum.pjrc.com/threads/65740...l=1#post297453
    * code restructure

    OSC group export:
    think I have the generation of objects working now
    it support the following structure
    Code:
    {"version":1,"settings":{"arduino":{"ProjectName":"GroupExampleStereo2","Board":{"Platform":"","Board":"","Options":""}},"BiDirDataWebSocketBridge":{},"workspaces":{},"sidebar":{"autoSwitchTabToInfoTab":false},"palette":{},"editor":{},"devTest":{},"IndexedDBfiles":{"testFileNames":"testFile.txt"},"NodeDefGenerator":{},"NodeDefManager":{},"NodeHelpManager":{},"OSC":{"LiveUpdate":false,"UseDebugLinkName":true}},"workspaces":[{"type":"tab","id":"3629fcd9.ccc604","label":"Main","inputs":0,"outputs":0,"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isOSCmain":true,"generateCppDestructor":false,"extraClassDeclarations":"","settings":{"workspaceBgColor":"#EDFFDF","scaleFactor":0.8,"showGridHminor":false,"showGridHmajor":false,"showGridVminor":false,"showGridVmajor":false,"snapToGridHsize":1,"snapToGridVsize":1,"useCenterBasedPositions":false},"nodes":[{"id":"Main_waveform1","type":"AudioSynthWaveform","name":"LFO","comment":"","x":5,"y":155,"z":"3629fcd9.ccc604","bgColor":"#E6E0F8","wires":[["Main_MonoSynth1:0","Main_MonoSynth3:0","Main_waveformMod1:0","Main_waveformMod2:0"]]},{"id":"Main_MonoSynth3","type":"MonoSynth","name":"MonoSynthC","x":160,"y":20,"z":"3629fcd9.ccc604","bgColor":"#CCFFCC","wires":[["Main_mixer3:0"]]},{"id":"Main_MonoSynth1","type":"MonoSynth","name":"MonoSynthA[6]","x":165,"y":80,"z":"3629fcd9.ccc604","bgColor":"#CCFFCC","wires":[["Main_mixer1:0"]]},{"id":"Main_waveformMod1","type":"AudioSynthWaveformModulated","name":"wfmA[3]","comment":"","x":145,"y":225,"z":"3629fcd9.ccc604","bgColor":"#E6E0F8","wires":[["Main_mixer4:0"]]},{"id":"Main_waveformMod2","type":"AudioSynthWaveformModulated","name":"wfmB[3]","comment":"","x":145,"y":265,"z":"3629fcd9.ccc604","bgColor":"#E6E0F8","wires":[["Main_mixer4:1"]]},{"id":"Main_mixer4","type":"AudioMixer","name":"mixer[3]","comment":"","inputs":2,"x":290,"y":245,"z":"3629fcd9.ccc604","bgColor":"#E6E0F8","wires":[["Main_mixer2:0"]]},{"id":"Main_mixer1","type":"AudioMixer","name":"mixerL","comment":"","inputs":1,"x":360,"y":80,"z":"3629fcd9.ccc604","bgColor":"#E6E0F8","wires":[["Main_mixer3:1"]]},{"id":"Main_mixer3","type":"AudioMixer","name":"mixerL1","comment":"","inputs":2,"x":488,"y":28,"z":"3629fcd9.ccc604","bgColor":"#E6E0F8","wires":[["Main_i2s1:0"]]},{"id":"Main_mixer2","type":"AudioMixer","name":"mixerR","comment":"","inputs":1,"x":489,"y":246,"z":"3629fcd9.ccc604","bgColor":"#E6E0F8","wires":[["Main_i2s1:1"]]},{"id":"Main_sgtl5000_1","type":"AudioControlSGTL5000","name":"sgtl5000","comment":"","x":685,"y":199,"z":"3629fcd9.ccc604","bgColor":"#E6E0F8","wires":[]},{"id":"Main_i2s1","type":"AudioOutputI2S","name":"i2s","comment":"","x":721.1111145019531,"y":132.77777290344238,"z":"3629fcd9.ccc604","bgColor":"#E6E0F8","wires":[]}]},{"type":"tab","id":"de8d0e25.32ebb","label":"MonoSynth","inputs":0,"outputs":0,"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isOSCmain":false,"generateCppDestructor":false,"extraClassDeclarations":"","settings":{"workspaceBgColor":"#EDFFDF","showGridHminor":false,"showGridHmajor":false,"showGridVminor":false,"showGridVmajor":false,"useCenterBasedPositions":false},"nodes":[{"id":"MonoSynth_In1","type":"TabInput","name":"In","comment":"","outputs":1,"x":105,"y":80,"z":"de8d0e25.32ebb","bgColor":"#cce6ff","wires":[["MonoSynth_waveformMod1:0"]]},{"id":"MonoSynth_Out1","type":"TabOutput","name":"Out1","comment":"","inputs":1,"x":620,"y":85,"z":"de8d0e25.32ebb","bgColor":"#cce6ff","wires":[]},{"id":"MonoSynth_waveformMod1","type":"AudioSynthWaveformModulated","name":"wav[4]","comment":"","x":210,"y":85,"z":"de8d0e25.32ebb","bgColor":"#E6E0F8","wires":[["MonoSynth_mixer1:0"]]},{"id":"MonoSynth_mixer1","type":"AudioMixer","name":"mixer","comment":"","inputs":1,"x":325,"y":85,"z":"de8d0e25.32ebb","bgColor":"#E6E0F8","wires":[["MonoSynth_envelope1:0"]]},{"id":"MonoSynth_envelope1","type":"AudioEffectEnvelope","name":"env","comment":"","x":425,"y":85,"z":"de8d0e25.32ebb","bgColor":"#E6E0F8","wires":[["MonoSynth_Eq1:0"]]},{"id":"MonoSynth_Eq1","type":"Eq","name":"eq","x":530,"y":85,"z":"de8d0e25.32ebb","bgColor":"#ccffcc","wires":[["MonoSynth_Out1:0"]]}]},{"type":"tab","id":"40738f40.4e3d7","label":"Eq","inputs":0,"outputs":0,"export":true,"isMain":false,"mainNameType":"tabName","mainNameExt":".ino","isOSCmain":false,"generateCppDestructor":false,"extraClassDeclarations":"","settings":{"workspaceBgColor":"#EDFFDF","showGridHminor":false,"showGridHmajor":false,"showGridVminor":false,"showGridVmajor":false,"useCenterBasedPositions":false},"nodes":[{"id":"SubObject_In1","type":"TabInput","name":"In","comment":"","outputs":1,"x":110,"y":80,"z":"40738f40.4e3d7","bgColor":"#cce6ff","wires":[["SubObject_filter1:0"]]},{"id":"SubObject_Out1","type":"TabOutput","name":"Out","comment":"","inputs":1,"x":415,"y":85,"z":"40738f40.4e3d7","bgColor":"#cce6ff","wires":[]},{"id":"SubObject_filter1","type":"AudioFilterStateVariable","name":"filter[3]","comment":"","x":200,"y":80,"z":"40738f40.4e3d7","bgColor":"#E6E0F8","wires":[[],["SubObject_mixer1:0"],[]]},{"id":"SubObject_mixer1","type":"AudioMixer","name":"mixer","comment":"","inputs":1,"x":310,"y":85,"z":"40738f40.4e3d7","bgColor":"#E6E0F8","wires":[["SubObject_Out1:0"]]}]}],"nodeAddons":{"h4yn0nnyNodes":{"isAddon":true,"label":"h4yn0nnyNodes","description":"","credits":"h4yn0nnym0u5e","homepage":"","url":"https://api.github.com/repos/h4yn0nnym0u5e","types":{"AudioEffectExpEnvelope":{"defaults":{"name":{"type":"c_cpp_name"},"comment":{},"color":{"value":"#c6c0d8"}},"shortName":"expEnv","inputs":1,"outputs":1,"inputTypes":{"0":"i16"},"outputTypes":{"0":"i16"},"category":"effect","help":"","color":"#c6c0d8","icon":"arrow-in.png"},"AudioMixerStereo":{"defaults":{"name":{"type":"c_cpp_name"},"id":{},"inputs":{"value":1,"maxval":255,"minval":1,"type":"int"},"comment":{},"color":{"value":"#E6E0F8"}},"dynInputs":"","shortName":"mixerS","inputs":1,"outputs":2,"category":"mixer","color":"#E6E0F8","icon":"arrow-in.png"}}}}
    }
    note. that it support arrays of dynmixers

    the audio connections part is still not finished
    I think I have to do that part from scratch
    as the old version (c++ export) was very clumsy

    the new version could also be used to export a class based design
    to a classic flat design
    as that share the same structure as required by the OSC lib

    another improvement of the Tool++ (not really visible to the user)
    is that it's always sorting the objects in the background now
    1. block of all ui objects (the ui objects are not sorted in that block, to preserve draw order)
    2. block of all TabInputs and TabOutputs (only sorted by y pos, top to bottom)
    3. block of all other objects (sorted in workspace columns top to bottom)

    this above makes it much easier to find the right TabInputs & TabOutputs

    when the osc group export is done it can also be used for non group designs and therefore the osc simple export is obsolete.

    another improvement is easier to read osc export dialog "messages"
    Last edited by manicksan; 01-13-2022 at 11:41 PM.

  10. #210
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    242
    Quote Originally Posted by manicksan View Post
    The only thing that all modern browsers support is WebSockets but that is client only.
    I would then say that full Teensy OSC-Client support doesn't make much sense for the GUI tool. The only real option is for the request/response format. Yes, the client support is necessary for something like TouchOSC.

    Quote Originally Posted by h4yn0nnym0u5e View Post
    a few well-chosen readouts would undoubtedly be helpful, e.g. being able to show memory use and CPU load as a design grows
    These are really just "extra" OSC commands, adding some non-audio tool related calls to the OSC address list. This information is outside of the audio library. I guess you'll just want to define them separately from the object list that has been created automatically. It will be low interval, right? It's just a call and a response - or would you have a subscription type method with duration and interval?

  11. #211
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    But as long as the websocket connection is open, data can be sent in both ways.

    And if the final system need multiple control surfaces/audio generators to be updated maybe it needs some kind of central hub server that links everything together.

    Don't mind the OSC touch control, as it's available for Android as well, do you know about roboremo? (Downside with that is no support for OSC messages, but a server could translate that)

  12. #212
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    this is my version of the mixed fade functionality
    no need for any special calculations

    in .cpp file
    Code:
    void AudioMixer::update(void)
    {
    	audio_block_t *in, *out=NULL;
    	unsigned int channel;
    
    	for (channel=0; channel < _ninputs; channel++) {
    		
    		if (currentMultiplier[channel] != multiplier[channel])
    		{
    			if (currentMultiplier[channel] < multiplier[channel])
    			{
    				currentMultiplier[channel] += multUpdateRate; // this could overflow the currentMultiplier datatype, but that would be very rare, as gains often don't get over 1.0 anyway 
    				// overflow check
    				if (currentMultiplier[channel] > multiplier[channel])
    					currentMultiplier[channel] = multiplier[channel]; // stop here
    			}
    			else /*if (currentMultiplier[channel] > multiplier[channel])*/
    			{
    				currentMultiplier[channel] -= multUpdateRate; // this could underflow the currentMultiplier datatype, but that would be very rare, as gains often don't get over 1.0 anyway 
    				// underflow check
    				if (currentMultiplier[channel] < multiplier[channel])
    					currentMultiplier[channel] = multiplier[channel]; // stop here
    			}
    		}
    		
    		if (NULL != out) {
    			in = receiveReadOnly(channel);
    			if (in == NULL) continue;
                applyGainThenAdd(out->data, in->data, currentMultiplier[channel]);
    			release(in);
    		} else {
                out = receiveWritable(channel);
                if (NULL == out) continue;
                int32_t mult = currentMultiplier[channel];
    			if (mult == MULTI_UNITYGAIN) continue;
                applyGain(out->data, mult);
    		}
    	}
    	if (NULL == out) return;
        transmit(out);
    	release(out);
    }
    in .h file
    Code:
    class AudioMixer : public AudioStream
    {
    #if defined(__ARM_ARCH_7EM__)
    public:
    	AudioMixer(unsigned char ninputs, audio_block_t **iqueue) : AudioStream(ninputs, iqueue),
        inputQueueArray(iqueue), _ninputs(ninputs)
        {
            //Serial.printf("\nninputs = %d %d\n\n", _ninputs, sizeof(this));
            multiplier = (int32_t*)malloc(_ninputs*4);
    		currentMultiplier = (int32_t*)malloc(_ninputs*4);
    		for (int i=0; i<_ninputs; i++) {
    			multiplier[i] = 65536;
    			currentMultiplier[i] = 65536;
    		}
    	}
        ~AudioMixer()
        {
            free(multiplier);
    		free(currentMultiplier);
            free(inputQueueArray);
        }
    	virtual void update(void);
    	void gain(unsigned int channel, float gain) {
    		if (channel >= _ninputs) return;
    		if (gain > 32767.0f) gain = 32767.0f;
    		else if (gain < -32767.0f) gain = -32767.0f;
    		multiplier[channel] = gain * 65536.0f; // TODO: proper roundoff?
    		if (fadeRate == 0)
    			currentMultiplier[channel] = gain * 65536.0f; // TODO: proper roundoff?
    	}
    	void fadeRate(int32_t rate)
    	{
    		if (rate > 32767*65536) rate = 127*127;
    		else if (rate < 0) rate = 0;
    		multUpdateRate = rate;
    		if (multUpdateRate == 0)
    		{
    			for (int i=0; i<_ninputs; i++)
    			{
    				currentMultiplier[i] = multiplier[i];
    			}
    		}
    	}
    private:
        unsigned char _ninputs;
    	int32_t *multiplier;
    	int32_t *currentMultiplier;
    	int32_t multUpdateRate;
        //audio_block_t *toBeIgnored[1];
    	audio_block_t **inputQueueArray;
    
    #elif defined(KINETISL)
    public:
    	AudioMixer(unsigned char ninputs, audio_block_t **iqueue) : AudioStream(ninputs, iqueue) {
            inputQueueArray = iqueue;
            _ninputs = ninputs;
            multiplier = (int16_t*)malloc(_ninputs);
    		currentMultiplier = (int16_t*)malloc(_ninputs*4);
    		for (int i=0; i<_ninputs; i++) {
    			multiplier[i] = 256;
    			currentMultiplier[i] = 256;
    		}
    	}
        ~AudioMixer()
        {
            free(multiplier);
    		free(currentMultiplier);
            free(inputQueueArray);
        }
    	virtual void update(void);
    	void gain(unsigned int channel, float gain) {
    		if (channel >= _ninputs) return;
    		if (gain > 127.0f) gain = 127.0f;
    		else if (gain < -127.0f) gain = -127.0f;
    		multiplier[channel] = gain * 256.0f; // TODO: proper roundoff?
    		if (multUpdateRate == 0) // if set to zero 
    			currentMultiplier[channel] = gain * 256.0f; // TODO: proper roundoff?
    	}
    	void fadeRate(int32_t rate)
    	{
    		if (rate > 127*256) rate = 127*256;
    		else if (rate < 0) rate = 0;
    		
    		multUpdateRate = rate;
    		if (multUpdateRate == 0)
    		{
    			for (int i=0; i<_ninputs; i++)
    			{
    				currentMultiplier[i] = multiplier[i];
    			}
    		}
    	}
    private:
    	int16_t *multiplier;
    	int16_t *currentMultiplier;
    	audio_block_t **inputQueueArray;
    #endif
    };

  13. #213
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    338
    That's fine as far as it goes, but it's going to suffer from "zipper noise" at some fade rates because the multiplier is only changed once per audio update cycle. It really needs updating more often - maybe not every sample, but surely better than once every AUDIO_BLOCK_SAMPLES!

    You might want to pull in my updates to your original DynMixer code, as I've spent some time removing the need to maintain two blocks of near-identical code just to support the Teensy LC.

  14. #214
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    Quote Originally Posted by h4yn0nnym0u5e View Post
    It really needs updating more often
    Is not the update called 44100 times a second?

    Quote Originally Posted by h4yn0nnym0u5e View Post
    I've spent some time removing the need to maintain two blocks of near-identical code just to support the Teensy LC.
    I did that as well with the "c++ template mixer code"(now replaced with Tool Built in code),
    don't really know why the original mixer code looks that way.

    And yes I have already looked at you mixer code, looks good (will use that for the built in code).


    OSC group/class export progress:
    can now export the following
    (not yet support for arrays of classes)

    main:
    Click image for larger version. 

Name:	ClassSupport_Main.png 
Views:	75 
Size:	7.3 KB 
ID:	27240

    voice (note I have included every possible situation):
    Click image for larger version. 

Name:	ClassSupport_Voice.png 
Views:	80 
Size:	7.1 KB 
ID:	27241

    sub (used inside voice):
    Name:  ClassSupport_Sub.png
Views: 76
Size:  3.7 KB

    I have now created a separate 'tab setting' that selects the
    Audio Main 'tab'
    so that the Main File (now called C++ Main File) can be set separately

  15. #215
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    338
    Quote Originally Posted by manicksan View Post
    Is not the update called 44100 times a second?
    No, the update interrupt is called 44100/128 times a second, and processes 128 samples (or technically AUDIO_BLOCK_SAMPLES, which is usually set to 128), so about every 2.9ms.

    At the moment I'm finding the OSC export is broken, as it doesn't create the connections. Is there any chance these could be made to work, even in a limited way (e.g. not bothering about array or stereo connections)? I'm exploring the limits of Teensy 3.x, and at the moment all my design uploads have to use Python, and I keep making mistakes! Doesn't have to be on the main development trunk.

  16. #216
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    Quote Originally Posted by h4yn0nnym0u5e View Post
    No, the update interrupt is called 44100/128 times a second, and processes 128 samples (or technically AUDIO_BLOCK_SAMPLES, which is usually set to 128), so about every 2.9ms.
    so if I understand this code correct (taken from effect_fade.cpp)
    Code:
    dir = direction;
    for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
        index = pos >> 24;
        val1 = fader_table[index];
        val2 = fader_table[index+1];
        scale = (pos >> 8) & 0xFFFF;
        val2 *= scale;
        val1 *= 0x10000 - scale;
        val = (val1 + val2) >> 16;
        sample = block->data[i];
        sample = (sample * val) >> 15;
        block->data[i] = sample;
        if (dir > 0) {
            // output is increasing
            if (inc < 0xFFFFFFFF - pos) pos += inc;
            else pos = 0xFFFFFFFF;
        } else {
            // output is decreasing
            if (inc < pos) pos -= inc;
            else pos = 0;
        }
    }
    position = pos;
    for example the currentMultiplier[channel] += multUpdateRate;
    need to be executed for every sample

    that would mean that currentMultiplier need to be updated in the applyGain and applyGainThenAdd respective, instead

    Quote Originally Posted by h4yn0nnym0u5e View Post
    At the moment I'm finding the OSC export is broken, as it doesn't create the connections. Is there any chance these could be made to work
    can you post a image of your design?
    is it because it's missing the leading / of the connections?
    strange I have not changed anything in the simple export

    You can try the group export it's also working on flat designs.

  17. #217
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    338
    Nothing weird in the design at all. I think it's the OSC library's bizarre "pattern matching", which sort of works some of the time. Looking at the code it's allowed to have a * wildcard in both address and pattern, but it doesn't seem to work the way you'd expect. As there are precisely 3 comments in the entire OSCMatch.cpp we're on to a bit of a loser trying to figure it out. The only thing you might have done is use crOb and crGrp to create objects and groups, but createConnection to create connections. I'd like users to have a few options to use long or short forms for readability or efficiency, but maybe that's not going to be possible.

    Too late to do more now.

  18. #218
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    242
    I always felt as if the pattern matching function was for the purposes of selecting multiple mixers, for example. Call /mixer* and get /mixer1, /mixer2, and /mixer3_diff. I envisioned it as a client function not a server function. If I'm not mistaken, you've utilized the wildcards for the server addresses.

  19. #219
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    All the "commands" in the Tool are declared in one place
    that means it's very easy to change them.
    I took
    crOb
    crGrp
    crCo (which I renamed to createConnection for clarify the debug output in the export dialog)

    but now I have invented a kind of comment which I can add to the bundle
    that is then shown in the export dialog but removed before sending the OSC data.
    comments are just a packet with the address that begins with //

  20. #220
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    Now it's possible to do a design like this
    (only in OSC group export)
    i.e. arrays of classes and standard objects now possible
    (main)
    Click image for larger version. 

Name:	ClassArraysSupport_Main.png 
Views:	75 
Size:	15.0 KB 
ID:	27265
    (monoVoice)
    Click image for larger version. 

Name:	ClassArraysSupport_MonoVoice.png 
Views:	71 
Size:	5.4 KB 
ID:	27266
    (sub)
    Name:  ClassSupport_Sub.png
Views: 54
Size:  3.7 KB

    here you can see that it's now possible to put multiple sized items into the mixer
    and the mixer in this case would be of size 10
    the inputs are added in the following order

    0 dcA
    1 waveform(0)
    2 waveform(1)
    3 waveform(2)
    4 waveform(3)
    5 subsB(0)
    6 subsB(1)
    7 monovoice(0)
    8 monovoice(1)
    9 dcB


    TODO.
    stereo mixer
    Junction support
    connect array to array:
    Name:  ArrayToArray.png
Views: 53
Size:  6.1 KB

  21. #221
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    The stereo mixer works, it is just that the inputs are gonna be put in the following order:
    L0 L1 L2 R0 R1 R2
    Instead of (expected)
    L0 R0 L1 R1 L2 R2

  22. #222
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    338
    Quote Originally Posted by JayShoe View Post
    I always felt as if the pattern matching function was for the purposes of selecting multiple mixers, for example. Call /mixer* and get /mixer1, /mixer2, and /mixer3_diff. I envisioned it as a client function not a server function. If I'm not mistaken, you've utilized the wildcards for the server addresses.
    You're right of course. But looking at the latest code on github there seems to be some sort of intent to provide (limited) wildcards in both. Here's a fragment of code from OSCMatch.c:
    Code:
    if(*pattern == '*'){
    	if(!osc_match_star(pattern, address)){
    		return 0;
    	}
    	while(*pattern != '/' && *pattern != '\0'){
    		pattern++;
    	}
    	while(*address != '/' && *address != '\0'){
    		address++;
    	}
    }else if(*address == '*'){
    	while(*pattern != '/' && *pattern != '\0'){
    		pattern++;
    	}
    	while(*address != '/' && *address != '\0'){
    		address++;
    	}
    }else{
    Then I wrote a quick fragment to try various address / pattern combinations, which gave odd results, so I went back to the source code. And found the following:
    Code:
    // From OSCMessage.cpp
    int OSCMessage::match(const  char * pattern, int addr_offset){
    	int pattern_offset;
    	int address_offset;
    	int ret = osc_match(address + addr_offset, pattern, &pattern_offset, &address_offset);
    
    // From OSCMatch.c
    int osc_match(const char *pattern, const char *address, int *pattern_offset, int *address_offset)
    Now, just guessing, but that looks suspiciously like a massive bug to me... that's been there since April 2013. I'm going to have a go at swapping those ol' parameters right over to see if it starts to make sense!

    EDIT: No , I think I'm wrong. Again. Apparently it makes perfect sense to say the OSC Address Pattern passed in the message is the "pattern", and the thing it matches in the OSC Address Space is the "address". Even if you put the Address Pattern in the OSCMessage using OSCMessage::setAddress(), whose function is to "Set the address of the OSCMessage".

    My brain hurts.
    Last edited by h4yn0nnym0u5e; 01-19-2022 at 09:23 PM. Reason: Correct gross misapprehension on my part.

  23. #223
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    338
    Quote Originally Posted by manicksan View Post
    The stereo mixer works, it is just that the inputs are gonna be put in the following order:
    L0 L1 L2 R0 R1 R2
    Instead of (expected)
    L0 R0 L1 R1 L2 R2
    This and the above all sound great. Input order isn't massively important, though I guess it means the user having to know how wide the mixer is ... ah, right ... now I remember ... I implemented getChannels() for just such an occasion! And of course it's easy to make any pan/balance UI objects address the correct channel pairs, just use the balance(chL,chR,position) method.

  24. #224
    Senior Member
    Join Date
    Apr 2021
    Location
    Cambridgeshire, UK
    Posts
    338
    Just pushed an update to OSCAudio, should be more tolerant of some variations we've used on the dynamic engine addressing. It'll also be less efficient, but we can tweak the rules later... I'm currently matching /crOb or /createObject, /crCo or /createConnection, /crGr or /crGrp or /createGroup, deOb or /destroyObject, /renameObject and /clearAll. Using an address pattern in the message means, for example, you can use /cre*Obj* if you want something a bit more readable than /crOb.

    Live editing in GUI++ seems to be working OK for me, though I've yet to try the array editing capabilities - that's next!

    EDIT: trying the array stuff, and needed to push some changes to allow missing the initial / character off group paths. I think that's reasonable: creating a group at root you can now not pass a parent, or pass either an empty string or "/". Similarly you can create objects in multiple groups by specifying either voice1/i* or /voice1/i*
    Last edited by h4yn0nnym0u5e; 01-20-2022 at 11:54 AM. Reason: Documented more changes to library

  25. #225
    Senior Member manicksan's Avatar
    Join Date
    Jun 2020
    Location
    Sweden
    Posts
    606
    Live edit of array/class is currently not supported.
    as I still struggling with the group export

    have also implemented the navigator map (taken direct from node-red)
    available at the zoom controls corner

    I did some changes how the group export behaves
    If there is many tabs selected as "Audio Main":
    1. If current selected tab is a "Audio Main" then that is used
    2. If current selected tab is not a "Audio Main" then the first "Audio Main" in the tab order is used

    while I did this I accidentally did
    Code:
    if (nns.workspaces[wi].id = RED.view.activeWorkspace) {
        mainSelected = wi;
    }
    instead of
    Code:
    if (nns.workspaces[wi].id == RED.view.activeWorkspace) {
        mainSelected = wi;
    }
    this resulted in a huge mess
    and my current design got lost into a 25MB json file
    good is that I had recently saved a json, so it was not so much loss,
    and I could "kind of"(the interface was really slow) look at the old design to make a new one in firefox.

    Because of that I have now implemented some safe guards:
    1. always making a copy of the workspace when doing exports
    2. implemented a save on "page reload" setting @ settings-global-"Auto Download JSON"
    this can be a good thing to use while doing huge and important stuff


    I have now fixed so that the initial / always are included.

Posting Permissions

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