Add Loop On/Off toggle input parameter to the file_player & stretch player ScriptNodes
-
As title says; please add Loop On/Off toggle input parameter to the file_player node in ScriptNode. It should work the same as the loop on/off toggle in the bigger sampler units.
Try as I might, I cannot find a way to disable looping in the file_player, and looking at the source code, this input parameter doesn't exist for the file_play where it does for the other sample playback tools.
Same for the stretch player node.
-
I would suggest another option - I never thought of the stretch and file player as more than proof of concept nodes for general file playback and most people will want to branch out into different functions anyways so I think the most efficient way is to just give you a starting point that you can customize yourself.
I've added a snippet to the browser (SNEX Oneshot Player) that will implement a minimal version of the file-player without looping, repitching which should be a good starting point for all kinds of drum samplers.
I've also tagged this topic for the discussion about this snippet (as the SNEX code is rather complex and uses many SNEX classes that provide high-level functionality).
-
That sounds perfect! I will take a look as soon as I can. Probably tonight or tomorrow morning. Thanks Christoph!
-
@Christoph-Hart Is that merged into develop already, or should I wait?
-
@Orvillain there‘s nothing to merge - the entire node is written from scratch in SNEX.
-
@Christoph-Hart Ahhhhhh, sorry I misunderstood!
Yes, I like this. I will dig in and start to figure out how it works. Thank you again.
-
So I thought I'd try plumbing in some kind of pitch capabilities by adjusting the playback speed. I figured a good simple test for this would be to multiply delta by 2 - doubling the playback speed. I just wanted to sanity check that it would be possible before building out logic to do it using midi notes and/or knob parameter values.
So I tried this:
void setExternalData(const ExternalData& ed, int index) { data = ed; ed.referBlockTo(sample[0], 0); ed.referBlockTo(sample[1], 1); if(data.numSamples > 0 && sr != 0.0) { const auto delta = (data.sampleRate / sr) * 2; for(auto& v: voiceData) { v.delta = delta; } } }
And it didn't work, even though I did compile the code again.
Then when I closed the editor and re-opened it (same HISE session) my edit to the code had disappeared. So.... is there some known bug or workflow thing with editing SNEX code and recompiling? Because that seemed a bit funky.
-
@Orvillain yes that was a glitch that I fixed yesterday - the entire snippet browser is still fresh out the oven and it didn‘t save changes to embedded code files.
If you rebuild it the latest HISE version it should work. And your assumption with the delta is correct, so for an octave pitch you multiply by 2.
-
Groovy! Will update! I meant to the other morning, but forgot.
-
I added an updateDelta method:
void updateDelta() { double semitones = (pitch - 0.5) * 48.0; double pitch_mult = Math.pow(2.0, semitones / 12.0); if (data.numSamples > 0 && sr != 0.0) { const auto newDelta = (data.sampleRate / sr) * pitch_mult; for (auto& v : voiceData) { v.delta = newDelta; } } }
And then in the setParameter method I did this:
template <int P> void setParameter(double v) { if (P == 0) { pitch = v; updateDelta(); } }
And then in setExternalData I did this:
void setExternalData(const ExternalData& ed, int index) { data = ed; ed.referBlockTo(sample[0], 0); ed.referBlockTo(sample[1], 1); if(data.numSamples > 0 && sr != 0.0) { const auto delta = (data.sampleRate / sr) * pitch; for(auto& v: voiceData) { v.delta = delta; updateDelta(); } } }
And I'm getting what I was after I think. I can set my pitch parameter, trigger a voice, and the voice will initialise with the pitch adjustment I've set. Then throughout the length of the sample playing back, I can continue to adjust the pitch and it will change the rate of playback.
The actual parameter in the SNEX module snaps to values of 0.021 in order to simulate snapping to semitones. The updateDelta() function will take the knob values and snap the pitch multiplier to the correct values so that I get -24 and +24 semitone pitch shifting.
I guess now if I wanted a fine-tune pitch knob, I could do the same thing, but change the ranges and multiply the delta by that as well.
-
@Orvillain yes I would more or less do the same, although you need updateDelta in the prepare callback to so that it correctly picks up sample rate changes.
Also I wouldn‘t scale the pitch paramater to -1…1 but use the real semitone values as parameter range, this makes using the node later a bit more clear.
-
So I've been digging into this some more. I managed to add looping and pitch shifting to the example one shot player, which was pretty cool to be honest!
But now I'm at the point where I want to be able to have a callback function that would update the sample data. However when testing out whether loading a new sample into a slot was possible, I've had a few issues.
Number 1 - On Windows, it just crashes outright.
Number 2 - On MacOS, it doesn't crash. But it also won't reliably update the audio data.Here's a snippet:
HiseSnippet 3479.3oc4ZstbaabEFTRP1RIoMdRZ5kY5zs5GZHajgHnnnHitjXqK0ZpkrporalISFNq.VJhQf.nXAojZFO8cnOG8An+rOB8QouAsmyt3xBPRcg1NIcJ0ODwd4bN6Y+Nmy2tfrqhXd1L6NbOmf.VjlVoYOIz2hw49gZkV3zqCXZk9P81W6E0a2dTGOsC2Sqzmo2938+ZRaZ+.WVH4DmHqdLasmdc.kyguTpzr+dbrkVXNMwm+8W9TpK0yhk0jl1q8crXO2ouSTVqm7U+AGW2Cn1rSc5qL55e0gV9d6565O.rqY0qpEPstfdN6XJNrYz0JM+91NQ9gsinQLtVo4dpu80s64eomb7u1g6blKCevTqMHHYyG36ZiVL1p1t8bbsSW+bvanq3MlU5M9T8ibrcRaOyq7whNHYyP0eTZl7l2r4LOSUyqph4MFSpjhIMmzjdjdaqPmfnrdP64CzOzKhE1kBtcUSQNVsY9aynuqOLBuHi9zKXGDBOjNixqUs5JDypUqr4hKBtddDYHMjDjnAx1DAnv3bVzSFX63KACoVP4kjpwy2lIGIi6vYglKABLSdccbYbW+HPbohNUjG.cVtnAPi50gBCeoUeEHN9p1grKWcOeqA8gEBe0mAJgzVhl4q9DXMi+KQZ7U4ByryPGeWGuNt99ALaiKoCWZyhJ4roWImExnWbFiF0od8KFuzsdqWBbGuLouXhizv0mZKbbxEA38R6ZPfM.9JWYytC7rhb78H9dG6GwdgW4JK9cKtvhuYQRwt51cr8g.mPeWH5erciAug2zDK6Mn+YrvU.Gh6.V5.ADc9vD8IGlnFEaIwwJCz26POmnWDv7lTrsVL3GCohsJHIRjHf5mEGPI75+I5PVW+v9ZN1XPdt1L0DK.0zZfOlpU5D82s3SsPp24LvsDBYK0KgJRzx9d13y682gFd0g6g5Nd8.pCVtArvHGzSVZO1PHeqLgwB56w3WD4GHFa+.eOzQLEq9qTW4Wq9PZ37gv79k5SHaf1kN1Q8Rm2+L7qzFM8FjX02dfKMJe1VrDSbG.1JWJNLMlG2I5Z0RPuyRAeWMwGoKpLNdablwXivV26CaLtv0Goue2tLqnLCbN8C952OUoTU+hIpWhADa+B0OYTwjYR7utqLIBtyLINdP+r.PLXZFXXwEIKsh98I60MxAwbJ4fL+O53f7+.wn4nIMuzF+4J3MAjAQHLIYoekJVTzIQ1adv3Q9d9A878brTweujEE5b94rP0FeaWelu+yAM6+2jC5lbQOTe+qhBol+HrBRroU66SS6k9Chb7N+HJfogx65P5w1PtRKFXVddL2zTjwOWMgORa3njhG9Ovm3NMwmKE2oYRmYTuzNlEcoe3EhH13uqU5AxpC7zHxNb0pCOw00+xS7cuNIRTr.AxVH7tzCzqZ.+IGERtwIF3CtN4iIqjmz2efWTxx4YT9oTGWrzb6Abf3n8K7ZCCVTqAgkGCFBjK4.pE3mu9DJxa4Q5H0SX6hEZXktsMYSOaeTrO.6OmPCgivBm5hKWGnVRpNI8qfQ2WPOCOk6rX+n2ZNo4nKOoG2icUGTcZ4LuOFLuPlQVu40epj+G5m1ygSDI+bPBnBxpDJTxi8XdO37Y3gHHAtzqgy6O.N5w4D7J.LHGFQntbeBG7WbB.cfI0OMYCg6btG0kbYOlGA7BDI8VBnK1U8nC3QLancZDwh5QNigGBLfdNUzruXFO6v16SFJRH2m5Am3GMMB+ZXp8MFi2pzbJrdkkAvmffkYPW0B565Bd.fT5qkD2ejNrF6fqwNxkmvgCF4Uwjo0OkdFRL.vvtN1rvSnVHbUSgy.nlzm.C3POa1UI1y9.SAan7tPZ3V3QNdwpF2BNhdU7Swj3AYH.CZZ64vQK5oCfLlxRZ4ZAWPREMm9iMGUOZJHKngzm.HblIniAKpFgtI7b6HVPam+BKNXplo.U+.8.rTBrvi76iaQwUotfcoDzkLcs6itPIOuNdPGsIXCSPIYkzNkFdNbLJLaRVLwCzo8r4gEiGXdCYt.hvP1a93gaI1S+FQV+TLSohOOcacFQxwKQeWpkB4XTZH9HadLAOSdggORupOpIyKcn3vYhvjNxvDHYSpwf89g5HeFI0lhP7RuSgZKb219WP2rJ7I2V8mBiQzJ7wrdqpllMxu2KFPqMZrgYillMpWa80psVbkxmDEAQlZ4zlJRbt6BR7ihEyyY.LIOfT6Ghk075OCNbPhg7I50Z1nkQMPL0qsw50qaZ9CiY8.88XVzq0FQ32aG9B5PwVr.5T5rW6c3pZA8WxbYTNSaDwO0.ocGDNLUdOD07ZMWOm3zu6YiyD+GnmcliIh3mJIOOblsHVAgdK4ZentKERHLRx1txZYFw8duR1NagTTo0ES9J7E0jR4yAM+3cCyqWqH.pVB.p.BoVsVspVe8F0psV8la.6YR29Agr+7.fS30YwjlM2vznViZ0MaVuV8FqWaRaBeBH10hAllspUsUsVJlxmn2xnYK7SCylquV0Zar1nFUi5aXtQSyMVqZi0Ma1PXTyp+GyLl5FUMqsdyVqA185v+2XRP2G.Nsl47EyqKZHADnFRd+QVEL70pZVEW5sZ0rdqMpZF6Ma222OpGvlLQUeFJspsje1XiF0aBKlZ2+vu4wSSwdGGZrf99dHWP6h4pTnEnFVLRA4bgG+hLtH4F1j3n+Wkbz65GdIMD3Ziji6CmZClijBM1fjnLLLafUaBE5IbyFjD8Czycr5QtD4GDgL0uH9aJjuGKU6YxEyU5s0+lihhBKSfAUNW7j0pJM06OI2zHipFqWuwcj0k5YuxQ3JwNztQVien9g7Wi8ZQS4a.q2m56eQep3bwS005+8xo36SsB86XIuSTbw8PQK.t0S7VYWP+H7YxXdII8crc5XYkWTiLwZS6DWaZmX8ochqOsSrwzNwMl1I171mHdIxwAfHZGH.bx9R19kRRAVpzrZr3CBHSvfX0zVfXhjyFOSWwwhE+SZI+5rXlUKb5aidIuLOsYL9I5vg7g1iXjsbfS8e7q2gviBGXEQJLsE+tEIvG7NI5b7K1a+xE5GeIx3.jWcgf2.FxP1l3fe+K9BKWZ+.l8VUWgzk5xY6rox3Ov0mFIlT1DF3wgPVXF19C.GxJYBM2TEuP8.ew85kWiPF3fsxj7NwVHPFFRFPjutXwh9UG0Y2m8jiOd+m2Flcs3ws+Uff8nt31BAemiRsxCndaYes2VcQIuyJ4l9Nw2+RrHjVNgiuLeHC3lpMhaVcFaOhrZh1WW19Y99tD7.7XioqBwtjHUNZghFk6QJhZPPjSelpNT5zl4FQk8YFKU7CTMxlDx3rnxURaLSxBO+XE6aVrnFNaP+fIJD4F.EBBHgCAYIE5liSQe91RiMemgrnAgdvjULAh3quId4fWgI5b1J0Msh.hOL4w3wIVxAgr.ZHq7Ix+2NfYwIA7JE7roy0HYBvXxr.wNZ.2PhCdIDYI66MJZR04lIXf2QYzarLY3WjokJ4VxCMhmaNgJPhjy.XS3.duxxGkFvJBDNz0drfndUTTG1de5UhygClbYSxVJi6wDyr0TriNAZWb4D+hnEAhP7RYQDhvHJDbrLoq8JYPVXgVzGnfI.Vq7WkfyFZHgRJvTmtjxJiYmsEwnFdPYXgcxqPl.vCmIFMUoP64cvjUWk7R76IfPvlphaRhHQHySt49FBTwmMFAJ8cBo0F3Mj3rvLWn0mvmrKwyOZ7RdbwWijzyw9JEugpeBgU3dsiH4A7usxso.s74e9jBQUQRBpSfLjO8MNe62.57ayGQ10FZOcLhYnldPYQjU1A+EQfEsRdkpHxPjjOG3pbgdWlDXWD73zsbAH.YaXMWYLIMT7OnDeIiZ+beqKHbWgHThmiiHQLXZf+4YQfICAV5XbusQj+AHEUbb4fqvI.bYk6Za.0kgYOIOdwXotnDaCATkqrBDtL9jsnMA.03aTjYKb7kGZLHALjyRc3m.iBAfPTOfLpjLPxViDCkMU7UFfJAN0mP7G1EotdNytbYYx9JoxsxDSQHbM2b9g2mapI8de1bGcCIaePcEBNCaWF9iOZ+g.2pxoeaYBScMoNG4OWr8vJaIUCxYdIzGX8ch.5VbbCSRN3wH4fJjeGodywTXWLlN8G3h+p+fCnzyHv+xx0L.pWYxZUhIzRwLpE806.47VdYrp1uUTsuxjKh6wtbuXFERwjUBDTFODM2LKqPhCHsZRwOhZ0uwlhNg3RhF2bhILK5wA.rJstxRyWsIX6xVVzTvhrHbDWXflY1Y5jYC0L5xBepK.1N0ubbBxpe6JjpUt0gYBCyL+tvsuIb6LpruS6DirIbqDPtosiwPQSxgSAku4sWaSs.79d7AgLwMkfo1Nihgy3KfGuAFw.ku4S.IH2ZflgFU22SRXIxfE6mEDpkQrYW7ZbdrkS9MGke4FyARjFDRQDm7KNSqXBaN95aHT5jcRgeo2fQr5FkCDFFdRgjbEJRDe.ggEIKOVG8aVbTQaNIQGeJigiV1F3TqoMxAM+M2vAMupua1QMWtj9VeIzBYn7mQv1KYZTcIByyx2FpYr8Ru5zCdbyk9xcVbwsxt9oc.km8HI4Fm1dop3jStvoXYkbeShdqYBMjdGoxQjGZd3daujvStDQQnqNkpbLpSnAzgVPw401Vqptb0z9u7sXCMO
The SNEX code here is a modified version of your one shot code. You can ignore the function for bitcrushing, as that was just an experiment that never panned out.
The main thing I want to know is.... is the SNEX code locking the sample buffer and never unlocking it?
Here is the Hisescript code I put together to just boilerplate the load sample concept:
Content.makeFrontInterface(300, 100); const var processor = Synth.getAudioSampleProcessor("Scriptnode Synthesiser1"); const var fileslot = processor.getAudioFile(0); const var path_a = "/Users/drew/Documents/Hise Snippets/Assets/AudioFiles/sample_violin_looped.wav"; const var path_b = "/Users/drew/Documents/Hise Snippets/Assets/AudioFiles/breakbeat_44k.wav"; const var path_c = "/Users/drew/Documents/Hise Snippets/Assets/AudioFiles/sample_sin44k.wav"; fileslot.loadFile(path_b); fileslot.update();
What I notice is.... if I select a sample slot by changing the int in the call to getAudioFile ... then the first time round, samples will switch. I can compile with a different path written in the call to loadFile .... and the sample wil update. But every subsequent time, it will not update, unless I switch to a different slot. Which I find a bit unusual.
I was expecting that I could just change the sample loaded in the External Slot, and that the one shot player would just gracefully deal with this.
All in all... I don't know where the error is occuring here.
-
I don't think it is anything to do with the SNEX node. Because if I do this in a brand new project:
Content.makeFrontInterface(600, 600); const var af = Engine.createAndRegisterAudioFile(0); af.loadFile("D:\Samples\Silent Hill\Ambience\Silent Hill 2\Promise\Atmos 12.aif");
It does indeed just crash out.
Whereas if I do this:
Content.makeFrontInterface(600, 600); const var af = Engine.createAndRegisterAudioFile(0); af.loadFile("D:\\Samples\\Silent Hill\\Ambience\\Silent Hill 2\\Promise\\Atmos 12.aif");
Then that doesn't instantly crash out.
Will post more as I explore this.
-
I might be getting a bit closer to understanding this. It seems you need to createAndRegisterAudioFile before loading it ???
The code below does not crash. It allows me to change the chosen_file to a, b, or c... and then compile, and each time it will work properly without a crash. I'm guessing because the Engine.createAndRegisterAudioFile(0) call acts like a reset, and unlocks the object and associated memory address??
Content.makeFrontInterface(600, 600); const var processor = Synth.getAudioSampleProcessor("Scriptnode Synthesiser1"); const var af = Engine.createAndRegisterAudioFile(0); const var filename_a = "D:\\Samples\\Silent Hill\\Ambience\\Silent Hill 2\\Promise\\Atmos 12.aif"; const var filename_b = "D:\\Samples\\Silent Hill\\Ambience\\Silent Hill 2\\Day of Night\\Atmos 05.aif"; const var filename_c = "D:\\Samples\\Silent Hill\\Ambience\\Silent Hill 2\\Day of Night\\SLEEPCYCL2.wav"; const var chosen_file = filename_a af.loadFile(chosen_file); const var proc_af = processor.setFile(chosen_file);
-
@Orvillain Spoke too soon. From a fresh instance on Windows, it crashes. Haven't tried MacOS again.