mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 18:58:54 +01:00
audio: Add AudioContext.Update (#177)
This commit is contained in:
parent
77b62615b2
commit
71312ba26f
@ -35,6 +35,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func update(screen *ebiten.Image) error {
|
||||||
|
audioContext.Update()
|
||||||
if !audioLoaded {
|
if !audioLoaded {
|
||||||
select {
|
select {
|
||||||
case <-audioLoadingDone:
|
case <-audioLoadingDone:
|
||||||
|
@ -131,6 +131,7 @@ func addNote() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func update(screen *ebiten.Image) error {
|
||||||
|
audioContext.Update()
|
||||||
defer func() {
|
defer func() {
|
||||||
frames++
|
frames++
|
||||||
}()
|
}()
|
||||||
|
@ -201,6 +201,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func update(screen *ebiten.Image) error {
|
||||||
|
audioContext.Update()
|
||||||
updateInput()
|
updateInput()
|
||||||
for i, key := range keys {
|
for i, key := range keys {
|
||||||
if keyStates[key] != 1 {
|
if keyStates[key] != 1 {
|
||||||
|
@ -79,6 +79,7 @@ func (s *stream) Seek(offset int64, whence int) (int64, error) {
|
|||||||
var player *audio.Player
|
var player *audio.Player
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func update(screen *ebiten.Image) error {
|
||||||
|
audioContext.Update()
|
||||||
if player == nil {
|
if player == nil {
|
||||||
var err error
|
var err error
|
||||||
player, err = audioContext.NewPlayer(&stream{})
|
player, err = audioContext.NewPlayer(&stream{})
|
||||||
|
@ -23,7 +23,8 @@ import (
|
|||||||
|
|
||||||
// TODO: In JavaScript, mixing should be done by WebAudio for performance.
|
// TODO: In JavaScript, mixing should be done by WebAudio for performance.
|
||||||
type mixedPlayersStream struct {
|
type mixedPlayersStream struct {
|
||||||
context *Context
|
context *Context
|
||||||
|
writtenBytes int
|
||||||
}
|
}
|
||||||
|
|
||||||
func min(a, b int) int {
|
func min(a, b int) int {
|
||||||
@ -37,9 +38,19 @@ func (s *mixedPlayersStream) Read(b []byte) (int, error) {
|
|||||||
s.context.Lock()
|
s.context.Lock()
|
||||||
defer s.context.Unlock()
|
defer s.context.Unlock()
|
||||||
|
|
||||||
|
// TODO: 60 (FPS) is a magic number
|
||||||
|
bytesPerFrame := s.context.sampleRate * 4 / 60
|
||||||
|
x := s.context.frames*bytesPerFrame + len(b)
|
||||||
|
if x <= s.writtenBytes {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
l := len(b) / 4 * 4
|
l := len(b) / 4 * 4
|
||||||
if len(s.context.players) == 0 {
|
if len(s.context.players) == 0 {
|
||||||
return 0, nil
|
l := min(len(b), x-s.writtenBytes)
|
||||||
|
copy(b, make([]byte, l))
|
||||||
|
s.writtenBytes += l
|
||||||
|
return l, nil
|
||||||
}
|
}
|
||||||
closed := []*Player{}
|
closed := []*Player{}
|
||||||
ll := l
|
ll := l
|
||||||
@ -76,6 +87,7 @@ func (s *mixedPlayersStream) Read(b []byte) (int, error) {
|
|||||||
for _, p := range closed {
|
for _, p := range closed {
|
||||||
delete(s.context.players, p)
|
delete(s.context.players, p)
|
||||||
}
|
}
|
||||||
|
s.writtenBytes += ll
|
||||||
return ll, nil
|
return ll, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +98,7 @@ type Context struct {
|
|||||||
stream *mixedPlayersStream
|
stream *mixedPlayersStream
|
||||||
players map[*Player]struct{}
|
players map[*Player]struct{}
|
||||||
innerPlayer *player
|
innerPlayer *player
|
||||||
|
frames int
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +108,9 @@ func NewContext(sampleRate int) *Context {
|
|||||||
sampleRate: sampleRate,
|
sampleRate: sampleRate,
|
||||||
players: map[*Player]struct{}{},
|
players: map[*Player]struct{}{},
|
||||||
}
|
}
|
||||||
c.stream = &mixedPlayersStream{c}
|
c.stream = &mixedPlayersStream{
|
||||||
|
context: c,
|
||||||
|
}
|
||||||
p, err := startPlaying(c.stream, c.sampleRate)
|
p, err := startPlaying(c.stream, c.sampleRate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("audio: NewContext error: %v", err))
|
panic(fmt.Sprintf("audio: NewContext error: %v", err))
|
||||||
@ -104,6 +119,18 @@ func NewContext(sampleRate int) *Context {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update proceeds the inner (logical) time of the context by 1/60 second.
|
||||||
|
// This is expected to be called in the game's updating function (sync mode)
|
||||||
|
// or an independent goroutine with timers (unsync mode).
|
||||||
|
// In sync mode, the game logical time syncs the audio logical time and
|
||||||
|
// you will find audio stops when the game stops e.g. when the window is deactivated.
|
||||||
|
// In unsync mode, the audio never stops even when the game stops.
|
||||||
|
func (c *Context) Update() {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
c.frames++
|
||||||
|
}
|
||||||
|
|
||||||
type Player struct {
|
type Player struct {
|
||||||
context *Context
|
context *Context
|
||||||
src io.ReadSeeker
|
src io.ReadSeeker
|
||||||
|
@ -79,11 +79,6 @@ func (p *player) proceed() error {
|
|||||||
const bytesPerSample = channelNum * 16 / 8
|
const bytesPerSample = channelNum * 16 / 8
|
||||||
bufferSize := p.sampleRate * bytesPerSample / 60
|
bufferSize := p.sampleRate * bytesPerSample / 60
|
||||||
c := int64(p.context.Get("currentTime").Float() * float64(p.sampleRate))
|
c := int64(p.context.Get("currentTime").Float() * float64(p.sampleRate))
|
||||||
// Buffer size is relatively big and it is needed to check that c.positionInSample doesn't
|
|
||||||
// proceed too far away (#180).
|
|
||||||
if c+int64(bufferSize) < p.positionInSamples {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if p.positionInSamples < c {
|
if p.positionInSamples < c {
|
||||||
p.positionInSamples = c
|
p.positionInSamples = c
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ type VorbisStream struct {
|
|||||||
buf *bytes.Reader
|
buf *bytes.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Rename to DecodeVorbis or Decode?
|
||||||
|
|
||||||
func (c *Context) NewVorbisStream(src io.Reader) (*VorbisStream, error) {
|
func (c *Context) NewVorbisStream(src io.Reader) (*VorbisStream, error) {
|
||||||
decoded, channels, sampleRate, err := vorbis.Decode(src)
|
decoded, channels, sampleRate, err := vorbis.Decode(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user