module Sound.Base (
SampleRate,
BitDepth,
SoundData,
SoundFrame,
FrameCount,
AudioSig,
SndFileInfo ( .. ),
SndFileType ( .. ),
SoundFile ( SoundFile ),
SndFileCls (getSfInfo, getSfType, fromSndFileCls, getAudioData, getAudioLength),
AudioError (..),
AudioMonad,
lengthInFrames,
audioData,
makeFrames,
interleave,
makeAudioSignal,
appendASig,
concatASig
) where
import Data.List (transpose)
import Control.Parallel.Strategies (rnf, (>|))
import qualified Control.Parallel.Strategies as Strat
import qualified Control.Monad.Error as Err
type SampleRate = Integer
type BitDepth = Integer
type SoundData = Double
type SoundFrame = [SoundData]
type FrameCount = Integer
data AudioSig = AudioSig {
audioData :: [SoundFrame],
lengthInFrames :: FrameCount
} deriving (Eq, Show)
makeAudioSignal :: FrameCount -> [SoundFrame] -> AudioSig
makeAudioSignal fc frames = AudioSig frames fc
appendASig :: AudioSig -> AudioSig -> AudioSig
appendASig a b = AudioSig (concat [audioData a, audioData b]) $ (lengthInFrames a) + (lengthInFrames b)
concatASig :: [AudioSig] -> AudioSig
concatASig a = AudioSig (concat . map audioData $ a) (sum . map lengthInFrames $ a)
type AudioMonad m = Err.ErrorT AudioError m
data AudioError = NoFormatError
| UnknownFileTypeError
| InvalidBitDepthError BitDepth [BitDepth]
| OtherError String
instance Err.Error AudioError where
noMsg = OtherError "An Audio Error!"
strMsg s = OtherError s
instance Show AudioError where
show (NoFormatError) = "No format information found."
show UnknownFileTypeError = "The file does not appear to be a recognized audio file."
show (InvalidBitDepthError r vs) = "Requested bit depth " ++ show r ++ " is invalid. Valid bit depths are " ++ show vs
show (OtherError s) = s
class SndFileCls a where
getSfInfo :: (Monad m) => a -> AudioMonad m SndFileInfo
getSfType :: a -> SndFileType
getAudioData :: (Monad m) => a -> AudioMonad m AudioSig
fromSndFileCls :: (Monad m) => a -> AudioMonad m SoundFile
fromSndFileCls sf = do
sfi <- getSfInfo sf
ad <- getAudioData sf
return $ SoundFile sfi ad
getAudioLength :: (Monad m) => a -> AudioMonad m FrameCount
getAudioLength a= do
ad <- getAudioData a
return $ lengthInFrames ad
data SndFileInfo = SndFileInfo {numChannels :: Int, sr :: SampleRate,
bitDepth :: BitDepth} deriving (Eq, Show)
instance Strat.NFData SndFileInfo where
rnf (SndFileInfo chn sr' bd) = rnf chn >| rnf sr' >| rnf bd
data SndFileType =
AIFF
| WavePCM
| OtherSoundFile String
| Internal deriving (Eq, Show)
data SoundFile = SoundFile { sfFileInfo :: SndFileInfo,
sfFileData :: AudioSig }
instance SndFileCls SoundFile where
getSfInfo = return . sfFileInfo
getAudioData = return . sfFileData
getSfType _ = Internal
instance Eq SoundFile where
a == b = sfFileInfo a == sfFileInfo b
instance Show SoundFile where
show = show . sfFileInfo
makeFrames :: Int -> [SoundData] -> [SoundFrame]
makeFrames _ [] = []
makeFrames 1 xs = map (:[]) xs
makeFrames numChans xs = frames : makeFrames numChans rest
where (frames, rest) = splitAt numChans xs
interleave :: [[SoundData]] -> [SoundFrame]
interleave = transpose