Live Code – Modular-esque

Live code session using csound-live-code and https://live.csound.com.

Initial code happens for about 2m40s, then sound begins.

For those interested in the code, the session uses:

1. start UDO for working with the different always-on instruments
2. vco2 square wave for enveloping (has a nicer quality to it than
using lfo with type 3, IMO)
3. portk for frequency glide
4. chnset for immediate setting of a channel value as part of performance
5. chnset within an always-on instrument (“Mod”) together with k-rate
randh to show how to approach using continuous values with channels

reface DX style FM Feedback (Saw, Square)

I have long avoided FM (Frequency Modulation) synthesis in my own musical practice as I never felt connected with the results I was able to get myself. However, I recently had the great pleasure to attend a talk about the 50th anniversary of FM synthesis, given by its creator, John Chowning, and I was very inspired to explore FM once again. In so doing, I came across the Yamaha reface DX synthesizer and became fascinated with reproducing its feedback system to morph an operator’s output from a Sine to either Saw or Square waveform.

Now, I do not own a reface DX, so most of my research into it was through looking at manuals and watching video demonstrations on YouTube to try to get an idea of how it might be done. I knew from going through literature on FM and PM (Phase Modulation) that using PM with feedback could get an operator’s signal to move from a Sine to Sawtooth wave, depending upon the amount of feedback. I was quickly able to setup a PM instrument in Csound and test this out and it sounded much like what I had heard for the reface DX.

;; feedback PM - feedback moves towards saw
instr PMFBSaw
ifreq = p4
iamp = p5

kfb = linseg(0, p3 * .5, 0.3, p3 * .5, 0)

aphs = phasor(ifreq)

; init for feedback
acar init 0
acar = tablei(aphs+(acar*kfb), 1, 1, 0, 1)
acar *= linen:a(1, 0.1, p3, 0.1) * iamp

outc(acar, acar)
endin

In the code above, one can see that the acar output from tablei is also used as input into the opcode. The code above runs in a single-sample context (in Csound parlance, with ksmps=1).

Now, the part I could not find anywhere in literature or discussion online was how to use operator feedback to morph from Sine to Square. (This is done by using 0 to -127 range for feedback on the reface DX.) After a couple days of research and exploration, I stumbled upon a calculation that sounded to my ears very much like what I had heard on the reface DX videos.

The code below shows the entire instrument:

;; feedback PM - feedback moves towards square
instr PMFBSquare 
  ifreq = p4 
  iamp = p5

  kfb = linseg(0, p3 * .5, 0.3, p3 * .5, 0)
  
  aphs = phasor(ifreq)

  ; init for feedback
  acar init 0 
  acar = tablei(aphs+(acar*acar*kfb), 1, 1, 0, 1)
  acar *= linen:a(1, 0.1, p3, 0.1) * iamp

  outc(acar, acar)
endin

This instrument is virtually the same as the first instrument with the exception of one additional calculation: the multiplication of the acar feedback by itself. (This is seen in the acar*acar calculation.) Adding this one additional multiplication made the signal move from Sine to Square.

I posted this to the Csound User list and Iain McCurdy gave great feedback that the waveform could be morphed between Saw and Square by interpolating between acar and 1. This made a lot of sense as when one of the acar‘s becomes 1, it reduces back down to the normal feedback addition to produce a Saw sound. After some further emails, I did some experiments to use a cosine-based mapping for the interpolation that resulted in a nice transition.

;; feedback PM - feedback moves from square to saw 
;; Based on Iain McCurdy's comments on Csound User List
instr PMFBSquareSaw 
  ifreq = p4 
  iamp = p5

  kfb = 0.25 
  ;;kfb = linseg(0, p3 * .5, 0.5, p3 * .5, 0)
  kwaveshape = linseg(0, p3 * .5, 1, p3 * .5, 0) ;; range 0-1 for saw->square
  kwaveshape *= kwaveshape ;; adjust curve
  kwaveshape = $M_PI * (kwaveshape + 1) ;; adjust from PI->2PI
  kwaveshape = (cos(kwaveshape) * 0.5) + 0.5  ;; adjust to 0-1
  
  aphs = phasor(ifreq)

  ; init for feedback
  acar init 0 
  acar = tablei(aphs+(ntrpol(acar, a(1), kwaveshape)*acar*kfb), 1, 1, 0, 1)
  acar *= linen:a(1, 0.1, p3, 0.1) * iamp

  outc(acar, acar)
endin

I do not know if these calculations are what are used in the reface DX, but regardless, the sine->square sounded good to my ear and I felt it was usable for the kind of sound work I was interested in doing. For now, I have posted the Csound CSD project file here. The audio example at the top of this post is an MP3 version of the output rendered from this project.

Cheers!
Steven

Published
Categorized as csound

Hex Beats

Hexadecimal (base 16) has been used in various forms of computer music for a very long time, generally as a condensed way to notate values within a power-of-two range.  For example, rather than write out “15” as a decimal value (base 10), one can use “F”, and rather than write out “255”, one can use “FF”. The notation of hexadecimal numbers, in general, take up less horizontal space on the screen than its base 10 counterpart.

The differences in screen real estate is even more pronounced when comparing the binary value (base 2) to the decimal and hex values.  Let’s compare some values here:

Binary:  1101
Decimal: 14
Hex:     E

Binary:  11001111
Decimal: 207
Hex:     CF

A chart showing the binary, decimal, and hex values for number 0-255 are available here.

Now, one of the interesting challenges in live coding pattern-oriented music for me has been trying to have a very condensed notation for expressing beats (onsets).  One way I’ve seen used is to notate values in a binary form within a string, such as “1000100010101000” which would mean “play notes where there are 1’s, but don’t play notes where there are 0’s”. In this case, on beat 1, 5, 9, 11, and 13.

Binary values in a string, on the one hand, quite clearly notates when an instrument should play. On the other hand, I’ve found it visually takes up quite some space and can be a bit slow to parse mentally.

One thing I’ve found rather useful is to notate onset patterns using hexadecimal strings.  I first explored this in my Clojure systems Pink and Score, but recently translated the function I was using to Csound code.  The Csound code turned out to be quite simple:

opcode hexbeat, i, Si
  Spat, ibeat xin

  ;; 4 bits/beats per hex value
  ipatlen = strlen(Spat) * 4
  ;; get beat within pattern length
  ibeat = ibeat % ipatlen
  ;; figure which hex value to use from string
  ipatidx = int(ibeat / 4)
  ;; figure out which bit from hex to use
  ibitidx = ibeat % 4 
  
  ;; convert individual hex from string to decimal/binary
  ibeatPat = strtol(strcat("0x", strsub(Spat, ipatidx, ipatidx + 1))) 

  ;; bit shift/mask to check onset from hex's bits
  xout (ibeatPat >> (3 - ibitidx)) & 1 

endop

And an example of its use is shown here:

  if(hexbeat("f0d0d0f0", ibeat % 32) == 1) then
    schedule("Synth1", 0, p3, inScale(48, 0) )
  endif

The above is saying: “within the hexadecimal beat string of f0d0d0f0, and given the current beat value between 0 and 32, check if the onset is a 1 and, if so, perform Synth 1”.

The code above may be a little tricky to grok at first glance. I’ve started a Github repository for this code and made an online web app for live coding with Csound and this library. The live web site is available at:

https://kunstmusik.github.io/csound-live-code/

and the source code is available at:

https://github.com/kunstmusik/csound-live-code

In working with the hex beat patterns, I found it took a little practice but the meaning of various hex values started to become intuitive over time.  Hexadecimal works really well, in my opinion, for notating pattern onsets as each hex value maps to 4 bits, which works perfectly for 4 16th-notes.  With this, 4 hex values can be used to notate a single measure of 16 16th-notes, 8 hex for 2 measures, and so on.

Published
Categorized as csound

Csound: adsr140

I’ve put together a UDO called adsr140 that is based on the Doepfer A-140 envelope generator[1].  It uses code by Nigel Redmon for its ADSR[2], but has been ported to Csound ORC code as well has the added ability to take in a retrigger signal.

To note: adsr140 uses positive values from signals for gate and retrigger as a gate on.  (The examples use the lfo opcode with default sine as a gate and retrigger signal.)

The first example sounds use instr 1 which only uses the gate signal to trigger the adsr.  The second example that comes in at 18 seconds uses instr 2 which uses both gate and retrigger.

Also to note, the code for adsr140 is using a-rate signals for gate and retrigger.  It also requires Csound 6.04 as it uses a while-loop.

Enjoy!
steven

[1] – http://www.doepfer.de/a100_man/A140_man.pdf
[2] – http://www.earlevel.com/main/2013/06/02/envelope-generators-adsr-part-2/

p.s. – Life’s been busy lately, but I plan to add this to Pink as soon as I have a chance.

Published
Categorized as csound

Announce: New Score Library in Clojure

Hi All,

I’d like to announce a score generation library written in Clojure called “score”:

https://github.com/kunstmusik/score

This library is currently a work in progress. I am planning to put all general composition functions that I use or plan to explore within this library.

Some notes:

The library currently offers two styles of score generation. One is styled after SuperCollider’s Patterns. Patterns in SC generate values without context, and map directly to standard Clojure sequences. gen-notes and gen-score in src/score/core.clj are functions for use with the score generation style. With this it is simple enough to emulate any feature in SC Patterns using standard Clojure sequence-related functions.

The other score generation style is CMask-based. In CMask, rather than have sequences, generator functions are used that function within a context of time. (The start time of the current event being generated is passed-in as an argument.) That difference of having time as an argument allows to express things like time-varying masks, frequencies, etc. So far, I have completed porting all of the features of CMask and have done light testing.

As for the future of this library, I will be using this in my pieces moving forward, and expect to maintain this library, adding features as required. I would warn that the library is still a little volatile, so functions may move namespaces and users may need to update code between these early versions. I hope to clean up and stabilize the API soon so backwards compatibility can be maintained. (The library is version 0.1.0 at the moment; it will be bumped to 1.0.0 when the API is stable.)

Also to note, the library is purposely designed to be generic. I am targeting Csound score generation at the moment, but the core of the library works to generate simply lists of lists (see core.clj, and note the difference between gen-notes and gen-score, or gen-notes2 and gen-score2). This allows the library to be used beyond Csound. For example, you could always create a formatting function to send the notes as MIDI, OSC, etc. (I have some plans to do some interesting event exploration using score with a Clojure music system I’m working on.)

For examples, I have some demo clj files I used while developing within a REPL (https://github.com/kunstmusik/score/tree/master/src/score/demo).They show a bit of what using the library would look like.

Comments and contributions would be very welcome.

Thanks!
steven

Clojure and Blue/Csound Example

I have started to work on a new composition and wanted to move from using Python to Clojure as the scripting language for my music.  I was experimenting yesterday and was pleased to be able to write a score generation function that was fairly flexible, allowing for easily using hardcoded values as well as any sequence for filling in p-fields of generated Csound Score.

From my work session I came up with some fairly condensed code I was happy with:

(require '[clojure.string :refer  [join]])

(defn pch-add  [bpch interval]
      (let  [scale-degrees 12
                       new-val  (+  (* scale-degrees  (first bpch))
                                                      (second bpch) interval)]
                [(quot new-val scale-degrees)
                          (rem new-val scale-degrees)]))

(defn pch->sco  [[a b]] 
      (format "%d.%02d" a b ))

(defn pch-interval-seq  [pch & intervals] 
    (reduce  (fn  [a b]  (conj a  (pch-add  (last a) b)))  [pch] intervals))

(defn pch-interval-sco  [pch & intervals] 
    (map pch->sco  (apply pch-interval-seq pch intervals)))

(defn score-arg  [a]
      (if  (number? a)
                (repeat a)
                a))

(defn gen-score  [& fields]
      (let  [pfields  (map score-arg fields)]
                (join "\n"
                                  (apply map (fn [& args] (join " " args)) (repeat "i") pfields))))

;; EXAMPLE CODE

(def score 
      (gen-score 1 0 1 
                         (pch-interval-sco  [6 0] 12 8 6 2 6)
                         (pch-interval-sco  [6 0] 12 8 6 2 6)
                         (range -10 -100 -1) 0 1))

(print score)

The output from the print statement is:

i1    0.0    1    6.00    6.00    -10    0    1
i1    0.0    1    7.00    7.00    -11    0    1
i1    0.0    1    7.08    7.08    -12    0    1
i1    0.0    1    8.02    8.02    -13    0    1
i1    0.0    1    8.04    8.04    -14    0    1
i1    0.0    1    8.10    8.10    -15    0    1

The key part is the gen-score function.  It can take in either a number or a sequence as arguments.  If a number is given, it will be repeated for each note.  For a sequence, they can be infinite or finite. The only important part of using gen-score is that the at least one of the arguments given is a finite list.

This is fairly similar to SuperCollider’s Pattern system, though it uses standard abstractions found in Clojure. To me, it is a bit simpler to think in sequences to generate events than to think about the Pattern library’s object-oriented abstractions, but that is just my own preference.  Also, the Pattern system in SC is designed for real-time scheduling and also has an option for a delta-time generator.  I think the delta-time aspect could be added fairly easily using an optional keyword-argument to gen-score.

As for making this work in realtime, gen-score would have to be rewritten to not format the strings but instead return the list of p-field values. A priority-queue could then be used to check against the time values of the notes and pause and wait to generate new notes when the scheduling time demands it.

Ultimately, I was very pleased with the short amount of time required to write this code, as well as the succinctness of it.  One thing that has been on my mind is whether to use a CMask/PMask kind of approach to the p-field sequence generators.  In those systems, what would be comparable to the sequence generators–I think they’re just called Fields in those systems–actually take in a time argument.  That gives the generators some context about what to generate next and allows the ability to mask values over time.  I am fairly certain I will need to update or create an alternative to the gen-score function to allow generator functions.  I’ll have to consider whether to use a Clojure protocol, but I may be able to get away with just testing if the argument to gen-score is a function, sequence, or number and act appropriately.  (Something to look at next work session. 🙂 )

Published
Categorized as Blue, csound

How Csound Works – Presentation from the 2nd International Csound Conference

I recently gave a talk at the 2nd International Csound Conference, held at the Berklee College of Music in Boston, entitled “How Csound Works”.  In the talk, I went through high-level design, key data structures, the Orchestra compiler, the event and runtime system, and some other features.  I have placed a copy of the presentation slides as a zip file here:

Download Slides

Additionally, the slides can be viewed online here.

Regarding the slides, I used Hakim El Hattab’s wonderful Javascript Slide framework, Reveal.js.

Note: I believe the presentation was recorded.  When that is made available, I will update this entry with a link to the video.

Published
Categorized as csound

2nd International Csound Conference 2013

The 2nd International Csound Conference took place this past weekend at the Berklee College of Music in Boston.  I had a fantastic time there getting to see Jean-Claude Risset, John Chowning, and Barry Vercoe all give keynotes.  There was a reallly nice tribute session for Max Matthews, with people like Tom Oberheim, David Ziccarelli, and Max’s family there sharing some beautiful stories about Max.

Beyond that, it was great to see all the latest going on in the Csound community. I thoroughly enjoyed Rory Walsh’s presentation on Cabbage, as well as Andres Cabrera’s presentation on CsoundQt. They’ve both made some great developments in their software!  I also really enjoyed Oeyvind Brandtsegg’s presentation “Sonification with Csound ­ Quasar Correlations”, discussing an upcoming installation work.

There were certainly many presentations given on the 2nd day, and as they were in parallel tracks one simply couldn’t attend everything.  I ended up giving one presentation on the first day, and two presentations on the second, one of which spanned two parts.  Because of that I certainly felt like I missed out on going to presentations, but I believe everything was being filmed so I am looking forward to watching those when they are out.

Regarding the presentations I gave, I think I did just a so-so job on the Blue presentation, and was happy with how the “How Csound Works” and “How to use the Csound API” talks went. Given that there was a lot to prepare, I was happy in the end with how it all turned out.

The concerts were a very nice variety of pieces in different aesthetics. I was happy to be listening to music on very nice speakers, and especially enjoyed being in the company of many friends to do so.  I also enjoyed meeting a number of new people, and also finally putting faces to names I had long known from the mailing list.

Overall, I had a great time in Boston. I think Dr. Boulanger and the Berklee College of Music did a wonder job in organizing and creating a very special and memorable event. I’m already looking forward to the next Csound Conference!

Published
Categorized as csound

Julian Parker Ring Modulator

I wanted to share an implementation of Julian Parker’s digital model of a Ring Modulator. The paper he wrote from DAFx 2011 [1][2] was also used by the BBC Radiophonic Workshop in the “Recreating the sounds of the BBC Radiophonic Workhop using the Web Audio API” [3].

I’ve implemented the ringmodulator as a UDO, available here:

Blue Project and CSD (ringmod.zip) 

To run the CSD on the commandline, you can use:

csound -i adc -o dac -b 128 -B 512 ringmod.csd

The Blue project has knobs you can use to adjust the carrier’s amp and frequency. In the generated CSD, you can adjust gk_blue_auto0 for amp and gk_blue_auto1 for frequency, or just modify the poscil line in instr 1.

Note, the paper suggests using a high amount of oversampling (32x; 8x or 16x being reasonable for using sinusoidal carrier). This implementation does not do oversampling, which I believe the BBC version does do not as well.

As far as I’ve checked, the implementation matches the BBC one with the exception of using a limiter instead of a compressor. Also, I did one optimization to the wavetable generation to extract out a constant in the part where v > vl, but this is a minimal optimization as the wavetable generation is done only once really.

  • [1] – http://www.acoustics.hut.fi/publications/papers/dafx11-ringmod/
  • [2] – http://recherche.ircam.fr/pub/dafx11/Papers/66_e.pdf
  • [3] – http://webaudio.prototyping.bbc.co.uk/ring-modulator/

UPDATE (2015-04-07):  I found a coding bug in the table generation in the original version I posted.  The zip file above has been updated with the corrected code.

 

Published
Categorized as Blue, csound

waveseq – Wave Sequencing User-Defined Opcode for Csound

Lately I’ve been interested in a number of hardware synthesizers that came out during the late 80’s/early 90’s, as I’ve found their synthesis methods rather curious and inventive.  One of them, the Korg Wavestation, has a very interesting synthesis system, using a combination of Vector Synthesis and Wave Sequencing. Vector Synthesis is easy enough to implement using a cross-fading between different oscillators or sound generators, but I was curious to see about implementing the Wave Sequencing in Csound code.

To implement this, I used information obtained online, information in the manuals, time experimenting on a hardware Korg Wavestation, as well as time with the Korg Legacy Wavestation Software (I ended up purchasing the whole Legacy Collection). Here is an example of the waveseq User-Defined Opcode (UDO) using f-tables generated by GEN10:

Example 1:

As well as f-tables using sampled drum sounds:

Example 2:

The UDO is implemented such that it takes in an f-table that describes the entire wave sequence.  Therefore, most of the work to using this opcode is done in creating the set of f-tables to sequence through.  I did implement the following features:

  • Tempo: 24 duration is a quarter note; if tempo is non-zero, it will be used to set the duration of the quarter note, if 0, tempo is about 105 bpm
  • WaveSequence: start wave, looping type (0 = forwards, 1 = forwards and backwards), start wave for loop, end wave
  • Wave Tables: single-cycle wave/single-shot wave/looped wave (determined on whether sample rate given in the waveseq table is 0, positive, or negative), amplitude adjustment, cross-fade time, duration of table to play
About the design, a wave sequence table holds information about how many tables are in the sequence, and how to play them. For example, in example 2, the wave sequence table used is:
itab_bass ftgenonce 0, 0, -9512, 1, "BDRUM11.WAV", 0, 0, 0
itab_tom ftgenonce 0, 0, -17600, 1, "TOM5.WAV", 0, 0, 0
itab_snare ftgenonce 0, 0, -10947, 1, "SNARE11.WAV", 0, 0, 0

iwaveseqtab ftgenonce 0, 0, -32, -2, 3, 1, 0, 0, 2,  
	itab_bass, ftsr(itab_bass), 1, 1, ixfade, iwavedur,  
	itab_tom, ftsr(itab_tom), 2, 1, ixfade, iwavedur,  
	itab_snare, ftsr(itab_snare), 2, 1, ixfade, iwavedur
The iwaveseqtab has 32 size (just needs to be big enough to hold the information for the other tables), and in its first line it describes:
  • 3 tables are in this wave sequence
  • 1 is used to denote backwards and forwards playing through the sequence
  • 0 is the index of the start wave
  • 0 is the index of the loop start
  • 2 is the index of the loop end
after that come the tables to be used.  For example, the part that starts with itab_bass says:
  • sample rate of the table (positive here, so play as single-shot)
  • amplitude adjustment of 1 (amplitude is multiplied by this factor)
  • pitch adjustment of 1 (not currently implemented)
  • crossfade of 0 (ixfade = 0 earlier in the code, not listed above)
  • duration of 6 (iwavedur = 6 earlier in the code, not listed above), this is equivalent to a 16th note
The waveseq UDO is uses the tablexkt opcode, does manual incrementing of phaser variables, use linear amplitude adjustments when cross-fading, and a lot of code for reading from the wave sequence table and configuring things. The code still requires some cleanup work, but I wanted to go ahead and make this initial, mostly-complete implementation available.  I plan to create implement some further features for the waveseq opcode, then create either a full Blue instrument plugin or a BlueSynthBuilder version of this instrument that will allow easier creation and organization of f-tables into wave sequences. I am also thinking about adding Vector Synthesis as well (using then four waveseq instances).
Overall, it was quite an enjoyable experience to study the Wavestation and learn to implement wave sequencing in Csound code.  In the end, I’m still looking at where I might use this opcode in my own work, but it’s nice to know it’s available should I find a use for it.
Download the Examples and MP3’s here: waveseq – example CSD’s and MP3’s

 

Published
Categorized as csound