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