-- | Dieses Module stellt Datentypen und Funktionen zum Umgang mit Mono- und -- Stereodaten zur Verfügung. Speziell im Falle des Öffnens einer WAV-Datei -- ist erst zur Laufzeit bekannt, ob es sich um eine Mono- oder Stereodatei -- handelt, hier wird der Typ 'Signal' benötigt. module Sound.Hommage.Signal ( -- * Mono and Stereo Values Mono , Stereo (..) , leftStereo , rightStereo , stereoToMono , monoToStereo , balance -- * Signal datatype , Signal (..) , readWavSignal , openWavSignal , writeWavMono , writeWavStereo , writeWavSignal , writeTracks , signalToMono , signalToStereo , eitherSignal , liftSignal , mergeSignal , mergeSignals , multSignal , infiniteSignal ) where import Sound.Hommage.WavFile import Sound.Hommage.Misc import System.IO.Unsafe import Data.List ------------------------------------------------------------------------------- -- | A mono value. type Mono = Double ------------------------------------------------------------------------------- -- | A stereo value with left and right part. It is an instance of class Num -- and Fractional. data Stereo = !Double :><: !Double deriving (Eq, Show) -- | Access to the left part of a 'Stereo' value. leftStereo :: Stereo -> Mono leftStereo (x :><: _) = x -- | Access to the right part of a 'Stereo' value. rightStereo :: Stereo -> Mono rightStereo (_ :><: x) = x instance Num Stereo where (l1:><:r1) + (l2:><:r2) = (l1+l2) :><: (r1+r2) (l1:><:r1) - (l2:><:r2) = (l1-l2) :><: (r1-r2) (l1:><:r1) * (l2:><:r2) = (l1*l2) :><: (r1*r2) negate (l:><:r) = negate l :><: negate r abs (l:><:r) = abs l :><: abs r signum (l:><:r) = signum l :><: signum r fromInteger i = fromInteger i :><: fromInteger i instance Fractional Stereo where (l1:><:r1) / (l2:><:r2) = (l1/l2) :><: (r1/r2) fromRational x = fromRational x :><: fromRational x ------------------------------------------------------------------------------- -- | Converts a Stereo value to a Mono value (Double) stereoToMono :: Stereo -> Mono stereoToMono (x:><:y) = (x+y) / 2.0 -- | Converts a Mono value (Double) to a Stereo value monoToStereo :: Mono -> Stereo monoToStereo x = x :><: x --panorama :: Double -> Mono -> Stereo --panorama p m | p == 0.0 = m :><: m -- | p > 0.0 = m :><: ((1.0 - p) * m) -- | p < 0.0 = m :><: ((1.0 + p) * m) -- | The range of the Double value must be between -1 and 1. If it is below 0 -- the left channel is turned down, if it is greater than 0 the right channel -- is turned down. NOTE: This function should be replaced by a better one. balance :: Double -> Stereo -> Stereo balance p (l :><: r) | p == 0.0 = l :><: r | p > 0.0 = l :><: ((1.0 - p) * r) | p < 0.0 = ((1.0 + p) * l) :><: r ------------------------------------------------------------------------------- -- | A Signal is either a list of 'Mono' values or a list of 'Stereo' values. data Signal = Mono [Mono] | Stereo [Stereo] eitherSignal :: ([Mono] -> a) -> ([Stereo] -> a) -> Signal -> a eitherSignal f g (Mono xs) = f xs eitherSignal f g (Stereo xs) = g xs -- | Applies the function to the input signal. If it is a stereo signal, the -- function is applied to both channels seperately. liftSignal :: ([Mono] -> [Mono]) -> Signal -> Signal liftSignal f (Mono xs) = Mono $ f xs liftSignal f (Stereo xs) = Stereo $ zipWith (:><:) (f $ map leftStereo xs) (f $ map rightStereo xs) -- mapSignal, liftSignal, ------------------------------------------------------------------------------- -- | Reads a 'Signal' from a WAV-File. readWavSignal :: FilePath -> IO Signal readWavSignal fp = readWavFile fp >>= return . either (Mono . map wavInt16ToDouble) (Stereo . map (\(l,r) -> (wavInt16ToDouble l :><: wavInt16ToDouble r))) -- | Opens a 'Signal' from a WAV-File. openWavSignal :: FilePath -> Signal openWavSignal fp = unsafePerformIO $ readWavSignal fp -- | Writes a list of mono values to a WAV-File. writeWavMono :: FilePath -> [Mono] -> IO () writeWavMono fp = writeWavFileMono fp . map wavDoubleToInt16 -- | Writes a list of stereo values to a WAV-File. writeWavStereo :: FilePath -> [Stereo] -> IO () writeWavStereo fp = writeWavFileStereo fp . map (\(l :><: r) -> (wavDoubleToInt16 l, wavDoubleToInt16 r)) -- | Writes a 'Signal' to a WAV-File. writeWavSignal :: FilePath -> Signal -> IO () writeWavSignal fp (Mono xs) = writeWavMono fp xs writeWavSignal fp (Stereo xs) = writeWavStereo fp xs writeTracks :: FilePath -> [Signal] -> IO () writeTracks fp s = writeWavFiles fp "" strs where strs = map foo s foo (Mono xs) = Left $ map wavDoubleToInt16 xs foo (Stereo xs) = Right $ map (\(l:><:r)->(wavDoubleToInt16 l, wavDoubleToInt16 r)) xs ------------------------------------------------------------------------------- -- | Transfroms a signal to a list of mono values. signalToMono :: Signal -> [Mono] signalToMono (Mono xs) = xs signalToMono (Stereo xs) = map stereoToMono xs -- | Transfroms a signal to a list of stereo values. signalToStereo :: Signal -> [Stereo] signalToStereo (Mono xs) = map monoToStereo xs signalToStereo (Stereo xs) = xs ------------------------------------------------------------------------------- -- | The sum of a set of signals. If all signals are mono, the result will be mono. -- Otherwise it will be stereo. mergeSignals :: [Signal] -> Signal mergeSignals ss = fun where loop (Mono x : xs) = let (l,r) = loop xs in (x:l,r) loop (Stereo x : xs) = let (l,r) = loop xs in (l,x:r) loop [] = ([],[]) fun = case loop ss of ([],[]) -> Mono [] (ls,[]) -> Mono $ map sum $ transpose ls ([],rs) -> Stereo $ map sum $ transpose rs (ls,rs) -> Stereo $ map sum $ transpose (map (monoToStereo . sum) (transpose ls) : rs) -- | The sum of two signals mergeSignal :: Signal -> Signal -> Signal mergeSignal (Stereo xs) (Stereo ys) = Stereo $ merge (+) xs ys mergeSignal (Stereo xs) (Mono ys) = Stereo $ merge (+) xs (map monoToStereo ys) mergeSignal (Mono xs) (Stereo ys) = Stereo $ merge (+) (map monoToStereo xs) ys mergeSignal (Mono xs) (Mono ys) = Mono $ merge (+) xs ys -- | The Double value is added to the input signal (offset). The resulting signal will be infinie -- in any case. infiniteSignal :: Double -> Signal -> Signal infiniteSignal d (Mono a) = Mono (map (+d) a ++ repeat d) infiniteSignal d (Stereo a) = Stereo (map (+(d:><:d)) a ++ repeat (d:><:d)) -- | Multiplies two signals. multSignal :: Signal -> Signal -> Signal multSignal (Mono a) (Mono b) = Mono $ zipWith (*) a b multSignal (Mono a) (Stereo b) = Stereo $ zipWith (\x y -> monoToStereo x * y) a b multSignal (Stereo a) (Stereo b) = Stereo $ zipWith (*) a b multSignal (Stereo a) (Mono b) = Stereo $ zipWith (\x y -> x * monoToStereo y) a b