@libraz/libsonare 1.5.3 → 1.5.4

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/dist/index.d.ts CHANGED
@@ -1176,12 +1176,12 @@ interface PercussiveRequest extends ValidateOptions {
1176
1176
  }
1177
1177
  interface TimeStretchRequest extends ValidateOptions {
1178
1178
  samples: Float32Array;
1179
- sampleRate: number;
1179
+ sampleRate?: number;
1180
1180
  rate: number;
1181
1181
  }
1182
1182
  interface PitchShiftRequest extends ValidateOptions {
1183
1183
  samples: Float32Array;
1184
- sampleRate: number;
1184
+ sampleRate?: number;
1185
1185
  semitones: number;
1186
1186
  }
1187
1187
  interface PitchCorrectToMidiRequest extends ValidateOptions {
@@ -1215,7 +1215,7 @@ interface NoteMoveRequest extends NoteMoveOptions, ValidateOptions {
1215
1215
  }
1216
1216
  interface NormalizeRequest extends ValidateOptions {
1217
1217
  samples: Float32Array;
1218
- sampleRate: number;
1218
+ sampleRate?: number;
1219
1219
  targetDb?: number;
1220
1220
  }
1221
1221
  interface SpectralEditRequest extends SpectralEditOptions, ValidateOptions {
@@ -1256,7 +1256,7 @@ declare function percussive(samples: Float32Array, sampleRate?: number, options?
1256
1256
  * Time-stretch audio without changing pitch.
1257
1257
  *
1258
1258
  * @param samples - Audio samples (mono, float32)
1259
- * @param sampleRate - Sample rate in Hz
1259
+ * @param sampleRate - Sample rate in Hz (default: 22050)
1260
1260
  * @param rate - Time stretch rate (0.5 = double duration, 2.0 = half duration)
1261
1261
  * @returns Time-stretched audio
1262
1262
  */
@@ -1266,7 +1266,7 @@ declare function timeStretch(samples: Float32Array, sampleRate: number, rate: nu
1266
1266
  * Pitch-shift audio without changing duration.
1267
1267
  *
1268
1268
  * @param samples - Audio samples (mono, float32)
1269
- * @param sampleRate - Sample rate in Hz
1269
+ * @param sampleRate - Sample rate in Hz (default: 22050)
1270
1270
  * @param semitones - Pitch shift in semitones (+12 = one octave up, -12 = one octave down)
1271
1271
  * @returns Pitch-shifted audio
1272
1272
  */
@@ -1340,7 +1340,7 @@ declare function noteMove(samples: Float32Array, sampleRate?: number, options?:
1340
1340
  * Normalize audio to target peak level.
1341
1341
  *
1342
1342
  * @param samples - Audio samples (mono, float32)
1343
- * @param sampleRate - Sample rate in Hz
1343
+ * @param sampleRate - Sample rate in Hz (default: 22050)
1344
1344
  * @param targetDb - Target peak level in dB (default: 0 dB = full scale)
1345
1345
  * @returns Normalized audio
1346
1346
  */
@@ -2645,6 +2645,10 @@ declare class Project {
2645
2645
  undo(): void;
2646
2646
  /** Redo the most recently undone edit. */
2647
2647
  redo(): void;
2648
+ /** Clear the undo/redo history without changing the current project state. */
2649
+ clearHistory(): void;
2650
+ /** Cap the undo history depth (clamped to >= 1); evicts oldest entries beyond the cap. */
2651
+ setMaxUndoDepth(depth: number): void;
2648
2652
  /** Replace a MIDI clip's entire event list. */
2649
2653
  setMidiEvents(clipId: number, events: ReadonlyArray<ProjectMidiEvent | readonly [number, number, number]>): void;
2650
2654
  /** Import an in-memory SMF buffer; returns the first added clip id. */
@@ -5079,8 +5083,10 @@ interface WaveformPeaksReport {
5079
5083
  declare function meteringStereoCorrelation(request: MeteringStereoRequest): number;
5080
5084
  declare function meteringStereoCorrelation(left: Float32Array, right: Float32Array, sampleRate?: number, options?: ValidateOptions): number;
5081
5085
  /**
5082
- * Side / mid energy ratio, clamped to `[0, 2]`: 0 = pure mono, ~1 = wide
5083
- * stereo, 2 = fully decorrelated / out-of-phase.
5086
+ * Side / mid energy ratio in `[0, +Infinity)`: 0 = pure mono, ~1 = wide stereo,
5087
+ * larger = increasingly decorrelated / out-of-phase. The value is unbounded and
5088
+ * returns `Infinity` when the mid channel is silent (a mono-collapsed / fully
5089
+ * out-of-phase signal).
5084
5090
  */
5085
5091
  declare function meteringStereoWidth(request: MeteringStereoRequest): number;
5086
5092
  declare function meteringStereoWidth(left: Float32Array, right: Float32Array, sampleRate?: number, options?: ValidateOptions): number;
@@ -6022,7 +6028,7 @@ declare class RealtimeVoiceChanger {
6022
6028
  constructor(config?: RealtimeVoiceChangerConfigInput);
6023
6029
  prepare(sampleRate: number, maxBlockSize?: number, channels?: number): void;
6024
6030
  reset(): void;
6025
- setConfig(config: RealtimeVoiceChangerConfigInput): void;
6031
+ setConfig(config: RealtimeVoiceChangerConfigInput | RealtimeVoiceChangerPodConfig): void;
6026
6032
  configJson(): string;
6027
6033
  latencySamples(): number;
6028
6034
  processMono(samples: Float32Array): Float32Array;
package/dist/index.js CHANGED
@@ -244,12 +244,16 @@ function percussive(samples, sampleRate = 22050, options = {}) {
244
244
  function timeStretch(samples, sampleRate, rate, options = {}) {
245
245
  const request = samples instanceof Float32Array ? { samples, sampleRate, rate, ...options } : samples;
246
246
  assertSamples("timeStretch", request.samples, request.validate !== false);
247
- return requireModule().timeStretch(request.samples, request.sampleRate, request.rate);
247
+ return requireModule().timeStretch(request.samples, request.sampleRate ?? 22050, request.rate);
248
248
  }
249
249
  function pitchShift(samples, sampleRate, semitones, options = {}) {
250
250
  const request = samples instanceof Float32Array ? { samples, sampleRate, semitones, ...options } : samples;
251
251
  assertSamples("pitchShift", request.samples, request.validate !== false);
252
- return requireModule().pitchShift(request.samples, request.sampleRate, request.semitones);
252
+ return requireModule().pitchShift(
253
+ request.samples,
254
+ request.sampleRate ?? 22050,
255
+ request.semitones
256
+ );
253
257
  }
254
258
  function pitchCorrectToMidi(samples, sampleRate = 22050, currentMidi = 69, targetMidi = 69, options = {}) {
255
259
  const request = samples instanceof Float32Array ? { samples, sampleRate, currentMidi, targetMidi, ...options } : samples;
@@ -336,7 +340,11 @@ function noteMove(samples, sampleRate = 22050, options = {}) {
336
340
  function normalize(samples, sampleRate, targetDb = 0, options = {}) {
337
341
  const request = samples instanceof Float32Array ? { samples, sampleRate, targetDb, ...options } : samples;
338
342
  assertSamples("normalize", request.samples, request.validate !== false);
339
- return requireModule().normalize(request.samples, request.sampleRate, request.targetDb ?? 0);
343
+ return requireModule().normalize(
344
+ request.samples,
345
+ request.sampleRate ?? 22050,
346
+ request.targetDb ?? 0
347
+ );
340
348
  }
341
349
  function spectralEdit(samples, sampleRate, ops = [], options = {}) {
342
350
  const request = samples instanceof Float32Array ? { samples, sampleRate, ops, ...options } : samples;
@@ -1257,7 +1265,11 @@ function analyzeMelody(samples, sampleRate = 22050, options = {}) {
1257
1265
  const fmax = options.fmax ?? 2093;
1258
1266
  validateFrequencyBounds("analyzeMelody", fmin, fmax);
1259
1267
  if (fmin <= 0) {
1260
- throw new RangeError("analyzeMelody: fmin must be positive");
1268
+ throw new SonareError(
1269
+ 4 /* InvalidParameter */,
1270
+ "InvalidParameter",
1271
+ "analyzeMelody: fmin must be positive"
1272
+ );
1261
1273
  }
1262
1274
  validatePositiveIntegers("analyzeMelody", {
1263
1275
  frameLength: options.frameLength ?? 2048,
@@ -1266,7 +1278,11 @@ function analyzeMelody(samples, sampleRate = 22050, options = {}) {
1266
1278
  const threshold = options.threshold ?? 0.1;
1267
1279
  assertFiniteScalar("analyzeMelody", threshold, "threshold");
1268
1280
  if (threshold <= 0) {
1269
- throw new RangeError("analyzeMelody: threshold must be positive");
1281
+ throw new SonareError(
1282
+ 4 /* InvalidParameter */,
1283
+ "InvalidParameter",
1284
+ "analyzeMelody: threshold must be positive"
1285
+ );
1270
1286
  }
1271
1287
  return requireModule9().analyzeMelody(
1272
1288
  samples,
@@ -3555,6 +3571,17 @@ var Project = class _Project {
3555
3571
  redo() {
3556
3572
  this.native.redo();
3557
3573
  }
3574
+ /** Clear the undo/redo history without changing the current project state. */
3575
+ clearHistory() {
3576
+ this.native.clearHistory();
3577
+ }
3578
+ /** Cap the undo history depth (clamped to >= 1); evicts oldest entries beyond the cap. */
3579
+ setMaxUndoDepth(depth) {
3580
+ if (!Number.isInteger(depth) || depth < 1) {
3581
+ throw new RangeError("Project.setMaxUndoDepth: depth must be an integer >= 1");
3582
+ }
3583
+ this.native.setMaxUndoDepth(depth);
3584
+ }
3558
3585
  /** Replace a MIDI clip's entire event list. */
3559
3586
  setMidiEvents(clipId, events) {
3560
3587
  assertProjectMidiEvents("Project.setMidiEvents", events);
@@ -4021,6 +4048,8 @@ function panLawCode(panLaw) {
4021
4048
  return panLaw;
4022
4049
  }
4023
4050
  switch (panLaw) {
4051
+ case "const3dB":
4052
+ return 0;
4024
4053
  case "const4.5dB":
4025
4054
  return 1;
4026
4055
  case "const6dB":
@@ -4028,7 +4057,7 @@ function panLawCode(panLaw) {
4028
4057
  case "linear0dB":
4029
4058
  return 3;
4030
4059
  default:
4031
- return 0;
4060
+ throw new Error(`Invalid pan law: ${panLaw}`);
4032
4061
  }
4033
4062
  }
4034
4063
  function panModeCode(panMode) {
@@ -4036,6 +4065,8 @@ function panModeCode(panMode) {
4036
4065
  return panMode;
4037
4066
  }
4038
4067
  switch (panMode) {
4068
+ case "balance":
4069
+ return 0;
4039
4070
  case "stereoPan":
4040
4071
  case "stereo-pan":
4041
4072
  return 1;
@@ -4043,17 +4074,34 @@ function panModeCode(panMode) {
4043
4074
  case "dual-pan":
4044
4075
  return 2;
4045
4076
  default:
4046
- return 0;
4077
+ throw new Error(`Invalid pan mode: ${panMode}`);
4047
4078
  }
4048
4079
  }
4049
4080
  function meterTapCode(tap) {
4050
- return tap === "preFader" || tap === 0 ? 0 : 1;
4081
+ if (typeof tap === "number") {
4082
+ return tap;
4083
+ }
4084
+ switch (tap) {
4085
+ case "preFader":
4086
+ return 0;
4087
+ case "postFader":
4088
+ return 1;
4089
+ default:
4090
+ throw new Error(`Invalid meter tap: ${tap}`);
4091
+ }
4051
4092
  }
4052
4093
  function sendTimingCode(timing) {
4053
4094
  if (typeof timing === "number") {
4054
4095
  return timing;
4055
4096
  }
4056
- return timing === "preFader" ? 1 : 0;
4097
+ switch (timing) {
4098
+ case "postFader":
4099
+ return 0;
4100
+ case "preFader":
4101
+ return 1;
4102
+ default:
4103
+ throw new Error(`Invalid send timing: ${timing}`);
4104
+ }
4057
4105
  }
4058
4106
 
4059
4107
  // src/realtime_engine.ts
@@ -5330,6 +5378,61 @@ var Mixer = class _Mixer {
5330
5378
  };
5331
5379
 
5332
5380
  // src/realtime_voice_changer.ts
5381
+ function isFlatVoiceChangerPod(config) {
5382
+ return typeof config === "object" && config !== null && "retuneSemitones" in config;
5383
+ }
5384
+ function flatVoiceChangerPodToNested(pod) {
5385
+ return {
5386
+ inputGainDb: pod.inputGainDb,
5387
+ outputGainDb: pod.outputGainDb,
5388
+ wetMix: pod.wetMix,
5389
+ retune: { semitones: pod.retuneSemitones, mix: pod.retuneMix, grainSize: pod.retuneGrainSize },
5390
+ formant: {
5391
+ factor: pod.formantFactor,
5392
+ amount: pod.formantAmount,
5393
+ body: pod.formantBody,
5394
+ brightness: pod.formantBrightness,
5395
+ nasal: pod.formantNasal
5396
+ },
5397
+ eq: {
5398
+ highpassHz: pod.eqHighpassHz,
5399
+ bodyDb: pod.eqBodyDb,
5400
+ presenceDb: pod.eqPresenceDb,
5401
+ airDb: pod.eqAirDb
5402
+ },
5403
+ gate: {
5404
+ thresholdDb: pod.gateThresholdDb,
5405
+ attackMs: pod.gateAttackMs,
5406
+ releaseMs: pod.gateReleaseMs,
5407
+ rangeDb: pod.gateRangeDb
5408
+ },
5409
+ compressor: {
5410
+ thresholdDb: pod.compressorThresholdDb,
5411
+ ratio: pod.compressorRatio,
5412
+ attackMs: pod.compressorAttackMs,
5413
+ releaseMs: pod.compressorReleaseMs,
5414
+ makeupGainDb: pod.compressorMakeupGainDb
5415
+ },
5416
+ deesser: {
5417
+ frequencyHz: pod.deesserFrequencyHz,
5418
+ thresholdDb: pod.deesserThresholdDb,
5419
+ ratio: pod.deesserRatio,
5420
+ rangeDb: pod.deesserRangeDb
5421
+ },
5422
+ reverb: {
5423
+ mix: pod.reverbMix,
5424
+ timeMs: pod.reverbTimeMs,
5425
+ damping: pod.reverbDamping,
5426
+ seed: pod.reverbSeed
5427
+ },
5428
+ limiter: {
5429
+ ceilingDb: pod.limiterCeilingDb,
5430
+ releaseMs: pod.limiterReleaseMs,
5431
+ enableIspLimiter: pod.limiterEnableIspLimiter,
5432
+ ispCeilingDbtp: pod.limiterIspCeilingDbtp
5433
+ }
5434
+ };
5435
+ }
5333
5436
  var RealtimeVoiceChanger = class {
5334
5437
  constructor(config = "neutral-monitor") {
5335
5438
  const module2 = getSonareModule();
@@ -5342,7 +5445,8 @@ var RealtimeVoiceChanger = class {
5342
5445
  this.changer.reset();
5343
5446
  }
5344
5447
  setConfig(config) {
5345
- this.changer.setConfig(config);
5448
+ const resolved = isFlatVoiceChangerPod(config) ? flatVoiceChangerPodToNested(config) : config;
5449
+ this.changer.setConfig(resolved);
5346
5450
  }
5347
5451
  configJson() {
5348
5452
  return this.changer.configJson();