{"_id":"@afta8/signalsmith-stretch","_rev":"6-ffba493fbcfe631aa1dea0755fcfd014","name":"@afta8/signalsmith-stretch","dist-tags":{"latest":"1.3.2-aura.6"},"versions":{"1.3.2-aura.1":{"name":"@afta8/signalsmith-stretch","version":"1.3.2-aura.1","keywords":["audio","pitch","time","web-audio","loop"],"author":{"name":"Geraint Luff"},"license":"MIT","_id":"@afta8/signalsmith-stretch@1.3.2-aura.1","maintainers":[{"name":"afta8","email":"fathand@gmail.com"}],"contributors":[{"name":"afta8"}],"homepage":"https://github.com/afta8/signalsmith-stretch#readme","bugs":{"url":"https://github.com/afta8/signalsmith-stretch/issues"},"dist":{"shasum":"1b5e5b0b5b30626cb4c3c361cbb2e66b8caa1592","tarball":"https://registry.npmjs.org/@afta8/signalsmith-stretch/-/signalsmith-stretch-1.3.2-aura.1.tgz","fileCount":5,"integrity":"sha512-wlsdPbueJs+atybNLmm1LH3xyz58A/KjAz4omlSbvblnbMDfVN245iooueaoh4zVuii8WbrTPIEvxDs+eoRdYA==","signatures":[{"sig":"MEUCIQC1V2S9/IYFhVflgKU8mKqflwAUoOkB+Z1fmhcQEbf49QIgcM9jfVHZva8TDiNSPe8xaw4e/ilrEp4Wr2xsXqzPwTk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":246918},"main":"SignalsmithStretch.mjs","type":"module","exports":{"import":"./SignalsmithStretch.mjs","require":"./SignalsmithStretch.js"},"gitHead":"fe4f2d6cece8a9f1ec53cf6e2c62fcf36779939e","scripts":{"test":"node --test ../test/topology.test.mjs ../test/audio.test.mjs"},"_npmUser":{"name":"afta8","email":"fathand@gmail.com"},"repository":{"url":"git+https://github.com/afta8/signalsmith-stretch.git","type":"git"},"_npmVersion":"10.9.2","description":"JS/WASM release of the Signalsmith Stretch library, extended with native loop topologies (forward/reverse/ping-pong loopMode)","directories":{},"_nodeVersion":"22.17.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/signalsmith-stretch_1.3.2-aura.1_1783978878838_0.05789533900838295","host":"s3://npm-registry-packages-npm-production"}},"1.3.2-aura.2":{"name":"@afta8/signalsmith-stretch","version":"1.3.2-aura.2","keywords":["audio","pitch","time","web-audio","loop"],"author":{"name":"Geraint Luff"},"license":"MIT","_id":"@afta8/signalsmith-stretch@1.3.2-aura.2","maintainers":[{"name":"afta8","email":"fathand@gmail.com"}],"contributors":[{"name":"afta8"}],"homepage":"https://github.com/afta8/signalsmith-stretch#readme","bugs":{"url":"https://github.com/afta8/signalsmith-stretch/issues"},"dist":{"shasum":"ed7e03155566d060308758e3ec712d7aff5b92df","tarball":"https://registry.npmjs.org/@afta8/signalsmith-stretch/-/signalsmith-stretch-1.3.2-aura.2.tgz","fileCount":5,"integrity":"sha512-U3/DgS/zL8rqNPkK4sUWRw6Skak9/Kf2EXWumZAZ5upFAzaxmTwl6BkBohAIXyL6T/rQju75WWxyulBPlUxGHg==","signatures":[{"sig":"MEYCIQDhCwn8G20jf6dSHK2bTfBD6oagle7CcMmQdDjYN4eATwIhANcSva3WSa0ksGF6/7lZUN2B5a16tZGOBgq7Z4aA8NBb","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":262074},"main":"SignalsmithStretch.mjs","type":"module","exports":{"import":"./SignalsmithStretch.mjs","require":"./SignalsmithStretch.js"},"gitHead":"61fabbce92165fdd3c78e30a2e89c9b1a19f0eee","scripts":{"test":"node --test ../test/topology.test.mjs ../test/audio.test.mjs ../test/reverse-style.test.mjs ../test/ended.test.mjs ../test/handoff-seed.test.mjs"},"_npmUser":{"name":"afta8","email":"fathand@gmail.com"},"repository":{"url":"git+https://github.com/afta8/signalsmith-stretch.git","type":"git"},"_npmVersion":"10.9.2","description":"JS/WASM release of the Signalsmith Stretch library, extended with native loop topologies (forward/reverse/ping-pong loopMode), tape-style mirror reverse, and natural-end notifications","directories":{},"_nodeVersion":"22.17.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/signalsmith-stretch_1.3.2-aura.2_1784020259056_0.1823072188486652","host":"s3://npm-registry-packages-npm-production"}},"1.3.2-aura.3":{"name":"@afta8/signalsmith-stretch","version":"1.3.2-aura.3","keywords":["audio","pitch","time","web-audio","loop"],"author":{"name":"Geraint Luff"},"license":"MIT","_id":"@afta8/signalsmith-stretch@1.3.2-aura.3","maintainers":[{"name":"afta8","email":"fathand@gmail.com"}],"contributors":[{"name":"afta8"}],"homepage":"https://github.com/afta8/signalsmith-stretch#readme","bugs":{"url":"https://github.com/afta8/signalsmith-stretch/issues"},"dist":{"shasum":"3475ee9e7a0359c2f6ebda0cae9ab8c02ae421b4","tarball":"https://registry.npmjs.org/@afta8/signalsmith-stretch/-/signalsmith-stretch-1.3.2-aura.3.tgz","fileCount":5,"integrity":"sha512-qAMTLhBSgbAY5UsM/i8fglfHlbu/6r7mcYdSd+l0KZPEg7Q0E+FrjsrPDUnxHFT7MgrX1QWM2UOB84V8PEyhtA==","signatures":[{"sig":"MEUCIQCUmix+sGeX1MTHA27v9Gyq4oKtFeqNeDU4uFQAkREDoQIge2PUgj8Vx2av/Y+9FY/drz1rCffXSWM3IANorZwKZKs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":275205},"main":"SignalsmithStretch.mjs","type":"module","exports":{"import":"./SignalsmithStretch.mjs","require":"./SignalsmithStretch.js"},"gitHead":"e25c72c2690d3c783b53862cd40eacd337eecf47","scripts":{"test":"node --test ../test/topology.test.mjs ../test/audio.test.mjs ../test/reverse-style.test.mjs ../test/ended.test.mjs ../test/handoff-seed.test.mjs ../test/crossfade.test.mjs"},"_npmUser":{"name":"afta8","email":"fathand@gmail.com"},"repository":{"url":"git+https://github.com/afta8/signalsmith-stretch.git","type":"git"},"_npmVersion":"10.9.2","description":"JS/WASM release of the Signalsmith Stretch library, extended with native loop topologies (forward/reverse/ping-pong loopMode), tape-style mirror reverse, natural-end notifications, and equal-power loop crossfade","directories":{},"_nodeVersion":"22.17.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/signalsmith-stretch_1.3.2-aura.3_1784039805881_0.3209045393874237","host":"s3://npm-registry-packages-npm-production"}},"1.3.2-aura.4":{"name":"@afta8/signalsmith-stretch","version":"1.3.2-aura.4","keywords":["audio","pitch","time","web-audio","loop"],"author":{"name":"Geraint Luff"},"license":"MIT","_id":"@afta8/signalsmith-stretch@1.3.2-aura.4","maintainers":[{"name":"afta8","email":"fathand@gmail.com"}],"contributors":[{"name":"afta8"}],"homepage":"https://github.com/afta8/signalsmith-stretch#readme","bugs":{"url":"https://github.com/afta8/signalsmith-stretch/issues"},"dist":{"shasum":"83371774a764e23a16728b4788cc9c9bd4ba1875","tarball":"https://registry.npmjs.org/@afta8/signalsmith-stretch/-/signalsmith-stretch-1.3.2-aura.4.tgz","fileCount":5,"integrity":"sha512-vviUvv5o/A0IlcK+pU02uv41Ex3vxqUJzOKpfb5VM5TcDFM+q4JJA+PQupKldrM49ap2djFmXNF2jldNcexglA==","signatures":[{"sig":"MEUCIC1tS3Mpv132iAGTJLeIYGqOtRuRLegENYifM6NExJ+3AiEAlifTG1ZCuEZDIEY0pR4zBtviApb1Eb66dPLd0xFTKoI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":277641},"main":"SignalsmithStretch.mjs","type":"module","exports":{"import":"./SignalsmithStretch.mjs","require":"./SignalsmithStretch.js"},"gitHead":"6869e62f7c7aa17a7cbb51c6cf55c329033b5ce6","scripts":{"test":"node --test ../test/topology.test.mjs ../test/audio.test.mjs ../test/reverse-style.test.mjs ../test/ended.test.mjs ../test/handoff-seed.test.mjs ../test/crossfade.test.mjs"},"_npmUser":{"name":"afta8","email":"fathand@gmail.com"},"repository":{"url":"git+https://github.com/afta8/signalsmith-stretch.git","type":"git"},"_npmVersion":"10.9.2","description":"JS/WASM release of the Signalsmith Stretch library, extended with native loop topologies (forward/reverse/ping-pong loopMode), tape-style mirror reverse, natural-end notifications, and duration-preserving equal-power loop crossfade","directories":{},"_nodeVersion":"22.17.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/signalsmith-stretch_1.3.2-aura.4_1784070817430_0.8158975485665854","host":"s3://npm-registry-packages-npm-production"}},"1.3.2-aura.5":{"name":"@afta8/signalsmith-stretch","version":"1.3.2-aura.5","keywords":["audio","pitch","time","web-audio","loop"],"author":{"name":"Geraint Luff"},"license":"MIT","_id":"@afta8/signalsmith-stretch@1.3.2-aura.5","maintainers":[{"name":"afta8","email":"fathand@gmail.com"}],"contributors":[{"name":"afta8"}],"homepage":"https://github.com/afta8/signalsmith-stretch#readme","bugs":{"url":"https://github.com/afta8/signalsmith-stretch/issues"},"dist":{"shasum":"c8929c3aad1fb7f23430483a7dd9e34d3d7b5451","tarball":"https://registry.npmjs.org/@afta8/signalsmith-stretch/-/signalsmith-stretch-1.3.2-aura.5.tgz","fileCount":5,"integrity":"sha512-wFViESUBMThI2iixMGkO4jg4HsGJo001KF/xU+TbPvaigwiyw/HxhLh/DcrKd9yySYrz/tP8Bgkt3HXmgyJK/g==","signatures":[{"sig":"MEQCIDgAPolsGXfWCJKLK2iQrPZEn0LECpiBlPer6hrfodXFAiA6ZF4AdLEfv9q5z4QqizOUg7PG9nOY5bwylGskzsMa7Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":280597},"main":"SignalsmithStretch.mjs","type":"module","exports":{"import":"./SignalsmithStretch.mjs","require":"./SignalsmithStretch.js"},"gitHead":"7eb206182b03a69d1cbdf887281d61ac165df183","scripts":{"test":"node --test ../test/topology.test.mjs ../test/audio.test.mjs ../test/reverse-style.test.mjs ../test/ended.test.mjs ../test/handoff-seed.test.mjs ../test/crossfade.test.mjs"},"_npmUser":{"name":"afta8","email":"fathand@gmail.com"},"repository":{"url":"git+https://github.com/afta8/signalsmith-stretch.git","type":"git"},"_npmVersion":"10.9.2","description":"JS/WASM release of the Signalsmith Stretch library, extended with native loop topologies (forward/reverse/ping-pong loopMode), tape-style mirror reverse, natural-end notifications, and duration-preserving equal-power loop crossfade","directories":{},"_nodeVersion":"22.17.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/signalsmith-stretch_1.3.2-aura.5_1784208432995_0.8031101134591825","host":"s3://npm-registry-packages-npm-production"}},"1.3.2-aura.6":{"name":"@afta8/signalsmith-stretch","version":"1.3.2-aura.6","description":"JS/WASM release of the Signalsmith Stretch library, extended with native loop topologies (forward/reverse/ping-pong loopMode), tape-style mirror reverse, natural-end notifications, duration-preserving equal-power loop crossfade, and shared sample-buffer s","main":"SignalsmithStretch.mjs","exports":{"import":"./SignalsmithStretch.mjs","require":"./SignalsmithStretch.js"},"type":"module","scripts":{"test":"node --test ../test/topology.test.mjs ../test/audio.test.mjs ../test/reverse-style.test.mjs ../test/ended.test.mjs ../test/handoff-seed.test.mjs ../test/crossfade.test.mjs"},"repository":{"type":"git","url":"git+https://github.com/afta8/signalsmith-stretch.git"},"keywords":["audio","pitch","time","web-audio","loop"],"author":{"name":"Geraint Luff"},"contributors":[{"name":"afta8"}],"license":"MIT","publishConfig":{"access":"public"},"_id":"@afta8/signalsmith-stretch@1.3.2-aura.6","gitHead":"ac103a6d5a900afc1fb5055118877fe63dfb41b7","bugs":{"url":"https://github.com/afta8/signalsmith-stretch/issues"},"homepage":"https://github.com/afta8/signalsmith-stretch#readme","_nodeVersion":"22.17.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-5Au/kvd0aR4Rr8t0ZLu3pGKgoInuXXE3J4nq3k8GwnkU5pBOUOBmk+XzdoOAI/M/XlYLk4vJMysKEDvDgf/dTQ==","shasum":"5bca8b2c950ef3aa8050e9e940a30383ff649558","tarball":"https://registry.npmjs.org/@afta8/signalsmith-stretch/-/signalsmith-stretch-1.3.2-aura.6.tgz","fileCount":5,"unpackedSize":282172,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCICHiFXG90q7iglW9jVoRtVqufiG2C6flkT0q2jWHmTW8AiEAvxej2xty/PbgsYeDl1RcrFlj7umwTWfCMQY1vDegjWI="}]},"_npmUser":{"name":"afta8","email":"fathand@gmail.com"},"directories":{},"maintainers":[{"name":"afta8","email":"fathand@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/signalsmith-stretch_1.3.2-aura.6_1785502390505_0.31018154719201796"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-13T21:41:18.695Z","modified":"2026-07-31T12:53:10.815Z","1.3.2-aura.1":"2026-07-13T21:41:18.982Z","1.3.2-aura.2":"2026-07-14T09:10:59.244Z","1.3.2-aura.3":"2026-07-14T14:36:46.022Z","1.3.2-aura.4":"2026-07-14T23:13:37.628Z","1.3.2-aura.5":"2026-07-16T13:27:13.132Z","1.3.2-aura.6":"2026-07-31T12:53:10.655Z"},"bugs":{"url":"https://github.com/afta8/signalsmith-stretch/issues"},"author":{"name":"Geraint Luff"},"license":"MIT","homepage":"https://github.com/afta8/signalsmith-stretch#readme","keywords":["audio","pitch","time","web-audio","loop"],"repository":{"type":"git","url":"git+https://github.com/afta8/signalsmith-stretch.git"},"description":"JS/WASM release of the Signalsmith Stretch library, extended with native loop topologies (forward/reverse/ping-pong loopMode), tape-style mirror reverse, natural-end notifications, duration-preserving equal-power loop crossfade, and shared sample-buffer s","contributors":[{"name":"afta8"}],"maintainers":[{"name":"afta8","email":"fathand@gmail.com"}],"readme":"# Signalsmith Stretch Web\n\nThis is a fork of the official Signalsmith Stretch release for Web Audio (WASM/AudioWorklet), extended with native loop topologies (`loopMode`: forward/reverse/ping-pong, signed-rate aware, with all loop-boundary decisions made inside the audio thread).  It includes both plain `.js` (UMD), and ES6 `.mjs` versions.\n\nUpstream: https://github.com/Signalsmith-Audio/signalsmith-stretch (MIT).  Consumers that don't set `loopMode` get the original upstream behaviour unchanged.\n\n## How to use it\n\nCall `SignalsmithStretch(audioContext, ?channelOptions)` from the main thread.  This returns a Promise which resolves to an `AudioNode`, with extra methods attached to it.  The optional [`channelOptions` object](https://developer.mozilla.org/en-US/docs/Web/API/AudioWorkletNode/AudioWorkletNode#options) can specify the number of inputs/outputs and channels.\n\nIt can operate either on live input (if connected to input audio), or on sample buffers you load into it (which can be added/removed dynamically, for streaming).  Either way, you need to call `.start()` (or equivalently `.schedule({active: true})`) for it to start processing audio.\n\n### `stretch.inputTime`\n\nThe current input time, within the sample buffer.  You can change how often this is updated, with an optional callback function, using `stretch.setUpdateInterval(seconds, ?callback)`.\n\n### `stretch.schedule({...})`\n\nThis adds a scheduled change, removing any scheduled changes occuring after this one.  The object properties are:\n\n* `output` (seconds): audio context time for this change.  The node compensates for its own latency, but this means you might want to schedule some things ahead of time, otherwise you'll have a softer transition as it catches up.\n* `active` (bool): processing audio\n* `input` (seconds): position in input buffer\n* `rate` (number): playback rate, e.g. 0.5 == half speed\n* `semitones` (number): pitch shift\n* `tonalityHz` (number): tonality limit (default 8000)\n* `formantSemitones` (number) / `formantCompensation` (bool): formant shift/compensation\n* `formantBaseHz` (number): rough fundamental used for formant analysis (e.g. 100 for low voice, 400 for high voice), or `0` to attempt pitch-tracking\n* `loopStart` (seconds) / `loopEnd` (seconds): sets a section of the input buffer to auto-loop.  Disabled if both are set to the same value.\n* `loopMode` (`'forward'` | `'reverse'` | `'pingpong'`, optional): loop topology.  When unset, the original (positive-rate forward) looping behaviour is used unchanged.\n* `playStart` / `playEnd` (seconds, optional): directional one-shot end boundaries for the natural-end notification (see `stretch.onended`).  They may sit inside the loaded sample and default to the loaded-material edges.  They only affect the notification - rendering is not stopped or silenced.\n* `loopTrapped` (bool) / `loopLeg` (`1` | `-1`) / `lastDirection` (`1` | `-1`) (optional, **onset-only**): loop-state seeds for engine handoffs — see \"Seeding loop state\" below.\n* `reverseStyle` (`'grain'` | `'mirror'`, optional): rendering character for backward travel (negative-rate playback, scrubs, and the backward legs of `reverse`/`pingpong` loops).  `'grain'` (the default) is the original behaviour: each analysis grain keeps its forward shape while the sequence plays backward.  `'mirror'` is true tape-style reverse: the analysis window is time-reversed, so attacks become swells, the synthesis keeps forward quality, and ping-pong reflections are continuous palindromes.  Setting `reverseStyle` on a segment without `loopMode` opts it into the loop engine (`loopMode: 'forward'`), so one-shots and scrubs can use it.\n* `loopCrossfade` (seconds, optional, default `0`): equal-power crossfade across the discontinuous loop seam — see \"Loop crossfade\" below.\n\nIf the node is processing live input (not a buffer) then `input`/`rate`/`loopStart`/`loopEnd`/`loopMode`/`loopCrossfade` are ignored.\n\n### Loop modes\n\nSetting `loopMode` enables a loop engine where every boundary decision happens inside the AudioWorklet, so loop timing never depends on main-thread messages.  `rate` is signed, and the actual travel direction is `rate sign x current loop leg`:\n\n* **`forward`**: positive rates wrap loop end to loop start; negative rates travel backward and wrap loop start to loop end.\n* **`reverse`**: playback first travels to the far boundary (loop end at positive rates, loop start at negative), turns there once, then cycles against the rate sign, wrapping at the boundaries.\n* **`pingpong`**: playback reflects at each boundary, preserving overshoot.  Positive rates take their first leg toward loop end, negative rates toward loop start.\n\nBehaviour rules shared by all modes:\n\n* The requested `input` position always has priority: playback becomes trapped in the loop only after reaching it from a reachable direction.  A positive-rate start beyond the loop end (or a negative-rate start before the loop start) plays as a one-shot.\n* Live `rate` polarity changes reverse the current travel without retriggering or resetting the loop leg.  `rate: 0` keeps the spectral hold, and the last non-zero direction stays defined.\n* Moving `loopStart`/`loopEnd` while playback is inside the loop keeps it trapped and phase-maps it into the new window; moving markers before entry doesn't override start reachability.\n* Setting invalid or zero-width bounds (`loopEnd <= loopStart`) safely disables looping and releases the voice to ordinary one-shot traversal.\n* High rates that cross one or more loop lengths within a render quantum are handled exactly (analytic wrap/reflect), independent of render-quantum size and free of cumulative drift.\n\n### Loop crossfade\n\n`loopCrossfade` (seconds) blends the otherwise-discontinuous loop seam with an equal-power crossfade, in the style of a sampler loop crossfade.  It applies only when a native `loopMode` is active (it does not opt a legacy segment into the loop engine), and only to the **wrapping** seam of a topology:\n\n* **Forward**: every wrap (loop end → loop start at positive rate, loop start → loop end at negative rate).\n* **Reverse**: only the wrapping seam *after* the initial turnaround.  The initial turn is a reflection, not a discontinuous wrap, and is never crossfaded.\n* **Ping-pong**: no effect (it reflects, so there is no discontinuous wrap).  The requested value is retained and becomes active again if you switch to Forward or Reverse.\n\n**Duration-preserving invariant.**  Loop crossfade softens a wrapping seam **without changing the loop's source duration or phase cadence**.  The loop period is exactly `loopLength / abs(rate)` regardless of `loopCrossfade`; the crossfade affects rendered audio only.  It never changes `inputTime`, the reported playhead, the wrap cadence, the topology leg/direction, trap/turn positions, or scheduling — a voice with `loopCrossfade > 0` follows exactly the same position trajectory as the same voice at `loopCrossfade = 0`.\n\nRules:\n\n* Unit is source/input seconds, consistent with `loopStart`/`loopEnd`.  Negative, non-finite or non-numeric values normalise to `0`.\n* The effective crossfade is clamped to half the loop width: `min(loopCrossfade, (loopEnd - loopStart) / 2)`.\n* It is inherited by continuation segments like ordinary parameters, and can be changed live — including across a seam — without retriggering, resetting pitch, changing the tempo, or releasing the loop trap.\n* All seam reads stay strictly **inside** `[loopStart, loopEnd)`; no material from outside the loop is used.\n* The taper is two-sided: it blends into same-direction material while approaching the wrap, then recovers to the authoritative phase after the wrap.  It never reflects or reverses source material.\n\n`reverseStyle` (`'grain'`/`'mirror'`) is fully compatible: the seam timing, duration and reported playhead are identical across styles; only the established backward-rendering character differs.\n\nThe implementation uses a **single** Signalsmith voice and one `process()` call per block — the seam is smoothed by synthesising the near-seam region of the analysis input in the worklet; there is no second voice.\n\n**Seam-quality note.**  Because a duration-preserving loop reads only in-loop audio, the seam discontinuity cannot be made perfectly transparent the way a cycle-shortening crossfade can (that would change the tempo).  The taper makes the seam substantially smoother than no crossfade — and never clickier — but a small residual can remain; on strongly correlated tonal material the equal-power blend can also introduce mild phasing or a brief level change in the fade region.  The phase-vocoder overlap-add tends to mask what remains.  Preserving duration and phase takes priority over fully erasing the seam.\n\n### Seeding loop state (onset handoffs)\n\nWhen initialising a new explicit-input onset that should *continue* existing loop\ntopology (e.g. handing a looping voice over from another playback engine), the same\n`schedule()` call that carries `input` can seed the voice state:\n\n* `loopTrapped` (bool): start already trapped in the loop instead of re-evaluating\n  start-position reachability.  If the provided `input` lies outside the loop window it\n  is phase-mapped in.  Ignored when the loop bounds are invalid/zero-width.\n* `loopLeg` (`1` | `-1`, default `1`): the current loop leg — `1` is the rate-sign\n  (\"first\"/Forward) leg, `-1` the return leg, following the direction model\n  `actual direction = rate sign x leg`.  For `reverse` mode, `-1` means the initial\n  turn has already happened; for `forward` mode the leg is ignored.\n* `lastDirection` (`1` | `-1`): seeds the last non-zero actual direction, so a\n  handoff at `rate: 0` holds with the correct orientation and resumes correctly.\n\nThese work for all three loop modes, both rate signs, and rate zero.  They are\n**onset-only**: consumed by the call that carries them (alongside an explicit `input`)\nand never inherited by later segments — a later plain scrub resets to untrapped and\nre-evaluates reachability as normal.  Omitting them preserves the existing\nexplicit-input behaviour exactly.\n\n### `stretch.onended`\n\nNatural-end callback for loop-engine segments (any segment with `loopMode` set), assigned like `AudioBufferSourceNode.onended`:\n\n```js\nstretch.onended = ({position, direction, output}) => { /* release the voice */ };\n```\n\nIt fires **once per run-out**, from the audio thread's own state, when the voice (a) is not trapped in the loop, (b) cannot currently become trapped (looping disabled, or the loop is unreachable in the travel direction), and (c) has crossed its directional end boundary - `playEnd` when travelling forward, `playStart` when travelling backward (defaults: the edges of the loaded audio).  `output` is the context time at which the crossing becomes audible.\n\nRendering is *not* stopped - the consumer decides how to end the voice (schedule `{active: false}`, release an envelope, or reuse the node).  The notification re-arms automatically whenever the condition clears: a scrub back into the material, a rate-polarity change back toward it, marker moves that restore reachability, or newly appended buffers extending past the position.  A voice held at `rate: 0` never fires.\n\n### `stretch.start(?when)` / `stretch.stop(?when)`\n\nStarts/stops playback or processing, immediately or at some future time.  These are convenience methods which call `.schedule(...)` under the hood.\n\n`.start()` actually has more parameters, presenting a similar interface to [AudioBufferSourceNode](https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/start).\n\n### `stretch.addBuffers([...])`\n\nThis adds buffers to the end of the current input sample buffers.  Buffers should be typed arrays of equal length, one per channel.  Channels may use ordinary `ArrayBuffer` storage or immutable `SharedArrayBuffer` storage; SAB-backed views are retained for read-only source lookup and are not mutated by Stretch.  Do not include a `SharedArrayBuffer` in a transfer list when delivering shared channels.\n\nIt can be called multiple times, and the new buffers are inserted immediately after the existing ones, which lets you start playback before the entire audio is loaded.  It returns a Promise for the new sample buffer end time, in seconds.\n\n### `stretch.dropBuffers()`\n\nThis drops all input buffers, and resets the input buffer end time to 0.  Ordinary `ArrayBuffer` backing stores are returned through the message transfer list; shared backing stores are released without being transferred.\n\n### `stretch.dropBuffers(toSeconds)`\n\nThis drops all input buffers before the given time, but doesn't change the end time.  It returns a Promise for an object with the current input buffer extent: `{start: ..., end: ...}`.  As with a full drop, ordinary backing stores are transferred and shared backing stores are only released.\n\nThis can be useful when processing streams or very long audio files, letting the Stretch node release old buffers once that section of the input will no longer be played back.\n\n### `stretch.latency()`\n\nReturns the latency when used in \"live input\" mode, in seconds.  This is also how far ahead you might want to schedule things (`output` in `.schedule()`) to give the node enough time to fully compensate for its own latency.\n\n### `stretch.configure({...})`\n\nOptionally reconfigure, with the following fields:\n\n* `blockMs`: block length in ms (e.g. 120ms)\n* `intervalMs`: interval (default `blockMs/4`)\n* `splitComputation`: spread computation more evenly across time (default `false`)\n\nIf you set `blockMs`  to `0` or `null`, it will check for a `preset` field (with the values `\"default\"`/`\"cheaper\"`).\n","readmeFilename":"README.md"}