Weird transposition problem
-
I've been trying to get my RR script to play nicely with a legato script and ran into a weird issue where on a certain RR no notes would play. I've eliminated a ton of stuff and narrowed it down to a simple example that demonstrates the issue. But I'm a bit stumped. Would you mind taking a look?
Just play one note repeatedly and you'll hear that every note hangs until the counter hits 0. What I'm trying to figure out is why does 0 stop the notes hanging, surely it should also hang...
<?xml version="1.0" encoding="UTF-8"?> <Processor Type="SynthChain" ID="AnotherTest" Bypassed="0" Gain="0.17579234" Balance="0" VoiceLimit="128" KillFadeTime="20" IconColour="0" packageName="" views="32.rk1bzA.....C.........LDZg4lakwVL.....PA...." currentView="-1"> <EditorStates BodyShown="0" Visible="1" Solo="0" Folded="1"/> <ChildProcessors> <Processor Type="MidiProcessorChain" ID="Midi Processor" Bypassed="0"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> <Processor Type="ModulatorChain" ID="GainModulation" Bypassed="0" Intensity="1"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> <Processor Type="ModulatorChain" ID="PitchModulation" Bypassed="1" Intensity="0"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> <Processor Type="EffectChain" ID="FX" Bypassed="0"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> <Processor Type="SineSynth" ID="Sine Wave Generator" Bypassed="0" Gain="0.25" Balance="0" VoiceLimit="128" KillFadeTime="20" IconColour="0" OctaveTranspose="0" SemiTones="0" UseFreqRatio="0" CoarseFreqRatio="1" FineFreqRatio="0" SaturationAmount="0"> <EditorStates BodyShown="1" Visible="1" Solo="0" GainModulationShown="1" Folded="0"/> <ChildProcessors> <Processor Type="MidiProcessorChain" ID="Midi Processor" Bypassed="0"> <EditorStates BodyShown="1" Visible="1" Solo="0" Folded="0"/> <ChildProcessors> <Processor Type="ScriptProcessor" ID="playNote" Bypassed="0" Script="reg lastId = -1; reg count;function onNoteOn() { 	count = (count + 1) % 5; 	lastId = Synth.playNote(Message.getNoteNumber() + count, Message.getVelocity()); 	Console.print(count); }function onNoteOff() { 	Message.ignoreEvent(true); 	 	Synth.noteOffByEventId(lastId); } function onController() { 	 } function onTimer() { 	 } function onControl(number, value) { 	 } "> <EditorStates BodyShown="1" Visible="1" Solo="0" contentShown="1" onInitOpen="0" onNoteOnOpen="1" onNoteOffOpen="0" onControllerOpen="0" onTimerOpen="0" onControlOpen="0" Folded="0"/> <ChildProcessors/> <Content/> </Processor> </ChildProcessors> </Processor> <Processor Type="ModulatorChain" ID="GainModulation" Bypassed="0" Intensity="1"> <EditorStates BodyShown="1" Visible="0" Solo="0"/> <ChildProcessors> <Processor Type="SimpleEnvelope" ID="DefaultEnvelope" Bypassed="0" Intensity="1" Attack="5" Release="10" LinearMode="1"> <EditorStates BodyShown="1" Visible="1" Solo="0"/> <ChildProcessors> <Processor Type="ModulatorChain" ID="Attack Time Modulation" Bypassed="0" Intensity="1"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> </ChildProcessors> </Processor> </ChildProcessors> </Processor> <Processor Type="ModulatorChain" ID="PitchModulation" Bypassed="0" Intensity="0"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> <Processor Type="EffectChain" ID="FX" Bypassed="0"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> </ChildProcessors> <RoutingMatrix NumSourceChannels="2" Channel0="0" Send0="-1" Channel1="1" Send1="-1"/> </Processor> </ChildProcessors> <RoutingMatrix NumSourceChannels="2" Channel0="0" Send0="-1" Channel1="1" Send1="-1"/> <macro_controls> <macro name="Macro 1" value="0" midi_cc="-1"/> <macro name="Macro 2" value="0" midi_cc="-1"/> <macro name="Macro 3" value="0" midi_cc="-1"/> <macro name="Macro 4" value="0" midi_cc="-1"/> <macro name="Macro 5" value="0" midi_cc="-1"/> <macro name="Macro 6" value="0" midi_cc="-1"/> <macro name="Macro 7" value="0" midi_cc="-1"/> <macro name="Macro 8" value="0" midi_cc="-1"/> </macro_controls> <MidiAutomation/> </Processor>
-
If the counter is 0, you play the same note twice and the second note kills the first note (that is the default behaviour of HISE also for sine waves) so it won't hang.
In all other cases, the real event is not matched with a note off (because you only ignore the real note off in the example).
You can check if changing the retrigger behaviour in your samplers fixes this.
-
Aha that makes sense, thank you!
-
Ok I've played around with this some more and it seems that my simple example had simplified too much and made me think it was something completely unrelated. Now I've got a better example and something very strange seems to be happening. I'm getting different results depending on if I paste the code in from Sublime text or if I edit it directly in HISE.
I have a very basic legato script that fades out old notes when new ones are played, when the first note of the phrase is played (a non-legato note) it adds a transposition amount to the note number. I have an array containing the numbers -4 to +4 and a counter than increments and selects the index in the array and applies that as a transposition to the note number in the
Synth.playNote()
function. For some reason when playing the same transition repeatedly the notes will cut out when the index is a certain number. I did most of my tests with D4 - F4 and the notes cut out everytime the transposition is -4.I think the problem has something to do with the fade out command I'm using - even though that's not triggered at the point the samples cut out. The weird thing is though I can get the problem to go away by using a
Synth.noteOffByEventId()
call instead of the fade out if I copy and paste it from Sublime Text, but if I try and make the same edit in HISE the problem persists.I've made a video to demonstrate and here's the preset I'm working with.
https://youtu.be/nd3eiAXByWA<?xml version="1.0" encoding="UTF-8"?> <Processor Type="SynthChain" ID="legatoRRTest" Bypassed="0" Gain="0.40738025" Balance="0" VoiceLimit="128" KillFadeTime="20" IconColour="0" packageName="" views="32.rk1bzA..........PF....vLsoNjA...A....PA...." currentView="-1"> <EditorStates BodyShown="0" Visible="1" Solo="0" Folded="0"/> <ChildProcessors> <Processor Type="MidiProcessorChain" ID="Midi Processor" Bypassed="0"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> <Processor Type="ModulatorChain" ID="GainModulation" Bypassed="0" Intensity="1"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> <Processor Type="ModulatorChain" ID="PitchModulation" Bypassed="1" Intensity="0"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> <Processor Type="EffectChain" ID="FX" Bypassed="0"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> <Processor Type="SineSynth" ID="Sine Wave Generator" Bypassed="0" Gain="0.25" Balance="0" VoiceLimit="128" KillFadeTime="20" IconColour="0" OctaveTranspose="0" SemiTones="0" UseFreqRatio="0" CoarseFreqRatio="1" FineFreqRatio="0" SaturationAmount="0"> <EditorStates BodyShown="1" Visible="1" Solo="0" GainModulationShown="1"/> <ChildProcessors> <Processor Type="MidiProcessorChain" ID="Midi Processor" Bypassed="0"> <EditorStates BodyShown="1" Visible="1" Solo="0" Folded="0"/> <ChildProcessors> <Processor Type="ScriptProcessor" ID="Script Processor" Bypassed="0" Script="reg count; reg transpositions = [-4, -3, -2, -1, 0, 1, 2, 3, 4]; reg lastNote = -1; reg lastId = -1; function onNoteOn() {	 	count = (count + 1) % 9; 	Console.print(transpositions[count]); 	Message.ignoreEvent(true); 	if (lastNote != -1) 	{ 		Synth.addVolumeFade(lastId, 50, -100); //Fade out old note 		//Synth.noteOffByEventId(lastId); 		lastId = Synth.playNote(Message.getNoteNumber(), Message.getVelocity()); //Play new legato note 	} 	else 	{ 		lastId = Synth.playNote(Message.getNoteNumber() + transpositions[count], Message.getVelocity()); //Play new first note 	} 	lastNote = Message.getNoteNumber(); } function onNoteOff() { 	Message.ignoreEvent(true); 	if (Message.getNoteNumber() == lastNote) 	{ 		Synth.noteOffByEventId(lastId); 		lastNote = -1; 		lastId = -1; 	} } reg count; reg transpositions = [-4, -3, -2, -1, 0, 1, 2, 3, 4]; reg lastNote = -1; reg lastId = -1; function onNoteOn() {	 	count = (count + 1) % 9; 	Console.print(transpositions[count]); 	Message.ignoreEvent(true); 	if (lastNote != -1) 	{ 		Synth.addVolumeFade(lastId, 50, -100); //Fade out old note 		//Synth.noteOffByEventId(lastId); 		lastId = Synth.playNote(Message.getNoteNumber(), Message.getVelocity()); //Play new legato note 	} 	else 	{ 		lastId = Synth.playNote(Message.getNoteNumber() + transpositions[count], Message.getVelocity()); //Play new first note 	} 	lastNote = Message.getNoteNumber(); } function onNoteOff() { 	Message.ignoreEvent(true); 	if (Message.getNoteNumber() == lastNote) 	{ 		Synth.noteOffByEventId(lastId); 		lastNote = -1; 		lastId = -1; 	} } function onTimer() { } function onController() { } function onControl(number, value) { 	 } "> <EditorStates BodyShown="1" Visible="1" Solo="0" contentShown="1" onInitOpen="0" onNoteOnOpen="1" onNoteOffOpen="0" onControllerOpen="0" onTimerOpen="0" onControlOpen="0"/> <ChildProcessors/> <Content/> </Processor> </ChildProcessors> </Processor> <Processor Type="ModulatorChain" ID="GainModulation" Bypassed="0" Intensity="1"> <EditorStates BodyShown="1" Visible="0" Solo="0"/> <ChildProcessors> <Processor Type="SimpleEnvelope" ID="DefaultEnvelope" Bypassed="0" Intensity="1" Attack="5" Release="10" LinearMode="1"> <EditorStates BodyShown="1" Visible="1" Solo="0"/> <ChildProcessors> <Processor Type="ModulatorChain" ID="Attack Time Modulation" Bypassed="0" Intensity="1"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> </ChildProcessors> </Processor> </ChildProcessors> </Processor> <Processor Type="ModulatorChain" ID="PitchModulation" Bypassed="0" Intensity="0"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> <Processor Type="EffectChain" ID="FX" Bypassed="0"> <EditorStates BodyShown="1" Visible="0" Solo="0" Folded="1"/> <ChildProcessors/> </Processor> </ChildProcessors> <RoutingMatrix NumSourceChannels="2" Channel0="0" Send0="-1" Channel1="1" Send1="-1"/> </Processor> </ChildProcessors> <RoutingMatrix NumSourceChannels="2" Channel0="0" Send0="-1" Channel1="1" Send1="-1"/> <macro_controls> <macro name="Macro 1" value="0" midi_cc="-1"/> <macro name="Macro 2" value="0" midi_cc="-1"/> <macro name="Macro 3" value="0" midi_cc="-1"/> <macro name="Macro 4" value="0" midi_cc="-1"/> <macro name="Macro 5" value="0" midi_cc="-1"/> <macro name="Macro 6" value="0" midi_cc="-1"/> <macro name="Macro 7" value="0" midi_cc="-1"/> <macro name="Macro 8" value="0" midi_cc="-1"/> </macro_controls> <MidiAutomation/> </Processor>
-
Alright, I took a look and there were 2 issues:
-
Somehow you ended up with a duplicated content of your script in the XML you posted. Check the
onController
callback, it contains the whole script again. Don't know what could have caused this (perhaps you pressed Ctrl+V instead of "Paste script from clipboard"), but you can copy it in Sublime, sanitize it and paste it back, it should work properly. -
After sorting this out I can reproduce the behaviour. It seems when you kill a note because of retriggering (that's what's happening when the counter is -4), the volume fade for the voice is not reset and continues to fade out the next note which is totally bogus.
I just checked in a fix for this. We are really in edge case territory now, but nice catch!
-
-
Ah that's odd. Thanks for taking the time to check it out and find the solution, I thought I was going crazy again :D
-
I figured out why my code had been pasted in the
on Controller
callback. I'd put the timer callback before the controller callback in my script - I think I've done this in a lot of scripts because I setup a template for it... time for some revision :)