An event is a timed musical occurrence: either a tone played at a frequency or a period of silence. Both variants store a start time and duration. section 10.3.1
A Haskell record-based definition can distinguish the two cases while sharing timing fields:
- Tone stores freq, start, and duration. section 10.3.1
- Silence stores start and duration without a frequency. section 10.3.1
- isTone and isSilence identify the variant, while end computes start plus duration. section 10.3.1
- Shared record fields generate selectors that work for both constructors; freq is partial and fails on Silence. section 10.3.1
Turning events into sound
An oscillator interprets an event as a Signal: Silence produces silence for its duration, while Tone applies a waveform and ADSR parameters to the specified frequency and duration. For example, Tone 440 0 1 can be sent to several oscillators and written as a WAV signal. section 10.3.2
Listing 10.9 A function to apply an ADSR envelope to a signal
module Composition.Performance where
import Util.Types
data Event
= Tone {freq :: Hz, start :: Seconds,
duration :: Seconds} #1
| Silence {start :: Seconds, duration :: Seconds} #2
deriving (Show) #3
isTone :: Event -> Bool #4
isTone Tone {} = True
isTone _ = False
isSilence :: Event -> Bool #5
isSilence Silence {} = True
isSilence _ = False
end :: Event -> Seconds #6
end e = start e + duration e
Use the complete definition to inspect the constructors, variant predicates, shared timing fields, and end calculation. section 10.3.1
Listing 11.3 A function to convert structures of notes and pauses to playable events
module Composition.Performance where
...
import Composition.Note
import Composition.Notelength
import Composition.Pitch
import Data.List (foldl')
...
structureToPerformance ::
TempoInfo ->
Seconds ->
NoteStructure ->
(Seconds, Performance)
structureToPerformance tempoInfo start structure =
case structure of
(Note length pitch) ->
let freq = toFrequency pitch #1
duration = timePerNotelength tempoInfo length #2
in (start + duration,
[Tone {freq, start, duration}]) #3
(Pause length) ->
let duration = timePerNotelength tempoInfo length #4
in (start + duration,
[Silence {start, duration}]) #5
(Group structs) -> foldl' f (start, []) structs #6
where
f (durAcc, perf) struct =
let (dur, tones) =
structureToPerformance tempoInfo start struct #7
in (max dur durAcc, perf ++ tones) #8
(Sequence structs) -> foldl' f (start, []) structs #9
where
f (durAcc, perf) struct =
let (newdur, tones) = structureToPerformance tempoInfo durAcc
struct #10
in (newdur, perf ++ tones) #11
See how notes and pauses become Tone and Silence values using tempo-derived durations and accumulated start times. section 11.2
