1d5ac70f0Sopenharmony_ci/** 2d5ac70f0Sopenharmony_ci * \file pcm/pcm_rate.c 3d5ac70f0Sopenharmony_ci * \ingroup PCM_Plugins 4d5ac70f0Sopenharmony_ci * \brief PCM Rate Plugin Interface 5d5ac70f0Sopenharmony_ci * \author Abramo Bagnara <abramo@alsa-project.org> 6d5ac70f0Sopenharmony_ci * \author Jaroslav Kysela <perex@perex.cz> 7d5ac70f0Sopenharmony_ci * \date 2000-2004 8d5ac70f0Sopenharmony_ci */ 9d5ac70f0Sopenharmony_ci/* 10d5ac70f0Sopenharmony_ci * PCM - Rate conversion 11d5ac70f0Sopenharmony_ci * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> 12d5ac70f0Sopenharmony_ci * 2004 by Jaroslav Kysela <perex@perex.cz> 13d5ac70f0Sopenharmony_ci * 14d5ac70f0Sopenharmony_ci * 15d5ac70f0Sopenharmony_ci * This library is free software; you can redistribute it and/or modify 16d5ac70f0Sopenharmony_ci * it under the terms of the GNU Lesser General Public License as 17d5ac70f0Sopenharmony_ci * published by the Free Software Foundation; either version 2.1 of 18d5ac70f0Sopenharmony_ci * the License, or (at your option) any later version. 19d5ac70f0Sopenharmony_ci * 20d5ac70f0Sopenharmony_ci * This program is distributed in the hope that it will be useful, 21d5ac70f0Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 22d5ac70f0Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23d5ac70f0Sopenharmony_ci * GNU Lesser General Public License for more details. 24d5ac70f0Sopenharmony_ci * 25d5ac70f0Sopenharmony_ci * You should have received a copy of the GNU Lesser General Public 26d5ac70f0Sopenharmony_ci * License along with this library; if not, write to the Free Software 27d5ac70f0Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 28d5ac70f0Sopenharmony_ci * 29d5ac70f0Sopenharmony_ci */ 30d5ac70f0Sopenharmony_ci#include "pcm_local.h" 31d5ac70f0Sopenharmony_ci#include "pcm_plugin.h" 32d5ac70f0Sopenharmony_ci#include "pcm_rate.h" 33d5ac70f0Sopenharmony_ci#include "plugin_ops.h" 34d5ac70f0Sopenharmony_ci#include "bswap.h" 35d5ac70f0Sopenharmony_ci#include <inttypes.h> 36d5ac70f0Sopenharmony_ci 37d5ac70f0Sopenharmony_ci#if 0 38d5ac70f0Sopenharmony_ci#define DEBUG_REFINE 39d5ac70f0Sopenharmony_ci#endif 40d5ac70f0Sopenharmony_ci 41d5ac70f0Sopenharmony_ci#ifndef PIC 42d5ac70f0Sopenharmony_ci/* entry for static linking */ 43d5ac70f0Sopenharmony_ciconst char *_snd_module_pcm_rate = ""; 44d5ac70f0Sopenharmony_ci#endif 45d5ac70f0Sopenharmony_ci 46d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN 47d5ac70f0Sopenharmony_ci 48d5ac70f0Sopenharmony_citypedef struct _snd_pcm_rate snd_pcm_rate_t; 49d5ac70f0Sopenharmony_ci 50d5ac70f0Sopenharmony_cistruct _snd_pcm_rate { 51d5ac70f0Sopenharmony_ci snd_pcm_generic_t gen; 52d5ac70f0Sopenharmony_ci snd_pcm_uframes_t appl_ptr, hw_ptr, last_slave_hw_ptr; 53d5ac70f0Sopenharmony_ci snd_pcm_uframes_t last_commit_ptr; 54d5ac70f0Sopenharmony_ci snd_pcm_uframes_t orig_avail_min; 55d5ac70f0Sopenharmony_ci snd_pcm_sw_params_t sw_params; 56d5ac70f0Sopenharmony_ci snd_pcm_format_t sformat; 57d5ac70f0Sopenharmony_ci unsigned int srate; 58d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *pareas; /* areas for splitted period (rate pcm) */ 59d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *sareas; /* areas for splitted period (slave pcm) */ 60d5ac70f0Sopenharmony_ci snd_pcm_rate_info_t info; 61d5ac70f0Sopenharmony_ci void *open_func; 62d5ac70f0Sopenharmony_ci void *obj; 63d5ac70f0Sopenharmony_ci snd_pcm_rate_ops_t ops; 64d5ac70f0Sopenharmony_ci unsigned int src_conv_idx; 65d5ac70f0Sopenharmony_ci unsigned int dst_conv_idx; 66d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *src_buf; 67d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *dst_buf; 68d5ac70f0Sopenharmony_ci int start_pending; /* start is triggered but not commited to slave */ 69d5ac70f0Sopenharmony_ci snd_htimestamp_t trigger_tstamp; 70d5ac70f0Sopenharmony_ci unsigned int plugin_version; 71d5ac70f0Sopenharmony_ci unsigned int rate_min, rate_max; 72d5ac70f0Sopenharmony_ci snd_pcm_format_t orig_in_format; 73d5ac70f0Sopenharmony_ci snd_pcm_format_t orig_out_format; 74d5ac70f0Sopenharmony_ci uint64_t in_formats; 75d5ac70f0Sopenharmony_ci uint64_t out_formats; 76d5ac70f0Sopenharmony_ci unsigned int format_flags; 77d5ac70f0Sopenharmony_ci}; 78d5ac70f0Sopenharmony_ci 79d5ac70f0Sopenharmony_ci#define SND_PCM_RATE_PLUGIN_VERSION_OLD 0x010001 /* old rate plugin */ 80d5ac70f0Sopenharmony_ci#endif /* DOC_HIDDEN */ 81d5ac70f0Sopenharmony_ci 82d5ac70f0Sopenharmony_ci/* allocate a channel area and a temporary buffer for the given size */ 83d5ac70f0Sopenharmony_cistatic snd_pcm_channel_area_t * 84d5ac70f0Sopenharmony_cirate_alloc_tmp_buf(snd_pcm_format_t format, 85d5ac70f0Sopenharmony_ci unsigned int channels, unsigned int frames) 86d5ac70f0Sopenharmony_ci{ 87d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *ap; 88d5ac70f0Sopenharmony_ci int width = snd_pcm_format_physical_width(format); 89d5ac70f0Sopenharmony_ci unsigned int i; 90d5ac70f0Sopenharmony_ci 91d5ac70f0Sopenharmony_ci ap = malloc(sizeof(*ap) * channels); 92d5ac70f0Sopenharmony_ci if (!ap) 93d5ac70f0Sopenharmony_ci return NULL; 94d5ac70f0Sopenharmony_ci ap->addr = malloc(frames * channels * width / 8); 95d5ac70f0Sopenharmony_ci if (!ap->addr) { 96d5ac70f0Sopenharmony_ci free(ap); 97d5ac70f0Sopenharmony_ci return NULL; 98d5ac70f0Sopenharmony_ci } 99d5ac70f0Sopenharmony_ci 100d5ac70f0Sopenharmony_ci /* set up in interleaved format */ 101d5ac70f0Sopenharmony_ci for (i = 0; i < channels; i++) { 102d5ac70f0Sopenharmony_ci ap[i].addr = ap[0].addr + (i * width) / 8; 103d5ac70f0Sopenharmony_ci ap[i].first = 0; 104d5ac70f0Sopenharmony_ci ap[i].step = width * channels; 105d5ac70f0Sopenharmony_ci } 106d5ac70f0Sopenharmony_ci 107d5ac70f0Sopenharmony_ci return ap; 108d5ac70f0Sopenharmony_ci} 109d5ac70f0Sopenharmony_ci 110d5ac70f0Sopenharmony_cistatic void rate_free_tmp_buf(snd_pcm_channel_area_t **ptr) 111d5ac70f0Sopenharmony_ci{ 112d5ac70f0Sopenharmony_ci snd_pcm_channel_area_t *c = *ptr; 113d5ac70f0Sopenharmony_ci 114d5ac70f0Sopenharmony_ci if (c) { 115d5ac70f0Sopenharmony_ci free(c->addr); 116d5ac70f0Sopenharmony_ci free(c); 117d5ac70f0Sopenharmony_ci *ptr = NULL; 118d5ac70f0Sopenharmony_ci } 119d5ac70f0Sopenharmony_ci} 120d5ac70f0Sopenharmony_ci 121d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params) 122d5ac70f0Sopenharmony_ci{ 123d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 124d5ac70f0Sopenharmony_ci int err; 125d5ac70f0Sopenharmony_ci snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM }; 126d5ac70f0Sopenharmony_ci snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR }; 127d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS, 128d5ac70f0Sopenharmony_ci &access_mask); 129d5ac70f0Sopenharmony_ci if (err < 0) 130d5ac70f0Sopenharmony_ci return err; 131d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT, 132d5ac70f0Sopenharmony_ci &format_mask); 133d5ac70f0Sopenharmony_ci if (err < 0) 134d5ac70f0Sopenharmony_ci return err; 135d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD); 136d5ac70f0Sopenharmony_ci if (err < 0) 137d5ac70f0Sopenharmony_ci return err; 138d5ac70f0Sopenharmony_ci if (rate->rate_min) { 139d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE, 140d5ac70f0Sopenharmony_ci rate->rate_min, 0); 141d5ac70f0Sopenharmony_ci if (err < 0) 142d5ac70f0Sopenharmony_ci return err; 143d5ac70f0Sopenharmony_ci } 144d5ac70f0Sopenharmony_ci if (rate->rate_max) { 145d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_RATE, 146d5ac70f0Sopenharmony_ci rate->rate_max, 0); 147d5ac70f0Sopenharmony_ci if (err < 0) 148d5ac70f0Sopenharmony_ci return err; 149d5ac70f0Sopenharmony_ci } 150d5ac70f0Sopenharmony_ci params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); 151d5ac70f0Sopenharmony_ci return 0; 152d5ac70f0Sopenharmony_ci} 153d5ac70f0Sopenharmony_ci 154d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams) 155d5ac70f0Sopenharmony_ci{ 156d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 157d5ac70f0Sopenharmony_ci snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP }; 158d5ac70f0Sopenharmony_ci _snd_pcm_hw_params_any(sparams); 159d5ac70f0Sopenharmony_ci _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS, 160d5ac70f0Sopenharmony_ci &saccess_mask); 161d5ac70f0Sopenharmony_ci if (rate->sformat != SND_PCM_FORMAT_UNKNOWN) { 162d5ac70f0Sopenharmony_ci _snd_pcm_hw_params_set_format(sparams, rate->sformat); 163d5ac70f0Sopenharmony_ci _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD); 164d5ac70f0Sopenharmony_ci } 165d5ac70f0Sopenharmony_ci _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE, 166d5ac70f0Sopenharmony_ci rate->srate, 0, rate->srate + 1, -1); 167d5ac70f0Sopenharmony_ci return 0; 168d5ac70f0Sopenharmony_ci} 169d5ac70f0Sopenharmony_ci 170d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, 171d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *sparams) 172d5ac70f0Sopenharmony_ci{ 173d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 174d5ac70f0Sopenharmony_ci snd_interval_t t, buffer_size; 175d5ac70f0Sopenharmony_ci const snd_interval_t *srate, *crate; 176d5ac70f0Sopenharmony_ci int err; 177d5ac70f0Sopenharmony_ci unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS | 178d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_PERIOD_TIME | 179d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_TICK_TIME); 180d5ac70f0Sopenharmony_ci if (rate->sformat == SND_PCM_FORMAT_UNKNOWN) 181d5ac70f0Sopenharmony_ci links |= (SND_PCM_HW_PARBIT_FORMAT | 182d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_SUBFORMAT | 183d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_SAMPLE_BITS | 184d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_FRAME_BITS); 185d5ac70f0Sopenharmony_ci snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE)); 186d5ac70f0Sopenharmony_ci snd_interval_unfloor(&buffer_size); 187d5ac70f0Sopenharmony_ci crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE); 188d5ac70f0Sopenharmony_ci srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE); 189d5ac70f0Sopenharmony_ci snd_interval_muldiv(&buffer_size, srate, crate, &t); 190d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t); 191d5ac70f0Sopenharmony_ci if (err < 0) 192d5ac70f0Sopenharmony_ci return err; 193d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_params_refine(sparams, links, params); 194d5ac70f0Sopenharmony_ci if (err < 0) 195d5ac70f0Sopenharmony_ci return err; 196d5ac70f0Sopenharmony_ci return 0; 197d5ac70f0Sopenharmony_ci} 198d5ac70f0Sopenharmony_ci 199d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, 200d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *sparams) 201d5ac70f0Sopenharmony_ci{ 202d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 203d5ac70f0Sopenharmony_ci snd_interval_t t; 204d5ac70f0Sopenharmony_ci#ifdef DEBUG_REFINE 205d5ac70f0Sopenharmony_ci snd_output_t *out; 206d5ac70f0Sopenharmony_ci#endif 207d5ac70f0Sopenharmony_ci const snd_interval_t *sbuffer_size, *buffer_size; 208d5ac70f0Sopenharmony_ci const snd_interval_t *srate, *crate; 209d5ac70f0Sopenharmony_ci int err; 210d5ac70f0Sopenharmony_ci unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS | 211d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_PERIOD_TIME | 212d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_TICK_TIME); 213d5ac70f0Sopenharmony_ci if (rate->sformat == SND_PCM_FORMAT_UNKNOWN) 214d5ac70f0Sopenharmony_ci links |= (SND_PCM_HW_PARBIT_FORMAT | 215d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_SUBFORMAT | 216d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_SAMPLE_BITS | 217d5ac70f0Sopenharmony_ci SND_PCM_HW_PARBIT_FRAME_BITS); 218d5ac70f0Sopenharmony_ci sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE); 219d5ac70f0Sopenharmony_ci crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE); 220d5ac70f0Sopenharmony_ci srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE); 221d5ac70f0Sopenharmony_ci snd_interval_muldiv(sbuffer_size, crate, srate, &t); 222d5ac70f0Sopenharmony_ci snd_interval_floor(&t); 223d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t); 224d5ac70f0Sopenharmony_ci if (err < 0) 225d5ac70f0Sopenharmony_ci return err; 226d5ac70f0Sopenharmony_ci buffer_size = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE); 227d5ac70f0Sopenharmony_ci /* 228d5ac70f0Sopenharmony_ci * this condition probably needs more work: 229d5ac70f0Sopenharmony_ci * in case when the buffer_size is known and we are looking 230d5ac70f0Sopenharmony_ci * for best period_size, we should prefer situation when 231d5ac70f0Sopenharmony_ci * (buffer_size / period_size) * period_size == buffer_size 232d5ac70f0Sopenharmony_ci */ 233d5ac70f0Sopenharmony_ci if (snd_interval_single(buffer_size) && buffer_size->integer) { 234d5ac70f0Sopenharmony_ci snd_interval_t *period_size; 235d5ac70f0Sopenharmony_ci period_size = (snd_interval_t *)snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE); 236d5ac70f0Sopenharmony_ci if (!snd_interval_checkempty(period_size) && 237d5ac70f0Sopenharmony_ci period_size->openmin && period_size->openmax && 238d5ac70f0Sopenharmony_ci period_size->min + 1 == period_size->max) { 239d5ac70f0Sopenharmony_ci if (period_size->min > 0 && (buffer_size->min / period_size->min) * period_size->min == buffer_size->min) { 240d5ac70f0Sopenharmony_ci snd_interval_set_value(period_size, period_size->min); 241d5ac70f0Sopenharmony_ci } else if ((buffer_size->max / period_size->max) * period_size->max == buffer_size->max) { 242d5ac70f0Sopenharmony_ci snd_interval_set_value(period_size, period_size->max); 243d5ac70f0Sopenharmony_ci } 244d5ac70f0Sopenharmony_ci } 245d5ac70f0Sopenharmony_ci } 246d5ac70f0Sopenharmony_ci#ifdef DEBUG_REFINE 247d5ac70f0Sopenharmony_ci snd_output_stdio_attach(&out, stderr, 0); 248d5ac70f0Sopenharmony_ci snd_output_printf(out, "REFINE (params):\n"); 249d5ac70f0Sopenharmony_ci snd_pcm_hw_params_dump(params, out); 250d5ac70f0Sopenharmony_ci snd_output_printf(out, "REFINE (slave params):\n"); 251d5ac70f0Sopenharmony_ci snd_pcm_hw_params_dump(sparams, out); 252d5ac70f0Sopenharmony_ci snd_output_close(out); 253d5ac70f0Sopenharmony_ci#endif 254d5ac70f0Sopenharmony_ci err = _snd_pcm_hw_params_refine(params, links, sparams); 255d5ac70f0Sopenharmony_ci#ifdef DEBUG_REFINE 256d5ac70f0Sopenharmony_ci snd_output_stdio_attach(&out, stderr, 0); 257d5ac70f0Sopenharmony_ci snd_output_printf(out, "********************\n"); 258d5ac70f0Sopenharmony_ci snd_output_printf(out, "REFINE (params) (%i):\n", err); 259d5ac70f0Sopenharmony_ci snd_pcm_hw_params_dump(params, out); 260d5ac70f0Sopenharmony_ci snd_output_printf(out, "REFINE (slave params):\n"); 261d5ac70f0Sopenharmony_ci snd_pcm_hw_params_dump(sparams, out); 262d5ac70f0Sopenharmony_ci snd_output_close(out); 263d5ac70f0Sopenharmony_ci#endif 264d5ac70f0Sopenharmony_ci if (err < 0) 265d5ac70f0Sopenharmony_ci return err; 266d5ac70f0Sopenharmony_ci return 0; 267d5ac70f0Sopenharmony_ci} 268d5ac70f0Sopenharmony_ci 269d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_refine(snd_pcm_t *pcm, 270d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t *params) 271d5ac70f0Sopenharmony_ci{ 272d5ac70f0Sopenharmony_ci return snd_pcm_hw_refine_slave(pcm, params, 273d5ac70f0Sopenharmony_ci snd_pcm_rate_hw_refine_cprepare, 274d5ac70f0Sopenharmony_ci snd_pcm_rate_hw_refine_cchange, 275d5ac70f0Sopenharmony_ci snd_pcm_rate_hw_refine_sprepare, 276d5ac70f0Sopenharmony_ci snd_pcm_rate_hw_refine_schange, 277d5ac70f0Sopenharmony_ci snd_pcm_generic_hw_refine); 278d5ac70f0Sopenharmony_ci} 279d5ac70f0Sopenharmony_ci 280d5ac70f0Sopenharmony_ci/* evaluate the best matching available format to the given format */ 281d5ac70f0Sopenharmony_cistatic int get_best_format(uint64_t mask, snd_pcm_format_t orig) 282d5ac70f0Sopenharmony_ci{ 283d5ac70f0Sopenharmony_ci int pwidth = snd_pcm_format_physical_width(orig); 284d5ac70f0Sopenharmony_ci int width = snd_pcm_format_width(orig); 285d5ac70f0Sopenharmony_ci int signd = snd_pcm_format_signed(orig); 286d5ac70f0Sopenharmony_ci int best_score = -1; 287d5ac70f0Sopenharmony_ci int match = -1; 288d5ac70f0Sopenharmony_ci int f, score; 289d5ac70f0Sopenharmony_ci 290d5ac70f0Sopenharmony_ci for (f = 0; f <= SND_PCM_FORMAT_LAST; f++) { 291d5ac70f0Sopenharmony_ci if (!(mask & (1ULL << f))) 292d5ac70f0Sopenharmony_ci continue; 293d5ac70f0Sopenharmony_ci score = 0; 294d5ac70f0Sopenharmony_ci if (snd_pcm_format_linear(f)) { 295d5ac70f0Sopenharmony_ci if (snd_pcm_format_physical_width(f) == pwidth) 296d5ac70f0Sopenharmony_ci score++; 297d5ac70f0Sopenharmony_ci if (snd_pcm_format_physical_width(f) >= pwidth) 298d5ac70f0Sopenharmony_ci score++; 299d5ac70f0Sopenharmony_ci if (snd_pcm_format_width(f) == width) 300d5ac70f0Sopenharmony_ci score++; 301d5ac70f0Sopenharmony_ci if (snd_pcm_format_signed(f) == signd) 302d5ac70f0Sopenharmony_ci score++; 303d5ac70f0Sopenharmony_ci } 304d5ac70f0Sopenharmony_ci if (score > best_score) { 305d5ac70f0Sopenharmony_ci match = f; 306d5ac70f0Sopenharmony_ci best_score = score; 307d5ac70f0Sopenharmony_ci } 308d5ac70f0Sopenharmony_ci } 309d5ac70f0Sopenharmony_ci 310d5ac70f0Sopenharmony_ci return match; 311d5ac70f0Sopenharmony_ci} 312d5ac70f0Sopenharmony_ci 313d5ac70f0Sopenharmony_ci/* set up the input and output formats from the available lists */ 314d5ac70f0Sopenharmony_cistatic int choose_preferred_format(snd_pcm_rate_t *rate) 315d5ac70f0Sopenharmony_ci{ 316d5ac70f0Sopenharmony_ci uint64_t in_mask = rate->in_formats; 317d5ac70f0Sopenharmony_ci uint64_t out_mask = rate->out_formats; 318d5ac70f0Sopenharmony_ci int in, out; 319d5ac70f0Sopenharmony_ci 320d5ac70f0Sopenharmony_ci if (!in_mask || !out_mask) 321d5ac70f0Sopenharmony_ci return 0; 322d5ac70f0Sopenharmony_ci 323d5ac70f0Sopenharmony_ci if (rate->orig_in_format == rate->orig_out_format) 324d5ac70f0Sopenharmony_ci if (in_mask & out_mask & (1ULL << rate->orig_in_format)) 325d5ac70f0Sopenharmony_ci return 0; /* nothing changed */ 326d5ac70f0Sopenharmony_ci 327d5ac70f0Sopenharmony_ci repeat: 328d5ac70f0Sopenharmony_ci in = get_best_format(in_mask, rate->orig_in_format); 329d5ac70f0Sopenharmony_ci out = get_best_format(out_mask, rate->orig_out_format); 330d5ac70f0Sopenharmony_ci if (in < 0 || out < 0) 331d5ac70f0Sopenharmony_ci return -ENOENT; 332d5ac70f0Sopenharmony_ci 333d5ac70f0Sopenharmony_ci if ((rate->format_flags & SND_PCM_RATE_FLAG_SYNC_FORMATS) && 334d5ac70f0Sopenharmony_ci in != out) { 335d5ac70f0Sopenharmony_ci if (out_mask & (1ULL << in)) 336d5ac70f0Sopenharmony_ci out = in; 337d5ac70f0Sopenharmony_ci else if (in_mask & (1ULL << out)) 338d5ac70f0Sopenharmony_ci in = out; 339d5ac70f0Sopenharmony_ci else { 340d5ac70f0Sopenharmony_ci in_mask &= ~(1ULL << in); 341d5ac70f0Sopenharmony_ci out_mask &= ~(1ULL << out); 342d5ac70f0Sopenharmony_ci goto repeat; 343d5ac70f0Sopenharmony_ci } 344d5ac70f0Sopenharmony_ci } 345d5ac70f0Sopenharmony_ci 346d5ac70f0Sopenharmony_ci rate->info.in.format = in; 347d5ac70f0Sopenharmony_ci rate->info.out.format = out; 348d5ac70f0Sopenharmony_ci return 0; 349d5ac70f0Sopenharmony_ci} 350d5ac70f0Sopenharmony_ci 351d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) 352d5ac70f0Sopenharmony_ci{ 353d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 354d5ac70f0Sopenharmony_ci snd_pcm_t *slave = rate->gen.slave; 355d5ac70f0Sopenharmony_ci snd_pcm_rate_side_info_t *sinfo, *cinfo; 356d5ac70f0Sopenharmony_ci unsigned int channels, acc; 357d5ac70f0Sopenharmony_ci int need_src_buf, need_dst_buf; 358d5ac70f0Sopenharmony_ci int err = snd_pcm_hw_params_slave(pcm, params, 359d5ac70f0Sopenharmony_ci snd_pcm_rate_hw_refine_cchange, 360d5ac70f0Sopenharmony_ci snd_pcm_rate_hw_refine_sprepare, 361d5ac70f0Sopenharmony_ci snd_pcm_rate_hw_refine_schange, 362d5ac70f0Sopenharmony_ci snd_pcm_generic_hw_params); 363d5ac70f0Sopenharmony_ci if (err < 0) 364d5ac70f0Sopenharmony_ci return err; 365d5ac70f0Sopenharmony_ci 366d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 367d5ac70f0Sopenharmony_ci cinfo = &rate->info.in; 368d5ac70f0Sopenharmony_ci sinfo = &rate->info.out; 369d5ac70f0Sopenharmony_ci } else { 370d5ac70f0Sopenharmony_ci sinfo = &rate->info.in; 371d5ac70f0Sopenharmony_ci cinfo = &rate->info.out; 372d5ac70f0Sopenharmony_ci } 373d5ac70f0Sopenharmony_ci err = INTERNAL(snd_pcm_hw_params_get_format)(params, &cinfo->format); 374d5ac70f0Sopenharmony_ci if (err < 0) 375d5ac70f0Sopenharmony_ci return err; 376d5ac70f0Sopenharmony_ci err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &cinfo->rate, 0); 377d5ac70f0Sopenharmony_ci if (err < 0) 378d5ac70f0Sopenharmony_ci return err; 379d5ac70f0Sopenharmony_ci err = INTERNAL(snd_pcm_hw_params_get_period_size)(params, &cinfo->period_size, 0); 380d5ac70f0Sopenharmony_ci if (err < 0) 381d5ac70f0Sopenharmony_ci return err; 382d5ac70f0Sopenharmony_ci err = INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &cinfo->buffer_size); 383d5ac70f0Sopenharmony_ci if (err < 0) 384d5ac70f0Sopenharmony_ci return err; 385d5ac70f0Sopenharmony_ci err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels); 386d5ac70f0Sopenharmony_ci if (err < 0) 387d5ac70f0Sopenharmony_ci return err; 388d5ac70f0Sopenharmony_ci err = INTERNAL(snd_pcm_hw_params_get_access)(params, &acc); 389d5ac70f0Sopenharmony_ci if (err < 0) 390d5ac70f0Sopenharmony_ci return err; 391d5ac70f0Sopenharmony_ci 392d5ac70f0Sopenharmony_ci rate->info.channels = channels; 393d5ac70f0Sopenharmony_ci sinfo->format = slave->format; 394d5ac70f0Sopenharmony_ci sinfo->rate = slave->rate; 395d5ac70f0Sopenharmony_ci sinfo->buffer_size = slave->buffer_size; 396d5ac70f0Sopenharmony_ci sinfo->period_size = slave->period_size; 397d5ac70f0Sopenharmony_ci 398d5ac70f0Sopenharmony_ci if (CHECK_SANITY(rate->pareas)) { 399d5ac70f0Sopenharmony_ci SNDMSG("rate plugin already in use"); 400d5ac70f0Sopenharmony_ci return -EBUSY; 401d5ac70f0Sopenharmony_ci } 402d5ac70f0Sopenharmony_ci 403d5ac70f0Sopenharmony_ci rate->pareas = rate_alloc_tmp_buf(cinfo->format, channels, 404d5ac70f0Sopenharmony_ci cinfo->period_size); 405d5ac70f0Sopenharmony_ci rate->sareas = rate_alloc_tmp_buf(sinfo->format, channels, 406d5ac70f0Sopenharmony_ci sinfo->period_size); 407d5ac70f0Sopenharmony_ci if (!rate->pareas || !rate->sareas) { 408d5ac70f0Sopenharmony_ci err = -ENOMEM; 409d5ac70f0Sopenharmony_ci goto error_pareas; 410d5ac70f0Sopenharmony_ci } 411d5ac70f0Sopenharmony_ci 412d5ac70f0Sopenharmony_ci rate->orig_in_format = rate->info.in.format; 413d5ac70f0Sopenharmony_ci rate->orig_out_format = rate->info.out.format; 414d5ac70f0Sopenharmony_ci if (choose_preferred_format(rate) < 0) { 415d5ac70f0Sopenharmony_ci SNDERR("No matching format in rate plugin"); 416d5ac70f0Sopenharmony_ci err = -EINVAL; 417d5ac70f0Sopenharmony_ci goto error_pareas; 418d5ac70f0Sopenharmony_ci } 419d5ac70f0Sopenharmony_ci 420d5ac70f0Sopenharmony_ci err = rate->ops.init(rate->obj, &rate->info); 421d5ac70f0Sopenharmony_ci if (err < 0) 422d5ac70f0Sopenharmony_ci goto error_init; 423d5ac70f0Sopenharmony_ci 424d5ac70f0Sopenharmony_ci rate_free_tmp_buf(&rate->src_buf); 425d5ac70f0Sopenharmony_ci rate_free_tmp_buf(&rate->dst_buf); 426d5ac70f0Sopenharmony_ci 427d5ac70f0Sopenharmony_ci need_src_buf = need_dst_buf = 0; 428d5ac70f0Sopenharmony_ci 429d5ac70f0Sopenharmony_ci if ((rate->format_flags & SND_PCM_RATE_FLAG_INTERLEAVED) && 430d5ac70f0Sopenharmony_ci !(acc == SND_PCM_ACCESS_MMAP_INTERLEAVED || 431d5ac70f0Sopenharmony_ci acc == SND_PCM_ACCESS_RW_INTERLEAVED)) { 432d5ac70f0Sopenharmony_ci need_src_buf = need_dst_buf = 1; 433d5ac70f0Sopenharmony_ci } else { 434d5ac70f0Sopenharmony_ci if (rate->orig_in_format != rate->info.in.format) 435d5ac70f0Sopenharmony_ci need_src_buf = 1; 436d5ac70f0Sopenharmony_ci if (rate->orig_out_format != rate->info.out.format) 437d5ac70f0Sopenharmony_ci need_dst_buf = 1; 438d5ac70f0Sopenharmony_ci } 439d5ac70f0Sopenharmony_ci 440d5ac70f0Sopenharmony_ci if (need_src_buf) { 441d5ac70f0Sopenharmony_ci rate->src_conv_idx = 442d5ac70f0Sopenharmony_ci snd_pcm_linear_convert_index(rate->orig_in_format, 443d5ac70f0Sopenharmony_ci rate->info.in.format); 444d5ac70f0Sopenharmony_ci rate->src_buf = rate_alloc_tmp_buf(rate->info.in.format, 445d5ac70f0Sopenharmony_ci channels, rate->info.in.period_size); 446d5ac70f0Sopenharmony_ci if (!rate->src_buf) { 447d5ac70f0Sopenharmony_ci err = -ENOMEM; 448d5ac70f0Sopenharmony_ci goto error; 449d5ac70f0Sopenharmony_ci } 450d5ac70f0Sopenharmony_ci } 451d5ac70f0Sopenharmony_ci 452d5ac70f0Sopenharmony_ci if (need_dst_buf) { 453d5ac70f0Sopenharmony_ci rate->dst_conv_idx = 454d5ac70f0Sopenharmony_ci snd_pcm_linear_convert_index(rate->info.out.format, 455d5ac70f0Sopenharmony_ci rate->orig_out_format); 456d5ac70f0Sopenharmony_ci rate->dst_buf = rate_alloc_tmp_buf(rate->info.out.format, 457d5ac70f0Sopenharmony_ci channels, rate->info.out.period_size); 458d5ac70f0Sopenharmony_ci if (!rate->dst_buf) { 459d5ac70f0Sopenharmony_ci err = -ENOMEM; 460d5ac70f0Sopenharmony_ci goto error; 461d5ac70f0Sopenharmony_ci } 462d5ac70f0Sopenharmony_ci } 463d5ac70f0Sopenharmony_ci 464d5ac70f0Sopenharmony_ci return 0; 465d5ac70f0Sopenharmony_ci 466d5ac70f0Sopenharmony_ci error: 467d5ac70f0Sopenharmony_ci rate_free_tmp_buf(&rate->src_buf); 468d5ac70f0Sopenharmony_ci rate_free_tmp_buf(&rate->dst_buf); 469d5ac70f0Sopenharmony_ci error_init: 470d5ac70f0Sopenharmony_ci if (rate->ops.free) 471d5ac70f0Sopenharmony_ci rate->ops.free(rate->obj); 472d5ac70f0Sopenharmony_ci error_pareas: 473d5ac70f0Sopenharmony_ci rate_free_tmp_buf(&rate->pareas); 474d5ac70f0Sopenharmony_ci rate_free_tmp_buf(&rate->sareas); 475d5ac70f0Sopenharmony_ci return err; 476d5ac70f0Sopenharmony_ci} 477d5ac70f0Sopenharmony_ci 478d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_free(snd_pcm_t *pcm) 479d5ac70f0Sopenharmony_ci{ 480d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 481d5ac70f0Sopenharmony_ci 482d5ac70f0Sopenharmony_ci rate_free_tmp_buf(&rate->pareas); 483d5ac70f0Sopenharmony_ci rate_free_tmp_buf(&rate->sareas); 484d5ac70f0Sopenharmony_ci if (rate->ops.free) 485d5ac70f0Sopenharmony_ci rate->ops.free(rate->obj); 486d5ac70f0Sopenharmony_ci rate_free_tmp_buf(&rate->src_buf); 487d5ac70f0Sopenharmony_ci rate_free_tmp_buf(&rate->dst_buf); 488d5ac70f0Sopenharmony_ci return snd_pcm_hw_free(rate->gen.slave); 489d5ac70f0Sopenharmony_ci} 490d5ac70f0Sopenharmony_ci 491d5ac70f0Sopenharmony_cistatic void recalc(snd_pcm_t *pcm, snd_pcm_uframes_t *val) 492d5ac70f0Sopenharmony_ci{ 493d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 494d5ac70f0Sopenharmony_ci snd_pcm_t *slave = rate->gen.slave; 495d5ac70f0Sopenharmony_ci unsigned long div; 496d5ac70f0Sopenharmony_ci 497d5ac70f0Sopenharmony_ci if (*val == pcm->buffer_size) { 498d5ac70f0Sopenharmony_ci *val = slave->buffer_size; 499d5ac70f0Sopenharmony_ci } else { 500d5ac70f0Sopenharmony_ci div = *val / pcm->period_size; 501d5ac70f0Sopenharmony_ci if (div * pcm->period_size == *val) 502d5ac70f0Sopenharmony_ci *val = div * slave->period_size; 503d5ac70f0Sopenharmony_ci else 504d5ac70f0Sopenharmony_ci *val = muldiv_near(*val, slave->period_size, pcm->period_size); 505d5ac70f0Sopenharmony_ci } 506d5ac70f0Sopenharmony_ci} 507d5ac70f0Sopenharmony_ci 508d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) 509d5ac70f0Sopenharmony_ci{ 510d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 511d5ac70f0Sopenharmony_ci snd_pcm_t *slave = rate->gen.slave; 512d5ac70f0Sopenharmony_ci snd_pcm_sw_params_t *sparams; 513d5ac70f0Sopenharmony_ci snd_pcm_uframes_t boundary1, boundary2, sboundary; 514d5ac70f0Sopenharmony_ci int err; 515d5ac70f0Sopenharmony_ci 516d5ac70f0Sopenharmony_ci sparams = &rate->sw_params; 517d5ac70f0Sopenharmony_ci err = snd_pcm_sw_params_current(slave, sparams); 518d5ac70f0Sopenharmony_ci if (err < 0) 519d5ac70f0Sopenharmony_ci return err; 520d5ac70f0Sopenharmony_ci sboundary = sparams->boundary; 521d5ac70f0Sopenharmony_ci *sparams = *params; 522d5ac70f0Sopenharmony_ci boundary1 = pcm->buffer_size; 523d5ac70f0Sopenharmony_ci boundary2 = slave->buffer_size; 524d5ac70f0Sopenharmony_ci while (boundary1 * 2 <= LONG_MAX - pcm->buffer_size && 525d5ac70f0Sopenharmony_ci boundary2 * 2 <= LONG_MAX - slave->buffer_size) { 526d5ac70f0Sopenharmony_ci boundary1 *= 2; 527d5ac70f0Sopenharmony_ci boundary2 *= 2; 528d5ac70f0Sopenharmony_ci } 529d5ac70f0Sopenharmony_ci params->boundary = boundary1; 530d5ac70f0Sopenharmony_ci sparams->boundary = sboundary; 531d5ac70f0Sopenharmony_ci 532d5ac70f0Sopenharmony_ci if (rate->ops.adjust_pitch) 533d5ac70f0Sopenharmony_ci rate->ops.adjust_pitch(rate->obj, &rate->info); 534d5ac70f0Sopenharmony_ci 535d5ac70f0Sopenharmony_ci recalc(pcm, &sparams->avail_min); 536d5ac70f0Sopenharmony_ci rate->orig_avail_min = sparams->avail_min; 537d5ac70f0Sopenharmony_ci recalc(pcm, &sparams->start_threshold); 538d5ac70f0Sopenharmony_ci if (sparams->avail_min < 1) sparams->avail_min = 1; 539d5ac70f0Sopenharmony_ci if (sparams->start_threshold <= slave->buffer_size) { 540d5ac70f0Sopenharmony_ci if (sparams->start_threshold > (slave->buffer_size / sparams->avail_min) * sparams->avail_min) 541d5ac70f0Sopenharmony_ci sparams->start_threshold = (slave->buffer_size / sparams->avail_min) * sparams->avail_min; 542d5ac70f0Sopenharmony_ci } 543d5ac70f0Sopenharmony_ci if (sparams->stop_threshold >= params->boundary) { 544d5ac70f0Sopenharmony_ci sparams->stop_threshold = sparams->boundary; 545d5ac70f0Sopenharmony_ci } else { 546d5ac70f0Sopenharmony_ci recalc(pcm, &sparams->stop_threshold); 547d5ac70f0Sopenharmony_ci } 548d5ac70f0Sopenharmony_ci recalc(pcm, &sparams->silence_threshold); 549d5ac70f0Sopenharmony_ci if (sparams->silence_size >= params->boundary) { 550d5ac70f0Sopenharmony_ci sparams->silence_size = sparams->boundary; 551d5ac70f0Sopenharmony_ci } else { 552d5ac70f0Sopenharmony_ci recalc(pcm, &sparams->silence_size); 553d5ac70f0Sopenharmony_ci } 554d5ac70f0Sopenharmony_ci return snd_pcm_sw_params(slave, sparams); 555d5ac70f0Sopenharmony_ci} 556d5ac70f0Sopenharmony_ci 557d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_init(snd_pcm_t *pcm) 558d5ac70f0Sopenharmony_ci{ 559d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 560d5ac70f0Sopenharmony_ci 561d5ac70f0Sopenharmony_ci if (rate->ops.reset) 562d5ac70f0Sopenharmony_ci rate->ops.reset(rate->obj); 563d5ac70f0Sopenharmony_ci rate->last_commit_ptr = 0; 564d5ac70f0Sopenharmony_ci rate->start_pending = 0; 565d5ac70f0Sopenharmony_ci return 0; 566d5ac70f0Sopenharmony_ci} 567d5ac70f0Sopenharmony_ci 568d5ac70f0Sopenharmony_cistatic void do_convert(const snd_pcm_channel_area_t *dst_areas, 569d5ac70f0Sopenharmony_ci snd_pcm_uframes_t dst_offset, unsigned int dst_frames, 570d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *src_areas, 571d5ac70f0Sopenharmony_ci snd_pcm_uframes_t src_offset, unsigned int src_frames, 572d5ac70f0Sopenharmony_ci unsigned int channels, 573d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate) 574d5ac70f0Sopenharmony_ci{ 575d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *out_areas; 576d5ac70f0Sopenharmony_ci snd_pcm_uframes_t out_offset; 577d5ac70f0Sopenharmony_ci 578d5ac70f0Sopenharmony_ci if (rate->dst_buf) { 579d5ac70f0Sopenharmony_ci out_areas = rate->dst_buf; 580d5ac70f0Sopenharmony_ci out_offset = 0; 581d5ac70f0Sopenharmony_ci } else { 582d5ac70f0Sopenharmony_ci out_areas = dst_areas; 583d5ac70f0Sopenharmony_ci out_offset = dst_offset; 584d5ac70f0Sopenharmony_ci } 585d5ac70f0Sopenharmony_ci 586d5ac70f0Sopenharmony_ci if (rate->src_buf) { 587d5ac70f0Sopenharmony_ci snd_pcm_linear_convert(rate->src_buf, 0, 588d5ac70f0Sopenharmony_ci src_areas, src_offset, 589d5ac70f0Sopenharmony_ci channels, src_frames, 590d5ac70f0Sopenharmony_ci rate->src_conv_idx); 591d5ac70f0Sopenharmony_ci src_areas = rate->src_buf; 592d5ac70f0Sopenharmony_ci src_offset = 0; 593d5ac70f0Sopenharmony_ci } 594d5ac70f0Sopenharmony_ci 595d5ac70f0Sopenharmony_ci if (rate->ops.convert) 596d5ac70f0Sopenharmony_ci rate->ops.convert(rate->obj, out_areas, out_offset, dst_frames, 597d5ac70f0Sopenharmony_ci src_areas, src_offset, src_frames); 598d5ac70f0Sopenharmony_ci else 599d5ac70f0Sopenharmony_ci rate->ops.convert_s16(rate->obj, 600d5ac70f0Sopenharmony_ci snd_pcm_channel_area_addr(out_areas, out_offset), 601d5ac70f0Sopenharmony_ci dst_frames, 602d5ac70f0Sopenharmony_ci snd_pcm_channel_area_addr(src_areas, src_offset), 603d5ac70f0Sopenharmony_ci src_frames); 604d5ac70f0Sopenharmony_ci if (rate->dst_buf) 605d5ac70f0Sopenharmony_ci snd_pcm_linear_convert(dst_areas, dst_offset, 606d5ac70f0Sopenharmony_ci rate->dst_buf, 0, 607d5ac70f0Sopenharmony_ci channels, dst_frames, 608d5ac70f0Sopenharmony_ci rate->dst_conv_idx); 609d5ac70f0Sopenharmony_ci} 610d5ac70f0Sopenharmony_ci 611d5ac70f0Sopenharmony_cistatic inline void 612d5ac70f0Sopenharmony_cisnd_pcm_rate_write_areas1(snd_pcm_t *pcm, 613d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *areas, 614d5ac70f0Sopenharmony_ci snd_pcm_uframes_t offset, 615d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *slave_areas, 616d5ac70f0Sopenharmony_ci snd_pcm_uframes_t slave_offset) 617d5ac70f0Sopenharmony_ci{ 618d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 619d5ac70f0Sopenharmony_ci do_convert(slave_areas, slave_offset, rate->gen.slave->period_size, 620d5ac70f0Sopenharmony_ci areas, offset, pcm->period_size, 621d5ac70f0Sopenharmony_ci pcm->channels, rate); 622d5ac70f0Sopenharmony_ci} 623d5ac70f0Sopenharmony_ci 624d5ac70f0Sopenharmony_cistatic inline void 625d5ac70f0Sopenharmony_cisnd_pcm_rate_read_areas1(snd_pcm_t *pcm, 626d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *areas, 627d5ac70f0Sopenharmony_ci snd_pcm_uframes_t offset, 628d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *slave_areas, 629d5ac70f0Sopenharmony_ci snd_pcm_uframes_t slave_offset) 630d5ac70f0Sopenharmony_ci{ 631d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 632d5ac70f0Sopenharmony_ci do_convert(areas, offset, pcm->period_size, 633d5ac70f0Sopenharmony_ci slave_areas, slave_offset, rate->gen.slave->period_size, 634d5ac70f0Sopenharmony_ci pcm->channels, rate); 635d5ac70f0Sopenharmony_ci} 636d5ac70f0Sopenharmony_ci 637d5ac70f0Sopenharmony_cistatic inline void snd_pcm_rate_sync_hwptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr) 638d5ac70f0Sopenharmony_ci{ 639d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate; 640d5ac70f0Sopenharmony_ci snd_pcm_sframes_t slave_hw_ptr_diff; 641d5ac70f0Sopenharmony_ci snd_pcm_sframes_t last_slave_hw_ptr_frac; 642d5ac70f0Sopenharmony_ci 643d5ac70f0Sopenharmony_ci if (pcm->stream != SND_PCM_STREAM_PLAYBACK) 644d5ac70f0Sopenharmony_ci return; 645d5ac70f0Sopenharmony_ci 646d5ac70f0Sopenharmony_ci rate = pcm->private_data; 647d5ac70f0Sopenharmony_ci slave_hw_ptr_diff = pcm_frame_diff(slave_hw_ptr, rate->last_slave_hw_ptr, rate->gen.slave->boundary); 648d5ac70f0Sopenharmony_ci if (slave_hw_ptr_diff == 0) 649d5ac70f0Sopenharmony_ci return; 650d5ac70f0Sopenharmony_ci last_slave_hw_ptr_frac = rate->last_slave_hw_ptr % rate->gen.slave->period_size; 651d5ac70f0Sopenharmony_ci /* While handling fraction part fo slave period, rounded value will be 652d5ac70f0Sopenharmony_ci * introduced by input_frames(). 653d5ac70f0Sopenharmony_ci * To eliminate rounding issue on rate->hw_ptr, subtract last rounded 654d5ac70f0Sopenharmony_ci * value from rate->hw_ptr and add new rounded value of present 655d5ac70f0Sopenharmony_ci * slave_hw_ptr fraction part to rate->hw_ptr. Hence, 656d5ac70f0Sopenharmony_ci * rate->hw_ptr += [ (no. of updated slave periods * pcm rate period size) - 657d5ac70f0Sopenharmony_ci * fractional part of last_slave_hw_ptr rounded value + 658d5ac70f0Sopenharmony_ci * fractional part of updated slave hw ptr's rounded value ] 659d5ac70f0Sopenharmony_ci */ 660d5ac70f0Sopenharmony_ci rate->hw_ptr += ( 661d5ac70f0Sopenharmony_ci (((last_slave_hw_ptr_frac + slave_hw_ptr_diff) / rate->gen.slave->period_size) * pcm->period_size) - 662d5ac70f0Sopenharmony_ci rate->ops.input_frames(rate->obj, last_slave_hw_ptr_frac) + 663d5ac70f0Sopenharmony_ci rate->ops.input_frames(rate->obj, (last_slave_hw_ptr_frac + slave_hw_ptr_diff) % rate->gen.slave->period_size)); 664d5ac70f0Sopenharmony_ci rate->last_slave_hw_ptr = slave_hw_ptr; 665d5ac70f0Sopenharmony_ci 666d5ac70f0Sopenharmony_ci rate->hw_ptr %= pcm->boundary; 667d5ac70f0Sopenharmony_ci} 668d5ac70f0Sopenharmony_ci 669d5ac70f0Sopenharmony_cistatic inline void snd_pcm_rate_sync_hwptr(snd_pcm_t *pcm) 670d5ac70f0Sopenharmony_ci{ 671d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 672d5ac70f0Sopenharmony_ci snd_pcm_rate_sync_hwptr0(pcm, *rate->gen.slave->hw.ptr); 673d5ac70f0Sopenharmony_ci} 674d5ac70f0Sopenharmony_ci 675d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hwsync(snd_pcm_t *pcm) 676d5ac70f0Sopenharmony_ci{ 677d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 678d5ac70f0Sopenharmony_ci int err = snd_pcm_hwsync(rate->gen.slave); 679d5ac70f0Sopenharmony_ci if (err < 0) 680d5ac70f0Sopenharmony_ci return err; 681d5ac70f0Sopenharmony_ci snd_pcm_rate_sync_hwptr(pcm); 682d5ac70f0Sopenharmony_ci return 0; 683d5ac70f0Sopenharmony_ci} 684d5ac70f0Sopenharmony_ci 685d5ac70f0Sopenharmony_cistatic snd_pcm_uframes_t snd_pcm_rate_playback_internal_delay(snd_pcm_t *pcm) 686d5ac70f0Sopenharmony_ci{ 687d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 688d5ac70f0Sopenharmony_ci 689d5ac70f0Sopenharmony_ci return pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary); 690d5ac70f0Sopenharmony_ci} 691d5ac70f0Sopenharmony_ci 692d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) 693d5ac70f0Sopenharmony_ci{ 694d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 695d5ac70f0Sopenharmony_ci snd_pcm_sframes_t slave_delay; 696d5ac70f0Sopenharmony_ci int err; 697d5ac70f0Sopenharmony_ci 698d5ac70f0Sopenharmony_ci snd_pcm_rate_hwsync(pcm); 699d5ac70f0Sopenharmony_ci 700d5ac70f0Sopenharmony_ci err = snd_pcm_delay(rate->gen.slave, &slave_delay); 701d5ac70f0Sopenharmony_ci if (err < 0) { 702d5ac70f0Sopenharmony_ci return err; 703d5ac70f0Sopenharmony_ci } 704d5ac70f0Sopenharmony_ci 705d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 706d5ac70f0Sopenharmony_ci *delayp = rate->ops.input_frames(rate->obj, slave_delay) 707d5ac70f0Sopenharmony_ci + snd_pcm_rate_playback_internal_delay(pcm); 708d5ac70f0Sopenharmony_ci } else { 709d5ac70f0Sopenharmony_ci *delayp = rate->ops.output_frames(rate->obj, slave_delay) 710d5ac70f0Sopenharmony_ci + snd_pcm_mmap_capture_delay(pcm); 711d5ac70f0Sopenharmony_ci } 712d5ac70f0Sopenharmony_ci return 0; 713d5ac70f0Sopenharmony_ci} 714d5ac70f0Sopenharmony_ci 715d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_prepare(snd_pcm_t *pcm) 716d5ac70f0Sopenharmony_ci{ 717d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 718d5ac70f0Sopenharmony_ci int err; 719d5ac70f0Sopenharmony_ci 720d5ac70f0Sopenharmony_ci err = snd_pcm_prepare(rate->gen.slave); 721d5ac70f0Sopenharmony_ci if (err < 0) 722d5ac70f0Sopenharmony_ci return err; 723d5ac70f0Sopenharmony_ci *pcm->hw.ptr = 0; 724d5ac70f0Sopenharmony_ci *pcm->appl.ptr = 0; 725d5ac70f0Sopenharmony_ci rate->last_slave_hw_ptr = 0; 726d5ac70f0Sopenharmony_ci err = snd_pcm_rate_init(pcm); 727d5ac70f0Sopenharmony_ci if (err < 0) 728d5ac70f0Sopenharmony_ci return err; 729d5ac70f0Sopenharmony_ci return 0; 730d5ac70f0Sopenharmony_ci} 731d5ac70f0Sopenharmony_ci 732d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_reset(snd_pcm_t *pcm) 733d5ac70f0Sopenharmony_ci{ 734d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 735d5ac70f0Sopenharmony_ci int err; 736d5ac70f0Sopenharmony_ci err = snd_pcm_reset(rate->gen.slave); 737d5ac70f0Sopenharmony_ci if (err < 0) 738d5ac70f0Sopenharmony_ci return err; 739d5ac70f0Sopenharmony_ci *pcm->hw.ptr = 0; 740d5ac70f0Sopenharmony_ci *pcm->appl.ptr = 0; 741d5ac70f0Sopenharmony_ci rate->last_slave_hw_ptr = 0; 742d5ac70f0Sopenharmony_ci err = snd_pcm_rate_init(pcm); 743d5ac70f0Sopenharmony_ci if (err < 0) 744d5ac70f0Sopenharmony_ci return err; 745d5ac70f0Sopenharmony_ci return 0; 746d5ac70f0Sopenharmony_ci} 747d5ac70f0Sopenharmony_ci 748d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_rewindable(snd_pcm_t *pcm ATTRIBUTE_UNUSED) 749d5ac70f0Sopenharmony_ci{ 750d5ac70f0Sopenharmony_ci return 0; 751d5ac70f0Sopenharmony_ci} 752d5ac70f0Sopenharmony_ci 753d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED) 754d5ac70f0Sopenharmony_ci{ 755d5ac70f0Sopenharmony_ci return 0; 756d5ac70f0Sopenharmony_ci} 757d5ac70f0Sopenharmony_ci 758d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_rewind(snd_pcm_t *pcm ATTRIBUTE_UNUSED, 759d5ac70f0Sopenharmony_ci snd_pcm_uframes_t frames ATTRIBUTE_UNUSED) 760d5ac70f0Sopenharmony_ci{ 761d5ac70f0Sopenharmony_ci return 0; 762d5ac70f0Sopenharmony_ci} 763d5ac70f0Sopenharmony_ci 764d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_forward(snd_pcm_t *pcm ATTRIBUTE_UNUSED, 765d5ac70f0Sopenharmony_ci snd_pcm_uframes_t frames ATTRIBUTE_UNUSED) 766d5ac70f0Sopenharmony_ci{ 767d5ac70f0Sopenharmony_ci return 0; 768d5ac70f0Sopenharmony_ci} 769d5ac70f0Sopenharmony_ci 770d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_commit_area(snd_pcm_t *pcm, snd_pcm_rate_t *rate, 771d5ac70f0Sopenharmony_ci snd_pcm_uframes_t appl_offset, 772d5ac70f0Sopenharmony_ci snd_pcm_uframes_t size ATTRIBUTE_UNUSED, 773d5ac70f0Sopenharmony_ci snd_pcm_uframes_t slave_size) 774d5ac70f0Sopenharmony_ci{ 775d5ac70f0Sopenharmony_ci snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset; 776d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *areas; 777d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *slave_areas; 778d5ac70f0Sopenharmony_ci snd_pcm_uframes_t slave_offset, xfer; 779d5ac70f0Sopenharmony_ci snd_pcm_uframes_t slave_frames = ULONG_MAX; 780d5ac70f0Sopenharmony_ci snd_pcm_sframes_t result; 781d5ac70f0Sopenharmony_ci 782d5ac70f0Sopenharmony_ci areas = snd_pcm_mmap_areas(pcm); 783d5ac70f0Sopenharmony_ci /* 784d5ac70f0Sopenharmony_ci * Because snd_pcm_rate_write_areas1() below will convert a full source period 785d5ac70f0Sopenharmony_ci * then there had better be a full period available in the current buffer. 786d5ac70f0Sopenharmony_ci */ 787d5ac70f0Sopenharmony_ci if (cont >= pcm->period_size) { 788d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames); 789d5ac70f0Sopenharmony_ci if (result < 0) 790d5ac70f0Sopenharmony_ci return result; 791d5ac70f0Sopenharmony_ci /* 792d5ac70f0Sopenharmony_ci * Because snd_pcm_rate_write_areas1() below will convert to a full slave period 793d5ac70f0Sopenharmony_ci * then there had better be a full slave period available in the slave buffer. 794d5ac70f0Sopenharmony_ci */ 795d5ac70f0Sopenharmony_ci if (slave_frames < rate->gen.slave->period_size) { 796d5ac70f0Sopenharmony_ci snd_pcm_rate_write_areas1(pcm, areas, appl_offset, rate->sareas, 0); 797d5ac70f0Sopenharmony_ci goto __partial; 798d5ac70f0Sopenharmony_ci } 799d5ac70f0Sopenharmony_ci snd_pcm_rate_write_areas1(pcm, areas, appl_offset, 800d5ac70f0Sopenharmony_ci slave_areas, slave_offset); 801d5ac70f0Sopenharmony_ci /* Only commit the requested slave_size, even if more was actually converted */ 802d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, slave_size); 803d5ac70f0Sopenharmony_ci if (result < (snd_pcm_sframes_t)slave_size) { 804d5ac70f0Sopenharmony_ci if (result < 0) 805d5ac70f0Sopenharmony_ci return result; 806d5ac70f0Sopenharmony_ci result = snd_pcm_rewind(rate->gen.slave, result); 807d5ac70f0Sopenharmony_ci if (result < 0) 808d5ac70f0Sopenharmony_ci return result; 809d5ac70f0Sopenharmony_ci return 0; 810d5ac70f0Sopenharmony_ci } 811d5ac70f0Sopenharmony_ci } else { 812d5ac70f0Sopenharmony_ci snd_pcm_areas_copy(rate->pareas, 0, 813d5ac70f0Sopenharmony_ci areas, appl_offset, 814d5ac70f0Sopenharmony_ci pcm->channels, cont, 815d5ac70f0Sopenharmony_ci pcm->format); 816d5ac70f0Sopenharmony_ci snd_pcm_areas_copy(rate->pareas, cont, 817d5ac70f0Sopenharmony_ci areas, 0, 818d5ac70f0Sopenharmony_ci pcm->channels, pcm->period_size - cont, 819d5ac70f0Sopenharmony_ci pcm->format); 820d5ac70f0Sopenharmony_ci 821d5ac70f0Sopenharmony_ci snd_pcm_rate_write_areas1(pcm, rate->pareas, 0, rate->sareas, 0); 822d5ac70f0Sopenharmony_ci 823d5ac70f0Sopenharmony_ci /* ok, commit first fragment */ 824d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames); 825d5ac70f0Sopenharmony_ci if (result < 0) 826d5ac70f0Sopenharmony_ci return result; 827d5ac70f0Sopenharmony_ci __partial: 828d5ac70f0Sopenharmony_ci cont = slave_frames; 829d5ac70f0Sopenharmony_ci if (cont > slave_size) 830d5ac70f0Sopenharmony_ci cont = slave_size; 831d5ac70f0Sopenharmony_ci snd_pcm_areas_copy(slave_areas, slave_offset, 832d5ac70f0Sopenharmony_ci rate->sareas, 0, 833d5ac70f0Sopenharmony_ci pcm->channels, cont, 834d5ac70f0Sopenharmony_ci rate->gen.slave->format); 835d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont); 836d5ac70f0Sopenharmony_ci if (result < (snd_pcm_sframes_t)cont) { 837d5ac70f0Sopenharmony_ci if (result < 0) 838d5ac70f0Sopenharmony_ci return result; 839d5ac70f0Sopenharmony_ci result = snd_pcm_rewind(rate->gen.slave, result); 840d5ac70f0Sopenharmony_ci if (result < 0) 841d5ac70f0Sopenharmony_ci return result; 842d5ac70f0Sopenharmony_ci return 0; 843d5ac70f0Sopenharmony_ci } 844d5ac70f0Sopenharmony_ci xfer = cont; 845d5ac70f0Sopenharmony_ci 846d5ac70f0Sopenharmony_ci if (xfer == slave_size) 847d5ac70f0Sopenharmony_ci goto commit_done; 848d5ac70f0Sopenharmony_ci 849d5ac70f0Sopenharmony_ci /* commit second fragment */ 850d5ac70f0Sopenharmony_ci cont = slave_size - cont; 851d5ac70f0Sopenharmony_ci slave_frames = cont; 852d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames); 853d5ac70f0Sopenharmony_ci if (result < 0) 854d5ac70f0Sopenharmony_ci return result; 855d5ac70f0Sopenharmony_ci#if 0 856d5ac70f0Sopenharmony_ci if (slave_offset) { 857d5ac70f0Sopenharmony_ci SNDERR("non-zero slave_offset %ld", slave_offset); 858d5ac70f0Sopenharmony_ci return -EIO; 859d5ac70f0Sopenharmony_ci } 860d5ac70f0Sopenharmony_ci#endif 861d5ac70f0Sopenharmony_ci snd_pcm_areas_copy(slave_areas, slave_offset, 862d5ac70f0Sopenharmony_ci rate->sareas, xfer, 863d5ac70f0Sopenharmony_ci pcm->channels, cont, 864d5ac70f0Sopenharmony_ci rate->gen.slave->format); 865d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont); 866d5ac70f0Sopenharmony_ci if (result < (snd_pcm_sframes_t)cont) { 867d5ac70f0Sopenharmony_ci if (result < 0) 868d5ac70f0Sopenharmony_ci return result; 869d5ac70f0Sopenharmony_ci result = snd_pcm_rewind(rate->gen.slave, result + xfer); 870d5ac70f0Sopenharmony_ci if (result < 0) 871d5ac70f0Sopenharmony_ci return result; 872d5ac70f0Sopenharmony_ci return 0; 873d5ac70f0Sopenharmony_ci } 874d5ac70f0Sopenharmony_ci } 875d5ac70f0Sopenharmony_ci 876d5ac70f0Sopenharmony_ci commit_done: 877d5ac70f0Sopenharmony_ci if (rate->start_pending) { 878d5ac70f0Sopenharmony_ci /* we have pending start-trigger. let's issue it now */ 879d5ac70f0Sopenharmony_ci snd_pcm_start(rate->gen.slave); 880d5ac70f0Sopenharmony_ci rate->start_pending = 0; 881d5ac70f0Sopenharmony_ci } 882d5ac70f0Sopenharmony_ci return 1; 883d5ac70f0Sopenharmony_ci} 884d5ac70f0Sopenharmony_ci 885d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_commit_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t appl_offset) 886d5ac70f0Sopenharmony_ci{ 887d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 888d5ac70f0Sopenharmony_ci 889d5ac70f0Sopenharmony_ci return snd_pcm_rate_commit_area(pcm, rate, appl_offset, pcm->period_size, 890d5ac70f0Sopenharmony_ci rate->gen.slave->period_size); 891d5ac70f0Sopenharmony_ci} 892d5ac70f0Sopenharmony_ci 893d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_grab_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t hw_offset) 894d5ac70f0Sopenharmony_ci{ 895d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 896d5ac70f0Sopenharmony_ci snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset; 897d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *areas; 898d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *slave_areas; 899d5ac70f0Sopenharmony_ci snd_pcm_uframes_t slave_offset, xfer; 900d5ac70f0Sopenharmony_ci snd_pcm_uframes_t slave_frames = ULONG_MAX; 901d5ac70f0Sopenharmony_ci snd_pcm_sframes_t result; 902d5ac70f0Sopenharmony_ci 903d5ac70f0Sopenharmony_ci areas = snd_pcm_mmap_areas(pcm); 904d5ac70f0Sopenharmony_ci if (cont >= pcm->period_size) { 905d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames); 906d5ac70f0Sopenharmony_ci if (result < 0) 907d5ac70f0Sopenharmony_ci return result; 908d5ac70f0Sopenharmony_ci if (slave_frames < rate->gen.slave->period_size) 909d5ac70f0Sopenharmony_ci goto __partial; 910d5ac70f0Sopenharmony_ci snd_pcm_rate_read_areas1(pcm, areas, hw_offset, 911d5ac70f0Sopenharmony_ci slave_areas, slave_offset); 912d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, rate->gen.slave->period_size); 913d5ac70f0Sopenharmony_ci if (result < (snd_pcm_sframes_t)rate->gen.slave->period_size) { 914d5ac70f0Sopenharmony_ci if (result < 0) 915d5ac70f0Sopenharmony_ci return result; 916d5ac70f0Sopenharmony_ci result = snd_pcm_rewind(rate->gen.slave, result); 917d5ac70f0Sopenharmony_ci if (result < 0) 918d5ac70f0Sopenharmony_ci return result; 919d5ac70f0Sopenharmony_ci return 0; 920d5ac70f0Sopenharmony_ci } 921d5ac70f0Sopenharmony_ci } else { 922d5ac70f0Sopenharmony_ci /* ok, grab first fragment */ 923d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames); 924d5ac70f0Sopenharmony_ci if (result < 0) 925d5ac70f0Sopenharmony_ci return result; 926d5ac70f0Sopenharmony_ci __partial: 927d5ac70f0Sopenharmony_ci cont = slave_frames; 928d5ac70f0Sopenharmony_ci if (cont > rate->gen.slave->period_size) 929d5ac70f0Sopenharmony_ci cont = rate->gen.slave->period_size; 930d5ac70f0Sopenharmony_ci snd_pcm_areas_copy(rate->sareas, 0, 931d5ac70f0Sopenharmony_ci slave_areas, slave_offset, 932d5ac70f0Sopenharmony_ci pcm->channels, cont, 933d5ac70f0Sopenharmony_ci rate->gen.slave->format); 934d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont); 935d5ac70f0Sopenharmony_ci if (result < (snd_pcm_sframes_t)cont) { 936d5ac70f0Sopenharmony_ci if (result < 0) 937d5ac70f0Sopenharmony_ci return result; 938d5ac70f0Sopenharmony_ci result = snd_pcm_rewind(rate->gen.slave, result); 939d5ac70f0Sopenharmony_ci if (result < 0) 940d5ac70f0Sopenharmony_ci return result; 941d5ac70f0Sopenharmony_ci return 0; 942d5ac70f0Sopenharmony_ci } 943d5ac70f0Sopenharmony_ci xfer = cont; 944d5ac70f0Sopenharmony_ci 945d5ac70f0Sopenharmony_ci if (xfer == rate->gen.slave->period_size) 946d5ac70f0Sopenharmony_ci goto __transfer; 947d5ac70f0Sopenharmony_ci 948d5ac70f0Sopenharmony_ci /* grab second fragment */ 949d5ac70f0Sopenharmony_ci cont = rate->gen.slave->period_size - cont; 950d5ac70f0Sopenharmony_ci slave_frames = cont; 951d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames); 952d5ac70f0Sopenharmony_ci if (result < 0) 953d5ac70f0Sopenharmony_ci return result; 954d5ac70f0Sopenharmony_ci#if 0 955d5ac70f0Sopenharmony_ci if (slave_offset) { 956d5ac70f0Sopenharmony_ci SNDERR("non-zero slave_offset %ld", slave_offset); 957d5ac70f0Sopenharmony_ci return -EIO; 958d5ac70f0Sopenharmony_ci } 959d5ac70f0Sopenharmony_ci#endif 960d5ac70f0Sopenharmony_ci snd_pcm_areas_copy(rate->sareas, xfer, 961d5ac70f0Sopenharmony_ci slave_areas, slave_offset, 962d5ac70f0Sopenharmony_ci pcm->channels, cont, 963d5ac70f0Sopenharmony_ci rate->gen.slave->format); 964d5ac70f0Sopenharmony_ci result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont); 965d5ac70f0Sopenharmony_ci if (result < (snd_pcm_sframes_t)cont) { 966d5ac70f0Sopenharmony_ci if (result < 0) 967d5ac70f0Sopenharmony_ci return result; 968d5ac70f0Sopenharmony_ci result = snd_pcm_rewind(rate->gen.slave, result + xfer); 969d5ac70f0Sopenharmony_ci if (result < 0) 970d5ac70f0Sopenharmony_ci return result; 971d5ac70f0Sopenharmony_ci return 0; 972d5ac70f0Sopenharmony_ci } 973d5ac70f0Sopenharmony_ci 974d5ac70f0Sopenharmony_ci __transfer: 975d5ac70f0Sopenharmony_ci cont = pcm->buffer_size - hw_offset; 976d5ac70f0Sopenharmony_ci if (cont >= pcm->period_size) { 977d5ac70f0Sopenharmony_ci snd_pcm_rate_read_areas1(pcm, areas, hw_offset, 978d5ac70f0Sopenharmony_ci rate->sareas, 0); 979d5ac70f0Sopenharmony_ci } else { 980d5ac70f0Sopenharmony_ci snd_pcm_rate_read_areas1(pcm, 981d5ac70f0Sopenharmony_ci rate->pareas, 0, 982d5ac70f0Sopenharmony_ci rate->sareas, 0); 983d5ac70f0Sopenharmony_ci snd_pcm_areas_copy(areas, hw_offset, 984d5ac70f0Sopenharmony_ci rate->pareas, 0, 985d5ac70f0Sopenharmony_ci pcm->channels, cont, 986d5ac70f0Sopenharmony_ci pcm->format); 987d5ac70f0Sopenharmony_ci snd_pcm_areas_copy(areas, 0, 988d5ac70f0Sopenharmony_ci rate->pareas, cont, 989d5ac70f0Sopenharmony_ci pcm->channels, pcm->period_size - cont, 990d5ac70f0Sopenharmony_ci pcm->format); 991d5ac70f0Sopenharmony_ci } 992d5ac70f0Sopenharmony_ci } 993d5ac70f0Sopenharmony_ci return 1; 994d5ac70f0Sopenharmony_ci} 995d5ac70f0Sopenharmony_ci 996d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_sync_playback_area(snd_pcm_t *pcm, snd_pcm_uframes_t appl_ptr) 997d5ac70f0Sopenharmony_ci{ 998d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 999d5ac70f0Sopenharmony_ci snd_pcm_t *slave = rate->gen.slave; 1000d5ac70f0Sopenharmony_ci snd_pcm_uframes_t xfer; 1001d5ac70f0Sopenharmony_ci snd_pcm_sframes_t slave_size; 1002d5ac70f0Sopenharmony_ci int err; 1003d5ac70f0Sopenharmony_ci 1004d5ac70f0Sopenharmony_ci slave_size = snd_pcm_avail_update(slave); 1005d5ac70f0Sopenharmony_ci if (slave_size < 0) 1006d5ac70f0Sopenharmony_ci return slave_size; 1007d5ac70f0Sopenharmony_ci 1008d5ac70f0Sopenharmony_ci xfer = pcm_frame_diff(appl_ptr, rate->last_commit_ptr, pcm->boundary); 1009d5ac70f0Sopenharmony_ci while (xfer >= pcm->period_size && 1010d5ac70f0Sopenharmony_ci (snd_pcm_uframes_t)slave_size >= rate->gen.slave->period_size) { 1011d5ac70f0Sopenharmony_ci err = snd_pcm_rate_commit_next_period(pcm, rate->last_commit_ptr % pcm->buffer_size); 1012d5ac70f0Sopenharmony_ci if (err == 0) 1013d5ac70f0Sopenharmony_ci break; 1014d5ac70f0Sopenharmony_ci if (err < 0) 1015d5ac70f0Sopenharmony_ci return err; 1016d5ac70f0Sopenharmony_ci xfer -= pcm->period_size; 1017d5ac70f0Sopenharmony_ci slave_size -= rate->gen.slave->period_size; 1018d5ac70f0Sopenharmony_ci rate->last_commit_ptr += pcm->period_size; 1019d5ac70f0Sopenharmony_ci if (rate->last_commit_ptr >= pcm->boundary) 1020d5ac70f0Sopenharmony_ci rate->last_commit_ptr -= pcm->boundary; 1021d5ac70f0Sopenharmony_ci } 1022d5ac70f0Sopenharmony_ci return 0; 1023d5ac70f0Sopenharmony_ci} 1024d5ac70f0Sopenharmony_ci 1025d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_mmap_commit(snd_pcm_t *pcm, 1026d5ac70f0Sopenharmony_ci snd_pcm_uframes_t offset ATTRIBUTE_UNUSED, 1027d5ac70f0Sopenharmony_ci snd_pcm_uframes_t size) 1028d5ac70f0Sopenharmony_ci{ 1029d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 1030d5ac70f0Sopenharmony_ci int err; 1031d5ac70f0Sopenharmony_ci 1032d5ac70f0Sopenharmony_ci if (size == 0) 1033d5ac70f0Sopenharmony_ci return 0; 1034d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 1035d5ac70f0Sopenharmony_ci err = snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr + size); 1036d5ac70f0Sopenharmony_ci if (err < 0) 1037d5ac70f0Sopenharmony_ci return err; 1038d5ac70f0Sopenharmony_ci } 1039d5ac70f0Sopenharmony_ci snd_pcm_mmap_appl_forward(pcm, size); 1040d5ac70f0Sopenharmony_ci return size; 1041d5ac70f0Sopenharmony_ci} 1042d5ac70f0Sopenharmony_ci 1043d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_avail_update_capture(snd_pcm_t *pcm, 1044d5ac70f0Sopenharmony_ci snd_pcm_sframes_t slave_size) 1045d5ac70f0Sopenharmony_ci{ 1046d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 1047d5ac70f0Sopenharmony_ci snd_pcm_t *slave = rate->gen.slave; 1048d5ac70f0Sopenharmony_ci snd_pcm_uframes_t xfer, hw_offset, size; 1049d5ac70f0Sopenharmony_ci 1050d5ac70f0Sopenharmony_ci xfer = snd_pcm_mmap_capture_avail(pcm); 1051d5ac70f0Sopenharmony_ci size = pcm->buffer_size - xfer; 1052d5ac70f0Sopenharmony_ci hw_offset = snd_pcm_mmap_hw_offset(pcm); 1053d5ac70f0Sopenharmony_ci while (size >= pcm->period_size && 1054d5ac70f0Sopenharmony_ci (snd_pcm_uframes_t)slave_size >= slave->period_size) { 1055d5ac70f0Sopenharmony_ci int err = snd_pcm_rate_grab_next_period(pcm, hw_offset); 1056d5ac70f0Sopenharmony_ci if (err < 0) 1057d5ac70f0Sopenharmony_ci return err; 1058d5ac70f0Sopenharmony_ci if (err == 0) 1059d5ac70f0Sopenharmony_ci return (snd_pcm_sframes_t)xfer; 1060d5ac70f0Sopenharmony_ci xfer += pcm->period_size; 1061d5ac70f0Sopenharmony_ci size -= pcm->period_size; 1062d5ac70f0Sopenharmony_ci slave_size -= slave->period_size; 1063d5ac70f0Sopenharmony_ci hw_offset += pcm->period_size; 1064d5ac70f0Sopenharmony_ci hw_offset %= pcm->buffer_size; 1065d5ac70f0Sopenharmony_ci snd_pcm_mmap_hw_forward(pcm, pcm->period_size); 1066d5ac70f0Sopenharmony_ci } 1067d5ac70f0Sopenharmony_ci return (snd_pcm_sframes_t)xfer; 1068d5ac70f0Sopenharmony_ci} 1069d5ac70f0Sopenharmony_ci 1070d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_avail_update(snd_pcm_t *pcm) 1071d5ac70f0Sopenharmony_ci{ 1072d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 1073d5ac70f0Sopenharmony_ci snd_pcm_sframes_t slave_size; 1074d5ac70f0Sopenharmony_ci 1075d5ac70f0Sopenharmony_ci slave_size = snd_pcm_avail_update(rate->gen.slave); 1076d5ac70f0Sopenharmony_ci if (slave_size < 0) 1077d5ac70f0Sopenharmony_ci return slave_size; 1078d5ac70f0Sopenharmony_ci 1079d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_CAPTURE) 1080d5ac70f0Sopenharmony_ci return snd_pcm_rate_avail_update_capture(pcm, slave_size); 1081d5ac70f0Sopenharmony_ci 1082d5ac70f0Sopenharmony_ci snd_pcm_rate_sync_hwptr(pcm); 1083d5ac70f0Sopenharmony_ci snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr); 1084d5ac70f0Sopenharmony_ci return snd_pcm_mmap_avail(pcm); 1085d5ac70f0Sopenharmony_ci} 1086d5ac70f0Sopenharmony_ci 1087d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_htimestamp(snd_pcm_t *pcm, 1088d5ac70f0Sopenharmony_ci snd_pcm_uframes_t *avail, 1089d5ac70f0Sopenharmony_ci snd_htimestamp_t *tstamp) 1090d5ac70f0Sopenharmony_ci{ 1091d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 1092d5ac70f0Sopenharmony_ci snd_pcm_sframes_t avail1; 1093d5ac70f0Sopenharmony_ci snd_pcm_uframes_t tmp; 1094d5ac70f0Sopenharmony_ci int ok = 0, err; 1095d5ac70f0Sopenharmony_ci 1096d5ac70f0Sopenharmony_ci while (1) { 1097d5ac70f0Sopenharmony_ci /* the position is from this plugin itself */ 1098d5ac70f0Sopenharmony_ci avail1 = snd_pcm_avail_update(pcm); 1099d5ac70f0Sopenharmony_ci if (avail1 < 0) 1100d5ac70f0Sopenharmony_ci return avail1; 1101d5ac70f0Sopenharmony_ci if (ok && (snd_pcm_uframes_t)avail1 == *avail) 1102d5ac70f0Sopenharmony_ci break; 1103d5ac70f0Sopenharmony_ci *avail = avail1; 1104d5ac70f0Sopenharmony_ci /* timestamp is taken from the slave PCM */ 1105d5ac70f0Sopenharmony_ci err = snd_pcm_htimestamp(rate->gen.slave, &tmp, tstamp); 1106d5ac70f0Sopenharmony_ci if (err < 0) 1107d5ac70f0Sopenharmony_ci return err; 1108d5ac70f0Sopenharmony_ci ok = 1; 1109d5ac70f0Sopenharmony_ci } 1110d5ac70f0Sopenharmony_ci return 0; 1111d5ac70f0Sopenharmony_ci} 1112d5ac70f0Sopenharmony_ci 1113d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) 1114d5ac70f0Sopenharmony_ci{ 1115d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 1116d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 1117d5ac70f0Sopenharmony_ci /* Try to sync as much as possible */ 1118d5ac70f0Sopenharmony_ci snd_pcm_rate_hwsync(pcm); 1119d5ac70f0Sopenharmony_ci snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr); 1120d5ac70f0Sopenharmony_ci } 1121d5ac70f0Sopenharmony_ci return snd_pcm_poll_descriptors_revents(rate->gen.slave, pfds, nfds, revents); 1122d5ac70f0Sopenharmony_ci} 1123d5ac70f0Sopenharmony_ci 1124d5ac70f0Sopenharmony_ci/* locking */ 1125d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_drain(snd_pcm_t *pcm) 1126d5ac70f0Sopenharmony_ci{ 1127d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 1128d5ac70f0Sopenharmony_ci 1129d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 1130d5ac70f0Sopenharmony_ci /* commit the remaining fraction (if any) */ 1131d5ac70f0Sopenharmony_ci snd_pcm_uframes_t size, ofs, saved_avail_min; 1132d5ac70f0Sopenharmony_ci snd_pcm_sw_params_t sw_params; 1133d5ac70f0Sopenharmony_ci int commit_err = 0; 1134d5ac70f0Sopenharmony_ci 1135d5ac70f0Sopenharmony_ci __snd_pcm_lock(pcm); 1136d5ac70f0Sopenharmony_ci /* temporarily set avail_min to one */ 1137d5ac70f0Sopenharmony_ci sw_params = rate->sw_params; 1138d5ac70f0Sopenharmony_ci saved_avail_min = sw_params.avail_min; 1139d5ac70f0Sopenharmony_ci sw_params.avail_min = 1; 1140d5ac70f0Sopenharmony_ci snd_pcm_sw_params(rate->gen.slave, &sw_params); 1141d5ac70f0Sopenharmony_ci 1142d5ac70f0Sopenharmony_ci size = pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary); 1143d5ac70f0Sopenharmony_ci ofs = rate->last_commit_ptr % pcm->buffer_size; 1144d5ac70f0Sopenharmony_ci while (size > 0) { 1145d5ac70f0Sopenharmony_ci snd_pcm_uframes_t psize, spsize; 1146d5ac70f0Sopenharmony_ci int err; 1147d5ac70f0Sopenharmony_ci 1148d5ac70f0Sopenharmony_ci err = __snd_pcm_wait_in_lock(rate->gen.slave, SND_PCM_WAIT_DRAIN); 1149d5ac70f0Sopenharmony_ci if (err < 0) 1150d5ac70f0Sopenharmony_ci break; 1151d5ac70f0Sopenharmony_ci if (size > pcm->period_size) { 1152d5ac70f0Sopenharmony_ci psize = pcm->period_size; 1153d5ac70f0Sopenharmony_ci spsize = rate->gen.slave->period_size; 1154d5ac70f0Sopenharmony_ci } else { 1155d5ac70f0Sopenharmony_ci psize = size; 1156d5ac70f0Sopenharmony_ci spsize = rate->ops.output_frames(rate->obj, size); 1157d5ac70f0Sopenharmony_ci if (! spsize) 1158d5ac70f0Sopenharmony_ci break; 1159d5ac70f0Sopenharmony_ci } 1160d5ac70f0Sopenharmony_ci commit_err = snd_pcm_rate_commit_area(pcm, rate, ofs, 1161d5ac70f0Sopenharmony_ci psize, spsize); 1162d5ac70f0Sopenharmony_ci if (commit_err == 1) { 1163d5ac70f0Sopenharmony_ci rate->last_commit_ptr += psize; 1164d5ac70f0Sopenharmony_ci if (rate->last_commit_ptr >= pcm->boundary) 1165d5ac70f0Sopenharmony_ci rate->last_commit_ptr -= pcm->boundary; 1166d5ac70f0Sopenharmony_ci } else if (commit_err == 0) { 1167d5ac70f0Sopenharmony_ci if (pcm->mode & SND_PCM_NONBLOCK) { 1168d5ac70f0Sopenharmony_ci commit_err = -EAGAIN; 1169d5ac70f0Sopenharmony_ci break; 1170d5ac70f0Sopenharmony_ci } 1171d5ac70f0Sopenharmony_ci continue; 1172d5ac70f0Sopenharmony_ci } else 1173d5ac70f0Sopenharmony_ci break; 1174d5ac70f0Sopenharmony_ci 1175d5ac70f0Sopenharmony_ci ofs = (ofs + psize) % pcm->buffer_size; 1176d5ac70f0Sopenharmony_ci size -= psize; 1177d5ac70f0Sopenharmony_ci } 1178d5ac70f0Sopenharmony_ci sw_params.avail_min = saved_avail_min; 1179d5ac70f0Sopenharmony_ci snd_pcm_sw_params(rate->gen.slave, &sw_params); 1180d5ac70f0Sopenharmony_ci __snd_pcm_unlock(pcm); 1181d5ac70f0Sopenharmony_ci if (commit_err < 0) 1182d5ac70f0Sopenharmony_ci return commit_err; 1183d5ac70f0Sopenharmony_ci } 1184d5ac70f0Sopenharmony_ci return snd_pcm_drain(rate->gen.slave); 1185d5ac70f0Sopenharmony_ci} 1186d5ac70f0Sopenharmony_ci 1187d5ac70f0Sopenharmony_cistatic snd_pcm_state_t snd_pcm_rate_state(snd_pcm_t *pcm) 1188d5ac70f0Sopenharmony_ci{ 1189d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 1190d5ac70f0Sopenharmony_ci if (rate->start_pending) /* pseudo-state */ 1191d5ac70f0Sopenharmony_ci return SND_PCM_STATE_RUNNING; 1192d5ac70f0Sopenharmony_ci return snd_pcm_state(rate->gen.slave); 1193d5ac70f0Sopenharmony_ci} 1194d5ac70f0Sopenharmony_ci 1195d5ac70f0Sopenharmony_ci 1196d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_start(snd_pcm_t *pcm) 1197d5ac70f0Sopenharmony_ci{ 1198d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 1199d5ac70f0Sopenharmony_ci snd_pcm_sframes_t avail; 1200d5ac70f0Sopenharmony_ci 1201d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_CAPTURE) 1202d5ac70f0Sopenharmony_ci return snd_pcm_start(rate->gen.slave); 1203d5ac70f0Sopenharmony_ci 1204d5ac70f0Sopenharmony_ci if (snd_pcm_state(rate->gen.slave) != SND_PCM_STATE_PREPARED) 1205d5ac70f0Sopenharmony_ci return -EBADFD; 1206d5ac70f0Sopenharmony_ci 1207d5ac70f0Sopenharmony_ci gettimestamp(&rate->trigger_tstamp, pcm->tstamp_type); 1208d5ac70f0Sopenharmony_ci 1209d5ac70f0Sopenharmony_ci avail = snd_pcm_mmap_playback_hw_avail(rate->gen.slave); 1210d5ac70f0Sopenharmony_ci if (avail < 0) /* can't happen on healthy drivers */ 1211d5ac70f0Sopenharmony_ci return -EBADFD; 1212d5ac70f0Sopenharmony_ci 1213d5ac70f0Sopenharmony_ci if (avail == 0) { 1214d5ac70f0Sopenharmony_ci /* postpone the trigger since we have no data committed yet */ 1215d5ac70f0Sopenharmony_ci rate->start_pending = 1; 1216d5ac70f0Sopenharmony_ci return 0; 1217d5ac70f0Sopenharmony_ci } 1218d5ac70f0Sopenharmony_ci rate->start_pending = 0; 1219d5ac70f0Sopenharmony_ci return snd_pcm_start(rate->gen.slave); 1220d5ac70f0Sopenharmony_ci} 1221d5ac70f0Sopenharmony_ci 1222d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_status(snd_pcm_t *pcm, snd_pcm_status_t * status) 1223d5ac70f0Sopenharmony_ci{ 1224d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 1225d5ac70f0Sopenharmony_ci snd_pcm_sframes_t err; 1226d5ac70f0Sopenharmony_ci 1227d5ac70f0Sopenharmony_ci err = snd_pcm_status(rate->gen.slave, status); 1228d5ac70f0Sopenharmony_ci if (err < 0) 1229d5ac70f0Sopenharmony_ci return err; 1230d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 1231d5ac70f0Sopenharmony_ci if (rate->start_pending) 1232d5ac70f0Sopenharmony_ci status->state = SND_PCM_STATE_RUNNING; 1233d5ac70f0Sopenharmony_ci status->trigger_tstamp = rate->trigger_tstamp; 1234d5ac70f0Sopenharmony_ci } 1235d5ac70f0Sopenharmony_ci snd_pcm_rate_sync_hwptr0(pcm, status->hw_ptr); 1236d5ac70f0Sopenharmony_ci status->appl_ptr = *pcm->appl.ptr; 1237d5ac70f0Sopenharmony_ci status->hw_ptr = *pcm->hw.ptr; 1238d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 1239d5ac70f0Sopenharmony_ci status->delay = rate->ops.input_frames(rate->obj, status->delay) 1240d5ac70f0Sopenharmony_ci + snd_pcm_rate_playback_internal_delay(pcm); 1241d5ac70f0Sopenharmony_ci status->avail = snd_pcm_mmap_playback_avail(pcm); 1242d5ac70f0Sopenharmony_ci status->avail_max = rate->ops.input_frames(rate->obj, status->avail_max); 1243d5ac70f0Sopenharmony_ci } else { 1244d5ac70f0Sopenharmony_ci status->delay = rate->ops.output_frames(rate->obj, status->delay) 1245d5ac70f0Sopenharmony_ci + snd_pcm_mmap_capture_delay(pcm); 1246d5ac70f0Sopenharmony_ci status->avail = snd_pcm_mmap_capture_avail(pcm); 1247d5ac70f0Sopenharmony_ci status->avail_max = rate->ops.output_frames(rate->obj, status->avail_max); 1248d5ac70f0Sopenharmony_ci } 1249d5ac70f0Sopenharmony_ci return 0; 1250d5ac70f0Sopenharmony_ci} 1251d5ac70f0Sopenharmony_ci 1252d5ac70f0Sopenharmony_cistatic void snd_pcm_rate_dump(snd_pcm_t *pcm, snd_output_t *out) 1253d5ac70f0Sopenharmony_ci{ 1254d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 1255d5ac70f0Sopenharmony_ci if (rate->sformat == SND_PCM_FORMAT_UNKNOWN) 1256d5ac70f0Sopenharmony_ci snd_output_printf(out, "Rate conversion PCM (%d)\n", 1257d5ac70f0Sopenharmony_ci rate->srate); 1258d5ac70f0Sopenharmony_ci else 1259d5ac70f0Sopenharmony_ci snd_output_printf(out, "Rate conversion PCM (%d, sformat=%s)\n", 1260d5ac70f0Sopenharmony_ci rate->srate, 1261d5ac70f0Sopenharmony_ci snd_pcm_format_name(rate->sformat)); 1262d5ac70f0Sopenharmony_ci if (rate->ops.dump) 1263d5ac70f0Sopenharmony_ci rate->ops.dump(rate->obj, out); 1264d5ac70f0Sopenharmony_ci snd_output_printf(out, "Protocol version: %x\n", rate->plugin_version); 1265d5ac70f0Sopenharmony_ci if (pcm->setup) { 1266d5ac70f0Sopenharmony_ci snd_output_printf(out, "Its setup is:\n"); 1267d5ac70f0Sopenharmony_ci snd_pcm_dump_setup(pcm, out); 1268d5ac70f0Sopenharmony_ci } 1269d5ac70f0Sopenharmony_ci snd_output_printf(out, "Slave: "); 1270d5ac70f0Sopenharmony_ci snd_pcm_dump(rate->gen.slave, out); 1271d5ac70f0Sopenharmony_ci} 1272d5ac70f0Sopenharmony_ci 1273d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_close(snd_pcm_t *pcm) 1274d5ac70f0Sopenharmony_ci{ 1275d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 1276d5ac70f0Sopenharmony_ci 1277d5ac70f0Sopenharmony_ci if (rate->ops.close) 1278d5ac70f0Sopenharmony_ci rate->ops.close(rate->obj); 1279d5ac70f0Sopenharmony_ci if (rate->open_func) 1280d5ac70f0Sopenharmony_ci snd_dlobj_cache_put(rate->open_func); 1281d5ac70f0Sopenharmony_ci return snd_pcm_generic_close(pcm); 1282d5ac70f0Sopenharmony_ci} 1283d5ac70f0Sopenharmony_ci 1284d5ac70f0Sopenharmony_ci/** 1285d5ac70f0Sopenharmony_ci * \brief Convert rate pcm frames to corresponding rate slave pcm frames 1286d5ac70f0Sopenharmony_ci * \param pcm PCM handle 1287d5ac70f0Sopenharmony_ci * \param frames Frames to be converted to slave frames 1288d5ac70f0Sopenharmony_ci * \retval Corresponding slave frames 1289d5ac70f0Sopenharmony_ci*/ 1290d5ac70f0Sopenharmony_cistatic snd_pcm_uframes_t snd_pcm_rate_slave_frames(snd_pcm_t *pcm, snd_pcm_uframes_t frames) 1291d5ac70f0Sopenharmony_ci{ 1292d5ac70f0Sopenharmony_ci snd_pcm_uframes_t sframes; 1293d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate = pcm->private_data; 1294d5ac70f0Sopenharmony_ci 1295d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) 1296d5ac70f0Sopenharmony_ci sframes = rate->ops.output_frames(rate->obj, frames); 1297d5ac70f0Sopenharmony_ci else 1298d5ac70f0Sopenharmony_ci sframes = rate->ops.input_frames(rate->obj, frames); 1299d5ac70f0Sopenharmony_ci 1300d5ac70f0Sopenharmony_ci return sframes; 1301d5ac70f0Sopenharmony_ci} 1302d5ac70f0Sopenharmony_ci 1303d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_may_wait_for_avail_min(snd_pcm_t *pcm, 1304d5ac70f0Sopenharmony_ci snd_pcm_uframes_t avail) 1305d5ac70f0Sopenharmony_ci{ 1306d5ac70f0Sopenharmony_ci return snd_pcm_plugin_may_wait_for_avail_min_conv(pcm, avail, 1307d5ac70f0Sopenharmony_ci snd_pcm_rate_slave_frames); 1308d5ac70f0Sopenharmony_ci} 1309d5ac70f0Sopenharmony_ci 1310d5ac70f0Sopenharmony_cistatic const snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = { 1311d5ac70f0Sopenharmony_ci .status = snd_pcm_rate_status, 1312d5ac70f0Sopenharmony_ci .state = snd_pcm_rate_state, 1313d5ac70f0Sopenharmony_ci .hwsync = snd_pcm_rate_hwsync, 1314d5ac70f0Sopenharmony_ci .delay = snd_pcm_rate_delay, 1315d5ac70f0Sopenharmony_ci .prepare = snd_pcm_rate_prepare, 1316d5ac70f0Sopenharmony_ci .reset = snd_pcm_rate_reset, 1317d5ac70f0Sopenharmony_ci .start = snd_pcm_rate_start, 1318d5ac70f0Sopenharmony_ci .drop = snd_pcm_generic_drop, 1319d5ac70f0Sopenharmony_ci .drain = snd_pcm_rate_drain, 1320d5ac70f0Sopenharmony_ci .pause = snd_pcm_generic_pause, 1321d5ac70f0Sopenharmony_ci .rewindable = snd_pcm_rate_rewindable, 1322d5ac70f0Sopenharmony_ci .rewind = snd_pcm_rate_rewind, 1323d5ac70f0Sopenharmony_ci .forwardable = snd_pcm_rate_forwardable, 1324d5ac70f0Sopenharmony_ci .forward = snd_pcm_rate_forward, 1325d5ac70f0Sopenharmony_ci .resume = snd_pcm_generic_resume, 1326d5ac70f0Sopenharmony_ci .writei = snd_pcm_mmap_writei, 1327d5ac70f0Sopenharmony_ci .writen = snd_pcm_mmap_writen, 1328d5ac70f0Sopenharmony_ci .readi = snd_pcm_mmap_readi, 1329d5ac70f0Sopenharmony_ci .readn = snd_pcm_mmap_readn, 1330d5ac70f0Sopenharmony_ci .avail_update = snd_pcm_rate_avail_update, 1331d5ac70f0Sopenharmony_ci .mmap_commit = snd_pcm_rate_mmap_commit, 1332d5ac70f0Sopenharmony_ci .htimestamp = snd_pcm_rate_htimestamp, 1333d5ac70f0Sopenharmony_ci .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, 1334d5ac70f0Sopenharmony_ci .poll_descriptors = snd_pcm_generic_poll_descriptors, 1335d5ac70f0Sopenharmony_ci .poll_revents = snd_pcm_rate_poll_revents, 1336d5ac70f0Sopenharmony_ci .may_wait_for_avail_min = snd_pcm_rate_may_wait_for_avail_min, 1337d5ac70f0Sopenharmony_ci}; 1338d5ac70f0Sopenharmony_ci 1339d5ac70f0Sopenharmony_cistatic const snd_pcm_ops_t snd_pcm_rate_ops = { 1340d5ac70f0Sopenharmony_ci .close = snd_pcm_rate_close, 1341d5ac70f0Sopenharmony_ci .info = snd_pcm_generic_info, 1342d5ac70f0Sopenharmony_ci .hw_refine = snd_pcm_rate_hw_refine, 1343d5ac70f0Sopenharmony_ci .hw_params = snd_pcm_rate_hw_params, 1344d5ac70f0Sopenharmony_ci .hw_free = snd_pcm_rate_hw_free, 1345d5ac70f0Sopenharmony_ci .sw_params = snd_pcm_rate_sw_params, 1346d5ac70f0Sopenharmony_ci .channel_info = snd_pcm_generic_channel_info, 1347d5ac70f0Sopenharmony_ci .dump = snd_pcm_rate_dump, 1348d5ac70f0Sopenharmony_ci .nonblock = snd_pcm_generic_nonblock, 1349d5ac70f0Sopenharmony_ci .async = snd_pcm_generic_async, 1350d5ac70f0Sopenharmony_ci .mmap = snd_pcm_generic_mmap, 1351d5ac70f0Sopenharmony_ci .munmap = snd_pcm_generic_munmap, 1352d5ac70f0Sopenharmony_ci .query_chmaps = snd_pcm_generic_query_chmaps, 1353d5ac70f0Sopenharmony_ci .get_chmap = snd_pcm_generic_get_chmap, 1354d5ac70f0Sopenharmony_ci .set_chmap = snd_pcm_generic_set_chmap, 1355d5ac70f0Sopenharmony_ci}; 1356d5ac70f0Sopenharmony_ci 1357d5ac70f0Sopenharmony_ci/** 1358d5ac70f0Sopenharmony_ci * \brief Get a default converter string 1359d5ac70f0Sopenharmony_ci * \param root Root configuration node 1360d5ac70f0Sopenharmony_ci * \retval A const config item if found, or NULL 1361d5ac70f0Sopenharmony_ci */ 1362d5ac70f0Sopenharmony_ciconst snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root) 1363d5ac70f0Sopenharmony_ci{ 1364d5ac70f0Sopenharmony_ci snd_config_t *n; 1365d5ac70f0Sopenharmony_ci /* look for default definition */ 1366d5ac70f0Sopenharmony_ci if (snd_config_search(root, "defaults.pcm.rate_converter", &n) >= 0) 1367d5ac70f0Sopenharmony_ci return n; 1368d5ac70f0Sopenharmony_ci return NULL; 1369d5ac70f0Sopenharmony_ci} 1370d5ac70f0Sopenharmony_ci 1371d5ac70f0Sopenharmony_cistatic void rate_initial_setup(snd_pcm_rate_t *rate) 1372d5ac70f0Sopenharmony_ci{ 1373d5ac70f0Sopenharmony_ci if (rate->plugin_version == SND_PCM_RATE_PLUGIN_VERSION) 1374d5ac70f0Sopenharmony_ci rate->plugin_version = rate->ops.version; 1375d5ac70f0Sopenharmony_ci 1376d5ac70f0Sopenharmony_ci if (rate->plugin_version >= 0x010002 && 1377d5ac70f0Sopenharmony_ci rate->ops.get_supported_rates) 1378d5ac70f0Sopenharmony_ci rate->ops.get_supported_rates(rate->obj, 1379d5ac70f0Sopenharmony_ci &rate->rate_min, 1380d5ac70f0Sopenharmony_ci &rate->rate_max); 1381d5ac70f0Sopenharmony_ci 1382d5ac70f0Sopenharmony_ci if (rate->plugin_version >= 0x010003 && 1383d5ac70f0Sopenharmony_ci rate->ops.get_supported_formats) { 1384d5ac70f0Sopenharmony_ci rate->ops.get_supported_formats(rate->obj, 1385d5ac70f0Sopenharmony_ci &rate->in_formats, 1386d5ac70f0Sopenharmony_ci &rate->out_formats, 1387d5ac70f0Sopenharmony_ci &rate->format_flags); 1388d5ac70f0Sopenharmony_ci } else if (!rate->ops.convert && rate->ops.convert_s16) { 1389d5ac70f0Sopenharmony_ci rate->in_formats = rate->out_formats = 1390d5ac70f0Sopenharmony_ci 1ULL << SND_PCM_FORMAT_S16; 1391d5ac70f0Sopenharmony_ci rate->format_flags = SND_PCM_RATE_FLAG_INTERLEAVED; 1392d5ac70f0Sopenharmony_ci } 1393d5ac70f0Sopenharmony_ci} 1394d5ac70f0Sopenharmony_ci 1395d5ac70f0Sopenharmony_ci#ifdef PIC 1396d5ac70f0Sopenharmony_cistatic int is_builtin_plugin(const char *type) 1397d5ac70f0Sopenharmony_ci{ 1398d5ac70f0Sopenharmony_ci return strcmp(type, "linear") == 0; 1399d5ac70f0Sopenharmony_ci} 1400d5ac70f0Sopenharmony_ci 1401d5ac70f0Sopenharmony_cistatic const char *const default_rate_plugins[] = { 1402d5ac70f0Sopenharmony_ci "speexrate", "linear", NULL 1403d5ac70f0Sopenharmony_ci}; 1404d5ac70f0Sopenharmony_ci 1405d5ac70f0Sopenharmony_cistatic int rate_open_func(snd_pcm_rate_t *rate, const char *type, const snd_config_t *converter_conf, int verbose) 1406d5ac70f0Sopenharmony_ci{ 1407d5ac70f0Sopenharmony_ci char open_name[64], open_conf_name[64], lib_name[64], *lib = NULL; 1408d5ac70f0Sopenharmony_ci snd_pcm_rate_open_func_t open_func; 1409d5ac70f0Sopenharmony_ci snd_pcm_rate_open_conf_func_t open_conf_func; 1410d5ac70f0Sopenharmony_ci int err; 1411d5ac70f0Sopenharmony_ci 1412d5ac70f0Sopenharmony_ci snprintf(open_name, sizeof(open_name), "_snd_pcm_rate_%s_open", type); 1413d5ac70f0Sopenharmony_ci snprintf(open_conf_name, sizeof(open_conf_name), "_snd_pcm_rate_%s_open_conf", type); 1414d5ac70f0Sopenharmony_ci if (!is_builtin_plugin(type)) { 1415d5ac70f0Sopenharmony_ci snprintf(lib_name, sizeof(lib_name), 1416d5ac70f0Sopenharmony_ci "libasound_module_rate_%s.so", type); 1417d5ac70f0Sopenharmony_ci lib = lib_name; 1418d5ac70f0Sopenharmony_ci } 1419d5ac70f0Sopenharmony_ci 1420d5ac70f0Sopenharmony_ci open_conf_func = snd_dlobj_cache_get(lib, open_conf_name, NULL, verbose && converter_conf != NULL); 1421d5ac70f0Sopenharmony_ci if (open_conf_func) { 1422d5ac70f0Sopenharmony_ci err = open_conf_func(SND_PCM_RATE_PLUGIN_VERSION, 1423d5ac70f0Sopenharmony_ci &rate->obj, &rate->ops, converter_conf); 1424d5ac70f0Sopenharmony_ci if (!err) { 1425d5ac70f0Sopenharmony_ci rate->open_func = open_conf_func; 1426d5ac70f0Sopenharmony_ci return 0; 1427d5ac70f0Sopenharmony_ci } else { 1428d5ac70f0Sopenharmony_ci snd_dlobj_cache_put(open_conf_func); 1429d5ac70f0Sopenharmony_ci return err; 1430d5ac70f0Sopenharmony_ci } 1431d5ac70f0Sopenharmony_ci } 1432d5ac70f0Sopenharmony_ci 1433d5ac70f0Sopenharmony_ci open_func = snd_dlobj_cache_get(lib, open_name, NULL, verbose); 1434d5ac70f0Sopenharmony_ci if (!open_func) 1435d5ac70f0Sopenharmony_ci return -ENOENT; 1436d5ac70f0Sopenharmony_ci 1437d5ac70f0Sopenharmony_ci rate->open_func = open_func; 1438d5ac70f0Sopenharmony_ci 1439d5ac70f0Sopenharmony_ci err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops); 1440d5ac70f0Sopenharmony_ci if (!err) 1441d5ac70f0Sopenharmony_ci return 0; 1442d5ac70f0Sopenharmony_ci 1443d5ac70f0Sopenharmony_ci /* try to open with the old protocol version */ 1444d5ac70f0Sopenharmony_ci rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD; 1445d5ac70f0Sopenharmony_ci err = open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD, 1446d5ac70f0Sopenharmony_ci &rate->obj, &rate->ops); 1447d5ac70f0Sopenharmony_ci if (!err) 1448d5ac70f0Sopenharmony_ci return 0; 1449d5ac70f0Sopenharmony_ci 1450d5ac70f0Sopenharmony_ci snd_dlobj_cache_put(open_func); 1451d5ac70f0Sopenharmony_ci rate->open_func = NULL; 1452d5ac70f0Sopenharmony_ci return err; 1453d5ac70f0Sopenharmony_ci} 1454d5ac70f0Sopenharmony_ci#endif 1455d5ac70f0Sopenharmony_ci 1456d5ac70f0Sopenharmony_ci/* 1457d5ac70f0Sopenharmony_ci * If the conf is an array of alternatives then the id of 1458d5ac70f0Sopenharmony_ci * the first element will be "0" (or maybe NULL). Otherwise assume it is 1459d5ac70f0Sopenharmony_ci * a structure. 1460d5ac70f0Sopenharmony_ci */ 1461d5ac70f0Sopenharmony_cistatic int is_string_array(const snd_config_t *conf) 1462d5ac70f0Sopenharmony_ci{ 1463d5ac70f0Sopenharmony_ci snd_config_iterator_t i; 1464d5ac70f0Sopenharmony_ci 1465d5ac70f0Sopenharmony_ci if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) 1466d5ac70f0Sopenharmony_ci return 0; 1467d5ac70f0Sopenharmony_ci 1468d5ac70f0Sopenharmony_ci i = snd_config_iterator_first(conf); 1469d5ac70f0Sopenharmony_ci if (i && i != snd_config_iterator_end(conf)) { 1470d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(i); 1471d5ac70f0Sopenharmony_ci const char *id; 1472d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 1473d5ac70f0Sopenharmony_ci return 0; 1474d5ac70f0Sopenharmony_ci if (id && strcmp(id, "0") != 0) 1475d5ac70f0Sopenharmony_ci return 0; 1476d5ac70f0Sopenharmony_ci } 1477d5ac70f0Sopenharmony_ci 1478d5ac70f0Sopenharmony_ci return 1; 1479d5ac70f0Sopenharmony_ci} 1480d5ac70f0Sopenharmony_ci 1481d5ac70f0Sopenharmony_ci/** 1482d5ac70f0Sopenharmony_ci * \brief Creates a new rate PCM 1483d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle 1484d5ac70f0Sopenharmony_ci * \param name Name of PCM 1485d5ac70f0Sopenharmony_ci * \param sformat Slave format 1486d5ac70f0Sopenharmony_ci * \param srate Slave rate 1487d5ac70f0Sopenharmony_ci * \param converter SRC type string node 1488d5ac70f0Sopenharmony_ci * \param slave Slave PCM handle 1489d5ac70f0Sopenharmony_ci * \param close_slave When set, the slave PCM handle is closed with copy PCM 1490d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code 1491d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense 1492d5ac70f0Sopenharmony_ci * of compatibility reasons. The prototype might be freely 1493d5ac70f0Sopenharmony_ci * changed in future. 1494d5ac70f0Sopenharmony_ci */ 1495d5ac70f0Sopenharmony_ciint snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name, 1496d5ac70f0Sopenharmony_ci snd_pcm_format_t sformat, unsigned int srate, 1497d5ac70f0Sopenharmony_ci const snd_config_t *converter, 1498d5ac70f0Sopenharmony_ci snd_pcm_t *slave, int close_slave) 1499d5ac70f0Sopenharmony_ci{ 1500d5ac70f0Sopenharmony_ci snd_pcm_t *pcm; 1501d5ac70f0Sopenharmony_ci snd_pcm_rate_t *rate; 1502d5ac70f0Sopenharmony_ci const char *type = NULL; 1503d5ac70f0Sopenharmony_ci int err; 1504d5ac70f0Sopenharmony_ci#ifndef PIC 1505d5ac70f0Sopenharmony_ci snd_pcm_rate_open_func_t open_func; 1506d5ac70f0Sopenharmony_ci extern int SND_PCM_RATE_PLUGIN_ENTRY(linear) (unsigned int version, void **objp, snd_pcm_rate_ops_t *ops); 1507d5ac70f0Sopenharmony_ci#endif 1508d5ac70f0Sopenharmony_ci 1509d5ac70f0Sopenharmony_ci assert(pcmp && slave); 1510d5ac70f0Sopenharmony_ci if (sformat != SND_PCM_FORMAT_UNKNOWN && 1511d5ac70f0Sopenharmony_ci snd_pcm_format_linear(sformat) != 1) 1512d5ac70f0Sopenharmony_ci return -EINVAL; 1513d5ac70f0Sopenharmony_ci rate = calloc(1, sizeof(snd_pcm_rate_t)); 1514d5ac70f0Sopenharmony_ci if (!rate) { 1515d5ac70f0Sopenharmony_ci return -ENOMEM; 1516d5ac70f0Sopenharmony_ci } 1517d5ac70f0Sopenharmony_ci rate->gen.slave = slave; 1518d5ac70f0Sopenharmony_ci rate->gen.close_slave = close_slave; 1519d5ac70f0Sopenharmony_ci rate->srate = srate; 1520d5ac70f0Sopenharmony_ci rate->sformat = sformat; 1521d5ac70f0Sopenharmony_ci 1522d5ac70f0Sopenharmony_ci rate->rate_min = SND_PCM_PLUGIN_RATE_MIN; 1523d5ac70f0Sopenharmony_ci rate->rate_max = SND_PCM_PLUGIN_RATE_MAX; 1524d5ac70f0Sopenharmony_ci rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION; 1525d5ac70f0Sopenharmony_ci 1526d5ac70f0Sopenharmony_ci err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode); 1527d5ac70f0Sopenharmony_ci if (err < 0) { 1528d5ac70f0Sopenharmony_ci free(rate); 1529d5ac70f0Sopenharmony_ci return err; 1530d5ac70f0Sopenharmony_ci } 1531d5ac70f0Sopenharmony_ci 1532d5ac70f0Sopenharmony_ci#ifdef PIC 1533d5ac70f0Sopenharmony_ci err = -ENOENT; 1534d5ac70f0Sopenharmony_ci if (!converter) { 1535d5ac70f0Sopenharmony_ci const char *const *types; 1536d5ac70f0Sopenharmony_ci for (types = default_rate_plugins; *types; types++) { 1537d5ac70f0Sopenharmony_ci err = rate_open_func(rate, *types, NULL, 0); 1538d5ac70f0Sopenharmony_ci if (!err) { 1539d5ac70f0Sopenharmony_ci type = *types; 1540d5ac70f0Sopenharmony_ci break; 1541d5ac70f0Sopenharmony_ci } 1542d5ac70f0Sopenharmony_ci } 1543d5ac70f0Sopenharmony_ci } else if (!snd_config_get_string(converter, &type)) 1544d5ac70f0Sopenharmony_ci err = rate_open_func(rate, type, NULL, 1); 1545d5ac70f0Sopenharmony_ci else if (is_string_array(converter)) { 1546d5ac70f0Sopenharmony_ci snd_config_iterator_t i, next; 1547d5ac70f0Sopenharmony_ci snd_config_for_each(i, next, converter) { 1548d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(i); 1549d5ac70f0Sopenharmony_ci if (snd_config_get_string(n, &type) < 0) 1550d5ac70f0Sopenharmony_ci break; 1551d5ac70f0Sopenharmony_ci err = rate_open_func(rate, type, NULL, 0); 1552d5ac70f0Sopenharmony_ci if (!err) 1553d5ac70f0Sopenharmony_ci break; 1554d5ac70f0Sopenharmony_ci } 1555d5ac70f0Sopenharmony_ci } else if (snd_config_get_type(converter) == SND_CONFIG_TYPE_COMPOUND) { 1556d5ac70f0Sopenharmony_ci snd_config_iterator_t i, next; 1557d5ac70f0Sopenharmony_ci snd_config_for_each(i, next, converter) { 1558d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(i); 1559d5ac70f0Sopenharmony_ci const char *id; 1560d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 1561d5ac70f0Sopenharmony_ci continue; 1562d5ac70f0Sopenharmony_ci if (strcmp(id, "name") != 0) 1563d5ac70f0Sopenharmony_ci continue; 1564d5ac70f0Sopenharmony_ci snd_config_get_string(n, &type); 1565d5ac70f0Sopenharmony_ci break; 1566d5ac70f0Sopenharmony_ci } 1567d5ac70f0Sopenharmony_ci if (!type) { 1568d5ac70f0Sopenharmony_ci SNDERR("No name given for rate converter"); 1569d5ac70f0Sopenharmony_ci snd_pcm_free(pcm); 1570d5ac70f0Sopenharmony_ci free(rate); 1571d5ac70f0Sopenharmony_ci return -EINVAL; 1572d5ac70f0Sopenharmony_ci } 1573d5ac70f0Sopenharmony_ci err = rate_open_func(rate, type, converter, 1); 1574d5ac70f0Sopenharmony_ci } else { 1575d5ac70f0Sopenharmony_ci SNDERR("Invalid type for rate converter"); 1576d5ac70f0Sopenharmony_ci snd_pcm_free(pcm); 1577d5ac70f0Sopenharmony_ci free(rate); 1578d5ac70f0Sopenharmony_ci return -EINVAL; 1579d5ac70f0Sopenharmony_ci } 1580d5ac70f0Sopenharmony_ci if (err < 0) { 1581d5ac70f0Sopenharmony_ci SNDERR("Cannot find rate converter"); 1582d5ac70f0Sopenharmony_ci snd_pcm_free(pcm); 1583d5ac70f0Sopenharmony_ci free(rate); 1584d5ac70f0Sopenharmony_ci return -ENOENT; 1585d5ac70f0Sopenharmony_ci } 1586d5ac70f0Sopenharmony_ci#else 1587d5ac70f0Sopenharmony_ci type = "linear"; 1588d5ac70f0Sopenharmony_ci open_func = SND_PCM_RATE_PLUGIN_ENTRY(linear); 1589d5ac70f0Sopenharmony_ci err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops); 1590d5ac70f0Sopenharmony_ci if (err < 0) { 1591d5ac70f0Sopenharmony_ci snd_pcm_free(pcm); 1592d5ac70f0Sopenharmony_ci free(rate); 1593d5ac70f0Sopenharmony_ci return err; 1594d5ac70f0Sopenharmony_ci } 1595d5ac70f0Sopenharmony_ci#endif 1596d5ac70f0Sopenharmony_ci 1597d5ac70f0Sopenharmony_ci if (! rate->ops.init || ! (rate->ops.convert || rate->ops.convert_s16) || 1598d5ac70f0Sopenharmony_ci ! rate->ops.input_frames || ! rate->ops.output_frames) { 1599d5ac70f0Sopenharmony_ci SNDERR("Inproper rate plugin %s initialization", type); 1600d5ac70f0Sopenharmony_ci snd_pcm_free(pcm); 1601d5ac70f0Sopenharmony_ci free(rate); 1602d5ac70f0Sopenharmony_ci return err; 1603d5ac70f0Sopenharmony_ci } 1604d5ac70f0Sopenharmony_ci 1605d5ac70f0Sopenharmony_ci rate_initial_setup(rate); 1606d5ac70f0Sopenharmony_ci 1607d5ac70f0Sopenharmony_ci pcm->ops = &snd_pcm_rate_ops; 1608d5ac70f0Sopenharmony_ci pcm->fast_ops = &snd_pcm_rate_fast_ops; 1609d5ac70f0Sopenharmony_ci pcm->private_data = rate; 1610d5ac70f0Sopenharmony_ci pcm->poll_fd = slave->poll_fd; 1611d5ac70f0Sopenharmony_ci pcm->poll_events = slave->poll_events; 1612d5ac70f0Sopenharmony_ci pcm->mmap_rw = 1; 1613d5ac70f0Sopenharmony_ci pcm->tstamp_type = slave->tstamp_type; 1614d5ac70f0Sopenharmony_ci snd_pcm_set_hw_ptr(pcm, &rate->hw_ptr, -1, 0); 1615d5ac70f0Sopenharmony_ci snd_pcm_set_appl_ptr(pcm, &rate->appl_ptr, -1, 0); 1616d5ac70f0Sopenharmony_ci *pcmp = pcm; 1617d5ac70f0Sopenharmony_ci 1618d5ac70f0Sopenharmony_ci return 0; 1619d5ac70f0Sopenharmony_ci} 1620d5ac70f0Sopenharmony_ci 1621d5ac70f0Sopenharmony_ci/*! \page pcm_plugins 1622d5ac70f0Sopenharmony_ci 1623d5ac70f0Sopenharmony_ci\section pcm_plugins_rate Plugin: Rate 1624d5ac70f0Sopenharmony_ci 1625d5ac70f0Sopenharmony_ciThis plugin converts a stream rate. The input and output formats must be linear. 1626d5ac70f0Sopenharmony_ci 1627d5ac70f0Sopenharmony_ci\code 1628d5ac70f0Sopenharmony_cipcm.name { 1629d5ac70f0Sopenharmony_ci type rate # Rate PCM 1630d5ac70f0Sopenharmony_ci slave STR # Slave name 1631d5ac70f0Sopenharmony_ci # or 1632d5ac70f0Sopenharmony_ci slave { # Slave definition 1633d5ac70f0Sopenharmony_ci pcm STR # Slave PCM name 1634d5ac70f0Sopenharmony_ci # or 1635d5ac70f0Sopenharmony_ci pcm { } # Slave PCM definition 1636d5ac70f0Sopenharmony_ci rate INT # Slave rate 1637d5ac70f0Sopenharmony_ci [format STR] # Slave format 1638d5ac70f0Sopenharmony_ci } 1639d5ac70f0Sopenharmony_ci converter STR # optional 1640d5ac70f0Sopenharmony_ci # or 1641d5ac70f0Sopenharmony_ci converter [ STR1 STR2 ... ] # optional 1642d5ac70f0Sopenharmony_ci # Converter type, default is taken from 1643d5ac70f0Sopenharmony_ci # defaults.pcm.rate_converter 1644d5ac70f0Sopenharmony_ci # or 1645d5ac70f0Sopenharmony_ci converter { # optional 1646d5ac70f0Sopenharmony_ci name STR # Convertor type 1647d5ac70f0Sopenharmony_ci xxx yyy # optional convertor-specific configuration 1648d5ac70f0Sopenharmony_ci } 1649d5ac70f0Sopenharmony_ci} 1650d5ac70f0Sopenharmony_ci\endcode 1651d5ac70f0Sopenharmony_ci 1652d5ac70f0Sopenharmony_ci\subsection pcm_plugins_rate_funcref Function reference 1653d5ac70f0Sopenharmony_ci 1654d5ac70f0Sopenharmony_ci<UL> 1655d5ac70f0Sopenharmony_ci <LI>snd_pcm_rate_open() 1656d5ac70f0Sopenharmony_ci <LI>_snd_pcm_rate_open() 1657d5ac70f0Sopenharmony_ci</UL> 1658d5ac70f0Sopenharmony_ci 1659d5ac70f0Sopenharmony_ci*/ 1660d5ac70f0Sopenharmony_ci 1661d5ac70f0Sopenharmony_ci/** 1662d5ac70f0Sopenharmony_ci * \brief Creates a new rate PCM 1663d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle 1664d5ac70f0Sopenharmony_ci * \param name Name of PCM 1665d5ac70f0Sopenharmony_ci * \param root Root configuration node 1666d5ac70f0Sopenharmony_ci * \param conf Configuration node with rate PCM description 1667d5ac70f0Sopenharmony_ci * \param stream Stream type 1668d5ac70f0Sopenharmony_ci * \param mode Stream mode 1669d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code 1670d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense 1671d5ac70f0Sopenharmony_ci * of compatibility reasons. The prototype might be freely 1672d5ac70f0Sopenharmony_ci * changed in future. 1673d5ac70f0Sopenharmony_ci */ 1674d5ac70f0Sopenharmony_ciint _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name, 1675d5ac70f0Sopenharmony_ci snd_config_t *root, snd_config_t *conf, 1676d5ac70f0Sopenharmony_ci snd_pcm_stream_t stream, int mode) 1677d5ac70f0Sopenharmony_ci{ 1678d5ac70f0Sopenharmony_ci snd_config_iterator_t i, next; 1679d5ac70f0Sopenharmony_ci int err; 1680d5ac70f0Sopenharmony_ci snd_pcm_t *spcm; 1681d5ac70f0Sopenharmony_ci snd_config_t *slave = NULL, *sconf; 1682d5ac70f0Sopenharmony_ci snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN; 1683d5ac70f0Sopenharmony_ci int srate = -1; 1684d5ac70f0Sopenharmony_ci const snd_config_t *converter = NULL; 1685d5ac70f0Sopenharmony_ci 1686d5ac70f0Sopenharmony_ci snd_config_for_each(i, next, conf) { 1687d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(i); 1688d5ac70f0Sopenharmony_ci const char *id; 1689d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 1690d5ac70f0Sopenharmony_ci continue; 1691d5ac70f0Sopenharmony_ci if (snd_pcm_conf_generic_id(id)) 1692d5ac70f0Sopenharmony_ci continue; 1693d5ac70f0Sopenharmony_ci if (strcmp(id, "slave") == 0) { 1694d5ac70f0Sopenharmony_ci slave = n; 1695d5ac70f0Sopenharmony_ci continue; 1696d5ac70f0Sopenharmony_ci } 1697d5ac70f0Sopenharmony_ci if (strcmp(id, "converter") == 0) { 1698d5ac70f0Sopenharmony_ci converter = n; 1699d5ac70f0Sopenharmony_ci continue; 1700d5ac70f0Sopenharmony_ci } 1701d5ac70f0Sopenharmony_ci SNDERR("Unknown field %s", id); 1702d5ac70f0Sopenharmony_ci return -EINVAL; 1703d5ac70f0Sopenharmony_ci } 1704d5ac70f0Sopenharmony_ci if (!slave) { 1705d5ac70f0Sopenharmony_ci SNDERR("slave is not defined"); 1706d5ac70f0Sopenharmony_ci return -EINVAL; 1707d5ac70f0Sopenharmony_ci } 1708d5ac70f0Sopenharmony_ci 1709d5ac70f0Sopenharmony_ci err = snd_pcm_slave_conf(root, slave, &sconf, 2, 1710d5ac70f0Sopenharmony_ci SND_PCM_HW_PARAM_FORMAT, 0, &sformat, 1711d5ac70f0Sopenharmony_ci SND_PCM_HW_PARAM_RATE, SCONF_MANDATORY, &srate); 1712d5ac70f0Sopenharmony_ci if (err < 0) 1713d5ac70f0Sopenharmony_ci return err; 1714d5ac70f0Sopenharmony_ci if (sformat != SND_PCM_FORMAT_UNKNOWN && 1715d5ac70f0Sopenharmony_ci snd_pcm_format_linear(sformat) != 1) { 1716d5ac70f0Sopenharmony_ci snd_config_delete(sconf); 1717d5ac70f0Sopenharmony_ci SNDERR("slave format is not linear"); 1718d5ac70f0Sopenharmony_ci return -EINVAL; 1719d5ac70f0Sopenharmony_ci } 1720d5ac70f0Sopenharmony_ci err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf); 1721d5ac70f0Sopenharmony_ci snd_config_delete(sconf); 1722d5ac70f0Sopenharmony_ci if (err < 0) 1723d5ac70f0Sopenharmony_ci return err; 1724d5ac70f0Sopenharmony_ci err = snd_pcm_rate_open(pcmp, name, sformat, (unsigned int) srate, 1725d5ac70f0Sopenharmony_ci converter, spcm, 1); 1726d5ac70f0Sopenharmony_ci if (err < 0) 1727d5ac70f0Sopenharmony_ci snd_pcm_close(spcm); 1728d5ac70f0Sopenharmony_ci return err; 1729d5ac70f0Sopenharmony_ci} 1730d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN 1731d5ac70f0Sopenharmony_ciSND_DLSYM_BUILD_VERSION(_snd_pcm_rate_open, SND_PCM_DLSYM_VERSION); 1732d5ac70f0Sopenharmony_ci#endif 1733