91 lines
2 KiB
Haskell
91 lines
2 KiB
Haskell
|
{-# LANGUAGE NoImplicitPrelude #-}
|
||
|
{-# LANGUAGE OverloadedStrings #-}
|
||
|
|
||
|
{- Inspiration: https://www.youtube.com/watch?v=FYTZkE5BZ-0&app=desktop
|
||
|
-}
|
||
|
|
||
|
import Protolude hiding (note)
|
||
|
import qualified Data.ByteString.Lazy as B
|
||
|
import qualified Data.ByteString.Builder as B
|
||
|
import System.Process
|
||
|
import Text.Printf (printf)
|
||
|
import Data.List (zipWith3)
|
||
|
|
||
|
outputFilePath :: FilePath
|
||
|
outputFilePath = "output.bin"
|
||
|
|
||
|
sampleRate :: Float
|
||
|
sampleRate = 48000
|
||
|
|
||
|
volume :: Float
|
||
|
volume = 0.5
|
||
|
|
||
|
type Duration = Float
|
||
|
type Hz = Float
|
||
|
type Seconds = Float
|
||
|
type Pulse = Float
|
||
|
type Soundwave = [Pulse]
|
||
|
type Beats = Float
|
||
|
|
||
|
bpm :: Beats
|
||
|
bpm = 120
|
||
|
|
||
|
beatDuration :: Seconds
|
||
|
beatDuration = 60.0/bpm
|
||
|
|
||
|
standardFreq :: Hz
|
||
|
standardFreq = 440
|
||
|
|
||
|
f n = standardFreq * ((2 ** (1/12)) ** n)
|
||
|
|
||
|
major = [0,2,4,5,7,9,11,12]
|
||
|
|
||
|
freq :: Hz -> Seconds -> [Float]
|
||
|
freq hz duration = map (* volume) $ zipWith3 (\x y z -> x * y * z) attack release output
|
||
|
where
|
||
|
nbpulse = sampleRate * duration
|
||
|
attackduration = 0.05
|
||
|
attack = map (min 1) [0, (1 / (attackduration * nbpulse)).. ]
|
||
|
release = reverse (take (length output) attack)
|
||
|
output = map (sin . (* step)) [0.0 .. sampleRate * duration]
|
||
|
step = (hz * 2 * pi) / sampleRate
|
||
|
|
||
|
note n l = freq (f n) (beatDuration * l)
|
||
|
|
||
|
maintrack =
|
||
|
[ note 0 0.25
|
||
|
, note 0 0.25
|
||
|
, note 0 0.25
|
||
|
, note 0 0.25
|
||
|
, note 0 0.5
|
||
|
, note 0 0.25
|
||
|
, note 0 0.25
|
||
|
, note 0 0.25
|
||
|
, note 0 0.25
|
||
|
, note 0 0.5
|
||
|
, note 5 0.25
|
||
|
, note 5 0.25
|
||
|
, note 5 0.25
|
||
|
, note 5 0.25
|
||
|
, note 5 0.5
|
||
|
, note 3 0.25
|
||
|
, note 3 0.25
|
||
|
, note 3 0.25
|
||
|
, note 3 0.25
|
||
|
, note 3 0.5
|
||
|
, note (-2) 0.5
|
||
|
]
|
||
|
|
||
|
wave :: Soundwave
|
||
|
wave = concat (maintrack <> maintrack)
|
||
|
|
||
|
save :: FilePath -> IO ()
|
||
|
save fp =
|
||
|
B.writeFile fp $ B.toLazyByteString $ fold $ map B.floatLE wave
|
||
|
|
||
|
play :: IO ()
|
||
|
play = do
|
||
|
save outputFilePath
|
||
|
void $ runCommand $ printf "ffplay -showmode 1 -f f32le -ar %f %s" sampleRate outputFilePath
|
||
|
|