Category Archives: slippery chicken @ goldsmiths 2016

symposium on slippery chicken at Goldsmiths College, London, June 10-12 2016

EDIT: add-auxiliary-notes

Hi Mike,

I’m loving add-auxiliary-notes but it plays havoc with handle-ties when used in conjunction with any of the tie-over-rests, etc. methods.

I’ve added an extra line to the add-auxiliary-notes-aux function which makes everything work nicely. Could this be incorporated into the download, please?

(defun add-auxiliary-notes-aux (pitch-list &key (num-notes 3) (interval 1)
                                             ignore (activity-level 5)
                                             destructively verbose)
  (let* ((igns (loop for p in (force-list ignore) collect
                    (frequency (make-pitch p))))
         (most-used (hash-least-useds (pitch-list-stats pitch-list)
                                      :ignore igns :num num-notes
                                      :auto-inc nil :invert t))
         (result '())
         (als (loop repeat num-notes collect (make-al 2))))
    (loop for thing in pitch-list
       for freq = (get-freq thing)
       for pos = (position freq most-used)
       do
         (push (if (and (atom freq)     ; don't fiddle with chords
                        pos
                        (active (nth pos als) activity-level))
		   ;; DJR - Mon Jun 27 18:34:35 BST 2016
		   ;; The following line stops the function from
		   ;; disrupting handle-ties
		   (unless (or (is-tied-to thing)(is-tied-from thing))
		     (if (event-p thing)
			 (prog2
			     (when verbose
			       (format t "~&At bar ~a, transposing ~a "
				       (bar-num thing) (get-pitch-symbol thing)))
			     (transpose thing interval
					:destructively destructively)
			   (when verbose
			     (format t "to ~a" (get-pitch-symbol thing))))
			 (transpose thing interval)))
                   thing)
               result))
    (nreverse result)))

Apropos the symposium decision to make some short…

Apropos the symposium decision to make some short examples—perhaps first of all, of slippery chicken stuff without a call to make-slippery-chicken—here’s some Nokia fun to make being on a busy train on a Friday night seem positively calm:

;;; the nokia tune
(let* ((motif '(e e q q))
       (rhythms (append (loop repeat 3 append motif) '(h.)))
       (notes '(e6 d fs5 gs cs6 b5 d e b a cs e a))
       (events (loop for r in rhythms and n in notes collect
                    (make-event n r))))
  (events-update-time events)
  (event-list-to-midi-file events :start-tempo 150))

;;; the nokia tune after 5 cans of red bull
(let* ((motif '(e e q q))
       (rhythms (append (loop repeat 3 append motif) '(q.)))
       (notes '(e6 d fs5 gs cs6 b5 d e b a cs e a))
       (events (loop repeat 20 appending
                    (loop for r in rhythms and n in notes collect
                         (make-event n r))))
       (events+ '()))
  (loop for e in events and i from 0 do
       (push e events+)
       (when (and (not (zerop i)) (zerop (mod i 11)))
         (loop repeat 9 do (push (make-event (data (pitch-or-chord e)) 's)
                                 events+))))
  (setf events+ (reverse events+))
  (events-update-time events+)
  (event-list-to-midi-file events+ :start-tempo 450))

;;; same red bull but getting more nervous as the night goes on
(let* ((motif '(e e q q))
       (rhythms (append (loop repeat 3 append motif) '(q.)))
       (notes '(e6 d fs5 gs cs6 b5 d e b a cs e a))
       (events (loop repeat 20 appending
                    (loop for r in rhythms and n in notes collect
                         (make-event n r))))
       (events+ '()))
  (loop for e in events and i from 0 do
       (push e events+)
       (when (and (not (zerop i)) (zerop (mod i 37)))
         (loop repeat (* 5 (floor i 13))
            do (push (make-event (data (pitch-or-chord e)) 's)
                     events+))))
  (setf events+ (reverse events+))
  (events-update-time events+)
  (event-list-to-midi-file events+ :start-tempo 450))

Composing with the logistic map

The following code, is an example of how to generate rhythm, set and pitch palettes, as well as maps, based on the Logistic Map (LM).

http://pastebin.com/CtQ8pVda

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun logistic-map (x r map n &key (exp 1.0) (offset 0.0) (round t))
  (unless (and (= x 0.0) (= r 0.0))
    (error "chaos: First argument should be in the [0;1] range; second ~
            argument should be in the [3;4] range."))
  (loop for i from 1 to n
     for result = (+ offset
                     (* map
			(expt
			 (setf x (* r (* x (- 1.0 x)))) exp)))
     collect (if round (round result)
		 result)))

(defun gen-set-palette (lower upper elements x r &key (exponent 1.0)) 
  (let* ((range
	  (- (note-to-midi upper)
	     (note-to-midi lower))))
    (cons 
     (loop for i from 0 to (- elements 1)
	for init in (logistic-map x 4 1 elements :round nil)
	for leap in (logistic-map x 4 range elements)
	for offset = (+ (note-to-midi lower)
			(first (logistic-map init 4 (- range leap) 1)))
	collect (list i
		      (list
		       (remove-duplicates
			(mapcar 'midi-to-note
				(sort
				 (loop for midi in
				      (logistic-map init r leap 20
						    :exp exponent)
				    collect (note-to-midi (+ midi offset)))
				 #'<))))))
     '(:recurse-simple-data nil))))

(defun gen-rsp (timesig smallest elements x r &key (exponent 1.0))
  (loop for i from 0 to (- elements 1)
     for init in (logistic-map x 4 1 elements :round nil)
     collect (list i
		   (let* ((rsp '())
			  (sum 0)
			  (notes 0))
		     (loop for x in
			  (remove 0
				  (logistic-map init r (* 2 smallest) 1000
					;double it to have a bipolar output
					 :exp exponent
					 :offset (* -1 smallest)))
			summing (/ 1.0 (abs x)) into tot
					; accumulate
			while (< tot (/ (first timesig)
					(second timesig)))
					; check if the accumulated values do
					; exceed bar length
			do (setf sum tot)
			if (< x 0) ; attack if positive, rest if negative
			do (setf rsp
				 (cons
				  (list (abs x)) rsp))
			else
			do (setf rsp
				 (cons x rsp)))
		     (setf rsp (cons    ; when notes exceed bar length,
				(/ 1	; fill the remaining part
				   (rationalize
				    (- (/ (first timesig)
					  (second timesig)) sum))) rsp))
		     (setf rsp
			   (list
			    (cons timesig (reverse rsp)))) ; correct order
		     (loop for i in (first rsp)
			when (numberp i)
			do (incf notes)) ; accumulate number of attacks
		     (setf rsp
			   (list rsp ':pitch-seq-palette
				 (list ; generate pitch curve
				  (logistic-map init r 10 notes))))))))

(let* ((seed .7342)
       (r 4)
       (bars 20)
       (sets 20)
       (seqs 20)
       (logistic-map-test
	(make-slippery-chicken
	 '+logistic-map-test+
	 :ensemble '(((hand0 (piano :midi-channel 1))
		      (hand1 (piano :midi-channel 1))))
	 :tempo-map '((1 (q 60)))
	 :set-palette (gen-set-palette 'c2 'c4 sets seed r)
	 :set-map `((1 ,(logistic-map seed 4 (1- sets) bars)))
	 :rthm-seq-palette (gen-rsp '(2 4) 32 seqs seed r)
	 :rthm-seq-map `((1
			  ((hand0 ,(logistic-map .24324 r (1- seqs) bars))
			   (hand1 ,(logistic-map .34312 r (1- seqs) bars))))))))
  (re-bar logistic-map-test
	  :min-time-sig '(4 4) 
	  :auto-beam 'q)
  (midi-play logistic-map-test)
  (cmn-display logistic-map-test))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

The LM is an iterated nonlinear function which, under specific conditions, can generate chaotic behaviours.

This is its form:

x_n+1 = r * x_n * (1 – x_n).

x_n (in the [0;1] range) is the initial condition, while r is sometimes called "growth rate". r (in the [0;4] range) is the most important parameter in the LM as it determines the degree of repetition/unpredictability in the sequence of values (in the [0;1] range) generated by the function. The x_n parameter can also have significant effects for some specific values, although it will generally create different outputs while the function keeps an overall consistent behaviour depending on r.

Recently, I've composed a piece for piano in occasion of the Slippery Chicken symposium at Goldsmiths and I've decided to create it entirely using the LM as an algorithmic technique. The reason is that my research is based on feedback, and I wanted to be consistent even in the context of non-realtime systems, which is different than my usual one. Besides, the LM seemed to offer an ideal solution since, in general, my idea of music creation implies varying structures which still keep a unique identity and overall character. This, indeed, could be achieved by controlling the x and r parameters: r would determine the overall infrastructure of the piece, while x could generate different versions of the same work.

For my piece, I also decided to have nested levels of unpredictability, meaning that the output of some LM(s) would control the values of x and r of other LM(s) used for the generation of the SC data. In this example, though, most of the values are fixed and there is perhaps only one case of nested LM(s).

Here, I'm showing how the LM can be used to algorithmically generate palettes and maps.

The LOGISTIC-MAP function takes four arguments, has three keywords, and returns a list of numbers. The four arguments are: initial value (x); rate (r); mapping range (map); list length (n). The three keywords determine the exponent to which the sequence will be raised to (before being mapped to its range), an offset, and whether the values are integers or floating point numbers. The reason why I decided to add an exponent keyword is that a [0;1] range is convenient in this case as it is possible to push the sequence towards either of the two extremes, depending on exponents which are greater than 1 or between 0 and 1, without exceeding its boundaries. Please note that the sequence generated by the logistic-map function starts at x_n+1.

The GEN-SET-PALETTE function takes five arguments, has one keyword, and returns a SC set palette. The five arguments are: lowest note in the sets (lower, a symbol); highest note in the sets (upper, a symbol); the number of sets in the palette (elements); the initial value of the LM (x); the LM rate (r). The keyword sets an exponent which is then passed to the LM function. For example, using an exponent greater than 1 will push the notes in the sets towards to lower limit, whereas an exponent within 0 and 1 would push the notes towards the upper limit. Please note that, internally, there is a LM-dependent offset which shifts each set within the lower and upper limits.

The GEN-RSP function takes five arguments, has one keyword, and returns a SC rhythm sequence palette. The five arguments are: a time signature (timesig, a two-integer list); the smallest possible duration for an attack or rest (smallest, a denominator); the number of sets in the palette (elements); the initial value for the LM (x); the LM rate (r). The keyword sets an exponent which is then passed to the LM function. Internally, the mechanism is rather trivial and it maps the sequence of the LM function to a range which goes from -smallest to smallest. If the resulting value is a negative value, that is added to the set as a rest, otherwise, it is added as an attack. Some conditionals will make sure that the set is filled with the right durations, and the right number of pitch points will be generated based on the LM. For example, the argument r, here, could be used to determine the regularity of durations as well as the repetition of notes, while the exponent could be used as a density index considering that it would push the sequence towards the negative or positive side of the range.

The rest of the code is also very straightforward and it just creates a very simple example to give you a basic idea. Hopefully, the GEN-RSP will eventually become something more advanced, capable of generating tuplets and other more articulated rhythms. The GEN-SET-PALETTE, too, could easily be improved by, for example, giving the possibility to set varying limits for each set. And as I already mentioned, extending these algorithms in order to implement nested LM could also result in more interesting outputs.

I still hope that this will be useful for some of you.

Thanks to Michael and Dan for putting together this event which was a very productive and enjoyable experience with nice people and good music.

The first functionality update that arose out of…

The first functionality update that arose out of the Goldsmiths symposium last weekend was a fix to the way MIDI velocities are handled. As of the latest svn-latest check-in (2 mins ago) when you call midi-play, event velocities will reflect the last dynamic seen in any given part (rather than retaining their default velocity). Also, if you have hairpins (crescendo/diminuendo marks), then the notes that are under these will have correspondingly increasing/decreasing amplitudes.

From SuperCollider to Slippery Chicken by trial-and-error

In the last 72 hours I used the trial-and-error (mostly error) method of composition to write a disklavier piece in Slippery Chicken. I had never used a lisp type language before and mostly discovered that this is nothing like SuperCollider (which I’ve been having a love-hate relationship with for the last decade) and it isn’t possible (at least for me) to learn enough to get creative with it in 3 days.

The result of this endeavour was a suite of six pieces: your-title-here1-6, which reflect various stages of learning to wrangle slippery bits of chicken code into new arrangements and executing it to work out what I’ve just changed. I quickly learned that to make anything more than minor changes between checking the code output would result in confusion and frustration as I tried to work out which brackets don’t match up or what coding concept my SuperCollider shaped brain has misunderstood.

This process of course wasn’t exactly aided by my aversion to reading manuals and inability to concentrate on watching five minute long videos which probably contain all the answers to everything in the slippery chickenverse.

My composition process ended, mostly because time-til-concert reached zero before piece seven –  which may have engaged more deeply with dynamics – was finished, but here’s some things I learned which might help future SuperCollider->Slippery Chicken converts get going a bit quicker than I did:

  • everything in Slippery Chicken happens in bars (although apparently this can be bypassed by working in lisp directly…). Your bars need a time signature, and if you have 4 durations in the bar then you also need 4 pitches. This was probably the biggest shock to my system after spending the last 10 years liberally shoving any number of pitches and durations in arrays and letting Pbinds work out the rest.
  • if your number of durations and number of pitches aren’t the same the code wont run.
  • you can’t actually specify the exact pitches you want to use. You specify pitch sets and then approximate points in the pitch set where you want to choose a pitch from.
  • you also can’t specify pitches or intervals in chords (i think?).
  • the chords are generated by chord functions which are specified per instrument not at the place you want to use the chord.
  • each rhythm sequence and pitch set gets some kind of name and that name could be a single integer.
  • but tempo is specified by bar number, which could look the same as your rhythm seq/pitch set naming, but is unrelated.
  • there’s no modularity in slippery chicken – the bar based structure means that all the parts of the code need to have the same number of bars match up to other bits of the code, so most of the time copy and pasting bits of code in from other examples in the manual will probably just break your code.

Of course all of these things are explained in the manual, so if you have more time/patience and a longer concentration span than me, then that’s a good place to start.

improving documentation usability

Drawing on my experiences as a new user, and further discussions with new and power users, as well as with the lead developer (Michael E.), we have reached a consensus on how to re-organize the documentation for improving usability.

There are two main points to be noted:

  • move some or all existing (non-interactive) documentation to a wiki
  • ‘atomize’ documentation of all functions, and illustrate each with a limited collection of simplified examples that are re-used across entries (in the spirit of ‘hello world’).

The atomization will allow for functions and techniques to be more easily learned, as well as more effectively communicating how to combine them to achieve desired results.

The role of traditional wiki ‘discussion’ (aka ‘talk’, ‘sandbox’, etc.) pages could be served by the current microblog (where this entry was originally posted). The microblog has already been earmarked as a target destination to which existing social media discussions of the software will be migrated.

Over time, a two-way relationship can evolve between the microblog and wiki, where questions can be answered by microblog links to the wiki, and solutions arrived at in microblog discussions can be added to the wiki when an entry is absent or missing details (including a link back to the original microblog discussion).

A further benefit of using a wiki is its internal network structure, where each entry is a node (aka ‘deep link’) that can be linked to any other entry, allowing for the expansion of explanations as needed by a given user (e.g. an entry on mapping pitches to rhythms might link to an entry on specifying pitches; an entry on specifying basic rhythms might link to an entry on adding more detailed articulations such as accents, etc.).

Lastly, the use of tags in a wiki would allow for multiple concurrent organisations of concepts. To illustrate why this would be useful, consider that the way dynamics are implemented in a single bar differs from how they are implemented in multi-bar structures. A hierarchically organised wiki structure with tags would allow for a user to approach the documentation in two different ways: a user could choose to focus on the group of all operations involving dynamics in one or more bars, or, alternatively, on all operations that can be performed on a single bar and/or those that can be performed across multiple bars.

Owen 's Symposium Reflections

DiskClav

Things That Came Up

The topics that seemed to stand out during Saturday’s discussions were (i) the interoperability of Slippery Chicken with Other Things and (possibly related) (ii) more gentle ways in to the system for the New of Lisp. There was some discussion of things like porting and GUIs, but I don’t have much to say about them.

Interoperability seems to be a straightforwardly desirable thing. Dan’s been working on communication with OpenMusic. Meanwhile, though, things like reading in MIDI files and making the OSC facilities more apparent would open up all sorts of possibilities.

There were lots of useful ideas about how to augment the existing docs. Adam, for instance, has a number of cunning thoughts on what could be done with a wiki to develop more basic examples for n00bs and to point people towards Lisp resources.

One thought is also that, because this is Lisp, a compact domain specific language could be implemented on top of SC that allows for more terse (and less intimidating) expression of basic functionality.

I certainly began to get a firmer grasp on both the design of the system and what standard usage might look like as a result of these discussions. Compared with my starting point a couple of months ago, I’ve got a much firmer understanding of what SC is for and, crucially, how to interrogate the existing code base to find out what it can do.

What Did I Do?

My entry in to all this had been starting to implement some of Joseph Schillinger’s basic techniques in Lisp as part of the ground work for a project with Sileni, my improv/hip-hop/doomnoise group:
Codex Teuthis by Sileni

Part of what I was looking for was a better way of chucking around rhythms and of working out larger scale structures. Schillinger’s techniques have the attraction of being both simple and quite productive (see Arden, J. (2011). Old Tricks New Media: Schillinger Techniques are Relevant to All Kinds of Contemporary Music Irrespective of Style. Contemporary Music Review, 30(2) for an overview).

The outcome of this first burst of work was that I produced a fair amount of code that works with lists of durations and intervals in quite a flexible way, but that still needed some wrangling if they were to slot in with Slippery Chicken’s core functionality. Whilst the original idea had been just to use some of SC’s more peripheral features to help with things like MIDI output, I ended up doing quite a bit of interfacing work between my stuff and SC in order to take advantage of the pitch selection functions and quick large-scale structuring.

The piece itself uses Schillinger’s technique of interference patterns (or ‘resultants’) for much of the initial material, for instance:

  • The basic rhythmic cells (‘rthm-seqs’)
  • The pitch sets (by producing variations and extensions of basic three-note scales)
  • The pitch selection curves (using JS’s ideas about melody as a combination of directional axes and intervals)
  • Patterns of accents and rests

I produced a set of basic 40-unit rhythmic cells, as two bars of semiquavers in 5 /4, and produced a structure for the piece spread over five independent parts for the piano. My first pitch selection curves were pretty arbitrary but got the job done in terms of producing a starting block of three-ish minutes with which to work.

The main musical problem to contend with at this point was how to relieve the monotony of the results. My seeding rhythms had no rests, so when a voice was in for a section, is was going all the time. I ended up implementing three different passes of post-generation code to turn selected notes to rests, first to everything and then in a more targeted way to particular voices at particular times.

It was then that I returned to my pitch curves to try and widen the range of notes that SC was selecting. I felt that my exciting scales should be yielding richer results; part of the problem was that I hadn’t thought very deeply about how my instrument limits for the various voices related to what was available in the sets (especially for sparser variations of the scales), but also that the curves themselves had little variety (in some cases they where just binomial oscillations because I hadn’t thought through my original code). The second try yielded much more satisfying results, although more attention here would certainly develop the piece more.

The final things I had time for before performance were massaging the dynamics to make things less robotic, and putting in short drops to half-time in the middle / end of certain bars to vary the pace a bit. This latter thing seemed like a simple undertaking at first, rather than re-scoring the bars in question to have, e.g., an extra beat. However, it turned out to be a slightly fiddly thing to do in the end as I hadn’t left myself the time to make a nice tidy abstraction and instead have a lot of quite repetitive (and therefore breakable) code.

How Do I Feel About My Piece?

S’alright. It certainly feels considerably more like music than the ‘raw’ output from make-slippery-chicken did, but still could benefit from some sculpting, especially in the middle where things drag a wee bit. Moreover, I could ease up on the aggressive stripping out of material in places and let it burst forth a bit more. If I were really going to town then I’d cook up some new contrasting materials (and perhaps condense the existing pool a bit) and develop a structure with some stronger contrasts. Something I didn’t get around to coding was to force some all-voice pauses in to the flow to break it up a bit.

And About Slippery Chicken?

I hadn’t imagined going in that I would have much use for the full make-slippery-chicken approach when it comes to the Sileni project. However, now that I feel like I can exercise a bit more agency on its behaviour, the possibilities seem richer. More generally – treating SC as more of a library of useful functionality for musical purposes – there’s a great deal of really valuable stuff there that I’ll certainly be using, although some of it needs to be dug for.

I plan to keep on developing Lisp implementations of Schillinger stuff, and to make sure that there’s ways of slotting this in to SC. If I make any sort of serious dent on the 1200+ of Schillinger’s System of Musical Composition then that will be a lot of material, that I should probably document as a separate project, given the possible wider interest and application to beat making.

‘sifting’ for piano a little bit on compositional…

‘sifting’ for piano – a little bit on compositional ideas behind the piece.

1. Pitch Sets

A selection of the pitch material for the piece was a two levelled process. At first I have used simple sieve generators (such as arithmetic series, sieve of Eratosthenes – a simple, ancient algorithm for finding all prime numbers up to any given limit) to generate lists of values of a range between 21 and 107 (a midi range of a piano instrument). Second stage consisted in logical operations (a binary operators – union, intersection and symmetric difference; a unary operator – complementation) on these lists. An inspiration for this way of working with musical material was sifting used by Iannis Xenakis. In numerous publications Xenakis developed an elegant and powerful system for creating integer-sequence generators. Xenakis used sieves (cribles) for the generation of pitch scales and rhythm sequences in many compositions, and he suggested their application to a variety of additional musical parameters (see an implementation of the algorithm by Christopher Ariza http://www.mitpressjournals.org/doi/pdf/10.1162/0148926054094396). Additionally in ‘sifting’ this technique had been used to generate values for pattern chop algorithm.

2. Rhythm Material and rhythm chop

The basis of a rhythmic structures of the piece were traditional – existing largely in aural tradition – folk tunes from Wielkopolska region. A five short (1 bar length) rhythm sequences were declared as a source material for the pattern chop algorithm (based on Michael Edwards’ chop example) and values from a sifting algorithm were used to select a section of a sequence. The resulting material still has a distinct feel of a folk tune – I think – with added complexity of expanded pitch content and a break-beat style rhythm processing.

Here is a version of the piece as played at the concert
sifting-for-piano_02

Why The title of my piece for the…

IMG_1057

Why

The title of my piece for the slippery chicken Symposium arose after attending a lecture by the composer Christopher Fox on musical memory. One of the points he mentioned was that when we listen to music we tend to have in mind the thing we are currently listening to and the thing we listened to just before that. Every time we hear something that we recognise it is like returning to the (a) chorus, therefore it is possible to say that all music is essentially in rondo form.

How

At the heart of this piece is a rhythm chain, or more specifically, two rhythm chains which give me four lines of counterpoint. Using the add-voice method I then double the number of voices to eight and use the data to generate two separate slippery chicken objects. These are spliced together using my copy-bars-sc2sc method. (Follow the link and you can have it for yourself.)

The rondo part of the piece comes from a third slippery chicken object which contains hand coded rthm-seqs. I substitute these for the sticking points of the rhythm chain using a loop that analyses time signatures bar-by-bar and copy-bars-sc2sc. When the time signature is ‘(3 8) the method does its thing. (The time signature is only ever ‘(3 8) during the sticking points because these are defined as repeated quavers (‘e) in multiples of 3. The rhythm chain itself tends to make bars of ‘(2 4) and ‘(3 4).)

The pitch data is generated from harmonic sequences. Most by harm-list. The ordering of the set-map is determined by L-system rules.

Tempo changes are dependent on the sl-c object and there are brief rallentandi between these sections to make the piece sound more natural. This is further aided by my sc-midi-groove method (NB this is not yet available to the public).

Afters

It is very easy to create interesting material within sl-c but it is much harder to combine several different sections together within the same call to make-slippery-chicken. My copy-bars-sc2sc method makes life a lot easier in this regard and I plan to use it a lot more. At the conference, I presented work I had made using the method and have made it available on my website for anyone else to use. http://vitruviandan.wordpress.com/code

Midi-file of the piece: rondo-del-computer

 

velocity-tweaked robot pianos

One of the greatest pleasures of developing my algorithmic composition software is watching what happens when it’s taken up by other composers. It’s always astonishing to me to hear the variety of music that comes out of even quite orthodox use of the software. We’ve just presented a concert of nine new algorithmic works composed for the Disklavier at Goldsmiths. Robot piano music, Dan Ross called it, and though there were no actual robots in the sense that you might imagine from such a description, the Disklavier’s computer controlled hammers offered a precision attack that was particularly striking in the onsets of some of the massive chords that were generated.

Several of the pieces were developed over the last couple of months (and several coding sessions) in Edinburgh; others arose in just the last week. They ranged from Schillinger-inspired rhythmic structures to conflations of different pieces made with the rhythm chains algorithm; from the generation of musical parameters from chaos algorithms to those based on biomechanics; from Xenakis-style sieving to randomness to irrational rhythms from permutated proportions. With the permission of the composers I’ll post recordings of the the music on this blog as soon as possible.

 

IMG_20160612_104443
Last-minute hammer velocity tweaking at the Goldsmiths slippery-chicken symposium

Post Generation Event-By-Event Processing

A helper function for processing events post-processing (e.g. for setting dynamics)

 
;; Process a set of events over pattern  with function
;; pattern is a list of durations (events to skip over), which will be looped over the returned events
;; provided function should take event as argument (so if you need player etc., figure it out)
(defun process-events (sc player pattern  function &optional (attacks t) (start-bar 1)  (end-bar nil))
  (next-event sc player attacks start-bar);initialise slurping
  (loop
     for ne = (next-event sc player attacks nil end-bar)
     while ne
     for i from 0
     with patt-pointer = 0
     with next-e = (nth patt-pointer pattern)
     do
       (when (equalp i next-e)
	 (funcall function ne)
	 (setf patt-pointer (mod (1+ patt-pointer) (length pattern)))
	 (incf next-e (nth patt-pointer pattern)))))

The point of the pattern thing is that I can use it to apply arbitrarily complex patterns on which events to select for the given player, e.g.

 
 (loop for ins in '(p1 p2 p3 p4 p5)
     for patt in '((5) (15 10) (10 5 10) (5 10 10) (5 20))
     do (process-events opus ins patt 
			#'(lambda (ne) (setf (amplitude ne) (min 1 (* 2 (amplitude ne))))) nil))

Which will select every 5th event in p1, every 5th then every 10th (then the next 5th etc.) for p2, and so on.

Marcin was asking about initialising set palettes from…

Marcin was asking about initialising set-palettes from MIDI note numbers instead of note names. The set-palette init method doesn’t (yet) accommodate MIDI numbers but this works fine for creating a set-palette ‘by hand’ via the add method:

(let* ((sp (make-set-palette 'empty nil)))
  (add (make-sc-set '(cs3 d4) :id 1) sp)
  (add (make-sc-set (loop for m in '(10 20 30 40 50 60) collect
			 (midi-to-note m))
		    :id 2)
       sp)
  sp)

Dan’s advice is to keep Disklavier velocities to…

Dan’s advice is to keep Disklavier velocities to within the 30 to 90 range. I’ve just implemented the rescale function to help with this (see below) so if you have events with amplitudes between 0.0 and 1.0 then the following would map those to velocities of 30 to 90:

(midi-play +jitterbug+ :force-velocity
	   #'(lambda (event) 
	       (round (rescale (amplitude event) 0 1 30 90))))

;;; ****f* utilities/rescale
;;; DATE
;;; June 8th 2016, Edinburgh
;;; 
;;; DESCRIPTION
;;; Given a value within an original range, return its value withing a new range
;;; 
;;; ARGUMENTS
;;; - the value we want to rescale
;;; - the original minimum
;;; - the original maximum
;;; - the new minimum
;;; - the new maximum
;;; 
;;; RETURN VALUE
;;; The value within the new range (a number)
;;; 
;;; EXAMPLE
#|
(rescale .5 0 1 0 100)
==> 50.0
|#
;;; SYNOPSIS
(defun rescale (val min max new-min new-max)
;;; ****
  (when (or (>= min max)
            (>= new-min new-max))
    (error "utilities::rescale: argument 2 (~a) must be < argument 3 (~a) ~
            ~%and sim. for argument 4 (~a) and 5 (~a)" min max new-min new-max))
  (unless (and (>= val min)
               (<= val max))
    (error "utilities::rescale: first argument (~a) must be within original ~
            range (~a to ~a)" val min max))
  (let* ((range1 (float (- max min)))
         (range2 (float (- new-max new-min)))
         (prop (float (/ (- val min) range1))))
    (+ new-min (* prop range2))))

rthm-seq-bar::get-nth-event: index = -1!

I’m having weirdness with sc-force-rest2, which throws the error above when I call it from this loop

 (loop
    ;;get instruments
    for ins in '(p1 p2 p3 p4 p5)
    ;;different numbers of cycles over activity curve for each part
    for cycles in '(1 2 13 2 15)
    ;;get total notes for scaling activity curve
    for notes =  (total-notes (get-data ins (ensemble opus)))
     do
       (next-event opus ins t 1);start slurping events
       (loop
	  for ne = (next-event opus ins t)
	  for i from 1
	  ;;scaled activity envelope 
	  with  activity = (make-ale (genal) (floor notes cycles))
	  while ne
	  do 
	    (when (not (active activity))
	      (sc-force-rest2 opus (bar-num ne) (bar-pos ne) ins)))))

Backtrace:
0: ((:METHOD GET-NTH-EVENT (T RTHM-SEQ-BAR)) -1 ..) [fast-method]
1: ((:METHOD SC-FORCE-REST2 (SLIPPERY-CHICKEN T T T)) ..) [fast-method]

How do I add a bar to the…

How do I add a bar to the end of a rthm-chain? The add-bar method returns an error.

(let* ((rch (make-rthm-chain
	     'test-rch 23
	     '((((e) e)			; 4 in total
		(- s (s) (s) s -)
		({ 3 (te) - te te - })
		((e.) s))
	       (({ 3 (te) te (te) })	; what we transition to
		({ 3 - te (te) te - })
		({ 3 (te) - te te - })
		({ 3 (te) (te) te })))
	     '((((q q)			; the 2/4 bars: 5 total
		 ((q) q)
		 ((q) q)
		 ((q) (s) e.)
		 (- e e - (e) e))
		(({ 3 te+te te+te te+te }) ; what we transition to
		 (q - s e. -)
		 (q (s) e.)
		 (q (s) - s e -)
		 ({ 3 te+te te+te - te te - })))
	       ((((e.) s (e) e (s) e.)	; the 3/4 bars: 4 total
		 (- e e - (e) e (q))
		 (- e. s - - +e e - (q))
		 (q (e.) s (q)))
		(({ 3 (te) (te) te+te te+te } (q)) ; what we transition to
		 (- e. s - (q) (s) - s e -)
		 ({ 3 te+te te } (q) q)
		 ({ 3 - te te te - } (e) e { 3 (te) (te) te }))))
	     :players '(pno pnol)
	     :slow-fibonacci t
	     :activity-curve '(0 1 100 10)
	     :harmonic-rthm-curve '(0 1 100 3)
	     :do-sticking t
	     :do-sticking-curve '(0 1 25 0 50 1 75 0 100 1)
	     :sticking-curve '(0 0 100 10)
	     :sticking-repeats '(3 5 7 11 2 7 5 3 13)
	     :sticking-rthms '(e s. 32 e.)
	     :split-data '(4 7))))

  (create-psps (palette rch))
  
  (make-slippery-chicken  
   '+mini+ 
   :title "Your Title Here" 
   :composer "Your Name Here"
   :ensemble '(((pno (piano :midi-channel 1))
		(pnol (piano-lh :midi-channel 1))))
   :staff-groupings '(2)
   :tempo-map '((1 (q 60)))
   :set-palette `((set1 ((b2 a4 d5 e5 a5 d6))) 
		  (set2 ((b2 fs3 d4 e4 a4 d5 e5 a5 d6))))
   :set-map `((1 ,(loop for i below (num-rthm-seqs rch)
		     collect (if (evenp i) 'set1 'set2))))
   :rthm-seq-palette (palette rch)
   :rthm-seq-map rch)

  (add-bar rch (make-rthm-seq-bar '((5 8) e e+32 s. +q)))

;;; Output
  (midi-play +mini+)			; midi file
  (cmn-display +mini+)			; score display
					;(lp-display +mini+) ; lilypond display
  )

Some of you will already know the rm…

Some of you will already know the rm-repeated-pitches method. This finds repeating pitches in any or all voices of a slippery-chicken object and replaces them with other pitches from the current set. Of course what this is doing is ironing out some of the problems inherent to algorithmic pitch selection, and slippery chicken’s in particular.

For the Goldsmith’s project next month I’ve developed a related method called add-auxiliary-notes. This tries to deal with some of the static pitch structures that arise out of limited but extended harmonic fields. It analyses slippery-chicken voices phrase by phrase to find the most frequently used notes, then changes some of them (according to an activity-levels object) to auxiliary notes (up a semitone by default but any and varied intervals are possible). It’s already available on the SVN server so those of you who want it will find it along with documentation in the file slippery-chicken-edit.lsp after doing (update-app-src …)

If you who read facebook slippery chicken group…

pedal

If you who read facebook slippery chicken group posts you may remember this one about pedalling:

“Those of you working on piano pieces: as of yesterday, if you add one of the three pedal marks ‘ped, ‘ped-up, or ‘ped^ you’ll get the correct controller information written to your MIDI files for free—in other words, during MIDI playback the sustain pedal should work.

If you want to set the controller message of an event yourself, just e.g.
(push '(1 64 127) (midi-control-changes event))
where the first 3-element list there is (midi-channel controller-number value). 64 is the sustain pedal controller number; 127 will depress and 0 will release.

If you need this functionality do (update-app-src …)”

Just wanted to update this to mention that this will also work with the ‘uc and ‘tc marks (una corda / tre corde) and the ‘sost and ‘sost-up marks (middle / sostenuto pedal), i.e. not only will the pedal marking appear in the score but the MIDI controller data will be written when you call midi-play.

Some of you might be interested in the…

Some of you might be interested in the following bit of code to get bass notes to play louder (not at all Fletcher-Munson curve correction but that could be implemented too by using the a-weighting function).

:force-velocity can either be a fixed number or a function. If the latter then each consecutive event is passed to the function automatically by midi-play. Below we subtract the degree (equivalent to MIDI note number when using the chromatic scale) from 127 to make lower notes louder than higher notes, but we do restrict this to between 70 and 110, so there’ll be a hard cut-off—notes above MIDI 55 will have a velocity of 70 and those < 18 will have 110. Note that we do randomise/humanise the velocities by 10% for a less mechanical feeling (assuming your synth/sampler will distinguish significantly there); also that get-degree will return an average for chords.

(midi-play +jitterbug+ :force-velocity
           #'(lambda (event)
               (let* ((d (get-degree event :average t))
                      (v (when d
                           (round
                            (randomise 
                             (min 110 (max 70 (- 127 d)))
                             10)))))
                 (format t “~&~a –> ~a” d v)
                 v)))