msresample.c 6.68 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;
aymeric's avatar
aymeric committed
41 42 43 44 45 46 47 48
} ResampleData;

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

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

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

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

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
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;
}

106 107
static void resample_process_ms2(MSFilter *obj){
	ResampleData *dt=(ResampleData*)obj->data;
108
	mblk_t *im, *om = NULL, *om_chan = NULL;
109 110
	
	if (dt->output_rate==dt->input_rate){
111 112 113 114 115 116 117
		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);
			}
118
		}
119
		return;
120
	}
121
	ms_filter_lock(obj);
122 123 124 125 126 127 128 129 130 131
	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){
		int err=0;
132
		dt->handle=speex_resampler_init(dt->in_nchannels, dt->input_rate, dt->output_rate, SPEEX_RESAMPLER_QUALITY_VOIP, &err);
133 134 135
	}

	
136 137
	while((im=ms_queue_get(obj->inputs[0]))!=NULL){
		unsigned int inlen=(im->b_wptr-im->b_rptr)/(2*dt->in_nchannels);
138 139
		unsigned int outlen=((inlen*dt->output_rate)/dt->input_rate)+1;
		unsigned int inlen_orig=inlen;
140 141 142
		om=allocb(outlen*2*dt->in_nchannels,0);
		mblk_meta_copy(im, om);
		if (dt->in_nchannels==1){
143 144
			speex_resampler_process_int(dt->handle, 
					0, 
145
					(int16_t*)im->b_rptr, 
146 147 148 149 150
					&inlen, 
					(int16_t*)om->b_wptr, 
					&outlen);
		}else{
			speex_resampler_process_interleaved_int(dt->handle, 
151
					(int16_t*)im->b_rptr, 
152 153 154 155
					&inlen, 
					(int16_t*)om->b_wptr, 
					&outlen);
		}
156 157 158 159
		if (inlen_orig!=inlen){
			ms_error("Bug in resampler ! only %u samples consumed instead of %u, out=%u",
				inlen,inlen_orig,outlen);
		}
160
		om->b_wptr+=outlen*2*dt->in_nchannels;
161 162
		mblk_set_timestamp_info(om,dt->ts);
		dt->ts+=outlen;
163 164 165 166 167 168 169
		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);
170
	}
171
	ms_filter_unlock(obj);
172 173 174
}


175 176 177 178 179
static int ms_resample_set_sr(MSFilter *obj, void *arg){
	ResampleData *dt=(ResampleData*)obj->data;
	dt->input_rate=((int*)arg)[0];
	return 0;
}
180

181 182 183 184
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
185 186
}

187
static int set_input_nchannels(MSFilter *f, void *arg){
188
	ResampleData *dt=(ResampleData*)f->data;
189
	int chans=*(int*)arg;
190
	ms_filter_lock(f);
191
	if (dt->in_nchannels!=chans && dt->handle!=NULL){
192 193 194
		speex_resampler_destroy(dt->handle);
		dt->handle=NULL;
	}
195 196 197 198 199 200 201 202 203 204 205 206 207 208
	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;
209 210
	ms_filter_unlock(f);
	return 0;
aymeric's avatar
aymeric committed
211 212
}

213 214 215
static MSFilterMethod methods[]={
	{	MS_FILTER_SET_SAMPLE_RATE	 ,	ms_resample_set_sr		},
	{	MS_FILTER_SET_OUTPUT_SAMPLE_RATE ,	ms_resample_set_output_sr	},
216 217
	{	MS_FILTER_SET_NCHANNELS,		set_input_nchannels		},
	{	MS_FILTER_SET_OUTPUT_NCHANNELS,		set_output_nchannels		},
218
	{	0				 ,	NULL	}
aymeric's avatar
aymeric committed
219 220 221 222 223 224 225
};

#ifdef _MSC_VER

MSFilterDesc ms_resample_desc={
	MS_RESAMPLE_ID,
	"MSResample",
226
	N_("Audio resampler"),
aymeric's avatar
aymeric committed
227 228 229 230 231
	MS_FILTER_OTHER,
	NULL,
	1,
	1,
	resample_init,
232
	NULL,
aymeric's avatar
aymeric committed
233
	resample_process_ms2,
234
	NULL,
aymeric's avatar
aymeric committed
235
	resample_uninit,
236
	methods
aymeric's avatar
aymeric committed
237 238 239 240 241 242 243
};

#else

MSFilterDesc ms_resample_desc={
	.id=MS_RESAMPLE_ID,
	.name="MSResample",
244
	.text=N_("Audio resampler"),
aymeric's avatar
aymeric committed
245 246 247 248 249 250
	.category=MS_FILTER_OTHER,
	.ninputs=1,
	.noutputs=1,
	.init=resample_init,
	.process=resample_process_ms2,
	.uninit=resample_uninit,
251
	.methods=methods
aymeric's avatar
aymeric committed
252 253 254 255 256
};

#endif

MS_FILTER_DESC_EXPORT(ms_resample_desc)
Simon Morlat's avatar
cleanup  
Simon Morlat committed
257 258 259