module Exercise06 where import Data.List import Effects import Types {-WETT-} sinPeriod :: Signal sinPeriod x = sin (x*(2*pi)) sqwPeriod :: Signal sqwPeriod 0.5 = 0 sqwPeriod x |x < 0.5 = -1 |x > 0.5 = 1 sawPeriod :: Signal sawPeriod x = 2*x - 1 triPeriod :: Signal triPeriod x |x <= 0.5 = 4*x-1 |x > 0.5 = -4*x+3 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 s a t x = adsr a x (\y -> s (y*f t - fromIntegral (floor $ y * f t))) adsr :: ADSR -> Seconds -> Signal -> Signal adsr (a, d, s, r) t g x | x < a = h * x / a | x < (a + d) = h * (x* (s - 1) / d + ((1*(a+d)-s*a)/d)) | x < t-r = h*s | x <= t = h*(x*(-s)/r + ((s*t)/r)) | otherwise = undefined where h = g x mix :: [SampledSignal] -> SampledSignal mix ss = map sum $ transpose $ map (map (\ x -> x / fromIntegral (length ss))) ss -- 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.25, 0.2, 0.3, 0.1) bass :: Oscillator bass = osc sinPeriod (0.001, 0.2, 0.9, 0.1) tri :: Oscillator tri = osc triPeriod (0.05, 3, 0.4, 1) -- you can add more effects here {-MCCOMMENT best to use with wilhelmus.mid stack run synth mid/wilhelmus.mid wilhelmus.wav (or stack run snyth "mid/"oh du froh.mid" ohdu.wav for a more seasonal variaton) -} -- 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 [tri, lead, bass] audioTracks = zipWith playNotes instrs tracks audioNoEffects = mixTracks audioTracks [0.8, 0.7, 0.6] --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, mult] 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-}