diff --git a/compositors.go b/compositors.go index 39a2c0f..e391db8 100644 --- a/compositors.go +++ b/compositors.go @@ -137,3 +137,37 @@ func Mix(s ...Streamer) Streamer { return n, ok }) } + +// Dup returns two Streamers which both stream the same data as the original s. The two Streamers +// can't be used concurrently without synchronization. +func Dup(s Streamer) (t, u Streamer) { + var tBuf, uBuf [][2]float64 + return &dup{&tBuf, &uBuf, s}, &dup{&uBuf, &tBuf, s} +} + +type dup struct { + myBuf, itsBuf *[][2]float64 + s Streamer +} + +func (d *dup) Stream(samples [][2]float64) (n int, ok bool) { + buf := *d.myBuf + n = copy(samples, buf) + ok = len(buf) > 0 + buf = buf[n:] + samples = samples[n:] + *d.myBuf = buf + + if len(samples) > 0 { + sn, sok := d.s.Stream(samples) + n += sn + ok = ok || sok + *d.itsBuf = append(*d.itsBuf, samples[:sn]...) + } + + return n, ok +} + +func (d *dup) Err() error { + return d.s.Err() +} diff --git a/compositors_test.go b/compositors_test.go index 8547cbe..a1f75d6 100644 --- a/compositors_test.go +++ b/compositors_test.go @@ -109,3 +109,29 @@ func TestMix(t *testing.T) { t.Error("Mix not working correctly") } } + +func TestDup(t *testing.T) { + for i := 0; i < 7; i++ { + s, data := randomDataStreamer(rand.Intn(1e5) + 1e4) + st, su := beep.Dup(s) + + var tData, uData [][2]float64 + for { + buf := make([][2]float64, rand.Intn(1e4)) + tn, tok := st.Stream(buf) + tData = append(tData, buf[:tn]...) + + buf = make([][2]float64, rand.Intn(1e4)) + un, uok := su.Stream(buf) + uData = append(uData, buf[:un]...) + + if !tok && !uok { + break + } + } + + if !reflect.DeepEqual(data, tData) || !reflect.DeepEqual(data, uData) { + t.Error("Dup not working correctly") + } + } +}