resound

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 23, 2023 License: MIT Imports: 5 Imported by: 0

README ¶

Resound 🔉

Resound is a library for applying sound effects when playing sounds back with Ebitengine. Resound was made primarily for game development, as you might expect.

Why did you make this?

C'mon man, you already know what it is

The general advantages of using Resound is two-fold. Firstly, it allows you to easily add non-standard effects (like low-pass filtering, distortion, or panning) to sound or music playback. Secondly, it allows you to easily apply these effects across multiple groups of sounds, like a DSP. The general idea of using buses / channels is, again, taken from how Godot does things, along with other DAWs and music creation tools, like Renoise, Reason, and SunVox.

How do I use it?

There's a couple of different ways.

  1. Create effects and play an audio stream through them. The effects themselves satisfy io.ReadSeeker, like an ordinary audio stream from Ebitengine, so you can chain them together.

// Let's assume our sound is read in or embedded as a series of bytes.
var soundBytes []byte

const sampleRate = 44100

func main() {

    // So first, we'll create an audio context, decode some bytes into a stream,
    // create a loop, etc. 
    context := audio.NewContext(sampleRate)

    reader := bytes.NewReader(soundBytes)

	stream, err := vorbis.DecodeWithSampleRate(sampleRate, reader)

	if err != nil {
		panic(err)
	}

	loop := audio.NewInfiniteLoop(stream, stream.Length())

    // But here, we'll create a Delay effect and apply it.
    delay := resound.NewDelay(loop).SetWait(0.1).SetStrength(0.2)

    // Effects in Resound wrap streams (including other effects), so you can just use them
    // like you would an ordinary audio stream in Ebitengine.

    // You can also easily chain effects by using resound.ChainEffects().

    // Now we create a new player of the loop + delay:
	player, err := audio.NewPlayer(context, delay)

	if err != nil {
		panic(err)
	}

    // Play it, and you're good to go.
	player.Play()

}

  1. Apply effects to a DSP Channel, and then play sounds through there. This allows you to automatically play sounds back using various shared properties (a shared volume, shared panning, shared filter, etc).

// Let's, again, assume our sound is read in or embedded as a series of bytes.
var soundBytes []byte

// Here, though, we'll be creating a DSPChannel.
var dsp *resound.DSPChannel

const sampleRate = 44100

func main() {

    // So first, we'll create an audio context, decode some bytes into a stream,
    // create a loop, etc. 
    context := audio.NewContext(sampleRate)

    reader := bytes.NewReader(soundBytes)

    stream, err := vorbis.DecodeWithSampleRate(sampleRate, reader)

    if err != nil {
	panic(err)
    }

    loop := audio.NewInfiniteLoop(stream, stream.Length())

    // But here, we create a DSPChannel. A DSPChannel represents a group of effects
    // that sound streams play through. When playing a stream through a DSPChannel,
    // the stream takes on the effects applied to the DSPChannel. We don't have to
    // pass a stream to effects when used with a DSPChannel, because every stream
    // played through the channel takes the effect.
    dsp = resound.NewDSPChannel(context)
    dsp.AddEffect("delay", NewDelay(nil).SetWait(0.1).SetStrength(0.25))
    dsp.AddEffect("distort", NewDistort(nil).SetStrength(0.25))
    dsp.AddEffect("volume", NewVolume(nil).SetStrength(0.25))

    // Now we create a new player from the DSP channel. This will return a
    // *resound.ChannelPlayback object, which works similarly to an audio.Player
    // (in fact, it embeds the *audio.Player).
    player := dsp.CreatePlayer(loop)

    // Play it, and you're good to go, again - this time, it will run its playback
    // through the effect stack in the DSPChannel, in this case Delay > Distort > Volume.
	player.Play()

}

What is implemented?

  • Global Stop - Tracking playing sounds to globally stop all sounds that are playing back
  • DSPChannel Stop - ^, but for a DSP channel
Effects
  • Volume
  • Pan
  • Delay
  • Distortion
  • Low-pass Filter
  • Bitcrush (?)
  • High-pass Filter
  • Reverb
  • Mix / Fade (between two streams, or between a stream and silence, and over a customizeable time)
  • Loop (like, looping a signal after so much time has passed)
  • Pitch shifting (playback using effectively a lower / higher sample rate?)
  • 3D Sound (quick and easy panning and volume adjustment based on distance from listener to source)
Generators
  • Silence

  • Static

    ... And whatever else may be necessary.

Known Issues

  • Currently, effects directly apply on top of streams, which means that any effects that could make streams longer (like reverbs or delays) will get cut off if the stream ends.

Documentation ¶

Index ¶

Constants ¶

This section is empty.

Variables ¶

This section is empty.

Functions ¶

This section is empty.

Types ¶

type Bitcrush ¶

type Bitcrush struct {
	Source io.ReadSeeker
	// contains filtered or unexported fields
}

Bitcrush is an effect that changes the pitch of the incoming audio byte stream.

func NewBitcrush ¶

func NewBitcrush(source io.ReadSeeker) *Bitcrush

NewBitcrush creates a new Bitcrush effect. source is the source stream to apply this effect to. If you add this effect to a DSPChannel, there's no need to pass a source, as it will take effect for whatever streams are played through the DSPChannel.

func (*Bitcrush) Active ¶

func (bitcrush *Bitcrush) Active() bool

Active returns if the effect is active.

func (*Bitcrush) ApplyEffect ¶

func (bitcrush *Bitcrush) ApplyEffect(p []byte)

func (*Bitcrush) Clone ¶

func (bitcrush *Bitcrush) Clone() IEffect

Clone clones the effect, returning an IEffect.

func (*Bitcrush) Read ¶

func (bitcrush *Bitcrush) Read(p []byte) (n int, err error)

func (*Bitcrush) Seek ¶

func (bitcrush *Bitcrush) Seek(offset int64, whence int) (int64, error)

func (*Bitcrush) SetActive ¶

func (bitcrush *Bitcrush) SetActive(active bool) *Bitcrush

SetActive sets the effect to be active.

func (*Bitcrush) SetSource ¶

func (bitcrush *Bitcrush) SetSource(source io.ReadSeeker)

func (*Bitcrush) SetStrength ¶

func (bitcrush *Bitcrush) SetStrength(bitcrushFactor float64) *Bitcrush

SetStrength sets the strength of the Bitcrush effect to the specified percentage.

func (*Bitcrush) Strength ¶

func (bitcrush *Bitcrush) Strength() float64

Strength returns the strength of the Bitcrush effect as a percentage.

type DSPChannel ¶

type DSPChannel struct {
	Active      bool
	Effects     map[string]IEffect
	EffectOrder []IEffect
}

DSPChannel represents a channel that can have various effects applied to it.

func NewDSPChannel ¶

func NewDSPChannel() *DSPChannel

NewDSPChannel returns a new DSPChannel.

func (*DSPChannel) Add ¶

func (dsp *DSPChannel) Add(name string, effect IEffect) *DSPChannel

Add adds the specified Effect to the DSPChannel under the given name. Note that effects added to DSPChannels don't need to specify source streams, as the DSPChannel automatically handles this.

func (*DSPChannel) CreatePlayer ¶

func (dsp *DSPChannel) CreatePlayer(sourceStream io.ReadSeeker) *DSPPlayer

CreatePlayer creates a new DSPPlayer to handle playback of a stream through the DSPChannel.

type DSPPlayer ¶

type DSPPlayer struct {
	*audio.Player
	Channel *DSPChannel
	Source  io.ReadSeeker
}

DSPPlayer embeds audio.Player and so has all of the functions and abilities of the default audio.Player while also applying effects placed on the source DSPChannel.

func (*DSPPlayer) Clone ¶

func (es *DSPPlayer) Clone() *DSPPlayer

Clone duplicates the DSPPlayer; note that the current playback values will not be cloned.

func (*DSPPlayer) Read ¶

func (es *DSPPlayer) Read(p []byte) (n int, err error)

func (*DSPPlayer) Seek ¶

func (es *DSPPlayer) Seek(offset int64, whence int) (int64, error)

type Delay ¶

type Delay struct {
	Source io.ReadSeeker
	// contains filtered or unexported fields
}

Delay is an effect that adds a delay to the sound.

func NewDelay ¶

func NewDelay(source io.ReadSeeker) *Delay

NewDelay creates a new Delay effect. If you add this effect to a DSPChannel, source can be nil, as it will take effect for whatever streams are played through the DSPChannel.

func (*Delay) Active ¶

func (delay *Delay) Active() bool

Active returns if the effect is active.

func (*Delay) ApplyEffect ¶

func (delay *Delay) ApplyEffect(p []byte)

func (*Delay) Clone ¶

func (delay *Delay) Clone() IEffect

Clone creates a clone of the Delay effect.

func (*Delay) FeedbackLoop ¶

func (delay *Delay) FeedbackLoop() bool

FeedbackLoop returns if the delay's results feed back into itself or not.

func (*Delay) Read ¶

func (delay *Delay) Read(p []byte) (n int, err error)

func (*Delay) Seek ¶

func (delay *Delay) Seek(offset int64, whence int) (int64, error)

func (*Delay) SetActive ¶

func (delay *Delay) SetActive(active bool) *Delay

SetActive sets the effect to be active.

func (*Delay) SetFeedbackLoop ¶

func (delay *Delay) SetFeedbackLoop(on bool) *Delay

SetFeedbackLoop sets the feedback loop of the delay. If set to on, the delay's results feed back into itself.

func (*Delay) SetSource ¶

func (delay *Delay) SetSource(source io.ReadSeeker)

func (*Delay) SetStrength ¶

func (delay *Delay) SetStrength(strength float64) *Delay

SetStrength sets the overall volume of the Delay effect as it's added on top of the original signal. 0 is the minimum value.

func (*Delay) SetWait ¶

func (delay *Delay) SetWait(waitTime float64) *Delay

SetWait sets the overall wait time of the Delay effect in seconds as it's added on top of the original signal. 0 is the minimum value.

func (*Delay) Strength ¶

func (delay *Delay) Strength() float64

Strength returns the strength of the Delay effect.

func (*Delay) Wait ¶

func (delay *Delay) Wait() float64

Wait returns the wait time of the Delay effect.

type Distort ¶

type Distort struct {
	Source io.ReadSeeker
	// contains filtered or unexported fields
}

Distort distorts the stream that plays through it, clipping the signal.

func NewDistort ¶

func NewDistort(source io.ReadSeeker) *Distort

NewDistort creates a new Distort effect. source is the source stream to apply the effect to. If you add this effect to a DSPChannel, you can pass nil as the source, as it will take effect for whatever streams are played through the DSPChannel.

func (*Distort) Active ¶

func (distort *Distort) Active() bool

Active returns if the effect is active.

func (*Distort) ApplyEffect ¶

func (distort *Distort) ApplyEffect(p []byte)

func (*Distort) Clone ¶

func (distort *Distort) Clone() IEffect

Clone clones the effect, returning an IEffect.

func (*Distort) Read ¶

func (distort *Distort) Read(p []byte) (n int, err error)

func (*Distort) Seek ¶

func (distort *Distort) Seek(offset int64, whence int) (int64, error)

func (*Distort) SetActive ¶

func (distort *Distort) SetActive(active bool) *Distort

SetActive sets the effect to be active.

func (*Distort) SetSource ¶

func (distort *Distort) SetSource(source io.ReadSeeker)

func (*Distort) SetStrength ¶

func (distort *Distort) SetStrength(strength float64) *Distort

SetStrength sets the overall strength of the Distort effect. 0 is the minimum value.

func (*Distort) Strength ¶

func (distort *Distort) Strength() float64

Strength returns the strength of the Distort effect.

type IEffect ¶

type IEffect interface {
	io.ReadSeeker
	ApplyEffect(data []byte)
	SetSource(io.ReadSeeker)
}

IEffect indicates an effect that implements io.ReadSeeker and generally takes effect on an existing audio stream. It represents the result of applying an effect to an audio stream, and is playable in its own right.

func ChainEffects ¶

func ChainEffects(effects ...IEffect) IEffect

ChainEffects chains multiple effects for you automatically, returning the last chained effect. Example: sfxChain := resound.Chain(

resound.NewDelay(nil).SetWait(0.2).SetStrength(0.5),
resound.NewPan(nil),
resound.NewVolume(nil),

) sfxChain at the end would be the Volume effect, which is being fed by the Pan effect, which is fed by the Delay effect.

type LowpassFilter ¶

type LowpassFilter struct {
	Source io.ReadSeeker
	// contains filtered or unexported fields
}

func NewLowpassFilter ¶

func NewLowpassFilter(source io.ReadSeeker) *LowpassFilter

NewLowpassFilter creates a new low-pass filter for the given source stream. If you add this effect to a DSPChannel, there's no need to pass a source, as it will take effect for whatever streams are played through the DSPChannel.

func (*LowpassFilter) Active ¶

func (lpf *LowpassFilter) Active() bool

Active returns if the effect is active.

func (*LowpassFilter) ApplyEffect ¶

func (lpf *LowpassFilter) ApplyEffect(p []byte)

func (*LowpassFilter) Clone ¶

func (lpf *LowpassFilter) Clone() IEffect

Clone clones the effect, returning an IEffect.

func (*LowpassFilter) Read ¶

func (lpf *LowpassFilter) Read(p []byte) (n int, err error)

func (*LowpassFilter) Seek ¶

func (lpf *LowpassFilter) Seek(offset int64, whence int) (int64, error)

func (*LowpassFilter) SetActive ¶

func (lpf *LowpassFilter) SetActive(active bool) *LowpassFilter

SetActive sets the effect to be active.

func (*LowpassFilter) SetSource ¶

func (lpf *LowpassFilter) SetSource(source io.ReadSeeker)

func (*LowpassFilter) SetStrength ¶

func (lpf *LowpassFilter) SetStrength(strength float64) *LowpassFilter

func (*LowpassFilter) Strength ¶

func (lpf *LowpassFilter) Strength() float64

type Pan ¶

type Pan struct {
	Source io.ReadSeeker
	// contains filtered or unexported fields
}

Pan is a panning effect, handling panning the sound between the left and right channels.

func NewPan ¶

func NewPan(source io.ReadSeeker) *Pan

NewPan creates a new Pan effect. source is the source stream to apply the effect on. Panning defaults to 0. If you add this effect to a DSPChannel, source can be nil, as it will take effect for whatever streams are played through the DSPChannel.

func (*Pan) Active ¶

func (pan *Pan) Active() bool

Active returns if the effect is active.

func (*Pan) ApplyEffect ¶

func (pan *Pan) ApplyEffect(p []byte)

func (*Pan) Clone ¶

func (pan *Pan) Clone() IEffect

Clone clones the effect, returning an IEffect.

func (*Pan) Pan ¶

func (pan *Pan) Pan() float64

Pan returns the panning value for the pan effect in a percentage, ranging from -1 (hard left) to 1 (hard right).

func (*Pan) Read ¶

func (pan *Pan) Read(p []byte) (n int, err error)

func (*Pan) Seek ¶

func (pan *Pan) Seek(offset int64, whence int) (int64, error)

func (*Pan) SetActive ¶

func (pan *Pan) SetActive(active bool) *Pan

SetActive sets the effect to be active.

func (*Pan) SetPan ¶

func (pan *Pan) SetPan(panPercent float64) *Pan

SetPan sets the panning percentage for the pan effect. The possible values range from -1 (hard left) to 1 (hard right).

func (*Pan) SetSource ¶

func (pan *Pan) SetSource(source io.ReadSeeker)

type Volume ¶

type Volume struct {
	Source io.ReadSeeker
	// contains filtered or unexported fields
}

Volume is an effect that changes the overall volume of the incoming audio byte stream.

func NewVolume ¶

func NewVolume(source io.ReadSeeker) *Volume

NewVolume creates a new Volume effect. source is the source stream to apply this effect to. If you add this effect to a DSPChannel, source can be nil, as it will take effect for whatever streams are played through the DSPChannel.

func (*Volume) Active ¶

func (volume *Volume) Active() bool

Active returns if the effect is active.

func (*Volume) ApplyEffect ¶

func (volume *Volume) ApplyEffect(p []byte)

func (*Volume) Clone ¶

func (volume *Volume) Clone() IEffect

Clone clones the effect, returning an IEffect.

func (*Volume) Read ¶

func (volume *Volume) Read(p []byte) (n int, err error)

func (*Volume) Seek ¶

func (volume *Volume) Seek(offset int64, whence int) (int64, error)

func (*Volume) SetActive ¶

func (volume *Volume) SetActive(active bool)

SetActive sets the effect to be active.

func (*Volume) SetSource ¶

func (volume *Volume) SetSource(source io.ReadSeeker)

func (*Volume) SetStrength ¶

func (volume *Volume) SetStrength(strength float64) *Volume

SetStrength sets the strength of the Volume effect to the specified percentage. The lowest possible value is 0.0, with 1.0 taking a 100% effect. The volume is altered on a sine-based easing curve. At over 100% volume, the sound is clipped as necessary.

func (*Volume) Strength ¶

func (volume *Volume) Strength() float64

Strength returns the strength of the Volume effect as a percentage.

Directories ¶

Path Synopsis
examples
dsp

Jump to

Keyboard shortcuts

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