mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 03:38:55 +01:00
audio: Move drivers to internal/driver
This commit is contained in:
parent
5c73357ef8
commit
2840bafd0a
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
@ -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)
|
||||||
}
|
}
|
@ -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)
|
||||||
@ -130,7 +130,7 @@ func (p *player) proceed() error {
|
|||||||
headerToWrite := (*header)(nil)
|
headerToWrite := (*header)(nil)
|
||||||
for _, h := range p.headers {
|
for _, h := range p.headers {
|
||||||
// TODO: Need to check WHDR_DONE?
|
// TODO: Need to check WHDR_DONE?
|
||||||
if h.waveHdr.dwFlags & C.WHDR_INQUEUE == 0 {
|
if h.waveHdr.dwFlags&C.WHDR_INQUEUE == 0 {
|
||||||
headerToWrite = h
|
headerToWrite = h
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -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
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user