2 * PCM - Rate conversion
3 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "pcm_local.h"
25 #include "pcm_plugin.h"
33 } snd_pcm_rate_state_t;
35 typedef snd_pcm_uframes_t (*rate_f)(const snd_pcm_channel_area_t *dst_areas,
36 snd_pcm_uframes_t dst_offset,
37 snd_pcm_uframes_t *dst_framesp,
38 const snd_pcm_channel_area_t *src_areas,
39 snd_pcm_uframes_t src_offset,
40 snd_pcm_uframes_t src_frames,
41 unsigned int channels,
42 int getidx, int putidx,
44 snd_pcm_rate_state_t *states);
47 /* This field need to be the first */
48 snd_pcm_plugin_t plug;
53 snd_pcm_format_t sformat;
55 snd_pcm_rate_state_t *states;
58 snd_pcm_uframes_t snd_pcm_rate_expand(const snd_pcm_channel_area_t *dst_areas,
59 snd_pcm_uframes_t dst_offset, snd_pcm_uframes_t *dst_framesp,
60 const snd_pcm_channel_area_t *src_areas,
61 snd_pcm_uframes_t src_offset, snd_pcm_uframes_t src_frames,
62 unsigned int channels,
63 int getidx, int putidx,
64 unsigned int get_threshold,
65 snd_pcm_rate_state_t *states)
69 #include "plugin_ops.h"
72 void *get = get16_labels[getidx];
73 void *put = put16_labels[putidx];
75 snd_pcm_uframes_t src_frames1 = 0;
76 snd_pcm_uframes_t dst_frames1 = 0;
77 snd_pcm_uframes_t dst_frames = *dst_framesp;
80 if (src_frames == 0 ||
83 for (channel = 0; channel < channels; ++channel) {
84 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
85 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
88 int src_step, dst_step;
89 int16_t old_sample = states->sample;
90 unsigned int pos = states->pos;
91 src = snd_pcm_channel_area_addr(src_area, src_offset);
92 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
93 src_step = snd_pcm_channel_area_step(src_area);
94 dst_step = snd_pcm_channel_area_step(dst_area);
97 while (dst_frames1 < dst_frames) {
98 if (pos >= get_threshold) {
100 if (src_frames1 == src_frames)
102 pos -= get_threshold;
104 #define GET16_END after_get
105 #include "plugin_ops.h"
111 sample = (old_sample * (DIV - pos) + new_sample * pos) / DIV;
112 old_sample = new_sample;
116 #define PUT16_END after_put
117 #include "plugin_ops.h"
124 states->sample = old_sample;
128 *dst_framesp = dst_frames1;
132 snd_pcm_uframes_t snd_pcm_rate_shrink(const snd_pcm_channel_area_t *dst_areas,
133 snd_pcm_uframes_t dst_offset, snd_pcm_uframes_t *dst_framesp,
134 const snd_pcm_channel_area_t *src_areas,
135 snd_pcm_uframes_t src_offset, snd_pcm_uframes_t src_frames,
136 unsigned int channels,
137 int getidx, int putidx,
138 unsigned int get_increment,
139 snd_pcm_rate_state_t *states)
143 #include "plugin_ops.h"
146 void *get = get16_labels[getidx];
147 void *put = put16_labels[putidx];
148 unsigned int channel;
149 snd_pcm_uframes_t src_frames1 = 0;
150 snd_pcm_uframes_t dst_frames1 = 0;
151 snd_pcm_uframes_t dst_frames = *dst_framesp;
154 if (src_frames == 0 ||
157 for (channel = 0; channel < channels; ++channel) {
158 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
159 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
164 int src_step, dst_step;
167 src = snd_pcm_channel_area_addr(src_area, src_offset);
168 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
169 src_step = snd_pcm_channel_area_step(src_area);
170 dst_step = snd_pcm_channel_area_step(dst_area);
173 while (src_frames1 < src_frames) {
176 #define GET16_END after_get
177 #include "plugin_ops.h"
182 pos += get_increment;
186 sum += s * (get_increment - pos);
190 #define PUT16_END after_put
191 #include "plugin_ops.h"
197 if (dst_frames1 == dst_frames)
200 sum += sample * get_increment;
206 *dst_framesp = dst_frames1;
210 static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
213 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_PLUGIN };
214 snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
215 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
219 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
223 err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
226 err = _snd_pcm_hw_param_set_min(params,
227 SND_PCM_HW_PARAM_RATE, RATE_MIN, 0);
230 err = _snd_pcm_hw_param_set_max(params,
231 SND_PCM_HW_PARAM_RATE, RATE_MAX, 0);
234 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
238 static int snd_pcm_rate_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
240 snd_pcm_rate_t *rate = pcm->private_data;
241 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
242 _snd_pcm_hw_params_any(sparams);
243 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
245 if (rate->sformat != SND_PCM_FORMAT_UNKNOWN) {
246 _snd_pcm_hw_params_set_format(sparams, rate->sformat);
247 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
249 _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
250 rate->srate, 0, rate->srate + 1, -1);
254 static int snd_pcm_rate_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
255 snd_pcm_hw_params_t *sparams)
257 snd_pcm_rate_t *rate = pcm->private_data;
258 snd_interval_t t, buffer_size;
259 const snd_interval_t *srate, *crate;
261 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
262 SND_PCM_HW_PARBIT_PERIOD_TIME |
263 SND_PCM_HW_PARBIT_TICK_TIME);
264 if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
265 links |= (SND_PCM_HW_PARBIT_FORMAT |
266 SND_PCM_HW_PARBIT_SUBFORMAT |
267 SND_PCM_HW_PARBIT_SAMPLE_BITS |
268 SND_PCM_HW_PARBIT_FRAME_BITS);
269 snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
270 snd_interval_unfloor(&buffer_size);
271 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
272 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
273 snd_interval_muldiv(&buffer_size, srate, crate, &t);
274 err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
277 err = _snd_pcm_hw_params_refine(sparams, links, params);
283 static int snd_pcm_rate_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
284 snd_pcm_hw_params_t *sparams)
286 snd_pcm_rate_t *rate = pcm->private_data;
288 const snd_interval_t *sbuffer_size;
289 const snd_interval_t *srate, *crate;
291 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
292 SND_PCM_HW_PARBIT_PERIOD_TIME |
293 SND_PCM_HW_PARBIT_TICK_TIME);
294 if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
295 links |= (SND_PCM_HW_PARBIT_FORMAT |
296 SND_PCM_HW_PARBIT_SUBFORMAT |
297 SND_PCM_HW_PARBIT_SAMPLE_BITS |
298 SND_PCM_HW_PARBIT_FRAME_BITS);
299 sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
300 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
301 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
302 snd_interval_muldiv(sbuffer_size, crate, srate, &t);
303 snd_interval_floor(&t);
304 err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
307 err = _snd_pcm_hw_params_refine(params, links, sparams);
313 static int snd_pcm_rate_hw_refine(snd_pcm_t *pcm,
314 snd_pcm_hw_params_t *params)
316 return snd_pcm_hw_refine_slave(pcm, params,
317 snd_pcm_rate_hw_refine_cprepare,
318 snd_pcm_rate_hw_refine_cchange,
319 snd_pcm_rate_hw_refine_sprepare,
320 snd_pcm_rate_hw_refine_schange,
321 snd_pcm_plugin_hw_refine_slave);
324 static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
326 snd_pcm_rate_t *rate = pcm->private_data;
327 snd_pcm_t *slave = rate->plug.slave;
328 snd_pcm_format_t src_format, dst_format;
329 unsigned int src_rate, dst_rate;
330 int err = snd_pcm_hw_params_slave(pcm, params,
331 snd_pcm_rate_hw_refine_cchange,
332 snd_pcm_rate_hw_refine_sprepare,
333 snd_pcm_rate_hw_refine_schange,
334 snd_pcm_plugin_hw_params_slave);
338 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
339 src_format = snd_pcm_hw_params_get_format(params);
340 dst_format = slave->format;
341 src_rate = snd_pcm_hw_params_get_rate(params, 0);
342 dst_rate = slave->rate;
344 src_format = slave->format;
345 dst_format = snd_pcm_hw_params_get_format(params);
346 src_rate = slave->rate;
347 dst_rate = snd_pcm_hw_params_get_rate(params, 0);
349 rate->get_idx = snd_pcm_linear_get_index(src_format, SND_PCM_FORMAT_S16);
350 rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, dst_format);
351 if (src_rate < dst_rate) {
352 rate->func = snd_pcm_rate_expand;
353 /* pitch is get_threshold */
355 rate->func = snd_pcm_rate_shrink;
356 /* pitch is get_increment */
358 rate->pitch = (((u_int64_t)dst_rate * DIV) + src_rate / 2) / src_rate;
359 assert(!rate->states);
360 rate->states = malloc(snd_pcm_hw_param_get(params, SND_PCM_HW_PARAM_CHANNELS, 0) * sizeof(*rate->states));
364 static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
366 snd_pcm_rate_t *rate = pcm->private_data;
371 return snd_pcm_hw_free(rate->plug.slave);
374 static int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
376 snd_pcm_rate_t *rate = pcm->private_data;
377 snd_pcm_t *slave = rate->plug.slave;
378 snd_pcm_sw_params_t sparams;
380 sparams.avail_min = muldiv_near(sparams.avail_min, slave->rate, pcm->rate);
381 sparams.xfer_align = muldiv_near(sparams.xfer_align, slave->rate, pcm->rate);
382 sparams.silence_threshold = muldiv_near(sparams.silence_threshold, slave->rate, pcm->rate);
383 sparams.silence_size = muldiv_near(sparams.silence_size, slave->rate, pcm->rate);
384 return snd_pcm_sw_params(slave, &sparams);
387 static int snd_pcm_rate_init(snd_pcm_t *pcm)
389 snd_pcm_rate_t *rate = pcm->private_data;
391 for (k = 0; k < pcm->channels; ++k) {
392 rate->states[k].sum = 0;
393 rate->states[k].sample = 0;
394 if (rate->func == snd_pcm_rate_expand) {
395 /* Get a sample on entry */
396 rate->states[k].pos = rate->pitch + DIV;
398 rate->states[k].pos = 0;
404 static snd_pcm_uframes_t
405 snd_pcm_rate_write_areas(snd_pcm_t *pcm,
406 const snd_pcm_channel_area_t *areas,
407 snd_pcm_uframes_t offset,
408 snd_pcm_uframes_t size,
409 const snd_pcm_channel_area_t *slave_areas,
410 snd_pcm_uframes_t slave_offset,
411 snd_pcm_uframes_t *slave_sizep)
413 snd_pcm_rate_t *rate = pcm->private_data;
414 return rate->func(slave_areas, slave_offset, slave_sizep,
417 rate->get_idx, rate->put_idx,
418 rate->pitch, rate->states);
421 static snd_pcm_uframes_t
422 snd_pcm_rate_read_areas(snd_pcm_t *pcm,
423 const snd_pcm_channel_area_t *areas,
424 snd_pcm_uframes_t offset,
425 snd_pcm_uframes_t size,
426 const snd_pcm_channel_area_t *slave_areas,
427 snd_pcm_uframes_t slave_offset,
428 snd_pcm_uframes_t *slave_sizep)
430 snd_pcm_rate_t *rate = pcm->private_data;
431 *slave_sizep = rate->func(areas, offset, &size,
432 slave_areas, slave_offset, *slave_sizep,
434 rate->get_idx, rate->put_idx,
435 rate->pitch, rate->states);
439 snd_pcm_sframes_t snd_pcm_rate_client_frames(snd_pcm_t *pcm, snd_pcm_sframes_t frames)
441 snd_pcm_rate_t *rate = pcm->private_data;
442 /* Round toward zero */
443 if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
444 return muldiv_down(frames, DIV, rate->pitch);
446 return muldiv_down(frames, rate->pitch, DIV);
449 snd_pcm_sframes_t snd_pcm_rate_slave_frames(snd_pcm_t *pcm, snd_pcm_sframes_t frames)
451 snd_pcm_rate_t *rate = pcm->private_data;
452 /* Round toward zero */
453 if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
454 return muldiv_down(frames, rate->pitch, DIV);
456 return muldiv_down(frames, DIV, rate->pitch);
459 static void snd_pcm_rate_dump(snd_pcm_t *pcm, snd_output_t *out)
461 snd_pcm_rate_t *rate = pcm->private_data;
462 if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
463 snd_output_printf(out, "Rate conversion PCM (%d)\n",
466 snd_output_printf(out, "Rate conversion PCM (%d, sformat=%s)\n",
468 snd_pcm_format_name(rate->sformat));
470 snd_output_printf(out, "Its setup is:\n");
471 snd_pcm_dump_setup(pcm, out);
473 snd_output_printf(out, "Slave: ");
474 snd_pcm_dump(rate->plug.slave, out);
477 snd_pcm_ops_t snd_pcm_rate_ops = {
478 close: snd_pcm_plugin_close,
479 info: snd_pcm_plugin_info,
480 hw_refine: snd_pcm_rate_hw_refine,
481 hw_params: snd_pcm_rate_hw_params,
482 hw_free: snd_pcm_rate_hw_free,
483 sw_params: snd_pcm_rate_sw_params,
484 channel_info: snd_pcm_plugin_channel_info,
485 dump: snd_pcm_rate_dump,
486 nonblock: snd_pcm_plugin_nonblock,
487 async: snd_pcm_plugin_async,
488 mmap: snd_pcm_plugin_mmap,
489 munmap: snd_pcm_plugin_munmap,
492 int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, int srate, snd_pcm_t *slave, int close_slave)
495 snd_pcm_rate_t *rate;
496 assert(pcmp && slave);
497 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
498 snd_pcm_format_linear(sformat) != 1)
500 rate = calloc(1, sizeof(snd_pcm_rate_t));
505 rate->sformat = sformat;
506 rate->plug.read = snd_pcm_rate_read_areas;
507 rate->plug.write = snd_pcm_rate_write_areas;
508 rate->plug.client_frames = snd_pcm_rate_client_frames;
509 rate->plug.slave_frames = snd_pcm_rate_slave_frames;
510 rate->plug.init = snd_pcm_rate_init;
511 rate->plug.slave = slave;
512 rate->plug.close_slave = close_slave;
514 pcm = calloc(1, sizeof(snd_pcm_t));
520 pcm->name = strdup(name);
521 pcm->type = SND_PCM_TYPE_RATE;
522 pcm->stream = slave->stream;
523 pcm->mode = slave->mode;
524 pcm->ops = &snd_pcm_rate_ops;
526 pcm->fast_ops = &snd_pcm_plugin_fast_ops;
527 pcm->fast_op_arg = pcm;
528 pcm->private_data = rate;
529 pcm->poll_fd = slave->poll_fd;
530 pcm->hw_ptr = &rate->plug.hw_ptr;
531 pcm->appl_ptr = &rate->plug.appl_ptr;
537 int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
539 snd_pcm_stream_t stream, int mode)
541 snd_config_iterator_t i, next;
542 const char *sname = NULL;
545 snd_config_t *slave = NULL;
546 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
548 snd_config_for_each(i, next, conf) {
549 snd_config_t *n = snd_config_iterator_entry(i);
550 const char *id = snd_config_get_id(n);
551 if (strcmp(id, "comment") == 0)
553 if (strcmp(id, "type") == 0)
555 if (strcmp(id, "slave") == 0) {
559 SNDERR("Unknown field %s", id);
563 SNDERR("slave is not defined");
566 err = snd_pcm_slave_conf(slave, &sname, 2,
567 SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
568 SND_PCM_HW_PARAM_RATE, 1, &srate);
571 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
572 snd_pcm_format_linear(sformat) != 1) {
573 SNDERR("slave format is not linear");
576 /* This is needed cause snd_config_update may destroy config */
577 sname = strdup(sname);
580 err = snd_pcm_open(&spcm, sname, stream, mode);
581 free((void *) sname);
584 err = snd_pcm_rate_open(pcmp, name, sformat, srate, spcm, 1);