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