From ada24fe35cbc785ecede9020332b6466ae04b062 Mon Sep 17 00:00:00 2001 From: faiface Date: Mon, 3 Jul 2017 17:28:54 +0200 Subject: [PATCH 1/7] minor stylistic corrections in Streamer doc --- audio/interface.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/audio/interface.go b/audio/interface.go index f7a0a81..91d8522 100644 --- a/audio/interface.go +++ b/audio/interface.go @@ -2,8 +2,8 @@ package 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, -// that this value does not change during runtime. +// This value should be set at most once before using audio package. It is safe to assume that this +// value does not change during runtime. var SampleRate = 48000 // Streamer is able to stream a finite or infinite sequence of audio samples. @@ -32,7 +32,7 @@ type Streamer interface { // // 3. n == 0 && !ok // - // The Streamer is drained and no more samples will come. Only this case may occur in the + // Streamer is drained and no more samples will come. Only this case may occur in the // following calls. Stream(samples [][2]float64) (n int, ok bool) } From 019c0f83e13610386642cb47275efed127332797 Mon Sep 17 00:00:00 2001 From: faiface Date: Mon, 3 Jul 2017 17:37:55 +0200 Subject: [PATCH 2/7] fix grammar in Streamer doc --- audio/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/interface.go b/audio/interface.go index 91d8522..adbc6a3 100644 --- a/audio/interface.go +++ b/audio/interface.go @@ -32,7 +32,7 @@ type Streamer interface { // // 3. n == 0 && !ok // - // Streamer is drained and no more samples will come. Only this case may occur in the + // The Streamer is drained and no more samples will come. Only this case may occur in the // following calls. Stream(samples [][2]float64) (n int, ok bool) } From a1542703d57a55ef29dccff2e12b237a87d9c813 Mon Sep 17 00:00:00 2001 From: faiface Date: Mon, 3 Jul 2017 18:55:27 +0200 Subject: [PATCH 3/7] change SampleRate to float64 --- audio/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/interface.go b/audio/interface.go index adbc6a3..395548c 100644 --- a/audio/interface.go +++ b/audio/interface.go @@ -4,7 +4,7 @@ package audio // // This value should be set at most once before using audio package. It is safe to assume that this // 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. type Streamer interface { From c142c3eb0af616f37546976d0f08df9d516a801e Mon Sep 17 00:00:00 2001 From: faiface Date: Mon, 3 Jul 2017 19:07:30 +0200 Subject: [PATCH 4/7] add StreamerFunc helper type --- audio/interface.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/audio/interface.go b/audio/interface.go index 395548c..d426beb 100644 --- a/audio/interface.go +++ b/audio/interface.go @@ -36,3 +36,22 @@ type Streamer interface { // following calls. 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) +} From f3e2b915bdafb95a3b864996d8403fd365de1be8 Mon Sep 17 00:00:00 2001 From: faiface Date: Wed, 5 Jul 2017 17:59:43 +0200 Subject: [PATCH 5/7] add Seq compositor --- audio/compositors.go | 17 +++++++++++ audio/compositors_test.go | 63 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 audio/compositors.go create mode 100644 audio/compositors_test.go diff --git a/audio/compositors.go b/audio/compositors.go new file mode 100644 index 0000000..bb4740f --- /dev/null +++ b/audio/compositors.go @@ -0,0 +1,17 @@ +package audio + +// 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 + }) +} diff --git a/audio/compositors_test.go b/audio/compositors_test.go new file mode 100644 index 0000000..4114b92 --- /dev/null +++ b/audio/compositors_test.go @@ -0,0 +1,63 @@ +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 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") + } +} From 5723022b5a6274cb7e9c450d5a219b9d261d503a Mon Sep 17 00:00:00 2001 From: faiface Date: Wed, 5 Jul 2017 18:14:18 +0200 Subject: [PATCH 6/7] add Take decorator function --- audio/compositors.go | 23 +++++++++++++++++++++++ audio/compositors_test.go | 16 ++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/audio/compositors.go b/audio/compositors.go index bb4740f..ceed138 100644 --- a/audio/compositors.go +++ b/audio/compositors.go @@ -1,5 +1,28 @@ 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 diff --git a/audio/compositors_test.go b/audio/compositors_test.go index 4114b92..450430b 100644 --- a/audio/compositors_test.go +++ b/audio/compositors_test.go @@ -44,6 +44,22 @@ func collect(s audio.Streamer) [][2]float64 { } } +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) From 777c7f1717e858d36d9565a7a0dc58ce4fe5f3b8 Mon Sep 17 00:00:00 2001 From: aerth Date: Wed, 5 Jul 2017 12:24:26 -0700 Subject: [PATCH 7/7] add libasound2-dev dependency --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index bef75a2..a02ff15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ addons: - libxinerama-dev - libxcursor-dev - libxi-dev + - libasound2-dev go: - 1.8 - 1.7.4