package main import ( "bytes" "embed" "io" "github.com/hajimehoshi/go-mp3" "github.com/hajimehoshi/oto/v2" ) //go:embed "bla_bla_bla.mp3" var audioFile embed.FS func main() { fileBytes, err := audioFile.ReadFile("bla_bla_bla.mp3") if err != nil { panic("reading bla_bla_bla.mp3 failed: " + err.Error()) } fileBytesReader := bytes.NewReader(fileBytes) decodedMp3, err := mp3.NewDecoder(fileBytesReader) if err != nil { panic("mp3.NewDecoder failed: " + err.Error()) } // Remember that you should **not** create more than one context otoCtx, readyChan, err := oto.NewContext(44100, 2, oto.FormatSignedInt16LE) if err != nil { panic("oto.NewContext failed: " + err.Error()) } // It might take a bit for the hardware audio devices to be ready, so we wait on the channel. <-readyChan player := otoCtx.NewPlayer(decodedMp3) defer player.Close() // Play starts playing the sound and returns without waiting for it (Play() is async). player.Play() // every time the player finishes playing, we rewind the file and play it again for { if !player.IsPlaying() { player.(io.Seeker).Seek(0, io.SeekStart) player.Play() } } }