10
10
11
11
#include "alsa-pcm.h"
12
12
13
+ #include <stdbool.h>
13
14
#include <stdio.h>
14
15
#include <stdlib.h>
15
16
#include <string.h>
16
17
17
18
#include "shared/log.h"
18
19
19
- static int alsa_pcm_set_hw_params (snd_pcm_t * pcm , snd_pcm_format_t format , int channels ,
20
- int rate , unsigned int * buffer_time , unsigned int * period_time , char * * msg ) {
20
+ static int alsa_pcm_set_hw_params (snd_pcm_t * pcm , snd_pcm_format_t format_1 ,
21
+ snd_pcm_format_t format_2 , snd_pcm_format_t * selected_format ,
22
+ int channels , unsigned int * rate , bool exact_rate ,
23
+ unsigned int * buffer_time , unsigned int * period_time , char * * msg ) {
21
24
22
25
const snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED ;
26
+ snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN ;
23
27
snd_pcm_hw_params_t * params ;
24
28
char buf [256 ];
25
29
int dir ;
@@ -37,18 +41,36 @@ static int alsa_pcm_set_hw_params(snd_pcm_t *pcm, snd_pcm_format_t format, int c
37
41
goto fail ;
38
42
}
39
43
40
- if ((err = snd_pcm_hw_params_set_format (pcm , params , format )) != 0 ) {
41
- snprintf (buf , sizeof (buf ), "Set format: %s: %s" , snd_strerror (err ), snd_pcm_format_name (format ));
44
+ /* Prefer format_1 if supported by the device. */
45
+ if ((err = snd_pcm_hw_params_set_format (pcm , params , format_1 )) == 0 )
46
+ format = format_1 ;
47
+ /* Otherwise try format 2 */
48
+ else if (format_2 != SND_PCM_FORMAT_UNKNOWN ) {
49
+ if ((err = snd_pcm_hw_params_set_format (pcm , params , format_2 )) == 0 )
50
+ format = format_2 ;
51
+ else {
52
+ snprintf (buf , sizeof (buf ), "Set format: %s: %s and %s" , snd_strerror (err ), snd_pcm_format_name (format_1 ), snd_pcm_format_name (format_2 ));
53
+ goto fail ;
54
+ }
55
+ }
56
+ else {
57
+ snprintf (buf , sizeof (buf ), "Set format: %s: %s" , snd_strerror (err ), snd_pcm_format_name (format_1 ));
42
58
goto fail ;
43
59
}
60
+ * selected_format = format ;
44
61
45
62
if ((err = snd_pcm_hw_params_set_channels (pcm , params , channels )) != 0 ) {
46
63
snprintf (buf , sizeof (buf ), "Set channels: %s: %d" , snd_strerror (err ), channels );
47
64
goto fail ;
48
65
}
49
66
50
- if ((err = snd_pcm_hw_params_set_rate (pcm , params , rate , 0 )) != 0 ) {
51
- snprintf (buf , sizeof (buf ), "Set sample rate: %s: %d" , snd_strerror (err ), rate );
67
+ dir = 0 ;
68
+ if (exact_rate )
69
+ err = snd_pcm_hw_params_set_rate (pcm , params , * rate , dir );
70
+ else
71
+ err = snd_pcm_hw_params_set_rate_near (pcm , params , rate , & dir );
72
+ if (err != 0 ) {
73
+ snprintf (buf , sizeof (buf ), "Set sample rate: %s: %u" , snd_strerror (err ), * rate );
52
74
goto fail ;
53
75
}
54
76
@@ -72,43 +94,36 @@ static int alsa_pcm_set_hw_params(snd_pcm_t *pcm, snd_pcm_format_t format, int c
72
94
return 0 ;
73
95
74
96
fail :
97
+ * selected_format = format ;
75
98
if (msg != NULL )
76
99
* msg = strdup (buf );
77
100
return err ;
78
101
}
79
102
80
- static int alsa_pcm_set_sw_params (struct alsa_pcm * pcm , snd_pcm_uframes_t buffer_size ,
81
- snd_pcm_uframes_t period_size , char * * msg ) {
103
+ static int alsa_pcm_set_sw_params (snd_pcm_t * pcm ,
104
+ snd_pcm_uframes_t start_threshold , char * * msg ) {
82
105
83
- snd_pcm_t * snd_pcm = pcm -> handle ;
84
106
snd_pcm_sw_params_t * params ;
85
107
char buf [256 ];
86
108
int err ;
87
109
88
110
snd_pcm_sw_params_alloca (& params );
89
111
90
- if ((err = snd_pcm_sw_params_current (snd_pcm , params )) != 0 ) {
91
- snprintf (buf , sizeof (buf ), "Get current params: %s" , snd_strerror (err ));
112
+ if ((err = snd_pcm_sw_params_current (pcm , params )) != 0 ) {
113
+ snprintf (buf , sizeof (buf ), "Get current sw params: %s" , snd_strerror (err ));
92
114
goto fail ;
93
115
}
94
116
95
- /* Start the transfer when three periods have been written (or when the
96
- * buffer is full if it holds less than three periods. */
97
- snd_pcm_uframes_t threshold = period_size * 3 ;
98
- if (threshold > buffer_size )
99
- threshold = buffer_size ;
100
- if ((err = snd_pcm_sw_params_set_start_threshold (snd_pcm , params , threshold )) != 0 ) {
101
- snprintf (buf , sizeof (buf ), "Set start threshold: %s: %lu" , snd_strerror (err ), threshold );
117
+ if ((err = snd_pcm_sw_params_set_start_threshold (pcm , params , start_threshold )) != 0 ) {
118
+ snprintf (buf , sizeof (buf ), "Set start threshold: %s: %lu" , snd_strerror (err ), start_threshold );
102
119
goto fail ;
103
120
}
104
121
105
- if ((err = snd_pcm_sw_params (snd_pcm , params )) != 0 ) {
106
- snprintf (buf , sizeof (buf ), "%s" , snd_strerror (err ));
122
+ if ((err = snd_pcm_sw_params (pcm , params )) != 0 ) {
123
+ snprintf (buf , sizeof (buf ), "Set sw params: %s" , snd_strerror (err ));
107
124
goto fail ;
108
125
}
109
126
110
- pcm -> start_threshold = threshold ;
111
-
112
127
return 0 ;
113
128
114
129
fail :
@@ -123,18 +138,19 @@ void alsa_pcm_init(struct alsa_pcm *pcm) {
123
138
}
124
139
125
140
int alsa_pcm_open (
126
- struct alsa_pcm * pcm ,
127
- const char * name ,
128
- snd_pcm_format_t format ,
129
- int channels ,
130
- unsigned int rate ,
131
- unsigned int buffer_time ,
132
- unsigned int period_time ,
133
- int flags ,
134
- char * * msg ) {
141
+ struct alsa_pcm * pcm , const char * name ,
142
+ snd_pcm_format_t format_1 , snd_pcm_format_t format_2 ,
143
+ int channels , unsigned int rate ,
144
+ unsigned int buffer_time , unsigned int period_time ,
145
+ int flags , char * * msg ) {
135
146
136
147
char * tmp = NULL ;
137
148
char buf [256 ];
149
+ unsigned int actual_buffer_time = buffer_time ;
150
+ unsigned int actual_period_time = period_time ;
151
+ snd_pcm_format_t actual_format = SND_PCM_FORMAT_UNKNOWN ;
152
+ unsigned int actual_rate = rate ;
153
+ const bool exact_rate = !(flags & SND_PCM_NO_AUTO_RESAMPLE );
138
154
int err ;
139
155
140
156
assert (pcm -> handle == NULL );
@@ -144,21 +160,28 @@ int alsa_pcm_open(
144
160
goto fail ;
145
161
}
146
162
147
- unsigned int actual_buffer_time = buffer_time ;
148
- unsigned int actual_period_time = period_time ;
149
- if (( err = alsa_pcm_set_hw_params ( pcm -> handle , format , channels , rate ,
163
+ pcm -> format = actual_format ;
164
+ if (( err = alsa_pcm_set_hw_params ( pcm -> handle , format_1 , format_2 ,
165
+ & actual_format , channels , & actual_rate , exact_rate ,
150
166
& actual_buffer_time , & actual_period_time , & tmp )) != 0 ) {
151
167
snprintf (buf , sizeof (buf ), "Set HW params: %s" , tmp );
152
168
goto fail ;
153
169
}
170
+ pcm -> format = actual_format ;
154
171
155
172
snd_pcm_uframes_t buffer_size , period_size ;
156
173
if ((err = snd_pcm_get_params (pcm -> handle , & buffer_size , & period_size )) != 0 ) {
157
174
snprintf (buf , sizeof (buf ), "Get params: %s" , snd_strerror (err ));
158
175
goto fail ;
159
176
}
160
177
161
- if ((err = alsa_pcm_set_sw_params (pcm , buffer_size , period_size , & tmp )) != 0 ) {
178
+ /* Start the transfer when three requested periods have been written (or
179
+ * when the buffer is full if it holds less than three requested periods. */
180
+ snd_pcm_uframes_t start_threshold = (period_time * 3 / 1000 ) * (rate / 1000 );
181
+ if (start_threshold > buffer_size )
182
+ start_threshold = buffer_size ;
183
+
184
+ if ((err = alsa_pcm_set_sw_params (pcm -> handle , start_threshold , & tmp )) != 0 ) {
162
185
snprintf (buf , sizeof (buf ), "Set SW params: %s" , tmp );
163
186
goto fail ;
164
187
}
@@ -168,15 +191,15 @@ int alsa_pcm_open(
168
191
goto fail ;
169
192
}
170
193
171
- pcm -> format = format ;
172
194
pcm -> channels = channels ;
173
- pcm -> sample_size = snd_pcm_format_size (format , 1 );
174
- pcm -> frame_size = snd_pcm_format_size (format , channels );
175
- pcm -> rate = rate ;
195
+ pcm -> sample_size = snd_pcm_format_size (actual_format , 1 );
196
+ pcm -> frame_size = snd_pcm_format_size (actual_format , channels );
197
+ pcm -> rate = actual_rate ;
176
198
pcm -> buffer_time = actual_buffer_time ;
177
199
pcm -> period_time = actual_period_time ;
178
200
pcm -> buffer_frames = buffer_size ;
179
201
pcm -> period_frames = period_size ;
202
+ pcm -> start_threshold = start_threshold ;
180
203
pcm -> delay = 0 ;
181
204
182
205
/* Maintain buffer fill level above 1 period plus 2ms to allow for
@@ -205,7 +228,7 @@ int alsa_pcm_write(struct alsa_pcm *pcm, ffb_t *buffer, bool drain, bool verbose
205
228
pcm -> underrun = false;
206
229
if ((ret = snd_pcm_avail_delay (pcm -> handle , & avail , & delay )) < 0 ) {
207
230
if (ret == - EPIPE ) {
208
- debug ("ALSA playback PCM underrun" );
231
+ warn ("ALSA playback PCM underrun" );
209
232
pcm -> underrun = true;
210
233
snd_pcm_prepare (pcm -> handle );
211
234
avail = pcm -> buffer_frames ;
@@ -221,7 +244,7 @@ int alsa_pcm_write(struct alsa_pcm *pcm, ffb_t *buffer, bool drain, bool verbose
221
244
snd_pcm_sframes_t written_frames = 0 ;
222
245
223
246
/* If not draining, write only as many frames as possible without blocking.
224
- * If necessary insert silemce frames to prevent underrun. */
247
+ * If necessary insert silence frames to prevent underrun. */
225
248
if (!drain ) {
226
249
if (frames > avail )
227
250
frames = avail ;
@@ -234,7 +257,10 @@ int alsa_pcm_write(struct alsa_pcm *pcm, ffb_t *buffer, bool drain, bool verbose
234
257
info ("Underrun imminent: inserting %zu silence frames" , padding / pcm -> channels );
235
258
snd_pcm_format_set_silence (pcm -> format , buffer -> tail , padding );
236
259
ffb_seek (buffer , padding );
237
- frames += padding ;
260
+ frames = ffb_len_out (buffer ) / pcm -> channels ;
261
+ /* Flag an underrun to indicate that we have caused a discontinuity
262
+ * in the input stream. */
263
+ pcm -> underrun = true;
238
264
}
239
265
}
240
266
@@ -245,7 +271,7 @@ int alsa_pcm_write(struct alsa_pcm *pcm, ffb_t *buffer, bool drain, bool verbose
245
271
case EINTR :
246
272
continue ;
247
273
case EPIPE :
248
- debug ("ALSA playback PCM underrun" );
274
+ warn ("ALSA playback PCM underrun" );
249
275
pcm -> underrun = true;
250
276
snd_pcm_prepare (pcm -> handle );
251
277
continue ;
0 commit comments