On Github jaycoskey / MusicProgrammingLanguages
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.
Music runs deep through the human psyche:
Traditional training vs. software
Any room for non-humans?
Examples: "Happy Birthday to You", "Greensleeves", "Yellow Submarine"
\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;
Not listed in this section:
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
% 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. The function "play" uses defaults to perform conversions:
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
C |----------------|----------------|----------------|----------X-----| R |x-x-x-x-x-x-x-x-|x-x-x-x-x-x-x-x-|x-x-x-x-x-X-x-x-|x-x-x-x-x---x-x-| S |----o--o-o--o--o|----o--o-o--o--o|----o--o-o----o-|-o--o--o-o----o-| B |o-o-------oo----|o-o-------oo----|o-o-------o-----|--oo------o-----| ->|1 + 2 + 3 + 4 + |1 + 2 + 3 + 4 + |1 + 2 + 3 + 4 + |1 + 2 + 3 + 4 + |
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.
			 }
		
			One of the uses of NFData's rnf (reduce to normal form?) is to force evaluation of lazy IO.
						type Performance = [ Event ]
			data MEvent = MEvent { eTime, eInstr, ePitch, eDur, eVol, eParams :: [ Double ] }
							deriving (Show, Eq, Ord)
			perform :: PMap a -> Context a -> Music a -> Performance  -- See p. 138 of HSoM
			type NoteFun a = Context a -> Dur -> a -> Performance
			type PhraseFun a = PMap a -> Context a -> [PhraseAttribute] -> Music a -> (Performance, DurT)
			type NotateFun a = ()
			The PMap is a map from PlayerName (String) to Player (a means of rendering sound & score).
			type PMap a = PlayerName -> Player a
			There is a similar map for channels:
			type ChannelMap = [ (InstrumentName, Channel) ]
			type ChannelMapFun = InstrumentName -> ChannelMap -> (Channel, ChannelMap)
		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] -> a
			This is commonly used with the type variable a being
			"Instr (AudSF () Double)".
			
			The handling of the [Double] parameter above is determined
			by the instrument designer.
		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?
				
			type InstrumentState = Maybe InstrumentName
filterByInstrument :: InstrumentName -> Music a -> Music a
filterByInstrument instrName mus = filterImpl instrName Nothing mus
  where
    filterImpl :: InstrumentName -> InstrumentState -> Music a -> Music a
    filterImpl instrName instrState mus =
      case mus of
        Prim p -> if (instrState == Just instrName) then mus else (rest $ dur mus)
        mus1 :+: mus2 -> line1  $ map (filterImpl instrName instrState) [mus1, mus2] 
        mus1 :=: mus2 -> chord1 $ map (filterImpl instrName instrState) [mus1, mus2]
        Modify (Tempo t) mus1 -> Modify (Tempo t) $ filterImpl instrName instrState mus1
        Modify (Instrument newName) mus1 -> Modify (Instrument newName) $ filterImpl instrName (Just newName) mus1
			type StandardNote =
  (PartT Part
    (ColorT
      (TextT
        (TremoloT
          (HarmonicT
            (SlideT
              (ArticulationT Articulation
                (DynamicT Dynamics
                  [TieT
                    Pitch]))))))))
type Music = Score StandardNote
			<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: 24
			Caution: 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 args
			or
						{ SinOsc.ar(440, 0, 0.5, 0) }.play;  // Positional args
			or just
						{ SinOsc.ar(440) }.play;  // Omitting arguments with default values
			where "ar" is the "audio rate" constructor of the SinOsc class.
			
			UGen is the superclass of all (250+) "unit generators" (basic signals).
				Some examples:
			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:
proc {RestrictMelodicInterval Note1}
    Note2 = {Note1 getTimeAspectPredecessor($)}
in
    7 >=: {FD.distance {Note1 getPitch($)} {Note2 getPitch($)}}
end
			And 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.
			Constrait order does matter.  Usually it's Timing ⇒ Melodic ⇒ Harmonic.
			A novice user should gradually increase the number of constraints to determine impact.
		Music & Music Theory