@effetune/dsp 0.6.0 → 0.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.
@@ -10,13 +10,13 @@ export const IR_ASSET_TOPOLOGY = Object.freeze({
10
10
  matrix: 4
11
11
  });
12
12
 
13
- const MAX_CHANNELS = 8;
14
- const MAX_MATRIX_PATHS = 8;
13
+ const MAX_CHANNELS = 16;
14
+ const MAX_MATRIX_PATHS = 16;
15
15
  const MATRIX_PATH_BYTES = 12;
16
16
 
17
17
  function validateChannels(channels) {
18
18
  if (!Array.isArray(channels) || channels.length < 1 || channels.length > MAX_CHANNELS) {
19
- throw new TypeError('IR channels must contain between 1 and 8 Float32Array values');
19
+ throw new TypeError('IR channels must contain between 1 and 16 Float32Array values');
20
20
  }
21
21
  const frames = channels[0] instanceof Float32Array ? channels[0].length : 0;
22
22
  if (frames === 0) throw new TypeError('IR channels must not be empty');
@@ -42,7 +42,7 @@ function validateTopology(topology, paths, channelCount) {
42
42
  return [];
43
43
  }
44
44
  if (!Array.isArray(paths) || paths.length < 1 || paths.length > MAX_MATRIX_PATHS) {
45
- throw new TypeError('Matrix topology requires between 1 and 8 paths');
45
+ throw new TypeError('Matrix topology requires between 1 and 16 paths');
46
46
  }
47
47
  return paths.map(path => {
48
48
  const inputSlot = path?.inputSlot;
@@ -5,7 +5,7 @@ export const IR_KERNEL_ASSET_CAPACITY_BYTES = 32 * 1024 * 1024;
5
5
 
6
6
  const KERNEL_FIXED_FOOTPRINT_BYTES = 2 * 1024 * 1024;
7
7
  const KERNEL_PATH_FRAME_BYTES = 16;
8
- const CONVOLVER_IMPL_BYTES_UPPER_BOUND = 512;
8
+ const CONVOLVER_IMPL_BYTES_UPPER_BOUND = 16 * 1024;
9
9
  const CONVOLVER_STAGE_BYTES_UPPER_BOUND = 512;
10
10
  const PFFFT_SETUP_FIXED_BYTES_UPPER_BOUND = 136;
11
11
  const MATRIX_PATH_BYTES = 12;
@@ -24,18 +24,22 @@ function topologyInputCount(topology, processingChannels, inputCount = 0) {
24
24
  }
25
25
 
26
26
  export function selectedIrChannelCount(channel, engineChannels) {
27
- if (!Number.isInteger(engineChannels) || engineChannels < 1 || engineChannels > 8) return 0;
27
+ if (!Number.isInteger(engineChannels) || engineChannels < 1 || engineChannels > 16) return 0;
28
28
  if (channel === 'A') return engineChannels;
29
29
  if (channel === null || channel === undefined) return engineChannels >= 2 ? 2 : 1;
30
- if (channel === 'L' || channel === 'R' || /^[1-8]$/.test(String(channel))) return 1;
30
+ if (channel === 'L' || channel === 'R' || /^([1-9]|1[0-6])$/.test(String(channel))) return 1;
31
31
  if (channel === '34') return engineChannels >= 4 ? 2 : 0;
32
32
  if (channel === '56') return engineChannels >= 6 ? 2 : 0;
33
33
  if (channel === '78') return engineChannels >= 8 ? 2 : 0;
34
+ if (channel === '910') return engineChannels >= 10 ? 2 : 0;
35
+ if (channel === '1112') return engineChannels >= 12 ? 2 : 0;
36
+ if (channel === '1314') return engineChannels >= 14 ? 2 : 0;
37
+ if (channel === '1516') return engineChannels >= 16 ? 2 : 0;
34
38
  return 0;
35
39
  }
36
40
 
37
41
  function diagonalPaths(channelCount, selectedChannels) {
38
- const count = Math.min(channelCount, selectedChannels, 8);
42
+ const count = Math.min(channelCount, selectedChannels, 16);
39
43
  return Array.from({ length: count }, (_, index) => ({
40
44
  inputSlot: index,
41
45
  outputSlot: index,
@@ -102,7 +106,7 @@ export function estimateIrConvolverMemoryUpperBound({
102
106
  export function resolveIrProcessingConfig({
103
107
  sampleRate,
104
108
  channelCount,
105
- engineChannels = 8,
109
+ engineChannels = 16,
106
110
  selectedChannels,
107
111
  channel,
108
112
  channelMode = 'auto',
@@ -112,7 +116,7 @@ export function resolveIrProcessingConfig({
112
116
  if (!Number.isFinite(sampleRate) || sampleRate <= 0) {
113
117
  return { valid: false, message: 'The current audio sample rate is unavailable.' };
114
118
  }
115
- if (!Number.isInteger(channelCount) || channelCount < 1 || channelCount > 8) {
119
+ if (!Number.isInteger(channelCount) || channelCount < 1 || channelCount > 16) {
116
120
  return { valid: false, message: 'This impulse response has an unsupported channel count.' };
117
121
  }
118
122
  const routedChannels = selectedChannels ?? selectedIrChannelCount(channel, engineChannels);
package/dist/preset.js CHANGED
@@ -58,7 +58,7 @@ function legacyChannel(value) {
58
58
  stereo: 'stereo'
59
59
  };
60
60
  const normalized = mapping[value] ?? value;
61
- if (['1', '2', '3', '4', '5', '6', '7', '8', '34', '56', '78'].includes(normalized)) {
61
+ if (['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '34', '56', '78', '910', '1112', '1314', '1516'].includes(normalized)) {
62
62
  return normalized;
63
63
  }
64
64
  if (['all', 'stereo', 'left', 'right'].includes(normalized)) return normalized;
@@ -309,7 +309,11 @@ function expandLegacyShortKeyArraysV1(parameters, { effectLabel, itemLabel, memb
309
309
  `Legacy ${effectLabel} contains unsupported or incomplete ${itemLabel} settings.`
310
310
  );
311
311
  }
312
- parameters[publicName] = values;
312
+ // App presets retain eight channels when the extended channels are at their defaults.
313
+ const originalCount = legacyName === 'l' ? 7 : 8;
314
+ const defaultValue = legacyName === 'v' || legacyName === 'd' ? 0 : false;
315
+ parameters[publicName] = values.length === originalCount
316
+ ? [...values, ...Array(8).fill(defaultValue)] : values;
313
317
  }
314
318
  }
315
319
 
package/dist/runtime.js CHANGED
@@ -17,8 +17,8 @@ import {
17
17
  const PARAMETER_EVENT_KEYS = new Set(['frame', 'effectId', 'parameters']);
18
18
 
19
19
  function validateAudio(audio) {
20
- if (!Array.isArray(audio) || audio.length < 1 || audio.length > 8) {
21
- throw new ValidationError('Audio must be an array containing between 1 and 8 Float32Array channels.');
20
+ if (!Array.isArray(audio) || audio.length < 1 || audio.length > 16) {
21
+ throw new ValidationError('Audio must be an array containing between 1 and 16 Float32Array channels.');
22
22
  }
23
23
  const frames = audio[0] instanceof Float32Array ? audio[0].length : -1;
24
24
  for (const channel of audio) {
@@ -390,8 +390,8 @@ export class Chain {
390
390
  } = {}) {
391
391
  this._assertOpen();
392
392
  const rate = validateSampleRate(sampleRate);
393
- if (!Number.isInteger(channels) || channels < 1 || channels > 8) {
394
- throw new ValidationError('channels must be an integer from 1 to 8.');
393
+ if (!Number.isInteger(channels) || channels < 1 || channels > 16) {
394
+ throw new ValidationError('channels must be an integer from 1 to 16.');
395
395
  }
396
396
  const frames = validateBlockSize(blockSize);
397
397
  const normalizedSeed = validateSeed(seed);
@@ -434,8 +434,8 @@ export class Chain {
434
434
  } = {}) {
435
435
  this._assertOpen();
436
436
  const rate = validateSampleRate(sampleRate);
437
- if (!Number.isInteger(channels) || channels < 1 || channels > 8) {
438
- throw new ValidationError('channels must be an integer from 1 to 8.');
437
+ if (!Number.isInteger(channels) || channels < 1 || channels > 16) {
438
+ throw new ValidationError('channels must be an integer from 1 to 16.');
439
439
  }
440
440
  const framesPerBlock = validateBlockSize(blockSize);
441
441
  const normalizedSeed = validateSeed(seed);
@@ -116,7 +116,7 @@
116
116
  "channels": {
117
117
  "type": "integer",
118
118
  "minimum": 1,
119
- "maximum": 8
119
+ "maximum": 16
120
120
  },
121
121
  "frames": {
122
122
  "type": "integer",
@@ -142,12 +142,12 @@
142
142
  "pathCount": {
143
143
  "type": "integer",
144
144
  "minimum": 0,
145
- "maximum": 8
145
+ "maximum": 16
146
146
  },
147
147
  "paths": {
148
148
  "type": "array",
149
149
  "minItems": 1,
150
- "maxItems": 8,
150
+ "maxItems": 16,
151
151
  "description": "Matrix routes. Distinct inputSlot values must be exactly 0..N-1, and N must not exceed the selected effect processing-channel count.",
152
152
  "items": {
153
153
  "type": "object",
@@ -161,18 +161,18 @@
161
161
  "inputSlot": {
162
162
  "type": "integer",
163
163
  "minimum": 0,
164
- "maximum": 7,
164
+ "maximum": 15,
165
165
  "description": "Zero-based matrix input; the set of input slots must be contiguous from 0."
166
166
  },
167
167
  "outputSlot": {
168
168
  "type": "integer",
169
169
  "minimum": 0,
170
- "maximum": 7
170
+ "maximum": 15
171
171
  },
172
172
  "irChannel": {
173
173
  "type": "integer",
174
174
  "minimum": 0,
175
- "maximum": 7
175
+ "maximum": 15
176
176
  }
177
177
  }
178
178
  }
@@ -198,7 +198,7 @@
198
198
  "pathCount": {
199
199
  "type": "integer",
200
200
  "minimum": 1,
201
- "maximum": 8
201
+ "maximum": 16
202
202
  }
203
203
  }
204
204
  },
@@ -39,9 +39,21 @@
39
39
  "6",
40
40
  "7",
41
41
  "8",
42
+ "9",
43
+ "10",
44
+ "11",
45
+ "12",
46
+ "13",
47
+ "14",
48
+ "15",
49
+ "16",
42
50
  "34",
43
51
  "56",
44
- "78"
52
+ "78",
53
+ "910",
54
+ "1112",
55
+ "1314",
56
+ "1516"
45
57
  ],
46
58
  "default": "all"
47
59
  },
@@ -780,7 +792,7 @@
780
792
  "matrixRoutes": {
781
793
  "type": "string",
782
794
  "maxLength": 3072,
783
- "pattern": "^(?:p?[0-8][0-8])*$",
795
+ "pattern": "^(?:p?[0-9a-f][0-9a-f])*$",
784
796
  "default": "0011"
785
797
  }
786
798
  }
@@ -816,12 +828,20 @@
816
828
  "properties": {
817
829
  "mute": {
818
830
  "type": "array",
819
- "minItems": 8,
820
- "maxItems": 8,
831
+ "minItems": 16,
832
+ "maxItems": 16,
821
833
  "items": {
822
834
  "type": "boolean"
823
835
  },
824
836
  "default": [
837
+ false,
838
+ false,
839
+ false,
840
+ false,
841
+ false,
842
+ false,
843
+ false,
844
+ false,
825
845
  false,
826
846
  false,
827
847
  false,
@@ -834,12 +854,20 @@
834
854
  },
835
855
  "solo": {
836
856
  "type": "array",
837
- "minItems": 8,
838
- "maxItems": 8,
857
+ "minItems": 16,
858
+ "maxItems": 16,
839
859
  "items": {
840
860
  "type": "boolean"
841
861
  },
842
862
  "default": [
863
+ false,
864
+ false,
865
+ false,
866
+ false,
867
+ false,
868
+ false,
869
+ false,
870
+ false,
843
871
  false,
844
872
  false,
845
873
  false,
@@ -852,8 +880,8 @@
852
880
  },
853
881
  "volume": {
854
882
  "type": "array",
855
- "minItems": 8,
856
- "maxItems": 8,
883
+ "minItems": 16,
884
+ "maxItems": 16,
857
885
  "items": {
858
886
  "type": "number",
859
887
  "minimum": -20,
@@ -861,6 +889,14 @@
861
889
  "x-effetune-unit": "dB"
862
890
  },
863
891
  "default": [
892
+ 0,
893
+ 0,
894
+ 0,
895
+ 0,
896
+ 0,
897
+ 0,
898
+ 0,
899
+ 0,
864
900
  0,
865
901
  0,
866
902
  0,
@@ -873,8 +909,8 @@
873
909
  },
874
910
  "delay": {
875
911
  "type": "array",
876
- "minItems": 8,
877
- "maxItems": 8,
912
+ "minItems": 16,
913
+ "maxItems": 16,
878
914
  "items": {
879
915
  "type": "number",
880
916
  "minimum": 0,
@@ -882,6 +918,14 @@
882
918
  "x-effetune-unit": "ms"
883
919
  },
884
920
  "default": [
921
+ 0,
922
+ 0,
923
+ 0,
924
+ 0,
925
+ 0,
926
+ 0,
927
+ 0,
928
+ 0,
885
929
  0,
886
930
  0,
887
931
  0,
@@ -894,12 +938,20 @@
894
938
  },
895
939
  "link": {
896
940
  "type": "array",
897
- "minItems": 7,
898
- "maxItems": 7,
941
+ "minItems": 15,
942
+ "maxItems": 15,
899
943
  "items": {
900
944
  "type": "boolean"
901
945
  },
902
946
  "default": [
947
+ false,
948
+ false,
949
+ false,
950
+ false,
951
+ false,
952
+ false,
953
+ false,
954
+ false,
903
955
  false,
904
956
  false,
905
957
  false,
@@ -5164,7 +5216,7 @@
5164
5216
  "type": "number",
5165
5217
  "minimum": 0,
5166
5218
  "maximum": 10,
5167
- "default": 0
5219
+ "default": 10
5168
5220
  },
5169
5221
  "wear": {
5170
5222
  "type": "integer",
@@ -6405,7 +6457,7 @@
6405
6457
  "minimum": -36,
6406
6458
  "maximum": 36,
6407
6459
  "x-effetune-unit": "dB",
6408
- "default": 30
6460
+ "default": 26
6409
6461
  }
6410
6462
  }
6411
6463
  }
@@ -6491,7 +6543,7 @@
6491
6543
  "minimum": -36,
6492
6544
  "maximum": 36,
6493
6545
  "x-effetune-unit": "dB",
6494
- "default": 30
6546
+ "default": 26
6495
6547
  }
6496
6548
  }
6497
6549
  }
@@ -64,9 +64,21 @@
64
64
  "6",
65
65
  "7",
66
66
  "8",
67
+ "9",
68
+ "10",
69
+ "11",
70
+ "12",
71
+ "13",
72
+ "14",
73
+ "15",
74
+ "16",
67
75
  "34",
68
76
  "56",
69
- "78"
77
+ "78",
78
+ "910",
79
+ "1112",
80
+ "1314",
81
+ "1516"
70
82
  ],
71
83
  "default": "all"
72
84
  },
@@ -805,7 +817,7 @@
805
817
  "matrixRoutes": {
806
818
  "type": "string",
807
819
  "maxLength": 3072,
808
- "pattern": "^(?:p?[0-8][0-8])*$",
820
+ "pattern": "^(?:p?[0-9a-f][0-9a-f])*$",
809
821
  "default": "0011"
810
822
  }
811
823
  }
@@ -841,12 +853,20 @@
841
853
  "properties": {
842
854
  "mute": {
843
855
  "type": "array",
844
- "minItems": 8,
845
- "maxItems": 8,
856
+ "minItems": 16,
857
+ "maxItems": 16,
846
858
  "items": {
847
859
  "type": "boolean"
848
860
  },
849
861
  "default": [
862
+ false,
863
+ false,
864
+ false,
865
+ false,
866
+ false,
867
+ false,
868
+ false,
869
+ false,
850
870
  false,
851
871
  false,
852
872
  false,
@@ -859,12 +879,20 @@
859
879
  },
860
880
  "solo": {
861
881
  "type": "array",
862
- "minItems": 8,
863
- "maxItems": 8,
882
+ "minItems": 16,
883
+ "maxItems": 16,
864
884
  "items": {
865
885
  "type": "boolean"
866
886
  },
867
887
  "default": [
888
+ false,
889
+ false,
890
+ false,
891
+ false,
892
+ false,
893
+ false,
894
+ false,
895
+ false,
868
896
  false,
869
897
  false,
870
898
  false,
@@ -877,8 +905,8 @@
877
905
  },
878
906
  "volume": {
879
907
  "type": "array",
880
- "minItems": 8,
881
- "maxItems": 8,
908
+ "minItems": 16,
909
+ "maxItems": 16,
882
910
  "items": {
883
911
  "type": "number",
884
912
  "minimum": -20,
@@ -886,6 +914,14 @@
886
914
  "x-effetune-unit": "dB"
887
915
  },
888
916
  "default": [
917
+ 0,
918
+ 0,
919
+ 0,
920
+ 0,
921
+ 0,
922
+ 0,
923
+ 0,
924
+ 0,
889
925
  0,
890
926
  0,
891
927
  0,
@@ -898,8 +934,8 @@
898
934
  },
899
935
  "delay": {
900
936
  "type": "array",
901
- "minItems": 8,
902
- "maxItems": 8,
937
+ "minItems": 16,
938
+ "maxItems": 16,
903
939
  "items": {
904
940
  "type": "number",
905
941
  "minimum": 0,
@@ -907,6 +943,14 @@
907
943
  "x-effetune-unit": "ms"
908
944
  },
909
945
  "default": [
946
+ 0,
947
+ 0,
948
+ 0,
949
+ 0,
950
+ 0,
951
+ 0,
952
+ 0,
953
+ 0,
910
954
  0,
911
955
  0,
912
956
  0,
@@ -919,12 +963,20 @@
919
963
  },
920
964
  "link": {
921
965
  "type": "array",
922
- "minItems": 7,
923
- "maxItems": 7,
966
+ "minItems": 15,
967
+ "maxItems": 15,
924
968
  "items": {
925
969
  "type": "boolean"
926
970
  },
927
971
  "default": [
972
+ false,
973
+ false,
974
+ false,
975
+ false,
976
+ false,
977
+ false,
978
+ false,
979
+ false,
928
980
  false,
929
981
  false,
930
982
  false,
@@ -5189,7 +5241,7 @@
5189
5241
  "type": "number",
5190
5242
  "minimum": 0,
5191
5243
  "maximum": 10,
5192
- "default": 0
5244
+ "default": 10
5193
5245
  },
5194
5246
  "wear": {
5195
5247
  "type": "integer",
@@ -6430,7 +6482,7 @@
6430
6482
  "minimum": -36,
6431
6483
  "maximum": 36,
6432
6484
  "x-effetune-unit": "dB",
6433
- "default": 30
6485
+ "default": 26
6434
6486
  }
6435
6487
  }
6436
6488
  }
@@ -6516,7 +6568,7 @@
6516
6568
  "minimum": -36,
6517
6569
  "maximum": 36,
6518
6570
  "x-effetune-unit": "dB",
6519
- "default": 30
6571
+ "default": 26
6520
6572
  }
6521
6573
  }
6522
6574
  }
package/dist/semantics.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { DSP_PARAM_PACKERS } from './internal/dsp-params.generated.js';
2
2
  import { getEffectDefinition, getEffectImplementation } from './catalog.js';
3
- import { Effect, validateChannel, validateParameterValue } from './effect.js';
3
+ import {
4
+ Effect, canonicalizeProcessingParameters, validateChannel, validateParameterValue
5
+ } from './effect.js';
4
6
  import {
5
7
  AssetError,
6
8
  EffeTuneError,
@@ -231,6 +233,7 @@ function toInternalValue(transform, value) {
231
233
  }
232
234
 
233
235
  export function packEffect(effect) {
236
+ const parameters = canonicalizeProcessingParameters(effect.type, effect.parameters);
234
237
  const implementation = getEffectImplementation(effect.type);
235
238
  const packer = DSP_PARAM_PACKERS.get(implementation.internalType);
236
239
  if (!packer || (packer.hash >>> 0) !== (implementation.layoutHash >>> 0)) {
@@ -238,7 +241,7 @@ export function packEffect(effect) {
238
241
  }
239
242
  const internal = {};
240
243
  for (const mapping of implementation.packedParameters) {
241
- const value = effect.parameters[mapping.publicName];
244
+ const value = parameters[mapping.publicName];
242
245
  if (mapping.count === 1) {
243
246
  internal[mapping.keys[0]] = toInternalValue(mapping.transform, value);
244
247
  } else {
@@ -249,7 +252,7 @@ export function packEffect(effect) {
249
252
  }
250
253
  const structured = implementation.structuredParameter;
251
254
  if (structured) {
252
- internal[structured.key] = effect.parameters[structured.publicName];
255
+ internal[structured.key] = parameters[structured.publicName];
253
256
  }
254
257
  // The generated packer reports capacity limits with plain JavaScript errors.
255
258
  // Translating them here keeps every public entry point (process, stream, setParam)
@@ -302,18 +305,7 @@ export function setEffectParameter(effect, parameterName, value) {
302
305
 
303
306
  export function canonicalizeEffectForProcessing(effect) {
304
307
  const canonical = cloneEffect(effect);
305
- const parameters = canonical.parameters;
306
- if (effect.type === 'AutoFilter' &&
307
- parameters.minimumFrequency > parameters.maximumFrequency) {
308
- [parameters.minimumFrequency, parameters.maximumFrequency] =
309
- [parameters.maximumFrequency, parameters.minimumFrequency];
310
- } else if (effect.type === 'Chorus' && parameters.depth > parameters.delay) {
311
- parameters.depth = parameters.delay;
312
- } else if (effect.type === 'FrequencyShifter' &&
313
- parameters.minimumShift > parameters.maximumShift) {
314
- [parameters.minimumShift, parameters.maximumShift] =
315
- [parameters.maximumShift, parameters.minimumShift];
316
- }
308
+ canonical.parameters = canonicalizeProcessingParameters(effect.type, canonical.parameters);
317
309
  return canonical;
318
310
  }
319
311
 
@@ -328,8 +320,8 @@ export function validateStreamParameterUpdate(effect, parameterName) {
328
320
 
329
321
  export function channelRange(channel, channelCount) {
330
322
  validateChannel(channel);
331
- if (!Number.isInteger(channelCount) || channelCount < 1 || channelCount > 8) {
332
- throw new ValidationError('Audio must contain between 1 and 8 channels.');
323
+ if (!Number.isInteger(channelCount) || channelCount < 1 || channelCount > 16) {
324
+ throw new ValidationError('Audio must contain between 1 and 16 channels.');
333
325
  }
334
326
  if (channel === 'all') return { start: 0, count: channelCount };
335
327
  if (channel === 'stereo') return { start: 0, count: channelCount >= 2 ? 2 : 1 };
@@ -338,8 +330,9 @@ export function channelRange(channel, channelCount) {
338
330
  if (channelCount < 2) throw new ValidationError('The right channel is unavailable in mono audio.');
339
331
  return { start: 1, count: 1 };
340
332
  }
341
- if (channel === '34' || channel === '56' || channel === '78') {
342
- const start = Number(channel[0]) - 1;
333
+ const pairStart = { '34': 2, '56': 4, '78': 6, '910': 8, '1112': 10, '1314': 12, '1516': 14 }[channel];
334
+ if (pairStart !== undefined) {
335
+ const start = pairStart;
343
336
  if (channelCount < start + 2) {
344
337
  throw new ValidationError(`Channel pair ${channel} is unavailable in ${channelCount}-channel audio.`);
345
338
  }
package/dist/telemetry.js CHANGED
@@ -34,7 +34,7 @@ function common(node, kind, sequence, dropped) {
34
34
  function decodeLevel(payload, node, sequence, dropped) {
35
35
  if (payload.byteLength < 16) return null;
36
36
  const channelCount = payload.getUint32(0, true);
37
- if (channelCount < 1 || channelCount > 8 || payload.byteLength !== 8 + channelCount * 8) {
37
+ if (channelCount < 1 || channelCount > 16 || payload.byteLength !== 8 + channelCount * 8) {
38
38
  return null;
39
39
  }
40
40
  const clipFlags = payload.getUint32(4 + channelCount * 8, true);
package/dist/worklet.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  } from './errors.js';
9
9
  import {
10
10
  normalizeChainDocument,
11
+ canonicalizeEffectForProcessing,
11
12
  channelRange,
12
13
  packEffect,
13
14
  setEffectParameter,
@@ -42,8 +43,8 @@ function cloneDocument(document) {
42
43
  }
43
44
 
44
45
  function validateChannels(channels) {
45
- if (!Number.isInteger(channels) || channels < 1 || channels > 8) {
46
- throw new ValidationError('channels must be an integer from 1 to 8.');
46
+ if (!Number.isInteger(channels) || channels < 1 || channels > 16) {
47
+ throw new ValidationError('channels must be an integer from 1 to 16.');
47
48
  }
48
49
  return channels;
49
50
  }
@@ -124,7 +125,11 @@ export class EffeTuneNode extends AudioWorkletNodeBase {
124
125
  })()
125
126
  : input;
126
127
  const { chain, manifest } = splitBundle(source);
127
- const document = normalizeChainDocument(chain);
128
+ const normalized = normalizeChainDocument(chain);
129
+ const document = {
130
+ version: 1,
131
+ chain: normalized.chain.map(canonicalizeEffectForProcessing)
132
+ };
128
133
  const resolvedAssets = await resolveChainAssets(document, {
129
134
  assetResolver: options.assetResolver,
130
135
  manifest
@@ -359,7 +364,9 @@ export class EffeTuneNode extends AudioWorkletNodeBase {
359
364
  if (index < 0) throw new ValidationError(`Unknown effect id: ${effectId}`);
360
365
  const effects = [...this._document.chain];
361
366
  validateStreamParameterUpdate(effects[index], parameterName);
362
- effects[index] = setEffectParameter(effects[index], parameterName, value);
367
+ effects[index] = canonicalizeEffectForProcessing(
368
+ setEffectParameter(effects[index], parameterName, value)
369
+ );
363
370
  if (effects[index].enabled) {
364
371
  const packed = packEffect(effects[index]);
365
372
  await this._command({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effetune/dsp",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "EffeTune's deterministic WebAssembly audio effects for JavaScript and AudioWorklet",
5
5
  "type": "module",
6
6
  "license": "MIT",