ebiten/exp/audio/internal/audio_openal.go
2015-06-07 22:01:14 +09:00

129 lines
3.0 KiB
Go

// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !js
package internal
import (
"bytes"
"encoding/binary"
"fmt"
"log"
"runtime"
"sync"
"time"
"github.com/timshannon/go-openal/openal"
)
var channelsMutex = sync.Mutex{}
func withChannels(f func()) {
channelsMutex.Lock()
defer channelsMutex.Unlock()
f()
}
func toBytesWithPadding(l, r []int16, size int) []byte {
if len(l) != len(r) {
panic("len(l) must equal to len(r)")
}
b := &bytes.Buffer{}
for i := 0; i < len(l); i++ {
if err := binary.Write(b, binary.LittleEndian, []int16{l[i], r[i]}); err != nil {
panic(err)
}
}
if 0 < size-len(l) {
if err := binary.Write(b, binary.LittleEndian, make([]int16, (size-len(l))*2)); err != nil {
panic(err)
}
}
return b.Bytes()
}
func initialize() {
// Creating OpenAL device must be done after initializing UI. I'm not sure the reason.
ch := make(chan struct{})
go func() {
runtime.LockOSThread()
device := openal.OpenDevice("")
context := device.CreateContext()
context.Activate()
if alErr := openal.GetError(); alErr != 0 {
log.Printf("OpenAL initialize error: %d", alErr)
close(ch)
// Graceful ending: Audio is not available on Travis CI.
return
}
audioEnabled = true
sources := openal.NewSources(MaxChannel)
close(ch)
const bufferSize = 512
emptyBytes := make([]byte, 4*bufferSize)
for _, source := range sources {
// 3 is the least number?
// http://stackoverflow.com/questions/14932004/play-sound-with-openalstream
const bufferNum = 4
buffers := openal.NewBuffers(bufferNum)
for _, buffer := range buffers {
buffer.SetData(openal.FormatStereo16, emptyBytes, SampleRate)
source.QueueBuffer(buffer)
}
source.Play()
if alErr := openal.GetError(); alErr != 0 {
panic(fmt.Sprintf("OpenAL error: %d", alErr))
}
}
for {
oneProcessed := false
for ch, source := range sources {
processed := source.BuffersProcessed()
if processed == 0 {
continue
}
oneProcessed = true
buffers := make([]openal.Buffer, processed)
source.UnqueueBuffers(buffers)
for _, buffer := range buffers {
l, r := loadChannelBuffer(ch, bufferSize)
b := toBytesWithPadding(l, r, bufferSize)
buffer.SetData(openal.FormatStereo16, b, SampleRate)
source.QueueBuffer(buffer)
}
if source.State() == openal.Stopped {
source.Rewind()
source.Play()
}
}
if !oneProcessed {
time.Sleep(1 * time.Millisecond)
}
}
}()
<-ch
}
func start() {
// Do nothing
}