Files

63 lines
1.8 KiB
Go

// Fenrir console screen reader.
// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package sound
/*
#cgo pkg-config: alsa
#include <alsa/asoundlib.h>
#include <stdlib.h>
static int fenrir_pcm_open(snd_pcm_t **pcm, const char *device) {
int result = snd_pcm_open(pcm, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (result < 0) return result;
result = snd_pcm_set_params(*pcm, SND_PCM_FORMAT_FLOAT,
SND_PCM_ACCESS_RW_INTERLEAVED, 2, 48000, 1, 20000);
if (result < 0) {
snd_pcm_close(*pcm);
*pcm = NULL;
}
return result;
}
*/
import "C"
import (
"fmt"
"syscall"
"unsafe"
)
type alsaPCM struct{ handle *C.snd_pcm_t }
func open_alsa(s *Service, device string) error {
name := C.CString(device)
defer C.free(unsafe.Pointer(name))
pcm := &alsaPCM{}
if result := C.fenrir_pcm_open(&pcm.handle, name); result < 0 {
return fmt.Errorf("open ALSA PCM %q: %s", device, C.GoString(C.snd_strerror(result)))
}
s.start_pcm(pcm)
return nil
}
// ALSA receives interleaved native-endian stereo float samples. All PCM calls
// after setup belong to the playback worker; Close runs after that worker exits.
func (p *alsaPCM) write(samples []float32) (int, error) {
result := C.snd_pcm_writei(p.handle, unsafe.Pointer(&samples[0]), C.snd_pcm_uframes_t(len(samples)/2))
if result < 0 {
return 0, syscall.Errno(-result)
}
return int(result) * 2, nil
}
func (p *alsaPCM) prepare() error { return pcm_error(C.snd_pcm_prepare(p.handle)) }
func (p *alsaPCM) drain() error { return pcm_error(C.snd_pcm_drain(p.handle)) }
func (p *alsaPCM) close() { C.snd_pcm_drop(p.handle); C.snd_pcm_close(p.handle) }
func pcm_error(result C.int) error {
if result < 0 {
return syscall.Errno(-result)
}
return nil
}