Documentation ¶
Index ¶
- Constants
- func NumHarmGains(ctx context.Context) int
- func SetNumHarmGains(rate int)
- func Trunc(f float32, n int) float32
- type Tone
- func NewSawtoothTone(ctx context.Context, frequency float32) Tone
- func NewSquareTone(ctx context.Context, frequency float32) Tone
- func NewTone(ctx context.Context) Tone
- func NewToneAt(ctx context.Context, frequency float32) Tone
- func NewToneFrom(ctx context.Context, note note.Note, octave int) Tone
- func NewToneWith(ctx context.Context, frequency float32, gain float32, harmonicGains []float32) Tone
- func NewTriangleTone(ctx context.Context, frequency float32) Tone
Examples ¶
Constants ¶
const ( // DefaultNumHarmGains is the default number of harmonic gains above the fundamental frequency // that are tracked for each tone. DefaultNumHarmGains = 20 )
const (
// Maximum number of significant figures to use when truncating decimals
MaxSigFigs = 6
)
Variables ¶
This section is empty.
Functions ¶
func NumHarmGains ¶
NumHarmGains returns the number of harmonic gains in a tone for this context, or 0 if no value is set.
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/tone" ) func main() { ctx := context.NewContext() numHarmGains := tone.NumHarmGains(ctx) fmt.Println(numHarmGains) }
Output: 20
func SetNumHarmGains ¶
func SetNumHarmGains(rate int)
SetNumHarmGains sets the global number of harmonic gains in a tone. All contexts created after this is called will use the value set here. The rate cannot be a negative numbers.
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/tone" ) func main() { for _, n := range []int{10, 100, tone.DefaultNumHarmGains} { tone.SetNumHarmGains(n) ctx := context.NewContext() numHarmGains := tone.NumHarmGains(ctx) fmt.Println(numHarmGains) } }
Output: 10 100 20
func Trunc ¶
Trunc truncates a decimal to have no more than n digits.
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/tone" ) func main() { frequencies := []float32{0.123456789, 0.987654321, 99.99} for _, f1 := range frequencies { f2 := tone.Trunc(f1, 3) f3 := tone.Trunc(f1, tone.MaxSigFigs) fmt.Println(f1, f2, f3) } }
Output: 0.12345679 0.123 0.123456 0.9876543 0.987 0.987654 99.99 99.9 99.99
Types ¶
type Tone ¶
type Tone struct { // Frequency is the tone's fundamental frequency. Frequency float32 // Gain is a multiplier that increases or decreases the amplitude of the tone. It is a ratio of // the amplitude of the output signal to the amplitude of the base signal. // // As an example, a value of 2 will double the signal's strength, while a value of 0.5 will // halve it. Gain float32 // HarmonicGains is a list of gains for each harmonic above the fundamental frequency. They are // ratios of the amplitude of the harmonic to the amplitude of the fundamental frequency. The // first element is the gain of the first harmonic (which has a frequency twice as high as the // fundamental frequency), the second element is the gain of the second harmonic (three times as // high), and so on. // // As an example, a value of 2 will double the amplitude of that harmonic relative to the // fundamental frequency, while a value of 0.5 will halve it. HarmonicGains []float32 }
A Tone represents a single tone, which is a fundamental frequency and its harmonics at regular intervals above it. A new tone must be created with NewTone before it can be used.
func NewSawtoothTone ¶
NewSawtoothTone creates a new tone that has a sawtooth waveform.
In a sawtooth waveform, each harmonic has a gain that is the inverse of its order. Because it is composed of every harmonic of the fundamental frequency, a sawtooth waveform is brighter and richer than other waveforms.
For example, a sawtooth tone that has a fundamental frequency (order=1) of 100Hz and a gain of 1 has these first four harmonics:
- Harmonic 1: order = 2, frequency = 200Hz, gain = 1/2
- Harmonic 2: order = 3, frequency = 300Hz, gain = 1/3
- Harmonic 3: order = 4, frequency = 400Hz, gain = 1/4
- Harmonic 4: order = 5, frequency = 500Hz, gain = 1/5
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/tone" ) func main() { ctx := context.NewContext() sawTone := tone.NewSawtoothTone(ctx, 440) fmt.Println(sawTone.Frequency, sawTone.Gain, sawTone.HarmonicGains) // Show the calculation for each harmonic gain. // gain = 1 / order gains := make([]float32, len(sawTone.HarmonicGains)) for i := range gains { gains[i] = tone.Trunc(1/float32(i+2), tone.MaxSigFigs) } fmt.Println(gains) }
Output: 440 0 [0.5 0.333333 0.25 0.2 0.166666 0.142857 0.125 0.111111 0.1 0.090909 0.0833333 0.076923 0.0714285 0.0666666 0.0625 0.0588235 0.0555555 0.0526315 0.05 0.047619] [0.5 0.333333 0.25 0.2 0.166666 0.142857 0.125 0.111111 0.1 0.090909 0.0833333 0.076923 0.0714285 0.0666666 0.0625 0.0588235 0.0555555 0.0526315 0.05 0.047619]
func NewSquareTone ¶
NewSquareTone creates a new tone that has a square waveform.
In a square waveform, the even-ordered harmonics have no gain, while the odd-ordered harmonics have gains that are the inverse of their orders. This waveform has a duty cycle of 50%, meaning that is has equal high and low periods.
For example, a square tone that has a fundamental frequency (order=1) of 100Hz and a gain of 1 has these first four harmonics:
- Harmonic 1: order = 2, frequency = 200Hz, gain = 0
- Harmonic 2: order = 3, frequency = 300Hz, gain = 1/3
- Harmonic 3: order = 4, frequency = 400Hz, gain = 0
- Harmonic 4: order = 5, frequency = 500Hz, gain = 1/5
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/tone" ) func main() { ctx := context.NewContext() sqrTone := tone.NewSquareTone(ctx, 440) fmt.Println(sqrTone.Frequency, sqrTone.Gain, sqrTone.HarmonicGains) // Show the calculation for each harmonic gain. // gain = 1 / order if order is odd, 0 if order is even gains := make([]float32, len(sqrTone.HarmonicGains)) for i := range gains { if i%2 > 0 { gains[i] = tone.Trunc(1/float32(i+2), tone.MaxSigFigs) } } fmt.Println(gains) }
Output: 440 0 [0 0.333333 0 0.2 0 0.142857 0 0.111111 0 0.090909 0 0.076923 0 0.0666666 0 0.0588235 0 0.0526315 0 0.047619] [0 0.333333 0 0.2 0 0.142857 0 0.111111 0 0.090909 0 0.076923 0 0.0666666 0 0.0588235 0 0.0526315 0 0.047619]
func NewTone ¶
NewTone initializes a tone with default/zero values.
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/tone" ) func main() { ctx := context.NewContext() tone := tone.NewTone(ctx) fmt.Println(tone.Frequency, tone.Gain, tone.HarmonicGains) }
Output: 0 0 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
func NewToneAt ¶
NewToneAt initializes a tone with the specified fundamental frequency.
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/tone" ) func main() { ctx := context.NewContext() tone := tone.NewToneAt(ctx, 523.2511) fmt.Println(tone.Frequency, tone.Gain, tone.HarmonicGains) }
Output: 523.2511 0 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
func NewToneFrom ¶
NewToneFrom initializes a tone from the specified note and octave.
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/note" "github.com/green-aloe/enobox/tone" ) func main() { ctx := context.NewContext() tone := tone.NewToneFrom(ctx, note.C, 5) fmt.Println(tone.Frequency, tone.Gain, tone.HarmonicGains) }
Output: 523.2511 0 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
func NewToneWith ¶
func NewToneWith(ctx context.Context, frequency float32, gain float32, harmonicGains []float32) Tone
NewToneWith initializes a tone with the specified fundamental frequency, gain, and harmonic gains. The harmonic gains are set directly in the tone, as opposed to allocating a new slice and copying over the values. If the number of harmonic gains provided does not match the number for the context, this grows or shrinks the slice to match the expected length.
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/tone" ) func main() { ctx := context.NewContext() for _, harmGains := range [][]float32{ {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}, {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0}, {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0}, } { tone := tone.NewToneWith(ctx, 440, 1, harmGains) fmt.Println(tone.Frequency, tone.Gain, tone.HarmonicGains) } }
Output: 440 1 [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 0 0 0 0 0 0 0 0 0 0] 440 1 [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2] 440 1 [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2]
func NewTriangleTone ¶
NewTriangleTone creates a new tone that has a triangle waveform.
In a triangle waveform, the even-ordered harmonics have no gain, while the odd-ordered harmonics have gains that are the inverse square of their orders (which also makes this waveform an integral of a square waveform). This leads to a much steeper roll-off than other waveforms.
For example, a triangle tone that has a fundamental frequency (order=1) of 100Hz and a gain of 1 has these first four harmonics:
- Harmonic 1: order = 2, frequency = 200Hz, gain = 0
- Harmonic 2: order = 3, frequency = 300Hz, gain = 1/9
- Harmonic 3: order = 4, frequency = 400Hz, gain = 0
- Harmonic 4: order = 5, frequency = 500Hz, gain = 1/25
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/tone" ) func main() { ctx := context.NewContext() triTone := tone.NewTriangleTone(ctx, 440) fmt.Println(triTone.Frequency, triTone.Gain, triTone.HarmonicGains) // Show the calculation for each harmonic gain. // gain = 1 / order^2 if order is odd, 0 if order is even gains := make([]float32, len(triTone.HarmonicGains)) for i := range gains { if i%2 > 0 { gains[i] = tone.Trunc(1/float32((i+2)*(i+2)), tone.MaxSigFigs) } } fmt.Println(gains) }
Output: 440 0 [0 0.111111 0 0.04 0 0.0204081 0 0.0123456 0 0.00826446 0 0.00591716 0 0.00444444 0 0.0034602 0 0.00277008 0 0.00226757] [0 0.111111 0 0.04 0 0.0204081 0 0.0123456 0 0.00826446 0 0.00591716 0 0.00444444 0 0.0034602 0 0.00277008 0 0.00226757]
func (*Tone) Clone ¶
Clone returns a complete copy of the tone that has all of the same values as the original but does not share any memory with it.
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/tone" ) func main() { ctx := context.NewContext() tone := tone.NewToneWith(ctx, 523.2511, 0.5, []float32{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0}) clone := tone.Clone() tone.Frequency = 440 tone.Gain = 1 tone.HarmonicGains[0] = 100 tone.HarmonicGains[1] = 200 tone.HarmonicGains[2] = 300 fmt.Println(tone.Frequency, tone.Gain, tone.HarmonicGains) fmt.Println(clone.Frequency, clone.Gain, clone.HarmonicGains) }
Output: 440 1 [100 200 300 0.4 0.5 0.6 0.7 0.8 0.9 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2] 523.2511 0.5 [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2]
func (*Tone) Empty ¶
Empty checks whether the tone does not have any values set.
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/tone" ) func main() { ctx := context.NewContext() tone := tone.NewTone(ctx) isEmpty1 := tone.Empty() tone.Frequency = 440 isEmpty2 := tone.Empty() tone.Frequency, tone.Gain = 0, 1 isEmpty3 := tone.Empty() fmt.Println(isEmpty1, isEmpty2, isEmpty3) }
Output: true false false
func (*Tone) HarmonicFreq ¶
HarmonicFreq calculates the frequency of one of the tone's harmonic. The fundamental frequency has an order of 1. The frequency is truncated to have no more than MaxSigFigs digits.
As an example, if the tone has a fundamental frequency of 440Hz, then the first harmonic (order=2) is 880Hz and the second harmonic (order=3) is 1320Hz,
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/tone" ) func main() { ctx := context.NewContext() tone := tone.NewTone(ctx) harmFreq1 := tone.HarmonicFreq(2) tone.Frequency = 440 harmFreq2 := tone.HarmonicFreq(1) harmFreq3 := tone.HarmonicFreq(2) harmFreq4 := tone.HarmonicFreq(3) fmt.Println(harmFreq1, harmFreq2, harmFreq3, harmFreq4) }
Output: 0 440 880 1320
func (*Tone) Reset ¶
func (tone *Tone) Reset()
Reset resets the tone to its zero values. The harmonic gains are set to zero, but the slice header does not change.
Example ¶
package main import ( "fmt" "github.com/green-aloe/enobox/context" "github.com/green-aloe/enobox/tone" ) func main() { ctx := context.NewContext() tone := tone.NewSquareTone(ctx, 440) fmt.Println(tone.Frequency, tone.Gain, tone.HarmonicGains) tone.Reset() fmt.Println(tone.Frequency, tone.Gain, tone.HarmonicGains) }
Output: 440 0 [0 0.333333 0 0.2 0 0.142857 0 0.111111 0 0.090909 0 0.076923 0 0.0666666 0 0.0588235 0 0.0526315 0 0.047619] 0 0 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]