spessasynth_lib 3.23.0 → 3.23.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.
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * @param zone {BasicInstrumentZone}
3
+ * @param globalZone {BasicInstrumentZone}
3
4
  * @this {BasicSoundFont}
4
5
  * @returns {IndexedByteArray}
5
6
  */
6
- export function writeDLSRegion(this: BasicSoundFont, zone: BasicInstrumentZone): IndexedByteArray;
7
+ export function writeDLSRegion(this: BasicSoundFont, zone: BasicInstrumentZone, globalZone: BasicInstrumentZone): IndexedByteArray;
7
8
  import { IndexedByteArray } from "../../../utils/indexed_array.js";
package/README.md CHANGED
@@ -105,7 +105,7 @@ document.getElementById("button").onclick = async () =>
105
105
  - **Variable compression quality:** You choose between file size and quality!
106
106
  - **Compression preserving:** Avoid decompressing and recompressing uncompressed samples for minimal quality loss!
107
107
 
108
- #### Read and play DLS Level 1 or 2 files
108
+ #### Read and write DLS Level 1 or 2 files
109
109
  - Read DLS (DownLoadable Sounds) files as SF2 files!
110
110
  - **Works like a normal soundfont:** *Saving it as sf2 is still [just one function!](https://github.com/spessasus/SpessaSynth/wiki/SoundFont2-Class#write)*
111
111
  - Converts articulators to both **modulators** and **generators**!
@@ -113,6 +113,7 @@ document.getElementById("button").onclick = async () =>
113
113
  - **Covers special generator cases:** *such as modLfoToPitch*!
114
114
  - **Correct volume:** *looking at you, Viena and gm.sf2!*
115
115
  - Support built right into the synthesizer!
116
+ - **Convert SF2 to DLS:** [with limitations](https://github.com/spessasus/SpessaSynth/wiki/DLS-Conversion-Problem);
116
117
 
117
118
  ### Export MIDI as WAV
118
119
  - Save the MIDI file as WAV audio!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_lib",
3
- "version": "3.23.0",
3
+ "version": "3.23.2",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "browser": "index.js",
6
6
  "types": "@types/index.d.ts",
@@ -58,7 +58,7 @@ export class BasicZone
58
58
  */
59
59
  getGeneratorValue(generatorType, notFoundValue)
60
60
  {
61
- return this.generators.find(g => g.generatorType === generatorType)?.generatorValue || notFoundValue;
61
+ return this.generators.find(g => g.generatorType === generatorType)?.generatorValue ?? notFoundValue;
62
62
  }
63
63
  }
64
64
 
@@ -70,7 +70,7 @@ export function writeIns(preset)
70
70
  {
71
71
  if (!z.isGlobal)
72
72
  {
73
- arrs.push(writeDLSRegion.apply(this, [z]));
73
+ arrs.push(writeDLSRegion.apply(this, [z, globalZone]));
74
74
  }
75
75
  return arrs;
76
76
  }, []));
@@ -7,10 +7,11 @@ import { writeArticulator } from "./art2.js";
7
7
 
8
8
  /**
9
9
  * @param zone {BasicInstrumentZone}
10
+ * @param globalZone {BasicInstrumentZone}
10
11
  * @this {BasicSoundFont}
11
12
  * @returns {IndexedByteArray}
12
13
  */
13
- export function writeDLSRegion(zone)
14
+ export function writeDLSRegion(zone, globalZone)
14
15
  {
15
16
  // region header
16
17
  const rgnhData = new IndexedByteArray(14);
@@ -32,10 +33,24 @@ export function writeDLSRegion(zone)
32
33
  rgnhData
33
34
  );
34
35
 
36
+ let rootKey = zone.getGeneratorValue(generatorTypes.overridingRootKey, zone.sample.samplePitch);
37
+
38
+ // a lot of soundfonts like to set scaletuning to 0 in drums and keep the key at 60
39
+ // since we implement scaletuning via a dls articulator and fluid doesn't support these,
40
+ // change the root key here
41
+ const scaleTuning = zone.getGeneratorValue(
42
+ generatorTypes.scaleTuning,
43
+ globalZone.getGeneratorValue(generatorTypes.scaleTuning, 100)
44
+ );
45
+ if (scaleTuning === 0 && zone.keyRange.max - zone.keyRange.min === 0)
46
+ {
47
+ rootKey = zone.keyRange.min;
48
+ }
49
+
35
50
  // wavesample (Wsmp)
36
51
  const wsmp = writeWavesample(
37
52
  zone.sample,
38
- zone.getGeneratorValue(generatorTypes.overridingRootKey, zone.sample.samplePitch),
53
+ rootKey,
39
54
  zone.getGeneratorValue(
40
55
  generatorTypes.fineTune,
41
56
  0
@@ -32,17 +32,31 @@ export function writeDLSSample(sample)
32
32
  sample.sampleLoopEndIndex,
33
33
  1
34
34
  );
35
-
36
35
  const audio = sample.getAudioData();
37
- const data16 = new Int16Array(audio.length);
38
- for (let i = 0; i < audio.length; i++)
36
+ let data;
37
+ // if sample is compressed, getRawData cannot be used
38
+ if (sample.isCompressed)
39
39
  {
40
- data16[i] = audio[i] * 32768;
40
+ const data16 = new Int16Array(audio.length);
41
+
42
+ for (let i = 0; i < audio.length; i++)
43
+ {
44
+ data16[i] = audio[i] * 32768;
45
+ }
46
+
47
+
48
+ data = writeRIFFOddSize(
49
+ "data",
50
+ new IndexedByteArray(data16.buffer)
51
+ );
52
+ }
53
+ else
54
+ {
55
+ data = writeRIFFOddSize(
56
+ "data",
57
+ sample.getRawData()
58
+ );
41
59
  }
42
- const data = writeRIFFOddSize(
43
- "data",
44
- new IndexedByteArray(data16.buffer)
45
- );
46
60
 
47
61
  const inam = writeRIFFOddSize(
48
62
  "INAM",
@@ -47,7 +47,7 @@ export function writeDLS()
47
47
  SpessaSynthGroupEnd();
48
48
 
49
49
  // write ptbl
50
- const ptblData = new IndexedByteArray(8 + 8 * ptblOffsets.length);
50
+ const ptblData = new IndexedByteArray(8 + 4 * ptblOffsets.length);
51
51
  writeDword(ptblData, 8);
52
52
  writeDword(ptblData, ptblOffsets.length);
53
53
  for (const offset of ptblOffsets)
@@ -2,6 +2,8 @@ import { writeDword, writeWord } from "../../../utils/byte_functions/little_endi
2
2
  import { IndexedByteArray } from "../../../utils/indexed_array.js";
3
3
  import { writeRIFFOddSize } from "../riff_chunk.js";
4
4
 
5
+ const WSMP_SIZE = 20;
6
+
5
7
  /**
6
8
  * @param sample {BasicSample}
7
9
  * @param rootKey {number}
@@ -21,9 +23,9 @@ export function writeWavesample(
21
23
  loopEnd,
22
24
  loopingMode)
23
25
  {
24
- // fixed size because always one loop
25
- const wsmpData = new IndexedByteArray(36);
26
- writeDword(wsmpData, 20); // cbSize
26
+ let loopCount = loopingMode === 0 ? 0 : 1;
27
+ const wsmpData = new IndexedByteArray(WSMP_SIZE + loopCount * 16);
28
+ writeDword(wsmpData, WSMP_SIZE); // cbSize
27
29
  // usUnityNote (apply root pitch here)
28
30
  writeWord(wsmpData, rootKey);
29
31
  // sFineTune
@@ -39,7 +41,6 @@ export function writeWavesample(
39
41
  writeDword(wsmpData, 0);
40
42
 
41
43
  const loopSize = loopEnd - loopStart;
42
- let loopCount = 1;
43
44
  let ulLoopType = 0;
44
45
  switch (loopingMode)
45
46
  {
@@ -63,10 +64,13 @@ export function writeWavesample(
63
64
 
64
65
  // cSampleLoops
65
66
  writeDword(wsmpData, loopCount);
66
- writeDword(wsmpData, 16); // cbSize
67
- writeDword(wsmpData, ulLoopType);
68
- writeDword(wsmpData, loopStart);
69
- writeDword(wsmpData, loopSize);
67
+ if (loopCount === 1)
68
+ {
69
+ writeDword(wsmpData, 16); // cbSize
70
+ writeDword(wsmpData, ulLoopType);
71
+ writeDword(wsmpData, loopStart);
72
+ writeDword(wsmpData, loopSize);
73
+ }
70
74
  return writeRIFFOddSize(
71
75
  "wsmp",
72
76
  wsmpData