lanczos-resampler 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # Lanczos resampler
2
+
3
+ [![Crates.io Version](https://img.shields.io/crates/v/lanczos-resampler)](https://crates.io/crates/lanczos-resampler)
4
+ [![Docs](https://docs.rs/lanczos-resampler/badge.svg)](https://docs.rs/lanczos-resampler)
5
+ [![dependency status](https://deps.rs/repo/github/igankevich/lanczos-resampler/status.svg)](https://deps.rs/repo/github/igankevich/lanczos-resampler)
6
+
7
+ An audio resampler that uses [Lanczos filter](https://en.wikipedia.org/wiki/Lanczos_resampling)
8
+ as an alternative to traditional windowed sinc filters.
9
+ The main advantage of such approach is small number of coefficients required to store the filter state;
10
+ this results in small memory footprint and high performance.
11
+
12
+
13
+ ## Features
14
+
15
+ #### Small memory footprint
16
+
17
+ The library doesn't use memory allocation by default,
18
+ and resampler's internal state occupies less than a hundred bytes.
19
+
20
+ #### High performance
21
+
22
+ Thanks to small kernel size the processing time of a typical audio chunk is very fast (below 100 μs on a typical laptop).
23
+
24
+ #### Robustness
25
+
26
+ When you're resampling from _N_ Hz to _M_ Hz, for each _N_ input samples you will get exactly _M_ output samples[^1].
27
+ This results in predictable audio stream playback
28
+ and simplifies time synchronization between different streams (e.g. video and audio).
29
+
30
+ [^1]: Seriously, why other libraries don't have this feature?
31
+
32
+ #### JS-compatible
33
+
34
+ This library can be used in web browsers and in general in any JS engine that supports WASM.
35
+ All of the abovementioned features are inherent to both Rust and WASM versions of the library.
36
+
37
+
38
+ ## Usage
39
+
40
+ ### Kernel parameters
41
+
42
+ This library uses [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling)
43
+ approximated by _2N - 1_ points and defined on interval _[-A; A]_. The kernel is interpolated
44
+ using cubic Hermite splines with second-order finite differences at spline endpoints. The
45
+ output is clamped to _[-1; 1]_.
46
+
47
+ The recommended parameters are _N = 16, A = 3_. Using _A = 2_ might improve performance a
48
+ little bit. Using larger _N_ will techincally improve precision, but precision isn't a good
49
+ metric for audio signal. With _N = 16_ the kernel fits into exactly 64 B (the size of a cache line).
50
+
51
+ ### Interleaved vs. non-interleaved format
52
+
53
+ Non-interleaved format means that audio samples for each channel are stored in separate arrays.
54
+ To resample such data you need to call `resample` for each channel individually.
55
+
56
+ Interleaved format on the other hand means that samples for each channel are stored in a single array using frames;
57
+ a frame is a sequence of samples, one sample for each channel.
58
+ To resample such data you need to call `resample` only once.
59
+
60
+ Usually resampling interleaved data is much faster than processing each channel individually
61
+ because a CPU can process such data efficiently with SIMD instructions.
62
+
63
+
64
+ ### Rust
65
+
66
+ #### Resampling audio stream in chunks
67
+
68
+ ```rust
69
+ use lanczos_resampler::ChunkedResampler;
70
+
71
+ let n = 1024;
72
+ let chunk = vec![0.1; n];
73
+ let mut resampler = ChunkedResampler::new(44100, 48000);
74
+ let mut output: Vec<f32> = Vec::with_capacity(resampler.max_num_output_frames(n));
75
+ let num_processed = resampler.resample(&chunk[..], &mut output);
76
+ assert_eq!(n, num_processed);
77
+ ```
78
+
79
+ #### Resampling the whole audio track
80
+
81
+ ```rust
82
+ use lanczos_resampler::WholeResampler;
83
+
84
+ let n = 1024;
85
+ let track = vec![0.1; n];
86
+ let output_len = lanczos_resampler::num_output_frames(n, 44100, 48000);
87
+ let mut output = vec![0.0; output_len];
88
+ let resampler = WholeResampler::new();
89
+ let mut output_slice = &mut output[..];
90
+ let num_processed = resampler.resample_into(&track[..], &mut output_slice);
91
+ assert_eq!(n, num_processed);
92
+ assert!(output_slice.is_empty());
93
+ ```
94
+
95
+ ### JS
96
+
97
+ #### Installation
98
+
99
+ ```bash
100
+ npm install lanczos-resampler
101
+ ```
102
+
103
+ #### Resampling audio stream in chunks
104
+
105
+ ```javascript
106
+ import { ChunkedResampler } from 'lanczos-resampler';
107
+
108
+ const resampler = new ChunkedResampler(44100, 48000);
109
+ const input = new Float32Array(1024);
110
+ input.fill(0.1);
111
+ const output = new Float32Array(resampler.maxNumOutputFrames(input.length));
112
+ const numProcessed = resampler.resample(input, output);
113
+ assert.equal(input.length, numProcessed);
114
+ ```
115
+
116
+ #### Resampling the whole audio track
117
+
118
+ ```javascript
119
+ import { WholeResampler, outputLength } as lanczos from 'lanczos-resampler';
120
+
121
+ const input = new Float32Array(1024);
122
+ input.fill(0.1);
123
+ const outputLen = outputLength(1024, 44100, 48000);
124
+ const output = new Float32Array(outputLen);
125
+ const resampler = new WholeResampler();
126
+ const numProcessed = resampler.resampleInto(input, output);
127
+ assert.equal(input.length, numProcessed);
128
+ console.log(output)
129
+ ```
130
+
131
+
132
+ ## Documentation
133
+
134
+ Rust: <https://docs.rs/lanczos-resampler>
135
+
136
+ JS: <https://igankevich.github.com/lanczos-resampler>
137
+
138
+
139
+ ## No-std support
140
+
141
+ This crate supports `no_std` via [`libm`](https://docs.rs/libm/latest/libm/).
142
+ When `std` feature is enabled (the default), it uses built-in mathematical functions
143
+ which are typically much faster than `libm`.
@@ -0,0 +1,208 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ export class ChunkedInterleavedResampler {
5
+ free(): void;
6
+ [Symbol.dispose](): void;
7
+ /**
8
+ * Get maximum output chunk length given the input chunk length.
9
+ *
10
+ * Returns the same value as {@link outputLength} plus one.
11
+ * This additional sample is used to compensate for unevenly divisible sample rates.
12
+ *
13
+ * You should consider updating buffer size every time you change output sample rate via
14
+ * {@link ChunkedInterleavedResampler.outputSampleRate}.
15
+ */
16
+ maxNumOutputFrames(numInputFrames: number): number;
17
+ /**
18
+ * Creates new instance of resampler with the specified input and output sample rates and the
19
+ * number of channels.
20
+ * @param inputSampleRate - input sample rate in Hz
21
+ * @param outputSampleRate - output sample rate in Hz
22
+ * @param numChannels - number of channels
23
+ */
24
+ constructor(inputSampleRate: number, outputSampleRate: number, numChannels: number);
25
+ /**
26
+ * Resets internal state.
27
+ *
28
+ * Erases any information about the previous chunk.
29
+ *
30
+ * Use this method when you want to reuse resampler for another audio stream.
31
+ */
32
+ reset(): void;
33
+ /**
34
+ * Resamples input signal chunk from the source to the target sample rate and appends the
35
+ * resulting signal to the output.
36
+ *
37
+ * Returns the number of processed input samples. The output is clamped to _[-1; 1]_.
38
+ *
39
+ * For each {@link ChunkedInterleavedResampler.inputSampleRate} input samples this method produces exactly
40
+ * {@link ChunkedInterleavedResampler.outputSampleRate} output samples even if it is called multiple times with a smaller
41
+ * amount of input samples; the only exception is when the output sample rate was changed in the process.
42
+ *
43
+ * #### Edge cases
44
+ *
45
+ * Returns 0 when either the number of input or output frames is less than _max(2, A-1)_, adjusted in
46
+ * accordance with sample rate ratio.
47
+ *
48
+ * #### Limitations
49
+ *
50
+ * The output depends on the chunk size, hence resampling the same audio track all at once and
51
+ * in chunks will produce slightly different results. This a consequence of the fact that Lanczos kernel
52
+ * isn't an interpolation function, but a filter. To minimize such discrepancies chunk size should
53
+ * be much larger than _2⋅A + 1_.
54
+ */
55
+ resample(chunk: Float32Array, output: Float32Array): number;
56
+ /**
57
+ * Get the number of channels.
58
+ */
59
+ readonly numChannels: number;
60
+ /**
61
+ * Get input sample rate in Hz.
62
+ */
63
+ readonly inputSampleRate: number;
64
+ /**
65
+ * Get/set output sample rate in Hz.
66
+ *
67
+ * After changing the sample rate you should consider updating buffer size to
68
+ * {@link ChunkedInterleavedResampler.maxNumOutputFrames}.
69
+ */
70
+ outputSampleRate: number;
71
+ }
72
+
73
+ export class ChunkedResampler {
74
+ free(): void;
75
+ [Symbol.dispose](): void;
76
+ /**
77
+ * Get maximum output chunk length given the input chunk length.
78
+ *
79
+ * Returns the same value as {@link outputLength} plus one.
80
+ * This additional sample is used to compensate for unevenly divisible sample rates.
81
+ *
82
+ * You should consider updating buffer size every time you change output sample rate via
83
+ * {@link ChunkedResampler.outputSampleRate}.
84
+ */
85
+ maxNumOutputFrames(numInputFrames: number): number;
86
+ /**
87
+ * Create new resampler with the specified input and output sample rates.
88
+ * @param inputSampleRate - input sample rate in Hz
89
+ * @param outputSampleRate - output sample rate in Hz
90
+ */
91
+ constructor(inputSampleRate: number, outputSampleRate: number);
92
+ /**
93
+ * Resets internal state.
94
+ *
95
+ * Erases any information about the previous chunk.
96
+ *
97
+ * Use this method when you want to reuse resampler for another audio stream.
98
+ */
99
+ reset(): void;
100
+ /**
101
+ * Resamples input signal chunk from the source to the target sample rate and appends the
102
+ * resulting signal to the output.
103
+ *
104
+ * Returns the number of processed input samples. The output is clamped to _[-1; 1]_.
105
+ *
106
+ * For each {@link ChunkedResampler.inputSampleRate} input samples this method produces exactly
107
+ * {@link ChunkedResampler.outputSampleRate} output samples even if it is called multiple times with a smaller
108
+ * amount of input samples; the only exception is when the output sample rate was changed in the process.
109
+ *
110
+ * #### Edge cases
111
+ *
112
+ * Returns 0 when either the input length or output length is less than _max(2, A-1)_, adjusted in
113
+ * accordance with sample rate ratio.
114
+ *
115
+ * #### Limitations
116
+ *
117
+ * The output depends on the chunk size, hence resampling the same audio track all at once and
118
+ * in chunks will produce slightly different results. This a consequence of the fact that Lanczos kernel
119
+ * isn't an interpolation function, but a filter. To minimize such discrepancies chunk size should
120
+ * be much larger than _2⋅A + 1_.
121
+ */
122
+ resample(chunk: Float32Array, output: Float32Array): number;
123
+ /**
124
+ * Get input sample rate in Hz.
125
+ */
126
+ readonly inputSampleRate: number;
127
+ /**
128
+ * Get/set output sample rate in Hz.
129
+ *
130
+ * After changing the sample rate you should consider updating buffer size to
131
+ * {@link ChunkedResampler.maxNumOutputFrames}.
132
+ */
133
+ outputSampleRate: number;
134
+ }
135
+
136
+ export class WholeResampler {
137
+ free(): void;
138
+ [Symbol.dispose](): void;
139
+ /**
140
+ * This is a variant of {@link WholeResampler.resample} that doesn't use memory allocation.
141
+ *
142
+ * Returns the number of samples read from the input. Currently this is either 0 (see "Panics") or
143
+ * the input length.
144
+ *
145
+ * #### Edge cases
146
+ *
147
+ * Returns 0 when either the input length or remaining output length is less than 2.
148
+ *
149
+ * #### Panics
150
+ *
151
+ * Panics when the output isn't large enough to hold all the resampled points.
152
+ * Use {@link outputLength} to ensure that the buffer size is sufficient.
153
+ */
154
+ resampleInto(input: Float32Array, output: Float32Array): number;
155
+ /**
156
+ * This is a variant of {@link resampleInto} that processes several audio channels (one audio frame) at a time.
157
+ *
158
+ * #### Edge cases
159
+ *
160
+ * Returns 0 when either the number of input frames or the number of remaining output frames is less than 2.
161
+ *
162
+ * #### Panics
163
+ *
164
+ * - Panics when the output isn't large enough to hold all the resampled points.
165
+ * Use {@link outputLength} to ensure that the buffer size is sufficient.
166
+ * - Panics when either the input or the output length isn't evenly divisible by the number of
167
+ * channels.
168
+ * @param input - input frames
169
+ * @param numChannels - number of channels
170
+ * @param output
171
+ */
172
+ resampleInterleavedInto(input: Float32Array, numChannels: number, output: Float32Array): number;
173
+ /**
174
+ * Create new resampler.
175
+ */
176
+ constructor();
177
+ /**
178
+ * Resample input signal from the source to the target sample rate and
179
+ * returns the resulting output signal as a vector.
180
+ *
181
+ * #### Edge cases
182
+ *
183
+ * - Returns an empty array when either the input length or calculated output length is less than 2.
184
+ * - Returns an empty array when either the input length or the output sample rate is too large.
185
+ * @param input - input samples
186
+ * @param inputSampleRate - input sample rate in Hz
187
+ * @param outputSampleRate - output sample rate in Hz
188
+ */
189
+ resample(input: Float32Array, inputSampleRate: number, outputSampleRate: number): Float32Array;
190
+ }
191
+
192
+ /**
193
+ * Calculates resampled length of the input for the given input/output sample
194
+ * rates.
195
+ *
196
+ * #### Edge cases
197
+ *
198
+ * Returns `Number.NAN` when the input length or the output sample rate is too large.
199
+ *
200
+ * #### Limitations
201
+ *
202
+ * This function shouldn't be used when processing audio track in chunks;
203
+ * use {@link ChunkedResampler.maxNumOutputFrames} instead.
204
+ * @param inputLength - input length
205
+ * @param inputSampleRate - input sample rate in Hz
206
+ * @param outputSampleRate - output sample rate in Hz
207
+ */
208
+ export function numOutputFrames(inputLength: number, inputSampleRate: number, outputSampleRate: number): number;
@@ -0,0 +1,4 @@
1
+ import * as wasm from "./lanczos_resampler_bg.wasm";
2
+ export * from "./lanczos_resampler_bg.js";
3
+ import { __wbg_set_wasm } from "./lanczos_resampler_bg.js";
4
+ __wbg_set_wasm(wasm);
@@ -0,0 +1,507 @@
1
+ let wasm;
2
+ export function __wbg_set_wasm(val) {
3
+ wasm = val;
4
+ }
5
+
6
+ function addHeapObject(obj) {
7
+ if (heap_next === heap.length) heap.push(heap.length + 1);
8
+ const idx = heap_next;
9
+ heap_next = heap[idx];
10
+
11
+ heap[idx] = obj;
12
+ return idx;
13
+ }
14
+
15
+ function addBorrowedObject(obj) {
16
+ if (stack_pointer == 1) throw new Error('out of js stack');
17
+ heap[--stack_pointer] = obj;
18
+ return stack_pointer;
19
+ }
20
+
21
+ function dropObject(idx) {
22
+ if (idx < 132) return;
23
+ heap[idx] = heap_next;
24
+ heap_next = idx;
25
+ }
26
+
27
+ let cachedFloat32ArrayMemory0 = null;
28
+ function getFloat32ArrayMemory0() {
29
+ if (cachedFloat32ArrayMemory0 === null || cachedFloat32ArrayMemory0.byteLength === 0) {
30
+ cachedFloat32ArrayMemory0 = new Float32Array(wasm.memory.buffer);
31
+ }
32
+ return cachedFloat32ArrayMemory0;
33
+ }
34
+
35
+ function getStringFromWasm0(ptr, len) {
36
+ ptr = ptr >>> 0;
37
+ return decodeText(ptr, len);
38
+ }
39
+
40
+ let cachedUint8ArrayMemory0 = null;
41
+ function getUint8ArrayMemory0() {
42
+ if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
43
+ cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
44
+ }
45
+ return cachedUint8ArrayMemory0;
46
+ }
47
+
48
+ function getObject(idx) { return heap[idx]; }
49
+
50
+ let heap = new Array(128).fill(undefined);
51
+ heap.push(undefined, null, true, false);
52
+
53
+ let heap_next = heap.length;
54
+
55
+ function passArrayF32ToWasm0(arg, malloc) {
56
+ const ptr = malloc(arg.length * 4, 4) >>> 0;
57
+ getFloat32ArrayMemory0().set(arg, ptr / 4);
58
+ WASM_VECTOR_LEN = arg.length;
59
+ return ptr;
60
+ }
61
+
62
+ let stack_pointer = 128;
63
+
64
+ function takeObject(idx) {
65
+ const ret = getObject(idx);
66
+ dropObject(idx);
67
+ return ret;
68
+ }
69
+
70
+ let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
71
+ cachedTextDecoder.decode();
72
+ const MAX_SAFARI_DECODE_BYTES = 2146435072;
73
+ let numBytesDecoded = 0;
74
+ function decodeText(ptr, len) {
75
+ numBytesDecoded += len;
76
+ if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
77
+ cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
78
+ cachedTextDecoder.decode();
79
+ numBytesDecoded = len;
80
+ }
81
+ return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
82
+ }
83
+
84
+ let WASM_VECTOR_LEN = 0;
85
+
86
+ const ChunkedInterleavedResamplerFinalization = (typeof FinalizationRegistry === 'undefined')
87
+ ? { register: () => {}, unregister: () => {} }
88
+ : new FinalizationRegistry(ptr => wasm.__wbg_chunkedinterleavedresampler_free(ptr >>> 0, 1));
89
+
90
+ const ChunkedResamplerFinalization = (typeof FinalizationRegistry === 'undefined')
91
+ ? { register: () => {}, unregister: () => {} }
92
+ : new FinalizationRegistry(ptr => wasm.__wbg_chunkedresampler_free(ptr >>> 0, 1));
93
+
94
+ const WholeResamplerFinalization = (typeof FinalizationRegistry === 'undefined')
95
+ ? { register: () => {}, unregister: () => {} }
96
+ : new FinalizationRegistry(ptr => wasm.__wbg_wholeresampler_free(ptr >>> 0, 1));
97
+
98
+ /**
99
+ * A resampler that processes audio input in chunks; the channels are interleaved with each other.
100
+ *
101
+ * Use it to process audio streams.
102
+ *
103
+ * ## Parameters
104
+ *
105
+ * This resampler uses default parameters: _N = 16, A = 3_.
106
+ *
107
+ * ## Limitations
108
+ *
109
+ * `ChunkedInterleavedResampler` produces slightly different output compared to processing the whole input at once.
110
+ * If this is undesired, consider using {@link WholeResampler}.
111
+ */
112
+ export class ChunkedInterleavedResampler {
113
+ __destroy_into_raw() {
114
+ const ptr = this.__wbg_ptr;
115
+ this.__wbg_ptr = 0;
116
+ ChunkedInterleavedResamplerFinalization.unregister(this);
117
+ return ptr;
118
+ }
119
+ free() {
120
+ const ptr = this.__destroy_into_raw();
121
+ wasm.__wbg_chunkedinterleavedresampler_free(ptr, 0);
122
+ }
123
+ /**
124
+ * Get the number of channels.
125
+ * @returns {number}
126
+ */
127
+ get numChannels() {
128
+ const ret = wasm.chunkedinterleavedresampler_numChannels(this.__wbg_ptr);
129
+ return ret >>> 0;
130
+ }
131
+ /**
132
+ * Get input sample rate in Hz.
133
+ * @returns {number}
134
+ */
135
+ get inputSampleRate() {
136
+ const ret = wasm.chunkedinterleavedresampler_inputSampleRate(this.__wbg_ptr);
137
+ return ret >>> 0;
138
+ }
139
+ /**
140
+ * Get/set output sample rate in Hz.
141
+ *
142
+ * After changing the sample rate you should consider updating buffer size to
143
+ * {@link ChunkedInterleavedResampler.maxNumOutputFrames}.
144
+ * @returns {number}
145
+ */
146
+ get outputSampleRate() {
147
+ const ret = wasm.chunkedinterleavedresampler_outputSampleRate(this.__wbg_ptr);
148
+ return ret >>> 0;
149
+ }
150
+ /**
151
+ * Get maximum output chunk length given the input chunk length.
152
+ *
153
+ * Returns the same value as {@link outputLength} plus one.
154
+ * This additional sample is used to compensate for unevenly divisible sample rates.
155
+ *
156
+ * You should consider updating buffer size every time you change output sample rate via
157
+ * {@link ChunkedInterleavedResampler.outputSampleRate}.
158
+ * @param {number} numInputFrames
159
+ * @returns {number}
160
+ */
161
+ maxNumOutputFrames(numInputFrames) {
162
+ const ret = wasm.chunkedinterleavedresampler_maxNumOutputFrames(this.__wbg_ptr, numInputFrames);
163
+ return ret >>> 0;
164
+ }
165
+ /**
166
+ * @param {number} value - new sample rate in Hz
167
+ */
168
+ set outputSampleRate(value) {
169
+ wasm.chunkedinterleavedresampler_set_outputSampleRate(this.__wbg_ptr, value);
170
+ }
171
+ /**
172
+ * Creates new instance of resampler with the specified input and output sample rates and the
173
+ * number of channels.
174
+ * @param {number} inputSampleRate - input sample rate in Hz
175
+ * @param {number} outputSampleRate - output sample rate in Hz
176
+ * @param {number} numChannels - number of channels
177
+ */
178
+ constructor(inputSampleRate, outputSampleRate, numChannels) {
179
+ const ret = wasm.chunkedinterleavedresampler_new(inputSampleRate, outputSampleRate, numChannels);
180
+ this.__wbg_ptr = ret >>> 0;
181
+ ChunkedInterleavedResamplerFinalization.register(this, this.__wbg_ptr, this);
182
+ return this;
183
+ }
184
+ /**
185
+ * Resets internal state.
186
+ *
187
+ * Erases any information about the previous chunk.
188
+ *
189
+ * Use this method when you want to reuse resampler for another audio stream.
190
+ */
191
+ reset() {
192
+ wasm.chunkedinterleavedresampler_reset(this.__wbg_ptr);
193
+ }
194
+ /**
195
+ * Resamples input signal chunk from the source to the target sample rate and appends the
196
+ * resulting signal to the output.
197
+ *
198
+ * Returns the number of processed input samples. The output is clamped to _[-1; 1]_.
199
+ *
200
+ * For each {@link ChunkedInterleavedResampler.inputSampleRate} input samples this method produces exactly
201
+ * {@link ChunkedInterleavedResampler.outputSampleRate} output samples even if it is called multiple times with a smaller
202
+ * amount of input samples; the only exception is when the output sample rate was changed in the process.
203
+ *
204
+ * #### Edge cases
205
+ *
206
+ * Returns 0 when either the number of input or output frames is less than _max(2, A-1)_, adjusted in
207
+ * accordance with sample rate ratio.
208
+ *
209
+ * #### Limitations
210
+ *
211
+ * The output depends on the chunk size, hence resampling the same audio track all at once and
212
+ * in chunks will produce slightly different results. This a consequence of the fact that Lanczos kernel
213
+ * isn't an interpolation function, but a filter. To minimize such discrepancies chunk size should
214
+ * be much larger than _2⋅A + 1_.
215
+ * @param {Float32Array} chunk
216
+ * @param {Float32Array} output
217
+ * @returns {number}
218
+ */
219
+ resample(chunk, output) {
220
+ const ptr0 = passArrayF32ToWasm0(chunk, wasm.__wbindgen_export);
221
+ const len0 = WASM_VECTOR_LEN;
222
+ const ret = wasm.chunkedinterleavedresampler_resample(this.__wbg_ptr, ptr0, len0, addHeapObject(output));
223
+ return ret >>> 0;
224
+ }
225
+ }
226
+ if (Symbol.dispose) ChunkedInterleavedResampler.prototype[Symbol.dispose] = ChunkedInterleavedResampler.prototype.free;
227
+
228
+ /**
229
+ * A resampler that processes audio input in chunks.
230
+ *
231
+ * Use it to process audio streams.
232
+ *
233
+ * ## Parameters
234
+ *
235
+ * This resampler uses default parameters: _N = 16, A = 3_.
236
+ *
237
+ * ## Limitations
238
+ *
239
+ * `ChunkedResampler` produces slightly different output compared to processing the whole input at once.
240
+ * If this is undesired, consider using {@link WholeResampler}.
241
+ */
242
+ export class ChunkedResampler {
243
+ __destroy_into_raw() {
244
+ const ptr = this.__wbg_ptr;
245
+ this.__wbg_ptr = 0;
246
+ ChunkedResamplerFinalization.unregister(this);
247
+ return ptr;
248
+ }
249
+ free() {
250
+ const ptr = this.__destroy_into_raw();
251
+ wasm.__wbg_chunkedresampler_free(ptr, 0);
252
+ }
253
+ /**
254
+ * Get input sample rate in Hz.
255
+ * @returns {number}
256
+ */
257
+ get inputSampleRate() {
258
+ const ret = wasm.chunkedresampler_inputSampleRate(this.__wbg_ptr);
259
+ return ret >>> 0;
260
+ }
261
+ /**
262
+ * Get/set output sample rate in Hz.
263
+ *
264
+ * After changing the sample rate you should consider updating buffer size to
265
+ * {@link ChunkedResampler.maxNumOutputFrames}.
266
+ * @returns {number}
267
+ */
268
+ get outputSampleRate() {
269
+ const ret = wasm.chunkedresampler_outputSampleRate(this.__wbg_ptr);
270
+ return ret >>> 0;
271
+ }
272
+ /**
273
+ * Get maximum output chunk length given the input chunk length.
274
+ *
275
+ * Returns the same value as {@link outputLength} plus one.
276
+ * This additional sample is used to compensate for unevenly divisible sample rates.
277
+ *
278
+ * You should consider updating buffer size every time you change output sample rate via
279
+ * {@link ChunkedResampler.outputSampleRate}.
280
+ * @param {number} numInputFrames
281
+ * @returns {number}
282
+ */
283
+ maxNumOutputFrames(numInputFrames) {
284
+ const ret = wasm.chunkedresampler_maxNumOutputFrames(this.__wbg_ptr, numInputFrames);
285
+ return ret >>> 0;
286
+ }
287
+ /**
288
+ * @param {number} value - new sample rate in Hz
289
+ */
290
+ set outputSampleRate(value) {
291
+ wasm.chunkedresampler_set_outputSampleRate(this.__wbg_ptr, value);
292
+ }
293
+ /**
294
+ * Create new resampler with the specified input and output sample rates.
295
+ * @param {number} inputSampleRate - input sample rate in Hz
296
+ * @param {number} outputSampleRate - output sample rate in Hz
297
+ */
298
+ constructor(inputSampleRate, outputSampleRate) {
299
+ const ret = wasm.chunkedresampler_new(inputSampleRate, outputSampleRate);
300
+ this.__wbg_ptr = ret >>> 0;
301
+ ChunkedResamplerFinalization.register(this, this.__wbg_ptr, this);
302
+ return this;
303
+ }
304
+ /**
305
+ * Resets internal state.
306
+ *
307
+ * Erases any information about the previous chunk.
308
+ *
309
+ * Use this method when you want to reuse resampler for another audio stream.
310
+ */
311
+ reset() {
312
+ wasm.chunkedresampler_reset(this.__wbg_ptr);
313
+ }
314
+ /**
315
+ * Resamples input signal chunk from the source to the target sample rate and appends the
316
+ * resulting signal to the output.
317
+ *
318
+ * Returns the number of processed input samples. The output is clamped to _[-1; 1]_.
319
+ *
320
+ * For each {@link ChunkedResampler.inputSampleRate} input samples this method produces exactly
321
+ * {@link ChunkedResampler.outputSampleRate} output samples even if it is called multiple times with a smaller
322
+ * amount of input samples; the only exception is when the output sample rate was changed in the process.
323
+ *
324
+ * #### Edge cases
325
+ *
326
+ * Returns 0 when either the input length or output length is less than _max(2, A-1)_, adjusted in
327
+ * accordance with sample rate ratio.
328
+ *
329
+ * #### Limitations
330
+ *
331
+ * The output depends on the chunk size, hence resampling the same audio track all at once and
332
+ * in chunks will produce slightly different results. This a consequence of the fact that Lanczos kernel
333
+ * isn't an interpolation function, but a filter. To minimize such discrepancies chunk size should
334
+ * be much larger than _2⋅A + 1_.
335
+ * @param {Float32Array} chunk
336
+ * @param {Float32Array} output
337
+ * @returns {number}
338
+ */
339
+ resample(chunk, output) {
340
+ const ptr0 = passArrayF32ToWasm0(chunk, wasm.__wbindgen_export);
341
+ const len0 = WASM_VECTOR_LEN;
342
+ const ret = wasm.chunkedresampler_resample(this.__wbg_ptr, ptr0, len0, addHeapObject(output));
343
+ return ret >>> 0;
344
+ }
345
+ }
346
+ if (Symbol.dispose) ChunkedResampler.prototype[Symbol.dispose] = ChunkedResampler.prototype.free;
347
+
348
+ /**
349
+ * A resampler that processes audio input as a whole.
350
+ *
351
+ * Use it to process audio streams.
352
+ *
353
+ * ## Parameters
354
+ *
355
+ * This resampler uses default parameters: _N = 16, A = 3_.
356
+ *
357
+ * ## Limitations
358
+ *
359
+ * `WholeResampler` shouldn't be used to process audio track in chunks; use {@link ChunkedResampler} instead.
360
+ */
361
+ export class WholeResampler {
362
+ __destroy_into_raw() {
363
+ const ptr = this.__wbg_ptr;
364
+ this.__wbg_ptr = 0;
365
+ WholeResamplerFinalization.unregister(this);
366
+ return ptr;
367
+ }
368
+ free() {
369
+ const ptr = this.__destroy_into_raw();
370
+ wasm.__wbg_wholeresampler_free(ptr, 0);
371
+ }
372
+ /**
373
+ * This is a variant of {@link WholeResampler.resample} that doesn't use memory allocation.
374
+ *
375
+ * Returns the number of samples read from the input. Currently this is either 0 (see "Panics") or
376
+ * the input length.
377
+ *
378
+ * #### Edge cases
379
+ *
380
+ * Returns 0 when either the input length or remaining output length is less than 2.
381
+ *
382
+ * #### Panics
383
+ *
384
+ * Panics when the output isn't large enough to hold all the resampled points.
385
+ * Use {@link outputLength} to ensure that the buffer size is sufficient.
386
+ * @param {Float32Array} input
387
+ * @param {Float32Array} output
388
+ * @returns {number}
389
+ */
390
+ resampleInto(input, output) {
391
+ try {
392
+ const ptr0 = passArrayF32ToWasm0(input, wasm.__wbindgen_export);
393
+ const len0 = WASM_VECTOR_LEN;
394
+ const ret = wasm.wholeresampler_resampleInto(this.__wbg_ptr, ptr0, len0, addBorrowedObject(output));
395
+ return ret >>> 0;
396
+ } finally {
397
+ heap[stack_pointer++] = undefined;
398
+ }
399
+ }
400
+ /**
401
+ * This is a variant of {@link resampleInto} that processes several audio channels (one audio frame) at a time.
402
+ *
403
+ * #### Edge cases
404
+ *
405
+ * Returns 0 when either the number of input frames or the number of remaining output frames is less than 2.
406
+ *
407
+ * #### Panics
408
+ *
409
+ * - Panics when the output isn't large enough to hold all the resampled points.
410
+ * Use {@link outputLength} to ensure that the buffer size is sufficient.
411
+ * - Panics when either the input or the output length isn't evenly divisible by the number of
412
+ * channels.
413
+ * @param {Float32Array} input - input frames
414
+ * @param {number} numChannels - number of channels
415
+ * @param {Float32Array} output
416
+ * @returns {number}
417
+ */
418
+ resampleInterleavedInto(input, numChannels, output) {
419
+ try {
420
+ const ptr0 = passArrayF32ToWasm0(input, wasm.__wbindgen_export);
421
+ const len0 = WASM_VECTOR_LEN;
422
+ const ret = wasm.wholeresampler_resampleInterleavedInto(this.__wbg_ptr, ptr0, len0, numChannels, addBorrowedObject(output));
423
+ return ret >>> 0;
424
+ } finally {
425
+ heap[stack_pointer++] = undefined;
426
+ }
427
+ }
428
+ /**
429
+ * Create new resampler.
430
+ */
431
+ constructor() {
432
+ const ret = wasm.wholeresampler_new();
433
+ this.__wbg_ptr = ret >>> 0;
434
+ WholeResamplerFinalization.register(this, this.__wbg_ptr, this);
435
+ return this;
436
+ }
437
+ /**
438
+ * Resample input signal from the source to the target sample rate and
439
+ * returns the resulting output signal as a vector.
440
+ *
441
+ * #### Edge cases
442
+ *
443
+ * - Returns an empty array when either the input length or calculated output length is less than 2.
444
+ * - Returns an empty array when either the input length or the output sample rate is too large.
445
+ * @param {Float32Array} input - input samples
446
+ * @param {number} inputSampleRate - input sample rate in Hz
447
+ * @param {number} outputSampleRate - output sample rate in Hz
448
+ * @returns {Float32Array}
449
+ */
450
+ resample(input, inputSampleRate, outputSampleRate) {
451
+ const ptr0 = passArrayF32ToWasm0(input, wasm.__wbindgen_export);
452
+ const len0 = WASM_VECTOR_LEN;
453
+ const ret = wasm.wholeresampler_resample(this.__wbg_ptr, ptr0, len0, inputSampleRate, outputSampleRate);
454
+ return takeObject(ret);
455
+ }
456
+ }
457
+ if (Symbol.dispose) WholeResampler.prototype[Symbol.dispose] = WholeResampler.prototype.free;
458
+
459
+ /**
460
+ * Calculates resampled length of the input for the given input/output sample
461
+ * rates.
462
+ *
463
+ * #### Edge cases
464
+ *
465
+ * Returns `Number.NAN` when the input length or the output sample rate is too large.
466
+ *
467
+ * #### Limitations
468
+ *
469
+ * This function shouldn't be used when processing audio track in chunks;
470
+ * use {@link ChunkedResampler.maxNumOutputFrames} instead.
471
+ * @param {number} inputLength - input length
472
+ * @param {number} inputSampleRate - input sample rate in Hz
473
+ * @param {number} outputSampleRate - output sample rate in Hz
474
+ * @returns {number}
475
+ */
476
+ export function numOutputFrames(inputLength, inputSampleRate, outputSampleRate) {
477
+ const ret = wasm.numOutputFrames(inputLength, inputSampleRate, outputSampleRate);
478
+ return takeObject(ret);
479
+ }
480
+
481
+ export function __wbg___wbindgen_throw_dd24417ed36fc46e(arg0, arg1) {
482
+ throw new Error(getStringFromWasm0(arg0, arg1));
483
+ };
484
+
485
+ export function __wbg_length_86ce4877baf913bb(arg0) {
486
+ const ret = getObject(arg0).length;
487
+ return ret;
488
+ };
489
+
490
+ export function __wbg_new_with_length_95ba657dfb7d3dfb(arg0) {
491
+ const ret = new Float32Array(arg0 >>> 0);
492
+ return addHeapObject(ret);
493
+ };
494
+
495
+ export function __wbg_set_index_165b46b0114d368c(arg0, arg1, arg2) {
496
+ getObject(arg0)[arg1 >>> 0] = arg2;
497
+ };
498
+
499
+ export function __wbindgen_cast_d6cd19b81560fd6e(arg0) {
500
+ // Cast intrinsic for `F64 -> Externref`.
501
+ const ret = arg0;
502
+ return addHeapObject(ret);
503
+ };
504
+
505
+ export function __wbindgen_object_drop_ref(arg0) {
506
+ takeObject(arg0);
507
+ };
Binary file
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "lanczos-resampler",
3
+ "type": "module",
4
+ "description": "Audio resampler for Rust/JS that uses Lanczos filter.",
5
+ "version": "0.1.0",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/igankevich/lanczos-resampler"
10
+ },
11
+ "files": [
12
+ "lanczos_resampler_bg.wasm",
13
+ "lanczos_resampler.js",
14
+ "lanczos_resampler_bg.js",
15
+ "lanczos_resampler.d.ts"
16
+ ],
17
+ "main": "lanczos_resampler.js",
18
+ "homepage": "https://github.com/igankevich/lanczos-resampler",
19
+ "types": "lanczos_resampler.d.ts",
20
+ "sideEffects": [
21
+ "./lanczos_resampler.js",
22
+ "./snippets/*"
23
+ ],
24
+ "keywords": [
25
+ "upsampling",
26
+ "downsampling",
27
+ "interpolation",
28
+ "webaudio"
29
+ ]
30
+ }