Karya, built on 2023-08-29T07:47:28 (patch 7a412d5d6ba4968ca4155ef276a062ccdeb9109a)
Safe HaskellSafe-Inferred

Util.Audio.Audio

Description

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 Samples, 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

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.

Constructors

Audio 

Fields

Instances

Instances details
Monad m => Monoid (Audio m rate chan) Source # 
Instance details

Defined in Util.Audio.Audio

Methods

mempty :: Audio m rate chan #

mappend :: Audio m rate chan -> Audio m rate chan -> Audio m rate chan #

mconcat :: [Audio m rate chan] -> Audio m rate chan #

Monad m => Semigroup (Audio m rate chan) Source # 
Instance details

Defined in Util.Audio.Audio

Methods

(<>) :: Audio m rate chan -> Audio m rate chan -> Audio m rate chan #

sconcat :: NonEmpty (Audio m rate chan) -> Audio m rate chan #

stimes :: Integral b => b -> Audio m rate chan -> Audio m rate chan #

type AudioIO rate chan = Audio (Resource.ResourceT IO) rate chan Source #

type AudioId rate chan = Audio Identity rate chan Source #

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.

Constructors

NAudio 

Fields

type NAudioId rate = NAudio Identity rate Source #

data UnknownAudio m Source #

This is an Audio with dynamic rate and channels.

Constructors

forall rate chan.(TypeLits.KnownNat rate, TypeLits.KnownNat chan) => UnknownAudio (Audio m rate chan) 

data Block 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.

Instances

Instances details
Monoid Block Source # 
Instance details

Defined in Util.Audio.Audio

Methods

mempty :: Block #

mappend :: Block -> Block -> Block #

mconcat :: [Block] -> Block #

Semigroup Block Source # 
Instance details

Defined in Util.Audio.Audio

Methods

(<>) :: Block -> Block -> Block #

sconcat :: NonEmpty Block -> Block #

stimes :: Integral b => b -> Block -> Block #

Show Block Source # 
Instance details

Defined in Util.Audio.Audio

Methods

showsPrec :: Int -> Block -> ShowS #

show :: Block -> String #

showList :: [Block] -> ShowS #

Eq Block Source # 
Instance details

Defined in Util.Audio.Audio

Methods

(==) :: Block -> Block -> Bool #

(/=) :: Block -> Block -> Bool #

Pretty.Pretty Block Source # 
Instance details

Defined in Util.Audio.Audio

type Sample = Float Source #

I hardcode the sample format to Float for now, since I have no need to work with any other format.

newtype Frames Source #

Should be >=0.

Constructors

Frames Int 

Instances

Instances details
Enum Frames Source # 
Instance details

Defined in Util.Audio.AudioT

Num Frames Source # 
Instance details

Defined in Util.Audio.AudioT

Integral Frames Source # 
Instance details

Defined in Util.Audio.AudioT

Real Frames Source # 
Instance details

Defined in Util.Audio.AudioT

Show Frames Source # 
Instance details

Defined in Util.Audio.AudioT

Eq Frames Source # 
Instance details

Defined in Util.Audio.AudioT

Methods

(==) :: Frames -> Frames -> Bool #

(/=) :: Frames -> Frames -> Bool #

Ord Frames Source # 
Instance details

Defined in Util.Audio.AudioT

Pretty.Pretty Frames Source # 
Instance details

Defined in Util.Audio.AudioT

Serialize Frames Source # 
Instance details

Defined in Util.Audio.AudioT

type Count = Int Source #

Sample count. This is Frames * channels.

type Rate = Int 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 #

toBlocks :: Monad m => Audio m rate chan -> m [Block] Source #

toSamples :: Monad m => Audio m rate chan -> m [V.Vector Sample] Source #

toBlocksN :: Monad m => NAudio m rate -> m [[Block]] Source #

toSamplesN :: Monad m => NAudio m rate -> m [[V.Vector Sample]] Source #

transform

apply :: (Stream (Of Block) m () -> Stream (Of Block) m ()) -> Audio m rate chan -> Audio m rate chan Source #

castRate :: Audio m rate1 chan -> Audio m rate2 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 #

mapSamples :: Monad m => (Float -> Float) -> 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/

panConstant :: Monad m => Sample -> Audio m rate 2 -> Audio m rate 2 Source #

Like pan, but more efficient. TODO also linear pan, as in pan.

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

nonInterleaved :: Monad m => Frames -> Frames -> [Audio m rate 1] -> NAudio m rate Source #

Merge Audios into a single NAudio stream, synchronized with the given block size.

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 #

takeN :: Monad m => Frames -> NAudio m rate -> NAudio m rate 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

silence :: (Monad m, TypeLits.KnownNat chan) => Audio m rate chan Source #

Silence. Forever.

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

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

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.

next :: Monad m => Audio m rate chan -> m (Maybe.Maybe (Block, Audio m rate chan)) Source #

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.