module Exercise06 where import Data.List import Effects import Types import Data.Ord (comparing) sinPeriod :: Signal sinPeriod d | d < 0 = 0 | d <= 1 = sin (pi * d * 2) triPeriod :: Signal triPeriod d | d < 0 = 0 | d < 0.5 = 4 * d - 1 | d <= 1 = -4 * d + 3 sawPeriod :: Signal sawPeriod d | d < 0 = 0 | d <= 1 = 2 * d - 1 specialSawPeriod :: Signal specialSawPeriod d | d < 0 = 0 | d <= 0.5 = -2 * d + 1 | d <= 1 = 2 * d - 1 sqwPeriod :: Signal sqwPeriod d | d < 0 = 0 | d < 0.5 = -1 | d == 0.5 = 0 | d > 0.5 = 1 | d > 1 = 0 guitarNope :: Signal guitarNope dx = let normal dx = sin (pi * dx * 2) ticks dx | dx < 0.05 = normal dx * 1.6 | dx < 0.08 = 0.85 | dx < 0.13 = normal dx * 1.2 | dx < 0.19 = 0.8 | dx < 0.20 = 0.6 | dx < 0.21 = 0.9 | dx < 0.22 = 0.95 | dx < 0.23 = 1 | dx < 0.3 = normal dx | dx < 0.55 = normal (dx+0.1) * 0.8 -- nach links verschieben | dx < 0.7 = normal dx * 0.2 - 0.6 | dx <= 1 = normal dx in if dx < 0 then 0 else if dx <= 1 then ticks dx else 0 guitarHope :: Signal guitarHope dx = let normal dx = sin (pi * dx * 2) ticks dx | dx <= 1 = normal dx * specialSawPeriod dx in if dx < 0 then 0 else if dx <= 1 then ticks dx else 0 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 original env tone duration = let -- some number between 100 and 700 periodsPS = f tone periods = periodsPS * duration periodTime = 1 / periodsPS -- dx is between 0 and duration fast dx = let lastFullPeriodTime = periodTime * fromInteger (floor(dx / periodTime)) -- phase in the original -- * periodsPS to get to max 1 phase = (dx - lastFullPeriodTime) * periodsPS in original phase in adsr env duration fast adsr :: ADSR -> Seconds -> Signal -> Signal adsr (att, dec, sus, rel) sampleTime fast dx = let susEnd = sampleTime - rel attF = (1 / att) * dx -- (y2 - y1) / (x2 - x1) für die Steigung -- ((att+dec) - att) decM = (sus - 1) / dec -- bekannter Punkt (att, 1) -- 1 = decM * att + decY <=> -- -decY = decM * att - 1 decY = -decM * att + 1 decF = decM * dx + decY -- releaseFunction relM = (-sus) / (sampleTime - susEnd) -- 0 = relM * sampleTime + relY -- -relY = relM * sampleTime relY = -relM * sampleTime relF = relM * dx + relY multiplier | dx < 0 = 0 | dx < att = attF | dx < att + dec = decF | dx < susEnd = sus | dx < sampleTime = relF |otherwise = 0 in fast dx * multiplier cast :: Seconds -> Double cast d = d mix :: [SampledSignal] -> SampledSignal mix signals = let -- maybe unnecessary -- sorted = map snd $ sortBy (comparing fst) $ map (\s -> (length s, s)) signals divL amp = amp / fromIntegral (length signals) quiter = [ [ divL amp | amp <- signal ] | signal <- signals ] normalized = [ sum l | l <- transpose quiter] in normalized {-WETT-} -- you can add new oscillators here sawW :: Oscillator sawW = osc sawPeriod (0.01, 0.1, 0.7, 0.2) revSawW :: Oscillator revSawW = osc sawPeriod (0.01, 0.3, 0.3, 0.2) sqW :: Oscillator sqW = osc sqwPeriod (0.01, 0.2, 0.3, 0.1) sinW :: Oscillator sinW = osc sinPeriod (0.01, 0.2, 0.9, 0.2) sinSaw :: Oscillator sinSaw = osc (sinPeriod . sawPeriod) (0.01, 0.2, 0.7, 0.2) sinSq :: Oscillator sinSq = osc (sinPeriod . sqwPeriod) (0.01, 0.2, 0.7, 0.2) sinTri :: Oscillator sinTri = osc (sinPeriod . triPeriod) (0.01, 0.2, 0.7, 0.2) sawSin :: Oscillator sawSin = osc (sawPeriod . sinPeriod) (0.01, 0.2, 0.7, 0.2) triSq :: Oscillator triSq = osc (triPeriod . sqwPeriod) (0.01, 0.2, 0.7, 0.2) triSaw :: Oscillator triSaw = osc (triPeriod . sqwPeriod) (0.01, 0.2, 0.7, 0.2) allW :: Oscillator allW = osc (sinPeriod . sawPeriod . triPeriod . sqwPeriod) (0.01, 0.1, 0.7, 0.2) -- you can add more effects here guitar :: Oscillator guitar = osc guitarHope (0.1, 0.1, 0.3, 0.1) -- 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 [sawW, sinTri] -- [guitar, sawSin] audioTracks = zipWith playNotes instrs tracks audioNoEffects = mixTracks audioTracks [0.8, 0.8, 0.2] -- 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 [distortion, addGain 4] audioNoEffects -- This doesn't work because it repeats the section -- 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-}