plug: Index | Files

package plug

import ""

Package plug provides support for audio processing plugs.

Audio plugs are implemented in a 2-tiered fashion. There is an io tier, which manages all the input/output between processing plugs, and a processing tier which handles the actual computation in a standardized fashion which enables its use in the io tier.

IO Tier

The i/o tier implements audio I/O based on multi-channel, fixed sample rate inputs and outputs. The number of channels and sample rate may vary between the input and output, but the sample rate is fixed across all inputs and likewise all outputs.

The main interfaces for the I/O tier is the IO interface. I/O endpoints, such as audio capture sources or audio file sources, or playback speakers are expected to take the form of snd.Source or snd.Sink, like in the sio package, and may be attached to processing plugs arbitrarily using SetInput, AddOutput.

Processing Tier

The processing tier implements computation behind a processing plug. The main interface in the processing tier is Processor. The job of the processor is

1. To provide a function which maps input samples to output samples.

2. To provide shape information to the IO tier so the I/O tier can provide
the processing function with the appropriate data (and vice versa).

"Shape" information describes how many frames of input and output are to be expected in one processing block and also how the function treats channels.

The number of frames of input and output is dynamic and is queried by IO before every processing block, by means of the NextFrames() method.

The channel mode describes whether channels are treated independently. In MonoMode, the processing function maps input channels to output channels one at a time and there is a supposition that the number of input channels equals the number of output channels and the ith input channel is mapped to the ith output channel. This mode enables the lifting of mono-channel processors uniformly to multi-channel data.

The FullMode on the other hand maps all the input data of all the channels to all the output data of all the output channels in one step, giving more context to the processing function but requiring more memory and putting more responsability on the processing function in terms of coordinating what data is where.


Package Files

block.go cmap.go conn.go disconn.go doc.go graph.go io.go packet.go proc.go


const (
    // default block size of processor in terms of frames
    DefaultInFrames  = 1024
    DefaultOutFrames = 1024


var PassThrough = NewProcessor(MonoMode, func(dst, src *Block) error {
    N := src.Frames
    copy(dst.Samples[:N], src.Samples[:N])
    dst.Frames = N
    return nil

PassThrough is no-op processor.

var ToMono = NewProcessor(FullMode, func(dst, src *Block) error {
    if dst.Channels != 1 {
        return fmt.Errorf("cannot make mono to %d channel dst", dst.Channels)
    D := float64(src.Channels)
    for f := 0; f < src.Frames; f++ {
        acc := 0.0
        for c := 0; c < src.Channels; c++ {
            i := c*src.Frames + f
            d := src.Samples[i]
            acc += d
        dst.Samples[f] = acc / D
    dst.Frames = src.Frames
    return nil

ToMono is a mono converter.

type Block Uses

type Block struct {
    Samples    []float64
    Frames     int    // setable by processor
    Channels   int    // read only, static w.r.t. IO lifecycle
    SampleRate freq.T // read only, static w.r.t. IO lifecycle

Block represents one block of data.

type ChannelMode Uses

type ChannelMode int

ChannelMode indicates how channels are processed.

const (
    // MonoMode implies Process(dst, src *Block) has one channel in src and one in dst.
    MonoMode ChannelMode = iota
    // FullMode implies Process(dst, src *Block) has all input channels in src and dst
    // in channel deinterleaved format.

type DisconnectedError Uses

type DisconnectedError struct {
    IsInput bool
    Chan    int

DisconnectedError is an error describing a channel and direction (in or out) of which has no connection.

func (*DisconnectedError) Error Uses

func (d *DisconnectedError) Error() string

type Graph Uses

type Graph struct {
    // contains filtered or unexported fields

Graph is a data type supporting operations for a set of interconnected IOs.

The zero value for Graph is an empty graph.

It is not necessary to use the graph api to create and interact with I/O plugs directly. However, Graph facilitates some operations when there are many I/O plugs.

func (*Graph) CheckConnectivity Uses

func (g *Graph) CheckConnectivity() error

CheckConnectivity checks whether the graph is fully connected and acyclic.

func (*Graph) New Uses

func (g *Graph) New(iForm, oForm sound.Form, proc Processor) IO

New creates a new I/O plug.

func (*Graph) Run Uses

func (g *Graph) Run() <-chan error

Run runs the graph and returns an error channel on which all nodes in the graph report errors.


for e := range g.Run() {
  // report/handle error

type IO Uses

type IO interface {

    // InForm returns the sample rate and number of channels of the
    // input.
    InForm() sound.Form

    // OutForm returns the sample rate and number of channels of the
    // output of the node.
    OutForm() sound.Form

    // SetInput sets the input to s.  If cs is empty, then s must map channels
    // one-to-one with InForm() and have the same sample rate.  If cs is not empty,
    // then when cs[i] == j, then the j'th channel in IO.InForm() corresponds to
    // the i'th channel of s.
    // SetInput panics if any c in cs is out of bounds w.r.t. InForm().Channels().
    // SetInput returns a non-nil error if the channel and sample rates of
    // IO.InForm() and s are not compatible, or if there is a channel in IO
    // which would map to more than one sound.Source as a result of the call to
    // SetInput.
    SetInput(s sound.Source, cs error

    // AddOutput, if successful, causes the object implementing IO to direct a copy of
    // its output to the destination d.
    // If cs is empty, the d must have the same sample rate and number of
    // channels as IO.  If cs is not empty, it specifies IO channels which
    // are mapped to d: cs[i] = j <=> the j'th channel in IO is mapped to
    // the i'th channel in d.
    // AddOutput panics if any c in cs is out of bounds w.r.t. OutForm().Channels()
    // AddOutput returns a non-nil error if the channel and sample rates of
    // IO.OutForm() and d are not compatible.
    AddOutput(d sound.Sink, cs error

    // Output returns the output of the node as a sound.Source.
    // If cs is empty, then the resulting source has valve equal to
    // IO.OutForm().  Otherwise, cs lists a sequence of channels in IO
    // to be used in the result.  If cs[i] = j then the i'th channel of
    // the resulting source is the j'th output channel of IO.
    // If any c in cs is out of bounds w.r.t. OutForm().Channels(), then Output panics.
    // Every non-panicking call to Output generates a distinct new sound.Source which
    // can be used independently in different goroutines.
    Output(cs sound.Source

    // Run runs the IO plug.  Run blocks until it returns.  It will return a non-nil
    // error if something other than io.EOF ended its inputs.  Upon return, all
    // Sources going into the node and Sinks going out have been Close()d.
    Run() error

IO provides a generic minimal interface for an audio/sound processor. Implementations must be safe for use in multiple goroutines, but may assume that the Run() method is called at most once.

func New Uses

func New(iForm, oForm sound.Form, proc Processor) IO

New creates a new plug mapping input of channels and sampling frequency iForm to output oForm, using the Processor proc

type ProcFunc Uses

type ProcFunc func(dst, src *Block) error

ProcFunc gives the type of a processing function. The semantics of ProcFunc are exactly as in Process() in the Processor interface.

type Processor Uses

type Processor interface {

    // Mode describes the processing mode in which the Process method is invoked.
    ChannelMode() ChannelMode

    // NextFrames returns the desired number of source and destination frames,
    // respectively, for the next processing block.
    NextFrames() (int, int)

    // Process processes samples from src to dst.
    // If ChannelMode() is MonoMode, then the block is processed by calling
    // calling Process for each channel independently, and both src.Channels and
    // dst.Channels == 1. If ChannelMode() is FullMode, then Process is called
    // once with all channels of input and output.
    // Assuming the last call to next frames returned N, M, Process may assume
    // that
    //  1. 1 <= src.Frames <= N
    //  2. dst.Frames == M
    //  3. len(src.Samples) = N * src.Channels
    //  4. len(dst.Samples) = M * dst.Channels
    //  5. src.Samples and dst.Samples are in channel deinterleaved format.
    // In turn, let us denote the value of dst.Frames before the call as M, and after,  M';
    // then process should guarantee that
    // 1. M' contains the real number of outputs written
    // 2. 0 <= M' <= M
    // 3. dst.Samples[:d.Channels*M'] is in channel de-interleaved format.
    Process(dst, src *Block) error

Proc couples the shape and channel mode of a processing function with the function itself.

The channel mode is static, while the mapping of number of input frames to output frames is dynamic, determined by NextFrames(), which is called before every call to Process in Full mode and before every sequence of calls to Process which occur on a per-channel basis in Mono mode.

func NewProcessor Uses

func NewProcessor(mode ChannelMode, fn ProcFunc) Processor

NewProcessor creates a new processor with default frames using channel mode "mode".

func NewProcessorFrames Uses

func NewProcessorFrames(mode ChannelMode, fn ProcFunc, ifrms, ofrms int) Processor

NewProcessorFrames is like NewProcessor but allows specifying the input and output frames.

Package plug imports 5 packages (graph). Updated 2018-08-21. Refresh now. Tools for package owners.