VSL Velocity Histogram
-
Hello, i created velocity histogram looks like VSL, maybe it will be useful to someone.
This is a not ideal version based on 128 sliders (i know that need uses sliderpack for more effiency). I want update this in the future.
Feedback on the quality of the code, suggestions for improvements, and reviews are most important to me.️
Screenshot:
Snippet:
HiseSnippet 3779.3oc6ctscaabEFFzxzITI1wIMM8bJpZZBUrBMAHAOj3DaKIKa0HYqZ43C00McD4PRDCAvB.JaEW2ld3p9lzWm9hzz66Z0tGfM3AQJGq8t9lnQQNKxYlMlM9mu4vOEzRaEFzRFEEDZja9aseeoQtWM+166G2akdBWei0W0H2YxuoHJVFZlVzx62WDEIaajK2bWUUPtBmzH4qu9hKK7D9sjiJxv31Atsja3tqa7nR25Repqm2Zh1xa4t6Xst5kVuUf+JAdACf7Yt7kM5KZ8PQW40Eplch7FWSD0yH26mW1tS8xsrDssKKqWGdSMKmNxcpVUZUuS8c1oovtZCQCKibm5JsciCB2NVDKiLxcxkCZu+18BdjeZGba2H2c7jp2XYrMzyoEuVfWa0snpTiU5450dqLgJx.tJaMR1lKU1dy7a511cX4ijuylTg4nHFW.ychISu4lH8rFO8JOV5MiTJ2XozISSoWO+1sBc6GOpFU9jOeTqIShzVYbh+0Yye9yattuabo48AEOBTeo4skdWyMJNX9mL+7Ef5WU1xSDJM6oJrqX24K.iYQwl6IBM666AMOnka79IwzMTrq4GatRferzOtjnc6sD9RuhKLqFtvRlkguW7iluvrptTjLt3BOxscbOnkV1MLOmoEzZSHmNmiYmvfcM8jchM2IHrsLbogEF51sWVokdVW6dRUKgKdkjjXh61HOWH7n4KntM2ChGtst+CR5bkf4J7b+Roob29w6aJBCE6WJsoQwhv39AQ2EZuSRy2VUhITDDUf+cMK5z+wKdfVeuCo02ahVeGnUVIs5NJUwrn0n5tFTmc4jJuVxs0vZ6DDZVzEpFp007BJkDdw4N2hlic6pjW7VFB4IyWnf5d99tOXxQyO0OXmhKnpZAXzvcowtcm3s2SomYWhIGHuyzUMbb3ZSWWT79dRnpEtsLL1skvagoaytt9KffzApP7XE5zXVWVY+sgQPUbkJW1ZFs.lTdK4iiWNPcM5H7hjynQsBC77tSOoz6vajzW.SoUKJrgTD5+LtZR+12V3MPdC+UCEceFMDxsjFtUP+A8Upy0ClktDLHRtszWwR6A3uRKJCeMcK2KcYmCqGS5qhIw8T07jChTNi.pMEOTN9zRyvfA9vRqSRUiO7nlW6Z99pK34gACmEmtarrqLC1MoqFex92beY0v7CLU+Ctref5pMryd57yCWxsfEwgYep0rFeIwjEwRpy0uKrtX5RE3TUrXnSyV4rzgsjSxk3lACf1KK1YfeK0b7hcWLMeguGaFIrZMDpvOYgVWEAolYlLWekfPeXhpheSV2.KV.uIFVktTWY7FAvTkkUxQTQmEyZw18DsCdTeAr1wno0sBkvdQaAEVLogiZjZF+MSkzaJaA4RWOYQwRi2+IQfIdn3QlQIAmjocKsirqq+Fh8kgEGgUcK0FZ3pgA8S6n0fkrS57Q86RlIch5TAQkdjabuK60umnX4GWN4K0TVamEWxzdIy6qd2CvKrneeu8gyt7vCdshCGj04vTrzL5fYd2cRSZXXJsqKNqLXkqn9ujLn7h3krCb1linLsGRGl9C1cGnMlB+1ld.UDUBtzOG4wDJAlGPDqAioEW3lhuncOguqZx0XZtZ0rhKTFJ89NvTg5KYBe23Aika1Uew121US5bXBuMlAVkGlBGhPNr4pVCeW6AKkrJwnr1o1K1r1oVVV6T6Hk0plenYciluXy5FMyx5FMORYsp4GZVaU9EbZCcPVdCuDSb6mqDOo8Sm4OM4rcv56iVOWM8ac+1vwIfihugnyvkzWIYsPSOQGyfc9B3xWZ7S6pVSGZ8TqclrX6FAAO7x9sWCNJPxZKXiKEBKBpbSsV1x8KntY2.lqKB2N4DWpccGtWvRpNF2P3nszzHM24Yuzz8gdnDrEi39kAUp3v2U4AvFv1Kp1cTU1dpM8yJAtnIYVZKsg3r.EeXyf2Obyu6.wXMtzO0wEF1T03PIOoe23doaqO0F2SIsnrlts8DCpY1KCGNZlsUZrpzIFIyZKjSWwG1jRhijIweijQ9hSYInCDznUtcy3mgCdPO35qVDeXII8yPPqHd6kHG6AWfjKFTXRoEb6.kp16N8zVKZdAPvT0lVcg8JoRfUB1sefOvdoaoBaB5jddNUCl7nZETGroPAIzLyot3exAt1CCchVoF3KaWVc5rIJGlrs3nt3o340FdlnLAe3PRorWo5mjRVQ34sCX1ezwfRUmTrWUapdm7+loVW3.haFuMqNUkUIkUzVIMoG16pxXS+f3Qiokl+fievs70gVjcXNHGgTzSwjlJddSvksnqLQYF1D0AKgzHChmbPScRDXtiJOSFRvFM9.CLpa9tuq4Lp5BiWX5QZWb5oL3v3zgeNyJ3ocUHQZbgx3Ag9oSl9n4OvWCkg.ekJbC0XzSvEJUCPcOn.N9vyTRmR1O3UrSmTIEpxbr5TKupLVoNl1LpNcj7YDXwziUsjYxhSCanQtSN4m9R9C+SeY7ObnVoq1OVCC7UeH.2nuz+v9HiLvsHLLx8UXVAMMN4yo4L3mSS5x+FtsMx8R4UZXYijD1HWgSk8woY7bGrEmfs4DbENAWkSvNbBtFmfqyI3FbBtIsfe4THoLqnsXEsMqnqvJ5prh1gUz0XEccVQ2fUzrXMaVrlMKVylEqYyh0rYwZ1rXMaVrlMKVylEqYyh0pvh0pvh0pvh0pvh0pvh0pvh0pvh0pvh0pvh0pvh0pxh0pxh0pxh0pxh0pxh0pxh0pxh0pxh0pxh0pxh0bXwZNrXMGVrlCKVygEq4vh0bXwZNrXMGVrlCKVqFKVqFKVqFKVqFKVqFKVqFKVqFKVqFKVqFKVqFKVqNKVqNKVqNKVqNKVqNKVqNKVqNKVqNKVqNKVqNKVqAKVqAKVqAKVqAKVqAKVqAKVqAKVqAKVqAKVqAKVqIKVqIKVqIKVqIKVqIKVqIKVqIKVqIKVqIKVqIQVq.9Y4VlW3V7B2lW3U3EdUdg6vK7Z7BuNuvavKbdTmEOpyhG0Ywi5r3QcV7nNKdTmEOpyhG0Ywi5r3Qc17nNadTmMOpylG0Yyi5r4Qc17ntY9yT3yVeUQrP8aw.9iIeqvf9pGva0OU9bqJ2yskL82ogB4WUF8v3f9IsEeLJLxcpzt8zY+FOnd.cS502J+rd3aMdrQt74T879YuH44QWkT+cLmReJzUk71XIpex8C6wy9M9ytOoOxm0GpWj7vqaja97YO75FYO24peGX9O+i+8+byc15h6Jd7nekXLuzvDK2TI1ahkL1yld1syXOI5YEcfm67gsbxmx7rhO3CKtp+N66k1e8EgfFLlXbHp7dYO+DoWRiif7YkJemho78NGSkO6T46kXJekOlJeURkuWlo78gGSkupoxWAlx2pGSkOmT4adpx2odCTaNdJe0RkuWQKejju5ox2qpkORxWiT46zZ4ij70LU9NiV9Nhx2vmmyj6kWSqezzOz2wY05GM8CMd75Z8il9gNOdCs9QS+PqGeGs9QS+PuGuoV+noen4iu62F0uCnKOux4QR+P2Gukl+nwen8iumV+noen+iuuV+Hoe1n+iefV+noen+iend+CR6eXi9O9QZ9iF+g9O9wZ9iF+g9O9IZ9iF+g9Odas9QS+P+G+T87WZyeQ+GlZ9iF+g9O9YZ9iF+g9OVPyej3uJn+ietl+HweUP+Guil+nwen+iegV+noen+i2UqezzOz+w6o0OZ5G5+nnd+CZ6ef9OVTyez3Oz+w6q0OZ5G5+3bZ8il9g9OVRqejzupn+iOPqezzOz+QIs9QS+P+GmWqezzOz+QYs9QS+P+GVZ8il9g9Or05GM8C8eTQ6eij+spn+ipZ9iF+g9Obz5GM8C8eTSqejzOGz+Qcs9QS+P+GMz6ePZ+CGz+QSM+Qi+P+Genl+nwen+iORyez3Oz+wEz5GM8C8e7w54uzl+h9O9DM+Qi+P+GWTyez3Oz+wkz7GI9qF5+3xZ9iD+UC8errl+nwen+iUz5GM8C8erpV+noen+iqn0OZ5G5+XMs9QS+P+GWUqezzOz+w0z5GM8C8ertV+noen+ieo97yjN+bcz+wmp4OR7Wcz+wFZ8il9g9O1TqezzOz+w005GM8C8ebC89Gz1+.8erkl+nwen+iekl+nwen+iap4OZ7G5+Xas9QS+P+G2RO+kz72Fn+iOSyej3uFn+iaq4OZ7G5+3NZ9iF+g9Otql+nwen+i6o4OZ7G5+3Wq0OZ5G5+3954uzl+h9O9MZ9iF+g9Odfl+nwen+ieql+HweMQ+GetV+noen+iemV+noen+CgV+noen+icz6ePZ+iln+iVZ9iF+g9OZq0OZ5G5+Pp0OZ5G5+niV+noen+itZ8il9g9O5o2+8Ht+K9m28xnADWM.dDAvLADcf7EZAjn.hVPdnV.IJfnGDOs.RT.QSH6p2Dg3lHnKDeMARj.QaHAZBjHAh9P5qIPhDHZD42qEPhBH5DITOEl1T3r+TnGoIPZDX1eKzi0DHQBDchLPSfDIPzIxdZBjHAhNQdjl.IRfnSjGqEPhBH5DYe8TXhSgQmHeol.IRfnSjmnIPhDH5D4OnIPZDX1eTzepV.IJfnSj+nV.IJfnSj+jV.IJfnSjuhr.9ZGyEPzIxelr.dli4BH5D4uPV.O8wbADch7WIKfu5wbADch72HKfux29NH8VgAsjQQAgF4N4sRDvyleyf1C7DwAgqzS35ar9ppBuJ7RrB2.eik2uuHJRBpat4V2ONIoTIzISSmu9h4NwUZ6BWisiEwxHibysbP682FTE3tbt7VF2NKgmKeYisC7BRekwJ8b8ZOLqTe32Omo3qmeK23V8lcNdhYjiFFuPxw4RywSm+Jc5HaEOJAOY90t6D51++59aFLH10u6lh3PWEuc8A6tcvfvVRn288kdQJt3Dp4KouubFm.zT6j27eguvJsTuOGVoUVkF6J.98yaE3GCXLzo4d4jRfbxWrqZ1R9MUu2zxXOE5NtNuK.3edqVSdolJPapAVgZfUoFnC0.qQMv5TCrw2bfp0et7f3fcSm1XXr4VWYUQr.Xzqjr.UBsZ7+.J9soE
Code:
// Init. namespace VelHisto { // Declare histogam const var pnlVelocityHistogram = Content.addPanel("pnlVelocityHistogram", 0, 0); pnlVelocityHistogram.set("width", 128 + 10); // +5 from left border, +5 from right border. pnlVelocityHistogram.set("height", 30); // Declare sliders var velo = []; // Initialize empty array. var startposX = 5; // Start positionX (5px). var startposY = 5; // Start positionY (5px). var W = 1; // Width (1px). var H = 20; // Height (1px). for (i = 0; i < 128; i++) // Declare 128 sliders. { velo[i] = Content.addKnob("velo" + i, startposX + i, startposY); velo[i].set("width", W); velo[i].set("height", H); velo[i].set("style", "Vertical"); velo[i].set("min", 0); velo[i].set("max", 18); velo[i].set("stepSize", 0.001); velo[i].set("showTextBox", false); velo[i].set("scrollWheel", false); velo[i].set("enableMidiLearn", false); velo[i].set("sendValueOnDrag", false); velo[i].set("showValuePopup", "No"); velo[i].set("mouseSensitivity", 10000); velo[i].set("visible", false); velo[i].setValue(0); } for (i = 0; i < 5; i++) // Make left border rounded. { velo[i].set("max", 8 + i * (i / 0.5)); } for (i = 123; i < 128; i++) // Make right border rounded. { velo[i].set("max", 18 - 8 - (i - 128)); } } // Paint panel. namespace PanelPainting { // Start painting. VelHisto.pnlVelocityHistogram.setPaintRoutine(function(g) { // Declare assistant variables. var CornersSize = 5; var a = this.getLocalBounds(5); var Shadowpath = Content.createPath(); Shadowpath.addRoundedRectangle(a, CornersSize); // Draw shadows. g.beginLayer(false); g.drawDropShadowFromPath(Shadowpath, a, Colours.withAlpha(0x000000, 0.25), 2, [0, 0]); g.applyMask(Shadowpath, a, true); g.endLayer(); // Draw gb. g.setColour(Colours.withAlpha(0xCECECE, 0.20)); g.fillRoundedRectangle(a, CornersSize); // Draw velocity numbers and lines. 0. g.setColour(Colours.withAlpha(0x000000, 0.25)); g.setFont("Rajdhani", 8); g.drawText("0", [5, 17, 7, 8]); // 24. g.setColour(Colours.withAlpha(0x000000, 0.25)); g.setFont("Rajdhani", 8); g.drawText("24", [5 + 25, 17, 10, 8]); g.fillRoundedRectangle([5 + 25, 7, 1, 16], 0.5); // 56. g.setColour(Colours.withAlpha(0x000000, 0.25)); g.setFont("Rajdhani", 8); g.drawText("56", [5 + 56, 17, 10, 8]); g.fillRoundedRectangle([5 + 56, 7, 1, 16], 0.5); // 89. g.setColour(Colours.withAlpha(0x000000, 0.25)); g.setFont("Rajdhani", 8); g.drawText("89", [5 + 89, 17, 10, 8]); g.fillRoundedRectangle([5 + 89, 7, 1, 16], 0.5); // 109. g.setColour(Colours.withAlpha(0x000000, 0.25)); g.setFont("Rajdhani", 8); g.drawText("109", [5 + 109, 17, 12, 8]); g.fillRoundedRectangle([5 + 109, 7, 1, 16], 0.5); }); } namespace veloIndicatorLaf { // Create laf object. const var VeloLaf = Content.createLocalLookAndFeel(); VeloLaf.registerFunction("drawLinearSlider", function(g, obj) { // Draw gb. g.setColour(Colours.withAlpha(0x000000, 0.50)); g.fillRoundedRectangle([obj.area[0], (obj.area[3] / 2) - (obj.value / 2) - 0.5, obj.area[2], 1 + obj.value], VelHisto.W / 1.5); }); for (i = 0; i < VelHisto.velo.length; i++) { velo[i].setLocalLookAndFeel(VeloLaf); } } namespace FadeTimer { // Declare timer. const var FadeTime = Engine.createTimerObject(); // Declare fade velocity indicator function. inline function FadeIndicator() { for (v in velo) { if (v.getValue() <= 0) { v.fadeComponent(false, 250); v.setValue(0); } else if (v.getValue() > 0) { v.setValue(v.getValue() - 0.020 * (v.getValue() + 1)); } } } // Start timer. FadeTimer.FadeTime.setTimerCallback(function() { // Call fade fade indicator function. FadeIndicator(); }); FadeTimer.FadeTime.startTimer(20); } // Get note velocity. inline function getNoteVelocity() { local i = Message.getVelocity() - 1; velo[i].fadeComponent(true, 1); if (velo[i].getValue() >= 0 && velo[i].getValue() < velo[i].get("max")) { velo[i].setValue(velo[i].getValue() + 3); } else { return; } }; function onNoteOn() { // Call get note velocity function. getNoteVelocity(); } function onNoteOff() { } function onController() { } function onTimer() { } function onControl(number, value) { }
-
@It_Used nice idea and from a coding style perspective it looks good, however I would suggest a few optimizations on the concept level as this is unnecessary heavyweight:
- you don't need 128 sliders (as you have noticed yourself already), but you also don't need a sliderpack. If the histogram is read only I would make it a single panel and draw the histogram as an array of Rectangle objects.
- the most efficient data structure for this kind of stuff is a ring buffer (you can read up on that concept, there's lot's of resources available), because it won't require to shuffle all values when a new value is added, instead it just moves the read / write pointer.
-
@Christoph-Hart I'll take that into consideration. My programming experience is about 1 - 1.5 months, so so far I don't know a lot of things (about the ring buffer), I think I would make it as simple as possible on sliderpack, well, for a beginner, and so that it is less resource-intensive than using 128 sliders. I am currently faced with the problem that the sliderpack does not display all sliders if there are 128 or more of them. as I understand it, in order to customize them, the drawLinearSlider function is needed, I use it, but I draw either 1 slider at the very end, or 1 slider in the middle, or 4 sliders with some kind of random padding. Maybe there are some pseudo-states that are not specified in the documentation...
Thanks for advice!️
-
@It_Used I don't want to discourage you from learning stuff from the ground up, that's always a good mindset and vibe coding is the death of software development. That being said, this is a prime use case to hammer into a LLM to get something that almost works. With a little bit of correction, I got this:
HiseSnippet 1216.3ocsV8+ShbDEeV08xIsWSuj9GvjKoIKmBBHhZnWJmndRZ0iHW8ZiwXF1cV1ItLCY1APZiI8m5e.8un92R+Kn+Gz9lcVXWPimGIkDH799m48dy6MskBWZTjPhrx8gICnHquztyDtJnY.gwQsNDY8U1mRhTTI1v5fICHQQTOjk0puSyvZ80Pwe9mu+.RHg6RSYgPWHXtzej0moR41twOvBCOl3Q+.qeFsq1nkqf2TDJFB3YU6RnAD2aH8nmQzpshM5DRT.x5016TYa2pd65UoxN6WykTituOw2u71dUqVau8Ku89jp6UiVZGj0yNxioDxNJhhFAN8.g2jNAhwbS.tfEw5FR0DkQcfHaXiZFvB8ZOM4DgPVq0NMUspIU8M1mx7Xy3mlx95XA3TKxlzrV4wfT4OCHYkARqYfzKs63JYCToRz34KrawgJnOApMYghQWzJ+hscSAnAWUrO4F5wRfXlEN0JUZSL7S954haMJ5Q8oxljvvtP4IxQIGRAY4fZWjBOhHwsIbZH9M3oNsGU0TzeffCDNuJVZ4WoMYqsvEJT.eNi2C2cnO3VMcFOYX1g8qTvckqrW8LxFQCEtL0jCLF9F7kWUOmj1COVxTzVbO5s.yRPX7ERrCKl.yveWFuBzarQ9bX3y7tq3fgQAN5ybNFOjwoX+gbWESvwZIWjnryHRHb5y8aOfKtLEGWAwNVy5w5MG.cxPsAtbd72lEe4t69..Rmyhuv2OhplB.fIIDy7VzuEvkguFcgfj5+Eil1IRpZnju3gAb5UwnYZQqMzsqvRwPkFb5x1h3zSRFeBKRI5II8c5MOHGCPLtSP2c7QlmJvIe8LxCxJ+DJqWfZdE5RjwlA5MFu08NEw+zqnOLo4sggNloJQE6FB8rI9oWwHcioVvL4iCfjltpqU3I02XNTo3ZDne1JTlVXSYfkuNFxghPOJzIGR8U3+9O9SLmNVSK0mzEbIbTMY.cYcDbXKWY2hkxieMNHAoo5pK8LPxzzS8EjOAjG.nXlOy3AS55bpqx4xa2DOYyzjbAbkMSs4JyQnPk3TTDLlFtAG6l6xcG3PSgCxtwMIma5QblqeHe8Y8IB9YBE88bGcGx5yc85TXHFr.nX1DZ9jxmIHR5.cLbLHRAoudvv.IUGLncEuXT78MgYAY5QURQXHU9fh0KpjOlgN7g86RkahmMPHVQXv77S6e1SaZuqYxYFEE7Vbl58CnIzGq6e7L++96FPIidg+8SsNjnH50EI7.8FPkJlFNVGRGAKnMKOV29PZzMJwfXcSlWCPVEK8ESWsnS6HFD5maaFkitMcG96ZLIk35Fi0MOyXz92aDD2+Liye0sA59awf8mBuggD07KU0O2HQ.j6maSldaEOB5Nx9bjOiMskdzMsOUH9R61LkavCiwUd.LBUk+OvXx6Sdg8QvnGWUJ.Wy93edYeLxmH7lK48Nk.WAg9A6yF1uCLQ0kBQmCcIfyssVQ2YZnKoo0YfNTtWLw+BeRDVVSakHr7Tgn9DWo3ZWyMN8KfddLG.S73WHtN7TUfFWFEeKLadtO7frqccm2U2yvJKqgaurFVcYMbmk0vZKqg6trFt2m1P86ke6Pknu4ZCBcZ6ihGaYYcDm.cfwcqn+Cjopn3.
That's more or less how I would implement it. And if you don't understand what happens here you can always ask the LLM to explain it to you, at this level it's pretty good.
-
@Christoph-Hart no, no, no, I do everything myself, I never use an AI assistant, now it's a nonsense generator that will waste more of my time and won't give me the desired result. I like to do everything on my own, it's very interesting to me)
-
@Christoph-Hart said in VSL Velocity Histogram:
@It_Used I don't want to discourage you from learning stuff from the ground up, that's always a good mindset and vibe coding is the death of software development. That being said, this is a prime use case to hammer into a LLM to get something that almost works. With a little bit of correction, I got this:
HiseSnippet 1216.3ocsV8+ShbDEeV08xIsWSuj9GvjKoIKmBBHhZnWJmndRZ0iHW8ZiwXF1cV1ItLCY1APZiI8m5e.8un92R+Kn+Gz9lcVXWPimGIkDH799m48dy6MskBWZTjPhrx8gICnHquztyDtJnY.gwQsNDY8U1mRhTTI1v5fICHQQTOjk0puSyvZ80Pwe9mu+.RHg6RSYgPWHXtzej0moR41twOvBCOl3Q+.qeFsq1nkqf2TDJFB3YU6RnAD2aH8nmQzpshM5DRT.x5016TYa2pd65UoxN6WykTituOw2u71dUqVau8Ku89jp6UiVZGj0yNxioDxNJhhFAN8.g2jNAhwbS.tfEw5FR0DkQcfHaXiZFvB8ZOM4DgPVq0NMUspIU8M1mx7Xy3mlx95XA3TKxlzrV4wfT4OCHYkARqYfzKs63JYCToRz34KrawgJnOApMYghQWzJ+hscSAnAWUrO4F5wRfXlEN0JUZSL7S954haMJ5Q8oxljvvtP4IxQIGRAY4fZWjBOhHwsIbZH9M3oNsGU0TzeffCDNuJVZ4WoMYqsvEJT.eNi2C2cnO3VMcFOYX1g8qTvckqrW8LxFQCEtL0jCLF9F7kWUOmj1COVxTzVbO5s.yRPX7ERrCKl.yveWFuBzarQ9bX3y7tq3fgQAN5ybNFOjwoX+gbWESvwZIWjnryHRHb5y8aOfKtLEGWAwNVy5w5MG.cxPsAtbd72lEe4t69..Rmyhuv2OhplB.fIIDy7VzuEvkguFcgfj5+Eil1IRpZnju3gAb5UwnYZQqMzsqvRwPkFb5x1h3zSRFeBKRI5II8c5MOHGCPLtSP2c7QlmJvIe8LxCxJ+DJqWfZdE5RjwlA5MFu08NEw+zqnOLo4sggNloJQE6FB8rI9oWwHcioVvL4iCfjltpqU3I02XNTo3ZDne1JTlVXSYfkuNFxghPOJzIGR8U3+9O9SLmNVSK0mzEbIbTMY.cYcDbXKWY2hkxieMNHAoo5pK8LPxzzS8EjOAjG.nXlOy3AS55bpqx4xa2DOYyzjbAbkMSs4JyQnPk3TTDLlFtAG6l6xcG3PSgCxtwMIma5QblqeHe8Y8IB9YBE88bGcGx5yc85TXHFr.nX1DZ9jxmIHR5.cLbLHRAoudvv.IUGLncEuXT78MgYAY5QURQXHU9fh0KpjOlgN7g86RkahmMPHVQXv77S6e1SaZuqYxYFEE7Vbl58CnIzGq6e7L++96FPIidg+8SsNjnH50EI7.8FPkJlFNVGRGAKnMKOV29PZzMJwfXcSlWCPVEK8ESWsnS6HFD5maaFkitMcG96ZLIk35Fi0MOyXz92aDD2+Liye0sA59awf8mBuggD07KU0O2HQ.j6maSldaEOB5Nx9bjOiMskdzMsOUH9R61LkavCiwUd.LBUk+OvXx6Sdg8QvnGWUJ.Wy93edYeLxmH7lK48Nk.WAg9A6yF1uCLQ0kBQmCcIfyssVQ2YZnKoo0YfNTtWLw+BeRDVVSakHr7Tgn9DWo3ZWyMN8KfddLG.S73WHtN7TUfFWFEeKLadtO7frqccm2U2yvJKqgaurFVcYMbmk0vZKqg6trFt2m1P86ke6Pknu4ZCBcZ6ihGaYYcDm.cfwcqn+Cjopn3.
That's more or less how I would implement it. And if you don't understand what happens here you can always ask the LLM to explain it to you, at this level it's pretty good.
I looked at the snippet. Thanks for this unique experience, I'll probably release an updated version tomorrow.
️
I think, here we have main throuble, if note with const velocity pressed double - we summon a geometry overlap, for example this geometryes colours are blended both itself, but we have non ideal solution - simply delete pervious geometry object instance before new was created. Anyway here i need a deep think, for create ideal (for me) solution... Thanks advance! -
Update: Histogram based on a ring buffer now!
Works same as pervious version, but maybe lightly different in FadeOut anim. (Thanks @Christoph-Hart for advice).
Updated 15:08 26/09/25: Added opacity fade.
Updated 17:02 26/09/25: Some refactor.Screenshot:
Script:
// Init. namespace VelHisto { // Declare histogram panel. const var pnlVelocityHistogram = Content.addPanel("pnlVelocityHistogram", 0, 0); pnlVelocityHistogram.set("width", 128 + 10); pnlVelocityHistogram.set("height", 30); // Ring buffer for contain velocity values. const velocityBuffer = []; const bufferSize = 128; // Max value for each velocity. const maxValues = []; // Init max values buffer. for (i = 0; i < bufferSize; i++) { velocityBuffer[i] = 0; // Make start end bars same background shape. if (i < 5) { maxValues[i] = 8 + i * (i / 2); } else if (i >= 123) { maxValues[i] = 18 - 8 - (i - 128); } else { maxValues[i] = 18; } } // Add velocity into buffer function. inline function addVelocity(velocity) { local index = velocity - 1; index = Math.max(0, Math.min(127, index)); velocityBuffer[index] = Math.min(maxValues[index], velocityBuffer[index] + 5); pnlVelocityHistogram.repaint(); } // Fade function. inline function fadeValues() { local needsRepaint = false; for (i = 0; i < bufferSize; i++) { if (velocityBuffer[i] > 0) { velocityBuffer[i] = Math.max(0, velocityBuffer[i] - 0.010 * (velocityBuffer[i] + 1)); needsRepaint = true; } } if (needsRepaint) { pnlVelocityHistogram.repaint(); } } inline function getVelocityData() { return velocityBuffer; } } namespace PanelPainting { VelHisto.pnlVelocityHistogram.setPaintRoutine(function(g) { // Declare assistant variables. var velocityData = VelHisto.getVelocityData(); var CornersSize = 5; var a = this.getLocalBounds(5); var Shadowpath = Content.createPath(); Shadowpath.addRoundedRectangle(a, CornersSize); // Draw shadows. g.beginLayer(false); g.drawDropShadowFromPath(Shadowpath, a, Colours.withAlpha(0x000000, 0.25), 2, [0, 0]); g.applyMask(Shadowpath, a, true); g.endLayer(); // Draw background. g.setColour(Colours.withAlpha(0xCECECE, 0.20)); g.fillRoundedRectangle(a, CornersSize); // Draw velocity bars. var barWidth = 1; var areaHeight = a[3]; // Full height. for (i = 0; i < velocityData.length; i++) { if (velocityData[i] > 0) { // Calculare bar height. var barHeight = 1 + velocityData[i]; // PosY: mid position - half height (bipolar view). var centerY = this.getLocalBounds(0)[3] / 2; // Do not reduce getted value (0). Because dividing by 2 will not give a ideal middle position value. var y = centerY - (barHeight / 2); g.setColour(Colours.withAlpha(0x000000, Math.range(velocityData[i] / 5, 0.00, 0.50))); // divide by 5 is a fade opacity speed. g.fillRoundedRectangle([a[0] + i, y, barWidth, barHeight], barWidth / 1.5); } } // Draw velocity numbers and lines. g.setColour(Colours.withAlpha(0x000000, 0.25)); g.setFont("Rajdhani", 8); // 0 g.drawText("0", [5, 17, 7, 8]); // 24 g.drawText("24", [5 + 25, 17, 10, 8]); g.fillRoundedRectangle([5 + 25, 7, 1, 16], 0.50); // 56 g.drawText("56", [5 + 56, 17, 10, 8]); g.fillRoundedRectangle([5 + 56, 7, 1, 16], 0.50); // 89 g.drawText("89", [5 + 89, 17, 10, 8]); g.fillRoundedRectangle([5 + 89, 7, 1, 16], 0.50); // 109 g.drawText("109", [5 + 109, 17, 12, 8]); g.fillRoundedRectangle([5 + 109, 7, 1, 16], 0.50); }); } namespace FadeTimer { const var FadeTime = Engine.createTimerObject(); FadeTime.setTimerCallback(function() { VelHisto.fadeValues(); }); FadeTime.startTimer(11); } // Get velocity data. inline function getData() { local velocity = Message.getVelocity(); VelHisto.addVelocity(velocity); } // Callbacks... function onNoteOn() { // Send velocity data. getData(); } function onNoteOff() { } function onController() { } function onTimer() { } function onControl(number, value) { }
Snippet:
HiseSnippet 2071.3ocsY01baabDFPVzSEmlNIsc5G6bi+DXLML.snDcURZrdKVSsr4H531LZzj4HvQhKBDGFfiRh0i+Czes8eP6t2gWIAiXTafkFAb29xyt2d6t24gIBOVZpHwvr86WDyLL+ssFsHRFbT.kGYb1wFl+tVmSSkrDhdnCWDSSSY9FllO56vAL2YaC0y+9udHMjF4wJGxv3CBtG6M7YbY4nC+1+FOL7TpO687YUnd2u8LOQzQhPwb.OOpkiQL06Z5T1aoHYa0x30zz.CyurEyex9NdtT+dNr82G9XO29SXi2cWl69S1e73WR6s6.5.WCyGehOWJRFIoRVpg41GJ7WLJPbajVAefmxGGxvObMFAZVO7ohPezDwQMNJfG5OL2QkZ.RYXoa6QZ21er04bedw3ktuOWMAojipNPyspCuGUCdtUgmSE30.jLq.os0P5KZMxKgGKKmAwyeHazRD4VCR5YM1Z2eeqm+bxYQboc6Hv+mBqELxGXgulmJEs+XaB7.TbLyKjlvHA3vSSnyHwzHVnsZdX4LURtglPhiBAdEdb4hWWP4WSNRDIYQRapu+PjMqmzDgOoKwA9oyAJg1DE1oLo0Stk6KC.hc6Mf7Th68yP.iOMPBb7hbZyMqK3QSIimOYBD3OQjfVhDVRI2jIHvnBmyRqYlYScnlqulb4UGTYZsvFw+mLXJ.g0024z6zhToMF0KnPdU0wL5cePo3JhOWD3ZERPFzxTnlaTnVbfImCHbxWUALv2O8oczB4is2Q8W7ot0bI+JEyESW7hB6WyHoRZhjvh7IioIojTHjAdy65oIh4vfoAzXlcAW7IHb9JR+NEC8wh2vmBCUqYb8jS9RjomS50oDGep3MVXJKSteC5fewFJZ2AjmQveAFeFtvrVouoxqI9aW9Y950q78KCm3QRQQ717HOIWDocW7nPdDqXPBrUIOZ1Jm8NsqCKXTZHvoO6N.QEJALuRrkO64TYfMXCVvVL867HK2d62USQmNMrnubvAR3UExB3uhOQMW20vxSgHfR423F0DVLrwSZkQWceHVD4d7WS.RzXwpY2TDi4mdgVKfMLgBK0MXya1VnUCOvPxU2L8MP5LRM5py051BVc0Z04eFww1w0A2nr5jPFwpKl4OKY9xj4r5D8o0ENmacUkv51zsIKskpnxh7xKmSYxbAcLURWYMMgImmDsjuIOz4SsqTKSUvYHB.HWeVAs75a1qqlgh9KDyAdXV4fxZZmh7m6rSkhhPUUfYZjpFHmBkxSKyAhkEuoho.N+B0uhUdPM1NRjDwRRyJlzu9jnfjP4XTHuAivODy.mZ0eIgLJf5KtMFBnpTH1KgAshLDFrpNKIEqTeAJOl+ELOvzlFxrncqhnNMWk33D5sXY.PPUbBSsGylxidCcAKwRs0qB6Ss8AlNNQDqAvoIhYJnUhmtDkxwVFSsukKCdUXb.0x4NG0Cz0fcu9c5R50kbI90U0DOMNNbAzd60KKQbWPMJgBaZL9yYck06pZfPTiFfVMgyiNA+mBmNcpoxIPSx+O3pKx5i0iqG0Ai72wVkvpUKE6.K+uV0TDLG8xWb0AprryCCI5dkru+DiUiosCYQSkAadFRjoMM+HfringdyUa0.aZEHtjMWXXtPtvkT3p4EaRcCEo+vegLi6ShEobU9nmQBngSxTMwZLOVDhar4ra6zLR7f8YrjeXM6Rc5.dcrIGkm+XAIRHgjZ9ygLV.oRleVeh.k1jCYdz4PeO97afSYfsrtfzibKD4n3aJ+FHIDg6yf5b.r8CYkHWIllg3B.b4vDZLpz2Uu4q04p149B5y2bpplk.Q1rUB.dNoOtoPuEtOr0nixgnLTFZl8I7Tv1vx6DAjPGC0SigRQ12+R4Z1bcI8RGrNIuKYQ2hcIcKictpbT.ft1867KnT4JaLilOaLrQlPg9iwhboadViZY2pk0.36THWt0Stf9S9AzHNb5lAqIOgyRYZeO6NfQGfiKAmuKzBH7yfqVC281sQ16sqhevM1KSHtNKIk049yYB4A9YuqxV6aV+82qQ82eub82euGf9Ql1L8O3kMp+AuLW+Cd4CP+HSal9ccZF.v34H.dMCB89E.AEWMigOA+sVST42hSRVCTkm3OeFHUxIQPIdVVuEJpe23eBzoU0CcmSOFAqnAxsGhUTKayZ4d8JZWpZO9k.stTwympjqkqq1J.e32wJO2NwGKX0tgFNyZzDZuSehgBNf9wYoozorpMrgPXmBn03w0Tpust9kxFSsssaWnRQzaER16PC9in6A5pDncDd35k.6NEnCzIJzkkwjIZfCyQpLG1tWhHLDanogo09oeFFsz4t5pKhTPng410uPqVq+Bspdead5tOqPnHBuNi2EyhV2svYj0xJ712eF5CvaAKaLftXVhjiPv7X1MbOl9Nw1o0wrzqkhXEsyhEQnDLerTM6mkeiY3ICL39Fl+oVMcP.i6.KyDuxxE4unt5IP9O9ekcwm5FBvQ9y42E5pWQ2m25bgOz+hr9MFh2qZ1DfWu10ycFZePA7EUu20+ucMhaJD+hVC4Ruflw3VMfQXs4WCLlc4qeVqSfS44IKA31sN8e7qyMsZnO72Tn0kDNFG714yFA0m8Xf1if3FLp2bKL9T+sSdHBt+U8w+AdxlzE+1LaR27IMlQ8RD+nmduFd8t+F0H.lhTWE9NsNG+l3Zn1+U0OCc4w+QOu5hZEF68PY7EOTF28gxX+GJi68PYb+GJiCteFw+y.d0boXldaigw4COQk7xz7jH7BBTQqF+W.sdb86C
-
@It_Used said in VSL Velocity Histogram:
reg velocityBuffer = []; reg bufferSize = 128; // Max value for each velocity. reg maxValues = [];
Arrays should generally use
const
. And if that bufferSize is fixed it should also be aconst
@It_Used said in VSL Velocity Histogram:
var velocity = Message.getVelocity();
This one can be a
local
-
@d-healey said in VSL Velocity Histogram:
Arrays should generally use const. And if that bufferSize is fixed it should also be a const
it is my typical stupid moment... Sometimes i forget primitives.
@d-healey said in VSL Velocity Histogram:
This one can be a local
Yeah, after publish move it part to init callback, into inline function, to avoid cluttering up the onNote callback. Now i update post!
Thanks a lot for the advice.Edit: Post are updated!