/// <summary> /// Create a new resampler with fractional input/output rates. The sampling /// rate ratio is an arbitrary rational number with both the numerator and /// denominator being 32-bit integers. /// </summary> /// <param name="nb_channels">The number of channels to be processed</param> /// <param name="ratio_num">Numerator of sampling rate ratio</param> /// <param name="ratio_den">Denominator of sampling rate ratio</param> /// <param name="in_rate">Input sample rate rounded to the nearest integer (in hz)</param> /// <param name="out_rate">Output sample rate rounded to the nearest integer (in hz)</param> /// <param name="quality">Resampling quality, from 0 to 10</param> /// <returns>A newly created restampler</returns> public SpeexResampler(int nb_channels, int ratio_num, int ratio_den, int in_rate, int out_rate, int quality) { int i; if (quality > 10 || quality < 0) { throw new ArgumentException("Quality must be between 0 and 10"); } this.initialised = 0; this.started = 0; this.in_rate = 0; this.out_rate = 0; this.num_rate = 0; this.den_rate = 0; this.quality = -1; this.sinc_table_length = 0; this.mem_alloc_size = 0; this.filt_len = 0; this.mem = null; this.resampler_ptr = null; this.cutoff = 1.0f; this.nb_channels = nb_channels; this.in_stride = 1; this.out_stride = 1; this.buffer_size = 160; /* Per channel data */ this.last_sample = new int[nb_channels]; this.magic_samples = new int[nb_channels]; this.samp_frac_num = new int[nb_channels]; for (i = 0; i < nb_channels; i++) { this.last_sample[i] = 0; this.magic_samples[i] = 0; this.samp_frac_num[i] = 0; } this.Quality = quality; this.SetRateFraction(ratio_num, ratio_den, in_rate, out_rate); this.update_filter(); this.initialised = 1; }
private void update_filter() { int old_length; old_length = this.filt_len; this.oversample = QualityMapping.quality_map[this.quality].oversample; this.filt_len = QualityMapping.quality_map[this.quality].base_length; if (this.num_rate > this.den_rate) { /* down-sampling */ this.cutoff = QualityMapping.quality_map[this.quality].downsample_bandwidth * this.den_rate / this.num_rate; /* FIXME: divide the numerator and denominator by a certain amount if they're too large */ this.filt_len = this.filt_len * this.num_rate / this.den_rate; /* Round up to make sure we have a multiple of 8 */ this.filt_len = ((this.filt_len - 1) & (~0x7)) + 8; if (2 * this.den_rate < this.num_rate) { this.oversample >>= 1; } if (4 * this.den_rate < this.num_rate) { this.oversample >>= 1; } if (8 * this.den_rate < this.num_rate) { this.oversample >>= 1; } if (16 * this.den_rate < this.num_rate) { this.oversample >>= 1; } if (this.oversample < 1) { this.oversample = 1; } } else { /* up-sampling */ this.cutoff = QualityMapping.quality_map[this.quality].upsample_bandwidth; } if (this.den_rate <= 16 * (this.oversample + 8)) { int i; if (this.sinc_table == null) { this.sinc_table = new short[this.filt_len * this.den_rate]; } else if (this.sinc_table_length < this.filt_len * this.den_rate) { this.sinc_table = new short[this.filt_len * this.den_rate]; this.sinc_table_length = this.filt_len * this.den_rate; } for (i = 0; i < this.den_rate; i++) { int j; for (j = 0; j < this.filt_len; j++) { this.sinc_table[i * this.filt_len + j] = sinc(this.cutoff, ((j - (int)this.filt_len / 2 + 1) - ((float)i) / this.den_rate), this.filt_len, QualityMapping.quality_map[this.quality].window_func); } } this.resampler_ptr = resampler_basic_direct_single; /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/ } else { int i; if (this.sinc_table == null) { this.sinc_table = new short[this.filt_len * this.oversample + 8]; } else if (this.sinc_table_length < this.filt_len * this.oversample + 8) { this.sinc_table = new short[this.filt_len * this.oversample + 8]; this.sinc_table_length = this.filt_len * this.oversample + 8; } for (i = -4; i < (int)(this.oversample * this.filt_len + 4); i++) { this.sinc_table[i + 4] = sinc(this.cutoff, (i / (float)this.oversample - this.filt_len / 2), this.filt_len, QualityMapping.quality_map[this.quality].window_func); } this.resampler_ptr = resampler_basic_interpolate_single; /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/ } this.int_advance = this.num_rate / this.den_rate; this.frac_advance = this.num_rate % this.den_rate; /* Here's the place where we update the filter memory to take into account * the change in filter length. It's probably the messiest part of the code * due to handling of lots of corner cases. */ if (this.mem == null) { int i; this.mem_alloc_size = this.filt_len - 1 + this.buffer_size; this.mem = new short[this.nb_channels * this.mem_alloc_size]; for (i = 0; i < this.nb_channels * this.mem_alloc_size; i++) { this.mem[i] = 0; } /*speex_warning("init filter");*/ } else if (this.started == 0) { int i; this.mem_alloc_size = this.filt_len - 1 + this.buffer_size; this.mem = new short[this.nb_channels * this.mem_alloc_size]; for (i = 0; i < this.nb_channels * this.mem_alloc_size; i++) { this.mem[i] = 0; } /*speex_warning("reinit filter");*/ } else if (this.filt_len > old_length) { int i; /* Increase the filter length */ /*speex_warning("increase filter size");*/ int old_alloc_size = this.mem_alloc_size; if ((this.filt_len - 1 + this.buffer_size) > this.mem_alloc_size) { this.mem_alloc_size = this.filt_len - 1 + this.buffer_size; this.mem = new short[this.nb_channels * this.mem_alloc_size]; } for (i = this.nb_channels - 1; i >= 0; i--) { int j; int olen = old_length; /*if (st.magic_samples[i])*/ { /* Try and remove the magic samples as if nothing had happened */ /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */ olen = old_length + 2 * this.magic_samples[i]; for (j = old_length - 2 + this.magic_samples[i]; j >= 0; j--) { this.mem[i * this.mem_alloc_size + j + this.magic_samples[i]] = this.mem[i * old_alloc_size + j]; } for (j = 0; j < this.magic_samples[i]; j++) { this.mem[i * this.mem_alloc_size + j] = 0; } this.magic_samples[i] = 0; } if (this.filt_len > olen) { /* If the new filter length is still bigger than the "augmented" length */ /* Copy data going backward */ for (j = 0; j < olen - 1; j++) { this.mem[i * this.mem_alloc_size + (this.filt_len - 2 - j)] = this.mem[i * this.mem_alloc_size + (olen - 2 - j)]; } /* Then put zeros for lack of anything better */ for (; j < this.filt_len - 1; j++) { this.mem[i * this.mem_alloc_size + (this.filt_len - 2 - j)] = 0; } /* Adjust last_sample */ this.last_sample[i] += (this.filt_len - olen) / 2; } else { /* Put back some of the magic! */ this.magic_samples[i] = (olen - this.filt_len) / 2; for (j = 0; j < this.filt_len - 1 + this.magic_samples[i]; j++) { this.mem[i * this.mem_alloc_size + j] = this.mem[i * this.mem_alloc_size + j + this.magic_samples[i]]; } } } } else if (this.filt_len < old_length) { int i; /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic" * samples so they can be used directly as input the next time(s) */ for (i = 0; i < this.nb_channels; i++) { int j; int old_magic = this.magic_samples[i]; this.magic_samples[i] = (old_length - this.filt_len) / 2; /* We must copy some of the memory that's no longer used */ /* Copy data going backward */ for (j = 0; j < this.filt_len - 1 + this.magic_samples[i] + old_magic; j++) { this.mem[i * this.mem_alloc_size + j] = this.mem[i * this.mem_alloc_size + j + this.magic_samples[i]]; } this.magic_samples[i] += old_magic; } } }