@audio/shift-pvoc 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 +39 -0
  2. package/package.json +40 -0
package/index.js ADDED
@@ -0,0 +1,39 @@
1
+ import { makeStftShift } from '@audio/shift-core/stft'
2
+ import { makeFrameRatio, wrapPhase, scatterGated } from '@audio/shift-core'
3
+
4
+ // Canonical phase vocoder pitch shift (Bernsee / SMB method), peak-gated bin scatter.
5
+ // Per frame: compute true instantaneous frequency at each analysis bin eligible under the
6
+ // peak ±1 gate, shift it to the destination bin `ratio` implies, and accumulate synthesis
7
+ // phase there at the shifted frequency (below). Colliding destination bins accumulate in
8
+ // the energy domain and are per-frame renormalized (see scatterGated) — the frequency
9
+ // written to a bin is its loudest contributor's. No time-stretch, no resample.
10
+ function process(mag, phase, state, ctx) {
11
+ if (!state.fr) state.fr = makeFrameRatio(ctx.ratioFn || ctx.ratio || 1)
12
+ let { half, hop } = ctx
13
+ let ratio = state.fr.at(ctx.frameStart, ctx.sampleRate)
14
+ if (!state.prev) {
15
+ state.prev = new Float64Array(half + 1)
16
+ state.syn = new Float64Array(half + 1)
17
+ state.newMag = new Float64Array(half + 1)
18
+ state.newFreq = new Float64Array(half + 1)
19
+ state.peakMag = new Float64Array(half + 1)
20
+ state.first = true
21
+ }
22
+
23
+ let { prev, syn, newMag, newFreq, peakMag } = state
24
+ newMag.fill(0)
25
+ newFreq.fill(0)
26
+ peakMag.fill(0)
27
+
28
+ scatterGated(mag, phase, state.first ? null : prev, ratio, ctx, newMag, newFreq, peakMag)
29
+ prev.set(phase)
30
+
31
+ for (let k = 0; k <= half; k++) syn[k] = wrapPhase(syn[k] + newFreq[k] * hop)
32
+
33
+ state.first = false
34
+ return { mag: newMag, phase: syn }
35
+ }
36
+
37
+ // Batch and stream share scatterGated's per-frame energy renormalization, so both
38
+ // reconstruct at identical loudness with no whole-signal gain correction needed.
39
+ export default makeStftShift(process, { post: (out) => out })
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@audio/shift-pvoc",
3
+ "version": "1.0.0",
4
+ "description": "Canonical phase vocoder (Bernsee/SMB) pitch shift",
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-pvoc#readme",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/audiojs/shift.git",
27
+ "directory": "packages/shift-pvoc"
28
+ },
29
+ "keywords": [
30
+ "audio",
31
+ "pitch-shift",
32
+ "phase-vocoder",
33
+ "pvoc",
34
+ "stft",
35
+ "dsp"
36
+ ],
37
+ "engines": {
38
+ "node": ">=18"
39
+ }
40
+ }