Created
October 20, 2017 12:16
-
-
Save pjagielski/54195fa1ac9c753171bd0bbca6aa5942 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
badum! tssss....?