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 1 sawPeriod :: Signal sawPeriod = \x -> 2*x-1 triPeriod :: Signal triPeriod = \x -> if x < 0.5 then 4*x-1 else -4*x+3 silence :: Signal silence = \x -> 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)) -- rep :: Double -> Signal -> Signal -- rep d signal = signal . (\x -> x - (fromIntegral $ floor x)) . (d*) osc :: Signal -> ADSR -> Oscillator osc signal a = \sem sec -> adsr a sec (signal . (\x -> x - (fromIntegral $ floor x)) . ((f sem)*)) linear :: (Seconds, Sample) -> (Seconds, Sample) -> (Signal -> Signal) linear (s1, l1) (s2, l2) signal = \x -> if x >= s1 && x < s2 then (((l2-l1)*x + s2*l1 - s1*l2) / (s2-s1)) * (signal x) else signal x adsr :: ADSR -> Seconds -> Signal -> Signal adsr (attack, decay, sustain, release) duration signal = linear (0,0) (attack,1) $ linear (attack,1) (ad,sustain) $ linear (ad,sustain) (dr,sustain) $ linear (dr,sustain) (duration,0) $ signal where ad = attack + decay dr = duration - release mix :: [SampledSignal] -> SampledSignal mix ss | all null ss = [] | otherwise = (sum [if null s then 0 else head s | s <- ss] / (fromIntegral $ length ss)) : mix [if null s then [] else tail s | s <- ss] {-WETT-} -- you can add new oscillators here piano :: Oscillator piano = osc sawPeriod (0.01, 0.1, 0.7, 0.2) coolInstrument :: Oscillator coolInstrument = osc (\x -> (sinPeriod x) + (0.8 * sinPeriod (2*x)) + (0.6*sinPeriod 3*x) + (0.5*sinPeriod 4*x) + (0.2*sinPeriod 5*x) + (0.3*sinPeriod 6*x)) (0.1, 0.2, 0.9, 0.4) lead :: Oscillator lead = osc sqwPeriod (0.01, 0.2, 0.3, 0.1) bass :: Oscillator bass = osc sinPeriod (0.001, 0.2, 0.9, 0.1) -- 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 [coolInstrument, bass] audioTracks = zipWith playNotes instrs tracks audioNoEffects = mixTracks audioTracks [0.8, 0.7] 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 [clip 0.9, addGain 4] 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. -- audio = applyEffectToInterval (2, 6) audioNoEffects distortion {-TTEW-}