func (c *Context) Play() { c.Lock() defer c.Unlock() n := c.source.BuffersProcessed() if n > 0 { rm, split := c.queue[:n], c.queue[n:] c.queue = split c.source.UnqueueBuffers(rm...) al.DeleteBuffers(rm...) } for len(c.queue) < QUEUE { b := al.GenBuffers(1) buf := make([]byte, NS*CZ) for n := 0; n < NS*CZ; n += CZ { v := int16(float32(32767) * c.oscilator()) binary.LittleEndian.PutUint16(buf[n:n+2], uint16(v)) } b[0].BufferData(Fmt, buf, SampleRate) c.source.QueueBuffers(b...) c.queue = append(c.queue, b...) } if c.source.State() != al.Playing { al.PlaySources(c.source) } }
func NewPlayer(sampleRate, channelNum, bytesPerSample int) (*Player, error) { var p *Player if err := al.OpenDevice(); err != nil { return nil, fmt.Errorf("driver: OpenAL initialization failed: %v", err) } s := al.GenSources(1) if e := al.Error(); e != 0 { return nil, fmt.Errorf("driver: al.GenSources error: %d", e) } p = &Player{ alSource: s[0], alBuffers: []al.Buffer{}, sampleRate: sampleRate, alFormat: alFormat(channelNum, bytesPerSample), } runtime.SetFinalizer(p, (*Player).Close) bs := al.GenBuffers(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.BufferData(p.alFormat, emptyBytes, int32(p.sampleRate)) p.alSource.QueueBuffers(b) } al.PlaySources(p.alSource) return p, nil }
// PlaySound will play the key's sound if the coordinates lay within the key itself. func (k *PianoKey) PlaySound(x float32, y float32, frameData util.FrameData) bool { if !k.pressed && k.DoesCoordsOverlapKey(x, y, frameData) { k.pressed = true al.PlaySources(k.soundSources...) return true } return false }
// Play buffers the source audio to the audio device and starts // to play the source. // If the player paused or stopped, it reuses the previously buffered // resources to keep playing from the time it has paused or stopped. func (p *Player) Play() error { if p == nil { return nil } // Prepares if the track hasn't been buffered before. if err := p.prepare(0, false); err != nil { return err } al.PlaySources(p.source) return lastErr() }
func (as *AudioSystem) Update(entity *ecs.Entity, dt float32) { var ac *AudioComponent var ok bool if ac, ok = entity.ComponentFast(ac).(*AudioComponent); !ok { return } if ac.player == nil { f := Files.Sound(ac.File) if f == nil { return } var err error ac.player, err = NewPlayer(f, 0, 0) if err != nil { log.Println("Error initializing AudioSystem:", err) return } } if ac.player.State() != Playing { if ac.player.State() == Stopped { if !ac.Repeat { al.RewindSources(ac.player.source) al.StopSources(ac.player.source) entity.RemoveComponent(ac) return } } // Prepares if the track hasn't been buffered before. if err := ac.player.prepare(ac.Background, 0, false); err != nil { log.Println("Error initializing AudioSystem:", err) return } al.PlaySources(ac.player.source) if !ac.Background { var space *SpaceComponent var ok bool if space, ok = entity.ComponentFast(space).(*SpaceComponent); !ok { return } ac.player.source.SetPosition(al.Vector{ (space.Position.X + space.Width/2) / Width(), (space.Position.Y + space.Height/2) / Height(), 0}) } } }
func (a *AudioSystem) Update(dt float32) { for _, e := range a.entities { if e.AudioComponent.player == nil { f := Files.Sound(e.AudioComponent.File) if f == nil { log.Println("Audio file not loaded:", e.AudioComponent.File) continue } var err error e.AudioComponent.player, err = NewPlayer(f, 0, 0) if err != nil { log.Println("Error initializing AudioComponent:", err) continue } } if MasterVolume != a.cachedVolume { e.AudioComponent.SetVolume(e.AudioComponent.RawVolume) } if e.AudioComponent.player.State() != Playing { if e.AudioComponent.player.State() == Stopped { if !e.AudioComponent.Repeat { al.RewindSources(e.AudioComponent.player.source) al.StopSources(e.AudioComponent.player.source) // Remove it from this system, defer because we want to be sure it doesn't interfere with // looping over a.entities defer a.Remove(*e.BasicEntity) continue } } // Prepares if the track hasn't been buffered before. if err := e.AudioComponent.player.prepare(e.AudioComponent.Background, 0, false); err != nil { log.Println("Error initializing AudioComponent:", err) continue } al.PlaySources(e.AudioComponent.player.source) if !e.AudioComponent.Background { e.AudioComponent.player.source.SetPosition(al.Vector{ (e.SpaceComponent.Position.X + e.SpaceComponent.Width/2) / GameWidth(), // TODO: ensure we're using correct Width/Height() (e.SpaceComponent.Position.Y + e.SpaceComponent.Height/2) / GameHeight(), 0, }) } } } }
func (k *PianoKey) Press(f int) { k.Angle = 3.14 / 16 k.Finger = f light.Intensities = k.LightColor bp := k.source.BuffersProcessed() if bp > 0 { b := make([]al.Buffer, bp) k.source.UnqueueBuffers(b...) } if k.source.BuffersQueued() == 0 { k.source.QueueBuffers(k.buffers[0]) al.PlaySources(k.source) } }
// Seek moves the play head to the given offset relative to the start of the source. func (p *Player) Seek(offset time.Duration) error { if p == nil { return nil } if err := p.Stop(); err != nil { return err } size := durToByteOffset(p.t, offset) if err := p.prepare(size, true); err != nil { return err } al.PlaySources(p.source) return lastErr() }
func (p *Player) Proceed(data []byte) error { if err := al.Error(); err != 0 { return fmt.Errorf("driver: before proceed: %d", err) } processedNum := p.alSource.BuffersProcessed() if 0 < processedNum { bufs := make([]al.Buffer, processedNum) p.alSource.UnqueueBuffers(bufs...) if err := al.Error(); err != 0 { return fmt.Errorf("driver: Unqueue in process: %d", 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.BufferData(p.alFormat, data, int32(p.sampleRate)) p.alSource.QueueBuffers(buf) if err := al.Error(); err != 0 { return fmt.Errorf("driver: Queue in process: %d", err) } if p.alSource.State() == al.Stopped || p.alSource.State() == al.Initial { al.RewindSources(p.alSource) al.PlaySources(p.alSource) if err := al.Error(); err != 0 { return fmt.Errorf("driver: PlaySource in process: %d", err) } } return nil }
func Tick() { start := time.Now() if code := al.DeviceError(); code != 0 { log.Printf("snd/al: unknown device error [err=%v]\n", code) } if code := al.Error(); code != 0 { log.Printf("snd/al: unknown error [err=%v]\n", code) } bufs := hwa.buf.Get() for _, buf := range bufs { hwa.tc++ // TODO the general idea here is that GetInputs is rather cheap to call, even with the // current first-draft implementation, so it could only return inputs that are actually // turned on. This would introduce software latency determined by snd.DefaultBufferLen // as turning an input back on would not get picked up until the next iteration. // if !realtime { // hwa.inputs = snd.GetInputs(hwa.in) // } dp.Dispatch(hwa.tc, hwa.inputs...) for i, x := range hwa.in.Samples() { // clip if x > 1 { x = 1 } else if x < -1 { x = -1 } n := int16(math.MaxInt16 * x) hwa.out[2*i] = byte(n) hwa.out[2*i+1] = byte(n >> 8) } buf.BufferData(hwa.format, hwa.out, int32(hwa.in.SampleRate())) if code := al.Error(); code != 0 { log.Printf("snd/al: buffer data failed [err=%v]\n", code) } } if len(bufs) != 0 { hwa.source.QueueBuffers(bufs) } if code := al.Error(); code != 0 { log.Printf("snd/al: queue buffer failed [err=%v]\n", code) } switch hwa.source.State() { case al.Initial: al.PlaySources(hwa.source) case al.Playing: case al.Paused: case al.Stopped: hwa.underruns++ al.PlaySources(hwa.source) } hwa.tdur += time.Now().Sub(start) }
// PlaySound plays the sound of the source i func PlaySound(i int) { al.PlaySources(sources[i]) }