module Termtab.Audio.Playback (
durationToMicroseconds,
playNote,
playSequence,
) where
import Control.Concurrent (threadDelay)
import Termtab.Audio (AudioEngine, engineNoteOff, engineNoteOn)
import Termtab.Types
durationToMicroseconds :: Duration -> Tempo -> Int
durationToMicroseconds :: Duration -> Tempo -> Int
durationToMicroseconds Duration
dur (Tempo Int
bpm) =
let quarterUs :: Int
quarterUs = Int
60_000_000 Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
bpm
in Duration -> Int -> Int
durationMultiplier Duration
dur Int
quarterUs
durationMultiplier :: Duration -> Int -> Int
durationMultiplier :: Duration -> Int -> Int
durationMultiplier Duration
Whole Int
base = Int
base Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
4
durationMultiplier Duration
Half Int
base = Int
base Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2
durationMultiplier Duration
Quarter Int
base = Int
base
durationMultiplier Duration
Eighth Int
base = Int
base Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2
durationMultiplier Duration
Sixteenth Int
base = Int
base Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
4
durationMultiplier Duration
Thirty2nd Int
base = Int
base Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
8
durationMultiplier (Dotted Duration
d) Int
base = Duration -> Int -> Int
durationMultiplier Duration
d Int
base Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Duration -> Int -> Int
durationMultiplier Duration
d Int
base Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2
durationMultiplier (Triplet Duration
d) Int
base = Duration -> Int -> Int
durationMultiplier Duration
d Int
base Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2 Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
3
playNote :: AudioEngine -> MidiChannel -> Pitch -> Velocity -> Duration -> Tempo -> IO ()
playNote :: AudioEngine
-> MidiChannel -> Pitch -> Velocity -> Duration -> Tempo -> IO ()
playNote AudioEngine
engine MidiChannel
ch Pitch
pitch Velocity
vel Duration
dur Tempo
tempo = do
AudioEngine -> MidiChannel -> Pitch -> Velocity -> IO ()
engineNoteOn AudioEngine
engine MidiChannel
ch Pitch
pitch Velocity
vel
Int -> IO ()
threadDelay (Duration -> Tempo -> Int
durationToMicroseconds Duration
dur Tempo
tempo)
AudioEngine -> MidiChannel -> Pitch -> IO ()
engineNoteOff AudioEngine
engine MidiChannel
ch Pitch
pitch
playSequence :: AudioEngine -> MidiChannel -> Tempo -> [Beat] -> IO ()
playSequence :: AudioEngine -> MidiChannel -> Tempo -> [Beat] -> IO ()
playSequence AudioEngine
engine MidiChannel
ch Tempo
tempo = (Beat -> IO ()) -> [Beat] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Beat -> IO ()
playBeat
where
playBeat :: Beat -> IO ()
playBeat Beat
beat
| Beat -> Bool
beatIsRest Beat
beat = Int -> IO ()
threadDelay (Duration -> Tempo -> Int
durationToMicroseconds (Beat -> Duration
beatDuration Beat
beat) Tempo
tempo)
| Bool
otherwise = do
(Note -> IO ()) -> [Note] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\Note
n -> AudioEngine -> MidiChannel -> Pitch -> Velocity -> IO ()
engineNoteOn AudioEngine
engine MidiChannel
ch (Note -> Pitch
notePitch Note
n) (Note -> Velocity
noteVelocity Note
n)) (Beat -> [Note]
beatNotes Beat
beat)
Int -> IO ()
threadDelay (Duration -> Tempo -> Int
durationToMicroseconds (Beat -> Duration
beatDuration Beat
beat) Tempo
tempo)
(Note -> IO ()) -> [Note] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (AudioEngine -> MidiChannel -> Pitch -> IO ()
engineNoteOff AudioEngine
engine MidiChannel
ch (Pitch -> IO ()) -> (Note -> Pitch) -> Note -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Note -> Pitch
notePitch) (Beat -> [Note]
beatNotes Beat
beat)