Skip to content

Commit

Permalink
Add way to stop looping sound
Browse files Browse the repository at this point in the history
Sfx(-2,...) stops the loop. Sound effect will be played to the end but will not repeat.
  • Loading branch information
elgopher committed Sep 3, 2023
1 parent dd07ccc commit 3d3b211
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 10 deletions.
2 changes: 1 addition & 1 deletion audio/audio.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const (
Channel1 Channel = 1
Channel2 Channel = 2
Channel3 Channel = 3
ChannelAny Channel = -1
ChannelAny Channel = -1 // Rename - it means all channels for Sfx(-2, ...)
ChannelStop Channel = -2
)

Expand Down
35 changes: 27 additions & 8 deletions audio/synth.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (c *channel) moveToNextNote(sfx SoundEffect) {
c.notesToGo--
c.noteNo++

if c.noteNo == int(sfx.LoopStop) {
if c.noteNo == int(sfx.LoopStop) && !c.loopingDisabled {
c.noteNo = int(sfx.LoopStart)
}

Expand All @@ -96,6 +96,11 @@ func (c *channel) moveToNextNote(sfx SoundEffect) {
func (s *Synthesizer) Sfx(sfxNo int, ch Channel, offset, length int) {
fmt.Println("Sfx is not implemented yet. Sorry...")

if sfxNo == -2 {
s.disableLooping(ch)
return
}

if ch >= ChannelStop && ch <= Channel3 {
s.stopSfx(sfxNo)
}
Expand Down Expand Up @@ -131,6 +136,19 @@ func (s *Synthesizer) Sfx(sfxNo int, ch Channel, offset, length int) {
s.channels[ch].oscillator.FreqHz = pitchToFreq(note0.Pitch)
}

func (s *Synthesizer) disableLooping(ch Channel) {
if ch == ChannelAny || ch == ChannelStop {
for i := range s.channels {
s.channels[i].loopingDisabled = true
}
return
}

if ch >= Channel0 && ch <= Channel3 {
s.channels[ch].loopingDisabled = true
}
}

func (s *Synthesizer) stopSfx(no int) {
for i, c := range s.channels {
if c.playing && c.sfxNo == no {
Expand Down Expand Up @@ -364,11 +382,12 @@ func boolToByte(b bool) byte {
}

type channel struct {
sfxNo int
noteNo int
notesToGo int
frame int
noteEndFrame int
oscillator internal.Oscillator
playing bool
sfxNo int
noteNo int
notesToGo int
frame int
noteEndFrame int
oscillator internal.Oscillator
playing bool
loopingDisabled bool
}
72 changes: 71 additions & 1 deletion audio/synth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,6 @@ func TestSynthesizer_Sfx(t *testing.T) {
assertNotSilence(t, readSamples(synth, durationOfNoteWhenSpeedIsOne))
})
}

})

sfxOffsetLengthTest(t)
Expand Down Expand Up @@ -820,6 +819,77 @@ func sfxLoopTest(t *testing.T) {
})
}
})

t.Run("should stop the loop on given channel", func(t *testing.T) {
channels := []audio.Channel{audio.Channel0, audio.Channel1, audio.Channel2, audio.Channel3}

for _, ch := range channels {
testName := fmt.Sprintf("%d", ch)

t.Run(testName, func(t *testing.T) {
var e audio.SoundEffect
e.Notes[0].Volume = audio.VolumeLoudest
e.Speed = 1
e.LoopStart = 0
e.LoopStop = 1

synth := &audio.Synthesizer{}
synth.SetSfx(0, e)

synth.Sfx(0, ch, 0, 32)
// when
synth.Sfx(-2, ch, 0, 0)
// then
assertNotSilence(t, readSamples(synth, durationOfNoteWhenSpeedIsOne)) // wait until entire sfx is played
assertSilence(t, readSamples(synth, durationOfNoteWhenSpeedIsOne)) // wait until entire sfx is played
})
}
})

t.Run("should stop the loop on all channels", func(t *testing.T) {
channels := []audio.Channel{audio.ChannelAny, audio.ChannelStop}

for _, ch := range channels {
testName := fmt.Sprintf("%d", ch)

t.Run(testName, func(t *testing.T) {
var e audio.SoundEffect
e.Notes[0].Volume = audio.VolumeLoudest
e.Speed = 1
e.LoopStart = 0
e.LoopStop = 1

synth := &audio.Synthesizer{}
synth.SetSfx(0, e)

synth.Sfx(0, audio.Channel0, 0, 32)
synth.Sfx(0, audio.Channel1, 0, 32)
synth.Sfx(0, audio.Channel2, 0, 32)
synth.Sfx(0, audio.Channel3, 0, 32)
// when
synth.Sfx(-2, ch, 0, 0)
// then
assertNotSilence(t, readSamples(synth, durationOfNoteWhenSpeedIsOne)) // wait until entire sfx is played
assertSilence(t, readSamples(synth, durationOfNoteWhenSpeedIsOne)) // wait until entire sfx is played
})
}
})

t.Run("should not panic when trying to stop the loop with invalid channel", func(t *testing.T) {
channels := []audio.Channel{-3, 4}

for _, ch := range channels {
testName := fmt.Sprintf("%d", ch)

t.Run(testName, func(t *testing.T) {
synth := &audio.Synthesizer{}

assert.NotPanics(t, func() {
synth.Sfx(-2, ch, 0, 0)
})
})
}
})
}

func sfxLengthTest(t *testing.T) {
Expand Down

0 comments on commit 3d3b211

Please sign in to comment.