1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include "mediastreamer2/msfilter.h"
21
22 #ifdef _MSC_VER
23 #include <malloc.h>
24 #endif
25
26 #include <speex/speex_resampler.h>
27 #include <speex/speex.h>
28
29 #ifdef __ANDROID__
30 #include "cpu-features.h"
31 #endif
32
33 typedef struct _ResampleData{
34 MSBufferizer *bz;
35 uint32_t ts;
36 uint32_t input_rate;
37 uint32_t output_rate;
38 int in_nchannels;
39 int out_nchannels;
40 SpeexResamplerState *handle;
41 int cpuFeatures; /*store because there is no SPEEX_LIB_GET_CPU_FEATURES*/
42 } ResampleData;
43
resample_data_new(void)44 static ResampleData * resample_data_new(void){
45 ResampleData *obj=ms_new0(ResampleData,1);
46 obj->bz=ms_bufferizer_new();
47 obj->ts=0;
48 obj->input_rate=8000;
49 obj->output_rate=16000;
50 obj->handle=NULL;
51 obj->in_nchannels=obj->out_nchannels=1;
52 obj->cpuFeatures=0;
53 return obj;
54 }
55
resample_data_destroy(ResampleData * obj)56 static void resample_data_destroy(ResampleData *obj){
57 if (obj->handle!=NULL)
58 speex_resampler_destroy(obj->handle);
59 ms_bufferizer_destroy(obj->bz);
60 ms_free(obj);
61 }
62
resample_init(MSFilter * obj)63 static void resample_init(MSFilter *obj){
64 ResampleData* data=resample_data_new();
65 #ifdef SPEEX_LIB_SET_CPU_FEATURES
66 #ifdef __ANDROID__
67 if (((android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM) && ((android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0))
68 || (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM64)) {
69 data->cpuFeatures = SPEEX_LIB_CPU_FEATURE_NEON;
70 }
71 #elif MS_HAS_ARM_NEON
72 data->cpuFeatures = SPEEX_LIB_CPU_FEATURE_NEON;
73 #endif
74 ms_message("speex_lib_ctl init with neon ? %d", (data->cpuFeatures == SPEEX_LIB_CPU_FEATURE_NEON));
75 speex_lib_ctl(SPEEX_LIB_SET_CPU_FEATURES, &data->cpuFeatures);
76 #else
77 ms_message("speex_lib_ctl does not support SPEEX_LIB_CPU_FEATURE_NEON");
78 #endif
79 obj->data=data;
80 }
81
resample_uninit(MSFilter * obj)82 static void resample_uninit(MSFilter *obj){
83 resample_data_destroy((ResampleData*)obj->data);
84 }
85
resample_channel_adapt(int in_nchannels,int out_nchannels,mblk_t * im,mblk_t ** om)86 static int resample_channel_adapt(int in_nchannels, int out_nchannels, mblk_t *im, mblk_t **om) {
87 if ((in_nchannels == 2) && (out_nchannels == 1)) {
88 size_t msgsize = msgdsize(im) / 2;
89 *om = allocb(msgsize, 0);
90 for (; im->b_rptr < im->b_wptr; im->b_rptr += 4, (*om)->b_wptr += 2) {
91 *(int16_t *)(*om)->b_wptr = *(int16_t *)im->b_rptr;
92 }
93 mblk_meta_copy(im, *om);
94 return 1;
95 } else if ((in_nchannels == 1) && (out_nchannels == 2)) {
96 size_t msgsize = msgdsize(im) * 2;
97 *om = allocb(msgsize, 0);
98 for (; im->b_rptr < im->b_wptr; im->b_rptr += 2, (*om)->b_wptr += 4) {
99 ((int16_t *)(*om)->b_wptr)[0] = *(int16_t *)im->b_rptr;
100 ((int16_t *)(*om)->b_wptr)[1] = *(int16_t *)im->b_rptr;
101 }
102 mblk_meta_copy(im, *om);
103 return 1;
104 }
105 return 0;
106 }
107
resample_init_speex(ResampleData * dt)108 static void resample_init_speex(ResampleData *dt){
109 int err=0;
110 int quality=SPEEX_RESAMPLER_QUALITY_VOIP; /*default value is voip*/
111 #if MS_HAS_ARM /*on ARM, NEON optimization are mandatory to support this quality, else using basic mode*/
112 #if SPEEX_LIB_SET_CPU_FEATURES
113 if (dt->cpuFeatures != SPEEX_LIB_CPU_FEATURE_NEON)
114 quality=SPEEX_RESAMPLER_QUALITY_MIN;
115 #elif !MS_HAS_ARM_NEON
116 quality=SPEEX_RESAMPLER_QUALITY_MIN;
117 #endif /*SPEEX_LIB_SET_CPU_FEATURES*/
118 #endif /*MS_HAS_ARM*/
119 ms_message("Initializing speex resampler in mode [%s] ",(quality==SPEEX_RESAMPLER_QUALITY_VOIP?"voip":"min"));
120 dt->handle=speex_resampler_init(dt->in_nchannels, dt->input_rate, dt->output_rate, quality, &err);
121 }
122
resample_preprocess(MSFilter * obj)123 static void resample_preprocess(MSFilter *obj){
124 ResampleData *dt=(ResampleData*)obj->data;
125 if(dt->handle == NULL) resample_init_speex(dt);
126 }
127
resample_process_ms2(MSFilter * obj)128 static void resample_process_ms2(MSFilter *obj){
129 ResampleData *dt=(ResampleData*)obj->data;
130 mblk_t *im, *om = NULL, *om_chan = NULL;
131
132 if (dt->output_rate==dt->input_rate){
133 while((im=ms_queue_get(obj->inputs[0]))!=NULL){
134 if (resample_channel_adapt(dt->in_nchannels, dt->out_nchannels, im, &om) == 0) {
135 ms_queue_put(obj->outputs[0], im);
136 } else {
137 ms_queue_put(obj->outputs[0], om);
138 freemsg(im);
139 }
140 }
141 return;
142 }
143 ms_filter_lock(obj);
144 if (dt->handle!=NULL){
145 spx_uint32_t inrate=0, outrate=0;
146 speex_resampler_get_rate(dt->handle,&inrate,&outrate);
147 if ((uint32_t)inrate!=dt->input_rate || (uint32_t)outrate!=dt->output_rate){
148 speex_resampler_destroy(dt->handle);
149 dt->handle=0;
150 }
151 }
152 if (dt->handle==NULL){
153 resample_init_speex(dt);
154 }
155
156
157 while((im=ms_queue_get(obj->inputs[0]))!=NULL){
158 spx_uint32_t inlen=(spx_uint32_t)((im->b_wptr-im->b_rptr)/(2*dt->in_nchannels));
159 spx_uint32_t outlen=(spx_uint32_t)(((inlen*dt->output_rate)/dt->input_rate)+1);
160 spx_uint32_t inlen_orig=inlen;
161 om=allocb(outlen*2*dt->in_nchannels,0);
162 mblk_meta_copy(im, om);
163 if (dt->in_nchannels==1){
164 speex_resampler_process_int(dt->handle,
165 0,
166 (spx_int16_t*)im->b_rptr,
167 &inlen,
168 (spx_int16_t*)om->b_wptr,
169 &outlen);
170 }else{
171 speex_resampler_process_interleaved_int(dt->handle,
172 (int16_t*)im->b_rptr,
173 &inlen,
174 (int16_t*)om->b_wptr,
175 &outlen);
176 }
177 if (inlen_orig!=inlen){
178 ms_error("Bug in resampler ! only %u samples consumed instead of %u, out=%u",
179 (unsigned int)inlen,(unsigned int)inlen_orig,(unsigned int)outlen);
180 }
181 om->b_wptr+=outlen*2*dt->in_nchannels;
182 mblk_set_timestamp_info(om,dt->ts);
183 dt->ts+=outlen;
184 if (resample_channel_adapt(dt->in_nchannels, dt->out_nchannels, om, &om_chan) == 0) {
185 ms_queue_put(obj->outputs[0], om);
186 } else {
187 ms_queue_put(obj->outputs[0], om_chan);
188 freemsg(om);
189 }
190 freemsg(im);
191 }
192 ms_filter_unlock(obj);
193 }
194
195
ms_resample_set_sr(MSFilter * obj,void * arg)196 static int ms_resample_set_sr(MSFilter *obj, void *arg){
197 ResampleData *dt=(ResampleData*)obj->data;
198 dt->input_rate=((int*)arg)[0];
199 return 0;
200 }
201
ms_resample_set_output_sr(MSFilter * obj,void * arg)202 static int ms_resample_set_output_sr(MSFilter *obj, void *arg){
203 ResampleData *dt=(ResampleData*)obj->data;
204 dt->output_rate=((int*)arg)[0];
205 return 0;
206 }
207
set_input_nchannels(MSFilter * f,void * arg)208 static int set_input_nchannels(MSFilter *f, void *arg){
209 ResampleData *dt=(ResampleData*)f->data;
210 int chans=*(int*)arg;
211 ms_filter_lock(f);
212 if (dt->in_nchannels!=chans && dt->handle!=NULL){
213 speex_resampler_destroy(dt->handle);
214 dt->handle=NULL;
215 }
216 dt->in_nchannels=chans;
217 ms_filter_unlock(f);
218 return 0;
219 }
220
set_output_nchannels(MSFilter * f,void * arg)221 static int set_output_nchannels(MSFilter *f, void *arg) {
222 ResampleData *dt = (ResampleData *)f->data;
223 int chans = *(int *)arg;
224 ms_filter_lock(f);
225 if (dt->out_nchannels != chans && dt->handle != NULL) {
226 speex_resampler_destroy(dt->handle);
227 dt->handle = NULL;
228 }
229 dt->out_nchannels = chans;
230 ms_filter_unlock(f);
231 return 0;
232 }
233
234 static MSFilterMethod methods[]={
235 { MS_FILTER_SET_SAMPLE_RATE , ms_resample_set_sr },
236 { MS_FILTER_SET_OUTPUT_SAMPLE_RATE , ms_resample_set_output_sr },
237 { MS_FILTER_SET_NCHANNELS, set_input_nchannels },
238 { MS_FILTER_SET_OUTPUT_NCHANNELS, set_output_nchannels },
239 { 0 , NULL }
240 };
241
242 #ifdef _MSC_VER
243
244 MSFilterDesc ms_resample_desc={
245 MS_RESAMPLE_ID,
246 "MSResample",
247 N_("Audio resampler"),
248 MS_FILTER_OTHER,
249 NULL,
250 1,
251 1,
252 resample_init,
253 resample_preprocess,
254 resample_process_ms2,
255 NULL,
256 resample_uninit,
257 methods
258 };
259
260 #else
261
262 MSFilterDesc ms_resample_desc={
263 .id=MS_RESAMPLE_ID,
264 .name="MSResample",
265 .text=N_("Audio resampler"),
266 .category=MS_FILTER_OTHER,
267 .ninputs=1,
268 .noutputs=1,
269 .init=resample_init,
270 .preprocess=resample_preprocess,
271 .process=resample_process_ms2,
272 .uninit=resample_uninit,
273 .methods=methods
274 };
275
276 #endif
277
278 MS_FILTER_DESC_EXPORT(ms_resample_desc)
279
280
281
282