module Exercise06 where import Data.List (transpose) import Effects ( addEffects, clip, addGain ) import Types sinPeriod :: Signal sinPeriod d = sin (2*pi*d) sqwPeriod :: Signal sqwPeriod d | d == 0.5 = 0 | d < 0.5 = -1 | otherwise = 1 sawPeriod :: Signal sawPeriod d = 2*d-1 triPeriod :: Signal triPeriod d | d < 0.5 = 4*d-1 | otherwise = -4*d+3 silence :: Signal silence = undefined -- 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 signal ad semitone duration = adsr ad duration fpshift where t = 1 / f semitone fpshift = \x -> fp (x / t) -- a new signal with the right periode fp = \x -> signal (x - 1 - fromInteger (floor (x - 1))) -- repeat the input signal within the duration adsr :: ADSR -> Seconds -> Signal -> Signal adsr (attack, decay, sustain, release) duration signal x | x < attack = sampl * x / attack | x < attack + decay = sampl * (sus * x + 1 - attack * sus) | x < duration - release = sustain * sampl | otherwise = sampl * ((-1) * x * sustain / release + duration * sustain / release) where sampl = signal x sus = (sustain -1) / decay mix :: [SampledSignal] -> SampledSignal mix xs = let l = length xs sg = map sum (transpose xs) in map (\a -> (a::Double) / fromIntegral l) sg {-WETT-} -- you can add new oscillators here piano :: Oscillator piano = osc sawPeriod (0.01, 0.1, 0.7, 0.2) 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) harmonica :: Oscillator harmonica = osc sawPeriod (0.98, 0.89, 0.02, 0.83) electronic :: Oscillator electronic = osc sawPeriod (0.98, 0.02, 8, 0.83) eightBit :: Oscillator eightBit = osc sqwPeriod (0.01, 0.09, 0.01, 0.01) -- you can add more effects here chorusEffect :: DSPEffect chorusEffect s = [x + 0.75 * delay x | x <- s] where delay x = harmonica (-92) 0.3 x flanger :: DSPEffect flanger s = [x + 0.75 * delay x | x <- s] where delay x = 0.1 * eightBit (-160) 0.1 x amplitude :: DSPEffect amplitude s = [1.8 * x | x <- s] noise :: DSPEffect noise s = [3 * (x ** 2) - 2 * (x ** 3) | x <- s] -- mix the signals with some volume mixTracks :: [SampledSignal] -> [Double] -> SampledSignal mixTracks trks vols = mix $ zipWith (\trk vol -> map (* vol) trk) trks vols {-MCCOMMENT: stack run synth star.mid star.wav-} -- 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 [bass, electronic] 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 = addEffects [chorusEffect, addGain 1, noise, flanger] audioNoEffects {-TTEW-}