Home | History | Annotate | Download | only in server
      1 /* Copyright (c) 2014 The Chromium OS Author. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file.
      4  */
      5 
      6 #include "cras_audio_area.h"
      7 #include "cras_util.h"
      8 #include "linear_resampler.h"
      9 
     10 /* A linear resampler.
     11  * Members:
     12  *    num_channels - The number of channles in once frames.
     13  *    format_bytes - The size of one frame in bytes.
     14  *    src_offset - The accumulated offset for resampled src data.
     15  *    dst_offset - The accumulated offset for resampled dst data.
     16  *    to_times_100 - The numerator of the rate factor used for SRC.
     17  *    from_times_100 - The denominator of the rate factor used for SRC.
     18  *    f - The rate factor used for linear resample.
     19  */
     20 struct linear_resampler {
     21 	unsigned int num_channels;
     22 	unsigned int format_bytes;
     23 	unsigned int src_offset;
     24 	unsigned int dst_offset;
     25 	unsigned int to_times_100;
     26 	unsigned int from_times_100;
     27 	float f;
     28 };
     29 
     30 struct linear_resampler *linear_resampler_create(unsigned int num_channels,
     31 					     unsigned int format_bytes,
     32 					     float src_rate,
     33 					     float dst_rate)
     34 {
     35 	struct linear_resampler *lr;
     36 
     37 	lr = (struct linear_resampler *)calloc(1, sizeof(*lr));
     38 	if (!lr)
     39 		return NULL;
     40 	lr->num_channels = num_channels;
     41 	lr->format_bytes = format_bytes;
     42 
     43 	linear_resampler_set_rates(lr, src_rate, dst_rate);
     44 
     45 	return lr;
     46 }
     47 
     48 void linear_resampler_destroy(struct linear_resampler *lr)
     49 {
     50 	if (lr)
     51 		free(lr);
     52 }
     53 
     54 void linear_resampler_set_rates(struct linear_resampler *lr,
     55 				float from, float to)
     56 {
     57 	lr->f = (float)to / from;
     58 	lr->to_times_100 = to * 100;
     59 	lr->from_times_100 = from * 100;
     60 	lr->src_offset = 0;
     61 	lr->dst_offset = 0;
     62 }
     63 
     64 /* Assuming the linear resampler transforms X frames of input buffer into
     65  * Y frames of output buffer. The resample method requires the last output
     66  * buffer at Y-1 be interpolated from input buffer in range (X-d, X-1) as
     67  * illustrated.
     68  *    Input Index:    ...      X-1 <--floor--|   X
     69  *    Output Index:   ... Y-1   |--ceiling-> Y
     70  *
     71  * That said, the calculation between input and output frames is based on
     72  * equations X-1 = floor(Y/f) and Y = ceil((X-1)*f).  Note that in any case
     73  * when the resampled frames number isn't sufficient to consume the first
     74  * buffer at input or output offset(index 0), always count as one buffer
     75  * used so the intput/output offset can always increment.
     76  */
     77 unsigned int linear_resampler_out_frames_to_in(struct linear_resampler *lr,
     78 					       unsigned int frames)
     79 {
     80 	float in_frames;
     81 	if (frames == 0)
     82 		return 0;
     83 
     84 	in_frames = (float)(lr->dst_offset + frames) / lr->f;
     85 	if ((in_frames > lr->src_offset))
     86 		return 1 + (unsigned int)(in_frames - lr->src_offset);
     87 	else
     88 		return 1;
     89 }
     90 
     91 unsigned int linear_resampler_in_frames_to_out(struct linear_resampler *lr,
     92 					       unsigned int frames)
     93 {
     94 	float out_frames;
     95 	if (frames == 0)
     96 		return 0;
     97 
     98 	out_frames = lr->f * (lr->src_offset + frames - 1);
     99 	if (out_frames > lr->dst_offset)
    100 		return 1 + (unsigned int)(out_frames - lr->dst_offset);
    101 	else
    102 		return 1;
    103 }
    104 
    105 int linear_resampler_needed(struct linear_resampler *lr)
    106 {
    107 	return lr->from_times_100 != lr->to_times_100;
    108 }
    109 
    110 unsigned int linear_resampler_resample(struct linear_resampler *lr,
    111 			     uint8_t *src,
    112 			     unsigned int *src_frames,
    113 			     uint8_t *dst,
    114 			     unsigned dst_frames)
    115 {
    116 	int ch;
    117 	unsigned int src_idx = 0;
    118 	unsigned int dst_idx = 0;
    119 	float src_pos;
    120 	int16_t *in, *out;
    121 
    122 	/* Check for corner cases so that we can assume both src_idx and
    123 	 * dst_idx are valid with value 0 in the loop below. */
    124 	if (dst_frames == 0 || *src_frames == 0) {
    125 		*src_frames = 0;
    126 		return 0;
    127 	}
    128 
    129 	for (dst_idx = 0; dst_idx <= dst_frames; dst_idx++) {
    130 		src_pos = (float)(lr->dst_offset + dst_idx) / lr->f;
    131 		if (src_pos > lr->src_offset)
    132 			src_pos -= lr->src_offset;
    133 		else
    134 			src_pos = 0;
    135 		src_idx = (unsigned int)src_pos;
    136 
    137 		if (src_pos > *src_frames - 1 || dst_idx >= dst_frames) {
    138 			if (src_pos > *src_frames - 1)
    139 				src_idx = *src_frames - 1;
    140 			/* When this loop stops, dst_idx is always at the last
    141 			 * used index incremented by 1. */
    142 			break;
    143 		}
    144 
    145 		in = (int16_t *)(src + src_idx * lr->format_bytes);
    146 		out = (int16_t *)(dst + dst_idx * lr->format_bytes);
    147 
    148 		/* Don't do linear interpolcation if src_pos falls on the
    149 		 * last index. */
    150 		if (src_idx == *src_frames - 1) {
    151 			for (ch = 0; ch < lr->num_channels; ch++)
    152 				out[ch] = in[ch];
    153 		} else {
    154 			for (ch = 0; ch < lr->num_channels; ch++) {
    155 				out[ch] = in[ch] + (src_pos - src_idx) *
    156 					(in[lr->num_channels + ch] - in[ch]);
    157 			}
    158 		}
    159 
    160 	}
    161 
    162 	*src_frames = src_idx + 1;
    163 
    164 	lr->src_offset += *src_frames;
    165 	lr->dst_offset += dst_idx;
    166 	while ((lr->src_offset > lr->from_times_100) &&
    167 	       (lr->dst_offset > lr->to_times_100)) {
    168 		lr->src_offset -= lr->from_times_100;
    169 		lr->dst_offset -= lr->to_times_100;
    170 	}
    171 
    172 	return dst_idx;
    173 }
    174