@grame/faustwasm 0.18.1 → 0.18.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.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grame/faustwasm",
3
- "version": "0.18.1",
3
+ "version": "0.18.2",
4
4
  "description": "WebAssembly version of Faust Compiler",
5
5
  "main": "dist/cjs/index.js",
6
6
  "types": "dist/esm/index.d.ts",
@@ -2285,6 +2285,8 @@ export class FaustPolyWebAudioDsp
2285
2285
  private fAudioMixing!: number;
2286
2286
  private fAudioMixingHalf!: number;
2287
2287
  private fMixingBase: number[] = [];
2288
+ /** Float view of the memory, kept from `initMemory` like `fHEAP32`. */
2289
+ private fHEAPF!: Float32Array | Float64Array;
2288
2290
  private fVoiceTable: FaustWebAudioDspVoice[];
2289
2291
  private fSampleRate: number;
2290
2292
 
@@ -2341,12 +2343,20 @@ export class FaustPolyWebAudioDsp
2341
2343
  /**
2342
2344
  * Render the stolen voices, each across the whole block.
2343
2345
  *
2344
- * A steal is a crossfade: the voice plays the note it is losing over the
2346
+ * A steal is a hand-over: the voice plays the note it is losing over the
2345
2347
  * first half of the buffer, that half fades out, and the new note plays
2346
- * the second half. The fade has to be half a block -- 64 frames -- rather
2347
- * than half a slice, which late in the block would be a frame or two, or
2348
- * nothing at all. So this runs before the slicing, and `fRenderSlice`
2349
- * skips these voices.
2348
+ * the second half, faded in. Both fades are needed. `fadeOut` scales the
2349
+ * buffer, not the voice: after `keyOn` the DSP carries on from its live
2350
+ * state -- an envelope that never reached silence is still up -- so
2351
+ * without the fade-in the second half started at full level, a step on
2352
+ * every stolen note. On a monophonic patch whose voice never falls
2353
+ * silent that is a click on every note after the first (measured on a
2354
+ * long-release bass: 0, 0, 0, 0.1485, 0.2618 across the split).
2355
+ *
2356
+ * The fades have to be half a block -- 64 frames -- rather than half a
2357
+ * slice, which late in the block would be a frame or two, or nothing at
2358
+ * all. So this runs before the slicing, and `fRenderSlice` skips these
2359
+ * voices.
2350
2360
  */
2351
2361
  private renderStolenVoices() {
2352
2362
  const stolen = this.fStolen;
@@ -2367,6 +2377,14 @@ export class FaustPolyWebAudioDsp
2367
2377
  this.getNumOutputs(),
2368
2378
  this.fAudioMixing
2369
2379
  );
2380
+ // FadeIn on second half buffer, the mirror of fadeOut: 1/count
2381
+ // up to 1. `computeLegato` gives an odd count's extra frame to
2382
+ // the second half, so it is measured from the split, not halved.
2383
+ this.fadeIn(
2384
+ this.fBufferSize - (this.fBufferSize >> 1),
2385
+ this.getNumOutputs(),
2386
+ this.fAudioMixingHalf
2387
+ );
2370
2388
  // Mix it in result
2371
2389
  voice.fLevel = this.fInstance.mixerAPI.mixCheckVoice(
2372
2390
  this.fBufferSize,
@@ -2377,6 +2395,23 @@ export class FaustPolyWebAudioDsp
2377
2395
  });
2378
2396
  }
2379
2397
 
2398
+ /**
2399
+ * Scale `count` frames of each channel at `$tables` from 1/count up to 1.
2400
+ *
2401
+ * The mixer has `fadeOut` in wasm; this is its counterpart on the JS
2402
+ * side, over the channel-pointer table the same way the mixer reads it.
2403
+ */
2404
+ private fadeIn(count: number, chans: number, $tables: number) {
2405
+ const shift = Math.log2(this.fSampleSize);
2406
+ const HEAPF = this.fHEAPF;
2407
+ for (let chan = 0; chan < chans; chan++) {
2408
+ const start = this.fHEAP32[($tables >> 2) + chan] >> shift;
2409
+ for (let i = 0; i < count; i++) {
2410
+ HEAPF[start + i] *= (i + 1) / count;
2411
+ }
2412
+ }
2413
+ }
2414
+
2380
2415
  /**
2381
2416
  * Move the mixing tables along with the input and output ones.
2382
2417
  *
@@ -2584,6 +2619,7 @@ export class FaustPolyWebAudioDsp
2584
2619
  for (let chan = 0; chan < this.getNumInputs(); chan++) {
2585
2620
  this.fInBase[chan] = HEAP32[(this.fAudioInputs >> 2) + chan];
2586
2621
  }
2622
+ this.fHEAPF = HEAPF;
2587
2623
  this.fOutBase = [];
2588
2624
  this.fMixingBase = [];
2589
2625
  for (let chan = 0; chan < this.getNumOutputs(); chan++) {
@@ -1099,6 +1099,8 @@ export declare class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implement
1099
1099
  private fAudioMixing;
1100
1100
  private fAudioMixingHalf;
1101
1101
  private fMixingBase;
1102
+ /** Float view of the memory, kept from `initMemory` like `fHEAP32`. */
1103
+ private fHEAPF;
1102
1104
  private fVoiceTable;
1103
1105
  private fSampleRate;
1104
1106
  /** Voices whose crossfade this block already rendered in full. */
@@ -1123,14 +1125,29 @@ export declare class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implement
1123
1125
  /**
1124
1126
  * Render the stolen voices, each across the whole block.
1125
1127
  *
1126
- * A steal is a crossfade: the voice plays the note it is losing over the
1128
+ * A steal is a hand-over: the voice plays the note it is losing over the
1127
1129
  * first half of the buffer, that half fades out, and the new note plays
1128
- * the second half. The fade has to be half a block -- 64 frames -- rather
1129
- * than half a slice, which late in the block would be a frame or two, or
1130
- * nothing at all. So this runs before the slicing, and `fRenderSlice`
1131
- * skips these voices.
1130
+ * the second half, faded in. Both fades are needed. `fadeOut` scales the
1131
+ * buffer, not the voice: after `keyOn` the DSP carries on from its live
1132
+ * state -- an envelope that never reached silence is still up -- so
1133
+ * without the fade-in the second half started at full level, a step on
1134
+ * every stolen note. On a monophonic patch whose voice never falls
1135
+ * silent that is a click on every note after the first (measured on a
1136
+ * long-release bass: 0, 0, 0, 0.1485, 0.2618 across the split).
1137
+ *
1138
+ * The fades have to be half a block -- 64 frames -- rather than half a
1139
+ * slice, which late in the block would be a frame or two, or nothing at
1140
+ * all. So this runs before the slicing, and `fRenderSlice` skips these
1141
+ * voices.
1132
1142
  */
1133
1143
  private renderStolenVoices;
1144
+ /**
1145
+ * Scale `count` frames of each channel at `$tables` from 1/count up to 1.
1146
+ *
1147
+ * The mixer has `fadeOut` in wasm; this is its counterpart on the JS
1148
+ * side, over the channel-pointer table the same way the mixer reads it.
1149
+ */
1150
+ private fadeIn;
1134
1151
  /**
1135
1152
  * Move the mixing tables along with the input and output ones.
1136
1153
  *
@@ -3960,12 +3960,20 @@ var FaustPolyWebAudioDsp = class _FaustPolyWebAudioDsp extends FaustBaseWebAudio
3960
3960
  /**
3961
3961
  * Render the stolen voices, each across the whole block.
3962
3962
  *
3963
- * A steal is a crossfade: the voice plays the note it is losing over the
3963
+ * A steal is a hand-over: the voice plays the note it is losing over the
3964
3964
  * first half of the buffer, that half fades out, and the new note plays
3965
- * the second half. The fade has to be half a block -- 64 frames -- rather
3966
- * than half a slice, which late in the block would be a frame or two, or
3967
- * nothing at all. So this runs before the slicing, and `fRenderSlice`
3968
- * skips these voices.
3965
+ * the second half, faded in. Both fades are needed. `fadeOut` scales the
3966
+ * buffer, not the voice: after `keyOn` the DSP carries on from its live
3967
+ * state -- an envelope that never reached silence is still up -- so
3968
+ * without the fade-in the second half started at full level, a step on
3969
+ * every stolen note. On a monophonic patch whose voice never falls
3970
+ * silent that is a click on every note after the first (measured on a
3971
+ * long-release bass: 0, 0, 0, 0.1485, 0.2618 across the split).
3972
+ *
3973
+ * The fades have to be half a block -- 64 frames -- rather than half a
3974
+ * slice, which late in the block would be a frame or two, or nothing at
3975
+ * all. So this runs before the slicing, and `fRenderSlice` skips these
3976
+ * voices.
3969
3977
  */
3970
3978
  renderStolenVoices() {
3971
3979
  const stolen = this.fStolen;
@@ -3984,6 +3992,11 @@ var FaustPolyWebAudioDsp = class _FaustPolyWebAudioDsp extends FaustBaseWebAudio
3984
3992
  this.getNumOutputs(),
3985
3993
  this.fAudioMixing
3986
3994
  );
3995
+ this.fadeIn(
3996
+ this.fBufferSize - (this.fBufferSize >> 1),
3997
+ this.getNumOutputs(),
3998
+ this.fAudioMixingHalf
3999
+ );
3987
4000
  voice.fLevel = this.fInstance.mixerAPI.mixCheckVoice(
3988
4001
  this.fBufferSize,
3989
4002
  this.getNumOutputs(),
@@ -3992,6 +4005,22 @@ var FaustPolyWebAudioDsp = class _FaustPolyWebAudioDsp extends FaustBaseWebAudio
3992
4005
  );
3993
4006
  });
3994
4007
  }
4008
+ /**
4009
+ * Scale `count` frames of each channel at `$tables` from 1/count up to 1.
4010
+ *
4011
+ * The mixer has `fadeOut` in wasm; this is its counterpart on the JS
4012
+ * side, over the channel-pointer table the same way the mixer reads it.
4013
+ */
4014
+ fadeIn(count, chans, $tables) {
4015
+ const shift = Math.log2(this.fSampleSize);
4016
+ const HEAPF = this.fHEAPF;
4017
+ for (let chan = 0; chan < chans; chan++) {
4018
+ const start = this.fHEAP32[($tables >> 2) + chan] >> shift;
4019
+ for (let i = 0; i < count; i++) {
4020
+ HEAPF[start + i] *= (i + 1) / count;
4021
+ }
4022
+ }
4023
+ }
3995
4024
  /**
3996
4025
  * Move the mixing tables along with the input and output ones.
3997
4026
  *
@@ -4090,6 +4119,7 @@ var FaustPolyWebAudioDsp = class _FaustPolyWebAudioDsp extends FaustBaseWebAudio
4090
4119
  for (let chan = 0; chan < this.getNumInputs(); chan++) {
4091
4120
  this.fInBase[chan] = HEAP32[(this.fAudioInputs >> 2) + chan];
4092
4121
  }
4122
+ this.fHEAPF = HEAPF;
4093
4123
  this.fOutBase = [];
4094
4124
  this.fMixingBase = [];
4095
4125
  for (let chan = 0; chan < this.getNumOutputs(); chan++) {