@libraz/libsonare 1.7.2 → 1.8.1
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/NOTICE +178 -0
- package/README.md +26 -203
- package/dist/_chain_config.d.ts +14 -0
- package/dist/_chain_config.d.ts.map +1 -0
- package/dist/_effects_common.d.ts +7 -0
- package/dist/_effects_common.d.ts.map +1 -0
- package/dist/_feature_validation.d.ts +8 -0
- package/dist/_feature_validation.d.ts.map +1 -0
- package/dist/_fft_options.d.ts +24 -0
- package/dist/_fft_options.d.ts.map +1 -0
- package/dist/align_take.d.ts +50 -0
- package/dist/align_take.d.ts.map +1 -0
- package/dist/analysis.d.ts +29 -5840
- package/dist/analysis.d.ts.map +1 -0
- package/dist/analysis.js +874 -722
- package/dist/analysis.js.map +1 -1
- package/dist/analysis_helpers.d.ts +9 -0
- package/dist/analysis_helpers.d.ts.map +1 -0
- package/dist/audio.d.ts +163 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/clip_page_streamer.d.ts +133 -0
- package/dist/clip_page_streamer.d.ts.map +1 -0
- package/dist/codes.d.ts +44 -0
- package/dist/codes.d.ts.map +1 -0
- package/dist/effects_mastering.d.ts +23 -0
- package/dist/effects_mastering.d.ts.map +1 -0
- package/dist/effects_note_ops.d.ts +502 -0
- package/dist/effects_note_ops.d.ts.map +1 -0
- package/dist/effects_percussive.d.ts +185 -0
- package/dist/effects_percussive.d.ts.map +1 -0
- package/dist/effects_separation.d.ts +65 -0
- package/dist/effects_separation.d.ts.map +1 -0
- package/dist/effects_spectral.d.ts +28 -0
- package/dist/effects_spectral.d.ts.map +1 -0
- package/dist/effects_timepitch.d.ts +134 -0
- package/dist/effects_timepitch.d.ts.map +1 -0
- package/dist/effects_voice_change.d.ts +53 -0
- package/dist/effects_voice_change.d.ts.map +1 -0
- package/dist/errors.d.ts +51 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/feature_core.d.ts +341 -0
- package/dist/feature_core.d.ts.map +1 -0
- package/dist/feature_decompose.d.ts +278 -0
- package/dist/feature_decompose.d.ts.map +1 -0
- package/dist/feature_inverse.d.ts +128 -0
- package/dist/feature_inverse.d.ts.map +1 -0
- package/dist/feature_loudness.d.ts +66 -0
- package/dist/feature_loudness.d.ts.map +1 -0
- package/dist/feature_music.d.ts +307 -0
- package/dist/feature_music.d.ts.map +1 -0
- package/dist/feature_pitch.d.ts +108 -0
- package/dist/feature_pitch.d.ts.map +1 -0
- package/dist/feature_resample.d.ts +16 -0
- package/dist/feature_resample.d.ts.map +1 -0
- package/dist/feature_spectral.d.ts +137 -0
- package/dist/feature_spectral.d.ts.map +1 -0
- package/dist/feature_spectrogram.d.ts +198 -0
- package/dist/feature_spectrogram.d.ts.map +1 -0
- package/dist/features.d.ts +10 -0
- package/dist/features.d.ts.map +1 -0
- package/dist/hrtf/default.shrf +0 -0
- package/dist/index.d.ts +74 -7431
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4173 -1621
- package/dist/index.js.map +1 -1
- package/dist/instrument_types.d.ts +517 -0
- package/dist/instrument_types.d.ts.map +1 -0
- package/dist/live_audio.d.ts +35 -0
- package/dist/live_audio.d.ts.map +1 -0
- package/dist/mastering_chain.d.ts +213 -0
- package/dist/mastering_chain.d.ts.map +1 -0
- package/dist/mastering_core.d.ts +483 -0
- package/dist/mastering_core.d.ts.map +1 -0
- package/dist/mastering_dynamics.d.ts +80 -0
- package/dist/mastering_dynamics.d.ts.map +1 -0
- package/dist/metering.d.ts +287 -0
- package/dist/metering.d.ts.map +1 -0
- package/dist/mixer.d.ts +464 -0
- package/dist/mixer.d.ts.map +1 -0
- package/dist/mixing_assistant.d.ts +62 -0
- package/dist/mixing_assistant.d.ts.map +1 -0
- package/dist/mixing_oneshot.d.ts +40 -0
- package/dist/mixing_oneshot.d.ts.map +1 -0
- package/dist/module_state.d.ts +15 -0
- package/dist/module_state.d.ts.map +1 -0
- package/dist/opfs_clip_pages.d.ts +28 -0
- package/dist/opfs_clip_pages.d.ts.map +1 -0
- package/dist/playback_renderer.d.ts +128 -0
- package/dist/playback_renderer.d.ts.map +1 -0
- package/dist/polyphony.d.ts +202 -0
- package/dist/polyphony.d.ts.map +1 -0
- package/dist/project.d.ts +8 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project_class.d.ts +562 -0
- package/dist/project_class.d.ts.map +1 -0
- package/dist/project_internal.d.ts +194 -0
- package/dist/project_internal.d.ts.map +1 -0
- package/dist/project_synth.d.ts +74 -0
- package/dist/project_synth.d.ts.map +1 -0
- package/dist/project_types.d.ts +654 -0
- package/dist/project_types.d.ts.map +1 -0
- package/dist/public_types.d.ts +185 -0
- package/dist/public_types.d.ts.map +1 -0
- package/dist/public_types_acoustic.d.ts +215 -0
- package/dist/public_types_acoustic.d.ts.map +1 -0
- package/dist/public_types_mastering.d.ts +528 -0
- package/dist/public_types_mastering.d.ts.map +1 -0
- package/dist/public_types_mixing.d.ts +436 -0
- package/dist/public_types_mixing.d.ts.map +1 -0
- package/dist/public_types_music.d.ts +619 -0
- package/dist/public_types_music.d.ts.map +1 -0
- package/dist/public_types_playback.d.ts +165 -0
- package/dist/public_types_playback.d.ts.map +1 -0
- package/dist/public_types_realtime.d.ts +174 -0
- package/dist/public_types_realtime.d.ts.map +1 -0
- package/dist/public_types_repair.d.ts +424 -0
- package/dist/public_types_repair.d.ts.map +1 -0
- package/dist/public_types_spectral.d.ts +697 -0
- package/dist/public_types_spectral.d.ts.map +1 -0
- package/dist/quick_analysis.d.ts +445 -0
- package/dist/quick_analysis.d.ts.map +1 -0
- package/dist/realtime_engine.d.ts +893 -0
- package/dist/realtime_engine.d.ts.map +1 -0
- package/dist/realtime_voice_changer.d.ts +158 -0
- package/dist/realtime_voice_changer.d.ts.map +1 -0
- package/dist/repair_dereverb.d.ts +187 -0
- package/dist/repair_dereverb.d.ts.map +1 -0
- package/dist/repair_impulsive.d.ts +186 -0
- package/dist/repair_impulsive.d.ts.map +1 -0
- package/dist/repair_noise.d.ts +239 -0
- package/dist/repair_noise.d.ts.map +1 -0
- package/dist/repair_trim.d.ts +123 -0
- package/dist/repair_trim.d.ts.map +1 -0
- package/dist/sample_bank.d.ts +84 -0
- package/dist/sample_bank.d.ts.map +1 -0
- package/dist/scale.d.ts +10 -0
- package/dist/scale.d.ts.map +1 -0
- package/dist/schemas/mixer-scene.schema.json +393 -0
- package/dist/schemas/playback-renderer-config.schema.json +392 -0
- package/dist/sonare-analysis.d.ts +8 -0
- package/dist/sonare-analysis.js +2 -2
- package/dist/sonare-analysis.wasm +0 -0
- package/dist/sonare.d.ts +3945 -0
- package/dist/sonare.js +2 -2
- package/dist/sonare.wasm +0 -0
- package/dist/stream_analyzer.d.ts +163 -0
- package/dist/stream_analyzer.d.ts.map +1 -0
- package/dist/stream_types.d.ts +214 -0
- package/dist/stream_types.d.ts.map +1 -0
- package/dist/streaming_mixing.d.ts +6 -0
- package/dist/streaming_mixing.d.ts.map +1 -0
- package/dist/streaming_processors.d.ts +340 -0
- package/dist/streaming_processors.d.ts.map +1 -0
- package/dist/transcribe.d.ts +77 -0
- package/dist/transcribe.d.ts.map +1 -0
- package/dist/validation.d.ts +140 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/web_midi.d.ts +77 -0
- package/dist/web_midi.d.ts.map +1 -0
- package/dist/worker.d.ts +5 -48
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +94 -41
- package/dist/worker.js.map +1 -1
- package/dist/worker_client.d.ts +96 -0
- package/dist/worker_client.d.ts.map +1 -0
- package/dist/worker_protocol.d.ts +43 -0
- package/dist/worker_protocol.d.ts.map +1 -0
- package/dist/worklet/audio_types.d.ts +21 -0
- package/dist/worklet/audio_types.d.ts.map +1 -0
- package/dist/worklet/engine-automation.d.ts +29 -0
- package/dist/worklet/engine-automation.d.ts.map +1 -0
- package/dist/worklet/engine-capture-facade.d.ts +35 -0
- package/dist/worklet/engine-capture-facade.d.ts.map +1 -0
- package/dist/worklet/engine-clips.d.ts +23 -0
- package/dist/worklet/engine-clips.d.ts.map +1 -0
- package/dist/worklet/engine-markers.d.ts +40 -0
- package/dist/worklet/engine-markers.d.ts.map +1 -0
- package/dist/worklet/engine-mixer-facade.d.ts +164 -0
- package/dist/worklet/engine-mixer-facade.d.ts.map +1 -0
- package/dist/worklet/engine-node.d.ts +83 -0
- package/dist/worklet/engine-node.d.ts.map +1 -0
- package/dist/worklet/engine-offline.d.ts +81 -0
- package/dist/worklet/engine-offline.d.ts.map +1 -0
- package/dist/worklet/engine-options.d.ts +12 -0
- package/dist/worklet/engine-options.d.ts.map +1 -0
- package/dist/worklet/engine-parameter-facade.d.ts +106 -0
- package/dist/worklet/engine-parameter-facade.d.ts.map +1 -0
- package/dist/worklet/engine-processor.d.ts +72 -0
- package/dist/worklet/engine-processor.d.ts.map +1 -0
- package/dist/worklet/engine-register.d.ts +2 -0
- package/dist/worklet/engine-register.d.ts.map +1 -0
- package/dist/worklet/engine-strips.d.ts +75 -0
- package/dist/worklet/engine-strips.d.ts.map +1 -0
- package/dist/worklet/engine-sync.d.ts +39 -0
- package/dist/worklet/engine-sync.d.ts.map +1 -0
- package/dist/worklet/engine-tempo-facade.d.ts +48 -0
- package/dist/worklet/engine-tempo-facade.d.ts.map +1 -0
- package/dist/worklet/engine.d.ts +418 -0
- package/dist/worklet/engine.d.ts.map +1 -0
- package/dist/worklet/guards.d.ts +53 -0
- package/dist/worklet/guards.d.ts.map +1 -0
- package/dist/worklet/messages.d.ts +710 -0
- package/dist/worklet/messages.d.ts.map +1 -0
- package/dist/worklet/mixer-processor.d.ts +46 -0
- package/dist/worklet/mixer-processor.d.ts.map +1 -0
- package/dist/worklet/playback-processor.d.ts +62 -0
- package/dist/worklet/playback-processor.d.ts.map +1 -0
- package/dist/worklet/protocol.d.ts +331 -0
- package/dist/worklet/protocol.d.ts.map +1 -0
- package/dist/worklet/voice-changer-processor.d.ts +41 -0
- package/dist/worklet/voice-changer-processor.d.ts.map +1 -0
- package/dist/worklet.d.ts +16 -2515
- package/dist/worklet.d.ts.map +1 -0
- package/dist/worklet.js +3200 -541
- package/dist/worklet.js.map +1 -1
- package/package.json +23 -12
- package/src/_effects_common.ts +47 -0
- package/src/_feature_validation.ts +34 -0
- package/src/_fft_options.ts +39 -0
- package/src/align_take.ts +64 -0
- package/src/analysis.ts +56 -3
- package/src/analysis_helpers.ts +7 -0
- package/src/audio.ts +106 -3
- package/src/codes.ts +39 -2
- package/src/effects_mastering.ts +101 -22
- package/src/effects_note_ops.ts +683 -0
- package/src/effects_percussive.ts +217 -0
- package/src/effects_separation.ts +150 -0
- package/src/effects_spectral.ts +60 -0
- package/src/effects_timepitch.ts +388 -0
- package/src/errors.ts +23 -1
- package/src/feature_core.ts +127 -2
- package/src/feature_decompose.ts +633 -0
- package/src/feature_inverse.ts +454 -0
- package/src/feature_loudness.ts +125 -0
- package/src/feature_music.ts +107 -14
- package/src/feature_pitch.ts +96 -1
- package/src/feature_spectral.ts +16 -611
- package/src/feature_spectrogram.ts +63 -450
- package/src/features.ts +36 -22
- package/src/index.ts +288 -30
- package/src/instrument_types.ts +645 -0
- package/src/live_audio.ts +27 -1
- package/src/mastering_chain.ts +184 -0
- package/src/mastering_core.ts +441 -32
- package/src/mastering_dynamics.ts +22 -11
- package/src/metering.ts +67 -24
- package/src/mixer.ts +251 -22
- package/src/mixing_assistant.ts +138 -0
- package/src/mixing_oneshot.ts +10 -5
- package/src/module_state.ts +24 -2
- package/src/playback_renderer.ts +252 -0
- package/src/polyphony.ts +279 -0
- package/src/project.ts +61 -24
- package/src/project_class.ts +450 -27
- package/src/project_internal.ts +149 -42
- package/src/project_synth.ts +67 -1
- package/src/project_types.ts +271 -271
- package/src/public_types.ts +122 -3
- package/src/public_types_acoustic.ts +112 -3
- package/src/public_types_mastering.ts +275 -73
- package/src/public_types_mixing.ts +363 -1
- package/src/public_types_music.ts +312 -2
- package/src/public_types_playback.ts +196 -0
- package/src/public_types_realtime.ts +39 -7
- package/src/public_types_repair.ts +446 -0
- package/src/public_types_spectral.ts +491 -5
- package/src/quick_analysis.ts +203 -26
- package/src/realtime_engine.ts +773 -34
- package/src/realtime_voice_changer.ts +55 -1
- package/src/repair_dereverb.ts +299 -0
- package/src/repair_impulsive.ts +395 -0
- package/src/repair_noise.ts +425 -0
- package/src/repair_trim.ts +226 -0
- package/src/sample_bank.ts +113 -0
- package/src/sonare.js.d.ts +1158 -30
- package/src/stream_analyzer.ts +36 -4
- package/src/stream_types.ts +37 -0
- package/src/streaming_mixing.ts +1 -1
- package/src/streaming_processors.ts +202 -10
- package/src/transcribe.ts +89 -0
- package/src/validation.ts +285 -11
- package/src/web_midi.ts +1 -6
- package/src/worker.ts +18 -2
- package/src/worklet/audio_types.ts +37 -0
- package/src/worklet/engine-mixer-facade.ts +800 -32
- package/src/worklet/engine-node.ts +99 -29
- package/src/worklet/engine-offline.ts +14 -8
- package/src/worklet/engine-parameter-facade.ts +21 -0
- package/src/worklet/engine-processor.ts +332 -93
- package/src/worklet/engine-register.ts +32 -18
- package/src/worklet/engine-strips.ts +275 -9
- package/src/worklet/engine-sync.ts +20 -7
- package/src/worklet/engine.ts +394 -48
- package/src/worklet/guards.ts +195 -44
- package/src/worklet/messages.ts +229 -2
- package/src/worklet/mixer-processor.ts +117 -48
- package/src/worklet/playback-processor.ts +300 -0
- package/src/worklet/protocol.ts +82 -11
- package/src/worklet/voice-changer-processor.ts +17 -11
- package/src/worklet.ts +17 -0
- package/src/effects_transform.ts +0 -718
- package/src/mastering_repair.ts +0 -273
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Percussive event editing: locating struck sounds in audio and rendering an
|
|
3
|
+
* edited set of them back.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getSonareModule } from './module_state';
|
|
7
|
+
import type { PercussiveEvent, PercussiveEventInput } from './public_types';
|
|
8
|
+
import type { ValidateOptions } from './validation';
|
|
9
|
+
import { assertPercussiveSeparation, assertSampleRate, assertSamples } from './validation';
|
|
10
|
+
|
|
11
|
+
function requireModule() {
|
|
12
|
+
return getSonareModule();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The separation a percussive event's signal is lifted out with. Extraction
|
|
17
|
+
* measures events against it and rendering has to repeat it, so both calls take
|
|
18
|
+
* the same four fields and a render must be given what the extraction was.
|
|
19
|
+
*/
|
|
20
|
+
export interface PercussiveSeparationOptions {
|
|
21
|
+
/**
|
|
22
|
+
* FFT size and hop the separation and the onset detector share. They cannot be
|
|
23
|
+
* set apart: an event measured on one framing and lifted out on another is not
|
|
24
|
+
* the same signal. Default 2048 and 512. The pair must overlap-add — `nFft`
|
|
25
|
+
* even and at least 2, `hopLength` no more than half of it — because the
|
|
26
|
+
* separation inverts an STFT. A negative value is rejected on its own, before
|
|
27
|
+
* the zero-is-default rule could swallow it.
|
|
28
|
+
*/
|
|
29
|
+
nFft?: number;
|
|
30
|
+
/** Hop length in samples. Default 512. */
|
|
31
|
+
hopLength?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Median filter lengths the separation runs, along time and along frequency. A
|
|
34
|
+
* longer harmonic kernel calls more of a sustained sound harmonic. Default 31;
|
|
35
|
+
* any other value must be odd and positive, so an even one is rejected rather
|
|
36
|
+
* than rounded. 1 is legal and degenerate rather than an error: a length-1
|
|
37
|
+
* median is the identity, so both components come back as the source.
|
|
38
|
+
*/
|
|
39
|
+
hpssKernelHarmonic?: number;
|
|
40
|
+
/** Vertical median filter length, under the same rule. Default 31. */
|
|
41
|
+
hpssKernelPercussive?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Canonical request form for {@link extractPercussiveEvents}. */
|
|
45
|
+
export interface ExtractPercussiveEventsRequest
|
|
46
|
+
extends PercussiveSeparationOptions,
|
|
47
|
+
ValidateOptions {
|
|
48
|
+
samples: Float32Array | readonly number[];
|
|
49
|
+
/**
|
|
50
|
+
* Sample rate in Hz. Required: `maxEventMs` is converted to samples with this
|
|
51
|
+
* rate, so a wrong/omitted value caps the spans differently.
|
|
52
|
+
*/
|
|
53
|
+
sampleRate: number;
|
|
54
|
+
/**
|
|
55
|
+
* Minimum frames between consecutive onsets. Default 1, and a whole number:
|
|
56
|
+
* 0 is how the default is spelled, so a fractional wait is refused rather
|
|
57
|
+
* than truncated onto it. Negative is rejected.
|
|
58
|
+
*/
|
|
59
|
+
onsetWait?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Offset added to the detector's adaptive threshold; raising it finds fewer,
|
|
62
|
+
* stronger hits and lowering it finds more. Default 0.06, so exactly zero is
|
|
63
|
+
* the one value not selectable here — a negative one is accepted and puts the
|
|
64
|
+
* threshold below the default, which is the direction a caller reaching for
|
|
65
|
+
* zero wanted anyway.
|
|
66
|
+
*/
|
|
67
|
+
onsetDelta?: number;
|
|
68
|
+
/**
|
|
69
|
+
* Caps a span that no onset follows. It binds at the end of a phrase and at the
|
|
70
|
+
* end of the track; anywhere else the next onset closes the span first. Default
|
|
71
|
+
* 500 ms.
|
|
72
|
+
*/
|
|
73
|
+
maxEventMs?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Drops an event whose `percussiveRatio` falls below this; must be in `[0, 1]`.
|
|
76
|
+
* 0 is both the default and the meaningful "keep everything". Raising it is
|
|
77
|
+
* useful on material that is mostly drums and wrong on a dense mix, where it
|
|
78
|
+
* also drops real hits sitting over a loud sustain.
|
|
79
|
+
*/
|
|
80
|
+
minPercussiveRatio?: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Canonical request form for {@link renderPercussiveEvents}. */
|
|
84
|
+
export interface RenderPercussiveEventsRequest
|
|
85
|
+
extends PercussiveSeparationOptions,
|
|
86
|
+
ValidateOptions {
|
|
87
|
+
samples: Float32Array | readonly number[];
|
|
88
|
+
/**
|
|
89
|
+
* Sample rate in Hz. Required: `fadeMs` is converted to samples with this rate,
|
|
90
|
+
* so a wrong/omitted value changes the fade length.
|
|
91
|
+
*/
|
|
92
|
+
sampleRate: number;
|
|
93
|
+
/** The events to render, with their edits. Source spans must not overlap. */
|
|
94
|
+
events: readonly PercussiveEventInput[];
|
|
95
|
+
/**
|
|
96
|
+
* Fade-out at the tail of each lifted span. Default 5 ms, so a zero-length
|
|
97
|
+
* fade is unreachable here rather than rejected — and a hard cut is not a thing
|
|
98
|
+
* to want anyway, because what the fade shapes is the signal being subtracted,
|
|
99
|
+
* so squaring it off leaves a step. There is deliberately
|
|
100
|
+
* no matching fade-in: a span opens in front of its transient, where the
|
|
101
|
+
* percussive component is near-silent, so cutting square there costs nothing
|
|
102
|
+
* and keeps a muted hit's attack from surviving inside a fade.
|
|
103
|
+
*/
|
|
104
|
+
fadeMs?: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Extract editable percussive events from audio alone.
|
|
109
|
+
*
|
|
110
|
+
* Each event is a struck sound located in time: a span in source samples, its
|
|
111
|
+
* detector strength, the percussive peak over the span, the share of the span's
|
|
112
|
+
* energy the separation called percussive, and an identity
|
|
113
|
+
* {@link PercussiveEventEdit}. Edit the events and hand them to
|
|
114
|
+
* {@link renderPercussiveEvents} to apply the result — the source audio is never
|
|
115
|
+
* mutated, and a set whose edits are all identity renders back to the input bit
|
|
116
|
+
* for bit.
|
|
117
|
+
*
|
|
118
|
+
* Onsets are detected on the percussive component rather than on the source, so
|
|
119
|
+
* a harmonic attack is attenuated before the detector sees it instead of being
|
|
120
|
+
* filtered out afterwards. Each onset opens a span that the next one closes,
|
|
121
|
+
* capped by `maxEventMs` and never running past the end of the audio.
|
|
122
|
+
*
|
|
123
|
+
* Each onset is backtracked to the transient's start, which is not optional and
|
|
124
|
+
* is why there is no knob for it: peak-picking lands after the attack, and a span
|
|
125
|
+
* that opened there would report the next hit's peak and leave its own attack
|
|
126
|
+
* behind when muted.
|
|
127
|
+
*
|
|
128
|
+
* An event carries no pitch and is never associated with a {@link NoteObject} —
|
|
129
|
+
* a struck sound has no steady F0 to edit, so the two models are extracted by
|
|
130
|
+
* separate calls.
|
|
131
|
+
*
|
|
132
|
+
* @param request - Audio, its sample rate, and the separation, peak-picking and
|
|
133
|
+
* span options
|
|
134
|
+
* @returns One {@link PercussiveEvent} per detected hit, in time order; an empty
|
|
135
|
+
* array when nothing was detected
|
|
136
|
+
* @throws RangeError when the samples or sample rate fail the shared input checks
|
|
137
|
+
* @throws SonareError (`InvalidParameter`) on a kernel size that is not an
|
|
138
|
+
* integer within the 32-bit range, a framing size that is negative or outside
|
|
139
|
+
* that range, a framing that breaks constant overlap-add, an `onsetWait` that
|
|
140
|
+
* is fractional, negative or non-finite, a negative or non-finite `onsetDelta`
|
|
141
|
+
* / `maxEventMs`, or a `minPercussiveRatio` outside `[0, 1]`
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* const events = extractPercussiveEvents({ samples, sampleRate });
|
|
146
|
+
* // Drop the second hit and push the third 10 ms late.
|
|
147
|
+
* events[1].edit.muted = true;
|
|
148
|
+
* events[2].edit.timeOffsetSamples = Math.round(0.01 * sampleRate);
|
|
149
|
+
* const edited = renderPercussiveEvents({ samples, sampleRate, events });
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
export function extractPercussiveEvents(
|
|
153
|
+
request: ExtractPercussiveEventsRequest,
|
|
154
|
+
): PercussiveEvent[] {
|
|
155
|
+
assertSamples('extractPercussiveEvents', request.samples, request.validate !== false);
|
|
156
|
+
assertSampleRate('extractPercussiveEvents', request.sampleRate);
|
|
157
|
+
assertPercussiveSeparation('extractPercussiveEvents', request);
|
|
158
|
+
return requireModule().extractPercussiveEvents(request.samples, request.sampleRate, request);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Render edited percussive events back over their source audio.
|
|
163
|
+
*
|
|
164
|
+
* Per event the lifted signal is the percussive component over
|
|
165
|
+
* `[onsetSample, offsetSample)` under the tail fade. It is subtracted where it
|
|
166
|
+
* sits and, unless the event is muted, added back at the shifted position scaled
|
|
167
|
+
* by the gain. Only that signal moves, so muting a hit leaves the harmonic
|
|
168
|
+
* content under it sounding and moving one does not drag its neighbours' sustain
|
|
169
|
+
* along.
|
|
170
|
+
*
|
|
171
|
+
* Each event's span and `edit` are read; `strength`, `peakAmplitude` and
|
|
172
|
+
* `percussiveRatio` are ignored, so an extracted event can be passed back as-is,
|
|
173
|
+
* or an event can be built by hand from the span alone. A set whose edits are all
|
|
174
|
+
* identity reproduces the input bit for bit and runs no separation at all.
|
|
175
|
+
*
|
|
176
|
+
* Pass the separation the events were extracted with: a different one lifts a
|
|
177
|
+
* different signal out of the span than the one the events describe. It is
|
|
178
|
+
* validated even when every edit is the identity and no separation runs, so an
|
|
179
|
+
* unusable framing is an error on every set rather than on some of them.
|
|
180
|
+
*
|
|
181
|
+
* Overlap is checked on the source spans only. Where `timeOffsetSamples` lands an
|
|
182
|
+
* event is not, so two moved events may be written over each other, and a shift
|
|
183
|
+
* that pushes the signal past either end is truncated there rather than wrapped.
|
|
184
|
+
*
|
|
185
|
+
* @param request - Source audio, the events to render, the separation and the
|
|
186
|
+
* tail fade
|
|
187
|
+
* @returns The rendered audio, the same length and sample rate as the input
|
|
188
|
+
* @throws RangeError when the samples or sample rate fail the shared input checks
|
|
189
|
+
* @throws SonareError (`InvalidParameter`) on an event whose span is empty,
|
|
190
|
+
* reversed or outside the audio, overlapping source spans, a non-finite
|
|
191
|
+
* `gainDb`, a kernel size that is not an integer within the 32-bit range, a
|
|
192
|
+
* framing that breaks constant overlap-add, or a negative or non-finite
|
|
193
|
+
* `fadeMs`
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* const events = extractPercussiveEvents({ samples, sampleRate });
|
|
198
|
+
*
|
|
199
|
+
* // Lift the loudest hit by 3 dB and leave the rest untouched.
|
|
200
|
+
* const loudest = events.reduce((a, b) => (a.strength >= b.strength ? a : b));
|
|
201
|
+
* const edited = events.map((event) =>
|
|
202
|
+
* event === loudest ? { ...event, edit: { ...event.edit, gainDb: 3 } } : event,
|
|
203
|
+
* );
|
|
204
|
+
* const rendered = renderPercussiveEvents({ samples, sampleRate, events: edited });
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
export function renderPercussiveEvents(request: RenderPercussiveEventsRequest): Float32Array {
|
|
208
|
+
assertSamples('renderPercussiveEvents', request.samples, request.validate !== false);
|
|
209
|
+
assertSampleRate('renderPercussiveEvents', request.sampleRate);
|
|
210
|
+
assertPercussiveSeparation('renderPercussiveEvents', request);
|
|
211
|
+
return requireModule().renderPercussiveEvents(
|
|
212
|
+
request.samples,
|
|
213
|
+
request.sampleRate,
|
|
214
|
+
request.events,
|
|
215
|
+
request,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harmonic/percussive separation: the masked split and the two shortcuts
|
|
3
|
+
* that return a single component.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { resolveFftOptions } from './_fft_options';
|
|
7
|
+
import { getSonareModule } from './module_state';
|
|
8
|
+
import type { HpssResult } from './public_types';
|
|
9
|
+
import type { ValidateOptions } from './validation';
|
|
10
|
+
import { assertHpssKernels, assertSamples } from './validation';
|
|
11
|
+
|
|
12
|
+
function requireModule() {
|
|
13
|
+
return getSonareModule();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function resolveHardMask(value: unknown, fnName: string): boolean {
|
|
17
|
+
if (value === undefined) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (typeof value !== 'boolean') {
|
|
21
|
+
throw new TypeError(`${fnName}: hardMask must be a boolean`);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Canonical request form for HPSS. */
|
|
27
|
+
export interface HpssRequest {
|
|
28
|
+
samples: Float32Array;
|
|
29
|
+
sampleRate?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Horizontal median filter size, in STFT frames: a positive odd integer at
|
|
32
|
+
* most 524287. Default 31. The ceiling is 524288 and an even kernel is
|
|
33
|
+
* refused, so 524287 is the largest legal value.
|
|
34
|
+
*/
|
|
35
|
+
kernelHarmonic?: number;
|
|
36
|
+
/** Vertical median filter size, in STFT bins, under the same rule. Default 31. */
|
|
37
|
+
kernelPercussive?: number;
|
|
38
|
+
nFft?: number;
|
|
39
|
+
hopLength?: number;
|
|
40
|
+
hardMask?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface HarmonicRequest extends ValidateOptions {
|
|
44
|
+
samples: Float32Array;
|
|
45
|
+
sampleRate?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface PercussiveRequest extends ValidateOptions {
|
|
49
|
+
samples: Float32Array;
|
|
50
|
+
sampleRate?: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Perform Harmonic-Percussive Source Separation (HPSS).
|
|
55
|
+
*
|
|
56
|
+
* @param samples - Audio samples (mono, float32)
|
|
57
|
+
* @param sampleRate - Sample rate in Hz (default: 22050)
|
|
58
|
+
* @param kernelHarmonic - Horizontal median filter size in STFT frames; a
|
|
59
|
+
* positive odd integer at most 524287 (default: 31)
|
|
60
|
+
* @param kernelPercussive - Vertical median filter size in STFT bins, under the
|
|
61
|
+
* same rule (default: 31)
|
|
62
|
+
* @returns Separated harmonic and percussive components
|
|
63
|
+
* @throws SonareError (`InvalidParameter`) on a kernel that is not an integer
|
|
64
|
+
* within the signed 32-bit range, or one the core rejects as even,
|
|
65
|
+
* non-positive or above its ceiling
|
|
66
|
+
*/
|
|
67
|
+
export function hpss(request: HpssRequest): HpssResult;
|
|
68
|
+
export function hpss(
|
|
69
|
+
samples: Float32Array,
|
|
70
|
+
sampleRate?: number,
|
|
71
|
+
kernelHarmonic?: number,
|
|
72
|
+
kernelPercussive?: number,
|
|
73
|
+
nFft?: number,
|
|
74
|
+
hopLength?: number,
|
|
75
|
+
hardMask?: boolean,
|
|
76
|
+
): HpssResult;
|
|
77
|
+
export function hpss(
|
|
78
|
+
samples: Float32Array | HpssRequest,
|
|
79
|
+
sampleRate = 22050,
|
|
80
|
+
kernelHarmonic = 31,
|
|
81
|
+
kernelPercussive = 31,
|
|
82
|
+
nFft?: number,
|
|
83
|
+
hopLength?: number,
|
|
84
|
+
hardMask?: boolean,
|
|
85
|
+
): HpssResult {
|
|
86
|
+
const request =
|
|
87
|
+
samples instanceof Float32Array
|
|
88
|
+
? { samples, sampleRate, kernelHarmonic, kernelPercussive, nFft, hopLength, hardMask }
|
|
89
|
+
: samples;
|
|
90
|
+
const fftOptions = resolveFftOptions('hpss', request.nFft, request.hopLength);
|
|
91
|
+
const resolvedHardMask = resolveHardMask(request.hardMask, 'hpss');
|
|
92
|
+
const resolvedKernelHarmonic = request.kernelHarmonic ?? 31;
|
|
93
|
+
const resolvedKernelPercussive = request.kernelPercussive ?? 31;
|
|
94
|
+
assertHpssKernels('hpss', resolvedKernelHarmonic, resolvedKernelPercussive);
|
|
95
|
+
return requireModule().hpssEx(
|
|
96
|
+
request.samples,
|
|
97
|
+
request.sampleRate ?? 22050,
|
|
98
|
+
resolvedKernelHarmonic,
|
|
99
|
+
resolvedKernelPercussive,
|
|
100
|
+
fftOptions.nFft,
|
|
101
|
+
fftOptions.hopLength,
|
|
102
|
+
resolvedHardMask,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Extract harmonic component from audio.
|
|
108
|
+
*
|
|
109
|
+
* @param samples - Audio samples (mono, float32)
|
|
110
|
+
* @param sampleRate - Sample rate in Hz
|
|
111
|
+
* @returns Harmonic component
|
|
112
|
+
*/
|
|
113
|
+
export function harmonic(request: HarmonicRequest): Float32Array;
|
|
114
|
+
export function harmonic(
|
|
115
|
+
samples: Float32Array,
|
|
116
|
+
sampleRate?: number,
|
|
117
|
+
options?: ValidateOptions,
|
|
118
|
+
): Float32Array;
|
|
119
|
+
export function harmonic(
|
|
120
|
+
samples: Float32Array | HarmonicRequest,
|
|
121
|
+
sampleRate = 22050,
|
|
122
|
+
options: ValidateOptions = {},
|
|
123
|
+
): Float32Array {
|
|
124
|
+
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
125
|
+
assertSamples('harmonic', request.samples, request.validate !== false);
|
|
126
|
+
return requireModule().harmonic(request.samples, request.sampleRate ?? 22050);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Extract percussive component from audio.
|
|
131
|
+
*
|
|
132
|
+
* @param samples - Audio samples (mono, float32)
|
|
133
|
+
* @param sampleRate - Sample rate in Hz
|
|
134
|
+
* @returns Percussive component
|
|
135
|
+
*/
|
|
136
|
+
export function percussive(request: PercussiveRequest): Float32Array;
|
|
137
|
+
export function percussive(
|
|
138
|
+
samples: Float32Array,
|
|
139
|
+
sampleRate?: number,
|
|
140
|
+
options?: ValidateOptions,
|
|
141
|
+
): Float32Array;
|
|
142
|
+
export function percussive(
|
|
143
|
+
samples: Float32Array | PercussiveRequest,
|
|
144
|
+
sampleRate = 22050,
|
|
145
|
+
options: ValidateOptions = {},
|
|
146
|
+
): Float32Array {
|
|
147
|
+
const request = samples instanceof Float32Array ? { samples, sampleRate, ...options } : samples;
|
|
148
|
+
assertSamples('percussive', request.samples, request.validate !== false);
|
|
149
|
+
return requireModule().percussive(request.samples, request.sampleRate ?? 22050);
|
|
150
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Region-based spectral editing: time x frequency rectangles applied over an
|
|
3
|
+
* STFT and resynthesized.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getSonareModule } from './module_state';
|
|
7
|
+
import type { SpectralEditOptions, SpectralRegionOp } from './public_types';
|
|
8
|
+
import type { ValidateOptions } from './validation';
|
|
9
|
+
import { assertSampleRate, assertSamples } from './validation';
|
|
10
|
+
|
|
11
|
+
function requireModule() {
|
|
12
|
+
return getSonareModule();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SpectralEditRequest extends SpectralEditOptions, ValidateOptions {
|
|
16
|
+
samples: Float32Array;
|
|
17
|
+
sampleRate: number;
|
|
18
|
+
ops?: SpectralRegionOp[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Apply region-based spectral edits (gain/attenuate/mute/heal) to mono audio.
|
|
23
|
+
*
|
|
24
|
+
* Each op is a time x frequency rectangle applied in array order over a single
|
|
25
|
+
* STFT buffer, so a later op observes the result of earlier ops. The output has
|
|
26
|
+
* the same length and sample rate as the input; an empty `ops` list is an
|
|
27
|
+
* identity transform (within the iSTFT's own tolerance).
|
|
28
|
+
*
|
|
29
|
+
* @param samples - Audio samples (mono, float32)
|
|
30
|
+
* @param sampleRate - Sample rate in Hz
|
|
31
|
+
* @param ops - Region edit ops applied in order ({@link SpectralRegionOp})
|
|
32
|
+
* @param options - STFT + heal configuration ({@link SpectralEditOptions})
|
|
33
|
+
* @returns Edited audio
|
|
34
|
+
*/
|
|
35
|
+
export function spectralEdit(request: SpectralEditRequest): Float32Array;
|
|
36
|
+
export function spectralEdit(
|
|
37
|
+
samples: Float32Array,
|
|
38
|
+
sampleRate: number,
|
|
39
|
+
ops?: SpectralRegionOp[],
|
|
40
|
+
options?: SpectralEditOptions & ValidateOptions,
|
|
41
|
+
): Float32Array;
|
|
42
|
+
export function spectralEdit(
|
|
43
|
+
samples: Float32Array | SpectralEditRequest,
|
|
44
|
+
sampleRate?: number,
|
|
45
|
+
ops: SpectralRegionOp[] = [],
|
|
46
|
+
options: SpectralEditOptions & ValidateOptions = {},
|
|
47
|
+
): Float32Array {
|
|
48
|
+
const request: SpectralEditRequest =
|
|
49
|
+
samples instanceof Float32Array
|
|
50
|
+
? { samples, sampleRate: sampleRate as number, ops, ...options }
|
|
51
|
+
: samples;
|
|
52
|
+
assertSamples('spectralEdit', request.samples, request.validate !== false);
|
|
53
|
+
assertSampleRate('spectralEdit', request.sampleRate);
|
|
54
|
+
return requireModule().spectralEdit(
|
|
55
|
+
request.samples,
|
|
56
|
+
request.sampleRate,
|
|
57
|
+
request.ops ?? [],
|
|
58
|
+
request as unknown as Record<string, unknown>,
|
|
59
|
+
);
|
|
60
|
+
}
|