blob: 910c7ff604a30fef380b61d9de238470ca7ce75e [file] [log] [blame]
Thomas Tsou03e6ecf2013-08-20 20:54:54 -04001/*
2 * Rational Sample Rate Conversion
3 * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
4 *
Pau Espin Pedrol21d03d32019-07-22 12:05:52 +02005 * SPDX-License-Identifier: LGPL-2.1+
6 *
Thomas Tsou03e6ecf2013-08-20 20:54:54 -04007 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040016 */
17
18#include <stdlib.h>
19#include <math.h>
20#include <string.h>
21#include <malloc.h>
22#include <iostream>
Tom Tsoud2e5c562017-06-19 15:23:59 -070023#include <algorithm>
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040024
25#include "Resampler.h"
26
27extern "C" {
28#include "convolve.h"
29}
30
31#ifndef M_PI
32#define M_PI 3.14159265358979323846264338327f
33#endif
34
35#define MAX_OUTPUT_LEN 4096
36
Tom Tsoud2e5c562017-06-19 15:23:59 -070037using namespace std;
38
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040039static float sinc(float x)
40{
41 if (x == 0.0)
42 return 0.9999999999;
43
44 return sin(M_PI * x) / (M_PI * x);
45}
46
Tom Tsoud2e5c562017-06-19 15:23:59 -070047void Resampler::initFilters(float bw)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040048{
Tom Tsoud2e5c562017-06-19 15:23:59 -070049 float cutoff;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040050 float sum = 0.0f, scale = 0.0f;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040051
Pau Espin Pedrolbdb970e2019-07-22 12:03:39 +020052 /*
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040053 * Allocate partition filters and the temporary prototype filter
54 * according to numerator of the rational rate. Coefficients are
55 * real only and must be 16-byte memory aligned for SSE usage.
56 */
Tom Tsoud2e5c562017-06-19 15:23:59 -070057 auto proto = vector<float>(p * filt_len);
58 for (auto &part : partitions)
59 part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040060
Pau Espin Pedrolbdb970e2019-07-22 12:03:39 +020061 /*
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040062 * Generate the prototype filter with a Blackman-harris window.
63 * Scale coefficients with DC filter gain set to unity divided
Pau Espin Pedrolbdb970e2019-07-22 12:03:39 +020064 * by the number of filter partitions.
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040065 */
66 float a0 = 0.35875;
67 float a1 = 0.48829;
68 float a2 = 0.14128;
69 float a3 = 0.01168;
70
71 if (p > q)
72 cutoff = (float) p;
73 else
74 cutoff = (float) q;
75
Tom Tsoud2e5c562017-06-19 15:23:59 -070076 float midpt = (proto.size() - 1) / 2.0;
77 for (size_t i = 0; i < proto.size(); i++) {
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040078 proto[i] = sinc(((float) i - midpt) / cutoff * bw);
79 proto[i] *= a0 -
Tom Tsoud2e5c562017-06-19 15:23:59 -070080 a1 * cos(2 * M_PI * i / (proto.size() - 1)) +
81 a2 * cos(4 * M_PI * i / (proto.size() - 1)) -
82 a3 * cos(6 * M_PI * i / (proto.size() - 1));
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040083 sum += proto[i];
84 }
85 scale = p / sum;
86
87 /* Populate filter partitions from the prototype filter */
88 for (size_t i = 0; i < filt_len; i++) {
Tom Tsoud2e5c562017-06-19 15:23:59 -070089 for (size_t n = 0; n < p; n++)
90 partitions[n][i] = complex<float>(proto[i * p + n] * scale);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040091 }
92
Tom Tsoud2e5c562017-06-19 15:23:59 -070093 /* Store filter taps in reverse */
94 for (auto &part : partitions)
95 reverse(&part[0], &part[filt_len]);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040096}
97
Eric7a52e422020-08-14 03:11:22 +020098#ifndef __OPTIMIZE__
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040099static bool check_vec_len(int in_len, int out_len, int p, int q)
100{
101 if (in_len % q) {
102 std::cerr << "Invalid input length " << in_len
103 << " is not multiple of " << q << std::endl;
104 return false;
105 }
106
107 if (out_len % p) {
108 std::cerr << "Invalid output length " << out_len
109 << " is not multiple of " << p << std::endl;
110 return false;
111 }
112
113 if ((in_len / q) != (out_len / p)) {
114 std::cerr << "Input/output block length mismatch" << std::endl;
115 std::cerr << "P = " << p << ", Q = " << q << std::endl;
116 std::cerr << "Input len: " << in_len << std::endl;
117 std::cerr << "Output len: " << out_len << std::endl;
118 return false;
119 }
120
121 if (out_len > MAX_OUTPUT_LEN) {
122 std::cerr << "Block length of " << out_len
123 << " exceeds max of " << MAX_OUTPUT_LEN << std::endl;
124 return false;
125 }
126
127 return true;
128}
Eric7a52e422020-08-14 03:11:22 +0200129#endif
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400130
Tom Tsou28670fb2015-08-21 19:32:58 -0700131int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400132{
133 int n, path;
Eric7a52e422020-08-14 03:11:22 +0200134#ifndef __OPTIMIZE__
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400135 if (!check_vec_len(in_len, out_len, p, q))
Tom Tsoud3253432016-03-06 03:08:01 -0800136 return -1;
Eric7a52e422020-08-14 03:11:22 +0200137#endif
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400138 /* Generate output from precomputed input/output paths */
139 for (size_t i = 0; i < out_len; i++) {
Pau Espin Pedrolbdb970e2019-07-22 12:03:39 +0200140 n = in_index[i];
141 path = out_path[i];
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400142
143 convolve_real(in, in_len,
Tom Tsoud2e5c562017-06-19 15:23:59 -0700144 reinterpret_cast<float *>(partitions[path]),
145 filt_len, &out[2 * i], out_len - i,
Sylvain Munauta3934a12018-12-20 19:10:26 +0100146 n, 1);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400147 }
148
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400149 return out_len;
150}
151
152bool Resampler::init(float bw)
153{
Tom Tsoud2e5c562017-06-19 15:23:59 -0700154 if (p == 0 || q == 0 || filt_len == 0) return false;
155
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400156 /* Filterbank filter internals */
Tom Tsoud2e5c562017-06-19 15:23:59 -0700157 initFilters(bw);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400158
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400159 /* Precompute filterbank paths */
Tom Tsoud2e5c562017-06-19 15:23:59 -0700160 int i = 0;
161 for (auto &index : in_index)
162 index = (q * i++) / p;
163 i = 0;
164 for (auto &path : out_path)
165 path = (q * i++) % p;
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400166
167 return true;
168}
169
170size_t Resampler::len()
171{
172 return filt_len;
173}
174
175Resampler::Resampler(size_t p, size_t q, size_t filt_len)
Tom Tsoud2e5c562017-06-19 15:23:59 -0700176 : in_index(MAX_OUTPUT_LEN), out_path(MAX_OUTPUT_LEN), partitions(p)
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400177{
178 this->p = p;
179 this->q = q;
180 this->filt_len = filt_len;
181}
182
183Resampler::~Resampler()
184{
Tom Tsoud2e5c562017-06-19 15:23:59 -0700185 for (auto &part : partitions)
186 free(part);
Thomas Tsou03e6ecf2013-08-20 20:54:54 -0400187}