spessasynth_lib 3.20.35 → 3.20.40

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.
@@ -7,12 +7,18 @@ export class DLSSample extends BasicSample {
7
7
  * @param loopStart {number} sample data points
8
8
  * @param loopEnd {number} sample data points
9
9
  * @param data {Float32Array}
10
+ * @param sampleDbAttenuation {number} in db
10
11
  */
11
- constructor(name: string, rate: number, pitch: number, pitchCorrection: number, loopStart: number, loopEnd: number, data: Float32Array);
12
+ constructor(name: string, rate: number, pitch: number, pitchCorrection: number, loopStart: number, loopEnd: number, data: Float32Array, sampleDbAttenuation: number);
12
13
  /**
13
14
  * @type {Float32Array}
14
15
  */
15
16
  sampleData: Float32Array;
17
+ /**
18
+ * in decibels of attenuation, WITHOUT EMU CORRECTION
19
+ * @type {number}
20
+ */
21
+ sampleDbAttenuation: number;
16
22
  getRawData(): Uint8Array;
17
23
  }
18
24
  import { BasicSample } from '../basic_soundfont/basic_sample.js';
@@ -69,6 +69,11 @@ export class Modulator {
69
69
  secSrcUsesCC: number;
70
70
  secSrcIndex: number;
71
71
  secSrcCurveType: number;
72
+ /**
73
+ * The current computed value of this modulator
74
+ * @type {number}
75
+ */
76
+ currentValue: number;
72
77
  /**
73
78
  * Sums transform and creates a NEW modulator
74
79
  * @param modulator {Modulator}
@@ -10,7 +10,7 @@
10
10
  * @property {number|undefined} loopCount - the times to loop the song
11
11
  */
12
12
  export const WORKLET_PROCESSOR_NAME: "spessasynth-worklet-system";
13
- export const VOICE_CAP: 450;
13
+ export const VOICE_CAP: 350;
14
14
  export const DEFAULT_PERCUSSION: 9;
15
15
  export const MIDI_CHANNEL_COUNT: 16;
16
16
  export const DEFAULT_SYNTH_MODE: "gs";
@@ -252,6 +252,13 @@ export class Synthetizer {
252
252
  * @param userChange {boolean} indicates if the program change has been called by user. defaults to false
253
253
  */
254
254
  programChange(channel: number, programNumber: number, userChange?: boolean): void;
255
+ /**
256
+ * Overrides velocity on a given channel
257
+ * @param channel {number} usually 0-15: the channel to change
258
+ * @param velocity {number} 1-127, the velocity to use.
259
+ * 0 Disables this functionality
260
+ */
261
+ velocityOverride(channel: number, velocity: number): void;
255
262
  /**
256
263
  * Causes the given midi channel to ignore controller messages for the given controller number
257
264
  * @param channel {number} usually 0-15: the channel to lock
@@ -9,6 +9,7 @@
9
9
  * @property {Int16Array} keyCentTuning - tuning of individual keys in cents
10
10
  * @property {boolean} holdPedal - indicates whether the hold pedal is active
11
11
  * @property {boolean} drumChannel - indicates whether the channel is a drum channel
12
+ * @property {number} velocityOverride - overrides velocity if > 0 otherwise disabled
12
13
  *
13
14
  * @property {dataEntryStates} dataEntryState - the current state of the data entry
14
15
  * @property {number} NRPCoarse - the current coarse value of the Non-Registered Parameter
@@ -57,6 +58,13 @@ export namespace customControllers {
57
58
  }
58
59
  export const CUSTOM_CONTROLLER_TABLE_SIZE: number;
59
60
  export const customResetArray: Float32Array;
61
+ /**
62
+ * This is a channel configuration enum, it is internally sent from Synthetizer via controller change
63
+ */
64
+ export type channelConfiguration = number;
65
+ export namespace channelConfiguration {
66
+ let velocityOverride: number;
67
+ }
60
68
  export type WorkletProcessorChannel = {
61
69
  /**
62
70
  * - array of MIDI controller values + the values used by modulators as source (pitch bend, bend range etc.)
@@ -90,6 +98,10 @@ export type WorkletProcessorChannel = {
90
98
  * - indicates whether the channel is a drum channel
91
99
  */
92
100
  drumChannel: boolean;
101
+ /**
102
+ * - overrides velocity if > 0 otherwise disabled
103
+ */
104
+ velocityOverride: number;
93
105
  /**
94
106
  * - the current state of the data entry
95
107
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_lib",
3
- "version": "3.20.35",
3
+ "version": "3.20.40",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "browser": "index.js",
6
6
  "types": "@types/index.d.ts",
@@ -10,6 +10,7 @@ export class DLSSample extends BasicSample
10
10
  * @param loopStart {number} sample data points
11
11
  * @param loopEnd {number} sample data points
12
12
  * @param data {Float32Array}
13
+ * @param sampleDbAttenuation {number} in db
13
14
  */
14
15
  constructor(
15
16
  name,
@@ -18,7 +19,8 @@ export class DLSSample extends BasicSample
18
19
  pitchCorrection,
19
20
  loopStart,
20
21
  loopEnd,
21
- data
22
+ data,
23
+ sampleDbAttenuation
22
24
  )
23
25
  {
24
26
  super(
@@ -32,6 +34,7 @@ export class DLSSample extends BasicSample
32
34
  (loopEnd - 1) * 2 // -1 sample because soundfont end is last sample and dls end is next sample
33
35
  );
34
36
  this.sampleData = data;
37
+ this.sampleDbAttenuation = sampleDbAttenuation;
35
38
  }
36
39
 
37
40
  getAudioData()
@@ -39,6 +42,12 @@ export class DLSSample extends BasicSample
39
42
  return this.sampleData;
40
43
  }
41
44
 
45
+ /**
46
+ * in decibels of attenuation, WITHOUT EMU CORRECTION
47
+ * @type {number}
48
+ */
49
+ sampleDbAttenuation;
50
+
42
51
  /**
43
52
  * @type {Float32Array}
44
53
  */
@@ -65,11 +65,10 @@ export function readRegion(chunk)
65
65
  );
66
66
 
67
67
  // gain correction: Each unit of gain represents 1/655360 dB
68
+ // it is set after linking the sample
68
69
  const gainCorrection = readLittleEndian(waveSampleChunk.chunkData, 4);
69
70
  // convert to signed and turn into attenuation (invert)
70
71
  const dbCorrection = (gainCorrection | 0) / -655360;
71
- // convert to centibels
72
- const attenuation = (dbCorrection * 10) / 0.4; // make sure to apply EMU correction
73
72
 
74
73
  // skip options
75
74
  readLittleEndian(waveSampleChunk.chunkData, 4);
@@ -118,11 +117,20 @@ export function readRegion(chunk)
118
117
  readLittleEndian(waveLinkChunk.chunkData, 4);
119
118
  // sampleID
120
119
  const sampleID = readLittleEndian(waveLinkChunk.chunkData, 4);
120
+ /**
121
+ * @type {DLSSample}
122
+ */
121
123
  const sample = this.samples[sampleID];
122
124
  if(sample === undefined)
123
125
  {
124
126
  throw new Error("Invalid sample ID!");
125
127
  }
128
+
129
+ // this correction overrides the sample gain correction
130
+ const actualDbCorrection = dbCorrection || sample.sampleDbAttenuation;
131
+ // convert to centibels
132
+ const attenuation = (actualDbCorrection * 10) / 0.4; // make sure to apply EMU correction
133
+
126
134
  zone.setWavesample(
127
135
  attenuation, loopingMode,
128
136
  loop,
@@ -106,6 +106,7 @@ export function readDLSSamples(waveListChunk)
106
106
  let samplePitch = 0;
107
107
  let sampleLoopStart = 0;
108
108
  let sampleLoopEnd = sampleData.length - 1;
109
+ let sampleDbAttenuation = 0;
109
110
 
110
111
  // read wsmp
111
112
  const wsmpChunk = waveChunks.find(c => c.header === "wsmp")
@@ -120,8 +121,10 @@ export function readDLSSamples(waveListChunk)
120
121
  wsmpChunk.chunkData[wsmpChunk.chunkData.currentIndex++],
121
122
  wsmpChunk.chunkData[wsmpChunk.chunkData.currentIndex++]
122
123
  );
123
- // gain is handled in regions as initialAttenuation
124
- readLittleEndian(wsmpChunk.chunkData, 4);
124
+ // gain is applied it manually here (literally multiplying the samples)
125
+ const gainCorrection = readLittleEndian(wsmpChunk.chunkData, 4);
126
+ // convert to signed and turn into decibels
127
+ sampleDbAttenuation = (gainCorrection | 0) / -655360;
125
128
  // no idea about ful options
126
129
  readLittleEndian(wsmpChunk.chunkData, 4);
127
130
  const loopsAmount = readLittleEndian(wsmpChunk.chunkData, 4);
@@ -162,7 +165,8 @@ export function readDLSSamples(waveListChunk)
162
165
  samplePitch,
163
166
  sampleLoopStart,
164
167
  sampleLength,
165
- sampleData
168
+ sampleData,
169
+ sampleDbAttenuation
166
170
  ));
167
171
 
168
172
  sampleID++;
@@ -16,7 +16,7 @@ export const generatorTypes = {
16
16
  endAddrOffset: 1, // sample control - moves sample end point
17
17
  startloopAddrsOffset: 2, // loop control - moves loop start point
18
18
  endloopAddrsOffset: 3, // loop control - moves loop end point
19
- startAddrsCoarseOffset: 4, // sample control - moves sample start point in 32767 increments
19
+ startAddrsCoarseOffset: 4, // sample control - moves sample start point in 32768 increments
20
20
  modLfoToPitch: 5, // pitch modulation - modulation lfo pitch modulation in cents
21
21
  vibLfoToPitch: 6, // pitch modulation - vibrato lfo pitch modulation in cents
22
22
  modEnvToPitch: 7, // pitch modulation - modulation envelope pitch modulation in cents
@@ -24,7 +24,7 @@ export const generatorTypes = {
24
24
  initialFilterQ: 9, // filter - lowpass filter resonance
25
25
  modLfoToFilterFc: 10, // filter modulation - modulation lfo lowpass filter cutoff in cents
26
26
  modEnvToFilterFc: 11, // filter modulation - modulation envelope lowpass filter cutoff in cents
27
- endAddrsCoarseOffset: 12, // ample control - moves sample end point in 32767 increments
27
+ endAddrsCoarseOffset: 12, // ample control - moves sample end point in 32768 increments
28
28
  modLfoToVolume: 13, // modulation lfo - volume (tremolo), where 100 = 10dB
29
29
  unused1: 14,
30
30
  chorusEffectsSend: 15, // effect send - how much is sent to chorus 0 - 1000
@@ -57,12 +57,12 @@ export const generatorTypes = {
57
57
  reserved1: 42,
58
58
  keyRange: 43, // zone - key range for which preset / instrument zone is active
59
59
  velRange: 44, // zone - velocity range for which preset / instrument zone is active
60
- startloopAddrsCoarseOffset: 45, // ample control - moves sample loop start point in 32767 increments
60
+ startloopAddrsCoarseOffset: 45, // sample control - moves sample loop start point in 32768 increments
61
61
  keyNum: 46, // zone - instrument only: always use this midi number (ignore what's pressed)
62
62
  velocity: 47, // zone - instrument only: always use this velocity (ignore what's pressed)
63
63
  initialAttenuation: 48, // zone - allows turning down the volume, 10 = -1dB
64
64
  reserved2: 49,
65
- endloopAddrsCoarseOffset: 50, // ample control - moves sample loop end point in 32767 increments
65
+ endloopAddrsCoarseOffset: 50, // sample control - moves sample loop end point in 32768 increments
66
66
  coarseTune: 51, // tune - pitch offset in semitones
67
67
  fineTune: 52, // tune - pitch offset in cents
68
68
  sampleID: 53, // sample - instrument zone only: which sample to use
@@ -84,6 +84,12 @@ export class Modulator{
84
84
  //this.precomputeModulatorTransform();
85
85
  }
86
86
 
87
+ /**
88
+ * The current computed value of this modulator
89
+ * @type {number}
90
+ */
91
+ currentValue = 0;
92
+
87
93
  /**
88
94
  * @param modulator {Modulator}
89
95
  * @returns {Modulator}
@@ -258,6 +264,15 @@ export const defaultModulators = [
258
264
  amt: 4000,
259
265
  secSrcEnum: 0x0, // no controller
260
266
  transform: 0
267
+ }),
268
+
269
+ // cc 71 (filter q) to filterq
270
+ new Modulator({
271
+ srcEnum: getModSourceEnum(modulatorCurveTypes.linear, 1, 0 , 1, midiControllers.timbreHarmonicContent), // linear forwards bipolar cc 74
272
+ dest: generatorTypes.initialFilterQ,
273
+ amt: 250,
274
+ secSrcEnum: 0x0, // no controller
275
+ transform: 0
261
276
  })
262
277
  ];
263
278
 
@@ -12,6 +12,7 @@ import {
12
12
  import { SpessaSynthInfo, SpessaSynthWarn } from '../utils/loggin.js'
13
13
  import { DEFAULT_EFFECTS_CONFIG } from './audio_effects/effects_config.js'
14
14
  import { SoundfontManager } from './synth_soundfont_manager.js'
15
+ import { channelConfiguration } from './worklet_system/worklet_utilities/worklet_processor_channel.js'
15
16
 
16
17
 
17
18
  /**
@@ -29,7 +30,7 @@ import { SoundfontManager } from './synth_soundfont_manager.js'
29
30
 
30
31
  export const WORKLET_PROCESSOR_NAME = "spessasynth-worklet-system";
31
32
 
32
- export const VOICE_CAP = 450;
33
+ export const VOICE_CAP = 350;
33
34
 
34
35
  export const DEFAULT_PERCUSSION = 9;
35
36
  export const MIDI_CHANNEL_COUNT = 16;
@@ -484,6 +485,7 @@ export class Synthetizer {
484
485
  */
485
486
  controllerChange(channel, controllerNumber, controllerValue, force=false)
486
487
  {
488
+ if(controllerNumber > 127 || controllerNumber < 0) throw new Error(`Invalid controller number: ${controllerNumber}`);
487
489
  controllerValue = Math.floor(controllerValue);
488
490
  controllerNumber = Math.floor(controllerNumber);
489
491
  this.post({
@@ -631,6 +633,21 @@ export class Synthetizer {
631
633
  })
632
634
  }
633
635
 
636
+ /**
637
+ * Overrides velocity on a given channel
638
+ * @param channel {number} usually 0-15: the channel to change
639
+ * @param velocity {number} 1-127, the velocity to use.
640
+ * 0 Disables this functionality
641
+ */
642
+ velocityOverride(channel, velocity)
643
+ {
644
+ this.post({
645
+ channelNumber: channel,
646
+ messageType: workletMessageType.ccChange,
647
+ messageData: [channelConfiguration.velocityOverride, velocity, true]
648
+ })
649
+ }
650
+
634
651
  /**
635
652
  * Causes the given midi channel to ignore controller messages for the given controller number
636
653
  * @param channel {number} usually 0-15: the channel to lock