msresample.c 7.7 KB
Newer Older
jehan's avatar
jehan committed
1
/*
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
aymeric's avatar
aymeric committed
19 20 21

#include "mediastreamer2/msfilter.h"

aymeric's avatar
aymeric committed
22 23 24 25
#ifdef _MSC_VER
#include <malloc.h>
#endif

aymeric's avatar
aymeric committed
26
#include <speex/speex_resampler.h>
27
#include <speex/speex.h>
jehan's avatar
jehan committed
28

29 30 31 32
#ifdef ANDROID
#include "cpu-features.h"
#endif
	
aymeric's avatar
aymeric committed
33 34 35 36 37
typedef struct _ResampleData{
	MSBufferizer *bz;
	uint32_t ts;
	uint32_t input_rate;
	uint32_t output_rate;
38 39
	int in_nchannels;
	int out_nchannels;
aymeric's avatar
aymeric committed
40
	SpeexResamplerState *handle;
41
	int cpuFeatures; /*store because there is no SPEEX_LIB_GET_CPU_FEATURES*/
aymeric's avatar
aymeric committed
42 43 44
} ResampleData;

static ResampleData * resample_data_new(){
45
	ResampleData *obj=ms_new0(ResampleData,1);
aymeric's avatar
aymeric committed
46 47 48 49
	obj->bz=ms_bufferizer_new();
	obj->ts=0;
	obj->input_rate=8000;
	obj->output_rate=16000;
aymeric's avatar
aymeric committed
50
	obj->handle=NULL;
51
	obj->in_nchannels=obj->out_nchannels=1;
52
	obj->cpuFeatures=0;
aymeric's avatar
aymeric committed
53 54 55 56
	return obj;
}

static void resample_data_destroy(ResampleData *obj){
57 58
	if (obj->handle!=NULL)
		speex_resampler_destroy(obj->handle);
aymeric's avatar
aymeric committed
59 60 61 62 63
	ms_bufferizer_destroy(obj->bz);
	ms_free(obj);
}

static void resample_init(MSFilter *obj){
64
	ResampleData* data=resample_data_new();
65
#ifdef SPEEX_LIB_SET_CPU_FEATURES
66
#if defined(__arm__) || defined(_M_ARM)
67 68 69
	#ifdef ANDROID
	if (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM 
		&& (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0) {
70
		data->cpuFeatures = SPEEX_LIB_CPU_FEATURE_NEON;
71
	}
72
	#elif defined(__ARM_NEON__)
73
	data->cpuFeatures = SPEEX_LIB_CPU_FEATURE_NEON;
74
	#endif
75
	ms_message("speex_lib_ctl init with neon ? %d", (data->cpuFeatures == SPEEX_LIB_CPU_FEATURE_NEON));
76
#endif
77
	speex_lib_ctl(SPEEX_LIB_SET_CPU_FEATURES, &data->cpuFeatures);
78 79 80
#else
	ms_message("speex_lib_ctl does not support SPEEX_LIB_CPU_FEATURE_NEON");
#endif
81
	obj->data=data;
aymeric's avatar
aymeric committed
82 83 84
}

static void resample_uninit(MSFilter *obj){
85
	resample_data_destroy((ResampleData*)obj->data); 
aymeric's avatar
aymeric committed
86 87
}

88 89
static int resample_channel_adapt(int in_nchannels, int out_nchannels, mblk_t *im, mblk_t **om) {
	if ((in_nchannels == 2) && (out_nchannels == 1)) {
90
		size_t msgsize = msgdsize(im) / 2;
91 92 93 94 95 96
		*om = allocb(msgsize, 0);
		for (; im->b_rptr < im->b_wptr; im->b_rptr += 4, (*om)->b_wptr += 2) {
			*(int16_t *)(*om)->b_wptr = *(int16_t *)im->b_rptr;
		}
		return 1;
	} else if ((in_nchannels == 1) && (out_nchannels == 2)) {
97
		size_t msgsize = msgdsize(im) * 2;
98 99 100 101 102
		*om = allocb(msgsize, 0);
		for (; im->b_rptr < im->b_wptr; im->b_rptr += 2, (*om)->b_wptr += 4) {
			((int16_t *)(*om)->b_wptr)[0] = *(int16_t *)im->b_rptr;
			((int16_t *)(*om)->b_wptr)[1] = *(int16_t *)im->b_rptr;
		}
103
		mblk_meta_copy(im, *om);
104 105 106 107 108
		return 1;
	}
	return 0;
}

109 110 111
static void resample_init_speex(ResampleData *dt){
	int err=0;
	int quality=SPEEX_RESAMPLER_QUALITY_VOIP; /*default value is voip*/
112
#if defined(__arm__) || defined(_M_ARM) /*on ARM, NEON optimization are mandatory to support this quality, else using basic mode*/
113 114 115
#if SPEEX_LIB_SET_CPU_FEATURES
	if (dt->cpuFeatures != SPEEX_LIB_CPU_FEATURE_NEON)
		quality=SPEEX_RESAMPLER_QUALITY_MIN;
116
#elif !defined(__ARM_NEON__)
117 118
	quality=SPEEX_RESAMPLER_QUALITY_MIN;
#endif /*SPEEX_LIB_SET_CPU_FEATURES*/
119
#endif /*defined(__arm__) || defined(_M_ARM)*/
120 121 122 123 124 125 126 127 128
	ms_message("Initializing speex resampler in mode [%s] ",(quality==SPEEX_RESAMPLER_QUALITY_VOIP?"voip":"min"));
	dt->handle=speex_resampler_init(dt->in_nchannels, dt->input_rate, dt->output_rate, quality, &err);
}

static void resample_preprocess(MSFilter *obj){
	ResampleData *dt=(ResampleData*)obj->data;
	resample_init_speex(dt);
}

129 130
static void resample_process_ms2(MSFilter *obj){
	ResampleData *dt=(ResampleData*)obj->data;
131
	mblk_t *im, *om = NULL, *om_chan = NULL;
132 133
	
	if (dt->output_rate==dt->input_rate){
134 135 136 137 138 139 140
		while((im=ms_queue_get(obj->inputs[0]))!=NULL){
			if (resample_channel_adapt(dt->in_nchannels, dt->out_nchannels, im, &om) == 0) {
				ms_queue_put(obj->outputs[0], im);
			} else {
				ms_queue_put(obj->outputs[0], om);
				freemsg(im);
			}
141
		}
142
		return;
143
	}
144
	ms_filter_lock(obj);
145
	if (dt->handle!=NULL){
146
		spx_uint32_t inrate=0, outrate=0;
147
		speex_resampler_get_rate(dt->handle,&inrate,&outrate);
148
		if ((uint32_t)inrate!=dt->input_rate || (uint32_t)outrate!=dt->output_rate){
149 150 151 152 153
			speex_resampler_destroy(dt->handle);
			dt->handle=0;
		}
	}
	if (dt->handle==NULL){
154
		resample_init_speex(dt);
155 156 157
	}

	
158
	while((im=ms_queue_get(obj->inputs[0]))!=NULL){
159 160 161
		spx_uint32_t inlen=(spx_uint32_t)((im->b_wptr-im->b_rptr)/(2*dt->in_nchannels));
		spx_uint32_t outlen=(spx_uint32_t)(((inlen*dt->output_rate)/dt->input_rate)+1);
		spx_uint32_t inlen_orig=inlen;
162 163 164
		om=allocb(outlen*2*dt->in_nchannels,0);
		mblk_meta_copy(im, om);
		if (dt->in_nchannels==1){
165 166
			speex_resampler_process_int(dt->handle, 
					0, 
167
					(spx_int16_t*)im->b_rptr, 
168
					&inlen, 
169
					(spx_int16_t*)om->b_wptr, 
170 171 172
					&outlen);
		}else{
			speex_resampler_process_interleaved_int(dt->handle, 
173
					(int16_t*)im->b_rptr, 
174 175 176 177
					&inlen, 
					(int16_t*)om->b_wptr, 
					&outlen);
		}
178 179 180 181
		if (inlen_orig!=inlen){
			ms_error("Bug in resampler ! only %u samples consumed instead of %u, out=%u",
				inlen,inlen_orig,outlen);
		}
182
		om->b_wptr+=outlen*2*dt->in_nchannels;
183 184
		mblk_set_timestamp_info(om,dt->ts);
		dt->ts+=outlen;
185 186 187 188 189 190 191
		if (resample_channel_adapt(dt->in_nchannels, dt->out_nchannels, om, &om_chan) == 0) {
			ms_queue_put(obj->outputs[0], om);
		} else {
			ms_queue_put(obj->outputs[0], om_chan);
			freemsg(om);
		}
		freemsg(im);
192
	}
193
	ms_filter_unlock(obj);
194 195 196
}


197 198 199 200 201
static int ms_resample_set_sr(MSFilter *obj, void *arg){
	ResampleData *dt=(ResampleData*)obj->data;
	dt->input_rate=((int*)arg)[0];
	return 0;
}
202

203 204 205 206
static int ms_resample_set_output_sr(MSFilter *obj, void *arg){
	ResampleData *dt=(ResampleData*)obj->data;
	dt->output_rate=((int*)arg)[0];
	return 0;
aymeric's avatar
aymeric committed
207 208
}

209
static int set_input_nchannels(MSFilter *f, void *arg){
210
	ResampleData *dt=(ResampleData*)f->data;
211
	int chans=*(int*)arg;
212
	ms_filter_lock(f);
213
	if (dt->in_nchannels!=chans && dt->handle!=NULL){
214 215 216
		speex_resampler_destroy(dt->handle);
		dt->handle=NULL;
	}
217 218 219 220 221 222 223 224 225 226 227 228 229 230
	dt->in_nchannels=chans;
	ms_filter_unlock(f);
	return 0;
}

static int set_output_nchannels(MSFilter *f, void *arg) {
	ResampleData *dt = (ResampleData *)f->data;
	int chans = *(int *)arg;
	ms_filter_lock(f);
	if (dt->out_nchannels != chans && dt->handle != NULL) {
		speex_resampler_destroy(dt->handle);
		dt->handle = NULL;
	}
	dt->out_nchannels = chans;
231 232
	ms_filter_unlock(f);
	return 0;
aymeric's avatar
aymeric committed
233 234
}

235 236 237
static MSFilterMethod methods[]={
	{	MS_FILTER_SET_SAMPLE_RATE	 ,	ms_resample_set_sr		},
	{	MS_FILTER_SET_OUTPUT_SAMPLE_RATE ,	ms_resample_set_output_sr	},
238 239
	{	MS_FILTER_SET_NCHANNELS,		set_input_nchannels		},
	{	MS_FILTER_SET_OUTPUT_NCHANNELS,		set_output_nchannels		},
240
	{	0				 ,	NULL	}
aymeric's avatar
aymeric committed
241 242 243 244 245 246 247
};

#ifdef _MSC_VER

MSFilterDesc ms_resample_desc={
	MS_RESAMPLE_ID,
	"MSResample",
248
	N_("Audio resampler"),
aymeric's avatar
aymeric committed
249 250 251 252 253
	MS_FILTER_OTHER,
	NULL,
	1,
	1,
	resample_init,
254
	resample_preprocess,
aymeric's avatar
aymeric committed
255
	resample_process_ms2,
256
	NULL,
aymeric's avatar
aymeric committed
257
	resample_uninit,
258
	methods
aymeric's avatar
aymeric committed
259 260 261 262 263 264 265
};

#else

MSFilterDesc ms_resample_desc={
	.id=MS_RESAMPLE_ID,
	.name="MSResample",
266
	.text=N_("Audio resampler"),
aymeric's avatar
aymeric committed
267 268 269 270
	.category=MS_FILTER_OTHER,
	.ninputs=1,
	.noutputs=1,
	.init=resample_init,
271
	.preprocess=resample_preprocess,
aymeric's avatar
aymeric committed
272 273
	.process=resample_process_ms2,
	.uninit=resample_uninit,
274
	.methods=methods
aymeric's avatar
aymeric committed
275 276 277 278 279
};

#endif

MS_FILTER_DESC_EXPORT(ms_resample_desc)
Simon Morlat's avatar
cleanup  
Simon Morlat committed
280 281 282