Merge pull request #1 from faiface/audio

Audio
This commit is contained in:
Christopher Cooper 2017-07-05 18:05:50 -04:00 committed by GitHub
commit e7a7b16be2
4 changed files with 142 additions and 3 deletions

View File

@ -9,6 +9,7 @@ addons:
- libxinerama-dev - libxinerama-dev
- libxcursor-dev - libxcursor-dev
- libxi-dev - libxi-dev
- libasound2-dev
go: go:
- 1.8 - 1.8
- 1.7.4 - 1.7.4

40
audio/compositors.go Normal file
View File

@ -0,0 +1,40 @@
package audio
import (
"math"
"time"
)
// Take returns a Streamer which streams s for at most d duration.
func Take(d time.Duration, s Streamer) Streamer {
currSample := 0
numSamples := int(math.Ceil(d.Seconds() * SampleRate))
return StreamerFunc(func(samples [][2]float64) (n int, ok bool) {
if currSample >= numSamples {
return 0, false
}
toStream := numSamples - currSample
if len(samples) < toStream {
toStream = len(samples)
}
sn, sok := s.Stream(samples[:toStream])
currSample += sn
return sn, sok
})
}
// Seq takes zero or more Streamers and returns a Streamer which streams them one by one without pauses.
func Seq(s ...Streamer) Streamer {
i := 0
return StreamerFunc(func(samples [][2]float64) (n int, ok bool) {
for i < len(s) && len(samples) > 0 {
sn, sok := s[i].Stream(samples)
samples = samples[sn:]
n, ok = n+sn, ok || sok
if !sok {
i++
}
}
return n, ok
})
}

79
audio/compositors_test.go Normal file
View File

@ -0,0 +1,79 @@
package audio_test
import (
"math"
"math/rand"
"reflect"
"testing"
"time"
"github.com/faiface/pixel/audio"
)
// randomDataStreamer generates random samples of duration d and returns a Streamer which streams
// them and the data itself.
func randomDataStreamer(d time.Duration) (s audio.Streamer, data [][2]float64) {
numSamples := int(math.Ceil(d.Seconds() * audio.SampleRate))
data = make([][2]float64, numSamples)
for i := range data {
data[i][0] = rand.Float64()*2 - 1
data[i][1] = rand.Float64()*2 - 1
}
return audio.StreamerFunc(func(samples [][2]float64) (n int, ok bool) {
if len(data) == 0 {
return 0, false
}
n = copy(samples, data)
data = data[n:]
return n, true
}), data
}
// collect drains Streamer s and returns all of the samples it streamed.
func collect(s audio.Streamer) [][2]float64 {
var (
result [][2]float64
buf [512][2]float64
)
for {
n, ok := s.Stream(buf[:])
if !ok {
return result
}
result = append(result, buf[:n]...)
}
}
func TestTake(t *testing.T) {
for i := 0; i < 7; i++ {
total := time.Nanosecond * time.Duration(1e8+rand.Intn(1e9))
s, data := randomDataStreamer(total)
d := time.Nanosecond * time.Duration(rand.Int63n(total.Nanoseconds()))
numSamples := int(math.Ceil(d.Seconds() * audio.SampleRate))
want := data[:numSamples]
got := collect(audio.Take(d, s))
if !reflect.DeepEqual(want, got) {
t.Error("Take not working correctly")
}
}
}
func TestSeq(t *testing.T) {
var (
s = make([]audio.Streamer, 7)
want [][2]float64
)
for i := range s {
var data [][2]float64
s[i], data = randomDataStreamer(time.Nanosecond * time.Duration(1e8+rand.Intn(1e9)))
want = append(want, data...)
}
got := collect(audio.Seq(s...))
if !reflect.DeepEqual(want, got) {
t.Error("Seq not working correctly")
}
}

View File

@ -2,9 +2,9 @@ package audio
// SampleRate is the number of audio samples a Streamer should produce per one second of audio. // SampleRate is the number of audio samples a Streamer should produce per one second of audio.
// //
// This value should be set at most once before using audio package. It is safe to rely on the fact, // This value should be set at most once before using audio package. It is safe to assume that this
// that this value does not change during runtime. // value does not change during runtime.
var SampleRate = 48000 var SampleRate float64 = 48000
// Streamer is able to stream a finite or infinite sequence of audio samples. // Streamer is able to stream a finite or infinite sequence of audio samples.
type Streamer interface { type Streamer interface {
@ -36,3 +36,22 @@ type Streamer interface {
// following calls. // following calls.
Stream(samples [][2]float64) (n int, ok bool) Stream(samples [][2]float64) (n int, ok bool)
} }
// StreamerFunc is a Streamer created by simply wrapping a streaming function (usually a closure,
// which encloses a time tracking variable). This sometimes simplifies creating new streamers.
//
// Example:
//
// noise := StreamerFunc(func(samples [][2]float64) (n int, ok bool) {
// for i := range samples {
// samples[i][0] = rand.Float64()*2 - 1
// samples[i][1] = rand.Float64()*2 - 1
// }
// return len(samples), true
// })
type StreamerFunc func(samples [][2]float64) (n int, ok bool)
// Stream calls the wrapped streaming function.
func (sf StreamerFunc) Stream(samples [][2]float64) (n int, ok bool) {
return sf(samples)
}