[Csnd] A remaining weak-spot w/re: Csound

Hi all,

I was recently discussing Csound with colleagues, and singing its praises. However I did have to mention an odd corner of concern: for anyone seriously interested in Karplus-Strong style synthesis that didn’t feel comfortable building that universe from scratch, I have to advise them to stay clear. Of all the out of box opcodes I have played with, only “pluck” stays in tune. But it is limited in its parameters and doesn’t support as many options like defining an excitation signal yourself, etc. To get in-tune plucks that are actually in tune to arbitrary accuracy, I’ve had to do one of the following:

  1. resort to ksmps=1 low level implementations (with varied levels of success)

  2. use a Faust custom-built opcode that csound could use.

  3. use other software altogether.

I know that there is a strong drive to preserve legacy opcode behavior, but honestly, I think in reality that doesn’t always come to pass, as I have had .csd files I try to use years later on a new version suddenly fail due to API changes. So, I wonder aloud if it is at all possible for the hardcore DSP devs to look at the limitations (tuning-wise) of the pluck-related opcodes and fix those pitch quantization issues…? Or maybe add some next-gen pitch accurate ones to the mix? It should be possible, because the Faust code plugin ended up working beautifully. And yes, I can and will continue to use it in the meantime, but I think newbs to Csound should also benefit from out-of-the-box flexible, and in tune, “*pluck” opcodes…

Another possibility is that the Faust code I am using can be used as a basis for a new opcode one of the devs could incorporate. I am happy to share it.

-A

Csound mailing list Csound@listserv.heanet.ie https://listserv.heanet.ie/cgi-bin/wa?A0=CSOUND Send bugs reports to https://github.com/csound/csound/issues Discussions of bugs and features can be posted here

Hi Aaron,

Victor did some nice UDOs in the 2016 Csound book on Springer. They might behave more to your expectations.
I attach an include file with the UDOs.
The String opcode is used like:
aout String iamp, icps, ifeedback, ipluck_pos, ipickup_pos

all best
Øyvind

(Attachment waveguide_udos.inc is missing)

If you are only using one delay line it is possible to implement a pluck waveguide without need to do sample-by-sample processing.

The delayr-w pair can implement feedback without much difficulty. The simple LP filter can be done with delay1, so that can be inserted without any problems. The fine tuning can be done with allpass filter that can be implemented with filter2 or even biquad. Or alternatively you can just used an interpolated tap to give the correct fractional delay.

I think I posted an example here from one of my classes last time this was discussed. I can look for it next time I’m on the computer.

Prof. Victor Lazzarini
Maynooth University
Ireland

and here is an example, attached.

pluck-model-cubic.csd (1.71 KB)

it runs for me, but with zero amp as output.

Csound 7.0 must have a bug:

--Csound version 6.18 (double samples) Nov 24 2022
[commit: a1580f9cdf331c35dceb486f4231871ce0b00266]
libsndfile-1.1.0
graphics suppressed, ascii substituted
sr = 44100.0, kr = 4410.000, ksmps = 10
0dBFS level = 1.0, A4 tuning = 440.0
orch now loaded
audio buffered in 1024 sample-frame blocks
PortAudio V19.7.0-devel, revision 147dd722548358763a8b649b3e4b41dfffbcfbb6
   0: dac0 (BlackHole 2ch [Core Audio, 2 in, 2 out])
   1: dac1 (External Headphones [Core Audio, 0 in, 2 out])
   2: dac2 (MacBook Pro Speakers [Core Audio, 0 in, 2 out])
   3: dac3 (Microsoft Teams Audio [Core Audio, 2 in, 2 out])
   4: dac4 (ZoomAudioDevice [Core Audio, 2 in, 2 out])
   5: dac5 (Multi-Output Device [Core Audio, 0 in, 2 out])
   6: dac6 (Aggregate Device [Core Audio, 2 in, 4 out])
PortAudio: selected output device 'External Headphones'
writing 1024 sample blks of 64-bit floats to dac
SECTION 1:
new alloc for instr 1:
  rtevent: T 0.375 TT 0.375 M: 0.46543
new alloc for instr 1:
  rtevent: T 0.750 TT 0.750 M: 0.68172
new alloc for instr 1:
  rtevent: T 1.125 TT 1.125 M: 0.74759
new alloc for instr 1:
  rtevent: T 1.500 TT 1.500 M: 0.78027
new alloc for instr 1:
  rtevent: T 1.875 TT 1.875 M: 0.82665
new alloc for instr 1:
  rtevent: T 2.250 TT 2.250 M: 0.67594
  rtevent: T 2.625 TT 2.625 M: 0.72071
  rtevent: T 3.000 TT 3.000 M: 0.75303
  rtevent: T 3.375 TT 3.375 M: 0.75758
  rtevent: T 3.750 TT 3.750 M: 0.77514
  rtevent: T 4.125 TT 4.125 M: 0.78055
  rtevent: T 4.500 TT 4.500 M: 0.68964
  rtevent: T 4.875 TT 4.875 M: 0.71552
  rtevent: T 5.250 TT 5.250 M: 0.65596
  rtevent: T 5.625 TT 5.625 M: 0.73197
  rtevent: T 6.000 TT 6.000 M: 0.72657
  rtevent: T 8.375 TT 8.375 M: 0.67049
Score finished in csoundPerform().
inactive allocs returned to freespace
end of score. overall amps: 0.82665
     overall samples out of range: 0
0 errors in performance
Elapsed time at end of performance: real: 8.496s, CPU: 0.166s
closing device
361 1024 sample blks of 64-bit floats written to dac

yes. i see this:
> WARNING: cngoto: perf-time code in global space, ignored

complete output:
UnifiedCSD: /home/jh/Desktop/test.csd
rtaudio: JACK module enabled
WARNING: cngoto: perf-time code in global space, ignored
Elapsed time at end of orchestra compile: real: 0.002s, CPU: 0.002s
sorting score ...
  ... done
Elapsed time at end of score sort: real: 0.003s, CPU: 0.003s
--Csound version 7.0 (double samples) Mar 9 2023
[commit: 2503fc0b439f70973a6ee9fdd810fda6d111882c]
libsndfile-1.0.31
sr = 44100.0, kr = 689.062, ksmps = 64
0dBFS level = 1.0, A4 tuning = 440.0
orch now loaded
audio buffered in 256 sample-frame blocks
reading 1024-byte blks of shorts from adc:system:capture_ (RAW)
system sr: 44100.000000
Jack input ports:
  0: adc0 (adc:system:capture_1)
  1: adc1 (adc:system:capture_2)
  2: adc2 (adc:csound6:output1)
  3: adc3 (adc:csound6:output2)
connecting channel 0 to system:capture_1
connecting channel 1 to system:capture_2
Jack output ports:
  0: dac0 (dac:system:playback_1)
  1: dac1 (dac:system:playback_2)
  2: dac2 (dac:csound6:input1)
  3: dac3 (dac:csound6:input2)
connecting channel 0 to system:playback_1
connecting channel 1 to system:playback_2
writing 512 sample blks of 64-bit floats to dac:system:playback_
SECTION 1:
new alloc for instr 1:
WARNING: rtjack: xrun in real time audio
   rtevent: T 0.374 TT 0.374 M: 0.00000 0.00000
new alloc for instr 1:
   rtevent: T 0.750 TT 0.750 M: 0.00000 0.00000
new alloc for instr 1:
   rtevent: T 1.125 TT 1.125 M: 0.00000 0.00000
new alloc for instr 1:
   rtevent: T 1.501 TT 1.501 M: 0.00000 0.00000
new alloc for instr 1:
   rtevent: T 1.875 TT 1.875 M: 0.00000 0.00000
new alloc for instr 1:
   rtevent: T 2.249 TT 2.249 M: 0.00000 0.00000
   rtevent: T 2.625 TT 2.625 M: 0.00000 0.00000
   rtevent: T 3.000 TT 3.000 M: 0.00000 0.00000
   rtevent: T 3.376 TT 3.376 M: 0.00000 0.00000
   rtevent: T 3.750 TT 3.750 M: 0.00000 0.00000
   rtevent: T 4.124 TT 4.124 M: 0.00000 0.00000
   rtevent: T 4.500 TT 4.500 M: 0.00000 0.00000
   rtevent: T 4.875 TT 4.875 M: 0.00000 0.00000
   rtevent: T 5.251 TT 5.251 M: 0.00000 0.00000
   rtevent: T 5.625 TT 5.625 M: 0.00000 0.00000
   rtevent: T 5.999 TT 5.999 M: 0.00000 0.00000
   rtevent: T 8.375 TT 8.375 M: 0.00000 0.00000
Score finished in csoundPerformKsmps() with 2.
inactive allocs returned to freespace
end of score. overall amps: 0.00000 0.00000
     overall samples out of range: 0 0
0 errors in performance
Elapsed time at end of performance: real: 8.441s, CPU: 0.670s
--Csound version 7.0 (double samples) Mar 9 2023
[commit: 2503fc0b439f70973a6ee9fdd810fda6d111882c]
1443 512 sample blks of 64-bit floats written to dac:system:playback_

that’s a spurious warning alright (and needs to be fixed), but the loop works anyway.

The problem appears to be with the feedback inside delayr/w that seems to make the
output 0. I don’t know why, another 7.0 bug to check.

The 7.0 parser does not like

ig*(aks + delay1(aks))*0.5

so if this is changed to

(aks + delay1(aks))*0.5*ig

there’s sound. A bug in expression parsing. At least we know where it is now.

Thanks for this example; will try out.

FYI, I’ve tried examples before, and they work fine…I was just more lobbying for an out-of-the-box solution that didn’t involve having to “roll-your-own”, as it were.

Another way of asking: can’t these solutions become 1st-class opcode citizens in some future release? If it’s deemed that “correcting” the existing opcodes might break backwards compatibility, perhaps a new opcode called “superpluck” or something like that? (That’s what I affectionately have called my Faust-ian solution locally). I see such an opcode a win overall for existing and future Csound users.

Cheers,

I think it is bad that those opcodes are out of tune. Not sure where to go with it. Possible best to deprecate.
I could certainly make an opcode to implement this code here, but not sure if it is the best thing.

Note that the classic “pluck” opcode is perfectly in tune although it does not have a decay time control.

Yeah, pluck works, as far as tuning, AFAICT…however, having more sculpting options open for the user – excitation signals, decay, stiffness, etc…in an out-of-box solution, would be ideal.

I’ll dig up my “superpluck” signal flow after work later and share the orchestra. Some kind of capturing of what that does, and the core code from Faust that keeps things in-tune, would be an ideal solution.

I like the idea of deprecation of the older opcodes in favor of a modern robust opcode or opcode-family.

Also streson is definitely in tune.

In fact we don’t need code from Faust, that’s just a standard waveguide. The delayr/w replaces it just fine, it id just as efficient.

Prof. Victor Lazzarini
Maynooth University
Ireland

Sounds good, Victor!