Skip to content

Commit ac30884

Browse files
committed
Span API support (#51)
* Span API support - Remove auto cast `arr_real` to `arr_cmplx`; - Add `complex(re)` function; - Add `span` implementation; - Return `span` from `slice` function for step=1; - Remove `base_array` reference from `slice`; - Check same array by pointer range; - Simplify cast for `cmplx_t` type; - Add span iterators; - Add span api for some functions; - Add span API for `concatenate`. Empty span support; - Remove base_array::operator(int i); - Add non-copy fft solve functions; - Add CMakePresets.json; - Remove deprecated; - Rename `slice_t` to `mut_slice_t`; - Rename `const_slice_t` to `slice_t`; - Limit the set of types for base_array<T>; - Rename `span` to `make_span`; - Add some `DSPLIB_ASSERT`; - Fix benchs error; - Code refactoring; - Fix CI config; - Update tests;
1 parent e75ff3e commit ac30884

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1093
-504
lines changed

.github/workflows/android-ndk.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55
paths-ignore: ["README.md"]
66

77
pull_request:
8-
branches: [master]
8+
branches: [master, develop]
99

1010
env:
1111
BUILD_TYPE: Release

.github/workflows/linux.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55
paths-ignore: ["README.md"]
66

77
pull_request:
8-
branches: [master]
8+
branches: [master, develop]
99

1010
env:
1111
BUILD_TYPE: Debug

.github/workflows/windows.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55
paths-ignore: ["README.md"]
66

77
pull_request:
8-
branches: [master]
8+
branches: [master, develop]
99

1010
env:
1111
BUILD_TYPE: Release

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ set(DSPLIB_SOURCES
4646
lib/ifft.cpp
4747
lib/czt.cpp
4848
lib/subband.cpp
49+
lib/internal/besseli.cpp
4950
)
5051

5152
if (DSPLIB_EXCLUDE_FFT)

CMakePresets.json

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{
2+
"version": 8,
3+
"cmakeMinimumRequired": {
4+
"major": 3,
5+
"minor": 18,
6+
"patch": 0
7+
},
8+
"configurePresets": [
9+
{
10+
"name": "default",
11+
"hidden": true,
12+
"generator": "Ninja",
13+
"binaryDir": "${sourceDir}/build",
14+
"cacheVariables": {
15+
"CMAKE_BUILD_TYPE": "Debug",
16+
"DSPLIB_BUILD_TESTS": "OFF",
17+
"DSPLIB_BUILD_BENCHS": "OFF",
18+
"DSPLIB_BUILD_EXAMPLES": "OFF",
19+
"DSPLIB_USE_FLOAT32": "OFF",
20+
"DSPLIB_EXCLUDE_FFT": "OFF",
21+
"DSPLIB_ASAN_ENABLED": "OFF",
22+
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
23+
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install"
24+
},
25+
"environment": {
26+
"CC": "clang",
27+
"CXX": "clang++"
28+
},
29+
"architecture": {
30+
"value": "x64",
31+
"strategy": "external"
32+
}
33+
},
34+
{
35+
"name": "tests",
36+
"inherits": "default",
37+
"cacheVariables": {
38+
"DSPLIB_BUILD_TESTS": "ON",
39+
"DSPLIB_ASAN_ENABLED": "ON"
40+
}
41+
},
42+
{
43+
"name": "benchs-float64",
44+
"inherits": "default",
45+
"cacheVariables": {
46+
"DSPLIB_BUILD_BENCHS": "ON",
47+
"CMAKE_BUILD_TYPE": "Release"
48+
}
49+
},
50+
{
51+
"name": "benchs-float32",
52+
"inherits": "default",
53+
"cacheVariables": {
54+
"DSPLIB_BUILD_BENCHS": "ON",
55+
"CMAKE_BUILD_TYPE": "Release",
56+
"DSPLIB_USE_FLOAT32": "ON"
57+
}
58+
},
59+
{
60+
"name": "examples",
61+
"inherits": "default",
62+
"cacheVariables": {
63+
"DSPLIB_BUILD_EXAMPLES": "ON",
64+
"CMAKE_BUILD_TYPE": "Release"
65+
}
66+
}
67+
]
68+
}

README.md

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ for (int i=0; i < r.size(); ++i) {
2222
r[i] = x1[i] * x2[i];
2323
}
2424

25-
auto p = fftw_plan_dft_1d(N, x, spec, FFTW_FORWARD, FFTW_ESTIMATE);
25+
auto p = fftw_plan_dft_1d(N, x.data(), spec.data(), FFTW_FORWARD, FFTW_ESTIMATE);
2626
fftw_execute(p);
2727
fftw_destroy_plan(p);
2828
```
@@ -32,7 +32,7 @@ and who likes this:
3232
```cpp
3333
using namespace dsplib;
3434
x *= 0.3;
35-
auto power = sum(abs2(*x.slice(lb, rb)));
35+
auto power = sum(abs2(x.slice(lb, rb)));
3636
auto r = x1 * x2;
3737
auto spec = fft(x);
3838
```
@@ -75,6 +75,7 @@ arr_cmplx y3 = x1 * x2;
7575
arr_cmplx y4 = x2 * 1000;
7676
arr_cmplx y5 = x2.slice(0, 2);
7777
arr_cmplx y6 = x1 * 2i;
78+
arr_cmplx y7 = complex(y1); // only explicit conversion
7879
```
7980

8081
### Slicing
@@ -90,18 +91,63 @@ x.slice(-8, 7) ///OUT_OF_RANGE, but numpy returns [0 1 2 3 4 5 6]
9091
```
9192
9293
### Fast Fourier Transform:
93-
The FFT/IFFT calculation table is cached on first run. To eliminate this behavior, you can use the FftPlan object.
94+
The `FFT` implementation has no radix size limitations.
95+
It supports power-of-two, prime, and semiprime radices.
96+
97+
If your platform has a faster implementation (e.g., `NE10` on `ARM`), you can set the `DSPLIB_EXCLUDE_FFT=ON` option and implement the `fft_plan_c`, `fft_plan_r`, `ifft_plan_c`, and `ifft_plan_r` functions (see the `FFTW` example).
98+
99+
The tables for the `FFT` are stored in the `LRU` cache and can be recalculated (if the pipeline uses many different bases). Use the `FftPlan` object to avoid this.
94100
```cpp
101+
//FFT fn
95102
arr_real x = randn(500);
96-
arr_cmplx y1 = fft(x); //500
97-
arr_cmplx y2 = fft(x, 1024); //1024
103+
arr_cmplx y1 = fft(x); // real fft, n=500
104+
arr_cmplx y2 = fft(x, 1024); // real fft, n=1024, zero padding
105+
arr_cmplx y3 = fft(complex(y1)); // cmplx fft, n=500
106+
arr_cmplx y4 = rfft(y1); // real fft, equal `fft(x)`
98107
```
99108

100-
### Inverse Fast Fourier Transform:
101109
```cpp
102-
arr_cmplx x = 1i * zeros(512);
103-
x[10] = 1;
104-
arr_cmplx y = ifft(x);
110+
//FFT Plan
111+
const int n = 512;
112+
const arr_real x = randn(n);
113+
std::shared_ptr<FftPlanR> plan = fft_plan_r(n);
114+
115+
// real fft, n=512
116+
arr_cmplx y1 = plan->solve(x);
117+
//or
118+
arr_cmplx y2 = plan->solve(x.slice(0, 512));
119+
//or
120+
arr_cmplx y3 = plan->solve(make_span(x.data(), n));
121+
122+
//real fft, n=512, result copy to `r`
123+
arr_cmplx r(n);
124+
plan->solve(x, r);
125+
//or
126+
plan->solve(make_span(x.data(), n), make_span(r.data(), n))
127+
```
128+
129+
130+
```cpp
131+
//IFFT fn
132+
const int n = 512;
133+
arr_cmplx x = complex(ones(n));
134+
arr_cmplx y1 = ifft(x);
135+
//or
136+
arr_real y2 = irfft(x.slice(0, n/2+1), n);
137+
//or
138+
arr_real y3 = irfft(x);
139+
```
140+
141+
```cpp
142+
//IFFT Plan
143+
const int n = 512;
144+
const auto x = complex(ones(n));
145+
auto plan = ifft_plan_r(n);
146+
147+
arr_real y1 = plan->solve(x);
148+
//or
149+
arr_real y2;
150+
plan->solve(make_span(x.data(), n/2+1), make_span(y2.data(), n));
105151
```
106152
107153
### FIR filter:
@@ -217,11 +263,11 @@ auto out = dsplib::resample(in, 2, 1);
217263
auto out = dsplib::resample(in, 32000, 16000);
218264
```
219265

220-
## Building
266+
## Build
221267

222268
### Requires:
223269
- CMake (>=3.10)
224-
- C++ compiler for C++17 standard (gcc, clang, msvc, mingw)
270+
- C++17 compiler (exceptions can be disabled)
225271

226272

227273
### Build and install:
@@ -241,7 +287,7 @@ CPMAddPackage(NAME dsplib
241287
GIT_REPOSITORY
242288
"https://github.com/vitalsong/dsplib.git"
243289
VERSION
244-
0.45.0
290+
0.55.3
245291
OPTIONS
246292
"DSPLIB_USE_FLOAT32 OFF"
247293
"DSPLIB_NO_EXCEPTIONS OFF"
@@ -259,6 +305,8 @@ cmake --build build
259305
./build/benchs/dsplib-benchs
260306
```
261307

308+
### FFT
309+
262310
The implementation of non-power-of-two FFT is based on the general factorization algorithm. It is usually slower, but not critical.
263311

264312
For prime and semi-prime numbers, the czt algorithm is used, which can be significantly slower (but not as slow as regular DFT).
@@ -310,9 +358,12 @@ BM_KISSFFT/16384/min_time:5.000 98.5 us 98.5 us 69101
310358
```
311359

312360
## TODO:
313-
- Select FFT backend type (fftw/ne10)
314-
- Add matrix syntax support
315-
- Add custom allocator for `base_array<T>` type
316-
- Add audioread/audiowrite functions (optional libsndfile?)
317-
- Add chain syntax like `fft(x)->abs2()->pow2db()`
318-
- Use `const_span<T>` args
361+
- Add matrix syntax support;
362+
- Add custom allocator for `base_array<T>` type;
363+
- Add audioread/audiowrite functions (optional libsndfile?);
364+
- Add chain syntax like `fft(x)->abs2()->pow2db()`;
365+
- `SOS` filters;
366+
- Multichannel resampler;
367+
- Thread-safe storage for `FFT` (not `thread_local`);
368+
- Add `chirp`, `conv`, `filter`, `dzt`, `remez` etc.
369+
- Real/Imag slice for `arr_cmplx`;

benchs/adaptive.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
static std::pair<dsplib::arr_real, dsplib::arr_real> _prepare_frame(int M, int L) {
55
const auto h = dsplib::randn(M);
6-
auto flt = dsplib::FftFilter(h);
6+
auto flt = dsplib::FirFilter(h);
77
auto x = dsplib::randn(L);
88
auto n = 0.01 * dsplib::randn(L);
99
auto d = flt(x) + n;

examples/fftw-backend/fft.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class FFTWPlanC : public FftPlanC
2828
fftw_free(in_);
2929
}
3030

31-
dsplib::arr_cmplx solve(const dsplib::arr_cmplx& x) const final {
31+
dsplib::arr_cmplx solve(span_t<cmplx_t> x) const final {
3232
DSPLIB_ASSERT(x.size() == n_, "input size must be equal `n`");
3333
std::memcpy(in_, x.data(), n_ * sizeof(x[0]));
3434
fftw_execute(plan_);
@@ -66,7 +66,7 @@ class FFTWPlanR : public FftPlanR
6666
fftw_free(in_);
6767
}
6868

69-
dsplib::arr_cmplx solve(const dsplib::arr_real& x) const final {
69+
dsplib::arr_cmplx solve(span_t<real_t> x) const final {
7070
DSPLIB_ASSERT(x.size() == n_, "input size must be equal `n`");
7171
std::memcpy(in_, x.data(), n_ * sizeof(x[0]));
7272
fftw_execute(plan_);
@@ -105,7 +105,7 @@ class IFFTWPlanR : public IfftPlanR
105105
fftw_free(in_);
106106
}
107107

108-
dsplib::arr_real solve(const dsplib::arr_cmplx& x) const final {
108+
dsplib::arr_real solve(span_t<cmplx_t> x) const final {
109109
const int n2 = n_ / 2 + 1;
110110
DSPLIB_ASSERT((x.size() == n_) || (x.size() == n2), "input size must be equal `n` or `n/2+1`");
111111
std::memcpy(in_, x.data(), n2 * sizeof(x[0]));

0 commit comments

Comments
 (0)