@libraz/libsonare 1.3.0 → 1.3.2

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 CHANGED
@@ -21,6 +21,11 @@ no model weights.
21
21
  > If you need to read WAV/MP3/M4A files directly in Node, use the native
22
22
  > N-API package [`@libraz/libsonare-native`](https://github.com/libraz/libsonare/tree/main/bindings/node) instead.
23
23
 
24
+ > **Platform constraints:** the WebAssembly build is single-threaded (analysis
25
+ > runs to completion on the calling thread — there is no non-blocking variant),
26
+ > has no host filesystem access, and expects pre-decoded `Float32Array` sample
27
+ > buffers. Drive long-running calls from a Web Worker to keep the UI responsive.
28
+
24
29
  ## Installation
25
30
 
26
31
  ```bash
@@ -124,6 +129,54 @@ const room = analyzeImpulseResponse(irSamples, sampleRate);
124
129
  console.log(blind.rt60, room.c50);
125
130
  ```
126
131
 
132
+ Acoustic simulation adds `synthesizeRir` (synthesize a shoebox-room impulse
133
+ response from geometry), `estimateRoom` (recover an equivalent room from a
134
+ recording or IR), and `roomMorph` (creatively re-reverberate audio toward a
135
+ target room — not dereverberation).
136
+
137
+ ```typescript
138
+ import { init, synthesizeRir, estimateRoom, roomMorph } from '@libraz/libsonare';
139
+
140
+ await init();
141
+
142
+ const { rir, hasError } = synthesizeRir({
143
+ lengthM: 6,
144
+ widthM: 4,
145
+ heightM: 3,
146
+ absorption: 0.2,
147
+ sampleRate: 48000,
148
+ });
149
+
150
+ const room = estimateRoom(samples, 48000); // { volume, length, width, height, ... }
151
+ const morphed = roomMorph(samples, sampleRate, { lengthM: 12, widthM: 9, wet: 0.5 });
152
+ ```
153
+
154
+ ### Error handling
155
+
156
+ Native (C++) failures are thrown as a `SonareError` carrying a numeric `code`
157
+ (an `ErrorCode` value) and its canonical `codeName`, so you can branch on the
158
+ cause instead of matching message text. Use the `isSonareError` type guard in a
159
+ `catch`:
160
+
161
+ ```typescript
162
+ import { init, analyze, ErrorCode, isSonareError } from '@libraz/libsonare';
163
+
164
+ await init();
165
+
166
+ try {
167
+ const result = analyze(samples, sampleRate);
168
+ } catch (error) {
169
+ if (isSonareError(error)) {
170
+ console.error(`${error.codeName} (${error.code}): ${error.message}`);
171
+ if (error.code === ErrorCode.InvalidParameter) {
172
+ // recover...
173
+ }
174
+ } else {
175
+ throw error;
176
+ }
177
+ }
178
+ ```
179
+
127
180
  ### Decoding files in the browser
128
181
 
129
182
  ```typescript
@@ -331,6 +384,149 @@ try {
331
384
  }
332
385
  ```
333
386
 
387
+ #### Clip warp
388
+
389
+ A clip can be time-warped during an offline `bounce`. `setClipWarpMode` selects
390
+ the playback mode (`ProjectWarpMode`: `'off'` | `'repitch'` | `'tempo-sync'`),
391
+ `setClipWarpRef` binds it to a warp map, and `setWarpMap` registers a first-class
392
+ warp map (anchors mapping warp-timeline samples to source samples).
393
+
394
+ ```typescript
395
+ project.setWarpMap({
396
+ id: 1,
397
+ name: 'main',
398
+ anchors: [
399
+ { warpSample: 0, sourceSample: 0 },
400
+ { warpSample: 48000, sourceSample: 24000 },
401
+ ],
402
+ });
403
+ project.setClipWarpRef(clipId, 1);
404
+ project.setClipWarpMode(clipId, 'tempo-sync');
405
+ ```
406
+
407
+ > Warp is an offline `Project.bounce` feature only. Realtime warp playback is
408
+ > **not** available in `RealtimeEngine`.
409
+
410
+ ### Instruments and synthesis
411
+
412
+ MIDI tracks bounce silently unless an instrument is bound. `Project` offers
413
+ three instrument backends, each as a `bounceWith…` variant that takes a binding
414
+ (or array of bindings) plus the usual `ProjectBounceOptions`:
415
+
416
+ - `bounceWithBuiltinInstrument(binding?, options?)` — simple built-in oscillator
417
+ synth (`BuiltinSynthConfig` / `BuiltinSynthBinding`: waveform + ADSR + gain).
418
+ - `bounceWithSynthInstrument(patchOrName?, options?)` — patch-driven NativeSynth
419
+ (`SynthPatch`, or a preset-name string like `'saw-lead'`).
420
+ - `bounceWithSf2Instrument(config?, options?)` — GS-compatible SoundFont player
421
+ (`Sf2InstrumentConfig`), fed by `loadSoundFont()`.
422
+
423
+ Discover NativeSynth presets with `synthPresetNames()` and fetch one as an
424
+ editable patch with `synthPresetPatch(name)`.
425
+
426
+ ```typescript
427
+ import { init, Project, synthPresetNames, synthPresetPatch } from '@libraz/libsonare';
428
+
429
+ await init();
430
+
431
+ const project = new Project();
432
+ try {
433
+ const { clipId } = project.addMidiClip(0, 4);
434
+ project.setMidiEvents(clipId, [
435
+ Project.midiNoteOn(0, 0, 0, 60, 100),
436
+ Project.midiNoteOff(1, 0, 0, 60),
437
+ ]);
438
+
439
+ // Built-in oscillator synth.
440
+ const a = project.bounceWithBuiltinInstrument({ waveform: 'saw' }, { numChannels: 2 });
441
+
442
+ // NativeSynth from a named preset, tweaked.
443
+ const patch = synthPresetPatch(synthPresetNames()[0]);
444
+ patch.cutoffHz = 4000;
445
+ const b = project.bounceWithSynthInstrument(patch, { numChannels: 2 });
446
+
447
+ // SoundFont player (requires loadSoundFont first).
448
+ project.loadSoundFont(sf2Bytes);
449
+ const c = project.bounceWithSf2Instrument({ gain: 0.6 }, { numChannels: 2 });
450
+ } finally {
451
+ project.delete();
452
+ }
453
+ ```
454
+
455
+ ### Real-time engine
456
+
457
+ `RealtimeEngine` is a control-thread-driven transport + render engine: it plays
458
+ a clip/automation timeline, hosts MIDI instruments, accepts live MIDI, and
459
+ renders blocks (or bounces offline). Bind it to an AudioWorklet for browser
460
+ playback — see the [AudioWorklet bridge](#audioworklet-bridge) below; the engine
461
+ is the offline/headless half, the worklet is the audio-thread half.
462
+
463
+ ```typescript
464
+ import { init, RealtimeEngine } from '@libraz/libsonare';
465
+
466
+ await init();
467
+
468
+ const engine = new RealtimeEngine(48000, 128); // sampleRate, maxBlockSize
469
+ try {
470
+ engine.setSynthInstrument('saw-lead', 0); // patch (or name), destinationId
471
+ engine.play();
472
+ engine.pushMidiNoteOn(0, 0, 0, 60, 100); // destination, group, channel, note, velocity
473
+ engine.pushMidiNoteOff(0, 0, 0, 60);
474
+
475
+ const blockL = new Float32Array(128);
476
+ const blockR = new Float32Array(128);
477
+ const out = engine.process([blockL, blockR]); // Float32Array[] per channel
478
+ const telemetry = engine.drainTelemetry();
479
+ } finally {
480
+ engine.destroy();
481
+ }
482
+ ```
483
+
484
+ Capabilities:
485
+
486
+ - **Transport**: `play` / `stop` / `seekSample` / `seekPpq` / `setTempo` /
487
+ `setTimeSignature` / `setLoop`, plus `getTransportState`.
488
+ - **Instruments**: `setBuiltinInstrument` / `setSynthInstrument` /
489
+ `setSf2Instrument` (+ `loadSoundFont`) per MIDI destination id.
490
+ - **Live MIDI**: `pushMidiNoteOn` / `pushMidiNoteOff` / `pushMidiCc` /
491
+ `pushMidiPanic`, and `bindMidiCc(channel, controller, paramId, options?)` to
492
+ map a CC to an automation parameter.
493
+ - **Process / bounce**: `process` (real-time blocks), the allocation-free
494
+ `prepareChannels` + `getChannelBuffer` + `processPrepared` worklet path,
495
+ `renderOffline`, `bounceOffline`, `freezeOffline`.
496
+ - **Clip page providers**: `createClipPageProvider` + `supplyClipPage` for
497
+ streaming large clip audio in pages; pair with the OPFS helpers
498
+ (`createOpfsClipPageProvider`).
499
+ - **Telemetry**: `drainTelemetry` / `drainMeterTelemetry`. Inspect runtime
500
+ capabilities (ABI compatibility, SharedArrayBuffer/Atomics) via
501
+ `engineCapabilities()`.
502
+
503
+ ### Real-time voice changer
504
+
505
+ `RealtimeVoiceChanger` runs a block-by-block voice transformation chain (retune,
506
+ formant shaping, EQ, gate, compressor). Construct it from a preset id (see
507
+ `realtimeVoiceChangerPresetNames()`) or a full config object, then process blocks.
508
+
509
+ ```typescript
510
+ import { init, RealtimeVoiceChanger, voiceChangeRealtime } from '@libraz/libsonare';
511
+
512
+ await init();
513
+
514
+ const changer = new RealtimeVoiceChanger('bright-idol');
515
+ try {
516
+ changer.prepare(48000, 128, 1); // sampleRate, maxBlockSize, channels
517
+ const out = changer.processMono(block);
518
+ } finally {
519
+ changer.delete();
520
+ }
521
+
522
+ // Whole-buffer convenience wrapper (constructs/prepares/disposes internally).
523
+ const processed = voiceChangeRealtime(samples, { preset: 'deep-narrator', sampleRate: 48000 });
524
+ ```
525
+
526
+ For a simple offline pitch + formant shift without the full chain, use
527
+ `voiceChange(samples, sampleRate, { pitchSemitones: -2, formantFactor: 1.1 })`.
528
+ Inspect a preset with `realtimeVoiceChangerPresetJson(name)`.
529
+
334
530
  ### AudioWorklet bridge
335
531
 
336
532
  The package exposes an optional worklet entry that uses the same `sonare.wasm`
@@ -476,17 +672,50 @@ chain.reset();
476
672
  chain.delete(); // release WASM memory
477
673
  ```
478
674
 
675
+ ### Streaming equalizer and retune
676
+
677
+ `StreamingEqualizer` wraps the unified `EqualizerProcessor` (up to 24 bands,
678
+ RBJ/Vicanek biquads, dynamic EQ, linear-phase FIR, mid/side, auto-gain) with
679
+ state maintained across calls. `StreamingRetune` is a block-by-block mono voice
680
+ retune / pitch shifter.
681
+
682
+ ```typescript
683
+ import { init, StreamingEqualizer, StreamingRetune } from '@libraz/libsonare';
684
+
685
+ await init();
686
+
687
+ const eq = new StreamingEqualizer({ sampleRate: 48000, maxBlockSize: 512 });
688
+ try {
689
+ eq.setBand(0, { type: 'HighShelf', frequencyHz: 8000, gainDb: 6, enabled: true });
690
+ const { left: eqL, right: eqR } = eq.processStereo(left, right);
691
+ } finally {
692
+ eq.delete();
693
+ }
694
+
695
+ const retune = new StreamingRetune({ semitones: 2, mix: 1.0 });
696
+ try {
697
+ retune.prepare(48000, 512); // sampleRate, maxBlockSize
698
+ const shifted = retune.processMono(monoBlock);
699
+ } finally {
700
+ retune.delete();
701
+ }
702
+ ```
703
+
479
704
  ## Features
480
705
 
481
- - **Detection**: BPM, key, beats, onsets, chords, sections
706
+ - **Detection**: `detectBeats`, `detectOnsets`, `detectDownbeats`, `detectChords`, `detectKey`, `detectKeyCandidates`, `chordFunctionalAnalysis`, sections
707
+ - **Analysis**: `analyze`, `analyzeWithProgress`, `analyzeBpm`, `analyzeRhythm`, `analyzeDynamics`, `analyzeTimbre`; `hasFfmpegSupport` capability check
482
708
  - **Effects**: HPSS, HPSS with residual, time stretch, phase vocoder, pitch shift, normalize, trim, remix
483
709
  - **Mastering**: EQ, compressor, tape/exciter, air band, stereo imaging,
484
710
  true-peak limiting, loudness optimization
485
711
  - **Features**: STFT, mel spectrogram, MFCC, chroma, CQT/VQT, spectral contrast, poly features, zero crossings
486
712
  - **Pitch**: YIN, pYIN algorithms with optional `fillNa`
487
713
  - **Decomposition & loudness**: NMF decomposition, nearest-neighbour filtering, multichannel LUFS, EBU R128 LRA
488
- - **Streaming**: Real-time analysis with progressive estimates
489
- - **Headless DAW**: `Project` arrangement model — audio/MIDI tracks & clips, undo/redo, MIDI sequencing, SMF / MIDI 2.0 Clip File I/O, deterministic JSON, offline `bounce`
714
+ - **Streaming**: Real-time analysis with progressive estimates; streaming mastering chain, equalizer, and retune
715
+ - **Instruments**: built-in synth, patch-driven NativeSynth, SoundFont (SF2) player bound to `Project` bounces or the `RealtimeEngine`
716
+ - **Real-time**: `RealtimeEngine` transport/MIDI/render, `RealtimeVoiceChanger`, AudioWorklet bridge
717
+ - **Room acoustics**: blind RT60/EDT, impulse-response clarity metrics, RIR synthesis, room estimation, room morphing
718
+ - **Headless DAW**: `Project` arrangement model — audio/MIDI tracks & clips, undo/redo, MIDI sequencing, clip warp, SMF / MIDI 2.0 Clip File I/O, deterministic JSON, offline `bounce`
490
719
  - **Conversions**: Hz/mel/MIDI/note, frames/time, resample
491
720
 
492
721
  ## Also available
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { A as AcousticOptions, a as AcousticResult, b as AnalysisResult, c as AnalyzeBpmOptions, d as AnalyzeDynamicsOptions, e as AnalyzeRhythmOptions, f as AnalyzeSectionsOptions, g as AnalyzeTimbreOptions, h as AnalyzerStats, i as Audio, j as AutomationCurve, B as BarChord, k as Beat, l as BindMicrophoneInputOptions, m as BindWebMidiOptions, n as BpmAnalysisResult, o as BpmCandidate, p as BrowserAudioDecodeOptions, q as BuiltinSynthBinding, r as BuiltinSynthConfig, s as BuiltinSynthWaveform, C as Chord, t as ChordAnalysisResult, u as ChordChange, v as ChordDetectionOptions, w as ChordQuality, x as ChromaResult, y as ClippingRegion, z as ClippingReport, D as CompressorDetector, E as CompressorOptions, F as CqtResult, G as DeclickOptions, H as DeclipOptions, I as DecomposeResult, J as DecrackleMode, K as DecrackleOptions, L as DehumOptions, M as DenoiseClassicalMode, N as DenoiseClassicalNoiseEstimator, O as DenoiseClassicalOptions, P as DereverbClassicalOptions, Q as DynamicRangeReport, R as Dynamics, S as DynamicsAnalysisResult, T as DynamicsResult, U as EXPECTED_ENGINE_ABI_VERSION, V as EXPECTED_PROJECT_ABI_VERSION, W as EngineAutomationPoint, X as EngineBounceOptions, Y as EngineBounceResult, Z as EngineCapabilities, _ as EngineCaptureStatus, $ as EngineClip, a0 as EngineFreezeOptions, a1 as EngineFreezeResult, a2 as EngineGraphSpec, a3 as EngineMarker, a4 as EngineMeterTelemetry, a5 as EngineMetronomeConfig, a6 as EngineParameterInfo, a7 as EngineTelemetry, a8 as EngineTransportState, a9 as EqBand, aa as EqBandPhase, ab as EqBandType, ac as EqCoeffMode, ad as EqMatchOptions, ae as EqSpectrumSnapshot, af as EqStereoPlacement, ag as FrameBuffer, ah as GateOptions, ai as GoniometerPoint, aj as HpssResult, ak as HpssWithResidualResult, al as Key, am as KeyCandidate, an as KeyDetectionOptions, ao as KeyProfile, ap as KeyProfileName, aq as LufsResult, ar as MasteringChainConfig, as as MasteringChainResult, at as MasteringOptions, au as MasteringPreset, av as MasteringProcessorParams, aw as MasteringResult, ax as MasteringStereoChainResult, ay as MasteringStereoResult, az as Matrix2dResult, aA as MelPowerResult, aB as MelSpectrogramResult, aC as MelodyOptions, aD as MelodyPoint, aE as MelodyResult, aF as MeterTap, aG as MeteringDetectClippingOptions, aH as MeteringDynamicRangeOptions, aI as MfccResult, aJ as MicrophoneInputBinding, aK as MidiCcBindOptions, aL as MidiCcLearnOptions, aM as MixMeterSnapshot, aN as MixOptions, aO as MixResult, aP as Mixer, aQ as MixerProcessResult, aR as MixerRealtimeBuffer, aS as Mode, aT as NoteStretchOptions, aU as OpfsClipPageProviderBinding, aV as OpfsClipPageProviderOptions, aW as PairAnalysis, aX as PairProcessor, aY as PanLaw, aZ as PanMode, a_ as PatternScore, a$ as PhaseScopeReport, b0 as Pitch, b0 as PitchClass, b1 as PitchResult, b2 as ProgressiveEstimate, b3 as Project, b4 as ProjectAssistSidecar, b5 as ProjectAutomationCurve, b6 as ProjectAutomationLaneDesc, b7 as ProjectAutomationPoint, b8 as ProjectBounceOptions, b9 as ProjectChordSymbol, ba as ProjectClipCompSegment, bb as ProjectClipDesc, bc as ProjectClipFade, bd as ProjectClipTake, be as ProjectCompileResult, bf as ProjectFadeCurve, bg as ProjectKeySegment, bh as ProjectLoopMode, bi as ProjectLoopRecordingDesc, bj as ProjectLoopRecordingResult, bk as ProjectMidiClipResult, bl as ProjectMidiEvent, bm as ProjectNotePairValidation, bn as ProjectTrackDesc, bo as ProjectTrackKind, bp as ProjectWarpAnchor, bq as ProjectWarpMapDesc, br as RealtimeEngine, bs as RealtimeVoiceChanger, bt as RealtimeVoiceChangerConfigInput, bu as RealtimeVoiceChangerInterleavedBuffer, bv as RealtimeVoiceChangerMonoBuffer, bw as RealtimeVoiceChangerPlanarBuffer, bx as RealtimeVoiceChangerPodConfig, by as RhythmAnalysisResult, bz as RhythmFeatures, bA as RirResult, bB as RirSynthOptions, bC as RoomEstimateOptions, bD as RoomEstimateResult, bE as RoomGeometryOptions, bF as RoomMorphOptions, bG as SYNTH_BODY_TYPES, bH as SYNTH_ENGINE_MODES, bI as SYNTH_FILTER_MODELS, bJ as SYNTH_FILTER_OUTPUTS, bK as SYNTH_MOD_DESTINATIONS, bL as SYNTH_MOD_SOURCES, bM as SYNTH_OSC_WAVEFORMS, bN as Section, bO as SectionType, bP as SendTiming, bQ as Sf2InstrumentConfig, bR as Sf2ProgramStatus, bS as SoloProcessor, bT as SourceBackend, bU as SpectrumOptions, bV as SpectrumReport, bW as StereoAnalysis, bX as StftPowerResult, bY as StftResult, bZ as StreamAnalyzer, b_ as StreamConfig, b$ as StreamConfigDefaults, c0 as StreamFramesI16, c1 as StreamFramesU8, c2 as StreamQuantizeConfig, c3 as StreamingEqualizer, c4 as StreamingEqualizerConfig, c5 as StreamingMasteringChain, c6 as StreamingMasteringChainConfig, c7 as StreamingPlatform, c8 as StreamingRetune, c9 as StreamingRetuneConfig, ca as SynthBodyType, cb as SynthEngineMode, cc as SynthEnumTables, cd as SynthFilterModel, ce as SynthFilterOutput, cf as SynthModDestination, cg as SynthModRouting, ch as SynthModSource, ci as SynthOscWaveform, cj as SynthPatch, ck as TempogramMode, cl as Timbre, cm as TimbreAnalysisResult, cn as TimbreFrame, co as TimeSignature, cp as TransientShaperOptions, cq as TrimSilenceMode, cr as TrimSilenceOptions, cs as ValidateOptions, ct as VectorscopeReport, cu as VoiceChangeOptions, cv as VoiceChangeRealtimeOptions, cw as VoicePresetId, cx as WaveformPeakPyramidOptions, cy as WaveformPeaksOptions, cz as WaveformPeaksReport, cA as WebMidiBinding, cB as WebMidiCcBinding, cC as WebMidiInputInfo, cD as amplitudeToDb, cE as analyze, cF as analyzeBpm, cG as analyzeDynamics, cH as analyzeImpulseResponse, cI as analyzeMelody, cJ as analyzeRhythm, cK as analyzeSections, cL as analyzeTimbre, cM as analyzeWithProgress, cN as bassChroma, cO as bindMicrophoneInput, cP as bindWebMidi, cQ as chordFunctionalAnalysis, cR as chroma, cS as chromaCens, cT as cqt, cU as createOpfsClipPageProvider, cV as createOpfsClipPageWorker, cW as cyclicTempogram, cX as dbToAmplitude, cY as dbToPower, cZ as decompose, c_ as decomposeWithInit, c$ as deemphasis, d0 as detectAcoustic, d1 as detectBeats, d2 as detectBpm, d3 as detectChords, d4 as detectDownbeats, d5 as detectKey, d6 as detectKeyCandidates, d7 as detectOnsets, d8 as ebur128LoudnessRange, d9 as engineAbiVersion, da as engineCapabilities, db as estimateRoom, dc as estimateTuning, dd as fixFrames, de as fixLength, df as fourierTempogram, dg as frameSignal, dh as framesToSamples, di as framesToTime, dj as harmonic, dk as hasFfmpegSupport, dl as hpss, dm as hpssWithResidual, dn as hybridCqt, dp as hzToMel, dq as hzToMidi, dr as hzToNote, init, isInitialized, ds as isWebMidiAvailable, dt as lufs, du as lufsInterleaved, dv as masterAudio, dw as masterAudioStereo, dx as masterAudioStereoWithProgress, dy as masterAudioWithProgress, dz as mastering, dA as masteringAssistantSuggest, dB as masteringAudioProfile, dC as masteringChain, dD as masteringChainStereo, dE as masteringChainStereoWithProgress, dF as masteringChainWithProgress, dG as masteringDynamicsCompressor, dH as masteringDynamicsGate, dI as masteringDynamicsTransientShaper, dJ as masteringInsertNames, dK as masteringPairAnalysisNames, dL as masteringPairAnalyze, dM as masteringPairProcess, dN as masteringPairProcessorNames, dO as masteringPresetNames, dP as masteringProcess, dQ as masteringProcessStereo, dR as masteringProcessorNames, dS as masteringRepairDeclick, dT as masteringRepairDeclip, dU as masteringRepairDecrackle, dV as masteringRepairDehum, dW as masteringRepairDenoiseClassical, dX as masteringRepairDereverbClassical, dY as masteringRepairTrimSilence, dZ as masteringStereoAnalysisNames, d_ as masteringStereoAnalyze, d$ as masteringStreamingPreview, e0 as melSpectrogram, e1 as melToAudio, e2 as melToHz, e3 as melToStft, e4 as meteringCrestFactorDb, e5 as meteringDcOffset, e6 as meteringDetectClipping, e7 as meteringDynamicRange, e8 as meteringPeakDb, e9 as meteringPhaseScope, ea as meteringPhaseScopeDecimated, eb as meteringRmsDb, ec as meteringSpectrum, ed as meteringSpectrumFrame, ee as meteringStereoCorrelation, ef as meteringStereoWidth, eg as meteringTruePeakDb, eh as meteringVectorscope, ei as meteringVectorscopeDecimated, ej as mfcc, ek as mfccToAudio, el as mfccToMel, em as midiToHz, en as mixStereo, eo as mixingScenePresetJson, ep as mixingScenePresetNames, eq as momentaryLufs, er as nnFilter, es as nnlsChroma, et as normalize, eu as noteStretch, ev as noteToHz, ew as onsetEnvelope, ex as onsetStrengthMulti, ey as opfsClipPageWorkerSource, ez as padCenter, eA as pcen, eB as peakPick, eC as percussive, eD as phaseVocoder, eE as pitchCorrectToMidi, eF as pitchCorrectToMidiTimevarying, eG as pitchPyin, eH as pitchShift, eI as pitchTuning, eJ as pitchYin, eK as plp, eL as polyFeatures, eM as powerToDb, eN as preemphasis, eO as projectAbiVersion, eP as pseudoCqt, eQ as realtimeVoiceChangerPresetConfig, eR as realtimeVoiceChangerPresetJson, eS as realtimeVoiceChangerPresetNames, eT as remix, eU as resample, eV as rmsEnergy, eW as roomMorph, eX as samplesToFrames, eY as scaleCorrectionSemitones, eZ as scalePitchClassEnabled, e_ as scaleQuantizeMidi, e$ as shortTermLufs, f0 as spectralBandwidth, f1 as spectralCentroid, f2 as spectralContrast, f3 as spectralFlatness, f4 as spectralRolloff, f5 as splitSilence, f6 as stft, f7 as stftDb, f8 as streamAnalyzerConfigDefaults, f9 as synthEnumTables, fa as synthPresetNames, fb as synthPresetPatch, fc as synthesizeRir, fd as tempogram, fe as tempogramRatio, ff as timeStretch, fg as timeToFrames, fh as tonnetz, fi as trim, fj as trimSilence, fk as validateRealtimeVoiceChangerPresetJson, fl as vectorNormalize, fm as version, fn as voiceChange, fo as voiceChangeRealtime, fp as voiceChangerAbiVersion, fq as voiceCharacterPresetId, fr as vqt, fs as waveformPeakPyramid, ft as waveformPeaks, fu as zeroCrossingRate, fv as zeroCrossings } from './worklet.js';
1
+ export { A as AcousticOptions, a as AcousticResult, b as AnalysisResult, c as AnalyzeBpmOptions, d as AnalyzeDynamicsOptions, e as AnalyzeRhythmOptions, f as AnalyzeSectionsOptions, g as AnalyzeTimbreOptions, h as AnalyzerStats, i as Audio, j as AutomationCurve, B as BarChord, k as Beat, l as BindMicrophoneInputOptions, m as BindWebMidiOptions, n as BpmAnalysisResult, o as BpmCandidate, p as BrowserAudioDecodeOptions, q as BuiltinSynthBinding, r as BuiltinSynthConfig, s as BuiltinSynthWaveform, C as Chord, t as ChordAnalysisResult, u as ChordChange, v as ChordDetectionOptions, w as ChordQuality, x as ChromaResult, y as ClippingRegion, z as ClippingReport, D as CompressorDetector, E as CompressorOptions, F as CqtResult, G as DeclickOptions, H as DeclipOptions, I as DecomposeResult, J as DecrackleMode, K as DecrackleOptions, L as DehumOptions, M as DenoiseClassicalMode, N as DenoiseClassicalNoiseEstimator, O as DenoiseClassicalOptions, P as DereverbClassicalOptions, Q as DynamicRangeReport, R as Dynamics, S as DynamicsAnalysisResult, T as DynamicsResult, U as EXPECTED_ENGINE_ABI_VERSION, V as EXPECTED_PROJECT_ABI_VERSION, W as EngineAutomationPoint, X as EngineBounceOptions, Y as EngineBounceResult, Z as EngineCapabilities, _ as EngineCaptureStatus, $ as EngineClip, a0 as EngineFreezeOptions, a1 as EngineFreezeResult, a2 as EngineGraphSpec, a3 as EngineMarker, a4 as EngineMeterTelemetry, a5 as EngineMetronomeConfig, a6 as EngineParameterInfo, a7 as EngineTelemetry, a8 as EngineTransportState, a9 as EqBand, aa as EqBandPhase, ab as EqBandType, ac as EqCoeffMode, ad as EqMatchOptions, ae as EqSpectrumSnapshot, af as EqStereoPlacement, ag as ErrorCode, ah as FrameBuffer, ai as GateOptions, aj as GoniometerPoint, ak as HpssResult, al as HpssWithResidualResult, am as Key, an as KeyCandidate, ao as KeyDetectionOptions, ap as KeyProfile, aq as KeyProfileName, ar as LufsResult, as as MasteringChainConfig, at as MasteringChainResult, au as MasteringOptions, av as MasteringPreset, aw as MasteringProcessorParams, ax as MasteringResult, ay as MasteringStereoChainResult, az as MasteringStereoResult, aA as Matrix2dResult, aB as MelPowerResult, aC as MelSpectrogramResult, aD as MelodyOptions, aE as MelodyPoint, aF as MelodyResult, aG as MeterTap, aH as MeteringDetectClippingOptions, aI as MeteringDynamicRangeOptions, aJ as MfccResult, aK as MicrophoneInputBinding, aL as MidiCcBindOptions, aM as MidiCcLearnOptions, aN as MixMeterSnapshot, aO as MixOptions, aP as MixResult, aQ as Mixer, aR as MixerProcessResult, aS as MixerRealtimeBuffer, aT as Mode, aU as NoteStretchOptions, aV as OpfsClipPageProviderBinding, aW as OpfsClipPageProviderOptions, aX as PairAnalysis, aY as PairProcessor, aZ as PanLaw, a_ as PanMode, a$ as PatternScore, b0 as PhaseScopeReport, b1 as Pitch, b1 as PitchClass, b2 as PitchResult, b3 as ProgressiveEstimate, b4 as Project, b5 as ProjectAssistSidecar, b6 as ProjectAutomationCurve, b7 as ProjectAutomationLaneDesc, b8 as ProjectAutomationPoint, b9 as ProjectBounceOptions, ba as ProjectChordSymbol, bb as ProjectClipCompSegment, bc as ProjectClipDesc, bd as ProjectClipFade, be as ProjectClipTake, bf as ProjectCompileResult, bg as ProjectFadeCurve, bh as ProjectKeySegment, bi as ProjectLoopMode, bj as ProjectLoopRecordingDesc, bk as ProjectLoopRecordingResult, bl as ProjectMidiClipResult, bm as ProjectMidiEvent, bn as ProjectNotePairValidation, bo as ProjectTrackDesc, bp as ProjectTrackKind, bq as ProjectWarpAnchor, br as ProjectWarpMapDesc, bs as RealtimeEngine, bt as RealtimeVoiceChanger, bu as RealtimeVoiceChangerConfigInput, bv as RealtimeVoiceChangerInterleavedBuffer, bw as RealtimeVoiceChangerMonoBuffer, bx as RealtimeVoiceChangerPlanarBuffer, by as RealtimeVoiceChangerPodConfig, bz as RhythmAnalysisResult, bA as RhythmFeatures, bB as RirResult, bC as RirSynthOptions, bD as RoomEstimateOptions, bE as RoomEstimateResult, bF as RoomGeometryOptions, bG as RoomMorphOptions, bH as SYNTH_BODY_TYPES, bI as SYNTH_ENGINE_MODES, bJ as SYNTH_FILTER_MODELS, bK as SYNTH_FILTER_OUTPUTS, bL as SYNTH_MOD_DESTINATIONS, bM as SYNTH_MOD_SOURCES, bN as SYNTH_OSC_WAVEFORMS, bO as Section, bP as SectionType, bQ as SendTiming, bR as Sf2InstrumentConfig, bS as Sf2ProgramStatus, bT as SoloProcessor, bU as SonareError, bV as SourceBackend, bW as SpectrumOptions, bX as SpectrumReport, bY as StereoAnalysis, bZ as StftPowerResult, b_ as StftResult, b$ as StreamAnalyzer, c0 as StreamConfig, c1 as StreamConfigDefaults, c2 as StreamFramesI16, c3 as StreamFramesU8, c4 as StreamQuantizeConfig, c5 as StreamingEqualizer, c6 as StreamingEqualizerConfig, c7 as StreamingMasteringChain, c8 as StreamingMasteringChainConfig, c9 as StreamingPlatform, ca as StreamingRetune, cb as StreamingRetuneConfig, cc as SynthBodyType, cd as SynthEngineMode, ce as SynthEnumTables, cf as SynthFilterModel, cg as SynthFilterOutput, ch as SynthModDestination, ci as SynthModRouting, cj as SynthModSource, ck as SynthOscWaveform, cl as SynthPatch, cm as TempogramMode, cn as Timbre, co as TimbreAnalysisResult, cp as TimbreFrame, cq as TimeSignature, cr as TransientShaperOptions, cs as TrimSilenceMode, ct as TrimSilenceOptions, cu as ValidateOptions, cv as VectorscopeReport, cw as VoiceChangeOptions, cx as VoiceChangeRealtimeOptions, cy as VoicePresetId, cz as WaveformPeakPyramidOptions, cA as WaveformPeaksOptions, cB as WaveformPeaksReport, cC as WebMidiBinding, cD as WebMidiCcBinding, cE as WebMidiInputInfo, cF as amplitudeToDb, cG as analyze, cH as analyzeBpm, cI as analyzeDynamics, cJ as analyzeImpulseResponse, cK as analyzeMelody, cL as analyzeRhythm, cM as analyzeSections, cN as analyzeTimbre, cO as analyzeWithProgress, cP as bassChroma, cQ as bindMicrophoneInput, cR as bindWebMidi, cS as chordFunctionalAnalysis, cT as chroma, cU as chromaCens, cV as cqt, cW as createOpfsClipPageProvider, cX as createOpfsClipPageWorker, cY as cyclicTempogram, cZ as dbToAmplitude, c_ as dbToPower, c$ as decompose, d0 as decomposeWithInit, d1 as deemphasis, d2 as detectAcoustic, d3 as detectBeats, d4 as detectBpm, d5 as detectChords, d6 as detectDownbeats, d7 as detectKey, d8 as detectKeyCandidates, d9 as detectOnsets, da as ebur128LoudnessRange, db as engineAbiVersion, dc as engineCapabilities, dd as estimateRoom, de as estimateTuning, df as fixFrames, dg as fixLength, dh as fourierTempogram, di as frameSignal, dj as framesToSamples, dk as framesToTime, dl as harmonic, dm as hasFfmpegSupport, dn as hpss, dp as hpssWithResidual, dq as hybridCqt, dr as hzToMel, ds as hzToMidi, dt as hzToNote, init, isInitialized, du as isSonareError, dv as isWebMidiAvailable, dw as lufs, dx as lufsInterleaved, dy as masterAudio, dz as masterAudioStereo, dA as masterAudioStereoWithProgress, dB as masterAudioWithProgress, dC as mastering, dD as masteringAssistantSuggest, dE as masteringAudioProfile, dF as masteringChain, dG as masteringChainStereo, dH as masteringChainStereoWithProgress, dI as masteringChainWithProgress, dJ as masteringDynamicsCompressor, dK as masteringDynamicsGate, dL as masteringDynamicsTransientShaper, dM as masteringInsertNames, dN as masteringInsertParamNames, dO as masteringPairAnalysisNames, dP as masteringPairAnalyze, dQ as masteringPairProcess, dR as masteringPairProcessorNames, dS as masteringPresetNames, dT as masteringProcess, dU as masteringProcessStereo, dV as masteringProcessorNames, dW as masteringRepairDeclick, dX as masteringRepairDeclip, dY as masteringRepairDecrackle, dZ as masteringRepairDehum, d_ as masteringRepairDenoiseClassical, d$ as masteringRepairDereverbClassical, e0 as masteringRepairTrimSilence, e1 as masteringStereoAnalysisNames, e2 as masteringStereoAnalyze, e3 as masteringStreamingPreview, e4 as melSpectrogram, e5 as melToAudio, e6 as melToHz, e7 as melToStft, e8 as meteringCrestFactorDb, e9 as meteringDcOffset, ea as meteringDetectClipping, eb as meteringDynamicRange, ec as meteringPeakDb, ed as meteringPhaseScope, ee as meteringPhaseScopeDecimated, ef as meteringRmsDb, eg as meteringSpectrum, eh as meteringSpectrumFrame, ei as meteringStereoCorrelation, ej as meteringStereoWidth, ek as meteringTruePeakDb, el as meteringVectorscope, em as meteringVectorscopeDecimated, en as mfcc, eo as mfccToAudio, ep as mfccToMel, eq as midiToHz, er as mixStereo, es as mixingScenePresetJson, et as mixingScenePresetNames, eu as momentaryLufs, ev as nnFilter, ew as nnlsChroma, ex as normalize, ey as noteStretch, ez as noteToHz, eA as onsetEnvelope, eB as onsetStrengthMulti, eC as opfsClipPageWorkerSource, eD as padCenter, eE as pcen, eF as peakPick, eG as percussive, eH as phaseVocoder, eI as pitchCorrectToMidi, eJ as pitchCorrectToMidiTimevarying, eK as pitchPyin, eL as pitchShift, eM as pitchTuning, eN as pitchYin, eO as plp, eP as polyFeatures, eQ as powerToDb, eR as preemphasis, eS as projectAbiVersion, eT as pseudoCqt, eU as realtimeVoiceChangerPresetConfig, eV as realtimeVoiceChangerPresetJson, eW as realtimeVoiceChangerPresetNames, eX as remix, eY as resample, eZ as rmsEnergy, e_ as roomMorph, e$ as samplesToFrames, f0 as scaleCorrectionSemitones, f1 as scalePitchClassEnabled, f2 as scaleQuantizeMidi, f3 as shortTermLufs, f4 as spectralBandwidth, f5 as spectralCentroid, f6 as spectralContrast, f7 as spectralFlatness, f8 as spectralRolloff, f9 as splitSilence, fa as stft, fb as stftDb, fc as streamAnalyzerConfigDefaults, fd as synthEnumTables, fe as synthPresetNames, ff as synthPresetPatch, fg as synthesizeRir, fh as tempogram, fi as tempogramRatio, fj as timeStretch, fk as timeToFrames, fl as tonnetz, fm as trim, fn as trimSilence, fo as validateRealtimeVoiceChangerPresetJson, fp as vectorNormalize, fq as version, fr as voiceChange, fs as voiceChangeRealtime, ft as voiceChangerAbiVersion, fu as voiceCharacterPresetId, fv as vqt, fw as waveformPeakPyramid, fx as waveformPeaks, fy as zeroCrossingRate, fz as zeroCrossings } from './worklet.js';
2
2
  export { ProgressCallback } from './sonare.js';
package/dist/index.js CHANGED
@@ -1,13 +1,106 @@
1
+ // src/errors.ts
2
+ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
3
+ ErrorCode2[ErrorCode2["Ok"] = 0] = "Ok";
4
+ ErrorCode2[ErrorCode2["FileNotFound"] = 1] = "FileNotFound";
5
+ ErrorCode2[ErrorCode2["InvalidFormat"] = 2] = "InvalidFormat";
6
+ ErrorCode2[ErrorCode2["DecodeFailed"] = 3] = "DecodeFailed";
7
+ ErrorCode2[ErrorCode2["InvalidParameter"] = 4] = "InvalidParameter";
8
+ ErrorCode2[ErrorCode2["OutOfMemory"] = 5] = "OutOfMemory";
9
+ ErrorCode2[ErrorCode2["NotSupported"] = 6] = "NotSupported";
10
+ ErrorCode2[ErrorCode2["InvalidState"] = 7] = "InvalidState";
11
+ ErrorCode2[ErrorCode2["Unknown"] = 99] = "Unknown";
12
+ return ErrorCode2;
13
+ })(ErrorCode || {});
14
+ var SonareError = class extends Error {
15
+ constructor(code, codeName, message) {
16
+ super(message);
17
+ this.name = "SonareError";
18
+ this.code = code;
19
+ this.codeName = codeName;
20
+ }
21
+ };
22
+ function isSonareError(value) {
23
+ return value instanceof Error && value.name === "SonareError" && typeof value.code === "number";
24
+ }
25
+
1
26
  // src/module_state.ts
2
- var wasmModule = null;
27
+ var wrappedModule = null;
28
+ function nativeExceptionPtr(error) {
29
+ if (typeof error === "number") {
30
+ return error;
31
+ }
32
+ if (error !== null && typeof error === "object") {
33
+ const ptr = error.excPtr;
34
+ if (typeof ptr === "number") {
35
+ return ptr;
36
+ }
37
+ }
38
+ return null;
39
+ }
40
+ function makeSonareError(raw, thrown) {
41
+ let code = 99 /* Unknown */;
42
+ let codeName = "Unknown";
43
+ let message = `libsonare native exception (${thrown})`;
44
+ try {
45
+ const info = raw.sonareExceptionInfo?.(thrown);
46
+ if (info) {
47
+ code = info.code ?? code;
48
+ codeName = info.codeName ?? codeName;
49
+ message = info.message || message;
50
+ }
51
+ } catch {
52
+ }
53
+ return new SonareError(code, codeName, message);
54
+ }
55
+ function wrapModuleErrors(raw) {
56
+ const cache = /* @__PURE__ */ new Map();
57
+ const convert = (error) => {
58
+ const ptr = nativeExceptionPtr(error);
59
+ if (ptr !== null) {
60
+ throw makeSonareError(raw, ptr);
61
+ }
62
+ throw error;
63
+ };
64
+ return new Proxy(raw, {
65
+ get(target, prop, receiver) {
66
+ const value = Reflect.get(target, prop, receiver);
67
+ if (typeof value !== "function") {
68
+ return value;
69
+ }
70
+ const cached = cache.get(prop);
71
+ if (cached) {
72
+ return cached;
73
+ }
74
+ const fn = value;
75
+ const wrapped = new Proxy(fn, {
76
+ apply(t, thisArg, args) {
77
+ try {
78
+ return Reflect.apply(t, thisArg, args);
79
+ } catch (error) {
80
+ return convert(error);
81
+ }
82
+ },
83
+ construct(t, args, newTarget) {
84
+ try {
85
+ return Reflect.construct(t, args, newTarget);
86
+ } catch (error) {
87
+ return convert(error);
88
+ }
89
+ }
90
+ });
91
+ cache.set(prop, wrapped);
92
+ return wrapped;
93
+ }
94
+ });
95
+ }
3
96
  function setSonareModule(module2) {
4
- wasmModule = module2;
97
+ wrappedModule = wrapModuleErrors(module2);
5
98
  }
6
99
  function getSonareModule() {
7
- if (!wasmModule) {
100
+ if (!wrappedModule) {
8
101
  throw new Error("Module not initialized. Call init() first.");
9
102
  }
10
- return wasmModule;
103
+ return wrappedModule;
11
104
  }
12
105
 
13
106
  // src/codes.ts
@@ -82,6 +175,16 @@ var Mixer = class _Mixer {
82
175
  compile() {
83
176
  this.mixer.compile();
84
177
  }
178
+ /**
179
+ * Non-fatal warnings captured when this mixer was built from scene JSON: one
180
+ * entry per channel-strip insert that was handed param keys it does not read
181
+ * (a likely typo, or a key meant for a different processor). The scene still
182
+ * loaded; these keys simply took no effect. Empty when every key was consumed.
183
+ * Use {@link masteringInsertParamNames} to discover the keys an insert accepts.
184
+ */
185
+ sceneWarnings() {
186
+ return this.mixer.sceneWarnings();
187
+ }
85
188
  /**
86
189
  * Mix one block of per-strip stereo audio into the stereo master.
87
190
  *
@@ -1019,6 +1122,9 @@ function masteringProcessorNames() {
1019
1122
  function masteringInsertNames() {
1020
1123
  return requireModule().masteringInsertNames();
1021
1124
  }
1125
+ function masteringInsertParamNames(name) {
1126
+ return requireModule().masteringInsertParamNames(name);
1127
+ }
1022
1128
  function masteringPairProcessorNames() {
1023
1129
  return requireModule().masteringPairProcessorNames();
1024
1130
  }
@@ -2354,9 +2460,11 @@ var Audio = class _Audio {
2354
2460
 
2355
2461
  // src/live_audio.ts
2356
2462
  async function bindMicrophoneInput(context, engine, options = {}) {
2357
- const stream = options.stream ?? await navigator.mediaDevices.getUserMedia({
2358
- audio: options.audio ?? true,
2359
- video: false
2463
+ const { stream: providedStream, stopTracksOnClose = true, ...constraints } = options;
2464
+ const stream = providedStream ?? await navigator.mediaDevices.getUserMedia({
2465
+ ...constraints,
2466
+ audio: constraints.audio ?? true,
2467
+ video: constraints.video ?? false
2360
2468
  });
2361
2469
  const source = context.createMediaStreamSource(stream);
2362
2470
  const node = "node" in engine ? engine.node : engine;
@@ -2371,7 +2479,7 @@ async function bindMicrophoneInput(context, engine, options = {}) {
2371
2479
  }
2372
2480
  closed = true;
2373
2481
  source.disconnect();
2374
- if (options.stopTracksOnClose !== false) {
2482
+ if (stopTracksOnClose) {
2375
2483
  for (const track of stream.getAudioTracks()) {
2376
2484
  track.stop();
2377
2485
  }
@@ -2527,12 +2635,22 @@ self.onmessage = async (event) => {
2527
2635
  const frames = Math.min(pageFrames, numSamples - startFrame);
2528
2636
  const frameBytes = numChannels * 4;
2529
2637
  const bytes = new Uint8Array(frames * frameBytes);
2530
- const bytesRead = access.read(bytes, { at: dataOffsetBytes + startFrame * frameBytes });
2531
- const framesRead = Math.floor(bytesRead / frameBytes);
2532
- if (framesRead <= 0) {
2638
+ let bytesReadTotal = 0;
2639
+ const readOffset = dataOffsetBytes + startFrame * frameBytes;
2640
+ while (bytesReadTotal < bytes.byteLength) {
2641
+ const bytesRead = access.read(bytes.subarray(bytesReadTotal), {
2642
+ at: readOffset + bytesReadTotal,
2643
+ });
2644
+ if (bytesRead <= 0) {
2645
+ break;
2646
+ }
2647
+ bytesReadTotal += bytesRead;
2648
+ }
2649
+ if (bytesReadTotal !== bytes.byteLength || bytesReadTotal % frameBytes !== 0) {
2533
2650
  self.postMessage({ type: 'sonare:clip-page', requestId, pageIndex, ok: false });
2534
2651
  return;
2535
2652
  }
2653
+ const framesRead = bytesReadTotal / frameBytes;
2536
2654
  const view = new DataView(bytes.buffer, 0, framesRead * frameBytes);
2537
2655
  const channelBuffers = Array.from({ length: numChannels }, () => new ArrayBuffer(framesRead * 4));
2538
2656
  for (let ch = 0; ch < numChannels; ++ch) {
@@ -2598,7 +2716,12 @@ function createOpfsClipPageProvider(engine, options) {
2598
2716
  entry.resolve(false);
2599
2717
  return;
2600
2718
  }
2601
- provider.supply(response.pageIndex, channels);
2719
+ try {
2720
+ provider.supply(response.pageIndex, channels);
2721
+ } catch {
2722
+ entry.resolve(false);
2723
+ return;
2724
+ }
2602
2725
  entry.resolve(true);
2603
2726
  };
2604
2727
  worker.addEventListener("message", onMessage);
@@ -3964,15 +4087,15 @@ function isWebMidiAvailable() {
3964
4087
  return typeof globalThis.navigator?.requestMIDIAccess === "function";
3965
4088
  }
3966
4089
  async function bindWebMidi(engine, options = {}) {
3967
- const requestMIDIAccess = globalThis.navigator?.requestMIDIAccess;
3968
- if (typeof requestMIDIAccess !== "function") {
4090
+ const navigatorWithMidi = globalThis.navigator;
4091
+ if (typeof navigatorWithMidi?.requestMIDIAccess !== "function") {
3969
4092
  throw new Error("Web MIDI is not available in this environment");
3970
4093
  }
3971
4094
  const group = options.group ?? 0;
3972
4095
  assertNibble("bindWebMidi", group, "group");
3973
4096
  const destinationId = options.destinationId ?? 0;
3974
4097
  const selectedIds = new Set(options.inputIds ?? []);
3975
- const access = await requestMIDIAccess({
4098
+ const access = await navigatorWithMidi.requestMIDIAccess({
3976
4099
  sysex: options.sysex ?? false,
3977
4100
  software: options.software ?? true
3978
4101
  });
@@ -4003,9 +4126,7 @@ async function bindWebMidi(engine, options = {}) {
4003
4126
  runningStatus,
4004
4127
  options.timestampToSamples
4005
4128
  );
4006
- if (status !== 0) {
4007
- runningStatus = status;
4008
- }
4129
+ runningStatus = status;
4009
4130
  };
4010
4131
  if (input.addEventListener) {
4011
4132
  input.addEventListener("midimessage", listener);
@@ -4103,18 +4224,18 @@ function dispatchMidiMessage(engine, event, group, runningStatus, timestampToSam
4103
4224
  const message = status & 240;
4104
4225
  const channel = status & 15;
4105
4226
  if (message < 128 || message > 224) {
4106
- return status;
4227
+ return status >= 248 ? runningStatus : 0;
4107
4228
  }
4108
4229
  const a = readU7(data, offset);
4109
4230
  const b = readU7(data, offset + 1);
4110
- if (a < 0) {
4231
+ if (a < 0 || b < 0) {
4111
4232
  return status;
4112
4233
  }
4113
4234
  const portTimeSamples = timestampToSamples ? timestampToSamples(event.receivedTime ?? event.timeStamp ?? 0) : 0;
4114
4235
  if (message === 128) {
4115
- engine.pushMidiInputNoteOff(group, channel, a, b < 0 ? 0 : b, portTimeSamples);
4236
+ engine.pushMidiInputNoteOff(group, channel, a, b, portTimeSamples);
4116
4237
  } else if (message === 144) {
4117
- if ((b < 0 ? 0 : b) === 0) {
4238
+ if (b === 0) {
4118
4239
  engine.pushMidiInputNoteOff(group, channel, a, 0, portTimeSamples);
4119
4240
  } else {
4120
4241
  engine.pushMidiInputNoteOn(group, channel, a, b, portTimeSamples);
@@ -4263,6 +4384,7 @@ export {
4263
4384
  ChordQuality,
4264
4385
  EXPECTED_ENGINE_ABI_VERSION,
4265
4386
  EXPECTED_PROJECT_ABI_VERSION,
4387
+ ErrorCode,
4266
4388
  KeyProfile,
4267
4389
  Mixer,
4268
4390
  Mode,
@@ -4279,6 +4401,7 @@ export {
4279
4401
  SYNTH_MOD_SOURCES,
4280
4402
  SYNTH_OSC_WAVEFORMS,
4281
4403
  SectionType,
4404
+ SonareError,
4282
4405
  StreamAnalyzer,
4283
4406
  StreamingEqualizer,
4284
4407
  StreamingMasteringChain,
@@ -4337,6 +4460,7 @@ export {
4337
4460
  hzToNote,
4338
4461
  init,
4339
4462
  isInitialized,
4463
+ isSonareError,
4340
4464
  isWebMidiAvailable,
4341
4465
  lufs,
4342
4466
  lufsInterleaved,
@@ -4355,6 +4479,7 @@ export {
4355
4479
  masteringDynamicsGate,
4356
4480
  masteringDynamicsTransientShaper,
4357
4481
  masteringInsertNames,
4482
+ masteringInsertParamNames,
4358
4483
  masteringPairAnalysisNames,
4359
4484
  masteringPairAnalyze,
4360
4485
  masteringPairProcess,