Safe Haskell | Safe-Inferred |
---|
This is a basic library for audio streaming. It uses the streaming
package to interleave streaming and IO, and represents signals as a stream
of Vectors of Sample
s, which is hardcoded to Float. There are separate
types for interleaved samples Audio
and non-interleaved samples NAudio
.
The sampling rate and channels are recorded as type naturals. This removes the need for runtime error checking, but makes dealing with an unknown sampling rate or channels more awkward.
Synopsis
- newtype Audio m (rate :: TypeLits.Nat) (chan :: TypeLits.Nat) = Audio {}
- type AudioIO rate chan = Audio (Resource.ResourceT IO) rate chan
- type AudioId rate chan = Audio Identity rate chan
- data NAudio m (rate :: TypeLits.Nat) = NAudio {}
- type NAudioIO rate = NAudio (Resource.ResourceT IO) rate
- type NAudioId rate = NAudio Identity rate
- data UnknownAudio m = forall rate chan.(TypeLits.KnownNat rate, TypeLits.KnownNat chan) => UnknownAudio (Audio m rate chan)
- type UnknownAudioIO = UnknownAudio (Resource.ResourceT IO)
- data Block
- type Sample = Float
- newtype Frames = Frames Int
- secondsToFrames :: Rate -> Seconds -> Frames
- secondsToFramesCeil :: Rate -> Seconds -> Frames
- framesToSeconds :: Rate -> Frames -> Seconds
- type Count = Int
- type Channels = Int
- type Rate = Int
- type Seconds = Double
- framesCount :: TypeLits.KnownNat chan => Proxy chan -> Frames -> Count
- countFrames :: TypeLits.KnownNat chan => Proxy chan -> Count -> Frames
- blockFrames :: TypeLits.KnownNat chan => Proxy chan -> Block -> Frames
- vectorFrames :: TypeLits.KnownNat chan => Proxy chan -> V.Vector Sample -> Frames
- blockCount :: Block -> Count
- isEmptyBlock :: Block -> Bool
- blockSamples :: Block -> [V.Vector Sample]
- blockVector :: Block -> V.Vector Sample
- fromBlocks :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => [Block] -> Audio m rate chan
- fromSamples :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => [V.Vector Sample] -> Audio m rate chan
- fromSampleLists :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => [[Sample]] -> Audio m rate chan
- toBlocks :: Monad m => Audio m rate chan -> m [Block]
- toSamples :: Monad m => Audio m rate chan -> m [V.Vector Sample]
- toBlocksN :: Monad m => NAudio m rate -> m [[Block]]
- toSamplesN :: Monad m => NAudio m rate -> m [[V.Vector Sample]]
- apply :: (Stream (Of Block) m () -> Stream (Of Block) m ()) -> Audio m rate chan -> Audio m rate chan
- castRate :: Audio m rate1 chan -> Audio m rate2 chan
- take :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Frames -> Audio m rate chan -> Audio m rate chan
- takeS :: forall m rate chan. (Monad m, TypeLits.KnownNat rate, TypeLits.KnownNat chan) => Seconds -> Audio m rate chan -> Audio m rate chan
- mapSamples :: Monad m => (Float -> Float) -> Audio m rate chan -> Audio m rate chan
- gain :: Monad m => Float -> Audio m rate chan -> Audio m rate chan
- multiply :: (Monad m, TypeLits.KnownNat chan) => Audio m rate chan -> Audio m rate chan -> Audio m rate chan
- takeClose :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => m () -> Frames -> Audio m rate chan -> Audio m rate chan
- takeCloseS :: forall m rate chan. (Monad m, TypeLits.KnownNat rate, TypeLits.KnownNat chan) => m () -> Seconds -> Audio m rate chan -> Audio m rate chan
- pan :: Monad m => Audio m rate 1 -> Audio m rate 2 -> Audio m rate 2
- panConstant :: Monad m => Sample -> Audio m rate 2 -> Audio m rate 2
- mix :: Monad m => [Audio m rate chan] -> Audio m rate chan
- mixV :: Count -> [V.Vector Sample] -> V.Vector Sample
- mergeChannels :: forall m rate chan1 chan2. (Monad m, TypeLits.KnownNat chan1, TypeLits.KnownNat chan2) => Audio m rate chan1 -> Audio m rate chan2 -> Audio m rate (chan1 TypeLits.+ chan2)
- extractChannel :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Channels -> Audio m rate chan -> Audio m rate 1
- splitChannels :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Audio m rate chan -> NAudio m rate
- expandChannels :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Audio m rate 1 -> Audio m rate chan
- expandV :: Channels -> V.Vector Sample -> V.Vector Sample
- mixChannels :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Audio m rate chan -> Audio m rate 1
- interleaveV :: Storable a => [V.Vector a] -> V.Vector a
- deinterleaveB :: Channels -> Block -> [Block]
- deinterleaveV :: Storable a => Channels -> V.Vector a -> [V.Vector a]
- nonInterleaved :: Monad m => Frames -> Frames -> [Audio m rate 1] -> NAudio m rate
- interleaved :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => NAudio m rate -> Either Text (Audio m rate chan)
- synchronizeToSize :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Frames -> Frames -> Audio m rate chan -> Audio m rate chan
- takeN :: Monad m => Frames -> NAudio m rate -> NAudio m rate
- zeroPadN :: Monad m => Frames -> NAudio m rate -> NAudio m rate
- silence :: (Monad m, TypeLits.KnownNat chan) => Audio m rate chan
- sine :: forall m rate. (Monad m, TypeLits.KnownNat rate) => Float -> Audio m rate 1
- linear :: forall m rate. (Monad m, TypeLits.KnownNat rate) => Bool -> [(Seconds, Double)] -> Audio m rate 1
- effect :: MonadIO m => IO () -> Audio m rate chan -> Audio m rate chan
- newtype Exception = Exception Text
- exceptionText :: Exception -> Text
- throw :: Stack.HasCallStack => Text -> AudioIO rate chan
- throwIO :: (Stack.HasCallStack, MonadIO m) => Text -> m a
- assert :: (Stack.HasCallStack, MonadIO m) => Bool -> Text -> m ()
- assertIn :: (Stack.HasCallStack, MonadIO m) => Bool -> Text -> Audio m rate chan -> Audio m rate chan
- blockSize :: Frames
- silentBlock :: V.Vector Sample
- dbToLinear :: Float -> Float
- linearToDb :: Float -> Float
- takeFramesGE :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Frames -> Audio m rate chan -> m ([Block], Audio m rate chan)
- splitAt :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Frames -> Audio m rate chan -> m ([Block], Audio m rate chan)
- next :: Monad m => Audio m rate chan -> m (Maybe.Maybe (Block, Audio m rate chan))
- isEmpty :: Monad m => Audio m rate chan -> m (Maybe.Maybe (Audio m rate chan))
- natVal :: TypeLits.KnownNat n => Proxy n -> Int
- someNat :: Int -> TypeLits.SomeNat
types
newtype Audio m (rate :: TypeLits.Nat) (chan :: TypeLits.Nat) Source #
A stream of blocks of interleaved samples.
There is an invariant that the length of the vector should always be
a multiple of channels, in other words that a block is an integral number
of frames. The blocks may be of variable size, but should default to
blockSize
, and align to multiples of blockSize, to minimize re-chunking
when synchronizing streams.
data NAudio m (rate :: TypeLits.Nat) Source #
Non-interleaved audio stream. Ok so it's still interleaved, just per block instead of per sample.
Each of the lists will be channels length. Each Sample vector will be the same length until the signal runs out, at which point it may be short, and then will be empty. The stream ends when all signals are empty.
The same block length guidelines as in Audio
also apply.
Unlike Audio
, the channel count is dynamic, not static. This is because
I use NAudio to stream a variably sized collection of control signals,
while I use Audio to represent audio signals, which generally have a global
number of channels determined by how many speakers you have. But it does
mean that you have to be careful that the length of each blocks list always
matches _nchannels
. TODO it should be possible to use an existentially
quantified type natural and length indexed list to ensure this, but not
sure if it's worth it.
data UnknownAudio m Source #
This is an Audio with dynamic rate and channels.
forall rate chan.(TypeLits.KnownNat rate, TypeLits.KnownNat chan) => UnknownAudio (Audio m rate chan) |
type UnknownAudioIO = UnknownAudio (Resource.ResourceT IO) Source #
There is a special representation for a constant block. 0 blocks are common when aligning sounds, and constant ones are common for control signals.
I hardcode the sample format to Float for now, since I have no need to work with any other format.
Should be >=0.
Instances
framesCount :: TypeLits.KnownNat chan => Proxy chan -> Frames -> Count Source #
countFrames :: TypeLits.KnownNat chan => Proxy chan -> Count -> Frames Source #
blockFrames :: TypeLits.KnownNat chan => Proxy chan -> Block -> Frames Source #
vectorFrames :: TypeLits.KnownNat chan => Proxy chan -> V.Vector Sample -> Frames Source #
blockCount :: Block -> Count Source #
isEmptyBlock :: Block -> Bool Source #
construct
fromBlocks :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => [Block] -> Audio m rate chan Source #
fromSamples :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => [V.Vector Sample] -> Audio m rate chan Source #
Construct audio manually for testing. The length of each vector should be a multiple of the channels, or this will crash.
fromSampleLists :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => [[Sample]] -> Audio m rate chan Source #
transform
apply :: (Stream (Of Block) m () -> Stream (Of Block) m ()) -> Audio m rate chan -> Audio m rate chan Source #
take :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Frames -> Audio m rate chan -> Audio m rate chan Source #
takeS :: forall m rate chan. (Monad m, TypeLits.KnownNat rate, TypeLits.KnownNat chan) => Seconds -> Audio m rate chan -> Audio m rate chan Source #
gain :: Monad m => Float -> Audio m rate chan -> Audio m rate chan Source #
Set linear gain. Use dbToLinear
to scale by dB.
multiply :: (Monad m, TypeLits.KnownNat chan) => Audio m rate chan -> Audio m rate chan -> Audio m rate chan Source #
Multiply two signals, and end with the shorter one.
takeClose :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => m () -> Frames -> Audio m rate chan -> Audio m rate chan Source #
takeCloseS :: forall m rate chan. (Monad m, TypeLits.KnownNat rate, TypeLits.KnownNat chan) => m () -> Seconds -> Audio m rate chan -> Audio m rate chan Source #
This is like take
, but it takes a close action to run when the audio
stream is terminated. This is because streaming can't otherwise close
the input file in a timely way, because the take can't tell upstream that
it will never demand another chunk. This appears to be a problem with all
pull-based streaming libraries, including pipes and conduit.
pan :: Monad m => Audio m rate 1 -> Audio m rate 2 -> Audio m rate 2 Source #
Pan a stereo signal with a mono one. The pan signal goes from -1 to 1.
TODO This is linear panning, try constant power or -4.5dB: http://www.cs.cmu.edu/~music/icm-online/readings/panlaws/
mix
mix :: Monad m => [Audio m rate chan] -> Audio m rate chan Source #
Mix together the audio streams at the given start times.
TODO the input could also be a stream, in case it somehow comes from IO.
mixV :: Count -> [V.Vector Sample] -> V.Vector Sample Source #
Add vectors of samples. The output will be the max of the longest vector and the provided minimum length.
channels
mergeChannels :: forall m rate chan1 chan2. (Monad m, TypeLits.KnownNat chan1, TypeLits.KnownNat chan2) => Audio m rate chan1 -> Audio m rate chan2 -> Audio m rate (chan1 TypeLits.+ chan2) Source #
extractChannel :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Channels -> Audio m rate chan -> Audio m rate 1 Source #
Extract a single channel. It will crash unless 0 <= idx < chan.
See splitChannels
to get them all.
splitChannels :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Audio m rate chan -> NAudio m rate Source #
De-interleave the audio. This is the inverse of splitChannels
, but it
converts to NAudio, since [Audio] would amount to an unzip, which streams
don't support in a straightforward way.
expandChannels :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Audio m rate 1 -> Audio m rate chan Source #
Take a single channel signal to multiple channels by copying samples.
This could be generalized to expand n to m where m is divisible by n, but that's more complicated and I don't need it.
mixChannels :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Audio m rate chan -> Audio m rate 1 Source #
Do the reverse of expandChannels
, mixing all channels to a mono signal.
non-interleaved
interleaved :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => NAudio m rate -> Either Text (Audio m rate chan) Source #
Convert a non-interleaved NAudio with an unknown number of channels
to an interleaved one with a known number. This is the inverse of
nonInterleaved
.
This is limited to only work when the channels are equal, or the NAudio has
1 channel, in which case it acts like expandChannels
. Similar to
expandChannels
, I could generalize it, but I don't need that now.
synchronizeToSize :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Frames -> Frames -> Audio m rate chan -> Audio m rate chan Source #
zeroPadN :: Monad m => Frames -> NAudio m rate -> NAudio m rate Source #
Extend blocks shorter than the given size with zeros, and pad the end with
zeros forever. Composed with nonInterleaved
, which may leave a short
final block, the output should be infinite and have uniform block size.
generate
sine :: forall m rate. (Monad m, TypeLits.KnownNat rate) => Float -> Audio m rate 1 Source #
Generate a test tone at the given frequency, forever. This is not efficient, but it's just for testing.
linear :: forall m rate. (Monad m, TypeLits.KnownNat rate) => Bool -> [(Seconds, Double)] -> Audio m rate 1 Source #
Generate a piecewise linear signal from breakpoints. The signal will continue forever with the last value.
TODO take blockSize and start args so a subsequent synchronization doesn't have to reallocate.
effects
effect :: MonadIO m => IO () -> Audio m rate chan -> Audio m rate chan Source #
Prepend an effect to the stream, useful to log when a stream is started.
error
Instances
Exception.Exception Exception Source # | |
Defined in Util.Audio.Audio | |
Show Exception Source # | |
exceptionText :: Exception -> Text Source #
assertIn :: (Stack.HasCallStack, MonadIO m) => Bool -> Text -> Audio m rate chan -> Audio m rate chan Source #
Insert an assertion into the audio stream.
constants
conversions
dbToLinear :: Float -> Float Source #
linearToDb :: Float -> Float Source #
util
takeFramesGE :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Frames -> Audio m rate chan -> m ([Block], Audio m rate chan) Source #
Take >= the given number of frames. It may take more if the size doesn't line up on a block boundary.
TODO rename to splitAtGE
splitAt :: forall m rate chan. (Monad m, TypeLits.KnownNat chan) => Frames -> Audio m rate chan -> m ([Block], Audio m rate chan) Source #
Take exactly the given number of frames.
isEmpty :: Monad m => Audio m rate chan -> m (Maybe.Maybe (Audio m rate chan)) Source #
Nothing if the Audio stream is completed. Otherwise, it returns the same stream but with the first chunk evaluated, to avoid duplicating effects.
someNat :: Int -> TypeLits.SomeNat Source #