Jay Coskey
jay<dot>coskey {at} gmail<dot>com
2016-05-04
reveal.js tips: Use n or <space> to advance to the next slide. Use p to return to the previous slide. Use <Esc> to toggle an overview of all slides. Use ? to see a list of all keyboard commands. Use s to see the speaker notes.
"To play the wrong note is insignificant; to play without passion is inexcusable."
- Beethoven
Music runs deep through the human psyche:
Traditional training vs. software
Any room for non-humans?
|
Happy Birthday: original & modified In The Mood (bars 5-8): original & swing |
Examples: "Happy Birthday to You", "Greensleeves", "Yellow Submarine"
(*) Here the term "tonal music" refers to any music consisting of clearly recognizable tones. It doesn't necessarily indicate a "tonal focus", such as D minor. The term "pitched music" might be more accurate, but perhaps also more confusing to some. |
MUSIC-N | 1957 | A very early music DSL, which gave rise to many others. |
Euterpea | 2012 | Music DSL in Haskell spanning tonal music and soundscapes, composition and analysis. |
Csound | 1986 | RTperformance, sound synth, algorithmic composition, acoustic research |
Pure Data (Pd) | 1990s | A graphical language for RT audio+video synthesis, like Max/MSP in the “Patcher” family |
ChucK | 2003 | RT synthesis, live coding, pedagogy, acoustic research, algo composition |
SuperCollider (SC) | 1996 | Smalltalk-inspired language.
RT synthesis, live coding, algorithmic composition, acoustic research
|
Strasheela | 2004-2012 | Constraint-based composition, based on the Oz language |
\version "2.18.2" \header { title = "Happy Birthday to You" } global = { \key c \major \time 3/4 } right = \relative c'' {\global g8 g8 a4 g4 c4 b2 } left = \relative c {\global r4 \chordmode{c,2} r4 <d f g>2} \score { \new PianoStaff \with {instrumentName = "Piano"} << \new Staff = "right" \with {midiInstrument = "acoustic grand"} \right \new Staff = "left" \with {midiInstrument = "acoustic grand"} {\clef bass \left} >> \layout { } \midi {\tempo 4=100} }
<score-partwise> <== Show the 1st part, then 2nd, etc. <work> <work-title>Happy Birthday to You</work-title> </work> ... <part-list>...<part-name>Piano</part-name>...</part-list> <part id="P1"> <measure number="1" width="273.03"> ... <note default-x="78.29" default-y="-30.00"> <== Just showing the first note here <pitch> <step>G</step> <octave>4</octave> </pitch> <duration>1</duration> <voice>1</voice> <type>eighth</type> <stem>up</stem> <staff>1</staff> <beam number="1">begin</beam> </note> ... </measure> ... </part> </score-partwise>
LilyPond is useful for printing beautiful scores.
\header { title = "Mary Had a Little Lamb" } song = \relative c' { \clef treble \key c \major \time 4/4 e4 d c d e e e2 d4 d d2 e4 e e2 e4 d c d e e e c d d e d c2 r2 } \score { \new Staff \song }
T:Mary had a little lamb M:C % meter L:1/4 % basic note length K:F % key AGFG|AAA2|GGG2|AAA2| AGFG|AAAA|GGAG|F4|]
"^" / "_" | sharp / flat (moves note up/down one semitone) |
"<apostrophe>" / "<comma>" | increments / decrements octave for given note |
"|" (pipe) | Bar line. (Lots of variations available.) |
"[", "]" | Grouping of notes in chord |
A2, A3, A4, A5, A6, etc. | Multiples of the default length of an 'A' note; |
A/2, A/3, A/4, A/5, A/6, etc. | Fractions of the default length of an 'A' note; |
piano: o3 g8 a b > c d e f+ g | a b > c d e f+ g4 g8 f+ e d c < b a g | f+ e d c < b a g4 << g1/>g/>g/b/>d/g
"+" / "-" | sharp / flat (moves note up/down one semitone) |
o | set default octave |
">" / "<" | increment / decrement default octave |
"|" (pipe) | For readability only; ignored by the compiler |
"/" | Separator for notes in chord |
1, 2, 4, etc. | whole, 1/2, 1/4, etc. (as in LilyPond) |
<periods> | Dotted notes (as in LilyPond) |
% ghci ghci> import Euterpea ghci> play $ c 4 qn -- Play a quarter note, with sensible defaults ghci> import HSoM ghci> :load HSoM.Examples.MUIExamples2 ghci> import HSoM.Examples.MUIExamples2 ghci> bifurcate -- GUI app/test that lives in MUIExamples2
data Primitive = Note Dur Pitch | Rest DurType "Music" consists of Primitive events: glued together
data Music a = Prim (Primitive a) -- primitive value | Music a :+: Music a -- sequential composition | Music a :=: Music a -- parallel composition | Modify Control (Music a) -- modifierSo the different constructors of Music are Prim, (:+:), (:=:), and Modify.
Music ==> to Music1 (non-parameterized) ==> [MEvent] ==> [MidiMessage]The final MidiMessage list is sent to a MIDI output.
import Euterpea c :: Octave -> Dur -> Music Pitch play $ c 4 qn mapM (\octave -> play $ c octave qn) [0 .. 8] -- Play C notes.Here is a slightly more complex: a basic version of Frère Jacques (from HSoM).
twice m = m :+: m fj1, fj2, fj3, fj4, fjNotes, fj :: Music Pitch fj1 = c 4 qn :+: d 4 qn :+: e 4 qn :+: c 4 qn fj2 = e 4 qn :+: f 4 qn :+: g 4 hn fj3 = g 4 en :+: a 4 en :+: g 4 en :+: f 4 en :+: e 4 qn :+: c 4 qn fj4 = c 4 qn :+: g 3 qn :+: c 4 hn fjNotes = twice fj1 :+: twice fj2 :+: twice fj3 :+: twice fj4 fj = Modify (Tempo 4) $ Modify (Instrument AcousticGrandPiano) fjNotes play :: Performable a => Music a -> IO () play fj
data Beat = Beat { bDur :: Dur, bHeard :: Int, bAcc :: Int } type Beats = [Beat] infixr 6 .+. infixr 7 .*. (.*.) n bs = concat $ replicate n bs (.+.) bs1 bs2 = bs1 ++ bs2 beats2m :: PercussionSound -> Beats -> Music Pitch beats2m percInstr [] = rest 0 beats2m percInstr (Beat { bDur=d, bHeard=h, bAcc=a } : beats) = m0 :+: (beats2m percInstr beats) where m = Modify (Instrument Percussion) $ perc percInstr d m0 = case (h,a) of (0, _) -> rest d -- Silent "beat" (1, 0) -> m -- Unaccented beat (1, 1) -> Modify (Phrase [Dyn (Accent 1.5)]) m instrB = AcousticBassDrum mkBeat d h a = [Beat { bDur = d, bHeard = h, bAcc = a }] qns = mkBeat qn 0 0; ens = mkBeat en 0 0; enb = mkBeat en 1 0; snb = mkBeat sn 1 0
amen1b = beats2m instrB $ 2.*.enb .+. qns .+. ens .+. 2.*.snb .+. qns
playA :: (ToMusic1 a, Control.DeepSeq.NFData a) => PMap Note1 -> Context Note1 -> Music a -> IO()The Player determines how to interpret
data Context a = Context { cTime :: PTime, cPlayer :: Player a , cInst :: InstrumentName, cDur :: DurT , cPch :: AbsPitch, cVol :: Volume , cKey :: (PitchClass, Mode) } data Player a = MkPlayer { pName :: PlayerName -- ID for selection , playNote :: NoteFun a -- Interp. notes , interpPhrase :: PhraseFun a -- Interp. phrases -- , notatePlayer :: NotateFun -- Removed. }
myPasHandler :: PhraseAttribute -> Performance -> Performance myPasHandler (Dyn (Crescendo x)) pf = boostVolsBy x pf where t0 = eTime (head performance) perfDur :: Dur perfDur = sum $ map eDur performance propTime :: PTime -> Rational propTime t = (t - t0) / perfDur propVolDelta :: PTime -> Rational propVolDelta t = x * (propTime t) newVol t v = round((1 + (propVolDelta t)) * (fromIntegral v)) boostMEventVol (e@MEvent {eTime = t, eVol = v}) = e { eVol = trace ("Vol=" ++ show (newVol t v)) (newVol t v) } boostVolsBy :: Rational -> [MEvent] -> [MEvent] boostVolsBy x pf = map boostMEventVol pf myPasHandler pa pf = defPasHandler pa pf
(note1 :=: (note2 :=: (note3 :=: note4)))Simplified internal representation can make later extraction of the real structure expensive.
(>>>) :: SF a b -> SF b c -> SF a c -- Left-to-right composition (<<<) :: SF b c -> SF a b -> SF a c -- Right-to-left composition z <- sigFunc -< (x, y) -- Signal function
{-# LANGUAGE Arrows #-} module Euterpea.Examples.SigFuns where import Euterpea import Control.Arrow ((>>>),(<<<),arr) s4 :: Clock c => SigFun c () Double s4 = proc () -> do f0 <- oscFixed 440 -< () f1 <- oscFixed 880 -< () f2 <- oscFixed 1320 -< () outA -< (f0 + 0.5*f1 + 0.33*f2) / 1.83 vibrato :: Clock c => Double -> Double -> SigFun c Double Double vibrato vfrq dep = proc afrq -> do vib <- osc tab1 0 -< vfrq aud <- osc tab2 0 -< afrq + vib * dep outA -< aud -- type a b = SigFun AudRate a b s5 :: AudSF () Double s5 = constA 1000 >>> vibrato 5 20
data Instrument Name = AcousticGrandPiano -- See p. 35 of HSoM | BrightAcousticPiano | ... | Custom String deriving (Show, Eq, Ord)This can be used as follows (see Chapter 19 of HSoM)
myMandolinName :: InstrumentName -- Or myFlugelhorn or myUkulele myMandolinName = Custom "My Mandolin" type Instr a = Dur -> AbsPitch -> Volume -> [Double] -> aThis is commonly used with the type variable a being "Instr (AudSF () Double)".
myMandolin :: Instr (AudSF () Double) -- Monaural: Could choose stereo instead. myMandolin dur ap vol [vfrq, dep] = proc () -> do vib <- osc tab1 () -< vfrq aud <- osc tab2 () -< apToHz ap + vib * dep outA -< aud
type InstrMap a = [(InstrumentName, Instr a)] myInstrMap :: InstrMap (AudSF () Double) myInstrMap = [(myMandolinName, myMandolin)]
renderSF :: (Performable a, AudioSample b, Clock c) => Music a -> InstrMap (SigFun p () b) -> (Double, SigFun p () b)
mandolinMelody = instrument myMandolinName $ ...music... (dur, sigFun) = renderSF mandolinMelody myInstrMap main = outFile "mandolinMelody.wav" dur sigFun
MTrait = MTInstrument IntrumentName | MTBars1 Int -- Begin | MTBars2 Int Int -- Begin, End mFilterBy :: [MTrait] -> Music a -> Music a
MAspect = MARhythm -- Presence of notes, but not freq/volume | MAFreq -- Accidentals, but no other annotations | MAVolume -- Dynamics only (accents/cresc/dim/fp, etc.) | MAFreqVolume -- Both frequency and volume mMatchCommon :: MAspect -> Music a -> Music a -> [Music a] mMatchDuplicate :: MAspect -> Music a -> [Music a] mDiff :: MAspect -> Bool -> Music a -> Music a -> Music a -- Bool=addBeats
MDistCost = | MDCAddition (Pitch -> Dur -> Float) | MDCDeletion (Pitch -> Dur -> Float) | MDCInstrument (InstrumentName -> InstrumentName -> Float) | MDCDuration (Dur -> Dur -> Float) | MDCPitch (Pitch -> Pitch -> Float) mMelodyDist :: MAspect -> [MDistanceCost] -> Music a -> Music a -> Float -- mDist: How to make this perceptually relevant?
Csound music uses:
Different (local) variables have different refresh rates
Local variable names begin with the letter p, i, k, or a.
Csound has an IDE (CsoundQt), & a composition environment (Blue). |
|
<CsoundSynthesizer> <CsOptions> </CsOptions> <CsInstruments> <== The "orchestra" section sr = 44100 <== Sample rate (Hz) for audio signals & vars ksmps = 128 <== Sample rate per control-block, e.g., mouse nchnls = 2 <== # of audio channels. Two for stereo. 0dbfs = 1 <== Max output in dBs before clipping instr 2 kFreq expon 100, 5, 1000 ; kFreq set to the output of expon(ential) aOut oscili 0.2, kFreq, 1 ; aOut set to the output of 'oscil' outvalue "freqsweep", kFreq outs aOut, aOut endin </CsInstruments> <CsScore> f 1 0 1024 10 1 ; this function table contains the sine information i 2 0 5 ; the instrument is called at t=0 & plays for 5 sec. e </CsScore> </CsoundSynthesizer>
instr 1 <== This takes 2 extra params: amp & freq. aSine poscil3 p4, p5, 1; <== p4 = 4th param; p5 = 5th param outs aSine, aSine <== One output for each channel endin
instr 1 iGreetingCount = 0 loop: <== Any label can be used here iGreetingCount = iGreetingCount + 1 prints "Hello world #%i\n", iGreetingCount if (iGreetingCount < 5) igoto loop endin
<<< "Hello, sine wave" >>> SinOsc s => dac; 0.6 => s.gain; 220 => s.freq; 5::second => now;In this script:
// Feedback setup adc => Gain g => dac; g => Gain feedback => DelayL delay => g; 0.75::second => delay.max => delay.delay; 0.5 => feedback.gain; // Note: gain < 1. 0.75 => delay.gain; while (true) 1::second => now;
// Karplus-Strong model of a plucked string Impulse imp => Delay plucked => dac; plucked => plucked; // Feedback 441.0::samp => plucked.delay; // Sample rate 0.98 => plucked.gain; // Round-trip gain < 1 1.0 => imp.next; // "Pluck" impulse 5.0::second => now; // The string resonates
15.squared; // Evaluates to 225. [45, 13, 10].sort; // Evaluates to [10, 13, 45] 5 pow: 8; // Same as 5.pow(8) 10.do({ "Hello world".postln }); // Writes to the "Post" pane. var factorial = { | n | var result = 1; n do: { | i | result = result * (i + 1) }; result; }; factorial.(4).postln; // Output: 24Caution: Binary operators are evaluated in left-to-right order, so 1 + 3 * 5 yields 20, not 16.
s.bootwhere the variable "s" is reserved to represent the server.
{ SinOsc.ar(440, phase: 0, mul: 0.5, add: 0) }.play; // Keyword argsor
{ SinOsc.ar(440, 0, 0.5, 0) }.play; // Positional argsor just
{ SinOsc.ar(440) }.play; // Omitting arguments with default valueswhere "ar" is the "audio rate" constructor of the SinOsc class.
Pseq([0,1,2,3,4,5], 3, 2) <== [2,3,4, 2,3,4, 2,3,4]
Prand([1,2,3,4,5,6], 3) <== 3 random rolls of a d6
Same as Prand, but selection without replacement.
Pbind(*[dur: 0.2/2 freq: Pseq([220, 440, 880]) )
Pchain( Pbind( \degree, Pseq([1, 2, 3], inf) ) , (detune: [0, 4]) ).trace.play;
{ var freq, latchrate, index, ratio, env, speed = 9; speed = MouseX.kr(2, 20); latchrate = speed * 1.61803399; index = Latch.kr( LFSaw.kr(latchrate, mul: 4, add: 8), Impulse.kr(speed) ); freq = Latch.kr( LFSaw.kr(latchrate, mul: 36, add: 60), Impulse.kr(speed) ).round(1).midicps; ratio = 2.01; env = EnvGen.kr(Env.perc(0, 2/speed), gate: Impulse.kr(speed)); Out.ar(0, PMOsc.ar([freq, freq * 1.5], [freq * ratio, freq * 1.5 * ratio], index, mul: env * 0.5) )}.play
(definst metallia [freq 440 dur 1 volume 1.0] (-> (sin-osc freq (sin-osc 1)) (+ (sin-osc (* 1/2 freq) (sin-osc 1/3))) (clip2 (mul-add (saw 1/4) 0.2 0.7)) (* (env-gen (adsr 0.03 0.6 0.3) (line:kr 1 0 dur) :action FREE)) (* 1/4 volume))) (defn minor [chord] (update-in chord [:iii] (scale/from -1/2))) (def melody (->> (phrase [2/3 13/3 5/3 9/3 1/3 2/3 13/3 2/3 13/3] [ 1 2 1 0 0 1 2 3 0]) (where :pitch scale/raise) (where :part (is :melody))))
See Overtone examples for more sample code. Note the three distinct functions above:
Faust | Haskell/Arrow | Meaning |
---|---|---|
f ~ g | <See wiki page> | Recursive composition |
f , g | f *** g | Parallel composition |
f : g | f >>> g | Sequential composition |
f <: g | f >> ^h >>> g | Split composition (with appropriate function h) |
f :> g | f >> ^h >>> g | Merge composition (with appropriate function h) |
proc {RestrictMelodicInterval Note1} Note2 = {Note1 getTimeAspectPredecessor($)} in 7 >=: {FD.distance {Note1 getPitch($)} {Note2 getPitch($)}} endAnd here is this rule being applied:
{MyScore forAll(RestrictMelodicInterval test:fun {$ X} {X isNote($)} andthen {X hasTimeAspectPredecessor($)} end)} }A typical Strasheela program can use hundreds of variables and constraints.
Music & Music Theory
Music Programming & Analysis
Abjad | Python | Lib | Writing tonal music |
Ableton Live | M/P/L | DAW | VST (Virt. Studio Tech.) plugins |
Audacity | M/P/L | App | Plugins are written in a Lispy lang called Nyquist |
Csound | M/P/L | Lang | Csound API; Plugin devel w/ Cabbage & JUCE |
Cubase | Mac/PC | DAW | Supports VST plugins |
Extempore (Schemey) | Mac | Lang | Uses LLVM for live coding, and uses IPC. |
Faust | M/P/L | Lang | Purely functional DSL for signal processing. |
Garageband | Mac | App | Audio Unit extensions |
Humdrum Analysis Toolkit | M/P/L | Toolkit | Flexible with 60+ inter-related tools |
Impromptu (Schemey) | Mac | Lang | Formerly the core of Extempore. Focus on live coding. |
Java Sound API | Java | API | Emphasizes low-level control. Compare with the higher-level JMF. |
Logic Pro | Mac | DAW | JavaScript plugins; Audio Unit extensions |
Max(& Max/MSP) | Mac/PC | Lang | The "lingua franca" of interactive musical performance software. |
midi-dot-net | .NET | Lib | Access to MIDI devices |
MuseScore | M/P/L | App | JavaScript plugins |
music21 | Python | Lib | Computational musicology; composition |
NAudio | .NET | Lib | Audio & MIDI |
Nyquist (Lispy) | M/P/L | Lang | Sound effect plugins work in Audacity |
OpenMusic (Lispy) | M/P/L | Lang | Visual programming language |
Processing | M/P/L | Lang | Java-based educational multimedia programming lang |
Pure Data | M/P/L | Lang | Plugin frameworks: plugin~/ ladspa~/ dssi~. Externals create new primitives. |
Reaktor | Mac/PC | Synth | A modular synthesizer |
Reaper | M/P/L | DAW | Plugins and extensions |
StaffPad | PC | App | Imports/exports MusicXML and MIDI |
SuperCollider | M/P/L | Lang | "Quarks" extend lang; plugins extend server |
TiMidity++ | PC/Lin | Synth | |
TuxGuitar | M/P/L | Editor | Tablature editor, but also supports editing treble staff music |
Language/Library | Considerations |
---|---|
Euterpea |
|
Csound |
|
Chuck |
|
Pure Data | If it's important to choose a visual programming language |
SuperCollider |
|
Strasheela | Support for constraint-based composition |