func AddSource(in snd.Sound) error { switch in.Channels() { case 1: hwa.format = al.FormatMono16 case 2: hwa.format = al.FormatStereo16 default: return fmt.Errorf("snd/al: can't handle input with channels(%v)", in.Channels()) } hwa.in = in hwa.out = make([]byte, in.BufferLen()*2) s := al.GenSources(1) if code := al.Error(); code != 0 { return fmt.Errorf("snd/al: generate source failed [err=%v]", code) } hwa.source = s[0] hwa.buf.src = s[0] log.Println("snd/al: software latency", SoftLatency()) hwa.inputs = snd.GetInputs(in) return nil }
// NewPlayer returns a new Player. // It initializes the underlying audio devices and the related resources. // If zero values are provided for format and sample rate values, the player // determines them from the source's WAV header. // An error is returned if the format and sample rate can't be determined. func NewPlayer(src ReadSeekCloser, format Format, samplesPerSecond int64) (*Player, error) { if err := al.OpenDevice(); err != nil { return nil, err } if err := createContext(); err != nil { return nil, err } s := al.GenSources(1) if code := al.Error(); code != 0 { return nil, fmt.Errorf("audio: cannot generate an audio source [err=%x]", code) } p := &Player{ t: &track{format: format, src: src, samplesPerSecond: samplesPerSecond}, source: s[0], } if err := p.discoverHeader(); err != nil { return nil, err } if p.t.format == 0 { return nil, errors.New("audio: cannot determine the format") } if p.t.samplesPerSecond == 0 { return nil, errors.New("audio: cannot determine the sample rate") } return p, nil }
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 }
func (p *Player) Close() error { if err := al.Error(); err != 0 { return fmt.Errorf("driver: error before closing: %d", err) } if p.isClosed { return nil } var bs []al.Buffer al.RewindSources(p.alSource) al.StopSources(p.alSource) if n := p.alSource.BuffersQueued(); 0 < n { bs = make([]al.Buffer, n) p.alSource.UnqueueBuffers(bs...) p.alBuffers = append(p.alBuffers, bs...) } p.isClosed = true if err := al.Error(); err != 0 { return fmt.Errorf("driver: error after closing: %d", err) } runtime.SetFinalizer(p, nil) return nil }
func NewContext(oscillator model.Oscillator) *Context { if err := al.OpenDevice(); err != nil { log.Fatal(err) } s := al.GenSources(1) if code := al.Error(); code != 0 { log.Fatalln("openal error:", code) } return &Context{ source: s[0], queue: []al.Buffer{}, oscillator: oscillator, } }
// Get returns a slice of buffers of length b.size ready to receive data and be queued. // If b.src reports processing at-least as many buffers as b.size, buffers up to b.size // will be unqueued for reuse. Otherwise, new buffers will be generated. // // If the length of b.bufs has grown to be greater than maxbufs, a nil slice is returned. func (b *Buffer) Get() (bufs []al.Buffer) { if proc := int(b.src.BuffersProcessed()); proc >= b.size { // advance by size, BuffersProcessed will report that many less next time. bufs = b.bufs[b.idx : b.idx+b.size] b.src.UnqueueBuffers(bufs) if code := al.Error(); code != 0 { log.Printf("snd/al: unqueue buffers failed [err=%v]\n", code) } b.idx = (b.idx + b.size) % len(b.bufs) } else if len(b.bufs) >= maxbufs { // likely programmer error, something has gone horribly wrong. log.Printf("snd/al: get buffers failed, maxbufs reached [len=%v]\n", len(b.bufs)) return nil } else { // make more buffers to fill data regardless of what openal says about processed. bufs = al.GenBuffers(b.size) if code := al.Error(); code != 0 { log.Printf("snd/al: generate buffers failed [err=%v]\n", code) } b.bufs = append(b.bufs, bufs...) } return bufs }
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 NewContext(oscilator Oscilator) *Context { if err := al.OpenDevice(); err != nil { log.Fatal(err) } s := al.GenSources(1) if code := al.Error(); code != 0 { log.Fatalln("openal error:", code) } //s[0].SetGain(s[0].MaxGain()) //s[0].SetPosition(al.ListenerPosition()) return &Context{ source: s[0], queue: []al.Buffer{}, oscilator: oscilator, } }
// lastErr returns the last error or nil if the last operation // has been succesful. func lastErr() error { if code := al.Error(); code != 0 { return fmt.Errorf("audio: openal failed with %x", code) } 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) }