snd

package module
v0.0.0-...-d3216ab Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 15, 2023 License: BSD-3-Clause Imports: 9 Imported by: 1

README

Package snd GoDoc

Package snd provides methods and types for sound processing and synthesis.

go get dasa.cc/snd

Windows

Tested with msys2. Additional setup required due to how gomobile currently links to openal on windows. This should go away in the future stepping away from gomobile's exp/al package.

pacman -S mingw-w64-x86_64-openal
cd /mingw64/lib
cp libopenal.a libOpenAL32.a
cp libopenal.dll.a libOpenAL32.dll.a

Tests

In addition to regular unit tests, there are plot tests that produce images saved to a plots/ folder. This depends on package gonum/plot and requires an additional tag flag to enable as follows:

go get github.com/gonum/plot
go test -tags plot

SndObj

This package was very much inspired by Victor Lazzarini's SndObj Library for which I spent countless hours enjoying, and without it I may never have come to program.

Documentation

Overview

Package snd provides methods and types for sound processing and synthesis.

Audio hardware is accessed via package snd/al which in turn manages the dispatching of sound synthesis via golang.org/x/mobile/audio/al. Start the dispatcher as follows:

const buffers = 1
if err := al.OpenDevice(buffers); err != nil {
    log.Fatal(err)
}
al.Start()

Once running, add a source for sound synthesis. For example:

osc := snd.NewOscil(snd.Sine(), 440, nil)
al.AddSource(osc)

This results in a 440Hz tone being played back through the audio hardware.

Synthesis types in package snd implement the Sound interface and many type methods accept a Sound argument that can affect sampling. For example, one may modulate an oscillator by passing in a third argument to NewOscil.

sine := snd.Sine()
mod := snd.NewOscil(sine, 2, nil)
osc := snd.NewOscil(sine, 440, mod)

The above results in a lower frequency sound that may require decent speakers to hear properly.

Signals

Note the sine argument in the previous example. There are two conceptual types of sounds, ContinuousFunc and Discrete. ContinuousFunc represents an indefinite series over time. Discrete is the sampling of a ContinuousFunc over an interval. Functions such as Sine, Triangle, and Square (non-exhaustive) return Discretes created by sampling a ContinuousFunc such as SineFunc, TriangleFunc, and SquareFunc.

Discrete signals serve as a lookup table to efficiently synthesize sound. A Discrete is a []float64 and can sample any ContinuousFunc, within the package or user defined which is a func(t float64) float64.

Discrete signals may be further modified with intent or arbitrarily. For example, Discrete.Add(Discrete, int) performs additive synthesis and is used by functions such as SquareSynthesis(int) to return an approximation of a square signal based on a sinusoidal.

Time Approximation

Functions that take a time.Duration argument approximate the value to the closest number of frames. For example, if sample rate is 44.1kHz and duration is 75ms, this results in the argument representing 3307 frames which is approximately 74.99ms.

Index

Constants

View Source
const (
	DefaultSampleRate     float64 = 48000 // 44100
	DefaultSampleBitDepth         = 16    // TODO not currently used for anything
	DefaultBufferLen              = 256
	DefaultAmpFac         float64 = 0.31622776601683794 // -10dB

)
View Source
const DefaultNotesLen = 128

Variables

This section is empty.

Functions

func Dtof

func Dtof(d time.Duration, sr float64) (f int)

Dtof converts time duration to approximate number of representative frames.

func EqualTempermantFunc

func EqualTempermantFunc(ns Notes, tones int, freq float64, pos int)

EqualTempermantFunc evaluates notes as an octave containing n tones at an equal distance on a logarithmic scale. The reference freq and pos is used to find all other values.

func ExpDrive

func ExpDrive() signal.Discrete

func Ftod

func Ftod(f int, sr float64) (d time.Duration)

Ftod converts f, number of frames, to approximate time duration.

func LinearDrive

func LinearDrive() signal.Discrete

Types

type ADSR

type ADSR struct {
	// contains filtered or unexported fields
}

TODO reimplement sustain functionality

func NewADSR

func NewADSR(attack, decay, sustain, release time.Duration, susamp, maxamp float64, in Sound) *ADSR

func (*ADSR) Dur

func (adsr *ADSR) Dur() time.Duration

func (ADSR) Prepare

func (sq ADSR) Prepare(uint64)

func (*ADSR) Release

func (adsr *ADSR) Release() (ok bool)

Release immediately releases envelope from anywhere and starts release period.

func (*ADSR) Restart

func (adsr *ADSR) Restart()

Restart resets envelope to start from attack period.

func (*ADSR) Sustain

func (adsr *ADSR) Sustain()

Sustain locks envelope when sustain period is reached.

type BPM

type BPM float64

BPM respresents beats per minute and is a measure of tempo.

func (BPM) Dur

func (bpm BPM) Dur() time.Duration

Dur returns the time duration of bpm.

func (BPM) Hertz

func (bpm BPM) Hertz() float64

Hertz returns the frequency of bpm as bpm / 2.

type ByWT

type ByWT []*Input

func (ByWT) Len

func (a ByWT) Len() int

func (ByWT) Less

func (a ByWT) Less(i, j int) bool

func (ByWT) Slice

func (a ByWT) Slice() (sl [][]*Input)

func (ByWT) Swap

func (a ByWT) Swap(i, j int)

type Comb

type Comb struct {
	// contains filtered or unexported fields
}

Comb adds a delayed version of a signal onto itself.

func NewComb

func NewComb(gain float64, d time.Duration, in Sound) *Comb

func (Comb) At

func (sd Comb) At(t float64) float64

func (Comb) Channels

func (sd Comb) Channels() int

func (Comb) Index

func (sd Comb) Index(i int) float64

func (Comb) Inputs

func (sd Comb) Inputs() []Sound

func (Comb) Interp

func (sd Comb) Interp(t float64) float64

func (Comb) IsOff

func (sd Comb) IsOff() bool

func (Comb) Off

func (sd Comb) Off()

func (Comb) On

func (sd Comb) On()

func (*Comb) Prepare

func (cmb *Comb) Prepare(uint64)

func (Comb) SampleRate

func (sd Comb) SampleRate() float64

func (Comb) Samples

func (sd Comb) Samples() signal.Discrete

type Damp

type Damp struct {
	// contains filtered or unexported fields
}

func NewDamp

func NewDamp(d time.Duration, in Sound) *Damp

func (Damp) At

func (sd Damp) At(t float64) float64

func (Damp) Channels

func (sd Damp) Channels() int

func (Damp) Index

func (sd Damp) Index(i int) float64

func (Damp) Inputs

func (sd Damp) Inputs() []Sound

func (Damp) Interp

func (sd Damp) Interp(t float64) float64

func (Damp) IsOff

func (sd Damp) IsOff() bool

func (Damp) Off

func (sd Damp) Off()

func (Damp) On

func (sd Damp) On()

func (*Damp) Prepare

func (dmp *Damp) Prepare(uint64)

func (Damp) SampleRate

func (sd Damp) SampleRate() float64

func (Damp) Samples

func (sd Damp) Samples() signal.Discrete

type Decibel

type Decibel float64

Decibel is relative to full scale; anything over 0dB will clip.

func (Decibel) Amp

func (db Decibel) Amp() float64

Amp converts dB to amplitude multiplier.

func (Decibel) String

func (db Decibel) String() string

type Delay

type Delay struct {
	// contains filtered or unexported fields
}

Delay represents a signal delayed by a given duration.

func NewDelay

func NewDelay(d time.Duration, in Sound) *Delay

NewDelay returns Delay with sample buffer of a length approximated by d.

func (Delay) At

func (sd Delay) At(t float64) float64

func (Delay) Channels

func (sd Delay) Channels() int

func (Delay) Index

func (sd Delay) Index(i int) float64

func (Delay) Inputs

func (sd Delay) Inputs() []Sound

func (Delay) Interp

func (sd Delay) Interp(t float64) float64

func (Delay) IsOff

func (sd Delay) IsOff() bool

func (Delay) Off

func (sd Delay) Off()

func (Delay) On

func (sd Delay) On()

func (*Delay) Prepare

func (dly *Delay) Prepare(uint64)

func (Delay) SampleRate

func (sd Delay) SampleRate() float64

func (Delay) Samples

func (sd Delay) Samples() signal.Discrete

type Dispatcher

type Dispatcher struct{ sync.WaitGroup }

func (*Dispatcher) Dispatch

func (dp *Dispatcher) Dispatch(tc uint64, inps ...*Input)

Dispatch blocks until all inputs are prepared.

type Drive

type Drive struct {
	// contains filtered or unexported fields
}

func NewDrive

func NewDrive(d time.Duration, in Sound) *Drive

func (Drive) At

func (sd Drive) At(t float64) float64

func (Drive) Channels

func (sd Drive) Channels() int

func (Drive) Index

func (sd Drive) Index(i int) float64

func (Drive) Inputs

func (sd Drive) Inputs() []Sound

func (Drive) Interp

func (sd Drive) Interp(t float64) float64

func (Drive) IsOff

func (sd Drive) IsOff() bool

func (Drive) Off

func (sd Drive) Off()

func (Drive) On

func (sd Drive) On()

func (*Drive) Prepare

func (drv *Drive) Prepare(uint64)

func (Drive) SampleRate

func (sd Drive) SampleRate() float64

func (Drive) Samples

func (sd Drive) Samples() signal.Discrete

type Freeze

type Freeze struct {
	// contains filtered or unexported fields
}

func NewFreeze

func NewFreeze(d time.Duration, in Sound) *Freeze

func (Freeze) At

func (sd Freeze) At(t float64) float64

func (Freeze) Channels

func (sd Freeze) Channels() int

func (Freeze) Index

func (sd Freeze) Index(i int) float64

func (Freeze) Inputs

func (sd Freeze) Inputs() []Sound

func (Freeze) Interp

func (sd Freeze) Interp(t float64) float64

func (Freeze) IsOff

func (sd Freeze) IsOff() bool

func (*Freeze) Off

func (frz *Freeze) Off()

func (Freeze) On

func (sd Freeze) On()

func (*Freeze) Prepare

func (frz *Freeze) Prepare(uint64)

func (*Freeze) Restart

func (frz *Freeze) Restart()

func (Freeze) SampleRate

func (sd Freeze) SampleRate() float64

func (Freeze) Samples

func (sd Freeze) Samples() signal.Discrete

type Gain

type Gain struct {
	// contains filtered or unexported fields
}

func NewGain

func NewGain(a float64, in Sound) *Gain

func (Gain) At

func (sd Gain) At(t float64) float64

func (Gain) Channels

func (sd Gain) Channels() int

func (Gain) Index

func (sd Gain) Index(i int) float64

func (Gain) Inputs

func (sd Gain) Inputs() []Sound

func (Gain) Interp

func (sd Gain) Interp(t float64) float64

func (Gain) IsOff

func (sd Gain) IsOff() bool

func (Gain) Off

func (sd Gain) Off()

func (Gain) On

func (sd Gain) On()

func (*Gain) Prepare

func (gn *Gain) Prepare(uint64)

func (Gain) SampleRate

func (sd Gain) SampleRate() float64

func (Gain) Samples

func (sd Gain) Samples() signal.Discrete

func (*Gain) SetAmp

func (gn *Gain) SetAmp(a float64)

type Hertz

type Hertz float64

Hertz is defined as cycles per second and is synonymous with frequency.

func (Hertz) Angular

func (hz Hertz) Angular() float64

Angular returns the angular frequency as 2 * pi * hz and is synonymous with radians.

func (Hertz) Normalized

func (hz Hertz) Normalized(sr float64) float64

Normalized returns the angular frequency of hz divided by the sample rate sr.

func (Hertz) Period

func (hz Hertz) Period() float64

func (Hertz) String

func (hz Hertz) String() string

type Input

type Input struct {
	// contains filtered or unexported fields
}

func GetInputs

func GetInputs(sd Sound) []*Input

type Instrument

type Instrument struct {
	// contains filtered or unexported fields
}

func NewInstrument

func NewInstrument(in Sound) *Instrument

func (Instrument) At

func (sd Instrument) At(t float64) float64

func (Instrument) Channels

func (sd Instrument) Channels() int

func (Instrument) Index

func (sd Instrument) Index(i int) float64

func (Instrument) Inputs

func (sd Instrument) Inputs() []Sound

func (Instrument) Interp

func (sd Instrument) Interp(t float64) float64

func (Instrument) IsOff

func (sd Instrument) IsOff() bool

func (Instrument) Off

func (sd Instrument) Off()

func (*Instrument) OffIn

func (nst *Instrument) OffIn(d time.Duration)

func (*Instrument) On

func (nst *Instrument) On()

func (*Instrument) Prepare

func (nst *Instrument) Prepare(uint64)

func (Instrument) SampleRate

func (sd Instrument) SampleRate() float64

func (Instrument) Samples

func (sd Instrument) Samples() signal.Discrete

type Loop

type Loop struct {
	// contains filtered or unexported fields
}

Loop records a signal by a given duration, repeating the recording in subsequent playback.

TODO cross-fade

func NewLoop

func NewLoop(d time.Duration, in Sound) *Loop

NewLoop returns a Loop with sample buffer of a length approximated by d.

func NewLoopFrames

func NewLoopFrames(nframes int, in Sound) *Loop

NewLoopFrames return a Loop with sample buffer of length nframes.

func (Loop) At

func (sd Loop) At(t float64) float64

func (Loop) Channels

func (sd Loop) Channels() int

func (Loop) Index

func (sd Loop) Index(i int) float64

func (Loop) Inputs

func (sd Loop) Inputs() []Sound

func (Loop) Interp

func (sd Loop) Interp(t float64) float64

func (Loop) IsOff

func (sd Loop) IsOff() bool

func (Loop) Off

func (sd Loop) Off()

func (Loop) On

func (sd Loop) On()

func (*Loop) Prepare

func (lp *Loop) Prepare(tc uint64)

func (*Loop) Record

func (lp *Loop) Record()

func (*Loop) Recording

func (lp *Loop) Recording() bool

func (Loop) SampleRate

func (sd Loop) SampleRate() float64

func (Loop) Samples

func (sd Loop) Samples() signal.Discrete

func (*Loop) SetBPM

func (lp *Loop) SetBPM(bpm BPM)

func (*Loop) Stop

func (lp *Loop) Stop()

func (*Loop) Syncing

func (lp *Loop) Syncing() bool

type LowPass

type LowPass struct {
	// contains filtered or unexported fields
}

LowPass is a 3rd order IIR filter.

Recursive implementation of the Gaussian filter.

func NewLowPass

func NewLowPass(freq float64, in Sound) *LowPass

func (LowPass) At

func (sd LowPass) At(t float64) float64

func (LowPass) Channels

func (sd LowPass) Channels() int

func (LowPass) Index

func (sd LowPass) Index(i int) float64

func (LowPass) Inputs

func (sd LowPass) Inputs() []Sound

func (LowPass) Interp

func (sd LowPass) Interp(t float64) float64

func (LowPass) IsOff

func (sd LowPass) IsOff() bool

func (LowPass) Off

func (sd LowPass) Off()

func (LowPass) On

func (sd LowPass) On()

func (*LowPass) Passthrough

func (lp *LowPass) Passthrough() bool

func (*LowPass) Prepare

func (lp *LowPass) Prepare(uint64)

func (LowPass) SampleRate

func (sd LowPass) SampleRate() float64

func (LowPass) Samples

func (sd LowPass) Samples() signal.Discrete

func (*LowPass) SetPassthrough

func (lp *LowPass) SetPassthrough(b bool)

type Mixer

type Mixer struct {
	// contains filtered or unexported fields
}

TODO should mixer be stereo out? TODO perhaps this class is unnecessary, any sound could be a mixer if you can set multiple inputs, but might get confusing. TODO consider embedding Gain type

func NewMixer

func NewMixer(ins ...Sound) *Mixer

func (*Mixer) Append

func (mix *Mixer) Append(s ...Sound)

func (Mixer) At

func (sd Mixer) At(t float64) float64

func (Mixer) Channels

func (sd Mixer) Channels() int

func (*Mixer) Empty

func (mix *Mixer) Empty()

func (Mixer) Index

func (sd Mixer) Index(i int) float64

func (*Mixer) Inputs

func (mix *Mixer) Inputs() []Sound

func (Mixer) Interp

func (sd Mixer) Interp(t float64) float64

func (Mixer) IsOff

func (sd Mixer) IsOff() bool

func (Mixer) Off

func (sd Mixer) Off()

func (Mixer) On

func (sd Mixer) On()

func (*Mixer) Prepare

func (mix *Mixer) Prepare(uint64)

func (Mixer) SampleRate

func (sd Mixer) SampleRate() float64

func (Mixer) Samples

func (sd Mixer) Samples() signal.Discrete

type Notes

type Notes []float64

Notes is a collection of note function evaluations.

func EqualTempermant

func EqualTempermant(tones int, freq float64, pos int) Notes

EqualTempermant is a helper function for returning an evaluated Notes.

func (*Notes) Eval

func (ns *Notes) Eval(tones int, freq float64, pos int, fn NotesFunc)

Eval evaluates fn over the length of ns. If ns is nil, ns will be allocated with length DefaultNotesLen.

type NotesFunc

type NotesFunc func(ns Notes, tones int, freq float64, pos int)

NotesFunc defines a note function to be evaluated over the length of ns.

type Oscil

type Oscil struct {
	// contains filtered or unexported fields
}

func NewOscil

func NewOscil(in signal.Discrete, freq float64, freqmod Sound) *Oscil

func (Oscil) At

func (sd Oscil) At(t float64) float64

func (Oscil) Channels

func (sd Oscil) Channels() int

func (Oscil) Index

func (sd Oscil) Index(i int) float64

func (*Oscil) Inputs

func (osc *Oscil) Inputs() []Sound

func (Oscil) Interp

func (sd Oscil) Interp(t float64) float64

func (Oscil) IsOff

func (sd Oscil) IsOff() bool

func (Oscil) Off

func (sd Oscil) Off()

func (Oscil) On

func (sd Oscil) On()

func (*Oscil) Prepare

func (osc *Oscil) Prepare(tc uint64)

func (Oscil) SampleRate

func (sd Oscil) SampleRate() float64

func (Oscil) Samples

func (sd Oscil) Samples() signal.Discrete

func (*Oscil) SetAmp

func (osc *Oscil) SetAmp(fac float64, mod Sound)

func (*Oscil) SetFreq

func (osc *Oscil) SetFreq(hz float64, mod Sound)

func (*Oscil) SetPhase

func (osc *Oscil) SetPhase(mod Sound)

type Pan

type Pan struct {
	// contains filtered or unexported fields
}

func NewPan

func NewPan(xf float64, in Sound) *Pan

func (Pan) At

func (sd Pan) At(t float64) float64

func (Pan) Channels

func (sd Pan) Channels() int

func (Pan) Index

func (sd Pan) Index(i int) float64

func (Pan) Inputs

func (sd Pan) Inputs() []Sound

func (Pan) Interp

func (sd Pan) Interp(t float64) float64

func (Pan) IsOff

func (sd Pan) IsOff() bool

func (Pan) Off

func (sd Pan) Off()

func (Pan) On

func (sd Pan) On()

func (*Pan) Prepare

func (pan *Pan) Prepare(uint64)

Prepare interleaves the left and right channels.

func (Pan) SampleRate

func (sd Pan) SampleRate() float64

func (Pan) Samples

func (sd Pan) Samples() signal.Discrete

func (*Pan) SetAmount

func (pan *Pan) SetAmount(xf float64)

SetAmount sets amount an input is panned across two outputs where amt belongs to [-1..1].

type Player

type Player struct {
	// contains filtered or unexported fields
}

func NewPlayer

func NewPlayer(in Sound) *Player

func (*Player) Channels

func (p *Player) Channels() uint32

func (*Player) Notify

func (p *Player) Notify()

func (*Player) Read

func (p *Player) Read(bin []byte) (int, error)

func (*Player) SampleRate

func (p *Player) SampleRate() uint32

func (*Player) Start

func (pl *Player) Start() error

func (*Player) Stop

func (pl *Player) Stop()

type Ring

type Ring struct {
	// contains filtered or unexported fields
}

Ring modulator

func NewRing

func NewRing(in0, in1 Sound) *Ring

func (Ring) At

func (sd Ring) At(t float64) float64

func (Ring) Channels

func (sd Ring) Channels() int

func (Ring) Index

func (sd Ring) Index(i int) float64

func (*Ring) Inputs

func (ng *Ring) Inputs() []Sound

func (Ring) Interp

func (sd Ring) Interp(t float64) float64

func (Ring) IsOff

func (sd Ring) IsOff() bool

func (Ring) Off

func (sd Ring) Off()

func (Ring) On

func (sd Ring) On()

func (*Ring) Prepare

func (ng *Ring) Prepare(uint64)

func (Ring) SampleRate

func (sd Ring) SampleRate() float64

func (Ring) Samples

func (sd Ring) Samples() signal.Discrete

type Sound

type Sound interface {

	// Channels returns the frame size in samples of the internal buffer.
	Channels() int

	// SampleRate returns the number of digital samples of sound pressure per second.
	SampleRate() float64

	// Prepare is when a sound should prepare sample frames.
	Prepare(uint64)

	// Inputs should return all inputs a Sound wants discovered by a dispatcher.
	// TODO consider other methods for handling this, check book multidimensional data structures
	Inputs() []Sound

	// Samples returns prepared samples slice.
	//
	// TODO maybe ditch this, point of architecture is you can't mess
	// with an input's output but a slice exposes that. Or, discourage
	// use by making a copy of data.
	// TODO rename to Data()? So, Buffer.Data()
	Samples() signal.Discrete

	Interp(t float64) float64

	At(t float64) float64

	Index(i int) float64
}

TODO rename as Buffer? TODO what about handling []byte instead of []float? Sound represents a type capable of producing sound data.

type Tap

type Tap struct {
	// contains filtered or unexported fields
}

Tap is a tapped delay line, essentially a shorter delay within a larger one.

TODO consider some type of method on Delay instead of a separate type. For example, Tap intentionally does not expose dly via Inputs() so why is it its own type? Conversely, that'd make Delay a mixer of sorts.

func NewTap

func NewTap(d time.Duration, in *Delay) *Tap

func (Tap) At

func (sd Tap) At(t float64) float64

func (Tap) Channels

func (sd Tap) Channels() int

func (Tap) Index

func (sd Tap) Index(i int) float64

func (Tap) Inputs

func (sd Tap) Inputs() []Sound

func (Tap) Interp

func (sd Tap) Interp(t float64) float64

func (Tap) IsOff

func (sd Tap) IsOff() bool

func (Tap) Off

func (sd Tap) Off()

func (Tap) On

func (sd Tap) On()

func (*Tap) Prepare

func (tap *Tap) Prepare(uint64)

func (Tap) SampleRate

func (sd Tap) SampleRate() float64

func (Tap) Samples

func (sd Tap) Samples() signal.Discrete

Directories

Path Synopsis
example
exp

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL