2026-10-24 10:08:44 +02:00
|
|
|
package audio
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
2026-10-24 11:15:35 +02:00
|
|
|
// Check verifies that the sink name is ready without producing sound: null
|
|
|
|
|
// is always ready; oto initializes the audio context (default format 44.1
|
|
|
|
|
// kHz stereo 16 bit) and waits for it to become ready, so "no audio device"
|
|
|
|
|
// (servers, containers, missing PulseAudio/ALSA library) shows up in
|
|
|
|
|
// `talk doctor` rather than at the first announcement.
|
2026-10-24 10:08:44 +02:00
|
|
|
func Check(ctx context.Context, name string) error {
|
|
|
|
|
switch name {
|
|
|
|
|
case NameNull:
|
|
|
|
|
return nil
|
|
|
|
|
case NameOto:
|
|
|
|
|
c, err := contextFor(pcmFormat{SampleRate: 44100, Channels: 2, BitsPerCoded: 16})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
select {
|
|
|
|
|
case <-c.ready:
|
|
|
|
|
if err := c.ctx.Err(); err != nil {
|
2026-10-24 11:15:35 +02:00
|
|
|
return fmt.Errorf("oto: initialization failed: %w", err)
|
2026-10-24 10:08:44 +02:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
case <-ctx.Done():
|
2026-10-24 11:15:35 +02:00
|
|
|
return fmt.Errorf("oto: audio device not responding (%v)", ctx.Err())
|
2026-10-24 10:08:44 +02:00
|
|
|
case <-time.After(5 * time.Second):
|
2026-10-24 11:15:35 +02:00
|
|
|
return fmt.Errorf("oto: audio device not responding within 5 s")
|
2026-10-24 10:08:44 +02:00
|
|
|
}
|
|
|
|
|
default:
|
2026-10-24 11:15:35 +02:00
|
|
|
return fmt.Errorf("unknown audio sink %q (allowed: %q, %q)", name, NameOto, NameNull)
|
2026-10-24 10:08:44 +02:00
|
|
|
}
|
|
|
|
|
}
|