[Csnd] 'linsegr' is not going back to zero

Hello Everybody!

I have some problems with the linsegr opcode, which i don’t understand.

I use it as a global gate for a mic-input. It gets triggered by a specific midi note and the instrument in which it is, is only used for this.
My problem is, when the note-off signal is coming to trigger the last segment of the opcode it does not gets down to 0.000. It stops always like on 0.0132 or something and the gate stays always a little bit open.
Maybe someone can help.

All the best,
Philipp

This is the Instrument.

instr 100 ; Mic-Gate
;;; Über massign wird MIDI-Kanal 1 auf Instrument 1 geroutet
;;; Instrument 1 startet bei Eingang eines MIDI-Signals
;;; Wenn MIDI-Note 1 eingeht wird linsegr gestartet
;;; Wenn nicht, dann ist MicGate = 0

iMidinote notnum
if iMidinote == 1 then
  gkMicGate linsegr 0, 0.01, 1, 0.1, 0
  else
  gkMicGate = 0
  printk2 gkMicGate
endif
endin

This is the whole Csound-Code:

<CsoundSynthesizer>
<CsOptions>
-d -odac3 -iadc2 -W -3 -+rtmidi=coremidi -M4
</CsOptions>
<CsInstruments>
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1.0
nchnls_i = 12

;-----------------------------------------------------------
#include "/Users/philippneumann/Documents/Csound/PHN-UDOs.txt"
;-----------------------------------------------------------
giMicIn = 11
gkMasterVol init 0
massign 1, 100
giDelBufSize1 = 5
gkMicGate init 0
;-----------------------------------------------------------
instr 100 ; Mic-Gate
;;; Über massign wird MIDI-Kanal 1 auf Instrument 1 geroutet
;;; Instrument 1 startet bei Eingang eines MIDI-Signals
;;; Wenn MIDI-Note 1 eingeht wird linsegr gestartet
;;; Wenn nicht, dann ist MicGate = 0

iMidinote notnum
if iMidinote == 1 then
  gkMicGate linsegr 0, 0.01, 1, 0.1, 0
  else
  gkMicGate = 0
  printk2 gkMicGate
endif
endin

instr 1
; -----
; Tap-Delay
; MIDI-Kanal 1
; CC-1: Mikrofon und Delay Volume
; CC-2: Feedback Ratio
; CC-3: Delay Time
; CC-4: Delay Time Shift
; -----
;;; Mikrofon Input
aIn inch giMicIn
initc7 1, 1, 0
kInVol ctrl7 1, 1, 0., 1.
kInVol port kInVol, 0.01
aMicGated = aIn*gkMicGate

aMic = aMicGated*kInVol
printks2 "Mic-Volume: %f \n", kInVol
; Wenn auf MIDI-Kanal 1 die MIDI-Note geschickt wird ist das Gate
; für das Mikrofon offen und das Signal geht in das Delay
;aMic = aIn*gkMicGate
;;; Delay
; Delay-Time
initc7 1, 3, 0.
aDelTime1 ctrl7 1, 3, 0.003, 2
; Delay-Time Offset für Links und Rechts
initc7 1, 4, 0.
aDelOffset ctrl7 1, 4, 0., 2.
aDelOffsetL = aDelOffset*1
aDelOffsetR = aDelOffset*1.1
; Feedback-Ratio
initc7 1, 2, 0
kFdb ctrl7 1, 2, 0., 1.
; Delay-Buffer
aDumpC delayr giDelBufSize1
aDel1C deltap3 aDelTime1
    delayw aMic+(aDel1C*kFdb)

aDumpL delayr giDelBufSize1
aDel1L deltap3 aDelTime1*aDelOffsetL
    delayw aMic+(aDel1C*kFdb)

aDumpR delayr giDelBufSize1
aDel1R deltap3 aDelTime1*aDelOffsetR
    delayw aMic+(aDel1C*kFdb)

;aDelL sum aDel1C*kInVol, aDel1L*kInVol
;aDelR sum aDel1C*kInVol, aDel1R*kInVol

aDelL sum aDel1C, aDel1L
aDelR sum aDel1C, aDel1R

outs (aMic+aDelL)*gkMasterVol, (aMic+aDelR)*gkMasterVol
endin

instr 8
; -----
; Master-Kanal
; MIDI-Kanal 8
; CC-1: Master Volume
; CC-2: G
; -----
;;; Master Volume
initc7 8, 1, 0.
kMasterVol ctrl7 8, 1, 0., 1.
gkMasterVol portk kMasterVol, 0.01
printks2 "Master-Volume: %f \n", gkMasterVol
endin
;-----------------------------------------------------------
</CsInstruments>
<CsScore>
i100 0 z
i1 0 z
i8 0 z
</CsScore>
</CsoundSynthesizer>
Csound mailing list
Csound@listserv.heanet.ie
https://listserv.heanet.ie/cgi-bin/wa?A0=CSOUND
Send bugs reports to
        Issues · csound/csound · GitHub
Discussions of bugs and features can be posted here

Hi
I tested the instrument and got the same result as you. There may even be a bug with linsegr as it seems that the value at the very last k-cycle is not being set. As a work around you could use the xtratim opcode to add some additional release time to the instrument.
linsegr already extends the instrument time by the release time given (0.1) so xtratim needs to be above that. 1/kr gives the time of one k-cycle (which with ksmps=64 is 0.0013 seconds), and I had success with the following:

instr 100 ; Mic-Gate
iMidinote notnum
if iMidinote == 1 then
         iReleasetime = 0.1 ; release time to be used by linsegr
         xtratim iReleasetime + (1/kr) ; add very short additional time to the existing release time
  gkMicGate linsegr 0, 0.01, 1, iReleasetime, 0
  else
  gkMicGate = 0
endif
printk2 gkMicGate
endin

Hope that helps
Richard

hi -

i think it is more a problem with the program flow. as i read the code, the if-condition is only activated as long as a certain midi key is pressed. as the linsegr statement is in this if-clause, it will not be finished, if the midi key is pressed too short.

an alternative would be to trigger another instrument which then puts the gkMicGate to zero. like:

if iMidiNote == 1 then
  schedule("PulDown",0,.1)
endif

in the instrument, and as instrument:

instr PullDown
  gkMicGate linseg 1,p3,0
endin

best -
  joachim

Hi
I didn't test with midi or the if condition - really just an instrument with linsegr & printk - and still found an inconsistency with linsegr.. I may try and have a look at the source at some point.

I don't use midi much and think yours is a good suggestion - however with the if running at init-time (ie iMidiNote == 1) and given linsegr is running at k-rate I would think the linsegr would run for the duration of the instrument regardless of what iMidiNote is after init? I know (but don't understand) there are some special/particular way that instruments are handled when used with midi though.

RK

hi richard -

what you would do with my suggestion: to give the job of guiding the gkMicGate to zero (fade out) to the instrument "PullDown". what happens there, is normal linseg. so no linsegr any more (also not in the other instrument).

can you share your test code for linsegr, showing that it does not really reach zero? if so, we should file a bug.

cheers -
  joachim

Here's the minimal reproducible example:

instr 1
  kval linsegr 0, 0.1, 1, 0.1, 0
  printk2 kval
endin
schedule(1, 0, 1)

the last value I see is 0.01449
..and a bit more examination of what's going on:

instr 1
  ktrig init 1
  kval linsegr 0, 0.1, 1, 0.1, 0
  if (kval == 0) then
    printf "it's 0 at d\\n&quot;, ktrig, timeinstk\(\) &nbsp;&nbsp;&nbsp;&nbsp;ktrig \+= 1 &nbsp;&nbsp;endif &nbsp;&nbsp;if \(lastcycle\(\) == 1\) then &nbsp;&nbsp;&nbsp;&nbsp;printf &quot;last value is .20f", 1, kval
  endif
endin
schedule(1, 0, 1)

for this I get
   it's 0 at 1
   last value is 0.01449275362318966538

if the release time in linsegr is 10, the last value is closer to 0:
   last value is 0.00014511681916878143

if xtratim is used like in my previous email, it's closer but doesn't absolutely return to the desired value. Even with 10s added using xtratim:
     last value is 0.00000000000000128023

Substituting linsegr with linseg ie
     kval linseg 1, p3, 0
works OK, kval == 0 condition is met in multiple k-cycles and the end result is
     last value is 0.00000000000000000000

all the best
rk

NB the results shown from the print statements here are when run at sr=44100 , ksmps=64 .
At other rates the results are different, but still don't end with kval as the desired value of 0 at the last cycle.

ah yes, i can confirm it.
you said you can have a look in the code?

Yes, I've had a look and have an alteration that works to ensure the last value is always the iz value, will test further.

Thank you both a lot!

I’m working, for this project at least, with the ‚xtratim‘ solution. Also it’s ksmps depending.

Greetings,
Philipp

There was what we could consider a bug in linsegr (if compared to linseg), in that it never settled exactly on the final value, but
one right before it and so the xtratim solution was not a complete workaround. How close would depend on ksmps and on the
values of the last segment (start, dur, end).

I have committed a fix to make linsegr behave like linseg, which is what some would expect. It is possible that someone
would have exploited the bug since it has always been there as far as I can see (thirty years or more).

There’s an associated issue I discovered, where it appears that any added extra time runs to 1 kcycle fewer than the equivalent
p3 time (with no extra time added). My fix includes extending extra time by 1 kcycle if linsegr is used but this is is likely to affect
other opcodes that depend on extra time. I am not sure where this discrepancy comes from, but I hope to find out. Once that is
resolved, I can remove the extra kcycle added to linsegr.

This fix has been applied to both csound6 branch (6.19 beta) and develop (7.0 beta)

This is great, thank you.
I did wonder, as it's evidently worked like that for a long time, if it should be a new opcode, even if it could objectively be considered a bug..
Good/interesting spot with the additional extra time, sounds quite curious.

i think it is good to have this as a fix. we all used linsegr so much and the small difference simply did not show up in an audible way. good to have it really clean now.

Thanks, Victor, for fixing this. I think that this issue probably was a real bug that could cause clicks in releases at times.

Thanks. Yes, I think on balance we should just accept the change and move on. I’m surprised it has not been detected before.

Prof. Victor Lazzarini
Maynooth University
Ireland

So I got to the bottom of this. What is happening is that the lastcycle opcode is adding one extra kcycle but only in the case of no xtratim,
that is where the discrepancy comes from. I can now adjust the code accordingly.

which turns out to be complicated.

Without lastcycle, linseg also finishes at the last kcycle without reaching the target, just as linsegr did (after my bug fix).
So now the situation is slightly more complex because clearly this is the behaviour of these opcodes, and I am not quite sure what we need to do
now.

linseg will reach the final value and hold it there if the instrument duration is longer than the sum of its segments (by at least 1 kcycle), but will not reach it if the
duration is equal to it. linsegr will never reach its final value unless we have an xtratim that is longer than its release duration by at least 1 kcycle.

So I would like to see what people think it’s best doing. I can modify both opcodes to reach the destination with 1 kcycle short at the end or I can
just modify linsegr.

NB: same thing applies to linen, I just checked and it does not go all the way to 0.

in my understanding, all the -r opcodes (linenr, linsegr) add an extra time segment which is as long as the requires release time. so there is no need for any xtratim statement, and it seems to be wrong to do both.

why is xtratim part of the discussion?

Because linsegr does not get to 0, as is the case of linseg and linen. They only get to 0 if the duration of performance is longer than the sum of segments. For
linsegr that can only be achieved by using xtratim alongside it.