[Csnd] [share] - MIDI Synthesizer

Hi All,

I thought I'd share a MIDI synth that I thought might be useful as a
starter template for exploration. One interesting thing about it is
the declick_rel UDO. I hadn't ever thought to do something like this,
but it's intentionally using a linseg that won't run until release
time. It allowed me to create a declicking envelope that I could
control easily what would happen over the release segment. (Kind of a
problem for most -r opcodes that allow setting only one release
segment curve.) Maybe it's a technique others have used before but was
new to me. It's also pretty efficient in that it just returns the
initialized 1 value up until release time.

opcode declick_rel, a, ii
  irel, idec xin

  asig init 1

  if release() == 1 then
    // replace with whatever shape you like for release segment
    asig = linseg(1, irel - idec, 1, idec, 0)
  endif

  xout asig
endop

The synth itself is pretty straightforward:

* 2-OSC (vco2)
* 2-pole filter with filter cutoff env
* drift LFO and DADSR for vibrato LFO

and there is a simple mixer with reverb amount and pre-delay.

The example is available on the Web IDE at:

https://ide.csound.com/editor/4DprFSwVtugMtoEzODuo

and I've pasted the code below.

Cheers!
Steven

[CODE]

;; Author: Steven Yi
;; Date: 2024-06-23
;; Description: MIDI Synthesizer, 2-OSC, DADSR for modulation, Key Tracking,
;; novel declick env for control in release stage

<CsoundSynthesizer>
<CsOptions>
-odac -M0
</CsOptions>
<CsInstruments>
0dbfs=1
nchnls=2

ga1 init 0
ga2 init 0

opcode declick_rel, a, ii
  irel, idec xin

  asig init 1

  if release() == 1 then
    // replace with whatever shape you like for release segment
    asig = linseg(1, irel - idec, 1, idec, 0)
  endif

  xout asig
endop

instr 1
  ifreq = cpsmidinn(notnum())
  iamp = ampdbfs(-12)

  // slow LFO for slight drift
  klfo = lfo(7, 0.05)

  // DADSR for vibrato
  kdadsr = linsegr:k(0, .5, 0, 1, 1, 1, 1)
  kvib = cent(kdadsr * lfo(20, 4.7))

  kfreq = ifreq * cent(klfo) * kvib

  asig = vco2(.5, kfreq)
  // sleight detuning
  asig += vco2(.25, kfreq * cent(10),2, .4)

  asig *= iamp

  aenv = transegr:a(0, .05, 0, 1, 3, -4.2, 0.5, 4, -4.2, 0)

  ioct = octcps(ifreq)

  // modulated filter using base oct + env * key-tracked value
  asig = zdf_2pole(asig, limit(cpsoct(5.5 + aenv * (2 + (ioct * .5))),
20, 18000), .5)

  asig *= declick_rel(4, 2)

  ga1 += asig
  ga2 += asig
endin

instr Mixer

  // amount to send to reverb
  iamt = ampdbfs(-12)

  // pre-delay: 150ms
  a1 = delay(ga1 * iamt, .15)
  a2 = delay(ga2 * iamt, .15)

  a1, a2 reverbsc a1, a2, 0.88, 12000

  // sum dry + wet
  a1 += ga1
  a2 += ga2

  out a1, a2
  clear ga1, ga2
endin
schedule("Mixer", 0, -1)

</CsInstruments>
</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

Thanks for sharing the example!

The release opcode is very clever, I will definitely use it.

And the whole code is inspiring!

Tarmo

P, 23. juuni 2024 21:21 Steven Yi <stevenyi@gmail.com> kirjutas:

Hi All,

I thought I’d share a MIDI synth that I thought might be useful as a
starter template for exploration. One interesting thing about it is
the declick_rel UDO. I hadn’t ever thought to do something like this,
but it’s intentionally using a linseg that won’t run until release
time. It allowed me to create a declicking envelope that I could
control easily what would happen over the release segment. (Kind of a
problem for most -r opcodes that allow setting only one release
segment curve.) Maybe it’s a technique others have used before but was
new to me. It’s also pretty efficient in that it just returns the
initialized 1 value up until release time.

opcode declick_rel, a, ii
irel, idec xin

asig init 1

if release() == 1 then
// replace with whatever shape you like for release segment
asig = linseg(1, irel - idec, 1, idec, 0)
endif

xout asig
endop

The synth itself is pretty straightforward:

  • 2-OSC (vco2)
  • 2-pole filter with filter cutoff env
  • drift LFO and DADSR for vibrato LFO

and there is a simple mixer with reverb amount and pre-delay.

The example is available on the Web IDE at:

https://ide.csound.com/editor/4DprFSwVtugMtoEzODuo

and I’ve pasted the code below.

Cheers!
Steven

[CODE]

;; Author: Steven Yi
;; Date: 2024-06-23
;; Description: MIDI Synthesizer, 2-OSC, DADSR for modulation, Key Tracking,
;; novel declick env for control in release stage

-odac -M0 0dbfs=1 nchnls=2

ga1 init 0
ga2 init 0

opcode declick_rel, a, ii
irel, idec xin

asig init 1

if release() == 1 then
// replace with whatever shape you like for release segment
asig = linseg(1, irel - idec, 1, idec, 0)
endif

xout asig
endop

instr 1
ifreq = cpsmidinn(notnum())
iamp = ampdbfs(-12)

// slow LFO for slight drift
klfo = lfo(7, 0.05)

// DADSR for vibrato
kdadsr = linsegr:k(0, .5, 0, 1, 1, 1, 1)
kvib = cent(kdadsr * lfo(20, 4.7))

kfreq = ifreq * cent(klfo) * kvib

asig = vco2(.5, kfreq)
// sleight detuning
asig += vco2(.25, kfreq * cent(10),2, .4)

asig *= iamp

aenv = transegr:a(0, .05, 0, 1, 3, -4.2, 0.5, 4, -4.2, 0)

ioct = octcps(ifreq)

// modulated filter using base oct + env * key-tracked value
asig = zdf_2pole(asig, limit(cpsoct(5.5 + aenv * (2 + (ioct * .5))),
20, 18000), .5)

asig *= declick_rel(4, 2)

ga1 += asig
ga2 += asig
endin

instr Mixer

// amount to send to reverb
iamt = ampdbfs(-12)

// pre-delay: 150ms
a1 = delay(ga1 * iamt, .15)
a2 = delay(ga2 * iamt, .15)

a1, a2 reverbsc a1, a2, 0.88, 12000

// sum dry + wet
a1 += ga1
a2 += ga2

out a1, a2
clear ga1, ga2
endin
schedule(“Mixer”, 0, -1)

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

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

Thanks for sharing, Steven, I’ll check it out.

An interesting side-meta-conversation can be had about the virtues of updating/clearing globals vs. using chnget/chnset (and obviously, the old deprecated ZAK opcodes).

These days, I see no reason not to use old-fashioned globals to pass info between instruments, like you did here.

Welcome guys!

I think channels are good for:

* Working with I/O with host app
* dynamic routing using sprintf to determine a channel to route to
(e.g., single instrument where each note reads from different
channels)

I think globals work well otherwise, including global arrays like in
my live code library's stereo bus system:

Larger projects I feel like benefit from some kind of abstraction like
the sbus UDOs in the above, or for the routing to be handled for you
(like in Blue), especially if you're planning out a complex mixing
scheme using a lot of effects and things like sidechaining.

Thanks Steven - all of this work is so inspiring.

The MIDI instrument is wonderful.

I see on https://soundcloud.com/filtercreed page, Steve Roach’s Ambient Lounge?
Steve Roach,… as in the synth group Moebius, with Bryce Robley and Bruce Courtois?

Aaron,… learning about ZAK and his math lately. Very interesting and useful here. Nice.
Dorian, I like it. Emoji

-Partev

Thanks for sharing, Steven, I’ll check it out.

An interesting side-meta-conversation can be had about the virtues of updating/clearing globals vs. using chnget/chnset (and obviously, the old deprecated ZAK opcodes).

These days, I see no reason not to use old-fashioned globals to pass info between instruments, like you did here.

Aaron Krister Johnson
Music, etc.:
https://soundcloud.com/aaron-krister-johnson
filtercreed

[

filtercreed

Ambient. Sit and listen.

](Stream filtercreed music | Listen to songs, albums, playlists for free on SoundCloud)

https://www.youtube.com/channel/UC_utjGYbSizWE0dNyr0Vdmg
https://aaronkristerjohnson.bandcamp.com/
http://www.untwelve.org
Code:
https://github.com/akjmicro