sounds

package
v0.0.0-...-921490c Latest Latest
Warning

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

Go to latest
Published: Oct 12, 2015 License: GPL-3.0 Imports: 11 Imported by: 0

Documentation

Overview

Package sounds provides the basic types for Sounds within this system, plus multiple implementations of different sounds that can be used.

Index

Constants

View Source
const (
	// The sample rate of each sound stream.
	CyclesPerSecond = 44100.0

	// Inverse sample rate.
	SecondsPerCycle = 1.0 / CyclesPerSecond

	// Inverse sample rate as a golang duration
	// DurationPerCycle = int64(SecondsPerCycle * 1e9) * time.Nanosecond
	DurationPerCycle = 22675 * time.Nanosecond // BIG HACK

	// The number of samples in the maximum duration.
	MaxLength = uint64(406750706825295)

	// Maximum duration, used for unending sounds.
	MaxDuration = time.Duration(int64(float64(MaxLength)*SecondsPerCycle*1e9)) * time.Nanosecond
)

Variables

This section is empty.

Functions

func DurationToSamples

func DurationToSamples(duration time.Duration) uint64

func SamplesToDuration

func SamplesToDuration(sampleCount uint64) time.Duration

SamplesToDuration converts a sample count to a duration of time.

func SawtoothMap

func SawtoothMap(at float64) float64

func SineMap

func SineMap(at float64) float64

Below are some sample mappers used for generating various useful shapes.

func SquareMap

func SquareMap(at float64) float64

func TriangleMap

func TriangleMap(at float64) float64

Types

type BaseSound

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

A BaseSound manages state around the definition, and adapts all the Sound methods.

func (*BaseSound) Duration

func (s *BaseSound) Duration() time.Duration

Duration returns the duration of time the sound runs for.

func (*BaseSound) GetSamples

func (s *BaseSound) GetSamples() <-chan float64

GetSamples returns the samples for this sound, valid between a Start() and Stop()

func (*BaseSound) Length

func (s *BaseSound) Length() uint64

Length returns the provided number of samples for this sound.

func (*BaseSound) Reset

func (s *BaseSound) Reset()

Reset clears all the state in a stopped sound back to pre-Start values.

func (*BaseSound) Running

func (s *BaseSound) Running() bool

Running returns whether Sound is still generating samples.

func (*BaseSound) Start

func (s *BaseSound) Start()

Start begins the Sound by initialzing the channel, running the definition on a separate goroutine, and cleaning up once it has finished.

func (*BaseSound) Stop

func (s *BaseSound) Stop()

Stop ends the sound, preventing any more samples from being written.

func (*BaseSound) String

func (s *BaseSound) String() string

String returns the textual representation

func (*BaseSound) WriteSample

func (s *BaseSound) WriteSample(sample float64) bool

WriteSample appends a sample to the channel, returning whether the write was successful.

type ChannelSound

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

A ChannelSound a sound of unknown length that is generated by a provided channel.

func (*ChannelSound) Duration

func (s *ChannelSound) Duration() time.Duration

func (*ChannelSound) GetSamples

func (s *ChannelSound) GetSamples() <-chan float64

func (*ChannelSound) Length

func (s *ChannelSound) Length() uint64

func (*ChannelSound) Reset

func (s *ChannelSound) Reset()

func (*ChannelSound) Running

func (s *ChannelSound) Running() bool

func (*ChannelSound) Start

func (s *ChannelSound) Start()

func (*ChannelSound) Stop

func (s *ChannelSound) Stop()

func (*ChannelSound) String

func (s *ChannelSound) String() string

type MidiInput

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

A MidiInput is a sound that is wrapping a portmidi Midi input device.

func (*MidiInput) Duration

func (s *MidiInput) Duration() time.Duration

Duration returns the duration of time the sound runs for, unknown as above.

func (*MidiInput) GetSamples

func (s *MidiInput) GetSamples() <-chan float64

GetSamples returns the samples for this sound, valid between a Start() and Stop()

func (*MidiInput) Length

func (s *MidiInput) Length() uint64

Length returns the number of samples - unknown in advance, so it returns MaxLength.

func (*MidiInput) Reset

func (s *MidiInput) Reset()

Reset unsupported for the MIDI stream.

func (*MidiInput) Running

func (s *MidiInput) Running() bool

Running returns whether Sound is still generating samples.

func (*MidiInput) Start

func (s *MidiInput) Start()

Start begins the Sound by opening two goroutines - one to take a set of active notes and convert it into sampled sine waves at the right frequencies, and the second to to listen to the midi input stream of events and convert that into the live set of active notes.

func (*MidiInput) Stop

func (s *MidiInput) Stop()

Stop ends the sound, preventing any more samples from being written.

func (*MidiInput) String

func (s *MidiInput) String() string

String returns the textual representation

type SimpleSampleMap

type SimpleSampleMap func(float64) float64

type Sound

type Sound interface {
	// Sound wave samples for the sound - only valid after Start() and before Stop()
	// NOTE: Only one sink should read from GetSamples()!! Otherwise it will not receive every sample.
	GetSamples() <-chan float64

	// Number of samples in this sound - MaxLength of unlimited.
	Length() uint64

	// Length of time this goes for. Convenience method, should always be SamplesToDuration(Length())
	Duration() time.Duration

	// Start begins writing the sound wave to the samples channel.
	Start()

	// Running indicates whether a sound has Start()'d but not yet Stop()'d
	Running() bool

	// Stop ceases writing samples, and closes the channel.
	Stop()

	// Reset converts the sound back to the pre-Start() state. Can only be called on a Stop()'d Sound.
	Reset()
}

A Sound is a model of a physical sound wave as a series of pressure changes over time.

Each Sound contains a channel of samples in the range [-1, 1] of the intensity at each time step, as well as a count of samples, which then also defines how long the sound lasts.

Sounds also provide a way to start and stop when the samples are written, and reset to an initial state.

func AddDelay

func AddDelay(wrapped Sound, delayMs float64) Sound

AddDelay takes a sound, and adds it with a delayed version of itself after a given duration.

For example, to have a three note progression with a delay of 123ms:

s.AddDelay(s.ConcatSounds(
  s.NewTimedSound(u.MidiToSound(55), 678),
  s.NewTimedSound(u.MidiToSound(59), 678),
  s.NewTimedSound(u.MidiToSound(62), 678),
), 123)

func ConcatSounds

func ConcatSounds(wrapped ...Sound) Sound

ConcatSounds creates a sound by concatenating multiple sounds in series.

For example, to create the 5-note sequence from Close Enounters:

s := sounds.ConcatSounds(
	sounds.NewTimedSound(sounds.MidiToSound(74), 400),
	sounds.NewTimedSound(sounds.MidiToSound(76), 400),
	sounds.NewTimedSound(sounds.MidiToSound(72), 400),
	sounds.NewTimedSound(sounds.MidiToSound(60), 400),
	sounds.NewTimedSound(sounds.MidiToSound(67), 1200),
)

func LinearSample

func LinearSample(wrapped Sound, pitchScale float64) Sound

LinearSample wraps an existing sound and samples it at a different rate, modifying both its pitch and duration.

For example, to modulate a sound up an octave, and make it half as long:

s := ...some sound...
higher := sounds.LinearSample(s, 2.0)

func LoadFlacAsSound

func LoadFlacAsSound(path string) Sound

LoadFlacAsSound loads a .flac file and converts the average of its channels to a Sound.

For example, to read the first channel from a local file at 'piano.flac':

sounds.LoadFlacAsSound("piano.flac")

func LoadWavAsSound

func LoadWavAsSound(path string, channel uint16) Sound

LoadWavAsSound loads a .wav file and converts one of its channels into a Sound.

For example, to read the first channel from a local file at 'piano.wav':

sounds.LoadWavAsSound("piano.wav", 0)

func MultiplyWithClip

func MultiplyWithClip(wrapped Sound, factor float64) Sound

MultiplyWithClip wraps an existing sound and scales its amplitude by a given factory, clipping the result to [-1, 1].

For example, to create a sound half as loud as the default E5 sine wave:

s := sounds.MultiplyWithClip(sounds.NewSineWave(659.25), 0.5)

func NewADSREnvelope

func NewADSREnvelope(wrapped Sound,
	attackMs float64, delayMs float64, sustainLevel float64, releaseMs float64) Sound

NewADSREnvelope wraps an existing sound with a parametric envelope.

For details, read https://en.wikipedia.org/wiki/Synthesizer#ADSR_envelope

For example, to create an envelope around an A440 note

s := sounds.NewADSREnvelope(
	sounds.NewTimedSound(sounds.NewSineWave(261.63), 1000),
	50, 200, 0.5, 100)

func NewBaseSound

func NewBaseSound(def SoundDefinition, sampleCount uint64) Sound

NewBaseSound takes a simpler definition of a sound, plus a duration, and converts them into something that implements the Sound interface.

func NewDenseIIR

func NewDenseIIR(wrapped Sound, inCoef []float64, outCoef []float64) Sound

NewDenseIIR wrapps a sound in an IIR filter, as specified by the coefficients. TODO(padster): Also implement the filter design algorithms, e.g:

http://engineerjs.com/?sidebar=docs/iir.html
http://www.mikroe.com/chapters/view/73/chapter-3-iir-filters/
http://www-users.cs.york.ac.uk/~fisher/mkfilter/

For example, to use a high-pass filter for 800hz+ with sample rate of 44.1k:

sound := s.NewDenseIIR(...some sound...,
  []float64{0.8922, -2.677, 2.677, -0.8922},
  []float64{2.772, -2.57, 0.7961},
)

func NewHzFromChannel

func NewHzFromChannel(wrapped <-chan float64) Sound

NewHzFromChannel takes stream of hz values, and generates a tone that sounds like those values over time. For a fixed tone, see NewSineWave.

func NewHzFromChannelWithAmplitude

func NewHzFromChannelWithAmplitude(wrappedWithAmplitute <-chan []float64) Sound

func NewMidiInput

func NewMidiInput(deviceId pm.DeviceId) Sound

NewMidiInput takes a given midi device and converts it into a sound that plays what the device is playing (as sine waves), and stops once a pitch-bend is received.

func NewSawtoothWave

func NewSawtoothWave(hz float64) Sound

NewSawtoothWave creates an unending sawtooth pattern (-1->1 then resets to -1.)

func NewSilence

func NewSilence() Sound

NewSilence creates an unending sound that is inaudible.

func NewSimpleWave

func NewSimpleWave(hz float64, mapper SimpleSampleMap) Sound

NewSimpleWave creates an unending repeating sound based on cycles defined by a given mapping function. For examples of usage, see sine/square/sawtooth/triangle waves below.

func NewSineWave

func NewSineWave(hz float64) Sound

NewSineWave creates an unending sinusoid at a given pitch (in hz).

For example, to create a sound represeting A440:

s := sounds.NewSineWave(440)

func NewSquareWave

func NewSquareWave(hz float64) Sound

NewSquareWave creates an unending [-1, 1] square wave at a given pitch.

func NewTimedSilence

func NewTimedSilence(durationMs float64) Sound

NewTimedSilence creates a silence that lasts for a given duration.

For example, Cage's 4'33" can be generated using:

s := sounds.NewTimedSilence(273000)

func NewTimedSound

func NewTimedSound(wrapped Sound, durationMs float64) Sound

NewSilence wraps an existing sound as something that stops after a given duration.

For example, to create a sound of middle C that lasts a second:

s := sounds.NewTimedSound(sounds.NewSineWave(261.63), 1000)

func NewTriangleWave

func NewTriangleWave(hz float64) Sound

NewTriangleWave creates an unending triangle pattern (-1->1->-1 linearly)

func RepeatSound

func RepeatSound(wrapped Sound, loopCount int32) Sound

RepeatSound forms a sound by repeating a given sound a number of times in series.

For example, for the cello part of Pachelbel's Canon in D:

sound := s.RepeatSound(s.ConcatSounds(
	s.NewTimedSound(s.MidiToSound(50), 800),
	s.NewTimedSound(s.MidiToSound(45), 800),
	s.NewTimedSound(s.MidiToSound(47), 800),
	s.NewTimedSound(s.MidiToSound(42), 800),
	s.NewTimedSound(s.MidiToSound(43), 800),
	s.NewTimedSound(s.MidiToSound(38), 800),
	s.NewTimedSound(s.MidiToSound(43), 800),
	s.NewTimedSound(s.MidiToSound(45), 800),
), -1 /* repeat indefinitely */)

func SumSounds

func SumSounds(wrapped ...Sound) Sound

SumSounds creates a sound by adding multiple sounds in parallel, playing them at the same time and normalizing their volume.

For example, to play a G7 chord for a second:

s := sounds.SumSounds(
	sounds.NewTimedSound(sounds.MidiToSound(55), 1000),
	sounds.NewTimedSound(sounds.MidiToSound(59), 1000),
	sounds.NewTimedSound(sounds.MidiToSound(62), 1000),
	sounds.NewTimedSound(sounds.MidiToSound(65), 1000),
	sounds.NewTimedSound(sounds.MidiToSound(67), 1000),
)

func WrapChannelAsSound

func WrapChannelAsSound(samples <-chan float64) Sound

WrapChannelAsSound takes an input sample channel and adapts it to be a Sound.

For example, to play a sample channel:

output.Play(sounds.WrapChannelAsSound(..samples..))

type SoundDefinition

type SoundDefinition interface {
	// Run executes the normal logic of the sound, writing to base.WriteSample until it is false.
	Run(base *BaseSound)

	// Stop cleans up at the end of the Sound.
	Stop()

	// Reset rewrites all state to the same as before Run()
	Reset()
}

A SoundDefinition represents the simplified requirements that BaseSound converts into a Sound. If possible it is recommended that implementations be immutable, with all mutable state within Run().

Jump to

Keyboard shortcuts

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