Unfortunately xtratim
won’t do the trick. This is because as soon as loscil
understands that it is in the release stage (which includes when xtratim
or ‘r’-type envelopes are used) it stops looping and simply proceeds to the end of the stored sound file.
If you examine the internal phase pointer output you can see what is happening. In the output below, the key was released at 5 seconds and even though an 8-second xtratim
was added, the pointer just moved to the end of the file (phase=1) in just over a second (and sound ceases).
This behaviour could be regarded as useful if the note release includes a distinctive articulation and might work well with things like trumpets or oboes which don’t simply fade out at the end of the note. It is a little unfortunate that other behavioural options are not offered by
loscil
and could merit a feature request.
The workaround I came up with as shown below:
<Cabbage>
form caption("Untitled") size(400, 300), guiMode("queue"), pluginId("def1")
keyboard bounds(8, 158, 381, 95)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -dm0 -+rtmidi=NULL -M0
</CsOptions>
<CsInstruments>
ksmps = 32
nchnls = 2
0dbfs = 1
gi1 ftgen 1, 0, 0, -1, "Alto-Flute-sus-A#3-PB-loop.wav",0,0,0
gkEnvs[] init 128 ; array that holds envelopes for each note
instr 1
iNum notnum
p1 += iNum * 0.001 ; create a unique fractional p1 for this note
iRto = cpsmidinn(iNum)/cpsoct(8) ; playback speed ratio
iFn = 1 ; function table containing the sample
gkEnvs[int(iNum)] = transegr:k(1, 5, -8, 0) ; write envelope into array at index location corresponding to this note. Release time is 5 seconds.
if timeinstk()==1 then
turnoff2 2 + iNum * 0.001, 4, 1 ; turnoff any existing instances of this note
event "i", 2 + iNum * 0.001, 0, 36000, iNum, iRto, iFn ; trigger held note in order to sustain looping
endif
endin
instr 2
if active:k(1+p4*0.001)==0 then ; if p1 instrument that triggered this p2 instance has completed its release envelope...
turnoff2 p1,4,1 ; force instrument to turnoff (better not to have silent instruments lingering)
endif
a1 loscil 1,p5,p6,1
a1 *= lineto:k(gkEnvs[int(p4)],0.05) ; read envelope for this note
a1 *= linsegr:a(1,0.05,0) ; anticlick (in case the note is stopped by turnoff2 in instr 1)
outall a1
endin
</CsInstruments>
<CsScore>
f 0 z
</CsScore>
</CsoundSynthesizer>
I’ve also attached the csd and the sound file I used here:
LoscilLoopTest.zip (199.2 KB)
We have to trick loscil
into believing that it is being performed by a held note and that is done by triggering it from instr 1 with a very long note. Envelopes and release stages are created in instr 1 and shared via an array. Elsewhere there is a rats’ nest of turnoff
s, active
s and fractional instrument numbers but it seems to work okay and it should be easy enough to build a more elaborate sampler around it with multiple samples, keygroups, velocity groups and so on.
The alternatives are a feature request for loscil
to allow optional looping in release stages as I mentioned or you could also look at flooper
/flooper2
. They will loop sound files with a crossfade at the loop point but they won’t read loop points from the sound file metadata; you have to tell them the loop points.