@audio/shift-sample 1.0.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.
Files changed (2) hide show
  1. package/index.js +46 -0
  2. package/package.json +41 -0
package/index.js ADDED
@@ -0,0 +1,46 @@
1
+ import { bufferedStream, makePitchShift, resolveRatio, sincRead } from '@audio/shift-core'
2
+
3
+ // Canonical sampler pitch shift. A Hann-windowed sinc interpolator reads the source at
4
+ // a fractional stride of `ratio` per output sample — the same intuition as a hardware
5
+ // sampler playing a one-shot at a different rate, or a tracker module running the same
6
+ // waveform faster. There is no time preservation: output duration is `input_length /
7
+ // ratio`. The tail is zero-padded so the unified batch API (`output.length === input
8
+ // .length`) still holds, but the active region is genuinely shorter on pitch-up.
9
+ //
10
+ // Pitch preservation is exact (fractional-stride sinc over a clean buffer is an ideal
11
+ // resampler), at the cost of losing time — the one thing every other algorithm in this
12
+ // package keeps. Use it when that is the intended effect: instrument one-shots, sampler
13
+ // voices, tracker playback, anything where "higher pitch = shorter clip" is the point.
14
+ // Anti-aliasing is inherited from the shared `sincRead`: `cutoff = min(1, 1/ratio)`
15
+ // suppresses content above the new Nyquist before it folds.
16
+ function sampleBatch(data, opts) {
17
+ let { ratio, ratioFn } = resolveRatio(opts)
18
+ let r = opts?.sincRadius ?? 8
19
+ let n = data.length
20
+ let out = new Float32Array(n)
21
+ let readPos = 0
22
+ // Scalar ratio (the common case) hoists cutoff out of the loop and never calls a host
23
+ // closure — the repo's one per-output-sample dynamic call otherwise sits in this loop.
24
+ if (!ratioFn) {
25
+ let cutoff = ratio > 1 ? 1 / ratio : 1
26
+ for (let i = 0; i < n; i++) {
27
+ if (readPos >= n) break
28
+ out[i] = sincRead(data, readPos, r, cutoff)
29
+ readPos += ratio
30
+ }
31
+ return out
32
+ }
33
+ let sr = opts?.sampleRate || 44100
34
+ for (let i = 0; i < n; i++) {
35
+ if (readPos >= n) break
36
+ let rNow = ratioFn(i / sr)
37
+ let cutoff = rNow > 1 ? 1 / rNow : 1
38
+ out[i] = sincRead(data, readPos, r, cutoff)
39
+ readPos += rNow
40
+ }
41
+ return out
42
+ }
43
+
44
+ let sampleStream = (opts) => bufferedStream(sampleBatch, opts)
45
+
46
+ export default makePitchShift(sampleBatch, sampleStream)
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@audio/shift-sample",
3
+ "version": "1.0.0",
4
+ "description": "Sampler-style pitch shift — sinc-interpolated fractional-stride resample (no time preservation)",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "index.js",
8
+ "exports": {
9
+ ".": "./index.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "index.js"
14
+ ],
15
+ "dependencies": {
16
+ "@audio/shift-core": "^1.0.0"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "license": "MIT",
22
+ "author": "audiojs",
23
+ "homepage": "https://github.com/audiojs/shift/tree/main/packages/shift-sample#readme",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/audiojs/shift.git",
27
+ "directory": "packages/shift-sample"
28
+ },
29
+ "keywords": [
30
+ "audio",
31
+ "pitch-shift",
32
+ "sampler",
33
+ "resample",
34
+ "sinc",
35
+ "tracker",
36
+ "dsp"
37
+ ],
38
+ "engines": {
39
+ "node": ">=18"
40
+ }
41
+ }