@libraz/libsonare 1.5.5 → 1.7.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/README.md +109 -20
- package/dist/analysis.d.ts +5809 -0
- package/dist/analysis.js +2574 -0
- package/dist/analysis.js.map +1 -0
- package/dist/index.d.ts +884 -55
- package/dist/index.js +1302 -292
- package/dist/index.js.map +1 -1
- package/dist/schemas/realtime-voice-changer-preset-pack.schema.json +27 -0
- package/dist/schemas/realtime-voice-changer-preset.schema.json +140 -0
- package/dist/sonare-analysis.js +2 -0
- package/dist/sonare-analysis.wasm +0 -0
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worker.d.ts +65 -0
- package/dist/worker.js +734 -0
- package/dist/worker.js.map +1 -0
- package/dist/worklet.d.ts +628 -88
- package/dist/worklet.js +1816 -266
- package/dist/worklet.js.map +1 -1
- package/package.json +23 -11
- package/src/_chain_config.ts +5 -3
- package/src/analysis.ts +137 -0
- package/src/analysis_helpers.ts +40 -29
- package/src/audio.ts +7 -2
- package/src/clip_page_streamer.ts +62 -8
- package/src/codes.ts +87 -64
- package/src/effects_mastering.ts +1 -0
- package/src/effects_transform.ts +179 -10
- package/src/errors.ts +1 -0
- package/src/feature_core.ts +140 -2
- package/src/feature_music.ts +25 -3
- package/src/feature_pitch.ts +70 -1
- package/src/feature_spectral.ts +295 -7
- package/src/feature_spectrogram.ts +203 -3
- package/src/features.ts +16 -1
- package/src/index.ts +120 -8
- package/src/mastering_chain.ts +20 -8
- package/src/mastering_core.ts +5 -0
- package/src/mastering_dynamics.ts +7 -1
- package/src/metering.ts +6 -1
- package/src/mixer.ts +3 -3
- package/src/opfs_clip_pages.ts +26 -1
- package/src/project.ts +13 -0
- package/src/project_class.ts +204 -23
- package/src/project_internal.ts +38 -34
- package/src/project_types.ts +114 -11
- package/src/public_types.ts +68 -0
- package/src/public_types_mastering.ts +22 -0
- package/src/public_types_mixing.ts +27 -2
- package/src/public_types_music.ts +9 -0
- package/src/public_types_realtime.ts +23 -5
- package/src/public_types_spectral.ts +33 -0
- package/src/quick_analysis.ts +46 -10
- package/src/realtime_engine.ts +167 -6
- package/src/realtime_voice_changer.ts +51 -77
- package/src/sonare-analysis.js.d.ts +8 -0
- package/src/sonare.js.d.ts +340 -6
- package/src/stream_analyzer.ts +6 -1
- package/src/streaming_processors.ts +15 -0
- package/src/web_midi.ts +46 -11
- package/src/worker.ts +196 -0
- package/src/worker_client.ts +352 -0
- package/src/worker_protocol.ts +59 -0
- package/src/worklet/engine-automation.ts +2 -1
- package/src/worklet/engine-capture-facade.ts +13 -4
- package/src/worklet/engine-clips.ts +12 -3
- package/src/worklet/engine-node.ts +264 -21
- package/src/worklet/engine-offline.ts +7 -2
- package/src/worklet/engine-parameter-facade.ts +55 -5
- package/src/worklet/engine-processor.ts +435 -62
- package/src/worklet/engine-register.ts +9 -1
- package/src/worklet/engine-strips.ts +19 -3
- package/src/worklet/engine-sync.ts +2 -6
- package/src/worklet/engine-tempo-facade.ts +8 -6
- package/src/worklet/engine.ts +390 -13
- package/src/worklet/guards.ts +121 -8
- package/src/worklet/messages.ts +166 -8
- package/src/worklet/protocol.ts +305 -17
- package/src/worklet/voice-changer-processor.ts +12 -15
- package/src/worklet.ts +20 -1
package/src/feature_spectral.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getSonareModule } from './module_state';
|
|
2
|
+
import type { SegmentMatrix } from './public_types';
|
|
2
3
|
import type {
|
|
3
4
|
WasmDecomposeResult,
|
|
4
5
|
WasmHpssWithResidualResult,
|
|
@@ -6,12 +7,53 @@ import type {
|
|
|
6
7
|
WasmMatrix2dResult,
|
|
7
8
|
} from './sonare.js';
|
|
8
9
|
import type { ValidateOptions } from './validation';
|
|
9
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
assertInterleavedSamples,
|
|
12
|
+
assertNonNegativeInteger,
|
|
13
|
+
assertPositiveInteger,
|
|
14
|
+
assertSampleRate,
|
|
15
|
+
assertSamples,
|
|
16
|
+
} from './validation';
|
|
10
17
|
|
|
11
18
|
function requireModule() {
|
|
12
19
|
return getSonareModule();
|
|
13
20
|
}
|
|
14
21
|
|
|
22
|
+
function resolveEffectFftOptions(
|
|
23
|
+
fnName: string,
|
|
24
|
+
nFft: unknown,
|
|
25
|
+
hopLength: unknown,
|
|
26
|
+
): { nFft: number; hopLength: number } {
|
|
27
|
+
const resolvedNFft = nFft === undefined ? 2048 : nFft;
|
|
28
|
+
const resolvedHopLength = hopLength === undefined ? 512 : hopLength;
|
|
29
|
+
if (typeof resolvedNFft !== 'number' || !Number.isInteger(resolvedNFft)) {
|
|
30
|
+
throw new TypeError(`${fnName}: nFft must be an integer`);
|
|
31
|
+
}
|
|
32
|
+
if (resolvedNFft < 2 || resolvedNFft > 2 ** 30) {
|
|
33
|
+
throw new RangeError(`${fnName}: nFft must be an even power of two >= 2`);
|
|
34
|
+
}
|
|
35
|
+
if ((resolvedNFft & (resolvedNFft - 1)) !== 0) {
|
|
36
|
+
throw new RangeError(`${fnName}: nFft must be an even power of two >= 2`);
|
|
37
|
+
}
|
|
38
|
+
if (typeof resolvedHopLength !== 'number' || !Number.isInteger(resolvedHopLength)) {
|
|
39
|
+
throw new TypeError(`${fnName}: hopLength must be an integer`);
|
|
40
|
+
}
|
|
41
|
+
if (resolvedHopLength <= 0 || resolvedHopLength > 2 ** 31 - 1) {
|
|
42
|
+
throw new RangeError(`${fnName}: hopLength must be a positive integer`);
|
|
43
|
+
}
|
|
44
|
+
return { nFft: resolvedNFft, hopLength: resolvedHopLength };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function resolveHardMask(fnName: string, value: unknown): boolean {
|
|
48
|
+
if (value === undefined) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (typeof value !== 'boolean') {
|
|
52
|
+
throw new TypeError(`${fnName}: hardMask must be a boolean`);
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
|
|
15
57
|
/** Canonical request form for frame-based spectral feature extraction. */
|
|
16
58
|
export interface SpectralFrameRequest {
|
|
17
59
|
samples: Float32Array;
|
|
@@ -76,6 +118,75 @@ export interface NnFilterRequest {
|
|
|
76
118
|
k?: number;
|
|
77
119
|
width?: number;
|
|
78
120
|
}
|
|
121
|
+
export interface SegmentCrossSimilarityRequest {
|
|
122
|
+
x: Float32Array;
|
|
123
|
+
xRows: number;
|
|
124
|
+
xCols: number;
|
|
125
|
+
y: Float32Array;
|
|
126
|
+
yRows: number;
|
|
127
|
+
yCols: number;
|
|
128
|
+
k?: number;
|
|
129
|
+
metric?: 'cosine' | 'euclidean';
|
|
130
|
+
mode?: 'connectivity' | 'affinity';
|
|
131
|
+
}
|
|
132
|
+
export interface SegmentRecurrenceMatrixRequest {
|
|
133
|
+
data: Float32Array;
|
|
134
|
+
rows: number;
|
|
135
|
+
cols: number;
|
|
136
|
+
k?: number;
|
|
137
|
+
width?: number;
|
|
138
|
+
sym?: boolean;
|
|
139
|
+
metric?: 'cosine' | 'euclidean';
|
|
140
|
+
mode?: 'connectivity' | 'affinity';
|
|
141
|
+
}
|
|
142
|
+
export interface SegmentRecurrenceToLagRequest {
|
|
143
|
+
recurrence: Float32Array;
|
|
144
|
+
n: number;
|
|
145
|
+
pad?: boolean;
|
|
146
|
+
}
|
|
147
|
+
export interface SegmentLagToRecurrenceRequest {
|
|
148
|
+
lag: Float32Array;
|
|
149
|
+
rows: number;
|
|
150
|
+
lags: number;
|
|
151
|
+
}
|
|
152
|
+
export interface SegmentSubsegmentRequest {
|
|
153
|
+
data: Float32Array;
|
|
154
|
+
rows: number;
|
|
155
|
+
cols: number;
|
|
156
|
+
boundaries: Int32Array;
|
|
157
|
+
nSegments?: number;
|
|
158
|
+
}
|
|
159
|
+
export interface SegmentAgglomerativeRequest {
|
|
160
|
+
data: Float32Array;
|
|
161
|
+
rows: number;
|
|
162
|
+
cols: number;
|
|
163
|
+
k: number;
|
|
164
|
+
linkage?: 'average' | 'single' | 'complete' | 'ward';
|
|
165
|
+
}
|
|
166
|
+
export interface SegmentPathEnhanceRequest {
|
|
167
|
+
recurrence: Float32Array;
|
|
168
|
+
n: number;
|
|
169
|
+
win: number;
|
|
170
|
+
maxRatio?: number;
|
|
171
|
+
minRatio?: number;
|
|
172
|
+
nFilters?: number;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function validateSegmentMatrix(
|
|
176
|
+
fnName: string,
|
|
177
|
+
data: Float32Array,
|
|
178
|
+
rows: number,
|
|
179
|
+
cols: number,
|
|
180
|
+
dataName: string,
|
|
181
|
+
): void {
|
|
182
|
+
assertPositiveInteger(fnName, rows, 'rows');
|
|
183
|
+
assertPositiveInteger(fnName, cols, 'cols');
|
|
184
|
+
assertSamples(fnName, data, true, dataName);
|
|
185
|
+
const expected = rows * cols;
|
|
186
|
+
if (!Number.isSafeInteger(expected) || data.length !== expected) {
|
|
187
|
+
throw new RangeError(`${fnName}: ${dataName} length must equal rows * cols`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
79
190
|
export interface RemixRequest {
|
|
80
191
|
samples: Float32Array;
|
|
81
192
|
intervals: Int32Array | ArrayLike<number>;
|
|
@@ -90,6 +201,9 @@ export interface HpssWithResidualRequest {
|
|
|
90
201
|
sampleRate?: number;
|
|
91
202
|
kernelHarmonic?: number;
|
|
92
203
|
kernelPercussive?: number;
|
|
204
|
+
nFft?: number;
|
|
205
|
+
hopLength?: number;
|
|
206
|
+
hardMask?: boolean;
|
|
93
207
|
}
|
|
94
208
|
export interface LufsInterleavedRequest extends ValidateOptions {
|
|
95
209
|
samples: Float32Array;
|
|
@@ -466,18 +580,42 @@ export function hpssWithResidual(
|
|
|
466
580
|
sampleRate?: number,
|
|
467
581
|
kernelHarmonic?: number,
|
|
468
582
|
kernelPercussive?: number,
|
|
583
|
+
nFft?: number,
|
|
584
|
+
hopLength?: number,
|
|
585
|
+
hardMask?: boolean,
|
|
469
586
|
): WasmHpssWithResidualResult;
|
|
470
587
|
export function hpssWithResidual(
|
|
471
588
|
samples: Float32Array | HpssWithResidualRequest,
|
|
472
589
|
sampleRate = 22050,
|
|
473
590
|
kernelHarmonic = 31,
|
|
474
591
|
kernelPercussive = 31,
|
|
592
|
+
nFft?: number,
|
|
593
|
+
hopLength?: number,
|
|
594
|
+
hardMask?: boolean,
|
|
475
595
|
): WasmHpssWithResidualResult {
|
|
476
596
|
if (!(samples instanceof Float32Array)) {
|
|
477
597
|
const r = samples;
|
|
478
|
-
return hpssWithResidual(
|
|
598
|
+
return hpssWithResidual(
|
|
599
|
+
r.samples,
|
|
600
|
+
r.sampleRate,
|
|
601
|
+
r.kernelHarmonic,
|
|
602
|
+
r.kernelPercussive,
|
|
603
|
+
r.nFft,
|
|
604
|
+
r.hopLength,
|
|
605
|
+
r.hardMask,
|
|
606
|
+
);
|
|
479
607
|
}
|
|
480
|
-
|
|
608
|
+
const fftOptions = resolveEffectFftOptions('hpssWithResidual', nFft, hopLength);
|
|
609
|
+
const resolvedHardMask = resolveHardMask('hpssWithResidual', hardMask);
|
|
610
|
+
return requireModule().hpssWithResidualEx(
|
|
611
|
+
samples,
|
|
612
|
+
sampleRate,
|
|
613
|
+
kernelHarmonic,
|
|
614
|
+
kernelPercussive,
|
|
615
|
+
fftOptions.nFft,
|
|
616
|
+
fftOptions.hopLength,
|
|
617
|
+
resolvedHardMask,
|
|
618
|
+
);
|
|
481
619
|
}
|
|
482
620
|
|
|
483
621
|
/**
|
|
@@ -537,23 +675,31 @@ export function ebur128LoudnessRange(
|
|
|
537
675
|
* @param hopLength - Hop length (default: 512)
|
|
538
676
|
* @returns Spectral bandwidth in Hz for each frame
|
|
539
677
|
*/
|
|
540
|
-
export function spectralBandwidth(request: SpectralFrameRequest): Float32Array;
|
|
678
|
+
export function spectralBandwidth(request: SpectralFrameRequest & { p?: number }): Float32Array;
|
|
541
679
|
export function spectralBandwidth(
|
|
542
680
|
samples: Float32Array,
|
|
543
681
|
sampleRate?: number,
|
|
544
682
|
nFft?: number,
|
|
545
683
|
hopLength?: number,
|
|
684
|
+
p?: number,
|
|
546
685
|
): Float32Array;
|
|
547
686
|
export function spectralBandwidth(
|
|
548
|
-
samples: Float32Array | SpectralFrameRequest,
|
|
687
|
+
samples: Float32Array | (SpectralFrameRequest & { p?: number }),
|
|
549
688
|
sampleRate = 22050,
|
|
550
689
|
nFft = 2048,
|
|
551
690
|
hopLength = 512,
|
|
691
|
+
p = 2,
|
|
552
692
|
): Float32Array {
|
|
553
693
|
if (!(samples instanceof Float32Array)) {
|
|
554
|
-
return spectralBandwidth(
|
|
694
|
+
return spectralBandwidth(
|
|
695
|
+
samples.samples,
|
|
696
|
+
samples.sampleRate,
|
|
697
|
+
samples.nFft,
|
|
698
|
+
samples.hopLength,
|
|
699
|
+
samples.p,
|
|
700
|
+
);
|
|
555
701
|
}
|
|
556
|
-
return requireModule().spectralBandwidth(samples, sampleRate, nFft, hopLength);
|
|
702
|
+
return requireModule().spectralBandwidth(samples, sampleRate, nFft, hopLength, p);
|
|
557
703
|
}
|
|
558
704
|
|
|
559
705
|
/**
|
|
@@ -621,6 +767,33 @@ export function spectralFlatness(
|
|
|
621
767
|
return requireModule().spectralFlatness(samples, sampleRate, nFft, hopLength);
|
|
622
768
|
}
|
|
623
769
|
|
|
770
|
+
export function spectralFlux(request: SpectralFrameRequest & { lag?: number }): Float32Array;
|
|
771
|
+
export function spectralFlux(
|
|
772
|
+
samples: Float32Array,
|
|
773
|
+
sampleRate?: number,
|
|
774
|
+
nFft?: number,
|
|
775
|
+
hopLength?: number,
|
|
776
|
+
lag?: number,
|
|
777
|
+
): Float32Array;
|
|
778
|
+
export function spectralFlux(
|
|
779
|
+
samples: Float32Array | (SpectralFrameRequest & { lag?: number }),
|
|
780
|
+
sampleRate = 22050,
|
|
781
|
+
nFft = 2048,
|
|
782
|
+
hopLength = 512,
|
|
783
|
+
lag = 1,
|
|
784
|
+
): Float32Array {
|
|
785
|
+
if (!(samples instanceof Float32Array)) {
|
|
786
|
+
return spectralFlux(
|
|
787
|
+
samples.samples,
|
|
788
|
+
samples.sampleRate,
|
|
789
|
+
samples.nFft,
|
|
790
|
+
samples.hopLength,
|
|
791
|
+
samples.lag,
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
return requireModule().spectralFlux(samples, sampleRate, nFft, hopLength, lag);
|
|
795
|
+
}
|
|
796
|
+
|
|
624
797
|
/**
|
|
625
798
|
* Compute zero crossing rate.
|
|
626
799
|
*
|
|
@@ -681,3 +854,118 @@ export function rmsEnergy(
|
|
|
681
854
|
}
|
|
682
855
|
return requireModule().rmsEnergy(samples, sampleRate, frameLength, hopLength);
|
|
683
856
|
}
|
|
857
|
+
|
|
858
|
+
/** Column-wise cross-similarity (librosa.segment.cross_similarity). */
|
|
859
|
+
export function segmentCrossSimilarity(request: SegmentCrossSimilarityRequest): SegmentMatrix {
|
|
860
|
+
validateSegmentMatrix('segmentCrossSimilarity', request.x, request.xRows, request.xCols, 'x');
|
|
861
|
+
validateSegmentMatrix('segmentCrossSimilarity', request.y, request.yRows, request.yCols, 'y');
|
|
862
|
+
if (request.xRows !== request.yRows) {
|
|
863
|
+
throw new RangeError('segmentCrossSimilarity: feature dimensions must match');
|
|
864
|
+
}
|
|
865
|
+
assertNonNegativeInteger('segmentCrossSimilarity', request.k ?? 0, 'k');
|
|
866
|
+
return requireModule().segmentCrossSimilarity(
|
|
867
|
+
request.x,
|
|
868
|
+
request.xRows,
|
|
869
|
+
request.xCols,
|
|
870
|
+
request.y,
|
|
871
|
+
request.yRows,
|
|
872
|
+
request.yCols,
|
|
873
|
+
request.k ?? 0,
|
|
874
|
+
request.metric ?? 'cosine',
|
|
875
|
+
request.mode ?? 'connectivity',
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/** Self-similarity recurrence matrix (librosa.segment.recurrence_matrix). */
|
|
880
|
+
export function segmentRecurrenceMatrix(request: SegmentRecurrenceMatrixRequest): SegmentMatrix {
|
|
881
|
+
validateSegmentMatrix(
|
|
882
|
+
'segmentRecurrenceMatrix',
|
|
883
|
+
request.data,
|
|
884
|
+
request.rows,
|
|
885
|
+
request.cols,
|
|
886
|
+
'data',
|
|
887
|
+
);
|
|
888
|
+
assertNonNegativeInteger('segmentRecurrenceMatrix', request.k ?? 0, 'k');
|
|
889
|
+
assertNonNegativeInteger('segmentRecurrenceMatrix', request.width ?? 1, 'width');
|
|
890
|
+
return requireModule().segmentRecurrenceMatrix(
|
|
891
|
+
request.data,
|
|
892
|
+
request.rows,
|
|
893
|
+
request.cols,
|
|
894
|
+
request.k ?? 0,
|
|
895
|
+
request.width ?? 1,
|
|
896
|
+
request.sym ?? false,
|
|
897
|
+
request.metric ?? 'euclidean',
|
|
898
|
+
request.mode ?? 'connectivity',
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/** Convert an `n × n` recurrence matrix to a lag matrix. */
|
|
903
|
+
export function segmentRecurrenceToLag(request: SegmentRecurrenceToLagRequest): SegmentMatrix {
|
|
904
|
+
validateSegmentMatrix(
|
|
905
|
+
'segmentRecurrenceToLag',
|
|
906
|
+
request.recurrence,
|
|
907
|
+
request.n,
|
|
908
|
+
request.n,
|
|
909
|
+
'recurrence',
|
|
910
|
+
);
|
|
911
|
+
return requireModule().segmentRecurrenceToLag(
|
|
912
|
+
request.recurrence,
|
|
913
|
+
request.n,
|
|
914
|
+
request.pad ?? false,
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/** Convert a lag matrix back to an `n × n` recurrence matrix. */
|
|
919
|
+
export function segmentLagToRecurrence(request: SegmentLagToRecurrenceRequest): SegmentMatrix {
|
|
920
|
+
validateSegmentMatrix('segmentLagToRecurrence', request.lag, request.rows, request.lags, 'lag');
|
|
921
|
+
return requireModule().segmentLagToRecurrence(request.lag, request.rows, request.lags);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/** Refine frame boundaries by clustering within each parent segment. */
|
|
925
|
+
export function segmentSubsegment(request: SegmentSubsegmentRequest): Int32Array {
|
|
926
|
+
validateSegmentMatrix('segmentSubsegment', request.data, request.rows, request.cols, 'data');
|
|
927
|
+
assertPositiveInteger('segmentSubsegment', request.nSegments ?? 4, 'nSegments');
|
|
928
|
+
return requireModule().segmentSubsegment(
|
|
929
|
+
request.data,
|
|
930
|
+
request.rows,
|
|
931
|
+
request.cols,
|
|
932
|
+
request.boundaries,
|
|
933
|
+
request.nSegments ?? 4,
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/** Cluster feature columns and return one label per column. */
|
|
938
|
+
export function segmentAgglomerative(request: SegmentAgglomerativeRequest): Int32Array {
|
|
939
|
+
validateSegmentMatrix('segmentAgglomerative', request.data, request.rows, request.cols, 'data');
|
|
940
|
+
assertPositiveInteger('segmentAgglomerative', request.k, 'k');
|
|
941
|
+
return requireModule().segmentAgglomerative(
|
|
942
|
+
request.data,
|
|
943
|
+
request.rows,
|
|
944
|
+
request.cols,
|
|
945
|
+
request.k,
|
|
946
|
+
request.linkage ?? 'average',
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
/** Enhance diagonal paths in an `n × n` recurrence matrix. */
|
|
951
|
+
export function segmentPathEnhance(request: SegmentPathEnhanceRequest): SegmentMatrix {
|
|
952
|
+
validateSegmentMatrix(
|
|
953
|
+
'segmentPathEnhance',
|
|
954
|
+
request.recurrence,
|
|
955
|
+
request.n,
|
|
956
|
+
request.n,
|
|
957
|
+
'recurrence',
|
|
958
|
+
);
|
|
959
|
+
assertPositiveInteger('segmentPathEnhance', request.win, 'win');
|
|
960
|
+
assertPositiveInteger('segmentPathEnhance', request.maxRatio ?? 2, 'maxRatio');
|
|
961
|
+
assertNonNegativeInteger('segmentPathEnhance', request.minRatio ?? 0, 'minRatio');
|
|
962
|
+
assertPositiveInteger('segmentPathEnhance', request.nFilters ?? 7, 'nFilters');
|
|
963
|
+
return requireModule().segmentPathEnhance(
|
|
964
|
+
request.recurrence,
|
|
965
|
+
request.n,
|
|
966
|
+
request.win,
|
|
967
|
+
request.maxRatio ?? 2,
|
|
968
|
+
request.minRatio ?? 0,
|
|
969
|
+
request.nFilters ?? 7,
|
|
970
|
+
);
|
|
971
|
+
}
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
MelPowerResult,
|
|
5
5
|
MelSpectrogramResult,
|
|
6
6
|
MfccResult,
|
|
7
|
+
ReassignedSpectrogramResult,
|
|
7
8
|
StftPowerResult,
|
|
8
9
|
StftResult,
|
|
9
10
|
} from './public_types';
|
|
@@ -49,6 +50,17 @@ export interface MfccRequest extends MelSpectrogramRequest {
|
|
|
49
50
|
lifter?: number;
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
export interface MelDeltaRequest extends GuardedOptions {
|
|
54
|
+
features: Float32Array;
|
|
55
|
+
nFeatures: number;
|
|
56
|
+
nFrames: number;
|
|
57
|
+
width?: number;
|
|
58
|
+
}
|
|
59
|
+
export interface ReassignedSpectrogramRequest extends SpectrogramRequest {
|
|
60
|
+
refPower?: number;
|
|
61
|
+
fillNan?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
52
64
|
export interface MfccToMelRequest extends GuardedOptions {
|
|
53
65
|
mfccCoefficients: Float32Array;
|
|
54
66
|
nMfcc: number;
|
|
@@ -75,6 +87,16 @@ export interface MelToAudioRequest extends MelToStftRequest {
|
|
|
75
87
|
hopLength?: number;
|
|
76
88
|
nIter?: number;
|
|
77
89
|
}
|
|
90
|
+
export interface GriffinLimRequest extends GuardedOptions {
|
|
91
|
+
magnitude: Float32Array;
|
|
92
|
+
nBins: number;
|
|
93
|
+
nFrames: number;
|
|
94
|
+
sampleRate?: number;
|
|
95
|
+
nFft?: number;
|
|
96
|
+
hopLength?: number;
|
|
97
|
+
nIter?: number;
|
|
98
|
+
momentum?: number;
|
|
99
|
+
}
|
|
78
100
|
|
|
79
101
|
/** Canonical request form for Griffin-Lim reconstruction from MFCCs. */
|
|
80
102
|
export interface MfccToAudioRequest extends MfccToMelRequest {
|
|
@@ -91,6 +113,8 @@ export interface TrimRequest extends GuardedOptions {
|
|
|
91
113
|
samples: Float32Array;
|
|
92
114
|
sampleRate: number;
|
|
93
115
|
thresholdDb?: number;
|
|
116
|
+
frameLength?: number;
|
|
117
|
+
hopLength?: number;
|
|
94
118
|
}
|
|
95
119
|
|
|
96
120
|
function validateSpectrogramSamples(
|
|
@@ -161,19 +185,58 @@ export function trim(
|
|
|
161
185
|
thresholdDb?: number,
|
|
162
186
|
options?: GuardedOptions,
|
|
163
187
|
): Float32Array;
|
|
188
|
+
export function trim(
|
|
189
|
+
samples: Float32Array,
|
|
190
|
+
sampleRate: number,
|
|
191
|
+
thresholdDb?: number,
|
|
192
|
+
frameLength?: number,
|
|
193
|
+
hopLength?: number,
|
|
194
|
+
options?: GuardedOptions,
|
|
195
|
+
): Float32Array;
|
|
164
196
|
export function trim(
|
|
165
197
|
samples: Float32Array | TrimRequest,
|
|
166
198
|
sampleRate = 22050,
|
|
167
199
|
thresholdDb = -60.0,
|
|
200
|
+
frameLengthOrOptions?: number | GuardedOptions,
|
|
201
|
+
hopLength?: number,
|
|
168
202
|
options: GuardedOptions = {},
|
|
169
203
|
): Float32Array {
|
|
170
204
|
if (!(samples instanceof Float32Array)) {
|
|
171
205
|
const r = samples;
|
|
172
|
-
return trim(r.samples, r.sampleRate, r.thresholdDb, r);
|
|
206
|
+
return trim(r.samples, r.sampleRate, r.thresholdDb, r.frameLength, r.hopLength, r);
|
|
207
|
+
}
|
|
208
|
+
if (frameLengthOrOptions === null) {
|
|
209
|
+
throw new TypeError('trim: frameLength must be an integer or options object');
|
|
210
|
+
}
|
|
211
|
+
if (
|
|
212
|
+
frameLengthOrOptions !== undefined &&
|
|
213
|
+
typeof frameLengthOrOptions !== 'number' &&
|
|
214
|
+
typeof frameLengthOrOptions !== 'object'
|
|
215
|
+
) {
|
|
216
|
+
throw new TypeError('trim: frameLength must be an integer or options object');
|
|
173
217
|
}
|
|
174
|
-
|
|
218
|
+
const positionalOptions =
|
|
219
|
+
typeof frameLengthOrOptions === 'object' && frameLengthOrOptions !== null
|
|
220
|
+
? frameLengthOrOptions
|
|
221
|
+
: options;
|
|
222
|
+
const positionalFrameLength =
|
|
223
|
+
typeof frameLengthOrOptions === 'number' ? frameLengthOrOptions : undefined;
|
|
224
|
+
const resolvedFrameLength = positionalFrameLength ?? 2048;
|
|
225
|
+
const resolvedHopLength = hopLength === undefined ? 512 : hopLength;
|
|
226
|
+
validateSpectrogramSamples('trim', samples, sampleRate, positionalOptions);
|
|
175
227
|
assertFiniteScalar('trim', thresholdDb, 'thresholdDb');
|
|
176
|
-
|
|
228
|
+
assertPositiveInteger('trim', resolvedFrameLength, 'frameLength');
|
|
229
|
+
assertPositiveInteger('trim', resolvedHopLength, 'hopLength');
|
|
230
|
+
if (resolvedFrameLength > 2 ** 31 - 1 || resolvedHopLength > 2 ** 31 - 1) {
|
|
231
|
+
throw new RangeError('trim: frameLength and hopLength must fit in a signed 32-bit integer');
|
|
232
|
+
}
|
|
233
|
+
return requireModule().trimEx(
|
|
234
|
+
samples,
|
|
235
|
+
sampleRate,
|
|
236
|
+
thresholdDb,
|
|
237
|
+
resolvedFrameLength,
|
|
238
|
+
resolvedHopLength,
|
|
239
|
+
);
|
|
177
240
|
}
|
|
178
241
|
|
|
179
242
|
// ============================================================================
|
|
@@ -524,6 +587,90 @@ export function mfcc(
|
|
|
524
587
|
);
|
|
525
588
|
}
|
|
526
589
|
|
|
590
|
+
/** First-order regression delta of a row-major feature matrix. */
|
|
591
|
+
export function melDelta(request: MelDeltaRequest): Float32Array;
|
|
592
|
+
export function melDelta(
|
|
593
|
+
features: Float32Array,
|
|
594
|
+
nFeatures: number,
|
|
595
|
+
nFrames: number,
|
|
596
|
+
width?: number,
|
|
597
|
+
): Float32Array;
|
|
598
|
+
export function melDelta(
|
|
599
|
+
features: Float32Array | MelDeltaRequest,
|
|
600
|
+
nFeatures?: number,
|
|
601
|
+
nFrames?: number,
|
|
602
|
+
width = 9,
|
|
603
|
+
): Float32Array {
|
|
604
|
+
const request: MelDeltaRequest =
|
|
605
|
+
features instanceof Float32Array
|
|
606
|
+
? { features, nFeatures: nFeatures ?? 0, nFrames: nFrames ?? 0, width }
|
|
607
|
+
: features;
|
|
608
|
+
assertPositiveInteger('melDelta', request.nFeatures, 'nFeatures');
|
|
609
|
+
assertPositiveInteger('melDelta', request.nFrames, 'nFrames');
|
|
610
|
+
assertPositiveInteger('melDelta', request.width ?? 9, 'width');
|
|
611
|
+
if ((request.width ?? 9) < 3 || (request.width ?? 9) % 2 === 0) {
|
|
612
|
+
throw new RangeError('melDelta: width must be an odd integer of at least 3');
|
|
613
|
+
}
|
|
614
|
+
if (request.features.length !== request.nFeatures * request.nFrames) {
|
|
615
|
+
throw new RangeError('melDelta: feature matrix length must equal nFeatures * nFrames');
|
|
616
|
+
}
|
|
617
|
+
return requireModule().melDelta(
|
|
618
|
+
request.features,
|
|
619
|
+
request.nFeatures,
|
|
620
|
+
request.nFrames,
|
|
621
|
+
request.width ?? 9,
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/** Auger-Flandrin reassigned spectrogram (row-major [nBins x nFrames] arrays). */
|
|
626
|
+
export function reassignedSpectrogram(
|
|
627
|
+
request: ReassignedSpectrogramRequest,
|
|
628
|
+
): ReassignedSpectrogramResult;
|
|
629
|
+
export function reassignedSpectrogram(
|
|
630
|
+
samples: Float32Array,
|
|
631
|
+
sampleRate?: number,
|
|
632
|
+
nFft?: number,
|
|
633
|
+
hopLength?: number,
|
|
634
|
+
refPower?: number,
|
|
635
|
+
fillNan?: boolean,
|
|
636
|
+
): ReassignedSpectrogramResult;
|
|
637
|
+
export function reassignedSpectrogram(
|
|
638
|
+
samples: Float32Array | ReassignedSpectrogramRequest,
|
|
639
|
+
sampleRate = 22050,
|
|
640
|
+
nFft = 2048,
|
|
641
|
+
hopLength = 512,
|
|
642
|
+
refPower = 1e-6,
|
|
643
|
+
fillNan = false,
|
|
644
|
+
): ReassignedSpectrogramResult {
|
|
645
|
+
if (!(samples instanceof Float32Array)) {
|
|
646
|
+
const request = samples;
|
|
647
|
+
return reassignedSpectrogram(
|
|
648
|
+
request.samples,
|
|
649
|
+
request.sampleRate,
|
|
650
|
+
request.nFft,
|
|
651
|
+
request.hopLength,
|
|
652
|
+
request.refPower,
|
|
653
|
+
request.fillNan,
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
assertSamples('reassignedSpectrogram', samples, true);
|
|
657
|
+
assertSampleRate('reassignedSpectrogram', sampleRate);
|
|
658
|
+
assertPositiveInteger('reassignedSpectrogram', nFft, 'nFft');
|
|
659
|
+
assertPositiveInteger('reassignedSpectrogram', hopLength, 'hopLength');
|
|
660
|
+
assertFiniteScalar('reassignedSpectrogram', refPower, 'refPower');
|
|
661
|
+
if (refPower < 0) {
|
|
662
|
+
throw new RangeError('reassignedSpectrogram: refPower must be non-negative');
|
|
663
|
+
}
|
|
664
|
+
return requireModule().reassignedSpectrogram(
|
|
665
|
+
samples,
|
|
666
|
+
sampleRate,
|
|
667
|
+
nFft,
|
|
668
|
+
hopLength,
|
|
669
|
+
refPower,
|
|
670
|
+
fillNan,
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
|
|
527
674
|
// ============================================================================
|
|
528
675
|
// Features - Inverse reconstruction
|
|
529
676
|
// ============================================================================
|
|
@@ -663,6 +810,59 @@ export function melToAudio(
|
|
|
663
810
|
);
|
|
664
811
|
}
|
|
665
812
|
|
|
813
|
+
/** Reconstruct audio from an STFT magnitude matrix via Griffin-Lim. */
|
|
814
|
+
export function griffinLim(request: GriffinLimRequest): Float32Array;
|
|
815
|
+
export function griffinLim(
|
|
816
|
+
magnitude: Float32Array,
|
|
817
|
+
nBins: number,
|
|
818
|
+
nFrames: number,
|
|
819
|
+
sampleRate?: number,
|
|
820
|
+
nFft?: number,
|
|
821
|
+
hopLength?: number,
|
|
822
|
+
nIter?: number,
|
|
823
|
+
momentum?: number,
|
|
824
|
+
options?: GuardedOptions,
|
|
825
|
+
): Float32Array;
|
|
826
|
+
export function griffinLim(
|
|
827
|
+
magnitude: Float32Array | GriffinLimRequest,
|
|
828
|
+
nBins = 0,
|
|
829
|
+
nFrames = 0,
|
|
830
|
+
sampleRate = 22050,
|
|
831
|
+
nFft = 2048,
|
|
832
|
+
hopLength = 512,
|
|
833
|
+
nIter = 32,
|
|
834
|
+
momentum = 0.99,
|
|
835
|
+
options: GuardedOptions = {},
|
|
836
|
+
): Float32Array {
|
|
837
|
+
if (!(magnitude instanceof Float32Array)) {
|
|
838
|
+
const request = magnitude;
|
|
839
|
+
return griffinLim(
|
|
840
|
+
request.magnitude,
|
|
841
|
+
request.nBins,
|
|
842
|
+
request.nFrames,
|
|
843
|
+
request.sampleRate,
|
|
844
|
+
request.nFft,
|
|
845
|
+
request.hopLength,
|
|
846
|
+
request.nIter,
|
|
847
|
+
request.momentum,
|
|
848
|
+
request,
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
assertSampleRate('griffinLim', sampleRate);
|
|
852
|
+
validateMatrix('griffinLim', magnitude, nBins, nFrames, 'magnitude', 'nBins', options);
|
|
853
|
+
validatePositiveIntegers('griffinLim', { nFft, hopLength, nIter });
|
|
854
|
+
return requireModule().griffinLim(
|
|
855
|
+
magnitude,
|
|
856
|
+
nBins,
|
|
857
|
+
nFrames,
|
|
858
|
+
sampleRate,
|
|
859
|
+
nFft,
|
|
860
|
+
hopLength,
|
|
861
|
+
nIter,
|
|
862
|
+
momentum,
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
|
|
666
866
|
/**
|
|
667
867
|
* Invert MFCC coefficients back to a Mel power spectrogram. Mirrors
|
|
668
868
|
* `feature::mfcc_to_mel`.
|
package/src/features.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export {
|
|
2
2
|
amplitudeToDb,
|
|
3
|
+
chirp,
|
|
4
|
+
clicks,
|
|
3
5
|
cyclicTempogram,
|
|
4
6
|
dbToAmplitude,
|
|
5
7
|
dbToPower,
|
|
@@ -15,6 +17,7 @@ export {
|
|
|
15
17
|
melToHz,
|
|
16
18
|
midiToHz,
|
|
17
19
|
noteToHz,
|
|
20
|
+
onsetBacktrack,
|
|
18
21
|
padCenter,
|
|
19
22
|
pcen,
|
|
20
23
|
peakPick,
|
|
@@ -25,6 +28,7 @@ export {
|
|
|
25
28
|
splitSilence,
|
|
26
29
|
tempogram,
|
|
27
30
|
timeToFrames,
|
|
31
|
+
tone,
|
|
28
32
|
tonnetz,
|
|
29
33
|
trimSilence,
|
|
30
34
|
vectorNormalize,
|
|
@@ -47,7 +51,7 @@ export {
|
|
|
47
51
|
vqt,
|
|
48
52
|
vqtToAudio,
|
|
49
53
|
} from './feature_music';
|
|
50
|
-
export { pitchPyin, pitchYin } from './feature_pitch';
|
|
54
|
+
export { noteSegments, piptrack, pitchPyin, pitchYin } from './feature_pitch';
|
|
51
55
|
export { resample } from './feature_resample';
|
|
52
56
|
export {
|
|
53
57
|
decompose,
|
|
@@ -62,10 +66,18 @@ export {
|
|
|
62
66
|
polyFeatures,
|
|
63
67
|
remix,
|
|
64
68
|
rmsEnergy,
|
|
69
|
+
segmentAgglomerative,
|
|
70
|
+
segmentCrossSimilarity,
|
|
71
|
+
segmentLagToRecurrence,
|
|
72
|
+
segmentPathEnhance,
|
|
73
|
+
segmentRecurrenceMatrix,
|
|
74
|
+
segmentRecurrenceToLag,
|
|
75
|
+
segmentSubsegment,
|
|
65
76
|
spectralBandwidth,
|
|
66
77
|
spectralCentroid,
|
|
67
78
|
spectralContrast,
|
|
68
79
|
spectralFlatness,
|
|
80
|
+
spectralFlux,
|
|
69
81
|
spectralRolloff,
|
|
70
82
|
zeroCrossingRate,
|
|
71
83
|
zeroCrossings,
|
|
@@ -75,12 +87,15 @@ export {
|
|
|
75
87
|
chroma,
|
|
76
88
|
chromaCens,
|
|
77
89
|
chromaCqt,
|
|
90
|
+
griffinLim,
|
|
91
|
+
melDelta,
|
|
78
92
|
melSpectrogram,
|
|
79
93
|
melToAudio,
|
|
80
94
|
melToStft,
|
|
81
95
|
mfcc,
|
|
82
96
|
mfccToAudio,
|
|
83
97
|
mfccToMel,
|
|
98
|
+
reassignedSpectrogram,
|
|
84
99
|
stft,
|
|
85
100
|
stftDb,
|
|
86
101
|
trim,
|