forked from ebitengine/oto
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdriver.go
175 lines (150 loc) · 5.32 KB
/
driver.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// Copyright 2021 The Oto Authors
//
// 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.
package oto
import (
"io"
"sync"
)
// Context is the main object in Oto. It interacts with the audio drivers.
//
// To play sound with Oto, first create a context. Then use the context to create
// an arbitrary number of players. Then use the players to play sound.
//
// There can only be one context at any time. Closing a context and opening a new one is allowed.
type Context struct {
context *context
}
// NewPlayer creates a new, ready-to-use Player belonging to the Context.
//
// The r's format is as follows:
// [data] = [sample 1] [sample 2] [sample 3] ...
// [sample *] = [channel 1] ...
// [channel *] = [byte 1] [byte 2] ...
// Byte ordering is little endian.
//
// A player has some amount of an underlying buffer.
// Read data from r is queued to the player's underlying buffer.
// The underlying buffer is consumed by its playing.
// Then, r's position and the current playing position don't necessarily match.
// If you want to clear the underlying buffer for some reasons e.g., you want to seek the position of r,
// call the player's Reset function.
//
// You cannot share r by multiple players.
//
// The returned player implements both Player and BufferSizeSetter.
// You can modify the buffer size of a player by the SetBufferSize function.
// A small buffer size is useful if you want to play a real-time PCM for example.
// Note that the audio quality might be affected if you modify the buffer size.
//
// NewPlayer is concurrent-safe.
//
// All the functions of a Player returned by NewPlayer are concurrent-safe.
func (c *Context) NewPlayer(r io.Reader) Player {
return c.context.NewPlayer(r)
}
// Suspend suspends the entire audio play.
//
// Suspend is concurrent-safe.
func (c *Context) Suspend() error {
return c.context.Suspend()
}
// Resume resumes the entire audio play, which was suspended by Suspend.
//
// Resume is concurrent-safe.
func (c *Context) Resume() error {
return c.context.Resume()
}
// Err returns the current error.
//
// Err is concurrent-safe.
func (c *Context) Err() error {
return c.context.Err()
}
// NewContext creates a new context, that creates and holds ready-to-use Player objects,
// and returns a context, a channel that is closed when the context is ready, and an error if it exists.
//
// The sampleRate argument specifies the number of samples that should be played during one second.
// Usual numbers are 44100 or 48000.
//
// The channelNum argument specifies the number of channels. One channel is mono playback. Two
// channels are stereo playback. No other values are supported.
//
// The bitDepthInBytes argument specifies the number of bytes per sample per channel. The usual value
// is 2. Only values 1 and 2 are supported.
func NewContext(sampleRate int, channelNum int, bitDepthInBytes int) (*Context, chan struct{}, error) {
ctx, ready, err := newContext(sampleRate, channelNum, bitDepthInBytes)
if err != nil {
return nil, nil, err
}
return &Context{context: ctx}, ready, nil
}
// Player is a PCM (pulse-code modulation) audio player.
type Player interface {
// Pause pauses its playing.
Pause()
// Play starts its playing if it doesn't play.
Play()
// IsPlaying reports whether this player is playing.
IsPlaying() bool
// Reset clears the underyling buffer and pauses its playing.
Reset()
// Volume returns the current volume in the range of [0, 1].
// The default volume is 1.
Volume() float64
// SetVolume sets the current volume in the range of [0, 1].
SetVolume(volume float64)
// UnplayedBufferSize returns the byte size in the underlying buffer that is not played yet.
UnplayedBufferSize() int
// Err returns an error if this player has an error.
Err() error
io.Closer
}
// BufferSizeSetter sets a buffer size.
// A player created by (*Context).NewPlayer implments both Player and BufferSizeSetter.
type BufferSizeSetter interface {
// SetBufferSize sets the buffer size.
// If 0 is specified, the default buffer size is used.
SetBufferSize(bufferSize int)
}
type playerState int
const (
playerPaused playerState = iota
playerPlay
playerClosed
)
// TODO: The term 'buffer' is confusing. Name each buffer with good terms.
// defaultBufferSize returns the default size of the buffer for the audio source.
// This buffer is used when unreading on pausing the player.
func (c *context) defaultBufferSize() int {
bytesPerSample := c.channelNum * c.bitDepthInBytes
s := c.sampleRate * bytesPerSample / 2 // 0.5[s]
// Align s in multiples of bytes per sample, or a buffer could have extra bytes.
return s / bytesPerSample * bytesPerSample
}
type atomicError struct {
err error
m sync.Mutex
}
func (a *atomicError) TryStore(err error) {
a.m.Lock()
defer a.m.Unlock()
if a.err == nil {
a.err = err
}
}
func (a *atomicError) Load() error {
a.m.Lock()
defer a.m.Unlock()
return a.err
}