-- | -- Module : DobutokO.Sound.Frequency -- Copyright : (c) OleksandrZhabenko 2020 -- License : MIT -- Stability : Experimental -- Maintainer : olexandr543@yahoo.com -- -- Helps to create experimental music. -- Can be used for applying the SoX \"synth\" effect and frequency modulation. -- {-# OPTIONS_GHC -threaded #-} {-# LANGUAGE CPP, FlexibleInstances #-} module DobutokO.Sound.Frequency where #ifdef __GLASGOW_HASKELL__ #if __GLASGOW_HASKELL__>=710 /* code that applies only to GHC 7.10.* and higher versions */ import GHC.Base (mconcat) #endif #endif import Numeric (showFFloat) import Data.List (intersperse) #ifdef __GLASGOW_HASKELL__ #if __GLASGOW_HASKELL__==708 /* code that applies only to GHC 7.8.* */ mconcat = concat #endif #endif data Frequency a b c = F a b [c] | F2 a a b [c] deriving Eq instance Show (Frequency Float Int Char) where show (F x n xs) = showFFloat (Just n) x xs show (F2 x1 x2 n xs) = showFFloat (Just n) x1 (freqChange n xs x2 x1) freqChange :: (RealFloat a) => Int -> String -> a -> a -> String freqChange n xs freq freq1 | compare freq 16 /= LT && compare freq 20000 /= GT && compare freq1 16 /= LT && compare freq1 20000 /= GT = if freq /= freq1 then case xs of "l" -> ':':showFFloat (Just n) freq "" "s" -> '+':showFFloat (Just n) freq "" "e" -> '/':showFFloat (Just n) freq "" _ -> '-':showFFloat (Just n) freq "" else "" | otherwise = error "DobutokO.Sound.Frequency.freqChange: undefined for this value of the frequencies. " data Swept a b c = SineS a b c | SquareS a b c | TriangleS a b c | SawtoothS a b c | TrapeziumS a b c | ExpS a b c deriving Eq sweptC :: Swept a b c -> String sweptC (SineS _ _ _) = "sine" sweptC (SquareS _ _ _) = "square" sweptC (TriangleS _ _ _) = "triangle" sweptC (SawtoothS _ _ _) = "sawtooth" sweptC (TrapeziumS _ _ _) = "trapezium" sweptC (ExpS _ _ _) = "exp" swept1 :: Swept a b c -> a swept1 (SineS x _ _) = x swept1 (SquareS x _ _) = x swept1 (TriangleS x _ _) = x swept1 (SawtoothS x _ _) = x swept1 (TrapeziumS x _ _) = x swept1 (ExpS x _ _) = x swept1N :: Int -> Swept [Float] b c -> Float swept1N n x | n == 1 = head . swept1 $ x | compare n 1 == GT && compare n 7 == LT = if null . drop (n - 1) . take n . swept1 $ x then error $ "DobutokO.Sound.Frequency.swept1N: Not defined for the arguments. " else head . drop (n - 1) . take n . swept1 $ x | otherwise = error $ "DobutokO.Sound.Frequency.swept1N: Not defined for the first argument " ++ show n toRange100 :: Float -> Float toRange100 percent = abs percent - fromIntegral (truncate (abs percent / 100) * 100) swept1N100 :: Int -> Swept [Float] b c -> Float swept1N100 n = toRange100 . swept1N n swept2 :: Swept a b c -> b swept2 (SineS _ x _) = x swept2 (SquareS _ x _) = x swept2 (TriangleS _ x _) = x swept2 (SawtoothS _ x _) = x swept2 (TrapeziumS _ x _) = x swept2 (ExpS _ x _) = x swept3 :: Swept a b c -> c swept3 (SineS _ _ x) = x swept3 (SquareS _ _ x) = x swept3 (TriangleS _ _ x) = x swept3 (SawtoothS _ _ x) = x swept3 (TrapeziumS _ _ x) = x swept3 (ExpS _ _ x) = x instance Show (Swept [Float] String Int) where show x | compare (length . swept1 $ x) 1 == GT = mconcat [sweptC x, " ", show (F2 (swept1N 1 x) (swept1N 2 x) (swept3 x) (swept2 x)), " ", if compare (length . swept1 $ x) 2 == GT then mconcat . intersperse " " . map (\z -> showFFloat (Just (swept3 x)) (swept1N100 z x) "") $ [3..length (swept1 x)] else ""] | otherwise = error $"DobutokO.Sound.Frequency.show: Too less arguments for " ++ show (swept1 x) ++ " sweep the frequencies to show them. " data Single a b = Whitenoise a b | Tpdfnoise a b | Pinknoise a b | Brownnoise a b | Pluck a b | Sine a b | Square a b | Triangle a b | Sawtooth a b | Trapezium a b | Exp a b deriving Eq singleC :: Single a b -> String singleC (Whitenoise _ _) = "whitenoise" singleC (Tpdfnoise _ _) = "tpdfnoise" singleC (Pinknoise _ _) = "pinknoise" singleC (Brownnoise _ _) = "brownnoise" singleC (Pluck _ _) = "pluck" singleC (Sine _ _) = "sine" singleC (Square _ _) = "square" singleC (Triangle _ _) = "triangle" singleC (Sawtooth _ _) = "sawtooth" singleC (Trapezium _ _) = "trapezium" singleC (Exp _ _) = "exp" single1 :: Single a b -> a single1 (Whitenoise x _) = x single1 (Tpdfnoise x _) = x single1 (Pinknoise x _) = x single1 (Brownnoise x _) = x single1 (Pluck x _) = x single1 (Sine x _) = x single1 (Square x _) = x single1 (Triangle x _) = x single1 (Sawtooth x _) = x single1 (Trapezium x _) = x single1 (Exp x _) = x single1N :: Int -> Single [Float] b -> Float single1N n x | n == 1 = head . single1 $ x | compare n 1 == GT && compare n 6 == LT = if null . drop (n - 1) . take n . single1 $ x then error $ "DobutokO.Sound.Frequency.single1N: Not defined for the arguments. " else head . drop (n - 1) . take n . single1 $ x | otherwise = error $ "DobutokO.Sound.Frequency.single1N: Not defined for the first argument " ++ show n single1N100 :: Int -> Single [Float] b -> Float single1N100 n = toRange100 . single1N n single2 :: Single a b -> b single2 (Whitenoise _ x) = x single2 (Tpdfnoise _ x) = x single2 (Pinknoise _ x) = x single2 (Brownnoise _ x) = x single2 (Pluck _ x) = x single2 (Sine _ x) = x single2 (Square _ x) = x single2 (Triangle _ x) = x single2 (Sawtooth _ x) = x single2 (Trapezium _ x) = x single2 (Exp _ x) = x instance Show (Single [Float] Int) where show x | null . single1 $ x = error $ "DobutokO.Sound.Frequency.show: Too less arguments. " | otherwise = mconcat [singleC x, " ", show (F (single1N 1 x) (single2 x) ""), " ", if compare (length . single1 $ x) 2 == GT then mconcat . intersperse " " . map (\z -> showFFloat (Just (single2 x)) (single1N100 z x) "") $ [2..length (single1 x)] else if (length . single1 $ x) == 2 then showFFloat (Just (single2 x)) (single1N100 1 x) "" else ""] --------------------------------------------------------------------------------------------------------------------------------------- data Di = O | T deriving Eq data Choice a b c d = C2 (Swept a b c) (Single a c) d deriving Eq choice1 :: Choice a b c d -> Swept a b c choice1 (C2 x _ _) = x choice2 :: Choice a b c d -> Single a c choice2 (C2 _ y _) = y choice3 :: Choice a b c d -> d choice3 (C2 _ _ z) = z choiceSet1 :: Swept a b c -> Choice a b c d -> Choice a b c d choiceSet1 x (C2 _ y z) = C2 x y z choiceSet2 :: Single a c -> Choice a b c d -> Choice a b c d choiceSet2 y (C2 x _ z) = C2 x y z choiceSet3 :: d -> Choice a b c d -> Choice a b c d choiceSet3 z (C2 x y _) = C2 x y z instance Show (Choice [Float] String Int Di) where show (C2 y _ O) = show y show (C2 _ z T) = show z type Synth = Choice [Float] String Int Di showQ :: Synth -> [String] showQ = words . show