audio: Move drivers to internal/driver

This commit is contained in:
Hajime Hoshi 2016-04-09 00:54:18 +09:00
parent 5c73357ef8
commit 2840bafd0a
5 changed files with 40 additions and 33 deletions

View File

@ -21,6 +21,7 @@ import (
"time" "time"
"github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/exp/audio/internal/driver"
) )
type mixingStream struct { type mixingStream struct {
@ -44,7 +45,6 @@ func min(a, b int) int {
const ( const (
channelNum = 2 channelNum = 2
bytesPerSample = 2 bytesPerSample = 2
bitsPerSample = bytesPerSample * 8
// TODO: This assumes that channelNum is a power of 2. // TODO: This assumes that channelNum is a power of 2.
mask = ^(channelNum*bytesPerSample - 1) mask = ^(channelNum*bytesPerSample - 1)
@ -197,15 +197,15 @@ func NewContext(sampleRate int) (*Context, error) {
players: map[*Player]struct{}{}, players: map[*Player]struct{}{},
} }
// TODO: Rename this other than player // TODO: Rename this other than player
p, err := newPlayer(c.stream, sampleRate) p, err := driver.NewPlayer(c.stream, sampleRate, channelNum, bytesPerSample)
if err != nil { if err != nil {
return nil, err return nil, err
} }
go func() { go func() {
// TODO: Is it OK to close asap? // TODO: Is it OK to close asap?
defer p.close() defer p.Close()
for { for {
err := p.proceed() err := p.Proceed()
if err == io.EOF { if err == io.EOF {
break break
} }

View File

@ -14,25 +14,26 @@
// +build js // +build js
package audio package driver
import ( import (
"errors" "errors"
"io" "io"
"github.com/gopherjs/gopherjs/js" "github.com/gopherjs/gopherjs/js"
"github.com/hajimehoshi/ebiten"
) )
type player struct { type Player struct {
src io.Reader src io.Reader
sampleRate int sampleRate int
channelNum int
bytesPerSample int
positionInSamples int64 positionInSamples int64
context *js.Object context *js.Object
bufferSource *js.Object bufferSource *js.Object
} }
func newPlayer(src io.Reader, sampleRate int) (*player, error) { func NewPlayer(src io.Reader, sampleRate, channelNum, bytesPerSample int) (*Player, error) {
class := js.Global.Get("AudioContext") class := js.Global.Get("AudioContext")
if class == js.Undefined { if class == js.Undefined {
class = js.Global.Get("webkitAudioContext") class = js.Global.Get("webkitAudioContext")
@ -40,9 +41,11 @@ func newPlayer(src io.Reader, sampleRate int) (*player, error) {
if class == js.Undefined { if class == js.Undefined {
return nil, errors.New("audio: audio couldn't be initialized") return nil, errors.New("audio: audio couldn't be initialized")
} }
p := &player{ p := &Player{
src: src, src: src,
sampleRate: sampleRate, sampleRate: sampleRate,
channelNum: channelNum,
bytesPerSample: bytesPerSample,
bufferSource: nil, bufferSource: nil,
context: class.New(), context: class.New(),
} }
@ -67,8 +70,12 @@ func max64(a, b int64) int64 {
return b return b
} }
func (p *player) proceed() error { const (
bufferSize := p.sampleRate * bytesPerSample * channelNum / ebiten.FPS // 1024 seems not enough (some noise remains after the tab is deactivated).
bufferSize = 2048
)
func (p *Player) Proceed() error {
c := int64(p.context.Get("currentTime").Float() * float64(p.sampleRate)) c := int64(p.context.Get("currentTime").Float() * float64(p.sampleRate))
if p.positionInSamples < c { if p.positionInSamples < c {
p.positionInSamples = c p.positionInSamples = c
@ -76,7 +83,7 @@ func (p *player) proceed() error {
b := make([]byte, bufferSize) b := make([]byte, bufferSize)
n, err := p.src.Read(b) n, err := p.src.Read(b)
if 0 < n { if 0 < n {
buf := p.context.Call("createBuffer", channelNum, n/bytesPerSample/channelNum, p.sampleRate) buf := p.context.Call("createBuffer", p.channelNum, n/p.bytesPerSample/p.channelNum, p.sampleRate)
l := buf.Call("getChannelData", 0) l := buf.Call("getChannelData", 0)
r := buf.Call("getChannelData", 1) r := buf.Call("getChannelData", 1)
il, ir := toLR(b[:n]) il, ir := toLR(b[:n])
@ -96,7 +103,7 @@ func (p *player) proceed() error {
return err return err
} }
func (p *player) close() error { func (p *Player) Close() error {
if p.bufferSource == nil { if p.bufferSource == nil {
return nil return nil
} }

View File

@ -14,7 +14,7 @@
// +build !js,!windows // +build !js,!windows
package audio package driver
import ( import (
"fmt" "fmt"
@ -28,7 +28,7 @@ const (
maxBufferNum = 8 maxBufferNum = 8
) )
type player struct { type Player struct {
alSource al.Source alSource al.Source
alBuffers []al.Buffer alBuffers []al.Buffer
source io.Reader source io.Reader
@ -36,7 +36,7 @@ type player struct {
isClosed bool isClosed bool
} }
func newPlayer(src io.Reader, sampleRate int) (*player, error) { func NewPlayer(src io.Reader, sampleRate, channelNum, bytesPerSample int) (*Player, error) {
if e := al.OpenDevice(); e != nil { if e := al.OpenDevice(); e != nil {
return nil, fmt.Errorf("audio: OpenAL initialization failed: %v", e) return nil, fmt.Errorf("audio: OpenAL initialization failed: %v", e)
} }
@ -44,13 +44,13 @@ func newPlayer(src io.Reader, sampleRate int) (*player, error) {
if err := al.Error(); err != 0 { if err := al.Error(); err != 0 {
return nil, fmt.Errorf("audio: al.GenSources error: %d", err) return nil, fmt.Errorf("audio: al.GenSources error: %d", err)
} }
p := &player{ p := &Player{
alSource: s[0], alSource: s[0],
alBuffers: []al.Buffer{}, alBuffers: []al.Buffer{},
source: src, source: src,
sampleRate: sampleRate, sampleRate: sampleRate,
} }
runtime.SetFinalizer(p, (*player).close) runtime.SetFinalizer(p, (*Player).Close)
bs := al.GenBuffers(maxBufferNum) bs := al.GenBuffers(maxBufferNum)
emptyBytes := make([]byte, bufferSize) emptyBytes := make([]byte, bufferSize)
@ -72,7 +72,7 @@ var (
tmpAlBuffers = make([]al.Buffer, maxBufferNum) tmpAlBuffers = make([]al.Buffer, maxBufferNum)
) )
func (p *player) proceed() error { func (p *Player) Proceed() error {
if err := al.Error(); err != 0 { if err := al.Error(); err != 0 {
return fmt.Errorf("audio: before proceed: %d", err) return fmt.Errorf("audio: before proceed: %d", err)
} }
@ -113,7 +113,7 @@ func (p *player) proceed() error {
return nil return nil
} }
func (p *player) close() error { func (p *Player) Close() error {
if err := al.Error(); err != 0 { if err := al.Error(); err != 0 {
return fmt.Errorf("audio: error before closing: %d", err) return fmt.Errorf("audio: error before closing: %d", err)
} }

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package audio package driver
// #cgo LDFLAGS: -lwinmm // #cgo LDFLAGS: -lwinmm
// //
@ -75,7 +75,7 @@ func releaseSemaphore() {
<-sem <-sem
} }
type player struct { type Player struct {
src io.Reader src io.Reader
out C.HWAVEOUT out C.HWAVEOUT
buffer []byte buffer []byte
@ -84,21 +84,21 @@ type player struct {
const bufferSize = 1024 const bufferSize = 1024
func newPlayer(src io.Reader, sampleRate int) (*player, error) { func NewPlayer(src io.Reader, sampleRate, channelNum, bytesPerSample int) (*Player, error) {
const numBlockAlign = channelNum * bitsPerSample / 8 const numBlockAlign = channelNum * bytesPerSample
f := C.WAVEFORMATEX{ f := C.WAVEFORMATEX{
wFormatTag: C.WAVE_FORMAT_PCM, wFormatTag: C.WAVE_FORMAT_PCM,
nChannels: channelNum, nChannels: channelNum,
nSamplesPerSec: C.DWORD(sampleRate), nSamplesPerSec: C.DWORD(sampleRate),
nAvgBytesPerSec: C.DWORD(sampleRate) * numBlockAlign, nAvgBytesPerSec: C.DWORD(sampleRate) * numBlockAlign,
wBitsPerSample: bitsPerSample, wBitsPerSample: bytesPerSample * 8,
nBlockAlign: numBlockAlign, nBlockAlign: numBlockAlign,
} }
var w C.HWAVEOUT var w C.HWAVEOUT
if err := C.waveOutOpen2(&w, &f); err != C.MMSYSERR_NOERROR { if err := C.waveOutOpen2(&w, &f); err != C.MMSYSERR_NOERROR {
return nil, fmt.Errorf("audio: waveOutOpen error: %d", err) return nil, fmt.Errorf("audio: waveOutOpen error: %d", err)
} }
p := &player{ p := &Player{
src: src, src: src,
out: w, out: w,
buffer: []byte{}, buffer: []byte{},
@ -114,7 +114,7 @@ func newPlayer(src io.Reader, sampleRate int) (*player, error) {
return p, nil return p, nil
} }
func (p *player) proceed() error { func (p *Player) Proceed() error {
if len(p.buffer) < bufferSize { if len(p.buffer) < bufferSize {
b := make([]byte, bufferSize) b := make([]byte, bufferSize)
n, err := p.src.Read(b) n, err := p.src.Read(b)
@ -146,6 +146,6 @@ func (p *player) proceed() error {
return nil return nil
} }
func (p *player) close() { func (p *Player) Close() {
// TODO: Implement this // TODO: Implement this
} }