Search code examples
haskelldata-structuresmultiplexing

Write a list demultiplexer


I'm trying to process the channels of an audio signal. I get the signal as list of frames where each frame has a sample for each channel. Now I would like to process the streams independently then merge them again. So I want

type Sample = Double
type Frame = [Sample]
type Stream = [Sample]
mux :: [Stream] -> [Frame]
demux :: [Frame] -> [Stream]
process :: Stream -> Stream

output = (mux . (map process) . demux) input

mux [[0.1, -0.1, -0.3], [0.2, 0.4, 0.6]] = [[0.1, 0.2], [-0.1, 0.4], [-0.3, 0.6]]
demux [[0.1, 0.2], [-0.1, 0.4], [-0.3, 0.6]] = [[0.1, -0.1, -0.3], [0.2, 0.4, 0.6]]

Because the streams are long, mux and demux must be lazy and not evaluate the whole list.

The multiplexer seems straightforward. The only thing I don't like about this version is the hard-to-read pattern matching.

mux streams@((_:_):_) = map head streams : mux (map tail streams)
mux _ = []

However I can't readily come up with a demultiplexer that would turn a list of frames into a list of streams. Since in my case there won't be more than two streams (stereo audio), I think I could manage by writing two demuxers, monoDemux and stereoDemux. But I would like one function to work on any number of channels. How can I lazily demux a list of frames?


Solution

  • I think that work has already been done for you: both mux and demux, are actually just transpose :: [[a]] -> [[a]] functions:

    import Data.List(transpose)
    
    mux = transpose
    demux = transpose
    

    This thus means that multiplexing is actually (at least in terms of a list of list) the same as demultiplexing.

    It generates the expected:

    Prelude Data.List> transpose [[0.1, -0.1, -0.3], [0.2, 0.4, 0.6]]
    [[0.1,0.2],[-0.1,0.4],[-0.3,0.6]]
    Prelude Data.List> transpose [[0.1, 0.2], [-0.1, 0.4], [-0.3, 0.6]]
    [[0.1,-0.1,-0.3],[0.2,0.4,0.6]]
    

    transpose works in a lazy manner. We can inspect the source code:

    transpose               :: [[a]] -> [[a]]
    transpose []             = []
    transpose ([]   : xss)   = transpose xss
    transpose ((x:xs) : xss) = (x : [h | (h:_) <- xss]) : transpose (xs : [ t | (_:t) <- xss])
    

    The last statement is a complex version of:

    transpose l = map head l : transpose (map tail l)