Audio support for TGE runtime - TGE
Based on :
- gomobile exp/audio/al - Copyright (c) 2009 The Go Authors. All rights reserved.
- OpenAL - Copyright (C) 1991 Free Software Foundation, Inc.
The Audio API is based on WebAudio API specifications ported to OpenAL for Desktop and Mobile targets.
- Desktop (Work In Progress)
- Mobile (Work In Progress)
- Browser
In order to use this package on Linux desktop distros, you may need OpenAL library as an external dependency:
sudo apt-get install libopenal-dev
Only Vorbis format (.ogg) is currently supported for audio files, MP3 will be eventually supported in future releases.
Only StereoPannerNode and GainNode are currently available, additional and custom AudioNodes and spacialization features are planned in future releases.
Audio can't be started without user interaction on browsers, see details in implementation.
The WebAudio API offers a complete and comprehensive approach to audio implementation in 2D/3D scenes, tge-audio tries to benefit from it by exposing also OpenAL the same way.
See example at AUDIO examples
package main
import (
"fmt"
"time"
tge "github.com/thommil/tge"
audio "github.com/thommil/tge-audio"
)
type AudioApp struct {
runtime tge.Runtime
audioInit bool
sampleBuffer audio.Buffer
stereoPanNode audio.StereoPannerNode
gainNode audio.GainNode
destinationNode audio.DestinationNode
mediaElementSourceNode audio.MediaElementSourceNode
width int32
height int32
}
func (app *AudioApp) OnCreate(settings *tge.Settings) error {
fmt.Println("OnCreate()")
settings.Fullscreen = true
return nil
}
func (app *AudioApp) OnStart(runtime tge.Runtime) error {
fmt.Println("OnStart()")
app.runtime = runtime
runtime.Subscribe(tge.MouseEvent{}.Channel(), app.OnMouseEvent)
runtime.Subscribe(tge.ResizeEvent{}.Channel(), app.OnResize)
return nil
}
// InitAudio is start on mouse click only due to browsers restrictions
func (app *AudioApp) InitAudio() error {
var err error
//Buffer (should be done in a loading screen off course)
fmt.Println("Loading left-right.ogg in buffer")
if app.sampleBuffer, err = audio.CreateBuffer("left-right.ogg"); err != nil {
return err
}
// Audio graph
fmt.Println("Creating audio graph")
if app.destinationNode, err = audio.CreateDestinationNode(); err != nil {
return err
}
if app.gainNode, err = audio.CreateGainNode(); err != nil {
return err
}
if app.stereoPanNode, err = audio.CreateStereoPannerNode(); err != nil {
return err
}
app.stereoPanNode.Connect(app.gainNode).Connect(app.destinationNode)
//Music (should be done in a loading screen off course)
fmt.Println("Loading music.ogg in media element")
if app.mediaElementSourceNode, err = audio.CreateMediaElementSourceNode("music.ogg"); err != nil {
return err
}
var mediaElementGainNode audio.GainNode
if mediaElementGainNode, err = audio.CreateGainNode(); err != nil {
return err
}
mediaElementGainNode.Gain(0.3)
app.mediaElementSourceNode.Connect(mediaElementGainNode).Connect(app.destinationNode)
fmt.Println("Start playing music")
app.mediaElementSourceNode.Play(true)
app.audioInit = true
return nil
}
func (app *AudioApp) OnResume() {
fmt.Println("OnResume()")
// Open sound
if app.audioInit {
app.gainNode.Connect(app.destinationNode)
app.mediaElementSourceNode.Play(true)
}
}
func (app *AudioApp) OnResize(event tge.Event) bool {
app.width = (event.(tge.ResizeEvent)).Width
app.height = (event.(tge.ResizeEvent)).Height
return false
}
func (app *AudioApp) OnMouseEvent(event tge.Event) bool {
mouseEvent := event.(tge.MouseEvent)
if mouseEvent.Type == tge.TypeDown && (mouseEvent.Button&tge.ButtonLeftOrTouchFirst != 0) {
if !app.audioInit {
if err := app.InitAudio(); err != nil {
fmt.Printf("ERROR: %s\n", err)
}
}
app.stereoPanNode.Pan(float32(mouseEvent.X)/float32(app.width)*2 - 1.0)
app.gainNode.Gain(float32(app.height-mouseEvent.Y) / float32(app.height))
fmt.Printf("Set pan to %v and gain to %v%%\n", (float32(mouseEvent.X)/float32(app.width)*2 - 1.0), float32(app.height-mouseEvent.Y)/float32(app.height)*100)
if sourceNode, err := audio.CreateBufferSourceNode(app.sampleBuffer); err != nil {
fmt.Printf("ERROR: %s\n", err)
} else {
sourceNode.Connect(app.stereoPanNode)
if mouseEvent.X < app.width/2 {
// Left chunk
sourceNode.Start(0, 0, 0.5, false, 0, 0)
} else {
// Right chunk
sourceNode.Start(0, 0.5, 0.5, false, 0, 0)
}
}
}
return false
}
func (app *AudioApp) OnRender(elaspedTime time.Duration, syncChan <-chan interface{}) {
<-syncChan
}
func (app *AudioApp) OnTick(elaspedTime time.Duration, syncChan chan<- interface{}) {
syncChan <- true
}
func (app *AudioApp) OnPause() {
fmt.Println("OnPause()")
// Close sound
if app.audioInit {
app.gainNode.Disconnect(app.destinationNode)
app.mediaElementSourceNode.Pause()
}
}
func (app *AudioApp) OnStop() {
fmt.Println("OnStop()")
app.runtime.Unsubscribe(tge.MouseEvent{}.Channel(), app.OnMouseEvent)
app.runtime.Unsubscribe(tge.ResizeEvent{}.Channel(), app.OnResize)
}
func (app *AudioApp) OnDispose() {
fmt.Println("OnDispose()")
}
func main() {
tge.Run(&AudioApp{})
}