1 /*
2 * Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
3 *
4 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
5 * the the people's Republic of China and other countries.
6 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
7 *
8 * DISCLAIMER
9 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
10 * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
11 * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
12 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
13 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
14 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
15 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
16 *
17 *
18 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
19 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
20 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
21 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
22 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <pthread.h>
37 #include "../../pcm_local.h"
38 #include <aw-alsa-lib/plugin/pcm_rate.h>
39 #include <aw-alsa-lib/pcm_config.h>
40 #include "speex_resampler.h"
41
42 struct rate_src {
43 int quality;
44 unsigned int channels;
45 SpeexResamplerState *st;
46 };
47
input_frames(void * obj,snd_pcm_uframes_t frames)48 static snd_pcm_uframes_t input_frames(void *obj, snd_pcm_uframes_t frames)
49 {
50 spx_uint32_t num, den;
51 struct rate_src *rate = obj;
52 if (frames == 0)
53 return 0;
54 speex_resampler_get_ratio(rate->st, &num, &den);
55 return (snd_pcm_uframes_t)((frames*num+(den>>1))/den);
56 }
57
output_frames(void * obj,snd_pcm_uframes_t frames)58 static snd_pcm_uframes_t output_frames(void *obj, snd_pcm_uframes_t frames)
59 {
60 spx_uint32_t num, den;
61 struct rate_src *rate = obj;
62 if (frames == 0)
63 return 0;
64 speex_resampler_get_ratio(rate->st, &num, &den);
65 return (snd_pcm_uframes_t)((frames*den+(num>>1))/num);
66 }
67
pcm_src_free(void * obj)68 static void pcm_src_free(void *obj)
69 {
70 struct rate_src *rate = obj;
71 if (rate->st)
72 {
73 speex_resampler_destroy(rate->st);
74 rate->st = NULL;
75 }
76 }
77
pcm_src_init(void * obj,snd_pcm_rate_info_t * info)78 static int pcm_src_init(void *obj, snd_pcm_rate_info_t *info)
79 {
80 struct rate_src *rate = obj;
81 int err;
82
83 if (! rate->st || rate->channels != info->channels) {
84 if (rate->st)
85 speex_resampler_destroy(rate->st);
86 rate->channels = info->channels;
87 #ifdef SND_PCM_RATE_FIX_PERIOD_SIZE
88 rate->st = speex_resampler_init_frac(rate->channels, info->in.rate, info->out.rate, info->in.rate, info->out.rate, rate->quality, &err);
89 #else
90 rate->st = speex_resampler_init_frac(rate->channels, info->in.period_size, info->out.period_size, info->in.rate, info->out.rate, rate->quality, &err);
91 #endif
92 if (! rate->st)
93 return -EINVAL;
94 }
95
96 return 0;
97 }
98
pcm_src_adjust_pitch(void * obj,snd_pcm_rate_info_t * info)99 static int pcm_src_adjust_pitch(void *obj, snd_pcm_rate_info_t *info)
100 {
101 struct rate_src *rate = obj;
102 #ifdef SND_PCM_RATE_FIX_PERIOD_SIZE
103 speex_resampler_set_rate_frac(rate->st, info->in.rate, info->out.rate, info->in.rate, info->out.rate);
104 #else
105 speex_resampler_set_rate_frac(rate->st, info->in.period_size, info->out.period_size, info->in.rate, info->out.rate);
106 #endif
107 return 0;
108 }
109
pcm_src_reset(void * obj)110 static void pcm_src_reset(void *obj)
111 {
112 struct rate_src *rate = obj;
113 speex_resampler_reset_mem(rate->st);
114 }
115
pcm_src_convert_s16(void * obj,int16_t * dst,unsigned int dst_frames,const int16_t * src,unsigned int src_frames)116 static void pcm_src_convert_s16(void *obj, int16_t *dst, unsigned int dst_frames,
117 const int16_t *src, unsigned int src_frames)
118 {
119 struct rate_src *rate = obj;
120 speex_resampler_process_interleaved_int(rate->st, src, &src_frames, dst, &dst_frames);
121 }
122
123 #ifdef SND_PCM_RATE_FIX_PERIOD_SIZE
pcm_src_convert_s16_fix(void * obj,int16_t * dst,unsigned int * dst_frames,const int16_t * src,unsigned int * src_frames)124 static void pcm_src_convert_s16_fix(void *obj, int16_t *dst, unsigned int *dst_frames,
125 const int16_t *src, unsigned int *src_frames)
126 {
127 struct rate_src *rate = obj;
128 speex_resampler_process_interleaved_int(rate->st, src, src_frames, dst, dst_frames);
129 }
130 #endif
131
pcm_src_close(void * obj)132 static void pcm_src_close(void *obj)
133 {
134 free(obj);
135 }
136
137 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
get_supported_rates(void * obj,unsigned int * rate_min,unsigned int * rate_max)138 static int get_supported_rates(void *obj, unsigned int *rate_min,
139 unsigned int *rate_max)
140 {
141 *rate_min = *rate_max = 0; /* both unlimited */
142 return 0;
143 }
144 #if 0
145 static void dump(void *obj, snd_output_t *out)
146 {
147 snd_output_printf(out, "Converter: libspeex "
148 #ifdef USE_LIBSPEEX
149 "(external)"
150 #else
151 "(builtin)"
152 #endif
153 "\n");
154 }
155 #endif
156 #endif
157
158 static snd_pcm_rate_ops_t pcm_src_ops = {
159 .close = pcm_src_close,
160 .init = pcm_src_init,
161 .free = pcm_src_free,
162 .reset = pcm_src_reset,
163 .adjust_pitch = pcm_src_adjust_pitch,
164 .convert_s16 = pcm_src_convert_s16,
165 #ifdef SND_PCM_RATE_FIX_PERIOD_SIZE
166 .convert_s16_fix = pcm_src_convert_s16_fix,
167 #endif
168 .input_frames = input_frames,
169 .output_frames = output_frames,
170 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
171 .version = SND_PCM_RATE_PLUGIN_VERSION,
172 .get_supported_rates = get_supported_rates,
173 // .dump = dump,
174 #endif
175 };
176
pcm_src_open(unsigned int version,void ** objp,snd_pcm_rate_ops_t * ops,int quality)177 static int pcm_src_open(unsigned int version, void **objp,
178 snd_pcm_rate_ops_t *ops, int quality)
179 {
180 struct rate_src *rate;
181
182 #if SND_PCM_RATE_PLUGIN_VERSION < 0x010002
183 if (version != SND_PCM_RATE_PLUGIN_VERSION) {
184 fprintf(stderr, "Invalid rate plugin version %x\n", version);
185 return -EINVAL;
186 }
187 #endif
188 rate = calloc(1, sizeof(*rate));
189 if (! rate)
190 return -ENOMEM;
191 rate->quality = quality;
192
193 *objp = rate;
194 #if SND_PCM_RATE_PLUGIN_VERSION >= 0x010002
195 //if (version == 0x010001)
196 // memcpy(ops, &pcm_src_ops, sizeof(snd_pcm_rate_old_ops_t));
197 //else
198 #endif
199 *ops = pcm_src_ops;
200 return 0;
201 }
202
SND_PCM_RATE_PLUGIN_ENTRY(speexrate)203 int SND_PCM_RATE_PLUGIN_ENTRY(speexrate) (unsigned int version, void **objp,
204 snd_pcm_rate_ops_t *ops)
205 {
206 return pcm_src_open(version, objp, ops, 3);
207 }
208