Safe Haskell | None |
---|

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 :: Nat) (chan :: Nat) = Audio {}
- type AudioIO rate chan = Audio (Resource.ResourceT IO) rate chan
- type AudioId rate chan = Audio Identity rate chan
- data NAudio m (rate :: Nat) = NAudio {}
- type NAudioIO rate = NAudio (Resource.ResourceT IO) rate
- type NAudioId rate = NAudio Identity rate
- data UnknownAudio m = forall rate chan.(KnownNat rate, 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 :: KnownNat chan => Proxy chan -> Frames -> Count
- countFrames :: KnownNat chan => Proxy chan -> Count -> Frames
- blockFrames :: KnownNat chan => Proxy chan -> Block -> Frames
- vectorFrames :: KnownNat chan => Proxy chan -> V.Vector Sample -> Frames
- blockCount :: Block -> Count
- isEmptyBlock :: Block -> Bool
- blockSamples :: Block -> [V.Vector Sample]
- blockVector :: Block -> V.Vector Sample
- fromSamples :: forall m rate chan. (Monad m, KnownNat chan) => [V.Vector Sample] -> Audio m rate chan
- fromSampleLists :: forall m rate chan. (Monad m, 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]]
- castRate :: Audio m rate1 chan -> Audio m rate2 chan
- take :: forall m rate chan. (Monad m, KnownNat chan) => Frames -> Audio m rate chan -> Audio m rate chan
- takeS :: forall m rate chan. (Monad m, KnownNat rate, 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, KnownNat chan) => Audio m rate chan -> Audio m rate chan -> Audio m rate chan
- takeClose :: forall m rate chan. (Monad m, KnownNat chan) => m () -> Frames -> Audio m rate chan -> Audio m rate chan
- takeCloseS :: forall m rate chan. (Monad m, KnownNat rate, 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, KnownNat chan1, KnownNat chan2) => Audio m rate chan1 -> Audio m rate chan2 -> Audio m rate (chan1 + chan2)
- extractChannel :: forall m rate chan. (Monad m, KnownNat chan) => Channels -> Audio m rate chan -> Audio m rate 1
- splitChannels :: forall m rate chan. (Monad m, KnownNat chan) => Audio m rate chan -> NAudio m rate
- expandChannels :: forall m rate chan. (Monad m, 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, 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, KnownNat chan) => NAudio m rate -> Either Text (Audio m rate chan)
- synchronizeToSize :: forall m rate chan. (Monad m, 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, KnownNat chan) => Audio m rate chan
- sine :: forall m rate. (Monad m, KnownNat rate) => Float -> Audio m rate 1
- linear :: forall m rate. (Monad m, KnownNat rate) => Bool -> [(Seconds, Double)] -> Audio m rate 1
- 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, KnownNat chan) => Frames -> Audio m rate chan -> m ([Block], Audio m rate chan)
- splitAt :: forall m rate chan. (Monad m, 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 :: KnownNat n => Proxy n -> Int
- someNat :: Int -> SomeNat

# types

newtype Audio m (rate :: Nat) (chan :: 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 :: 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.(KnownNat rate, 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.

blockCount :: Block -> Count Source #

isEmptyBlock :: Block -> Bool Source #

# construct

fromSamples :: forall m rate chan. (Monad m, 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, KnownNat chan) => [[Sample]] -> Audio m rate chan Source #

# transform

take :: forall m rate chan. (Monad m, KnownNat chan) => Frames -> Audio m rate chan -> Audio m rate chan Source #

takeS :: forall m rate chan. (Monad m, KnownNat rate, 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, 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, KnownNat chan) => m () -> Frames -> Audio m rate chan -> Audio m rate chan Source #

takeCloseS :: forall m rate chan. (Monad m, KnownNat rate, 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, KnownNat chan1, KnownNat chan2) => Audio m rate chan1 -> Audio m rate chan2 -> Audio m rate (chan1 + chan2) Source #

`splitChannels`

is the inverse of this, but it converts to NAudio, since
[Audio] would amount to an unzip, which streams don't support in
a straightforward way.

extractChannel :: forall m rate chan. (Monad m, 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, KnownNat chan) => Audio m rate chan -> NAudio m rate Source #

De-interleave the audio.

expandChannels :: forall m rate chan. (Monad m, 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, 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, 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, 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, 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, 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.

# error

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, 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, KnownNat chan) => Frames -> Audio m rate chan -> m ([Block], Audio m rate chan) Source #

Take exactly the given number of frames.