|
25 | 25 | #include "util.h"
|
26 | 26 | #include "sampleconv.h"
|
27 | 27 |
|
| 28 | +#define DUMP_PCM_INFO 0 |
| 29 | + |
28 | 30 | struct alsa_enc_info {
|
29 | 31 | const char *name;
|
30 | 32 | snd_pcm_format_t fmt;
|
@@ -132,8 +134,10 @@ ssize_t alsa_delay(struct codec *c)
|
132 | 134 | void alsa_drop(struct codec *c)
|
133 | 135 | {
|
134 | 136 | struct alsa_state *state = (struct alsa_state *) c->data;
|
135 |
| - snd_pcm_drop(state->dev); |
136 |
| - state->delay = 0; |
| 137 | + if (c->write) { |
| 138 | + snd_pcm_drop(state->dev); |
| 139 | + state->delay = 0; |
| 140 | + } |
137 | 141 | }
|
138 | 142 |
|
139 | 143 | void alsa_pause(struct codec *c, int p)
|
@@ -175,96 +179,131 @@ static struct alsa_enc_info * alsa_get_enc_info(const char *enc)
|
175 | 179 | return NULL;
|
176 | 180 | }
|
177 | 181 |
|
178 |
| -struct codec * alsa_codec_init(const char *path, const char *type, const char *enc, int fs, int channels, int endian, int mode) |
| 182 | +struct codec * alsa_codec_init(const struct codec_params *p) |
179 | 183 | {
|
180 | 184 | int err;
|
181 | 185 | snd_pcm_t *dev = NULL;
|
182 |
| - snd_pcm_hw_params_t *p = NULL; |
| 186 | + snd_pcm_hw_params_t *hw_p = NULL; |
| 187 | + snd_pcm_sw_params_t *sw_p = NULL; |
183 | 188 | struct codec *c = NULL;
|
184 | 189 | struct alsa_state *state = NULL;
|
185 |
| - snd_pcm_uframes_t buf_frames; |
| 190 | + snd_pcm_uframes_t buf_frames_min, buf_frames_max, buf_frames; |
186 | 191 | struct alsa_enc_info *enc_info;
|
187 | 192 |
|
188 |
| - if ((err = snd_pcm_open(&dev, path, (mode == CODEC_MODE_WRITE) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, 0)) < 0) { |
| 193 | + if ((err = snd_pcm_open(&dev, p->path, (p->mode == CODEC_MODE_WRITE) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, 0)) < 0) { |
189 | 194 | LOG_FMT(LL_OPEN_ERROR, "%s: error: failed to open device: %s", codec_name, snd_strerror(err));
|
190 | 195 | goto fail;
|
191 | 196 | }
|
192 |
| - if ((enc_info = alsa_get_enc_info(enc)) == NULL) { |
193 |
| - LOG_FMT(LL_ERROR, "%s: error: bad encoding: %s", codec_name, enc); |
| 197 | + if ((enc_info = alsa_get_enc_info(p->enc)) == NULL) { |
| 198 | + LOG_FMT(LL_ERROR, "%s: error: bad encoding: %s", codec_name, p->enc); |
194 | 199 | goto fail;
|
195 | 200 | }
|
196 |
| - if ((err = snd_pcm_hw_params_malloc(&p)) < 0) { |
| 201 | + |
| 202 | + if ((err = snd_pcm_hw_params_malloc(&hw_p)) < 0) { |
197 | 203 | LOG_FMT(LL_ERROR, "%s: error: failed to allocate hw params: %s", codec_name, snd_strerror(err));
|
198 | 204 | goto fail;
|
199 | 205 | }
|
200 |
| - if ((err = snd_pcm_hw_params_any(dev, p)) < 0) { |
| 206 | + if ((err = snd_pcm_hw_params_any(dev, hw_p)) < 0) { |
201 | 207 | LOG_FMT(LL_ERROR, "%s: error: failed to initialize hw params: %s", codec_name, snd_strerror(err));
|
202 | 208 | goto fail;
|
203 | 209 | }
|
204 |
| - if ((err = snd_pcm_hw_params_set_access(dev, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { |
| 210 | + if ((err = snd_pcm_hw_params_set_access(dev, hw_p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { |
205 | 211 | LOG_FMT(LL_ERROR, "%s: error: failed to set access: %s", codec_name, snd_strerror(err));
|
206 | 212 | goto fail;
|
207 | 213 | }
|
208 |
| - if ((err = snd_pcm_hw_params_set_format(dev, p, enc_info->fmt)) < 0) { |
| 214 | + if ((err = snd_pcm_hw_params_set_format(dev, hw_p, enc_info->fmt)) < 0) { |
209 | 215 | LOG_FMT(LL_ERROR, "%s: error: failed to set format: %s", codec_name, snd_strerror(err));
|
210 | 216 | goto fail;
|
211 | 217 | }
|
212 |
| - if ((err = snd_pcm_hw_params_set_rate(dev, p, (unsigned int) fs, 0)) < 0) { |
| 218 | + if ((err = snd_pcm_hw_params_set_rate(dev, hw_p, (unsigned int) p->fs, 0)) < 0) { |
213 | 219 | LOG_FMT(LL_ERROR, "%s: error: failed to set rate: %s", codec_name, snd_strerror(err));
|
214 | 220 | goto fail;
|
215 | 221 | }
|
216 |
| - if ((err = snd_pcm_hw_params_set_channels(dev, p, channels)) < 0) { |
| 222 | + if ((err = snd_pcm_hw_params_set_channels(dev, hw_p, p->channels)) < 0) { |
217 | 223 | LOG_FMT(LL_ERROR, "%s: error: failed to set channels: %s", codec_name, snd_strerror(err));
|
218 | 224 | goto fail;
|
219 | 225 | }
|
220 |
| - buf_frames = dsp_globals.buf_frames; |
221 |
| - if ((err = snd_pcm_hw_params_set_buffer_size_min(dev, p, &buf_frames)) < 0) { |
222 |
| - LOG_FMT(LL_ERROR, "%s: error: failed to set buffer size minimum: %s", codec_name, snd_strerror(err)); |
| 226 | + if ((err = snd_pcm_hw_params_get_buffer_size_min(hw_p, &buf_frames_min)) < 0) { |
| 227 | + LOG_FMT(LL_ERROR, "%s: error: failed to get minimum buffer size: %s", codec_name, snd_strerror(err)); |
| 228 | + goto fail; |
| 229 | + } |
| 230 | + if ((err = snd_pcm_hw_params_get_buffer_size_max(hw_p, &buf_frames_max)) < 0) { |
| 231 | + LOG_FMT(LL_ERROR, "%s: error: failed to get maximum buffer size: %s", codec_name, snd_strerror(err)); |
| 232 | + goto fail; |
| 233 | + } |
| 234 | + buf_frames = MINIMUM(MAXIMUM(p->block_frames, buf_frames_min), buf_frames_max); |
| 235 | + if ((err = snd_pcm_hw_params_set_buffer_size_min(dev, hw_p, &buf_frames)) < 0) { |
| 236 | + LOG_FMT(LL_ERROR, "%s: error: failed to set minimum buffer size: %s", codec_name, snd_strerror(err)); |
223 | 237 | goto fail;
|
224 | 238 | }
|
225 |
| - buf_frames = dsp_globals.buf_frames * dsp_globals.max_buf_ratio; |
226 |
| - if ((err = snd_pcm_hw_params_set_buffer_size_max(dev, p, &buf_frames)) < 0) { |
227 |
| - LOG_FMT(LL_ERROR, "%s: error: failed to set buffer size maximum: %s", codec_name, snd_strerror(err)); |
| 239 | + buf_frames = MINIMUM(MAXIMUM(p->block_frames * p->buf_ratio, buf_frames), buf_frames_max); |
| 240 | + if ((err = snd_pcm_hw_params_set_buffer_size_near(dev, hw_p, &buf_frames)) < 0) { |
| 241 | + LOG_FMT(LL_ERROR, "%s: error: failed to set buffer size: %s", codec_name, snd_strerror(err)); |
228 | 242 | goto fail;
|
229 | 243 | }
|
230 |
| - if ((err = snd_pcm_hw_params(dev, p)) < 0) { |
231 |
| - LOG_FMT(LL_ERROR, "%s: error: failed to set params: %s", codec_name, snd_strerror(err)); |
| 244 | + LOG_FMT(LL_VERBOSE, "%s: info: buffer size: %lu frames [%lu %lu]", codec_name, buf_frames, buf_frames_min, buf_frames_max); |
| 245 | + if ((err = snd_pcm_hw_params(dev, hw_p)) < 0) { |
| 246 | + LOG_FMT(LL_ERROR, "%s: error: failed to set hw params: %s", codec_name, snd_strerror(err)); |
232 | 247 | goto fail;
|
233 | 248 | }
|
234 | 249 |
|
| 250 | + if (p->mode == CODEC_MODE_WRITE) { |
| 251 | + if ((err = snd_pcm_sw_params_malloc(&sw_p)) < 0) { |
| 252 | + LOG_FMT(LL_ERROR, "%s: error: failed to allocate sw params: %s", codec_name, snd_strerror(err)); |
| 253 | + goto fail; |
| 254 | + } |
| 255 | + if ((err = snd_pcm_sw_params_current(dev, sw_p)) < 0) { |
| 256 | + LOG_FMT(LL_ERROR, "%s: error: failed to get current sw params: %s", codec_name, snd_strerror(err)); |
| 257 | + goto fail; |
| 258 | + } |
| 259 | + if ((err = snd_pcm_sw_params_set_start_threshold(dev, sw_p, MINIMUM(p->block_frames * 2, buf_frames))) < 0) { |
| 260 | + LOG_FMT(LL_ERROR, "%s: error: failed to set start threshold: %s", codec_name, snd_strerror(err)); |
| 261 | + goto fail; |
| 262 | + } |
| 263 | + if ((err = snd_pcm_sw_params(dev, sw_p)) < 0) { |
| 264 | + LOG_FMT(LL_ERROR, "%s: error: failed to set sw params: %s", codec_name, snd_strerror(err)); |
| 265 | + goto fail; |
| 266 | + } |
| 267 | + } |
| 268 | + |
| 269 | + #if DUMP_PCM_INFO |
| 270 | + snd_output_t *output = NULL; |
| 271 | + if (snd_output_stdio_attach(&output, stderr, 0) == 0) |
| 272 | + snd_pcm_dump(dev, output); |
| 273 | + #endif |
| 274 | + |
235 | 275 | state = calloc(1, sizeof(struct alsa_state));
|
236 | 276 | state->dev = dev;
|
237 | 277 | state->enc_info = enc_info;
|
238 | 278 | state->delay = 0;
|
239 | 279 |
|
240 | 280 | c = calloc(1, sizeof(struct codec));
|
241 |
| - c->path = path; |
242 |
| - c->type = type; |
| 281 | + c->path = p->path; |
| 282 | + c->type = p->type; |
243 | 283 | c->enc = enc_info->name;
|
244 |
| - c->fs = fs; |
245 |
| - c->channels = channels; |
| 284 | + c->fs = p->fs; |
| 285 | + c->channels = p->channels; |
246 | 286 | c->prec = enc_info->prec;
|
247 |
| - c->can_dither = enc_info->can_dither; |
248 |
| - c->interactive = (mode == CODEC_MODE_WRITE) ? 1 : 0; |
| 287 | + if (enc_info->can_dither) c->hints |= CODEC_HINT_CAN_DITHER; |
| 288 | + if (p->mode == CODEC_MODE_WRITE) c->hints |= CODEC_HINT_INTERACTIVE; |
249 | 289 | c->frames = -1;
|
250 |
| - c->read = alsa_read; |
251 |
| - c->write = alsa_write; |
| 290 | + if (p->mode == CODEC_MODE_READ) c->read = alsa_read; |
| 291 | + else c->write = alsa_write; |
252 | 292 | c->seek = alsa_seek;
|
253 | 293 | c->delay = alsa_delay;
|
254 | 294 | c->drop = alsa_drop;
|
255 | 295 | c->pause = alsa_pause;
|
256 | 296 | c->destroy = alsa_destroy;
|
257 | 297 | c->data = state;
|
258 | 298 |
|
259 |
| - snd_pcm_hw_params_free(p); |
| 299 | + snd_pcm_hw_params_free(hw_p); |
260 | 300 |
|
261 | 301 | return c;
|
262 | 302 |
|
263 | 303 | fail:
|
264 |
| - if (p != NULL) |
265 |
| - snd_pcm_hw_params_free(p); |
266 |
| - if (dev != NULL) |
267 |
| - snd_pcm_close(dev); |
| 304 | + if (hw_p) snd_pcm_hw_params_free(hw_p); |
| 305 | + if (sw_p) snd_pcm_sw_params_free(sw_p); |
| 306 | + if (dev) snd_pcm_close(dev); |
268 | 307 | return NULL;
|
269 | 308 | }
|
270 | 309 |
|
|
0 commit comments