audio: Implement for desktops with OpenAL (#104)

This commit is contained in:
Hajime Hoshi 2015-01-25 01:51:51 +09:00
parent a8e3b2b619
commit fe91d341ac
3 changed files with 115 additions and 24 deletions

View File

@ -111,7 +111,7 @@ func update(screen *ebiten.Image) error {
}
func main() {
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Rotate (Ebiten Demo)"); err != nil {
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Audio (Ebiten Demo)"); err != nil {
log.Fatal(err)
}
}

View File

@ -14,6 +14,10 @@
package audio
import (
"sync"
)
const bufferSize = 1024
const SampleRate = 44100
@ -28,6 +32,7 @@ type channel struct {
var MaxChannel = 32
var channels = make([]*channel, MaxChannel)
var channelsLock sync.Mutex
func init() {
for i, _ := range channels {
@ -46,8 +51,30 @@ func Start() {
start()
}
func isPlaying(channel int) bool {
ch := channels[channel]
return nextInsertionPosition < len(ch.l)
}
func channelAt(i int) *channel {
if i == -1 {
for i, _ := range channels {
if !isPlaying(i) {
return channels[i]
}
}
return nil
}
if !isPlaying(i) {
return channels[i]
}
return nil
}
func Play(channel int, l []float32, r []float32) bool {
// TODO: Mutex (especially for OpenAL)
channelsLock.Lock()
defer channelsLock.Unlock()
if len(l) != len(r) {
panic("len(l) must equal to len(r)")
}
@ -63,7 +90,9 @@ func Play(channel int, l []float32, r []float32) bool {
}
func Queue(channel int, l []float32, r []float32) {
// TODO: Mutex (especially for OpenAL)
channelsLock.Lock()
defer channelsLock.Unlock()
if len(l) != len(r) {
panic("len(l) must equal to len(r)")
}
@ -76,21 +105,6 @@ func Update() {
nextInsertionPosition += SampleRate / 60
}
func channelAt(i int) *channel {
if i == -1 {
for i, _ := range channels {
if !IsPlaying(i) {
return channels[i]
}
}
return nil
}
if !IsPlaying(i) {
return channels[i]
}
return nil
}
func min(a, b int) int {
if a < b {
return a
@ -98,7 +112,22 @@ func min(a, b int) int {
return b
}
func isChannelsEmpty() bool {
channelsLock.Lock()
defer channelsLock.Unlock()
for _, ch := range channels {
if 0 < len(ch.l) {
return false
}
}
return true
}
func loadChannelBuffers() (l, r []float32) {
channelsLock.Lock()
defer channelsLock.Unlock()
inputL := make([]float32, bufferSize)
inputR := make([]float32, bufferSize)
for _, ch := range channels {
@ -118,6 +147,8 @@ func loadChannelBuffers() (l, r []float32) {
}
func IsPlaying(channel int) bool {
ch := channels[channel]
return nextInsertionPosition < len(ch.l)
channelsLock.Lock()
defer channelsLock.Unlock()
return isPlaying(channel)
}

View File

@ -17,10 +17,35 @@
package audio
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/timshannon/go-openal/openal"
"math"
"runtime"
"time"
)
func toBytes(l, r []float32) []byte {
if len(l) != len(r) {
panic("len(l) must equal to len(r)")
}
b := &bytes.Buffer{}
for i, _ := range l {
l := int16(l[i] * math.MaxInt16)
r := int16(r[i] * math.MaxInt16)
if err := binary.Write(b, binary.LittleEndian, []int16{l, r}); err != nil {
panic(err)
}
}
return b.Bytes()
}
type sample struct {
l []float32
r []float32
}
func initialize() {
ch := make(chan struct{})
go func() {
@ -30,15 +55,50 @@ func initialize() {
context := device.CreateContext()
context.Activate()
buffer := openal.NewBuffer()
//buffer.SetData(openal.FormatStereo16)
_ = buffer
source := openal.NewSource()
if alErr := openal.GetError(); alErr != 0 {
panic(fmt.Sprintf("OpenAL initialize error: %d", alErr))
}
close(ch)
emptyBytes := make([]byte, 4*bufferSize)
const bufferNum = 16
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 {
if source.State() != openal.Playing {
panic(fmt.Sprintf("invalid source state: %d (0x%[1]x)", source.State()))
}
processed := source.BuffersProcessed()
if processed == 0 {
time.Sleep(1)
continue
}
buffers := make([]openal.Buffer, processed)
source.UnqueueBuffers(buffers)
for _, buffer := range buffers {
l, r := loadChannelBuffers()
b := toBytes(l, r)
buffer.SetData(openal.FormatStereo16, b, SampleRate)
source.QueueBuffer(buffer)
nextInsertionPosition -= min(bufferSize, nextInsertionPosition)
}
}
}()
<-ch
}
func start() {
// TODO: Implement
// Do nothing
}