@@ -38,6 +38,7 @@ type alsaOutput struct {
3838 periodCount int
3939 periodSize int
4040 bufferSize int
41+ pcmFormat C.snd_pcm_format_t
4142
4243 externalVolume bool
4344
@@ -100,6 +101,15 @@ func (out *alsaOutput) alsaError(name string, err C.int) error {
100101 return fmt .Errorf ("ALSA error at %s: %s" , name , C .GoString (C .snd_strerror (err )))
101102}
102103
104+ func (out * alsaOutput ) isFormatSupported (hwparams * C.snd_pcm_hw_params_t , format C.snd_pcm_format_t ) bool {
105+ errCode := C .snd_pcm_hw_params_test_format (out .pcmHandle , hwparams , format )
106+ if errCode < 0 {
107+ return false
108+ }
109+
110+ return true
111+ }
112+
103113func (out * alsaOutput ) setupPcm () error {
104114 cdevice := C .CString (out .device )
105115 defer C .free (unsafe .Pointer (cdevice ))
@@ -119,8 +129,24 @@ func (out *alsaOutput) setupPcm() error {
119129 return out .alsaError ("snd_pcm_hw_params_set_access" , err )
120130 }
121131
122- if err := C .snd_pcm_hw_params_set_format (out .pcmHandle , hwparams , C .SND_PCM_FORMAT_FLOAT_LE ); err < 0 {
123- return out .alsaError ("snd_pcm_hw_params_set_format" , err )
132+ var formatFound bool
133+ for _ , format := range []C.snd_pcm_format_t {
134+ C .SND_PCM_FORMAT_FLOAT_LE , // 32-bit floating point, little-endian
135+ C .SND_PCM_FORMAT_S32_LE , // 32-bit signed integer, little-endian
136+ C .SND_PCM_FORMAT_S16_LE , // 16-bit signed integer, little-endian
137+ } {
138+ if ! out .isFormatSupported (hwparams , format ) {
139+ continue
140+ }
141+
142+ if err := C .snd_pcm_hw_params_set_format (out .pcmHandle , hwparams , format ); err < 0 {
143+ return out .alsaError ("snd_pcm_hw_params_set_format" , err )
144+ }
145+ formatFound = true
146+ }
147+
148+ if ! formatFound {
149+ return fmt .Errorf ("could not find a supported PCM format" )
124150 }
125151
126152 if err := C .snd_pcm_hw_params_set_channels (out .pcmHandle , hwparams , C .unsigned (out .channels )); err < 0 {
@@ -173,6 +199,13 @@ func (out *alsaOutput) setupPcm() error {
173199 out .bufferSize = int (frames )
174200 }
175201
202+ var pcmFormat C.snd_pcm_format_t
203+ if err := C .snd_pcm_hw_params_get_format (hwparams , & pcmFormat ); err < 0 {
204+ return out .alsaError ("snd_pcm_hw_params_get_format" , err )
205+ } else {
206+ out .pcmFormat = pcmFormat
207+ }
208+
176209 var swparams * C.snd_pcm_sw_params_t
177210 C .snd_pcm_sw_params_malloc (& swparams )
178211 defer C .free (unsafe .Pointer (swparams ))
@@ -202,6 +235,45 @@ func (out *alsaOutput) setupPcm() error {
202235 return nil
203236}
204237
238+ func pcmFormatName (format C.snd_pcm_format_t ) string {
239+ switch format {
240+ case C .SND_PCM_FORMAT_S8 :
241+ return "S8"
242+ case C .SND_PCM_FORMAT_U8 :
243+ return "U8"
244+ case C .SND_PCM_FORMAT_S16_LE :
245+ return "S16_LE"
246+ case C .SND_PCM_FORMAT_S16_BE :
247+ return "S16_BE"
248+ case C .SND_PCM_FORMAT_U16_LE :
249+ return "U16_LE"
250+ case C .SND_PCM_FORMAT_U16_BE :
251+ return "U16_BE"
252+ case C .SND_PCM_FORMAT_S24_LE :
253+ return "S24_LE"
254+ case C .SND_PCM_FORMAT_S24_BE :
255+ return "S24_BE"
256+ case C .SND_PCM_FORMAT_U24_LE :
257+ return "U24_LE"
258+ case C .SND_PCM_FORMAT_U24_BE :
259+ return "U24_BE"
260+ case C .SND_PCM_FORMAT_S32_LE :
261+ return "S32_LE"
262+ case C .SND_PCM_FORMAT_S32_BE :
263+ return "S32_BE"
264+ case C .SND_PCM_FORMAT_U32_LE :
265+ return "U32_LE"
266+ case C .SND_PCM_FORMAT_U32_BE :
267+ return "U32_BE"
268+ case C .SND_PCM_FORMAT_FLOAT_LE :
269+ return "FLOAT_LE"
270+ case C .SND_PCM_FORMAT_FLOAT_BE :
271+ return "FLOAT_BE"
272+ default :
273+ return "unknown"
274+ }
275+ }
276+
205277func (out * alsaOutput ) logParams (params * C.snd_pcm_hw_params_t ) error {
206278 var dir C.int
207279
@@ -235,12 +307,51 @@ func (out *alsaOutput) logParams(params *C.snd_pcm_hw_params_t) error {
235307 return out .alsaError ("snd_pcm_hw_params_get_periods" , err )
236308 }
237309
238- out .log .Debugf ("alsa driver configured, rate = %d bps, period time = %d us, period size = %d frames, buffer time = %d us, buffer size = %d frames, periods per buffer = %d frames" ,
239- rate , periodTime , frames , bufferTime , bufferSize , periods )
310+ var format C.snd_pcm_format_t
311+ if err := C .snd_pcm_hw_params_get_format (params , & format ); err < 0 {
312+ return out .alsaError ("snd_pcm_hw_params_get_format" , err )
313+ }
314+
315+ out .log .Debugf ("alsa driver configured, rate = %d bps, period time = %d us, period size = %d frames, buffer time = %d us, buffer size = %d frames, periods per buffer = %d frames, PCM format = %s" ,
316+ rate , periodTime , frames , bufferTime , bufferSize , periods , pcmFormatName (format ))
240317
241318 return nil
242319}
243320
321+ func floatLeToS16Le (floats []float32 ) []C.int16_t {
322+ shorts := make ([]C.int16_t , len (floats ))
323+ for i , f := range floats {
324+ // Clip the float to the range [-1.0, 1.0]
325+ if f < - 1.0 {
326+ f = - 1.0
327+ }
328+ if f > 1.0 {
329+ f = 1.0
330+ }
331+
332+ // Convert to int16 (short) in the range [-32768, 32767]
333+ shorts [i ] = C .int16_t (f * 32767 )
334+ }
335+ return shorts
336+ }
337+
338+ func floatLeToS32Le (floats []float32 ) []C.int32_t {
339+ ints := make ([]C.int32_t , len (floats ))
340+ for i , f := range floats {
341+ // Clip the float to the range [-1.0, 1.0]
342+ if f < - 1.0 {
343+ f = - 1.0
344+ }
345+ if f > 1.0 {
346+ f = 1.0
347+ }
348+
349+ // Convert to int32 in the range [-2147483648, 2147483647]
350+ ints [i ] = C .int32_t (f * 2147483647 )
351+ }
352+ return ints
353+ }
354+
244355func (out * alsaOutput ) outputLoop (pcmHandle * C.snd_pcm_t ) {
245356 floats := make ([]float32 , out .channels * out .periodSize )
246357
@@ -296,7 +407,24 @@ func (out *alsaOutput) outputLoop(pcmHandle *C.snd_pcm_t) {
296407 // be very fast. It might be delayed a bit however if the sleep above
297408 // didn't sleep long enough to wait for room in the buffer.
298409 if n > 0 {
299- if nn := C .snd_pcm_writei (pcmHandle , unsafe .Pointer (& floats [0 ]), C .snd_pcm_uframes_t (n / out .channels )); nn < 0 {
410+ var data unsafe.Pointer
411+ switch out .pcmFormat {
412+ case C .SND_PCM_FORMAT_FLOAT_LE :
413+ data = unsafe .Pointer (& floats [0 ])
414+ case C .SND_PCM_FORMAT_S32_LE :
415+ ints := floatLeToS32Le (floats )
416+ data = unsafe .Pointer (& ints [0 ])
417+ case C .SND_PCM_FORMAT_S16_LE :
418+ shorts := floatLeToS16Le (floats )
419+ data = unsafe .Pointer (& shorts [0 ])
420+ default :
421+ out .err <- fmt .Errorf ("unsupported PCM format: %s" , pcmFormatName (out .pcmFormat ))
422+ out .closed = true
423+ out .lock .Unlock ()
424+ return
425+ }
426+
427+ if nn := C .snd_pcm_writei (pcmHandle , data , C .snd_pcm_uframes_t (n / out .channels )); nn < 0 {
300428 // Got an error, so must recover (even for an underrun).
301429 errCode := C .snd_pcm_recover (pcmHandle , C .int (nn ), 1 )
302430 if errCode < 0 {
0 commit comments