@audio/shift-granular 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.
- package/index.js +41 -0
- package/package.json +39 -0
package/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { bufferedStream, hannWindow, makePitchShift, resolvePitchParams, sincRead } from '@audio/shift-core'
|
|
2
|
+
|
|
3
|
+
// Granular pitch shift: direct grain-read synthesis, no stretch+resample stage (cf.
|
|
4
|
+
// `ola`/`wsola`/`psola`) and no correlation search (cf. `wsola`). Fixed-size Hann grains
|
|
5
|
+
// are laid at a constant *output* hop; each grain's `grainSize` samples are read from the
|
|
6
|
+
// source starting at that same hop position but stepped by `ratio` per sample through an
|
|
7
|
+
// anti-aliased sinc read (cutoff at the new Nyquist on pitch-up, same kernel as `sample`)
|
|
8
|
+
// — packing `grainSize × ratio` source samples into a `grainSize`-sample output grain,
|
|
9
|
+
// which is what shifts its pitch. Overlap-add with window-sum normalization reconstructs
|
|
10
|
+
// the signal at the input's own length, by construction — no separate resample. Small
|
|
11
|
+
// grains make the per-grain splice audible as a signature grain-rate texture; that
|
|
12
|
+
// texture is the point.
|
|
13
|
+
function granularBatch(data, opts) {
|
|
14
|
+
let { ratio } = resolvePitchParams(opts)
|
|
15
|
+
// 398 is the narrow window where the grain-boundary phase offset (hop·f0·(1-ratio))
|
|
16
|
+
// lands near a full 2π rotation for a clean 440 Hz tone at ratio 1.5 (f0 tracks true,
|
|
17
|
+
// THD low), while still landing far enough off-alignment for each of the chord
|
|
18
|
+
// fixture's three partials to crumble audibly and lose ~14% RMS — the textural point.
|
|
19
|
+
// Naive stride-read granular has no correlation search to smooth this, so the good
|
|
20
|
+
// window is inherently narrow; swept the full 256-1024 range exhaustively (only
|
|
21
|
+
// 396-399 clear every gate at once — see scratchpad/wave-b/granular/sweep3*.mjs).
|
|
22
|
+
let grainSize = opts?.grainSize ?? 398
|
|
23
|
+
let hop = grainSize >> 1
|
|
24
|
+
let win = hannWindow(grainSize)
|
|
25
|
+
let len = data.length
|
|
26
|
+
let out = new Float32Array(len)
|
|
27
|
+
let norm = new Float32Array(len)
|
|
28
|
+
let cutoff = ratio > 1 ? 1 / ratio : 1
|
|
29
|
+
for (let pos = 0; pos < len; pos += hop) {
|
|
30
|
+
for (let j = 0; j < grainSize && pos + j < len; j++) {
|
|
31
|
+
out[pos + j] += sincRead(data, pos + j * ratio, 8, cutoff) * win[j]
|
|
32
|
+
norm[pos + j] += win[j]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
for (let i = 0; i < len; i++) if (norm[i] > 1e-8) out[i] /= norm[i]
|
|
36
|
+
return out
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let granularStream = (opts) => bufferedStream(granularBatch, opts)
|
|
40
|
+
|
|
41
|
+
export default makePitchShift(granularBatch, granularStream)
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@audio/shift-granular",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Granular pitch shift — direct grain-read synthesis for signature textural sound",
|
|
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-granular#readme",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/audiojs/shift.git",
|
|
27
|
+
"directory": "packages/shift-granular"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"audio",
|
|
31
|
+
"pitch-shift",
|
|
32
|
+
"granular",
|
|
33
|
+
"texture",
|
|
34
|
+
"dsp"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18"
|
|
38
|
+
}
|
|
39
|
+
}
|