Skip to content

Commit 49095b1

Browse files
committed
add additional waveform generators
1 parent c2062e6 commit 49095b1

File tree

6 files changed

+256
-56
lines changed

6 files changed

+256
-56
lines changed

examples/tone-player/main.go

+61-8
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,88 @@ package main
22

33
import (
44
"fmt"
5+
"os"
6+
"strconv"
7+
"time"
8+
59
"github.com/faiface/beep"
610
"github.com/faiface/beep/generators"
711
"github.com/faiface/beep/speaker"
8-
"os"
9-
"strconv"
1012
)
1113

1214
func usage() {
1315
fmt.Printf("usage: %s freq\n", os.Args[0])
14-
fmt.Println("where freq must be an integer from 1 to 24000")
16+
fmt.Println("where freq must be a float between 1 and 24000")
1517
fmt.Println("24000 because samplerate of 48000 is hardcoded")
1618
}
19+
1720
func main() {
1821
if len(os.Args) < 2 {
1922
usage()
2023
return
2124
}
22-
f, err := strconv.Atoi(os.Args[1])
25+
26+
f, err := strconv.ParseFloat(os.Args[1], 64)
2327
if err != nil {
2428
usage()
2529
return
2630
}
27-
speaker.Init(beep.SampleRate(48000), 4800)
28-
s, err := generators.SinTone(beep.SampleRate(48000), f)
31+
32+
sr := beep.SampleRate(48000)
33+
speaker.Init(sr, 4800)
34+
35+
sine, err := generators.SineTone(sr, f)
36+
if err != nil {
37+
panic(err)
38+
}
39+
40+
triangle, err := generators.TriangleTone(sr, f)
41+
if err != nil {
42+
panic(err)
43+
}
44+
45+
square, err := generators.SquareTone(sr, f)
46+
if err != nil {
47+
panic(err)
48+
}
49+
50+
sawtooth, err := generators.SawtoothTone(sr, f)
2951
if err != nil {
3052
panic(err)
3153
}
32-
speaker.Play(s)
33-
for {
3454

55+
sawtoothReversed, err := generators.SawtoothToneReversed(sr, f)
56+
if err != nil {
57+
panic(err)
58+
}
59+
60+
// Play 2 seconds of each tone
61+
two := sr.N(2 * time.Second)
62+
63+
ch := make(chan struct{})
64+
sounds := []beep.Streamer{
65+
beep.Callback(print("sine")),
66+
beep.Take(two, sine),
67+
beep.Callback(print("triangle")),
68+
beep.Take(two, triangle),
69+
beep.Callback(print("square")),
70+
beep.Take(two, square),
71+
beep.Callback(print("sawtooth")),
72+
beep.Take(two, sawtooth),
73+
beep.Callback(print("sawtooth reversed")),
74+
beep.Take(two, sawtoothReversed),
75+
beep.Callback(func() {
76+
ch <- struct{}{}
77+
}),
78+
}
79+
speaker.Play(beep.Seq(sounds...))
80+
81+
<-ch
82+
}
83+
84+
// a simple clousure to wrap fmt.Println
85+
func print(s string) func() {
86+
return func() {
87+
fmt.Println(s)
3588
}
3689
}

generators/sawtooth.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package generators
2+
3+
import (
4+
"errors"
5+
"math"
6+
7+
"github.com/faiface/beep"
8+
)
9+
10+
type sawGenerator struct {
11+
dt float64
12+
t float64
13+
14+
reverse bool
15+
}
16+
17+
// Creates a streamer which will procude an infinite sawtooth wave with the given frequency.
18+
// use other wrappers of this package to change amplitude or add time limit.
19+
// sampleRate must be at least two times grater then frequency, otherwise this function will return an error.
20+
func SawtoothTone(sr beep.SampleRate, freq float64) (beep.Streamer, error) {
21+
dt := freq / float64(sr)
22+
23+
if dt >= 1.0/2.0 {
24+
return nil, errors.New("faiface sawtooth tone generator: samplerate must be at least 2 times grater then frequency")
25+
}
26+
27+
return &sawGenerator{dt, 0, false}, nil
28+
}
29+
30+
// Creates a streamer which will procude an infinite sawtooth tone with the given frequency.
31+
// sawtooth is reversed so the slope is negative.
32+
// use other wrappers of this package to change amplitude or add time limit.
33+
// sampleRate must be at least two times grater then frequency, otherwise this function will return an error.
34+
func SawtoothToneReversed(sr beep.SampleRate, freq float64) (beep.Streamer, error) {
35+
dt := freq / float64(sr)
36+
37+
if dt >= 1.0/2.0 {
38+
return nil, errors.New("faiface triangle tone generator: samplerate must be at least 2 times grater then frequency")
39+
}
40+
41+
return &sawGenerator{dt, 0, true}, nil
42+
}
43+
44+
func (g *sawGenerator) Stream(samples [][2]float64) (n int, ok bool) {
45+
if g.reverse {
46+
for i := range samples {
47+
samples[i][0] = 2.0 * (1 - g.t)
48+
samples[i][1] = 2.0 * (1 - g.t)
49+
_, g.t = math.Modf(g.t + g.dt)
50+
}
51+
} else {
52+
for i := range samples {
53+
samples[i][0] = 2.0*g.t - 1.0
54+
samples[i][1] = 2.0*g.t - 1.0
55+
_, g.t = math.Modf(g.t + g.dt)
56+
}
57+
}
58+
59+
return len(samples), true
60+
}
61+
62+
func (*sawGenerator) Err() error {
63+
return nil
64+
}

generators/sine.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package generators
2+
3+
import (
4+
"errors"
5+
"math"
6+
7+
"github.com/faiface/beep"
8+
)
9+
10+
type sineGenerator struct {
11+
dt float64
12+
t float64
13+
}
14+
15+
// Creates a streamer which will procude an infinite sine wave with the given frequency.
16+
// use other wrappers of this package to change amplitude or add time limit.
17+
// sampleRate must be at least two times grater then frequency, otherwise this function will return an error.
18+
func SineTone(sr beep.SampleRate, freq float64) (beep.Streamer, error) {
19+
dt := freq / float64(sr)
20+
21+
if dt >= 1.0/2.0 {
22+
return nil, errors.New("faiface sine tone generator: samplerate must be at least 2 times grater then frequency")
23+
}
24+
25+
return &sineGenerator{dt, 0}, nil
26+
}
27+
28+
func (g *sineGenerator) Stream(samples [][2]float64) (n int, ok bool) {
29+
for i := range samples {
30+
v := math.Sin(g.t * 2.0 * math.Pi)
31+
samples[i][0] = v
32+
samples[i][1] = v
33+
_, g.t = math.Modf(g.t + g.dt)
34+
}
35+
36+
return len(samples), true
37+
}
38+
39+
func (*sineGenerator) Err() error {
40+
return nil
41+
}

generators/square.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package generators
2+
3+
import (
4+
"errors"
5+
"math"
6+
7+
"github.com/faiface/beep"
8+
)
9+
10+
type squareGenerator struct {
11+
dt float64
12+
t float64
13+
}
14+
15+
// Creates a streamer which will procude an infinite square wave with the given frequency.
16+
// use other wrappers of this package to change amplitude or add time limit.
17+
// sampleRate must be at least two times grater then frequency, otherwise this function will return an error.
18+
func SquareTone(sr beep.SampleRate, freq float64) (beep.Streamer, error) {
19+
dt := freq / float64(sr)
20+
21+
if dt >= 1.0/2.0 {
22+
return nil, errors.New("faiface square tone generator: samplerate must be at least 2 times grater then frequency")
23+
}
24+
25+
return &squareGenerator{dt, 0}, nil
26+
}
27+
28+
func (g *squareGenerator) Stream(samples [][2]float64) (n int, ok bool) {
29+
for i := range samples {
30+
if g.t < 0.5 {
31+
samples[i][0] = 1.0
32+
samples[i][1] = 1.0
33+
} else {
34+
samples[i][0] = -1.0
35+
samples[i][1] = -1.0
36+
}
37+
_, g.t = math.Modf(g.t + g.dt)
38+
}
39+
40+
return len(samples), true
41+
}
42+
43+
func (*squareGenerator) Err() error {
44+
return nil
45+
}

generators/toner.go

-48
This file was deleted.

generators/triangle.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package generators
2+
3+
import (
4+
"errors"
5+
"math"
6+
7+
"github.com/faiface/beep"
8+
)
9+
10+
type triangleGenerator struct {
11+
dt float64
12+
t float64
13+
}
14+
15+
// Creates a streamer which will procude an infinite triangle wave with the given frequency.
16+
// use other wrappers of this package to change amplitude or add time limit.
17+
// sampleRate must be at least two times grater then frequency, otherwise this function will return an error.
18+
func TriangleTone(sr beep.SampleRate, freq float64) (beep.Streamer, error) {
19+
dt := freq / float64(sr)
20+
21+
if dt >= 1.0/2.0 {
22+
return nil, errors.New("faiface triangle tone generator: samplerate must be at least 2 times grater then frequency")
23+
}
24+
25+
return &triangleGenerator{dt, 0}, nil
26+
}
27+
28+
func (g *triangleGenerator) Stream(samples [][2]float64) (n int, ok bool) {
29+
for i := range samples {
30+
if g.t < 0.5 {
31+
samples[i][0] = 2.0 * g.t
32+
samples[i][1] = 2.0 * g.t
33+
} else {
34+
samples[i][0] = 2.0 * (1.0 - g.t)
35+
samples[i][1] = 2.0 * (1.0 - g.t)
36+
}
37+
_, g.t = math.Modf(g.t + g.dt)
38+
}
39+
40+
return len(samples), true
41+
}
42+
43+
func (*triangleGenerator) Err() error {
44+
return nil
45+
}

0 commit comments

Comments
 (0)