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 MidiInput
- type SimpleSampleMap
- type Sound
- func AddDelay(wrapped Sound, delayMs float64) Sound
- func ConcatSounds(wrapped ...Sound) Sound
- func LinearSample(wrapped Sound, pitchScale float64) Sound
- func LoadFlacAsSound(path string) Sound
- func LoadWavAsSound(path string, channel uint16) 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 NewMidiInput(deviceId pm.DeviceId) 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
- type SoundDefinition
Constants ¶
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 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 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 ¶
Duration returns the duration of time the sound runs for, unknown as above.
func (*MidiInput) GetSamples ¶
GetSamples returns the samples for this sound, valid between a Start() and Stop()
func (*MidiInput) Length ¶
Length returns the number of samples - unknown in advance, so it returns MaxLength.
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.
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 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 ¶
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 LoadFlacAsSound ¶
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 ¶
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 ¶
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 NewMidiInput ¶
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 ¶
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..))
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().