module Exercise06 where import Data.List import Effects import Types sinPeriod :: Signal sinPeriod = sin . (2*pi*) sqwPeriod :: Signal sqwPeriod = \x -> (if x < 0.5 then -1 else if x > 0.5 then 1 else 0) sawPeriod :: Signal sawPeriod = (+) (-1) . (*2) triPeriod :: Signal triPeriod = \x -> (if x < 0.5 then x*4-1 else x*(-4)+3) custPeriod :: Signal custPeriod = \x -> ((sawPeriod x + sinPeriod x)/2 + 0.2) silence :: Signal silence = \_ -> 0 -- NOTE: the formula is taken from https://pages.mtu.edu/~suits/NoteFreqCalcs.html f :: Semitone -> Hz f n = 440.0 * (2 ** (fromInteger n / 12.0)) osc :: Signal -> ADSR -> Oscillator osc sig a = res where res :: Oscillator res sem dur = adsr a dur sg where frq = f sem sg = \x -> sig (x*frq - fromIntegral(floor(x*frq))) adsr :: ADSR -> Seconds -> Signal -> Signal adsr (a,d,s,r) dur sig = res where res :: Signal res x | x < a = (x*(1/a)) * (sig x) | x < a+d = ((x-a)*((s-1)/d) + 1) * (sig x) | x < (dur-r) = s * (sig x) | otherwise = ((x-dur+r) * ((-s)/r) + s) * (sig x) vadd :: Num a => [a] -> [a] -> [a] vadd xs [] = xs vadd [] xs = xs vadd (x:xs) (y:ys) = (x+y):(vadd xs ys) mixAux :: [SampledSignal] -> SampledSignal mixAux [] = [] mixAux [x] = x mixAux (x:y:xs) = mixAux ((vadd x y):xs) mix :: [SampledSignal] -> SampledSignal mix xs = let s = fromIntegral (length xs) in mixAux (map (map (/s)) xs) {-WETT-} -- you can add new oscillators here piano :: Oscillator piano = osc sawPeriod (0.1, 0.1, 0.7, 0.2) lead :: Oscillator lead = osc sqwPeriod (0.01, 0.02, 0.5, 0.1) bass :: Oscillator bass = osc sinPeriod (0.001, 0.02, 0.9, 0.1) violin :: Oscillator violin = osc sawPeriod (0.1, 0.01, 0.9, 0.03) -- you can add more effects here -- mix the signals with some volume mixTracks :: [SampledSignal] -> [Double] -> SampledSignal mixTracks trks vols = mix $ zipWith (\trk vol -> map (* vol) trk) trks vols -- here we mix it all together and apply the effects notesToSignal :: (Oscillator -> [Note] -> SampledSignal) -> [[Note]] -> SampledSignal notesToSignal playNotes tracks = audio where -- specify the instruments of each track -- try the following instruments with the mario.mid file instrs = cycle [violin, lead] audioTracks = zipWith playNotes instrs tracks audioNoEffects = mixTracks audioTracks [0.2, 0.8] --audio = audioNoEffects -- To add effects, replace the line above by the line at the end of this comment and change the list off effects to your own effects. -- Note that the effects are applied from right to left, ie. the rightmost effect is applied first. --audio = addEffects [delay 0.03] audioNoEffects -- If you want to apply an effect only to parts of the signal, you can use the function applyEffectToInterval, as shown below. -- Thereby the first argument specifies the interval to which the effect should be applied in seconds. --audio1 = addEffects [distortion] audioNoEffects audio1 = applyEffectToInterval (16.5, 40) audioNoEffects $ echo 0.4 audio2 = applyEffectToInterval (40, 56.7) audio1 $ crush --audio3 = applyEffectToInterval (40, 56.7) audio2 $ ampM triPeriod 50 audio3 = applyEffectToInterval (40, 56.7) audio2 $ ffrq [2,4,8,16] audio4 = applyEffectToInterval (56.77, 79.8) audio3 $ echo 0.4 audio5 = applyEffectToInterval (80, 113) audio4 $ ffrq [2,4,8,16] audio6 = applyEffectToInterval (0, 16) audio5 $ distortion audio7 = applyEffectToInterval (80, 113) audio6 $ ampM sinPeriod 10 audio = applyEffectToInterval (75, 113) audio7 $ distortion {-TTEW-} {-MCCOMMENT compile: stack run synth ./mid/best-song-you-ever-listened-to.mid best-song-you-ever-listened-to.wav - mid file src: https://bitmidi.com/never-gonna-give-you-up-3-mid I take no responsibility for hearing damage-}