diff --git a/mixer.go b/mixer.go index 9b622e4..8954bcf 100644 --- a/mixer.go +++ b/mixer.go @@ -45,12 +45,12 @@ func (m *Mixer) Stream(samples [][2]float64) (n int, ok bool) { for len(samples) > 0 { toStream := min(len(tmp), len(samples)) - // clear the samples + // Clear the samples clear(samples[:toStream]) snMax := 0 for si := 0; si < len(m.streamers); si++ { - // mix the stream + // Mix the stream sn, sok := m.streamers[si].Stream(tmp[:toStream]) for i := range tmp[:sn] { samples[i][0] += tmp[i][0] @@ -61,12 +61,16 @@ func (m *Mixer) Stream(samples [][2]float64) (n int, ok bool) { } if sn < toStream || !sok { - // remove drained streamer - last := len(m.streamers) - 1 - m.streamers[si] = m.streamers[last] - m.streamers[last] = nil - m.streamers = m.streamers[:last] - si-- + // Remove drained streamer. + // Check the length of m.streamers again in case the call to Stream() + // had a callback which clears the Mixer. + if len(m.streamers) > 0 { + last := len(m.streamers) - 1 + m.streamers[si] = m.streamers[last] + m.streamers[last] = nil + m.streamers = m.streamers[:last] + si-- + } if m.stopWhenEmpty && len(m.streamers) == 0 { return n + snMax, true diff --git a/mixer_test.go b/mixer_test.go index 843ee20..8e4662d 100644 --- a/mixer_test.go +++ b/mixer_test.go @@ -96,6 +96,17 @@ func TestMixer_PlaysSilenceWhenNoStreamersProduceSamples(t *testing.T) { assert.Equal(t, make([][2]float64, 10), samples) } +// Bug: https://github.com/gopxl/beep/issues/197#issuecomment-2468951277 +func TestMixer_CanClearDuringCallback(t *testing.T) { + m := beep.Mixer{} + + m.Add(beep.Callback(func() { + m.Clear() + })) + + testtools.CollectNum(10, &m) +} + func BenchmarkMixer_MultipleStreams(b *testing.B) { s1, _ := testtools.RandomDataStreamer(b.N) s2, _ := testtools.RandomDataStreamer(b.N)