Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
(ns talk
(:require [overtone.core :refer :all]
[clojure.java.io :as io]
[clj-http.client :as http]
[clojure.test :as test]))
(def talk
{:title "Sequencing dance music with Clojure"
:author "Piotr Jagielski"
:company {:name "TouK" :what "Software house" :from "Warsaw, Poland"}
:twitter "pjagielski" :github "pjagielski"})
;; Clojure main ideas
;; REPL driven development
(defn hello-geecon [text]
(str "Hello " text "!!!"))
(comment
(hello-geecon "world")
;; Turn REPL session into test cases
(test/is
(= "Hello world!!!" (hello-geecon "world"))))
;; Model the world using built-in data structures
(comment
(->
(http/get "https://api.github.com/users/pjagielski"
; (http/get "http://localhost:5000/github.json"
{:as :json}))
; :body)
; :name)
;; -> macro shows the order of transformations
(+ 2 (:a {:a 1}))
(->
{:a 1}
:a
(+ 2)))
;; Overtone - sound synthesis library by Sam Aaron & Jeff Rose
;; Defining instrument like defining function
(definst da-funk [freq 440 dur 1.0 amp 1.0 cutoff 1700 boost 6 dist-level 0.015]
(let [env (env-gen (adsr 0.3 0.7 0.5 0.3) (line:kr 1.0 0.0 dur) :action FREE)
filter-env (+ (* freq 0.15)
(env-gen (adsr 0.5 0.3 1 0.5)
(line:kr 1.0 0.0 (/ dur 2)) :level-scale cutoff))
osc (mix [
(saw freq)
(saw (* freq 0.75))])]
(-> osc
(bpf filter-env 0.6)
(* env amp)
pan2
(clip2 dist-level)
(* boost)
distort)))
(comment
(overtone.live/volume 1.5)
;; Call as normal function to play instrument
(da-funk (midi->hz (note :G4)))
(da-funk (midi->hz (note :F4)) :dur 3/4 :cutoff 5000))
;; Leipzig by Chris Ford
(require '[leipzig.melody :refer :all])
(require '[leipzig.scale :as scale])
(require '[leipzig.live :as live])
(require '[leipzig.temperament :as temperament])
(require '[leipzig.chord :refer :all])
;; Melody as sequence of notes
(def leipzig-melody
[{:time 0 :duration 12/11 :pitch 391.99 :part :da-funk}
{:time 12/11 :duration 3/11 :pitch 349.22 :part :da-funk}
{:time 15/11 :duration 3/11 :pitch 391.99 :part :da-funk}])
;; Clojure way of polymorphism
(defmethod live/play-note :da-funk [{hertz :pitch seconds :duration amp :amp cutoff :cutoff}]
(when hertz
(da-funk :freq hertz :dur seconds :amp (or amp 1) :cutoff (or cutoff 1600))))
(comment
(live/play-note {:time 0 :duration 1 :pitch 391.99 :part :da-funk :amp 1})
(live/play leipzig-melody))
;; Scales - function composition
(def g-minor (comp scale/G scale/minor))
(comment
(find-note-name (g-minor 0))
(find-note-name (g-minor 1))
(find-note-name (g-minor 2))
(->> (range 8)
(map (comp find-note-name g-minor)))
;; Phrase - takes a sequence of note lenghts
;; and a sequence with note indexes of some scale
(->>
(phrase [2 1 1/2 1/2]
[0 1 nil 2])
(where :pitch g-minor)))
(def da-funk-phrase
(->> (phrase (concat [2] (take 12 (cycle [1/2 1/2 1/2 5/2])) [1 1])
[7 6 7 9 4 3 4 6 2 1 2 4 0 1 2])
(where :pitch (comp scale/low scale/G scale/minor))
(all :amp 0.5)))
(comment
(count da-funk-phrase))
(def da-funk-track
(->> da-funk-phrase
(all :part :da-funk)
(wherever :pitch, :pitch temperament/equal)
(tempo (bpm 110))))
(comment
(live/play da-funk-track))
;; Disclojure - live coding environment based on Leipzig and Overtone
(require '[disclojure.live :as l])
(require '[disclojure.play :as p])
(require '[disclojure.kit :as k])
(require '[disclojure.track :as t])
(require '[disclojure.sampler :as s])
(require '[disclojure.midi :as midi])
(require '[disclojure.melody :refer :all])
(require 'inst)
(reset! t/metro 110)
(l/reset-track {:beat []})
;; Playing drum patterns
(k/load-kit! (io/file "work/beats/da-funk"))
(comment
(live/play-note {:part :beat :drum :fat-kick :time 0 :amp 0.5})
(live/play-note {:part :beat :drum :snare :time 0 :amp 0.3})
(live/play-note {:part :beat :drum :close-hat :time 0 :amp 1})
(live/play (->> [{:time 0 :duration 1 :drum :fat-kick :part :beat}
{:time 1 :duration 1 :drum :fat-kick :part :beat}
{:time 1 :duration 1 :drum :snare :part :beat}
{:time 2 :duration 1 :drum :fat-kick :part :beat}
{:time 3 :duration 1 :drum :fat-kick :part :beat}
{:time 3 :duration 1 :drum :snare :part :beat}]
(all :amp 0.5)
(tempo (bpm 110)))))
(def da-funk-beats
(->>
[[(range 4) :fat-kick :amp 0.5]
[(range 1 4 2) :snare :amp 0.5]
[(sort (conj (range 1/2 4 1) 3.75)) :close-hat]]
(t/beats 4)))
(comment
(live/play
(->> (times 2 da-funk-beats)
(all :part :beat)
(tempo (bpm 110))))
(live/play
(->>
(times 4 da-funk-beats)
(with (->> da-funk-phrase
(wherever :pitch, :pitch temperament/equal)
(all :part :da-funk)))
(tempo (bpm 110)))))
;; Samples and loop support
(comment
(reset! t/metro 110))
;; Metadata in file names
(s/load-samples! (io/file "work/samples"))
(def da-funk-303
{:beats 4 :amp 0.7 :time 0 :part :sampler :sample :da-funk-303 :bpm 110})
(comment
(assoc {:a 1} :b 2 :a 3)
(live/play-note da-funk-303)
(live/play-note (assoc da-funk-303 :bpm 100))
(live/play-note (assoc da-funk-303 :cutoff 3000))
(live/play-note (assoc da-funk-303 :beats 1))
(live/play-note (assoc da-funk-303 :beats 1 :start-beat 3))
(live/play-note (assoc da-funk-303 :beats 1/4 :start-beat 1/2))
(live/play-note (assoc da-funk-303 :beats 1/4 :start-beat 1))
;; http://www.junglebreaks.co.uk/breaks.html
(live/play-note {:time 0 :part :sampler :sample :skull-snaps :bpm 110 :beats 4})
(live/play-note {:time 0 :part :sampler :sample :funky-drummer :bpm 110 :beats 4})
(live/play-note {:time 0 :part :sampler :sample :smack-beat :bpm 110 :beats 4})
(live/play-note {:time 0 :part :sampler :sample :apache :bpm 110 :beats 4})
(live/play-note {:time 0 :part :sampler :sample :giorgio-arp :bpm 110 :beats 4})
(live/stop)
(->> [
[0 :giorgio-arp 16 :amp 0.75]
; [(range 0 16 4) :skull-snaps 4 :amp 0.75]
; [(range 0 16 4) :smack-beat 4 :amp 0.5]
; [(range 0 16 4) :hotpants 4 :amp 0.5]]
[[0 8] :dhs-drum-loop-1b 8 :amp 0.5]
[[0 8] :dhs-drum-loop-2b 8 :amp 0.5]
[[0 8] :dhs-drum-loop-4c 8 :amp 0.5]]
(t/sampler)
(tempo (bpm 110))
(live/play)))
;; Many tracks: samples with melody
(def leanon-chords
[[-9 -2 0 2 4]
[-8 -1 -3 1 3]
[-7 0 2 4]
[-5 -1 2 4 5 6]
[-5 -1 2 3 4]])
(def leanon
(let [[ch1 ch2 ch3 ch4 ch5] leanon-chords]
(->> (phrase (take 20
(cycle (concat (take 9 (cycle [1/2 1/4])) [1/2])))
[ch1 nil ch1 nil ch1 nil ch2 nil ch2 nil
ch3 nil ch3 nil ch3 nil ch4 nil ch4 ch5])
(wherever :pitch, :pitch (comp scale/low scale/G scale/minor))
(all :part :plucky)
(all :amp 0.3)
(all :cutoff 500))))
(def leanon-track
(->> leanon
(wherever :pitch, :pitch temperament/equal)
(tempo (bpm 100))))
(comment
(live/play
[{:pitch 77.78174593052024, :time 0, :duration 3/10, :part :plucky, :amp 0.4}
{:pitch 155.5634918610405, :time 0, :duration 3/10, :part :plucky, :amp 0.4}
{:pitch 195.99771799087472, :time 0, :duration 3/10, :part :plucky, :amp 0.4}
{:pitch 233.08188075904496, :time 0, :duration 3/10, :part :plucky, :amp 0.4}
{:pitch 293.66476791740763, :time 0, :duration 3/10, :part :plucky, :amp 0.4}])
(live/play leanon-track)
(live/play-note {:bpm 120 :time 0 :part :sampler :sample :smack-beat :beats 8})
(live/play-note {:bpm 100 :time 0 :part :sampler :sample :smack-beat :beats 8})
(reset! t/metro 100)
(live/stop)
(live/play
(->>
(times 2 leanon-track)
(with
(->>
[
[(range 0 16 4) :smack-beat 4 :amp 1]
[0 :lean-verse-2 16 :amp 0.5]]
(t/sampler)
(tempo (bpm 100)))))))
;; Looping and song state management
(def state (atom {:a 1}))
(comment
(into {} @state)
(swap! state assoc :b 2)
(reset! state {:c 3}))
(comment
(reset! t/metro 110)
(l/reset-track {:beat []})
(k/load-kit! (io/file "work/beats/da-funk"))
(into [] @(l/track))
(do
(->>
[
[[0 1/2] :da-funk-303 1/2 :amp 0.75]
[[1 3/2] :da-funk-303 1/2 :start-beat 1 :amp 0.75]
[(range 2 3 1) :da-funk-303 1/2 :start-beat 5/2 :amp 0.75]
[[3 3.5] :da-funk-303 1/2 :start-beat 3 :amp 0.75]
[[4] :da-funk-303 4 :amp 0.7]
[0 :da-funk 8 :amp 0.5]]
(t/sampler)
(l/assoc-track :sampler))
(->>
[
; [(range 4) :fat-kick :amp 0.5]
; [(range 4) :kick :amp 0.5]
[[0 1/2 5/4 2 11/4] :fat-kick :amp 0.7]
[(range 1 4 2) :snare :amp 0.5]
[(sort (conj (range 1/2 4 1) 3.75)) :close-hat]
[[0 1/2 5/4 2 11/4] :horn :amp 0.2]]
(t/beats 4)
(times 2)
(all :part :beat)
(l/assoc-track :beat)))
(l/assoc-track :da-funk
(->> (phrase [6 1 1]
[nil 1 2])
(where :pitch (comp scale/low scale/G scale/minor))
(all :part :da-funk)
(all :amp 0.25)
(all :cutoff 1950)))
(live/jam (l/track))
(live/stop))
;; midi
(defn load-midi
([file beats part]
(load-midi file beats part 0))
([file beats part start]
(->> (midi/midi-file->notes file)
(take-beats start beats)
(fill-to-beats beats)
(all :part part)
(all :amp 1))))
(def giorgio
(load-midi "work/midi/GiorgiobyMoroder.mid" 16 :bass))
(comment
(reset! t/metro 110)
(live/play
(->>
(->> giorgio (all :part :plucky))
(with giorgio)
(with
(->> giorgio
(all :part :supersaw)
(wherever :pitch :pitch scale/high)))
(wherever :pitch :pitch temperament/equal)
(all :amp 0.2)
(with
(->>
[
[0 :giorgio-arp 16 :amp 0.5]]
; [(range 0 16 4) :skull-snaps 4 :amp 0.75]]
; [(range 0 16 4) :smack-beat 4 :amp 0.5]]
; [(range 0 16 4) :hotpants 4 :amp 0.5]]
(t/sampler)))
(tempo (bpm 110))))
(live/stop))
;; Live coding
(defn th [i j k]
[[i j k] nil])
(defn ff [notes]
(->> notes
(mapcat (fn [[i j k]] (th i j k)))))
(def shape-notes
[
[[7 4 0] [9 4 0] [7 4 0]]
[[7 5 -4] [9 5 -4] [7 5 -4]]
[[7 5 -2] [9 5 -2] [7 5 -2]]
[[8 3 -1] [7 3 -1] [6 3 -1]]])
(def shape-lead
(->>
(phrase
(cycle [1/4 1/2 1/4 1/2 1/4 1/4])
(take 24 (cycle (ff (apply concat shape-notes)))))
(wherever :pitch, :pitch (comp scale/C scale/sharp scale/minor))
(all :part :shape)
(all :amp 0.4)))
(comment
(live/play-note {:time 0 :pitch 554 :duration 1/2 :amp 0.4 :part :shape})
(l/reset-track {:beat []})
(reset! t/metro 100)
(k/load-kit! "work/beats/shape")
(live/stop)
(live/jam (l/track))
(do
(->>
[
; [0 :skull-snaps 4]
; [0 :hotpants 4]
; [0 :dhs-drum-loop-1b 4 :amp 0.5]
; [0 :dhs-drum-loop-2b 4 :amp 0.5]
; [0 :dhs-drum-loop-4c 4 :amp 0.5]
; [[1 3] :dhs-drum-loop-1b 1 :start-beat 1/2 :amp 0.5]
; [[0 3/4 2 11/4] :dhs-drum-loop-2a 1/8 :amp 0.8]
; [(range 3/2 2) :hotpants 1/2]
; [(range 3 4) :hotpants 1/2]
[0 :dhs-noise 4 :amp 0.2 :start-beat 5]]
; [0 :dhs-noise 4 :amp 0.3]]
; [0 :dhs-noise 4 :amp 0.5 :start-beat 6]]
; [0 :dhs-noise 4 :amp 0.5 :start-beat 3]]
(t/sampler)
(times 2)
(l/assoc-track :sampler))
(->>
[]
; [(range 0 4 1/4) :hh :amp 2]
; [(range 3 3.5 1/8) :hh :amp 2.5]
; [(range 1.5 2 1/8) :hh :amp 2.5]
; [[0 3/4 2 11/4] :bd2 :amp 0.7]]
; [[1.48 3.48] :clap :amp 0.3]]
; [[1.5 3.5] :snare :amp 0.5]]
; [[1 3] :snare :amp 0.5]
; [[0.99 2.99] :clap :amp 0.3]]
(t/beats 4)
(times 2)
(with (t/tap :mhm [6.5] 8 :amp 0.2))
; (with (t/tap :oay [0] 8 :amp 0.15))
(all :part :beat)
(l/assoc-track :beat)))
(do
(l/assoc-track :sampler [])
(l/assoc-track :beat [])
(l/assoc-track :808 [])
(l/assoc-track :supersaw2 [])
(l/assoc-track :supersaw [])
(l/assoc-track :shape []))
(do
(l/assoc-track :beat [])
(l/assoc-track :sampler [])
(l/assoc-track :shape shape-lead))
(do
(l/assoc-track :shape
(->> shape-lead
(all :amp 0.2)))
(l/assoc-track :808
(->> shape-lead
(wherever :pitch, :pitch scale/low)
(all :amp 0.15)
(all :cutoff 150)
(all :part :808))))
(l/assoc-track :supersaw2
(->> shape-lead
; (wherever :pitch, :pitch scale/high)
(all :amp 0.5)
(all :part :supersaw)))
(live/jam (l/track))
(live/stop))
(defn ch [root-idx]
(-> triad (root root-idx)))
(defn ph [root-idx]
(take 6 (repeat [(ch root-idx) nil])))
(def shape-chords
(->>
(phrase (cycle [1/4 1/2 1/4 1/2 1/4 1/4])
(concat (ph 0) (ph 3) (ph 4) (ph 7)))
(where :pitch (comp scale/C scale/sharp scale/minor))))
(comment
(do
(swap! p/controls assoc-in [:supersaw :cutoff] 2000)
(swap! p/controls assoc-in [:supersaw :release] 0.3))
(l/assoc-track :supersaw
(->> shape-chords
(all :amp 0.65)
(all :part :supersaw))))
(defn n-sin [x dstlo dsthi]
(let [scale (/ (- dsthi dstlo) (- 1 -1))
offset (- dstlo (* scale -1))]
(-> x (* 1/8) (Math/sin) (* scale) (+ offset))))
(defn lfo [t controls min max]
(let [value (n-sin t min max)]
(println "applying" controls "to" value)
(swap! p/controls assoc-in controls value)))
(def metro (metronome @t/metro))
(def pool (overtone.at-at/mk-pool))
(comment
(let [beat (metro)]
(doseq [x (range 0 64 1/2)]
(let [time (metro (+ beat x))]
(overtone.at-at/at time #(lfo time [:supersaw :cutoff] 2000 11000) pool)))))
@jakubnabrdalik
Copy link

jakubnabrdalik commented Oct 20, 2017

badum! tssss....?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment