diff --git a/interface.go b/interface.go index 39fa5a1..2fc8f05 100644 --- a/interface.go +++ b/interface.go @@ -36,6 +36,7 @@ type Engine interface { // Playable is a music or sound effect object that can be played and managed. type Playable interface { Play(loops int) error + Playing() bool Pause() error Stop() error diff --git a/sdl/currently_playing.go b/sdl/currently_playing.go new file mode 100644 index 0000000..b54f4fd --- /dev/null +++ b/sdl/currently_playing.go @@ -0,0 +1,30 @@ +package sdl + +import "errors" + +// setPlaying associates a channel no. to the Track playing. +func (e *Engine) setPlaying(channel int, t *Track) { + e.channelMu.Lock() + defer e.channelMu.Unlock() + e.channelsPlaying[channel] = t +} + +// unsetPlaying associates a channel no. to the Track playing. +func (e *Engine) unsetPlaying(channel int) error { + e.channelMu.Lock() + defer e.channelMu.Unlock() + if track, ok := e.channelsPlaying[channel]; ok { + track.channel = -1 + delete(e.channelsPlaying, channel) + return nil + } + return errors.New("didn't even know channel %d was playing a sound") +} + +// isPlaying checks if a sound effect is playing on a channel. +func (e *Engine) isPlaying(channel int) bool { + e.channelMu.RLock() + defer e.channelMu.RUnlock() + _, ok := e.channelsPlaying[channel] + return ok +} diff --git a/sdl/music.go b/sdl/music.go index 285d114..b7903c6 100644 --- a/sdl/music.go +++ b/sdl/music.go @@ -9,6 +9,8 @@ import ( type Track struct { isMusic bool // false = is sound effect + engine *Engine + // If isMusic mus *mix.Music @@ -46,6 +48,7 @@ func (e *Engine) LoadSound(filename string) (Track, error) { wav, err := mix.LoadWAV(filename) return Track{ isMusic: false, + engine: e, wav: wav, channel: -1, }, err @@ -62,6 +65,7 @@ func (e *Engine) LoadSoundBin(data []byte) (Track, error) { wav, err := mix.LoadWAVRW(rw, false) return Track{ isMusic: false, + engine: e, wav: wav, channel: -1, }, err @@ -93,9 +97,18 @@ func (t *Track) Play(loops int) error { } channel, err := t.wav.Play(-1, loops) t.channel = channel + t.engine.setPlaying(channel, t) return err } +// Playing tells if this sound is already playing. +func (t *Track) Playing() bool { + if t.isMusic { + return mix.PlayingMusic() + } + return t.channel >= 0 && t.engine.isPlaying(t.channel) +} + // Pause the track. func (t Track) Pause() error { if t.isMusic { diff --git a/sdl/sdl.go b/sdl/sdl.go index f27fe76..561f88a 100644 --- a/sdl/sdl.go +++ b/sdl/sdl.go @@ -1,24 +1,35 @@ // Package sdl implements an audio engine using libSDL2. package sdl -import "github.com/veandco/go-sdl2/mix" +import ( + "sync" + + "github.com/veandco/go-sdl2/mix" +) // Engine is the SDL2 audio engine. type Engine struct { initFlags int + + // Keep track of which SDL channel numbers are ACTUALLY playing sound + // effects right now, and watch for when the channels are finished. + channelMu sync.RWMutex + channelsPlaying map[int]*Track } // New initializes an SDL2 Mixer for the audio engine. // // Pass the SDL2 Mixer flags for its initialization. The flags are an OR'd // value made up of: -// mix.INIT_MP3 -// mix.INIT_OGG -// mix.INIT_FLAC -// mix.INIT_MOD +// +// mix.INIT_MP3 +// mix.INIT_OGG +// mix.INIT_FLAC +// mix.INIT_MOD func New(flags int) (*Engine, error) { return &Engine{ - initFlags: flags, + initFlags: flags, + channelsPlaying: map[int]*Track{}, }, nil } @@ -29,6 +40,11 @@ func (e *Engine) Setup() error { return err } + // Subscribe to sound effect channels finishing. + mix.ChannelFinished(func(channel int) { + _ = e.unsetPlaying(channel) + }) + // Open the audio mixer. // the '2' is stereo (two channels), '1' would be mono. // 4096 is the chunk size.