Scheduling and turning off fractional instruments

I’m revising my mincer crossfade code and running into a problem. See the code below. The basic idea is that kphs moves through the wav sample until it passes kloop_end. At that point, kcrossfade is toggled to 1 and event triggers a new instance of the instrument, which plays the loop again starting from the start of the loop.

The first instance of the instrument should keep playing until kphs passes kloop_end + kcrossfade_duration. In other words, this is the crossfade segment that will fade out with an amplitude envelope. This instance will then be turned off with turnoff.

However, this isn’t working the way I expect. While the new instance is triggered and plays from the beginning of the loop, the crossfade segment isn’t playing at all. You can play around with it here. Perhaps I’m not using turnoff correctly? Or my control logic is flawed? Could it have to do with the fact that my project uses fractional instruments (e.g. 1.1, 1.2, 1.3) and therefore event and turnoff are not working how I’m expecting? Speaking of fractional instruments, does Csound treat 1.1 and 1.100000 as different instruments?

I’ve tinkered with this for a couple hours and haven’t figured out a solution. Seems like this approach should be able to work.

Any guidance would be appreciated! Thanks,
Jason

EDIT: It seems like the issue has to do with the event opcode. Does calling event "i", 1.1, 0, -1, 1, kreset_count automatically turn off any other instances with an instrument number of 1.1 and a note duration of -1? That seems to be the behavior I’m getting.

    ; read ktimescale, kloop_end, and kcrossfade_duration from GUI
    ; kloop_end and kcrossfade_duration are expressed in numbers of samples

    kcrossfade init 0
    kndx = 0
    while (kndx < ksmps) do
		aphs[kndx] = kphs/sr
		kphs = kphs + ktimescale 
		kndx += 1 
	od

	if kphs > kloop_end then
		if kcrossfade == 0 then
			event "i", p1, 0, -1, p4, kreset_count
			kcrossfade = 1
		endif
		if kcrossfade == 1 then
			if kphs > kloop_end + kcrossfade_duration then
				turnoff
			endif
		endif
	
	asigl mincer aphs, kamp, kpitch, kfunction, ilock, 2048, 10

I can’t give this the attention is deserves just now as I’m running off to a class, but I think you’re overcomplicating this somewhat. I would just use an ‘r’ envelope that will apply the fade in and out once it reaches the release stage. And instead of calling event with a negative duration I would simply set it to be the length of the loop minus the fade out time. That way you don’t have to worry about turning the instrument off.

Thanks for chiming in, Rory. I need to be able to change the length of the loop on the fly, and the loop should never restart until it hits the right boundary. So the loop length might be 1.5 seconds when the event starts but I might drag the right boundary to make the loop 2.5 seconds. My understanding with your approach is that the loop would be locked into the 1.5 second length because I’ve pre-defined the duration of the event. This is why I’m using negative duration, because I can never know in advance exactly how long a loop will be.

Unless I can define note duration with a k-rate variable that I set equal to kloop_length?

Jason

Yes you can. You can pass k rate vars to event, even though those pfields are i-rate.

Are you thinking something like event "i", 1.1, 0, kloop_length? I was under the impression that in this case the value of kloop_length would be frozen to whatever its value was at the initialization of the note event, so the note duration still wouldn’t dynamically adjust to the kloop_length. I’m wrong, though?

What I was wondering was whether you can define note duration with a k-rate variable within the instrument definition. For example:

instr 1
    kloop_length = chnget:k("loop_length")
    p3 = kloop_length
    ...
endin

But Csound flagged an error saying that p3 can’t be defined with a k-rate variable.

Jason

    event "i", 1.1, 0, kloop_length
    (...)
    instr 1
        iDuration = p3 ; which will be whatever kloop_length was when event was called..
        ...
    endin

Yeah, I think that wouldn’t address my use case. It still locks the event duration into whatever the kloop_length was at the moment the event was called. Instead, I’d need the event’s duration to adjust in real time to the kloop_length that’s being controlled by the GUI.

This is why I’ve tried to make this work with negative duration instead. That frees me up from having to care what the duration of the event is supposed to be.

Right so, back to the origin plan then :laughing:

I never work with negative p3 values, so I can’t say if there are any odd caveats to using them. Does it work if you use really long durations like an hour - 3600?

turnoff will simply kill the instrument. turnoff2 will allow release section to be played. I’m not a big fan of using turnoff or turnoff2. I’ve been burned by them before.

I’ll try to throw a simplified example together for you later. I’m sure there is a straightforward way of doing this :+1 This being have a sample playback with variable loop length and crossfades. I’m sure I wrote something like this before :thinking: Right now it’s time to eat :wink:

Ah! Your suggestion to use a long duration like 3600 might have done the trick! It’s now triggering the new event from the start of the loop and continuing to play the original event until the end of the crossfade and then turning off. Let me tweak some things and see if this is the solution. I’ll report back.

Thanks for your help!
Jason

Ah great. That would save me some time throwing together another example :+1:

Here’s the solution that works. It’s very close to the first code I posted. The main difference is that I’m setting the event duration to 3600 instead of -1. There was something about using negative duration values that wasn’t playing well with turnoff/turnoff2 and the linsegr/expsegr amplitude envelopes.

I’m surprised this works because I thought I read somewhere that turnoff won’t allow the linsegr/expsegr envelopes to play out, but that’s not the case. Overall, compared to the solution I came up with a few days ago, this crossfade is much more elegant from a coding perspective and sounds smoother.

Thanks Rory for your pivotal input on this!

Jason

; read ktimescale, kloop_end, and kcrossfade_duration from GUI
; kloop_end and kcrossfade_duration are expressed in numbers of samples

kcrossfade init 0
kndx = 0
while (kndx < ksmps) do
	aphs[kndx] = kphs/sr
	kphs = kphs + ktimescale 
	kndx += 1 
od

if kphs > kloop_end then
	if kcrossfade == 0 then
		event "i", p1, 0, 3600, p4, kreset_count
		kcrossfade = 1
        turnoff
	endif
endif

irelease = chnget:i(SCrossfadeDurationChannel)
kamp_env linsegr 0.00, 0.01, 1, irelease/44100, 0.00
asigl mincer aphs, kamp*kamp_env, kpitch, kfunction, ilock, 2048, 10
1 Like