Skip to content

Commit 4549d6d

Browse files
committed
Add way to stop looping sound
Sfx(-2,...) stops the loop. Sound effect will be played to the end but will not repeat.
1 parent afb64d3 commit 4549d6d

File tree

3 files changed

+98
-11
lines changed

3 files changed

+98
-11
lines changed

audio/audio.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ const (
9696
Channel1 Channel = 1
9797
Channel2 Channel = 2
9898
Channel3 Channel = 3
99-
ChannelAny Channel = -1
99+
ChannelAny Channel = -1 // Rename - it means all channels for Sfx(-2, ...)
100100
ChannelStop Channel = -2
101101
)
102102

audio/synth.go

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func (c *channel) moveToNextNote(sfx SoundEffect) {
7575
c.notesToGo--
7676
c.noteNo++
7777

78-
if c.noteNo == int(sfx.LoopStop) {
78+
if c.noteNo == int(sfx.LoopStop) && !c.loopingDisabled {
7979
c.noteNo = int(sfx.LoopStart)
8080
}
8181

@@ -94,7 +94,10 @@ func (c *channel) moveToNextNote(sfx SoundEffect) {
9494
}
9595

9696
func (s *Synthesizer) Sfx(sfxNo int, ch Channel, offset, length int) {
97-
fmt.Println("Sfx is not implemented yet. Sorry...")
97+
if sfxNo == -2 {
98+
s.disableLooping(ch)
99+
return
100+
}
98101

99102
if ch >= ChannelStop && ch <= Channel3 {
100103
s.stopSfx(sfxNo)
@@ -131,6 +134,19 @@ func (s *Synthesizer) Sfx(sfxNo int, ch Channel, offset, length int) {
131134
s.channels[ch].oscillator.FreqHz = pitchToFreq(note0.Pitch)
132135
}
133136

137+
func (s *Synthesizer) disableLooping(ch Channel) {
138+
if ch == ChannelAny || ch == ChannelStop {
139+
for i := range s.channels {
140+
s.channels[i].loopingDisabled = true
141+
}
142+
return
143+
}
144+
145+
if ch >= Channel0 && ch <= Channel3 {
146+
s.channels[ch].loopingDisabled = true
147+
}
148+
}
149+
134150
func (s *Synthesizer) stopSfx(no int) {
135151
for i, c := range s.channels {
136152
if c.playing && c.sfxNo == no {
@@ -364,11 +380,12 @@ func boolToByte(b bool) byte {
364380
}
365381

366382
type channel struct {
367-
sfxNo int
368-
noteNo int
369-
notesToGo int
370-
frame int
371-
noteEndFrame int
372-
oscillator internal.Oscillator
373-
playing bool
383+
sfxNo int
384+
noteNo int
385+
notesToGo int
386+
frame int
387+
noteEndFrame int
388+
oscillator internal.Oscillator
389+
playing bool
390+
loopingDisabled bool
374391
}

audio/synth_test.go

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,6 @@ func TestSynthesizer_Sfx(t *testing.T) {
564564
assertNotSilence(t, readSamples(synth, durationOfNoteWhenSpeedIsOne))
565565
})
566566
}
567-
568567
})
569568

570569
sfxOffsetLengthTest(t)
@@ -820,6 +819,77 @@ func sfxLoopTest(t *testing.T) {
820819
})
821820
}
822821
})
822+
823+
t.Run("should stop the loop on given channel", func(t *testing.T) {
824+
channels := []audio.Channel{audio.Channel0, audio.Channel1, audio.Channel2, audio.Channel3}
825+
826+
for _, ch := range channels {
827+
testName := fmt.Sprintf("%d", ch)
828+
829+
t.Run(testName, func(t *testing.T) {
830+
var e audio.SoundEffect
831+
e.Notes[0].Volume = audio.VolumeLoudest
832+
e.Speed = 1
833+
e.LoopStart = 0
834+
e.LoopStop = 1
835+
836+
synth := &audio.Synthesizer{}
837+
synth.SetSfx(0, e)
838+
839+
synth.Sfx(0, ch, 0, 32)
840+
// when
841+
synth.Sfx(-2, ch, 0, 0)
842+
// then
843+
assertNotSilence(t, readSamples(synth, durationOfNoteWhenSpeedIsOne)) // wait until entire sfx is played
844+
assertSilence(t, readSamples(synth, durationOfNoteWhenSpeedIsOne)) // wait until entire sfx is played
845+
})
846+
}
847+
})
848+
849+
t.Run("should stop the loop on all channels", func(t *testing.T) {
850+
channels := []audio.Channel{audio.ChannelAny, audio.ChannelStop}
851+
852+
for _, ch := range channels {
853+
testName := fmt.Sprintf("%d", ch)
854+
855+
t.Run(testName, func(t *testing.T) {
856+
var e audio.SoundEffect
857+
e.Notes[0].Volume = audio.VolumeLoudest
858+
e.Speed = 1
859+
e.LoopStart = 0
860+
e.LoopStop = 1
861+
862+
synth := &audio.Synthesizer{}
863+
synth.SetSfx(0, e)
864+
865+
synth.Sfx(0, audio.Channel0, 0, 32)
866+
synth.Sfx(0, audio.Channel1, 0, 32)
867+
synth.Sfx(0, audio.Channel2, 0, 32)
868+
synth.Sfx(0, audio.Channel3, 0, 32)
869+
// when
870+
synth.Sfx(-2, ch, 0, 0)
871+
// then
872+
assertNotSilence(t, readSamples(synth, durationOfNoteWhenSpeedIsOne)) // wait until entire sfx is played
873+
assertSilence(t, readSamples(synth, durationOfNoteWhenSpeedIsOne)) // wait until entire sfx is played
874+
})
875+
}
876+
})
877+
878+
t.Run("should not panic when trying to stop the loop with invalid channel", func(t *testing.T) {
879+
channels := []audio.Channel{-3, 4}
880+
881+
for _, ch := range channels {
882+
testName := fmt.Sprintf("%d", ch)
883+
884+
t.Run(testName, func(t *testing.T) {
885+
synth := &audio.Synthesizer{}
886+
887+
assert.NotPanics(t, func() {
888+
synth.Sfx(-2, ch, 0, 0)
889+
})
890+
})
891+
}
892+
})
823893
}
824894

825895
func sfxLengthTest(t *testing.T) {

0 commit comments

Comments
 (0)