Text values for output bus, effects or gain reduction - use label, paintRoutine or animated strip?
-
VU meters have lots of questions, most of which have excellent answers ( @dustbro ). But I have not found so many describing value outputs in labels. I mean the actual values of an audio output, bus output or a gain reduction value to be used in a label or a slider output value. Thats my aim here, to show the values. I managed to do so, but with issues.
In general, is it best to use a label or a modified panel/slider/knob to get the value? Or is paintRoutine more bug-free? Or is it safer to use a combined custom graphics film-strip that also includes the text values? It´s less code and could be done with Knobman or PShop. If it´s cusom all for the ui I guess thats what I would try to swing.
I have a script from this thread, that works in the slider part and for about 1 minute it rocks solid, but then crashes.
Apple Crash report here:
macOS 10.13 - crash reports - VU METER (LevelMeterTest) 1.txtExtract from the report gives:
Time Awake Since Boot: 12000 seconds System Integrity Protection: enabled Crashed Thread: 0 JUCE Message Thread Dispatch queue: com.apple.main-thread Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0xffffffffffffff8c Exception Note: EXC_CORPSE_NOTIFY Termination Signal: Segmentation fault: 11 Termination Reason: Namespace SIGNAL, Code 0xb Terminating Process: exc handler [0]
My hise script code is this:
(using labels to show values)Content.makeFrontInterface(600, 500); const var LevelMeterL = Content.getComponent("LevelMeterL"); const var LevelMeterR = Content.getComponent("LevelMeterR"); const var LevelMeterLock = Content.getComponent("LevelMeterLock"); // show values as well, via label with whole integer db values. const var Label_MeterValue = Content.getComponent("Label_MeterValue"); // the peak of the strongest channel. const var Label_MeterValue_L = Content.getComponent("Label_MeterValue_L"); const var Label_MeterValue_R = Content.getComponent("Label_MeterValue_R"); const var Pan = Content.getComponent("Pan"); const var SimpleGain1 = Synth.getEffect("SimpleGain1"); const var DECAY_RATE = 0.93; //Current Values var curLevelL = 0.0; var curLevelR = 0.0; var curLevelM = 0.0; //Timer Callback const var t = Engine.createTimerObject(); t.setTimerCallback(function() { //Synth Values var LevelL = SimpleGain1.getCurrentLevel(1); var LevelR = SimpleGain1.getCurrentLevel(0); //Peak Synth Values var peakLevelL = Math.max(LevelL, LevelL); var peakLevelR = Math.max(LevelR, LevelR); var peakLevelMax = Math.max(LevelL, LevelR); //Kick Left //----------------------------------------------------------------------------- if (peakLevelL > curLevelL) { curLevelL = peakLevelL; } else { curLevelL *= DECAY_RATE; } //Kick Right //----------------------------------------------------------------------------- if (peakLevelR > curLevelR) { curLevelR = peakLevelR; } else { curLevelR *= DECAY_RATE; } //Kick Max peak for mono out or label with max peak for strongest channel. //----------------------------------------------------------------------------- if (peakLevelMax > curLevelM) { curLevelM = peakLevelMax; } else { curLevelM *= DECAY_RATE; } //Decibel Conversion //----------------------------------------------------------------------------- LevelL = Engine.getDecibelsForGainFactor(curLevelL); LevelR = Engine.getDecibelsForGainFactor(curLevelR); var LevelMax = Engine.getDecibelsForGainFactor(curLevelM); //Set Values //---------------------------------------------------------------------------- LevelMeterL.setValue(LevelL); LevelMeterR.setValue(LevelR); Label_MeterValue.set("text", (LevelMax >= -99) ? Math.round(LevelMax) : -100); // use Math.round (round up) for all except minimum value, -100. Label_MeterValue_L.set("text", (LevelL >= -99) ? Math.round(LevelL) : -100); Label_MeterValue_R.set("text", (LevelR >= -99) ? Math.round(LevelR) : -100); }); t.startTimer(30);
Anyone got an idéa about what the issue might be for the crash?
- using one timer for all three values?
- too low intervalls?
And here is how it looks:
Full project files:
Here is that project (scriptnode 2020-08-20): LevelMeterTest--andioak-peak-values.zipUsing Scriptnode branch 2020-08-20.
Original code from @DimitrisSP via https://forum.hise.audio/topic/2928/vuuuu-meter-vu-meter-vu-meter-master/46 -
When I take away the labels in the function controlling the timer, it works:
// use Math.round (round up) for all except minimum value, -100. Label_MeterValue.set("text", (LevelMax >= -99) ? Math.round(LevelMax) : -100); Label_MeterValue_L.set("text", (LevelL >= -99) ? Math.round(LevelL) : -100); Label_MeterValue_R.set("text", (LevelR >= -99) ? Math.round(LevelR) : -100);
So in essence, the safest way would be to use a film-strip for values on another slider, which I tried yesterday. But what´s the correct way to add text to the interface that is following a gain parameter?
-
Strange that it crashes when setting the text. Maybe setting labels in fast timers is bad? I think if you draw the text to the panel it'll be a lot cleaner.
-
@andioak I encountered tons of crashes when using timers and drawing more or less complex things... months of debugging to understand what happened in Hise, knowing that the exported plugins are generally stable. I was always trying different things to ameliorate the stability, then a new wave of crashes happened, it just made no sense that the cause was the code...
So I finally found a solution and no more crashes! I simply preview my interface in an additional floating window on the second monitor and hide the main one.
It appears that the preview in the main Hise window causes crashes when drawing permanently different components, eventually via timers...
Don't ask why, but it works nicely since I do this ;)
Perhaps @Christoph-Hart can have a clue... -
@ustk said in Text values for output bus, effects or gain reduction - use label, paintRoutine or animated strip?:
... I simply preview my interface in an additional floating window on the second monitor and hide the main one.
It appears that the preview in the main Hise window causes crashes when drawing permanently different components, eventually via timers...Weird with crashes, but timers kind of makes sens that they crash though, would be great with a indicator output when setting things and functions inside of the timers
Hmm, how does one make a second preview window on another monitor? I´m using mac and I´ve never found that function...
@Lunacy-Audio said in Text values for output bus, effects or gain reduction - use label, paintRoutine or animated strip?:
Strange that it crashes when setting the text. Maybe setting labels in fast timers is bad? I think if you draw the text to the panel it'll be a lot cleaner.
Yes, I think the apple crash report say that
Crashed Thread: 0 JUCE Message Thread
. However, the lineTermination Reason: Namespace SIGNAL, Code 0xb
seems to indicate that the 'signal' namespace is affected and perhaps not able to handle the load or access correct info. There are 3 separate checks for current gain factors and also text set with boolean logic in the last bit. Perhaps not easy around everything else at 30ms a pop :) But then again, all applications that show sound levels use a timer...Perhaps a panel, indeed, but then it´s the paintRoutine. Not that high on that atm, but perhaps later, for now it´ll be another slider with a transparent filmstrip of 1px slides. Done deal. And not crashing nor slow.
-
@andioak Hise -> View -> Add floating window
You can customize it the way you want -
@andioak Hi man and as you suffered from crashes about all this stuff(Value meters with Labels) i suffered also...(i had come to a point that i was pressing save in every additional step i was doing in order not to loose the new step)...the difficult thing was that i had/have to use in my project the Level Meters and the Labels showing the peak values for my hole mixer (16 tracks + a Mastertrack and if Hise let me work till the end of my Drumming Library i will need about 50 more for the internal mic mixers)...i tried many different senarios to avoid crashes (by the way now i don't have crashes or if i have its happening very rarely in a point that i don't know if it is caused anymore by the labels and level meters or from anything else...the whole project became very hudge and rather heavy...anyway coming again to the point)
I tested your project and it behaves exactly as it was happening to me...the only difference between our approach is that you use the labels to show the values until the values go down to -100 and i from the other side use them to show the peak values...so if a hit is louder it pushes the label to a greater value and stays there until it will be kicked by an even louder hit again...as it happens in all DAWS (Cubase, Logic etc)...of course if the user press the mouse on that value(label) it clears the value to -100 ...see example below
I am using a different panel for the level meters and a different panel for the labels and also use the internal timers of these panels and not the Engine timer object (Maybe it is not the case but it worked for me)
Anyway i made for you this modification to your project and i send it so you can test and see if it fixed (maybe not)
The snippet is
HiseSnippet 2507.3oc4Z8taaiiDWJIZ2F2lCsE8.1ORTrevYQZhsaZRKB1stwNYubqUigb1tWOr.oJRz17prnfDc9yUTf9vbOH8K26w8Hz6I3tgT+iRV1QwHo21aSAZhHmY3LC+weCIk55SsvAATeE0kO5BOrh5cz5cgKaXqglDWkCZqndWsN3SwN5XF1+Hb.SY2K7LCBv1JppK9iboTWdIEwOe546Z5X5ZgSaRQ4UThEtCYDgk1Z2l+DwwYeSa7QjQRRuYyCrntsnNzwfGsnVMEOSq2ZN.+RStXKnon9U6YSXT+dLSFNPQcoco1WzaH8L2P4eEIfbhCl+PckdfgBadepiM2i4spzZHwwtabjGnnnp0MMOrXXd3AZ5DaRR6x4CdGnTMjyGpKLK2qtr6Uq7tmpj6sTn6cOsdV9DOVZObe61ZG3BSS8Mgo.Y2JTVkE9m2WqEEjvks9Hy2h22GdHQipaUq1ZnmTq1p6rRkUprwFGMDi5Z5hcPvjBCh+.DCZR.GPB7P.xz0VzHHmK1ekJfjALzol9nTTSnM9dT7PO.yZQG4QcgGp9vbx8vKazMOA6DHOPc66L6QHVfHSWjK1oDtWGg9EosQIz1XpZ2gZ81xL7fXIIGT.fo.63LFCyAAnyvNNqgNkXhb34GzYD1PzYCoNXDAL6.rOx9jH4WOiavE+Xw.7JduS2QxIH3JHvO3SIdXy2hn8E+c.CvTC.ZBj0PNjvYczrFti6T5A73Ix+4Evn71xHJSFkMejzOYgH.vY53JS2IfT8Hi7bvbZw5fdBpTtV60uO1BTQp6j4x1XKyKPF.ggrgZuWqW75iMdwQ6A1o15O6wQR2ZruOL5HQb.qC3xZM1WfS5HDs1NYa0nvV0SZUrXCng8QsLcbNAHb4sj5ILPx8bGPbwqa4iA2TH7gm723QDOH1XC15AXln4XSTs+XWKFg5Vc0UpjaI9rE9cbCJRbIwHJcECODkRhhYjvLhn6p0AXIJiFFWhFB5NtNarQWNPVdnSMEGim3.5lvr5HyyqF1xZQtl7PmHuwDxaDIuQ7.mSCcyym5XXDCZ9IBPZzA2mE54O557mPehzGUUJp+gTT1p7tQn2E9KTF3WpF6D1864+BXrwSUou66k.6oZkDkFjACuYBSTAQpgTjZLsH0PNRMJYjZLsHMzOhhV9rufPsO0GMh5RQzwLD72RD6ijkYRF2OSYJtmllqzmVtRWNWA5Txrk9zwEgYKf1jvyH.27ovNQ.1iaFLBe7Rf2Q7f.GRzvGrO0myqruoErsupoqQ1QRSiqfl4XEjXDJqEzSIz5gYRLYWy4lXhBosnvY1EiW0BxBgaCJmHP3h3youOjXahJELS+vZEUebs3R1argndd7VP6wF2ue1hiEVcLzEi2kSAUDE92kzud19WoR7lKuzhZHX6RR9Q1I3LtVB0+rKzs1kTHLDFjszRwiS99VaBuZ0choo3+eVZ.Ia9CEkrWMdocxZbzTlSJxl6DqTDQYJkQQjFRlqH5iDqLAxM1EJ8hr74lI2tJGQT8gL74rGtVjYsoigS+cDsGym3Nnf7d3rVdvhwjfEiqDXo1k.VpMUvhwL.KFS.VLJEXwnPvhQ4.KFECVLJDrTF3hwb.WLlK3hwrfKFWE3hQAvEcXuBShSzuVIUlNNQeF3D8IvI5kBmnWHNQub3D8hwI5yMNQeNvI5yENQeF3jqBJQOCJg6aQUYSqYkVd8I0BKVm9u35WHp6KoL7gPgrJuqxxUdeET9t52uv93Ga1m53.luntCG3YnXU2wiNA6uV3EWjHnh5RYukMsoeKaxWBnU3o3kDj5dfKgcnG1cZ28lRzQ+UTT+5HuBDkItCt+Pzcv0ygXi8UH1JpqHcYocTDtc5sa95OpbEMgw7YB4qrkesQ4sxmddYrhlF.RxqZTFQV0UhTU.UEZdes7v1XybKsGUuFr0oxXkGnMYM0qI6XLoc94CZaxL4W1ZzLNfB7v9LBGfo1FeJwBGd0qKq0FG7VF0SQcgjaCRQsR1wWr.SL92SK2tZUNWNedg7CmLH9xuqnUeqs2d6F0ehBggGIem3oO2HrgyH1rgIl4SdMGh4mYNoEzIM4LFw13NZ0q8jsd1VatUssg0RRwv8KIvVJ.Z2TJ.91l8INifSjR7NXj4.vTHs2003v+7dsN538OrS68LdepcV2ycfBrDmSb4EjXkO9WalMfTta9.5Muo4H4W7vq+HTwI8wuoY.C60i72yf4CfiIPNmiqQ16pLhXa6f6RCHbNGoWAwGsw8MG6DdHE4gfekqGAowcomGl1GQsifCBJcEt.Bs5R8FCniuRieAMJdl7BnRY4IADJx8VRlAo38OkcNnDYuqmoo7If+2LmHmx+ZMwsEcUy4KWdpzzP5C6Nan+2NibJ2TSIuBYkbSfMJXB7JFf29RX4kln9U4npUSu3hgGHPfR6NjmjMGwGOdWKqE+t+lEJ30QXlvQcZy2JW04uox8VQKduNSizE3Fylt+vulOc27maNe7x4HcebsmtUiFOcSfzcwImaJUYTofXGo4oOnmOHZmOHT1TTCPpjGF1CkYxKirOT0qG6BGwBZC7fwNl9Rkiti1lM19o0eVsZOs1jyNRoYkxDYEVZWJ1dfLF76xSn05xiso33EFjYyC2DwlLY8GN32nw1jui46poSsAyvx95u4K9i5fupU98LyeWxtvp4KjYytQdm3k0cumVWByZXw96BE3uvN.uo82nuvfUzBeqfoN6RZ6+W9L+4Dbqju5iDhcgqjkoeJexGu4e38uedariYl72ujEQ+5lE7Qgb.+N6Yco.FMe1ewrg7hk6rcEDlKUZHBOjPew.ouulHk+kjCKfDe43v2ku6jeC3tFzwLh6.cSXGh7MI+RXyh.wuEtU3a5ie0GpKvqZD9bM9ybGpG10V7v+A9Ipy57mUi5rdbmxojGDlRts1uXdJV7ZvEYi+n349T+QneD6h844p5WGeEXdk9q.6PKF3BG4a5F3QCv0ksbt9ZH2WaLaraVwCaJiT7vaeH7jj6VMiargbi.rHiwfmyXIchzlX9WOeOWds2dXHZrOLvBBVdtSNS0crS.Vr5ntrhoM2Pt47biKUNtwun+3297sujEKm6dIETWN1GCqetGTnygBsDybKNGabqYWFwuAeugTWhkLlx.Cq9GL.mA2TX.8BFyDNgpztnMvNXy.r7oT6Pbwl95gmbedxE0KetXVyWeiVn6h3q8Q++w9IW72k6mr7qPA5wemTU8ywXLxzxmdrU38eyW5cKQKPb6J9ZoWVSm+Lpd7kcqowO93Hf.+XKKN53QP9oXcZLG5734PmMmCcdxbnyVygNaOG57zYpCu14KFyniBw+PCc2S7VGTUC2pfXofx+EHNYGrD
and i hope it will work for you!!!...check it and tell me about!
-
What would be the best timer approach ?
- Engine timer for all meters
- One panel timer for all meters
- Or one panel timer per meter
Also I am not sure using a timer is the best approach to detect the peaks, especially for high transient content like percussion. In 30ms you have the time to miss the real transient...
-
@ustk I wonder about the best timer approach also... I feel (it doesn't mean that i for sure know) that using Engine timer for all meters value labels and animations is probably leading to more crashes because i faced that option in my project...
Maybe it should be a good option to use one panel per meter but then if someone have to use a lot of meters and value labels its getting dangerous(i remember i read somewhere in another topic where @Christoph-Hart told that you are ok with about 100 different timers) but this number is not so big as it sounds...(imagine a drumming instrument with every kit element independent on the mixer and 2 meters for each track (left, right)...so for a 17 track mixer you have already 34 timers...if you use value labels that they also need a timer we go to 68...and if you go further to create independent microphone mixers for each drum element (because for example a snare recording and the final sound contains usually these different mic sources: top, bottom, oh, room, the bleed to the other mics, and the compressed signal...so a mic mixer for the snare should use 6 tracks...so 6 timers for meters and 6 more for value labels)...so in this option it is not that difficult to pass the 100 timers...Thats why i thing that maybe the best approach is to divide logicaly all your stuff to different categories and put them in different panels(Maybe about 10-20) and use the timer of every different panel
Anyway from the moment that i followed this option i dont have any crashes or i have very rarely a crash that is probably not happened for sure from timers (it could happen from any other cause...)
My big question in all the subject was...is...and probably will be...Should we somehow stop the timers?...Because i dont feel safe when i thing that the timers are running endless...that "smells" a possible crash to me...but how to stop that kind of timer?...
In different senarios when we need a timer for an animation things are more controllable...we simply stop the timer when the animation finishes (or when we like to stop) and we coding to begin the timer again when for example we press a certain button
But with level meters it is not possible using this way...For a moment i thought that i found the solution to stop timers and fire them again in the "on note" callback when a key is pressed...and it was a logical thought but i had a lot of crashes...
Maybe a logical contition should be:
"if all values of a panel == -100...stop the timer"
else start the timer...
For sure i have to test itThe funny thing is that i have read so many times in different posts about timers but till now i didn't have a clear opinion about stopping or not timers
-
Just throwing out a wild idea, but this might do something. If you don't need precision in your Synth timer (note: this is different from the Engine timers you created), you can call
Synth.deferCallbacks(true);
which will defer the callbacks to the message thread instead of the audio thread. More on that functionality here.You could try updating all of your panels in the
onTimer()
callback with the setting above and see how it runs. Or you could even just try setting that to true first and see if it helps -
@Lunacy-Audio Hi my friend...I have already called
Synth.deferCallbacks(true);
and maybe it is one of the reasons that i dont have any crashes now...although i can not be sure because i had tried many different options...Sorry that i forgot to mention that...Another cure that i probably suspect is the fact that i changed the declaration for the values of the labels...
In my early attempts i declared them asreg
values (and i think that they were more accurate) and at some point i changed them tovar
...
Things are now complicated enough to my project so i am really not sure what was the cure... and because i have a long way to go till the end (i say again "if Hise allow me to go till there") i just continue having in my mind that using different panels with their timers instead of the Engine timer...callingSynth.deferCallbacks(true);
and usingvar
instead ofreg
for my value labels is ok for nowMaybe the easiest test would be to call
Synth.deferCallbacks(false);
and if i will have crashes means that this was maybe the cure -
@Lunacy-Audio said in Text values for output bus, effects or gain reduction - use label, paintRoutine or animated strip?:
Just throwing out a wild idea, but this might do something. If you don't need precision in your Synth timer (note: this is different from the Engine timers you created), you can call
Synth.deferCallbacks(true);
which will defer the callbacks to the message thread instead of the audio thread. More on that functionality here.After some more reading it´s clear that the audio thread should be kept as clean as can be. Instead I would probably use a timer on the audio-thread that simply probes all values into an array, that is then processed on the message thread with another timer that has the "option", NOT a requirement, to process and view information. This is my theory:
- on audio thread: use a timer to probe all channels and store values in 5ms intervalls. But with an option to fail and move to the next channel, in a for-loop style manner, like:
const var allChannels = [channel1, channel2, channel3, channel4]; // fill with your desired channels. const var currentMixerValues = []; // latest value array that we will fill with latest values from mixer gain-probe. // the currentMixerValues array can also house 10 or more previous values (using a multi-dimensional array/sub-arrays), for use with RMS or loudness-values. const var t = Engine.createTimerObject(); t.setTimerCallback(function() { for (i in allChannels) { local level = allChannels[i].getCurrentLevel(1); if (level) { // if level returns true, aka not undefined, store the result in... currentMixerValues[i] = Engine.getDecibelsForGainFactor(level); } } ClipIndicator.setValue(LevelMax > 0); // boolean, 1 is red, slider second state. }); t.startTimer(5); // start audio level probe timer for all of the mixer values.
- Use the Message thread to probe the values from the previous fast timer on the audio thread. This one updates message-thread, a.k.a. "user interface thread", instead, with new values, but only every 50 ms. This way we get smooth values, and we could additionally get Loudness or RMS values as well from previous probe saves, if we save it to a multi-dimensional array, like
currentValues[i][j] = value;
, or anything alike.
const var view = Engine.createTimerObject(); const var previousVal = []; view.setTimerCallback(function() { for (i in allChannels) { local level = allChannels[i].getCurrentLevel(1); // for the Left Channel only here, or mono, if you got that. if (level != undefined) { if (level > -99) { // if level returns true, aka not undefined, store the result in... outputValue = currentMixerValues[i]; } else { outputValue = -100; } previousVal[i] = value; // also, store values in an array for next time a value prove fails: } else { outputValue = previousVal[i]; // for all undefined, use the last probe value. } } // then print it to your mixer-part or output panel... Mixer[i].setValue(outputValue); }); view.startTimer(50); // start message thread timer at 50ms intervals.
@Lunacy-Audio How would I use the different audio / message threads for this? The manual gives us:
But be aware that you can't defer only selected callbacks of a script (there's a "all or none"-policy for deferring)...
-
So I think if you set
deferCallbacks
to true, it will affect the MIDI callbacks only. The Synth timer is different than the Engine timers you create withEngine.createTimerObject()
. You'd use this call insteadSynth.startTimer(double seconds)
and put your logic in the onTimer callback of your MIDI processor. I believe other timers are on the message thread already? I'm not totally sure, but worth experimenting. -
@DimitrisSP Yeah, you should always use
var
for all graphics-related stuff. You only get 32reg
variables per namespace, so that might have been creating issues. -
@Lunacy-Audio said:
So I think if you set
deferCallbacks
to true, it will affect the MIDI callbacks only.Guessing you mean
Synth.deferCallbacks(true);
here, and in that case the manual says that :... the other callbacks ( onNoteOn , onNoteOff , onController , onTimer ) live in the audio-thread.
Which means they live in the audio thread and get deferred to the message thread.
This deferring from one thread to another, it seems to be a per-node/processor basis. As in "ScriptNode". So perhaps only info true for the ScriptNode branch?
So if my main interface.js is running in "normal mode", with no deferring - i could then use another script processor in another part of the instrument structure to be deferring it´s internal callbacks to the message thread, thus saving the audio thread from excessive work ?? @Christoph-Hart
On another note, there must surely be a list of all the values already in hise, running and ready to be read, with all values currently active, right?
Otherwise, perhaps a internal structure that updates a list of values upon a certain percentage of changes would be in order. Like a "ping" whenever a value changes like 0.4db or something. I think that´s already in JUCE, if it´s as good as they say :)
-
@DimitrisSP said in Text values for output bus, effects or gain reduction - use label, paintRoutine or animated strip?:
Maybe a logical contition should be:
"if all values of a panel == -100...stop the timer"
else start the timer...Yes, we want to stop anything at times we don´t need it. But on the other hand it will have to be started again once a sample reaches a certain value. Which cannot be known unless we probe the values in a timer. Catch 22. :/
So at the time we got sound to process (using cpu) we also have to start the timer. And when it´s off ... well no stress, I guess... :)
If we got x amount of channels, we have to always probe x amount of channels. That´s almost all we would need to do at a very fast rate. Except for transforming the values into gainFactors. Which is a calculation. So the calculation could be set to only have to be made if we have a value change. Or if it´s above -100. But that requires all x channels to be probed first.
Anyway I understand your fear of "infinite timers", I definitely share your concern there :/