spessasynth_core 4.2.1 → 4.2.3

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
@@ -3,14 +3,14 @@
3
3
  <img src='https://raw.githubusercontent.com/spessasus/SpessaSynth/refs/heads/master/src/website/spessasynth_logo_rounded.png' width='300' alt='SpessaSynth logo'>
4
4
  </p>
5
5
 
6
- _A powerful SF2/DLS/MIDI TypeScript/JavaScript library. It works with any modern JS environment that supports
6
+ _A powerful multipurpose SF2/DLS/MIDI JavaScript library. It works with any modern JS environment that supports
7
7
  WebAssembly._
8
8
 
9
9
  It allows you to:
10
10
 
11
11
  - Play MIDI files using SF2/SF3/DLS files!
12
12
  - Read and write MIDI files!
13
- - Write SF2/SF3 files!
13
+ - Read and write SF2/SF3 files!
14
14
  - Convert DLS to SF2! (and back!)
15
15
  - [and more!](#current-features)
16
16
 
@@ -23,7 +23,7 @@ Featuring Reverb, Chorus, Delay, Insertion effects and more!
23
23
  > Looking for an easy-to-use WebAudioAPI browser wrapper?
24
24
  > Try [spessasynth_lib](https://github.com/spessasus/spessasynth_lib)!
25
25
 
26
- ### [Project site (consider giving it a star!)](https://github.com/spessasus/spessasynth_core)
26
+ **[Project site (consider giving it a star!)](https://github.com/spessasus/spessasynth_core)**
27
27
 
28
28
  ### Made with spessasynth_core
29
29
 
@@ -44,12 +44,12 @@ Featuring Reverb, Chorus, Delay, Insertion effects and more!
44
44
 
45
45
  ### Easy Integration
46
46
 
47
- - **Modular design:** _Easy integration into other projects (load what you need)_
47
+ - **Modular design:** _Easy integration into other projects (only load what you need)_
48
48
  - **Flexible:** _It's not just a MIDI player!_
49
49
  - **Easy to Use:** _Basic setup is
50
50
  just [two lines of code!](https://spessasus.github.io/spessasynth_core/getting-started#minimal-setup)_
51
51
  - **No dependencies:** _Batteries included!_
52
- - **TypeScript definitions:** _Autocompletion in IDEs!_
52
+ - **Full TypeScript definitions:** _Autocompletion in IDEs!_
53
53
 
54
54
  ### Powerful MIDI Synthesizer
55
55
 
@@ -57,13 +57,11 @@ Featuring Reverb, Chorus, Delay, Insertion effects and more!
57
57
  - **Excellent SoundFont support:**
58
58
  - **Full Generator Support**
59
59
  - **Full Modulator Support:** _First (to my knowledge) JavaScript SoundFont synth with that feature!_
60
- - **GeneralUserGS Compatible:**
60
+ - **GeneralUser-GS Compatible:**
61
61
  _[See more here!](https://github.com/mrbumpy409/GeneralUser-GS/blob/main/documentation/README.md)_
62
62
  - **SoundFont3 Support:** Play compressed SoundFonts!
63
63
  - **Experimental SF2Pack Support:** Play soundfonts compressed with BASSMIDI! (_Note: only works with vorbis
64
64
  compression_)
65
- - **Can load very large SoundFonts:** up to 4GB! _Note: Only Firefox handles this well; Chromium has a hard-coded
66
- memory limit_
67
65
  - **Great DLS Support:**
68
66
  - **DLS Level 1 Support**
69
67
  - **DLS Level 2 Support**
@@ -74,14 +72,15 @@ Featuring Reverb, Chorus, Delay, Insertion effects and more!
74
72
  - **A-Law encoding support**
75
73
  - **Both unsigned 8-bit and signed 16-bit sample support (24-bit theoretically supported as well!)**
76
74
  - **Detects special articulator combinations:** _Such as vibratoLfoToPitch_
77
- - **Soundfont manager:** Stack multiple soundfonts!
75
+ - **Sound bank manager:** Stack multiple sound banks!
78
76
  - **Unlimited channel count:** Your CPU is the limit!
79
77
  - **Built-in, configurable effects:**
80
78
  - **Reverb:** _Multiple characters including delay and panning delay!_
81
79
  - **Chorus:** _Modulated delay lines with multiple presets!_
82
80
  - **Delay:** _Three delay lines for all of your delay needs!_
83
81
  - **Insertion Effects:** _The ultimate effects, they can give your sounds a completely different character! (limited support)_
84
- - **Replaceable:** _Effects not to your taste? You can bring your own!_
82
+ - **GS Compatible:** _MIDI files can configure the effects accurately!_
83
+ - **Replaceable:** _Effects not to your liking? You can replace them with your own!_
85
84
  - **Excellent MIDI Standards Support:**
86
85
  - **MIDI Controller Support:** Default supported
87
86
  controllers [here](https://spessasus.github.io/spessasynth_core/extra/midi-implementation#default-supported-controllers)
@@ -89,14 +88,13 @@ Featuring Reverb, Chorus, Delay, Insertion effects and more!
89
88
  - **Sound Controllers:** _Real-time filter and envelope control!_
90
89
  - **MIDI Tuning Standard Support:**
91
90
  _[more info here](https://spessasus.github.io/spessasynth_core/extra/midi-implementation#midi-tuning-standard)_
92
- - [Full **RPN** and limited **NRPN**
91
+ - [Full **RPN** and extensive **NRPN**
93
92
  support](https://spessasus.github.io/spessasynth_core/extra/midi-implementation#supported-registered-parameters)
94
93
  - **SoundFont2 NRPN Support**
95
94
  - [**AWE32**
96
95
  NRPN Compatibility Layer](https://spessasus.github.io/spessasynth_core/extra/midi-implementation#awe32-nrpn-compatibility-layer)
97
96
  - [**Roland GS** and **Yamaha XG**
98
97
  support!](https://spessasus.github.io/spessasynth_core/extra/midi-implementation#supported-system-exclusives)
99
- - Built-in effects are GS-Compatible!
100
98
 
101
99
  ### Powerful and Fast MIDI Sequencer
102
100
 
@@ -181,7 +179,7 @@ Featuring Reverb, Chorus, Delay, Insertion effects and more!
181
179
 
182
180
  ### Limitations
183
181
 
184
- - Synth's performance may be questionable sometimes
182
+ - Audio engine is written in pure TypeScript, so it may not be as performant as native implementations
185
183
  - [SF2 to DLS Conversion limits](https://spessasus.github.io/spessasynth_core/extra/dls-conversion-problem)
186
184
 
187
185
  #### TODO
@@ -208,6 +206,8 @@ Featuring Reverb, Chorus, Delay, Insertion effects and more!
208
206
 
209
207
  ### Short example: MIDI to wav converter
210
208
 
209
+ For use with node.js
210
+
211
211
  ```ts
212
212
  import * as fs from "fs/promises";
213
213
  import {
@@ -226,24 +226,30 @@ if (args.length !== 3) {
226
226
  );
227
227
  process.exit();
228
228
  }
229
+ // Read MIDI and sound bank
229
230
  const sf = await fs.readFile(args[0]);
230
231
  const mid = await fs.readFile(args[1]);
232
+ // Parse the MIDI and sound bank
231
233
  const midi = BasicMIDI.fromArrayBuffer(mid.buffer);
232
- const sampleRate = 44100;
233
- const sampleCount = Math.ceil(44100 * (midi.duration + 2));
234
+ const soundBank = SoundBankLoader.fromArrayBuffer(sf.buffer);
235
+
236
+ // Initialize the synthesizer
237
+ const sampleRate = 48000;
234
238
  const synth = new SpessaSynthProcessor(sampleRate, {
235
- enableEventSystem: false,
236
- enableEffects: false
239
+ enableEventSystem: false
237
240
  });
238
- synth.soundBankManager.addSoundBank(
239
- SoundBankLoader.fromArrayBuffer(sf.buffer),
240
- "main"
241
- );
241
+ synth.soundBankManager.addSoundBank(soundBank, "main");
242
242
  await synth.processorInitialized;
243
+ // Enable uncapped voice count
244
+ synth.setMasterParameter("autoAllocateVoices", true);
245
+
246
+ // Initialize the sequencer
243
247
  const seq = new SpessaSynthSequencer(synth);
244
248
  seq.loadNewSongList([midi]);
245
249
  seq.play();
246
250
 
251
+ // Prepare the output buffers
252
+ const sampleCount = Math.ceil(sampleRate * (midi.duration + 2));
247
253
  const outLeft = new Float32Array(sampleCount);
248
254
  const outRight = new Float32Array(sampleCount);
249
255
  const start = performance.now();
@@ -252,13 +258,12 @@ let filledSamples = 0;
252
258
  const BUFFER_SIZE = 128;
253
259
  let i = 0;
254
260
  const durationRounded = Math.floor(seq.midiData!.duration * 100) / 100;
255
- const outputArray = [outLeft, outRight];
256
261
  while (filledSamples < sampleCount) {
257
262
  // Process sequencer
258
263
  seq.processTick();
259
264
  // Render
260
265
  const bufferSize = Math.min(BUFFER_SIZE, sampleCount - filledSamples);
261
- synth.renderAudio(outputArray, [], [], filledSamples, bufferSize);
266
+ synth.process(outLeft, outRight, filledSamples, bufferSize);
262
267
  filledSamples += bufferSize;
263
268
  i++;
264
269
  // Log progress
@@ -282,6 +287,8 @@ await fs.writeFile(args[2], new Uint8Array(wave));
282
287
  console.info(`File written to ${args[2]}`);
283
288
  ```
284
289
 
290
+ Read more in the [documentation](https://spessasus.github.io/spessasynth_core)
291
+
285
292
  ### Building
286
293
 
287
294
  To build the NPM package, do:
@@ -301,6 +308,7 @@ Licensed under the Apache-2.0 License.
301
308
  #### Legal
302
309
 
303
310
  This project is in no way endorsed or otherwise affiliated with the MIDI Manufacturers Association,
304
- Creative Technology Ltd. or E-mu Systems, Inc., or any other organization mentioned.
311
+ Roland Corporation, Yamaha Corporation, Creative Technology Ltd. or E-mu Systems, Inc.,
312
+ or any other organization mentioned.
305
313
  SoundFont® is a registered trademark of Creative Technology Ltd.
306
314
  All other trademarks are the property of their respective owners.
package/dist/index.d.ts CHANGED
@@ -2714,8 +2714,6 @@ declare function dataEntryFine(this: MIDIChannel, dataValue: number): void;
2714
2714
  * midiControllers table and handling special cases like bank select,
2715
2715
  * data entry, and sustain pedal. It also computes modulators for all voices
2716
2716
  * in the channel based on the controller change.
2717
- * If the controller number is greater than 127, it is treated as a channel
2718
- * configuration controller, and the `force` parameter must be set to true
2719
2717
  * to allow changes.
2720
2718
  */
2721
2719
  declare function controllerChange(this: MIDIChannel, controllerNumber: MIDIController, controllerValue: number, sendEvent?: boolean): void;
@@ -3063,10 +3061,9 @@ declare class SynthesizerCore {
3063
3061
  * Processes a raw MIDI message.
3064
3062
  * @param message The message to process.
3065
3063
  * @param channelOffset The channel offset for the message.
3066
- * @param force If true, forces the message to be processed.
3067
3064
  * @param options Additional options for scheduling the message.
3068
3065
  */
3069
- processMessage(message: Uint8Array | number[], channelOffset?: number, force?: boolean, options?: SynthMethodOptions): void;
3066
+ processMessage(message: Uint8Array | number[], channelOffset?: number, options?: SynthMethodOptions): void;
3070
3067
  destroySynthProcessor(): void;
3071
3068
  /**
3072
3069
  * @param channel channel to get voices for
@@ -3654,10 +3651,9 @@ declare class SpessaSynthProcessor {
3654
3651
  * Processes a raw MIDI message.
3655
3652
  * @param message The message to process.
3656
3653
  * @param channelOffset The channel offset for the message.
3657
- * @param force If true, forces the message to be processed.
3658
3654
  * @param options Additional options for scheduling the message.
3659
3655
  */
3660
- readonly processMessage: (message: Uint8Array | number[], channelOffset?: number, force?: boolean, options?: SynthMethodOptions) => void;
3656
+ readonly processMessage: (message: Uint8Array | number[], channelOffset?: number, options?: SynthMethodOptions) => void;
3661
3657
  /**
3662
3658
  * Core synthesis engine.
3663
3659
  */
package/dist/index.js CHANGED
@@ -4773,7 +4773,6 @@ setResetValue(midiControllers.vibratoDepth, 64);
4773
4773
  setResetValue(midiControllers.vibratoDelay, 64);
4774
4774
  setResetValue(midiControllers.generalPurposeController6, 64);
4775
4775
  setResetValue(midiControllers.generalPurposeController8, 64);
4776
- setResetValue(midiControllers.reverbDepth, 40);
4777
4776
  setResetValue(midiControllers.registeredParameterLSB, 127);
4778
4777
  setResetValue(midiControllers.registeredParameterMSB, 127);
4779
4778
  setResetValue(midiControllers.nonRegisteredParameterLSB, 127);
@@ -9798,6 +9797,7 @@ function dataEntryCoarse(dataCoarse) {
9798
9797
  break;
9799
9798
  }
9800
9799
  // Process NRPNs
9800
+ case dataEntryStates.NRPCoarse:
9801
9801
  case dataEntryStates.NRPFine: {
9802
9802
  const paramCoarse = this.midiControllers[midiControllers.nonRegisteredParameterMSB] >> 7;
9803
9803
  const paramFine = this.midiControllers[midiControllers.nonRegisteredParameterLSB] >> 7;
@@ -16875,21 +16875,19 @@ var SynthesizerCore = class {
16875
16875
  * Processes a raw MIDI message.
16876
16876
  * @param message The message to process.
16877
16877
  * @param channelOffset The channel offset for the message.
16878
- * @param force If true, forces the message to be processed.
16879
16878
  * @param options Additional options for scheduling the message.
16880
16879
  */
16881
- processMessage(message, channelOffset = 0, force = false, options = DEFAULT_SYNTH_METHOD_OPTIONS) {
16880
+ processMessage(message, channelOffset = 0, options = DEFAULT_SYNTH_METHOD_OPTIONS) {
16882
16881
  const time = options.time;
16883
16882
  if (time > this.currentTime) {
16884
16883
  this.eventQueue.push({
16885
16884
  message,
16886
16885
  channelOffset,
16887
- force,
16888
16886
  time
16889
16887
  });
16890
16888
  this.eventQueue.sort((e1, e2) => e1.time - e2.time);
16891
16889
  } else {
16892
- this.processMessageInternal(message, channelOffset, force);
16890
+ this.processMessageInternal(message, channelOffset);
16893
16891
  }
16894
16892
  }
16895
16893
  destroySynthProcessor() {
@@ -17050,11 +17048,7 @@ var SynthesizerCore = class {
17050
17048
  while (this.eventQueue[0]?.time <= time) {
17051
17049
  const q = this.eventQueue.shift();
17052
17050
  if (q) {
17053
- this.processMessageInternal(
17054
- q.message,
17055
- q.channelOffset,
17056
- q.force
17057
- );
17051
+ this.processMessageInternal(q.message, q.channelOffset);
17058
17052
  }
17059
17053
  }
17060
17054
  }
@@ -17567,7 +17561,7 @@ var SynthesizerCore = class {
17567
17561
  const p = new proc(this.sampleRate);
17568
17562
  this.insertionEffects.set(p.type, p);
17569
17563
  }
17570
- processMessageInternal(message, channelOffset, force) {
17564
+ processMessageInternal(message, channelOffset) {
17571
17565
  const statusByteData = getEvent(message[0]);
17572
17566
  const channelNumber = statusByteData.channel + channelOffset;
17573
17567
  switch (statusByteData.status) {
@@ -17581,11 +17575,7 @@ var SynthesizerCore = class {
17581
17575
  break;
17582
17576
  }
17583
17577
  case midiMessageTypes.noteOff: {
17584
- if (force) {
17585
- this.midiChannels[channelNumber].killNote(message[1]);
17586
- } else {
17587
- this.noteOff(channelNumber, message[1]);
17588
- }
17578
+ this.noteOff(channelNumber, message[1]);
17589
17579
  break;
17590
17580
  }
17591
17581
  case midiMessageTypes.pitchWheel: {
@@ -22357,7 +22347,6 @@ var SpessaSynthProcessor = class {
22357
22347
  * Processes a raw MIDI message.
22358
22348
  * @param message The message to process.
22359
22349
  * @param channelOffset The channel offset for the message.
22360
- * @param force If true, forces the message to be processed.
22361
22350
  * @param options Additional options for scheduling the message.
22362
22351
  */
22363
22352
  processMessage;