@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.
Files changed (80) hide show
  1. package/README.md +109 -20
  2. package/dist/analysis.d.ts +5809 -0
  3. package/dist/analysis.js +2574 -0
  4. package/dist/analysis.js.map +1 -0
  5. package/dist/index.d.ts +884 -55
  6. package/dist/index.js +1302 -292
  7. package/dist/index.js.map +1 -1
  8. package/dist/schemas/realtime-voice-changer-preset-pack.schema.json +27 -0
  9. package/dist/schemas/realtime-voice-changer-preset.schema.json +140 -0
  10. package/dist/sonare-analysis.js +2 -0
  11. package/dist/sonare-analysis.wasm +0 -0
  12. package/dist/sonare.js +1 -1
  13. package/dist/sonare.wasm +0 -0
  14. package/dist/worker.d.ts +65 -0
  15. package/dist/worker.js +734 -0
  16. package/dist/worker.js.map +1 -0
  17. package/dist/worklet.d.ts +628 -88
  18. package/dist/worklet.js +1816 -266
  19. package/dist/worklet.js.map +1 -1
  20. package/package.json +23 -11
  21. package/src/_chain_config.ts +5 -3
  22. package/src/analysis.ts +137 -0
  23. package/src/analysis_helpers.ts +40 -29
  24. package/src/audio.ts +7 -2
  25. package/src/clip_page_streamer.ts +62 -8
  26. package/src/codes.ts +87 -64
  27. package/src/effects_mastering.ts +1 -0
  28. package/src/effects_transform.ts +179 -10
  29. package/src/errors.ts +1 -0
  30. package/src/feature_core.ts +140 -2
  31. package/src/feature_music.ts +25 -3
  32. package/src/feature_pitch.ts +70 -1
  33. package/src/feature_spectral.ts +295 -7
  34. package/src/feature_spectrogram.ts +203 -3
  35. package/src/features.ts +16 -1
  36. package/src/index.ts +120 -8
  37. package/src/mastering_chain.ts +20 -8
  38. package/src/mastering_core.ts +5 -0
  39. package/src/mastering_dynamics.ts +7 -1
  40. package/src/metering.ts +6 -1
  41. package/src/mixer.ts +3 -3
  42. package/src/opfs_clip_pages.ts +26 -1
  43. package/src/project.ts +13 -0
  44. package/src/project_class.ts +204 -23
  45. package/src/project_internal.ts +38 -34
  46. package/src/project_types.ts +114 -11
  47. package/src/public_types.ts +68 -0
  48. package/src/public_types_mastering.ts +22 -0
  49. package/src/public_types_mixing.ts +27 -2
  50. package/src/public_types_music.ts +9 -0
  51. package/src/public_types_realtime.ts +23 -5
  52. package/src/public_types_spectral.ts +33 -0
  53. package/src/quick_analysis.ts +46 -10
  54. package/src/realtime_engine.ts +167 -6
  55. package/src/realtime_voice_changer.ts +51 -77
  56. package/src/sonare-analysis.js.d.ts +8 -0
  57. package/src/sonare.js.d.ts +340 -6
  58. package/src/stream_analyzer.ts +6 -1
  59. package/src/streaming_processors.ts +15 -0
  60. package/src/web_midi.ts +46 -11
  61. package/src/worker.ts +196 -0
  62. package/src/worker_client.ts +352 -0
  63. package/src/worker_protocol.ts +59 -0
  64. package/src/worklet/engine-automation.ts +2 -1
  65. package/src/worklet/engine-capture-facade.ts +13 -4
  66. package/src/worklet/engine-clips.ts +12 -3
  67. package/src/worklet/engine-node.ts +264 -21
  68. package/src/worklet/engine-offline.ts +7 -2
  69. package/src/worklet/engine-parameter-facade.ts +55 -5
  70. package/src/worklet/engine-processor.ts +435 -62
  71. package/src/worklet/engine-register.ts +9 -1
  72. package/src/worklet/engine-strips.ts +19 -3
  73. package/src/worklet/engine-sync.ts +2 -6
  74. package/src/worklet/engine-tempo-facade.ts +8 -6
  75. package/src/worklet/engine.ts +390 -13
  76. package/src/worklet/guards.ts +121 -8
  77. package/src/worklet/messages.ts +166 -8
  78. package/src/worklet/protocol.ts +305 -17
  79. package/src/worklet/voice-changer-processor.ts +12 -15
  80. package/src/worklet.ts +20 -1
@@ -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 { assertInterleavedSamples, assertSampleRate } from './validation';
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(r.samples, r.sampleRate, r.kernelHarmonic, r.kernelPercussive);
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
- return requireModule().hpssWithResidual(samples, sampleRate, kernelHarmonic, kernelPercussive);
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(samples.samples, samples.sampleRate, samples.nFft, samples.hopLength);
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
- validateSpectrogramSamples('trim', samples, sampleRate, options);
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
- return requireModule().trim(samples, sampleRate, thresholdDb);
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,