forked from hajimehoshi/ebiten
-
Notifications
You must be signed in to change notification settings - Fork 0
/
driver_openal.go
156 lines (141 loc) · 4.22 KB
/
driver_openal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// 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,!windows
package driver
import (
"fmt"
"runtime"
"github.com/hajimehoshi/go-openal/openal"
)
// As x/mobile/exp/audio/al is broken on Mac OS X (https://github.com/golang/go/issues/15075),
// let's use github.com/hajimehoshi/go-openal instead.
const (
maxBufferNum = 8
)
type Player struct {
alDevice *openal.Device
alSource openal.Source
alBuffers []openal.Buffer
sampleRate int
isClosed bool
alFormat openal.Format
}
func alFormat(channelNum, bytesPerSample int) openal.Format {
switch {
case channelNum == 1 && bytesPerSample == 1:
return openal.FormatMono8
case channelNum == 1 && bytesPerSample == 2:
return openal.FormatMono16
case channelNum == 2 && bytesPerSample == 1:
return openal.FormatStereo8
case channelNum == 2 && bytesPerSample == 2:
return openal.FormatStereo16
}
panic(fmt.Sprintf("driver: invalid channel num (%d) or bytes per sample (%d)", channelNum, bytesPerSample))
}
func NewPlayer(sampleRate, channelNum, bytesPerSample int) (*Player, error) {
d := openal.OpenDevice("")
if d == nil {
return nil, fmt.Errorf("driver: OpenDevice must not return nil")
}
c := d.CreateContext()
if c == nil {
return nil, fmt.Errorf("driver: CreateContext must not return nil")
}
// Don't check openal.Err until making the current context is done.
// Linux might fail this check even though it succeeds (#204).
c.Activate()
if err := openal.Err(); err != nil {
return nil, fmt.Errorf("driver: Activate: %v", err)
}
s := openal.NewSource()
if err := openal.Err(); err != nil {
return nil, fmt.Errorf("driver: NewSource: %v", err)
}
p := &Player{
alDevice: d,
alSource: s,
alBuffers: []openal.Buffer{},
sampleRate: sampleRate,
alFormat: alFormat(channelNum, bytesPerSample),
}
runtime.SetFinalizer(p, (*Player).Close)
bs := openal.NewBuffers(maxBufferNum)
const bufferSize = 1024
emptyBytes := make([]byte, bufferSize)
for _, b := range bs {
// Note that the third argument of only the first buffer is used.
b.SetData(p.alFormat, emptyBytes, int32(p.sampleRate))
//p.alSource.QueueBuffer(b)
p.alBuffers = append(p.alBuffers, b)
}
p.alSource.Play()
return p, nil
}
func (p *Player) Proceed(data []byte) error {
if err := openal.Err(); err != nil {
return fmt.Errorf("driver: starting Proceed: %v", err)
}
processedNum := p.alSource.BuffersProcessed()
if 0 < processedNum {
bufs := make([]openal.Buffer, processedNum)
p.alSource.UnqueueBuffers(bufs)
if err := openal.Err(); err != nil {
return fmt.Errorf("driver: UnqueueBuffers: %v", err)
}
p.alBuffers = append(p.alBuffers, bufs...)
}
if len(p.alBuffers) == 0 {
// This can happen (#207)
return nil
}
buf := p.alBuffers[0]
p.alBuffers = p.alBuffers[1:]
buf.SetData(p.alFormat, data, int32(p.sampleRate))
p.alSource.QueueBuffer(buf)
if err := openal.Err(); err != nil {
return fmt.Errorf("driver: QueueBuffer: %v", err)
}
if p.alSource.State() == openal.Stopped || p.alSource.State() == openal.Initial {
p.alSource.Rewind()
p.alSource.Play()
if err := openal.Err(); err != nil {
return fmt.Errorf("driver: Rewind or Play: %v", err)
}
}
return nil
}
func (p *Player) Close() error {
if err := openal.Err(); err != nil {
return fmt.Errorf("driver: starting Close: %v", err)
}
if p.isClosed {
return nil
}
var bs []openal.Buffer
p.alSource.Rewind()
p.alSource.Play()
if n := p.alSource.BuffersQueued(); 0 < n {
bs = make([]openal.Buffer, n)
p.alSource.UnqueueBuffers(bs)
p.alBuffers = append(p.alBuffers, bs...)
}
p.alDevice.CloseDevice()
p.isClosed = true
if err := openal.Err(); err != nil {
return fmt.Errorf("driver: CloseDevice: %v", err)
}
runtime.SetFinalizer(p, nil)
return nil
}