Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Underrun every second time calling PlaybackDevice.Write(buffer) #6

Open
ratzupaltuff opened this issue Aug 19, 2024 · 1 comment
Open

Comments

@ratzupaltuff
Copy link

ratzupaltuff commented Aug 19, 2024

Hi, thanks for your great library!
Unfortunately I am unable to play audio every second time I call the Write method. It plays fine the first time and fails with an underrun every second time. When adjusting the Buffer properties nothing changes.

My code:

const (
	sampleRate = 44100
	frequency  = 1760
	format   = alsa.FormatS8
	channels = 1
        
        // I tried many different combinations, I got the same behavior with every combination
	bufferFrames = periodFrames * periods
	periodFrames = sampleRate * 0.1
	periods      =  4
)

type AlsaAudioDevice struct {
	playbackDevice *alsa.PlaybackDevice
	sampleRate     int
	frequency      int
	format         alsa.Format
	channels       int
}

func NewAudioDevice(bluetoothMacAddr BTMAC) (*AlsaAudioDevice, error) {
	bufferParams := alsa.BufferParams{
		BufferFrames: bufferFrames,
		PeriodFrames: periodFrames,
		Periods:      periods,
	}
	p, err := alsa.NewPlaybackDevice("bluealsa:SRV=org.bluealsa,DEV="+string(bluetoothMacAddr)+",PROFILE=a2dp",
		channels, format, sampleRate,
		bufferParams)
	if err != nil {
		return nil, err
	}

	return &AlsaAudioDevice{playbackDevice: p}, nil
}

func (ad AlsaAudioDevice) CloseAudioDevice() {
	ad.playbackDevice.Close()
}

func (ad AlsaAudioDevice) PlayBeep(duration time.Duration) error {
	milliseconds := int(duration.Milliseconds())
	numSamples := milliseconds * sampleRate / 1000
	samples := make([]int8, numSamples*channels)

        // generate sine wave
	for i := 0; i < numSamples; i++ {
		// maxInt-1 because the max negative int is one less
		sample := int8((math.MaxInt8 - 1) * math.Sin(2*math.Pi*frequency*float64(i)/sampleRate))
		switch channels {
		case 1:
			samples[i] = sample
		case 2:
			samples[2*i] = sample   // Left channel
			samples[2*i+1] = sample // Right channel
		}
	}

	if ad.playbackDevice == nil {return errors.New("Could not access playback device")}
	samplesPlayed, err := ad.playbackDevice.Write(samples)
	if err == alsa.ErrUnderrun {
		// triggers every second time i call the Write(samples) method
		return err
	}
	if err != nil {
		return err
	}
	return nil
}

If I run PlayBeep(100*time.Millisecond) it works the first time and fails the second time. The same is true if I generate longer sine wave beeps. What can I do to fix this problem or at least debug it further? Thanks a lot!

@ratzupaltuff
Copy link
Author

ratzupaltuff commented Aug 19, 2024

It fails in this section in the Write(buffer) method:

(Part of the library code)

ret := C.snd_pcm_writei(p.h, bufPtr, frames)
if ret == -C.EPIPE { // this returns true every second time calling the method
	C.snd_pcm_prepare(p.h) // this probably fixes the playback for the third call
	return 0, ErrUnderrun

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant