{-# OPTIONS -fno-implicit-prelude #-} module Music.FMBassLine where import PreludeBase import NumericPrelude import qualified Synthesizer as Syn import qualified Synthesizer.Plain.Oscillator as Osci import Instruments import Sox.File import qualified Haskore.Basic.Pitch as Pitch import qualified Haskore.Melody as Melody import Haskore.Melody.Standard as StdMelody import qualified Haskore.Music as Music import Haskore.Music.Standard as StdMusic import Haskore.Performance.Player as Player import qualified Haskore.Interface.Signal.Note as Note import qualified Haskore.Interface.Signal.Write as MusicSignal import Haskore.Interface.Signal.Write(Time,Volume) import System.Random import qualified Algebra.Transcendental as Trans import qualified Algebra.Module as Module import System.Exit(ExitCode) import qualified Data.List as List ------------ The song description ------------- data MyNote = Bass ModIndex ModDepth Pitch.T | Saw Volume Vibrato Pitch.T deriving (Show, Eq, Ord) type ModIndex = Int type ModDepth = Time type ModParam = (ModIndex, ModDepth) type Vibrato = Time type NoteIon = Music.Dur -> () -> Melody.T () makeBassLine :: [NoteIon] -> [ModIndex] -> [ModDepth] -> Music.T MyNote makeBassLine mel indexes depths = line (zipWith3 (\n index depth -> Music.mapNote (\(Melody.Note _ p) -> (Bass index depth p)) (n en ())) mel indexes depths) bassPattern :: [NoteIon] bassPattern = cycle [a 0, a 0, a 0, e 1, a 0, a 0, f 1, a 0, a 0, d 1, a 0, a 0] bassLine :: Music.T MyNote bassLine = transpose (-12) (makeBassLine bassPattern (randomRs (1,4) (mkStdGen 12354)) (randomRs (0,2) (mkStdGen 35902))) makeSawLine :: [([NoteIon], [(Dur, Dur, Vibrato)])] -> Music.T MyNote makeSawLine = line . concatMap (\(chrd, params) -> map (\(durTone,durRest,vib) -> let mkNote dur vel = chord (map (\n -> Music.mapNote (\(Melody.Note _ p) -> (Saw vel vib p)) (n dur ())) chrd) in mkNote durTone 1 +:+ mkNote durRest 0.3) params) sawPattern :: [([NoteIon], [(Dur, Dur, Vibrato)])] sawPattern = let v0 = (sn,sn,0.01) v1 = (en,0,0.02) v2 = (qn,0,0.05) v3 = (qn,0,0.00) in cycle [([a 0, c 1, e 1], replicate 8 v0 ++ [v0,v0,v0,v1]), ([g 0, b 0, d 1], [v2]), ([a 0, c 1, e 1], [v3]), ([a 0, d 1, f 1], replicate 8 v0 ++ [v0,v0,v0,v1]), ([a 0, e 1, g 1], [v2]), ([a 0, d 1, f 1], [v3])] sawLine :: Music.T MyNote sawLine = makeSawLine sawPattern song :: Music.T MyNote song = bassLine =:= sawLine ----------- Configuration of the player ----------- noteToSignal :: Time -> Volume -> Pitch.Relative -> MyNote -> Note.T Volume Volume noteToSignal detune dyn trans note = let freq p = detune * Note.pitchFromStd trans p in Note.Cons (\sampleRate -> case note of Bass index depth p -> Syn.amplify (dyn * 0.3) (fmBell sampleRate depth (fromIntegral index) (freq p)) Saw vel vib p -> Syn.amplify (dyn * vel*0.4) (saw sampleRate vib 10 (freq p))) saw :: (Module.C a a, Trans.C a, RealFrac a) => a -> a -> a -> a -> [a] saw sampleRate modDepth modFreq freq = Osci.freqModSaw 0 (map (\y -> freq/sampleRate * (1+modDepth*y)) (Osci.staticSine 0.25 (modFreq/sampleRate))) -- Volume type arises from Haskore songToSignalMono :: Time -> Time -> Music.T MyNote -> [Volume] songToSignalMono sampleRate dif music = MusicSignal.fromMusic sampleRate (noteToSignal dif) Player.fancyMap (MusicSignal.contextMetro 240 qn) music stereoSignal :: Time -> [(Volume, Volume)] stereoSignal sampleRate = zip (songToSignalMono sampleRate 1.003 song) (songToSignalMono sampleRate 0.997 song) main :: IO ExitCode main = Sox.File.renderStereo "FMBassLine" 44100 (\sampleRate -> take (round (sampleRate*16)) (stereoSignal sampleRate))