pixel-examples/community/amidakuji/glossary/jukebox/music.go

131 lines
3.0 KiB
Go

package jukebox
import (
"errors"
"io/ioutil"
"os"
"sync"
"time"
gg "github.com/faiface/pixel-examples/community/amidakuji/glossary"
"github.com/faiface/beep"
"github.com/faiface/beep/speaker"
"github.com/faiface/beep/vorbis"
)
// -------------------------------------------------------------------------
const nMusics = 2
var (
mutex sync.Mutex
isPlaying bool
musics [nMusics]*_Music
)
// -------------------------------------------------------------------------
// singleton
func init() {
// city pop favorites
musics[0] = _NewMusicFromAsset("nighttempo-purepresent1", "karaoke/kikuchimomoko-nightcruising.ogg")
musics[1] = _NewMusicFromAsset("nighttempo-purepresent2", "karaoke/takeuchimariya-plasticlove.ogg")
// speaker on
speaker.Init(musics[0].format.SampleRate, musics[0].format.SampleRate.N(time.Second))
speaker.Play(beep.Iterate(func() (soundtrack beep.Streamer) {
musics[0].stream.Seek(0)
musics[1].stream.Seek(0)
return beep.Seq(musics[0].stream, musics[1].stream)
}))
speaker.Lock()
}
// IsPlaying determines whether the soundtrack is currently playing or not.
func IsPlaying() bool {
return isPlaying
}
// Play unlocks the speaker.
func Play() {
mutex.Lock()
defer mutex.Unlock()
if !isPlaying {
isPlaying = true
speaker.Unlock()
}
return
}
// Pause locks the speaker.
func Pause() {
mutex.Lock()
defer mutex.Unlock()
if isPlaying {
isPlaying = false
speaker.Lock()
}
return
}
// Finalize should be called on program exit.
// This function deletes the temporary music file its package generates.
func Finalize() error {
errs := ""
for _, music := range musics {
music.Close()
err := music._Destory()
if err != nil {
errs += " " + err.Error()
}
}
if errs != "" {
return errors.New(errs)
}
return nil
}
// -------------------------------------------------------------------------
// NewMusicFromAsset is a constructor.
func _NewMusicFromAsset(nameMusic, nameAsset string) *_Music {
asset, err := gg.Asset(nameAsset)
if err != nil {
// log.Fatal(err) //
}
return _NewMusic(nameMusic, asset)
}
// Music is a temporary file to play a single background music. It should be destroyed on program exit.
type _Music struct {
os.File
stream beep.StreamSeekCloser
format beep.Format
}
// NewMusic creates an instance of Music, a temporary file from which the speaker plays a music.
// speaker.Lock() to pause.
// speaker.Unlock() to resume/play.
func _NewMusic(name string, asset []byte) *_Music {
tmpfile, err := ioutil.TempFile("", name)
if err != nil {
// log.Fatal(err) //
}
// log.Println(tmpfile.Name()) //
_, err = tmpfile.Write(asset)
if err != nil {
// log.Fatal(err) //
}
stream, format, err := vorbis.Decode(tmpfile)
if err != nil {
// log.Fatal(err) //
}
return &_Music{*tmpfile, stream, format}
}
// Destory deletes the temporary music file.
func (music *_Music) _Destory() error {
return os.Remove(music.Name())
}