mirror of
https://github.com/faiface/beep.git
synced 2025-12-23 23:38:45 -05:00
add additional waveform generators
This commit is contained in:
@@ -2,35 +2,88 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/faiface/beep"
|
||||
"github.com/faiface/beep/generators"
|
||||
"github.com/faiface/beep/speaker"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Printf("usage: %s freq\n", os.Args[0])
|
||||
fmt.Println("where freq must be an integer from 1 to 24000")
|
||||
fmt.Println("where freq must be a float between 1 and 24000")
|
||||
fmt.Println("24000 because samplerate of 48000 is hardcoded")
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
usage()
|
||||
return
|
||||
}
|
||||
f, err := strconv.Atoi(os.Args[1])
|
||||
|
||||
f, err := strconv.ParseFloat(os.Args[1], 64)
|
||||
if err != nil {
|
||||
usage()
|
||||
return
|
||||
}
|
||||
speaker.Init(beep.SampleRate(48000), 4800)
|
||||
s, err := generators.SinTone(beep.SampleRate(48000), f)
|
||||
|
||||
sr := beep.SampleRate(48000)
|
||||
speaker.Init(sr, 4800)
|
||||
|
||||
sine, err := generators.SineTone(sr, f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
speaker.Play(s)
|
||||
for {
|
||||
|
||||
triangle, err := generators.TriangleTone(sr, f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
square, err := generators.SquareTone(sr, f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sawtooth, err := generators.SawtoothTone(sr, f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sawtoothReversed, err := generators.SawtoothToneReversed(sr, f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Play 2 seconds of each tone
|
||||
two := sr.N(2 * time.Second)
|
||||
|
||||
ch := make(chan struct{})
|
||||
sounds := []beep.Streamer{
|
||||
beep.Callback(print("sine")),
|
||||
beep.Take(two, sine),
|
||||
beep.Callback(print("triangle")),
|
||||
beep.Take(two, triangle),
|
||||
beep.Callback(print("square")),
|
||||
beep.Take(two, square),
|
||||
beep.Callback(print("sawtooth")),
|
||||
beep.Take(two, sawtooth),
|
||||
beep.Callback(print("sawtooth reversed")),
|
||||
beep.Take(two, sawtoothReversed),
|
||||
beep.Callback(func() {
|
||||
ch <- struct{}{}
|
||||
}),
|
||||
}
|
||||
speaker.Play(beep.Seq(sounds...))
|
||||
|
||||
<-ch
|
||||
}
|
||||
|
||||
// a simple clousure to wrap fmt.Println
|
||||
func print(s string) func() {
|
||||
return func() {
|
||||
fmt.Println(s)
|
||||
}
|
||||
}
|
||||
|
||||
64
generators/sawtooth.go
Normal file
64
generators/sawtooth.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/faiface/beep"
|
||||
)
|
||||
|
||||
type sawGenerator struct {
|
||||
dt float64
|
||||
t float64
|
||||
|
||||
reverse bool
|
||||
}
|
||||
|
||||
// Creates a streamer which will procude an infinite sawtooth wave with the given frequency.
|
||||
// use other wrappers of this package to change amplitude or add time limit.
|
||||
// sampleRate must be at least two times grater then frequency, otherwise this function will return an error.
|
||||
func SawtoothTone(sr beep.SampleRate, freq float64) (beep.Streamer, error) {
|
||||
dt := freq / float64(sr)
|
||||
|
||||
if dt >= 1.0/2.0 {
|
||||
return nil, errors.New("faiface sawtooth tone generator: samplerate must be at least 2 times grater then frequency")
|
||||
}
|
||||
|
||||
return &sawGenerator{dt, 0, false}, nil
|
||||
}
|
||||
|
||||
// Creates a streamer which will procude an infinite sawtooth tone with the given frequency.
|
||||
// sawtooth is reversed so the slope is negative.
|
||||
// use other wrappers of this package to change amplitude or add time limit.
|
||||
// sampleRate must be at least two times grater then frequency, otherwise this function will return an error.
|
||||
func SawtoothToneReversed(sr beep.SampleRate, freq float64) (beep.Streamer, error) {
|
||||
dt := freq / float64(sr)
|
||||
|
||||
if dt >= 1.0/2.0 {
|
||||
return nil, errors.New("faiface triangle tone generator: samplerate must be at least 2 times grater then frequency")
|
||||
}
|
||||
|
||||
return &sawGenerator{dt, 0, true}, nil
|
||||
}
|
||||
|
||||
func (g *sawGenerator) Stream(samples [][2]float64) (n int, ok bool) {
|
||||
if g.reverse {
|
||||
for i := range samples {
|
||||
samples[i][0] = 2.0 * (1 - g.t)
|
||||
samples[i][1] = 2.0 * (1 - g.t)
|
||||
_, g.t = math.Modf(g.t + g.dt)
|
||||
}
|
||||
} else {
|
||||
for i := range samples {
|
||||
samples[i][0] = 2.0*g.t - 1.0
|
||||
samples[i][1] = 2.0*g.t - 1.0
|
||||
_, g.t = math.Modf(g.t + g.dt)
|
||||
}
|
||||
}
|
||||
|
||||
return len(samples), true
|
||||
}
|
||||
|
||||
func (*sawGenerator) Err() error {
|
||||
return nil
|
||||
}
|
||||
41
generators/sine.go
Normal file
41
generators/sine.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/faiface/beep"
|
||||
)
|
||||
|
||||
type sineGenerator struct {
|
||||
dt float64
|
||||
t float64
|
||||
}
|
||||
|
||||
// Creates a streamer which will procude an infinite sine wave with the given frequency.
|
||||
// use other wrappers of this package to change amplitude or add time limit.
|
||||
// sampleRate must be at least two times grater then frequency, otherwise this function will return an error.
|
||||
func SineTone(sr beep.SampleRate, freq float64) (beep.Streamer, error) {
|
||||
dt := freq / float64(sr)
|
||||
|
||||
if dt >= 1.0/2.0 {
|
||||
return nil, errors.New("faiface sine tone generator: samplerate must be at least 2 times grater then frequency")
|
||||
}
|
||||
|
||||
return &sineGenerator{dt, 0}, nil
|
||||
}
|
||||
|
||||
func (g *sineGenerator) Stream(samples [][2]float64) (n int, ok bool) {
|
||||
for i := range samples {
|
||||
v := math.Sin(g.t * 2.0 * math.Pi)
|
||||
samples[i][0] = v
|
||||
samples[i][1] = v
|
||||
_, g.t = math.Modf(g.t + g.dt)
|
||||
}
|
||||
|
||||
return len(samples), true
|
||||
}
|
||||
|
||||
func (*sineGenerator) Err() error {
|
||||
return nil
|
||||
}
|
||||
45
generators/square.go
Normal file
45
generators/square.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/faiface/beep"
|
||||
)
|
||||
|
||||
type squareGenerator struct {
|
||||
dt float64
|
||||
t float64
|
||||
}
|
||||
|
||||
// Creates a streamer which will procude an infinite square wave with the given frequency.
|
||||
// use other wrappers of this package to change amplitude or add time limit.
|
||||
// sampleRate must be at least two times grater then frequency, otherwise this function will return an error.
|
||||
func SquareTone(sr beep.SampleRate, freq float64) (beep.Streamer, error) {
|
||||
dt := freq / float64(sr)
|
||||
|
||||
if dt >= 1.0/2.0 {
|
||||
return nil, errors.New("faiface square tone generator: samplerate must be at least 2 times grater then frequency")
|
||||
}
|
||||
|
||||
return &squareGenerator{dt, 0}, nil
|
||||
}
|
||||
|
||||
func (g *squareGenerator) Stream(samples [][2]float64) (n int, ok bool) {
|
||||
for i := range samples {
|
||||
if g.t < 0.5 {
|
||||
samples[i][0] = 1.0
|
||||
samples[i][1] = 1.0
|
||||
} else {
|
||||
samples[i][0] = -1.0
|
||||
samples[i][1] = -1.0
|
||||
}
|
||||
_, g.t = math.Modf(g.t + g.dt)
|
||||
}
|
||||
|
||||
return len(samples), true
|
||||
}
|
||||
|
||||
func (*squareGenerator) Err() error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// tones generator
|
||||
|
||||
package generators
|
||||
|
||||
import (
|
||||
"math"
|
||||
"errors"
|
||||
. "github.com/faiface/beep"
|
||||
)
|
||||
|
||||
// simple sinusoid tone generator
|
||||
type toneStreamer struct {
|
||||
stat float64
|
||||
delta float64
|
||||
}
|
||||
|
||||
// create streamer which will produce infinite sinusoid tone with the given frequency
|
||||
// use other wrappers of this package to change amplitude or add time limit
|
||||
// sampleRate must be at least two times grater then frequency, otherwise this function will return an error
|
||||
func SinTone(sr SampleRate, freq int) (Streamer, error) {
|
||||
if int(sr)/freq < 2 {
|
||||
return nil, errors.New("faiface beep tone generator: samplerate must be at least 2 times grater then frequency")
|
||||
}
|
||||
r := new(toneStreamer)
|
||||
r.stat = 0.0
|
||||
srf := float64(sr)
|
||||
ff := float64(freq)
|
||||
steps := srf / ff
|
||||
r.delta = 1.0 / steps
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (c *toneStreamer) nextSample() float64 {
|
||||
r := math.Sin(c.stat * 2.0 * math.Pi)
|
||||
_, c.stat = math.Modf(c.stat + c.delta)
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *toneStreamer) Stream(buf [][2]float64) (int, bool) {
|
||||
for i := 0; i < len(buf); i++ {
|
||||
s := c.nextSample()
|
||||
buf[i] = [2]float64{s, s}
|
||||
}
|
||||
return len(buf), true
|
||||
}
|
||||
func (_ *toneStreamer) Err() error {
|
||||
return nil
|
||||
}
|
||||
45
generators/triangle.go
Normal file
45
generators/triangle.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/faiface/beep"
|
||||
)
|
||||
|
||||
type triangleGenerator struct {
|
||||
dt float64
|
||||
t float64
|
||||
}
|
||||
|
||||
// Creates a streamer which will procude an infinite triangle wave with the given frequency.
|
||||
// use other wrappers of this package to change amplitude or add time limit.
|
||||
// sampleRate must be at least two times grater then frequency, otherwise this function will return an error.
|
||||
func TriangleTone(sr beep.SampleRate, freq float64) (beep.Streamer, error) {
|
||||
dt := freq / float64(sr)
|
||||
|
||||
if dt >= 1.0/2.0 {
|
||||
return nil, errors.New("faiface triangle tone generator: samplerate must be at least 2 times grater then frequency")
|
||||
}
|
||||
|
||||
return &triangleGenerator{dt, 0}, nil
|
||||
}
|
||||
|
||||
func (g *triangleGenerator) Stream(samples [][2]float64) (n int, ok bool) {
|
||||
for i := range samples {
|
||||
if g.t < 0.5 {
|
||||
samples[i][0] = 2.0 * g.t
|
||||
samples[i][1] = 2.0 * g.t
|
||||
} else {
|
||||
samples[i][0] = 2.0 * (1.0 - g.t)
|
||||
samples[i][1] = 2.0 * (1.0 - g.t)
|
||||
}
|
||||
_, g.t = math.Modf(g.t + g.dt)
|
||||
}
|
||||
|
||||
return len(samples), true
|
||||
}
|
||||
|
||||
func (*triangleGenerator) Err() error {
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user