msresample.c 7.52 KB
Newer Older
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

26
#include <speex/speex_resampler.h>
27
#include <speex/speex.h>
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;
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
#ifdef __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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
static int resample_channel_adapt(int in_nchannels, int out_nchannels, mblk_t *im, mblk_t **om) {
	if ((in_nchannels == 2) && (out_nchannels == 1)) {
		int msgsize = msgdsize(im) / 2;
		*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)) {
		int msgsize = msgdsize(im) * 2;
		*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;
		}
		return 1;
	}
	return 0;
}

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
static void resample_init_speex(ResampleData *dt){
	int err=0;
	int quality=SPEEX_RESAMPLER_QUALITY_VOIP; /*default value is voip*/
#if __arm__ /*on ARM, NEON optimization are mandatory to support this quality, else using basic mode*/
#if SPEEX_LIB_SET_CPU_FEATURES
	if (dt->cpuFeatures != SPEEX_LIB_CPU_FEATURE_NEON)
		quality=SPEEX_RESAMPLER_QUALITY_MIN;
#elif !__ARM_NEON__
	quality=SPEEX_RESAMPLER_QUALITY_MIN;
#endif /*SPEEX_LIB_SET_CPU_FEATURES*/
#endif /*__arm__*/
	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);
}

128 129
static void resample_process_ms2(MSFilter *obj){
	ResampleData *dt=(ResampleData*)obj->data;
130
	mblk_t *im, *om = NULL, *om_chan = NULL;
131 132
	
	if (dt->output_rate==dt->input_rate){
133 134 135 136 137 138 139
		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);
			}
140
		}
141
		return;
142
	}
143
	ms_filter_lock(obj);
144 145 146 147 148 149 150 151 152
	if (dt->handle!=NULL){
		unsigned int inrate=0, outrate=0;
		speex_resampler_get_rate(dt->handle,&inrate,&outrate);
		if (inrate!=dt->input_rate || outrate!=dt->output_rate){
			speex_resampler_destroy(dt->handle);
			dt->handle=0;
		}
	}
	if (dt->handle==NULL){
153
		resample_init_speex(dt);
154 155 156
	}

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


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

202 203 204 205
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
206 207
}

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 214 215
		speex_resampler_destroy(dt->handle);
		dt->handle=NULL;
	}
216 217 218 219 220 221 222 223 224 225 226 227 228 229
	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;
230 231
	ms_filter_unlock(f);
	return 0;
aymeric's avatar
aymeric committed
232 233
}

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

#ifdef _MSC_VER

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

#else

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

#endif

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