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
- func DurationToSamples(duration time.Duration) uint64
- func SamplesToDuration(sampleCount uint64) time.Duration
- func SawtoothMap(at float64) float64
- func SineMap(at float64) float64
- func SquareMap(at float64) float64
- func TriangleMap(at float64) float64
- type BaseSound
- func (s *BaseSound) Duration() time.Duration
- func (s *BaseSound) GetSamples() <-chan float64
- func (s *BaseSound) Length() uint64
- func (s *BaseSound) Reset()
- func (s *BaseSound) Running() bool
- func (s *BaseSound) Start()
- func (s *BaseSound) Stop()
- func (s *BaseSound) String() string
- func (s *BaseSound) WriteSample(sample float64) bool
- type ChannelSound
- func (s *ChannelSound) Duration() time.Duration
- func (s *ChannelSound) GetSamples() <-chan float64
- func (s *ChannelSound) Length() uint64
- func (s *ChannelSound) Reset()
- func (s *ChannelSound) Running() bool
- func (s *ChannelSound) Start()
- func (s *ChannelSound) Stop()
- func (s *ChannelSound) String() string
- type SimpleSampleMap
- type Sound
- func AddDelay(wrapped Sound, delayMs float64) Sound
- func ConcatSounds(wrapped ...Sound) Sound
- func LinearSample(wrapped Sound, pitchScale float64) Sound
- func MultiplyWithClip(wrapped Sound, factor float64) Sound
- func NewADSREnvelope(wrapped Sound, attackMs float64, delayMs float64, sustainLevel float64, ...) Sound
- func NewBaseSound(def SoundDefinition, sampleCount uint64) Sound
- func NewDenseIIR(wrapped Sound, inCoef []float64, outCoef []float64) Sound
- func NewHzFromChannel(wrapped <-chan float64) Sound
- func NewHzFromChannelWithAmplitude(wrappedWithAmplitute <-chan []float64) Sound
- func NewKarplusStrong(hz float64, sustain float64) Sound
- func NewSawtoothWave(hz float64) Sound
- func NewSilence() Sound
- func NewSimpleWave(hz float64, mapper SimpleSampleMap) Sound
- func NewSineWave(hz float64) Sound
- func NewSquareWave(hz float64) Sound
- func NewTimedSilence(durationMs float64) Sound
- func NewTimedSound(wrapped Sound, durationMs float64) Sound
- func NewTriangleWave(hz float64) Sound
- func RepeatSound(wrapped Sound, loopCount int32) Sound
- func SumSounds(wrapped ...Sound) Sound
- func WrapChannelAsSound(samples <-chan float64) Sound
- func WrapSliceAsSound(samples []float64) Sound
- type SoundDefinition
Constants ¶
const ( // CyclesPerSecond is the sample rate of each sound stream. CyclesPerSecond = 44100.0 // SecondsPerCycle is the inverse sample rate. SecondsPerCycle = 1.0 / CyclesPerSecond // DurationPerCycle = int64(SecondsPerCycle * 1e9) * time.Nanosecond DurationPerCycle = 22675 * time.Nanosecond // BIG HACK // MaxLength represents the number of samples in the maximum duration. MaxLength = uint64(406750706825295) // MaxDuration is used for unending sounds. MaxDuration = time.Duration(int64(float64(MaxLength)*SecondsPerCycle*1e9)) * time.Nanosecond )
Variables ¶
This section is empty.
Functions ¶
func DurationToSamples ¶
DurationToSamples converts a duration of time to a sample count
func SamplesToDuration ¶
SamplesToDuration converts a sample count to a duration of time.
func SawtoothMap ¶
func TriangleMap ¶
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) GetSamples ¶
GetSamples returns the samples for this sound, valid between a Start() and Stop()
func (*BaseSound) Reset ¶
func (s *BaseSound) Reset()
Reset clears all the state in a stopped sound back to pre-Start values.
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) WriteSample ¶
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 SimpleSampleMap ¶
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 if 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 ¶
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 ¶
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 ¶
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 MultiplyWithClip ¶
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 ¶
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 ¶
NewHzFromChannel takes stream of hz values, and generates a tone that sounds like those values over time. For a fixed tone, see NewSineWave.
func NewKarplusStrong ¶
NewKarplusStrong creates a note at a given frequency by starting with white noise then feeding that back into itself with a delay, which ends up sounding like a string. See http://music.columbia.edu/cmc/MusicAndComputers/chapter4/04_09.php
For example, to create a string sound at 440hz that never gets quieter:
stringA := sounds.NewKarplusStrong(440.0, 1.0)
func NewSawtoothWave ¶
NewSawtoothWave creates an unending sawtooth pattern (-1->1 then resets to -1.)
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 ¶
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 ¶
NewSquareWave creates an unending [-1, 1] square wave at a given pitch.
func NewTimedSilence ¶
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 ¶
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 ¶
NewTriangleWave creates an unending triangle pattern (-1->1->-1 linearly)
func RepeatSound ¶
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 ¶
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 ¶
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..))
func WrapSliceAsSound ¶
WrapSliceAsSound wraps an already created slice of [-1, 1] as a sound.
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().