Effects

Effects processes such as reverb, chorus, and delay are not special: They’re implemented in Csound using the same kind of code used for synthesis. It’s possible to create an effect within the instrument whose signal will be processed by the effect, but this is not usually the best approach. For more on this topic, see “Global Signal Routing,” earlier in this chapter.

Reverb

With Csound, you can build your own reverb using comb and allpass filters, which can be configured however you like. This is an exercise for DSP experts, and we won’t get into it in this book. (See the upcoming sidebar for a cautionary note, however.) Fortunately, you don’t have to go there if you don’t want to, as several good reverbs are available among Csound’s opcode complement.

The reverbsc opcode is a good choice for making a stereo reverb. The basic way to use this has already been covered in the QuickStart tutorial in Chapter 3 and earlier in this chapter in the section on global signal routing.

freeverb is easy to use and sounds good. The room size and high-frequency damping are set in arbitrary values between 0 and 1, and can be modulated in real time. High values for room size (around 0.99) coupled with low values of damping produce a repetitious looping quality, but below are some values that produce a pleasant wash of reverb:


  aoutL, aoutR freeverb ain, ain, 0.89, 0.47

Also worth exploring is babo. (The name is short for “ball in a box.”) babo takes a mono input and produces a stereo output by modeling the echoes bouncing off the walls of a room. The dimensions of the room and the amount of diffusion from the walls are controllable, as are the position of the listener, the position of the sound source, and the distance between the listener’s ears. A set of eight default parameters can be user-programmed using a small data table created with GEN 02. Here is a simple reverb instrument that uses babo:


  instr 101; a modeled room reverberator
  ain = gaRevIn
  gaRevIn = 0
  giData ftgen 0, 0, 8, -2, 0.99, 0.9, 0, 0, 0, 0.3, 1, 0.8
  aoutL, aoutR babo ain, 6, 4, 3, 14.39, 11.86, 10, 0.9, giData
  outs aoutL, aoutR
  endin

If you’re curious how this works, you should definitely study the babo page in the manual. We won’t cover the details here. Basically, the ftgen line uses GEN 02 to create eight optional parameter values that are then passed to babo, overriding the defaults. Most of the values shown above are defaults, but I’ve increased the high-frequency damping from 0.1 to 0.9, increased the attenuation of the direct signal to 1 from the default of 0.5, and reduced the diffusion from 1 to 0.9 because the default of 1 produces a long-lasting roar. The values for the room dimensions are taken from the babo page in the manual.


image

Filters Aren’t Just for Tone-Shaping One of the things that can happen, when you start writing DSP code, is that the values produced by your code or by the opcodes you’re employing can gradually spiral higher and higher. This is especially likely with any process, such as reverberation, that involves delay lines and feedback. When the values of an audio signal gradually drift higher (or lower), you may not be able to hear the difference immediately, but the dynamic range of your system will be compromised. When this happens, clipping distortion is likely to occur sooner than it would otherwise, and your speakers will work less efficiently.

This type of mathematical error is often referred to as DC, because it’s analogous to the direct current in an analog audio system. Another way of looking at it is that it’s a signal with some non-zero amplitude and a frequency of 0 Hz. A standard method of getting rid of DC is to run the output of a reverb-type instrument through an atone opcode with a half-power point of 15 or 20 Hz. Recent versions of Csound include the dcblock2 opcode, which does the same thing.


Delay

Delaying an audio signal before sending it to the output is a surprisingly powerful sound modification technique. In addition to simple echoes, delay can be used for pitch changes, chorusing, and comb filtering. Csound is well equipped to create effects of this type. The primary opcodes you need to know are delayr, delayw, and deltapi. The vcomb opcode is described in the manual as “reverberating” a signal, but in my tests it seems to behave like a delay line with a built-in feedback loop. While not covered in this book, it’s also worth looking at if you’re seeking ways to build complex delay effects.

The delayr opcode reads the output of the delay and also sets the length of the delay. delayw writes a signal to the input of the delay. deltapi is a “tap” into the data being held by the delay; it can be used to create multi-tap delay effects.

You’ll often hear the term “delay line” used to refer to a delay effect. You might imagine the signal traveling down the line from the input (the write operation) to the output (the read operation). In fact, this is more or less what happens. A delay line is implemented in software as a “first-in, first-out” memory buffer. As a signal is received at the input of the delay, during each k-rate cycle the data in the buffer is passed from one memory location down the line to the next location. When the data reaches the end of the buffer (after some number of k-rate cycles), it is sent to the output.

delayr and delayw

The first example below creates a very simple delay line with feedback. The delay line is implemented as instrument 101. It receives a plucked sound from instrument 1. Take a look at this code and then read the explanation that follows. (The tags at the beginning and end of the .csd file have, as usual in this book, been omitted.)


  sr = 44100
  ksmps = 4
  nchnls = 2
  0dbfs = 1
  gaDelayBus init 0
  instr 1; a plucked sound
  ; setup
  iCos ftgen 0, 0, 8192, 11, 1
  idur = p3
  iamp = p4
  ifreq = cpspch(p5)

  ; synthesis
  kampenv line iamp, idur, 0
  asig gbuzz kampenv, ifreq, 25, 1, 0.8, iCos
  kfiltenv line 3500, idur, 500
  afilt lpf18 asig, kfiltenv, 0.3, 0

  ; output
  gaDelayBus = gaDelayBus + afilt
  outs afilt, afilt
  endin

  instr 101; a simple delay
  ; setup
  ain = gaDelayBus
  gaDelayBus = 0
  iamp = p4

  ; delay line with feedback:
  ifdbk = 0.5
  aout delayr 0.25
  delayw ain + (aout * ifdbk)

  ; output
  aout = aout * iamp
  outs aout, aout
  endin

  </CsInstruments>
  <CsScore>

  i101 0 4 0.5
  i1 0 0.5 0.8 7.00
  i1 +.. 7.04
  i1 +.. 7.07

The first thing to note is that the delay line (instrument 101) has to be started in the score. Thanks to a p3 value of 4, it runs for a couple of seconds after the end of the last note coming from instrument 1. This gives the echoes coming from the delay time to die away.

The second thing to note is that delayr, which reads the output of the delay, is used before the input is created using delayw. At first glance this may seem backward, but it’s actually very sensible. The argument to delayr tells Csound how long (in seconds) the delay will be. This argument causes Csound to set up a memory buffer of the proper length. The output variable (aout) can then be passed back to the input to create feedback—hopefully at a reduced level. The value ifdbk in this instrument controls the feedback amount. In a more complex orchestra, both the delay time and the feedback amount might be controlled using p-fields in the score.

The third thing to note is that this delay line is a wet-only effect; the delay instrument has no dry (un-delayed) output. The dry output is handled by the instrument generating the tones. As an exercise, you might try rewriting this example so that the delay instrument produces all of the output and has control over the wet/dry mix.

To create a stereo delay effect, you can use two separate delay lines, like this:


  instr 101; a stereo delay
  ain = gaDelayBus
  gaDelayBus = 0
  iamp = p4

  ; delay lines with feedback:
  ifdbkL = 0.75
  ifdbkR = 0.4
  aoutL delayr 0.125
  delayw ain + (aoutL * ifdbkL)
  aoutR delayr 0.25
  delayw ain + (aoutR * ifdbkR)

  ; output:
  aoutL = aoutL * iamp
  aoutR = aoutR * iamp
  outs aoutL, aoutR
  endin

This example could be spiced up by adding a cross-delay amount. With cross-delay, the output of the left delay line is used as an input to the right delay line, and vice versa. (One of the audio variables has to be created ahead of time using init.) If you try this, be careful that the total amount of delay feedback doesn’t exceed 1.0. If it does, runaway feedback will occur.

deltapi

The simplest way to use deltapi is with a fixed delay time. This opcode should be positioned between the delayr/delayw pair. The next example shows a multitap delay with four taps, two of which are panned left and two right. When using this in the .csd file given at the beginning of this section, you may want to increase the length of the i101 event in the score.


  instr 101; a multitap delay
  ain = gaDelayBus
  gaDelayBus = 0
  iamp = p4

  ; multitap delay with feedback:
  aout delayr 1.5
  aout1 deltapi 0.25
  aout2 deltapi 0.75
  aout3 deltapi 1.00
  aout4 deltapi 1.25
  delayw ain + (aout * 0.2)

  ; output:
  aoutL = (aout1 + aout3) * iamp
  aoutR = (aout2 + aout4) * iamp
  outs aoutL, aoutR
  endin

A more interesting use of deltapi is to sweep the delay time in some manner. This is most often done with an LFO. You’ll notice that deltapi is an interpolating opcode. This is important, because the interpolation smooths the audio signal when the delay time is changing. Below is a basic stereo chorus constructed with deltapi. Instrument 1 uses the same code given earlier in this section, but the notes in the score have been lengthened to make it easier to hear the chorus effect.


  instr 101; a stereo chorus
  ain = gaDelayBus
  gaDelayBus = 0
  iamp = p4

  ; swept delay with feedback:
  klfo1 lfo 0.003, 1.0
  klfo2 lfo 0.003, 0.8
  aout delayr 0.1
  atap1 deltapi 0.03 + klfo1
  atap2 deltapi 0.05 + klfo2
  delayw ain + (atap1 * 0.3) + (atap2 * 0.3)

  ; output:
  aoutL = atap1 * iamp
  aoutR = atap2 * iamp
  outs aoutL, aoutR
  endin

  </CsInstruments>
  <CsScore>
  i101 0 7 0.8
  i1 0 2 0.8 7.00
  i1 +.. 7.04
  i1 +.. 7.07

When using deltapi, it’s important that you keep the minimum delay time greater than zero and the maximum less than the total delay time created by delayr. In the code above, delayr creates a memory buffer containing 0.1 second of audio. This is actually more than is needed. The delay time for the first deltapi output varies from 0.027 to 0.033 as it is swept by the LFO, while the second varies from 0.047 to 0.053. You can experiment with a wider LFO sweep, as long as you’re careful that the maximum and minimum values stay in range. The values I used for the LFOs in this instrument were arrived at empirically: I like the sound. When I increase the LFO amplitude, the chorusing starts to sound wobbly to me.

vdelay

Using vdelay is a bit easier than using a delayr/deltapi/delayw set, because the Csound code is more concise. The main limitation of vdelay is that it only produces one tap into the delay line. Within a delayr/delayw pair, you can run a number of taps. Another difference is that delayr expects to see a delay time in seconds, while vdelay expects the delay time to be given in milliseconds. Even though its name doesn’t end with -i, vdelay is an interpolating opcode, so it will produce smooth audio.

Here’s a simple example in which a stereo chorus, constructed using two vdelay units, is build into an instrument. The same principle could be used to build a six-voice chorus modulated by several LFOs. Play a note on this instrument for five or six seconds to hear the chorusing:


  giSine ftgen 0, 0, 8192, 10, 1
  giWave ftgen 0, 0, 8192, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1

  instr 1
  kpitchramp expseg 800, 2, 200, 1, 200
  asig oscil 0.3, kpitchramp, giWave
  imoddepth = 2.5
  imodrate = 0.7
  aLFO oscil imoddepth, imodrate, giSine
  imaxdel = 15
  adelL vdelay asig, 8 + aLFO, imaxdel
  adelR vdelay asig, 8 - aLFO, imaxdel
  kampenv linseg 0, 0.1, 1, p3 - 0.11, 1, 0.1, 0
  aL = (adelL + asig) * kampenv
  aR = (adelR + asig) * kampenv
  outs aL, aR
  endin

vdelay is one of a family of closely related opcodes. The details will be found in the manual.

Distortion

Rumor has it that the deliberate use of distortion began in the 1960s, when guitarists found that they could make a nastier, more aggressive sound by slicing through their speaker cones with a razor blade. Not long afterward, the fuzz tone effect pedal, which produced a similar sound but without physical damage to the equipment, was introduced. In any event, the introduction of deliberate distortion has become an important technique in electronic music-making.

There are various ways to add distortion to a signal. Modulating the frequency of an audio oscillator with a noise source, for instance, is a form of distortion. In this section we’ll take a quick look at waveshaping distortion. Waveshaping introduces new partials (frequency components) into the sound. Generally this is an amplitude-dependent process. That is, the louder an input signal is, the more distorted it will be, and the more new partials will be added.

Waveshaping with mirror

The mirror opcode provides a quick, easy way to do waveshaping. It has lower and upper thresholds. Signals that exceed these thresholds are “reflected” back into the range between the thresholds in a way that sounds pleasant and natural. Play this instrument with a 12-second score event to hear the effect. When the waveform of the audio signal has an amplitude greater than ± 0.4, waveshaping takes place.


  giSine ftgen 0, 0, 8192, 10, 1

  instr 1
  kamp linseg 0, 1, 0.4, 5, 1.3, 1, 1.3, 2, 0.4, 3, 0
  asig oscil kamp, 200, giSine
  amir mirror asig, -0.4, 0.4
  outs amir, amir
  endin

Waveshaping with table

The example below doesn’t make use of any opcodes not already covered in this chapter. It shows a way of using table at audio rate so as to add distortion. Instrument 1 in this example generates a sine wave with a gradually increasing amplitude, but it doesn’t send the resulting signal to the output; instead, the signal is routed to the distortion instrument. The latter contains an f-table generated using GEN 08. GEN 08 produces a smooth curve with arbitrary peaks and valleys.

The input signal (coming from gaDistBus) is expected to have a maximum amplitude of 1.0. It’s multiplied by 8,192 because that is the length of the iShape table that has been created using ftgen. The signal is then used as an index into the iShape table. The output is no longer a sine wave; instead, it has whatever peaks and valleys were defined in the table.


  giSine ftgen 0, 0, 8192, 10, 1
  gaDistBus init 0

  instr 1
  idur = p3
  iamp = p4
  ifrq = p5
  iatk = 0.01
  idec = 0.1
  isustime = idur – 0.11

  kamp linseg 0, iatk, 0.01, isustime, iamp, idec, 0
  asig oscil kamp, ifrq, giSine
  gaDistBus = gaDistBus + asig
  endin

  instr 11; distortion
  iamp = p4
  iShape ftgen 0, 0, 8192, 8, 0, 1024, 1, 512, -1, 512, 1, 2048, -1, 4096, 0
  ain = gaDistBus
  gaDistBus = 0

  asig table ain * 8192, iShape
  asig = asig * iamp
  outs asig, asig
  endin

  </CsInstruments>
  <CsScore>

  i1 0 10 0.6 200
  ; uncomment the next line to hear intermodulation distortion:
  ; i1 3 7 0.4 350
  i11 0 10 0.5

This effect could easily be modified by adding a lowpass filter to produce a fatter sound.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.51.228