spessasynth_lib 3.24.28 → 3.24.35

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.
Files changed (74) hide show
  1. package/README.md +1 -1
  2. package/external_midi/web_midi_link.js +1 -1
  3. package/index.js +8 -52
  4. package/midi_parser/basic_midi.js +15 -30
  5. package/midi_parser/midi_builder.js +6 -6
  6. package/midi_parser/midi_data.js +2 -2
  7. package/midi_parser/midi_editor.js +15 -15
  8. package/midi_parser/midi_loader.js +3 -3
  9. package/midi_parser/midi_message.js +2 -2
  10. package/midi_parser/midi_sequence.js +32 -4
  11. package/midi_parser/midi_writer.js +8 -8
  12. package/midi_parser/rmidi_writer.js +20 -21
  13. package/midi_parser/used_keys_loaded.js +8 -6
  14. package/package.json +1 -1
  15. package/sequencer/sequencer.js +6 -6
  16. package/sequencer/worklet_sequencer/events.js +2 -1
  17. package/sequencer/worklet_sequencer/play.js +2 -3
  18. package/sequencer/worklet_sequencer/process_event.js +1 -1
  19. package/sequencer/worklet_sequencer/process_tick.js +1 -1
  20. package/sequencer/worklet_sequencer/sequencer_message.js +1 -1
  21. package/sequencer/worklet_sequencer/song_control.js +6 -7
  22. package/sequencer/worklet_sequencer/worklet_sequencer.js +2 -1
  23. package/soundfont/basic_soundfont/basic_soundfont.js +197 -13
  24. package/soundfont/basic_soundfont/generator.js +18 -16
  25. package/soundfont/basic_soundfont/modulator.js +3 -0
  26. package/soundfont/basic_soundfont/riff_chunk.js +2 -2
  27. package/soundfont/basic_soundfont/write_dls/art2.js +1 -1
  28. package/soundfont/basic_soundfont/write_dls/combine_zones.js +12 -10
  29. package/soundfont/basic_soundfont/write_dls/ins.js +2 -2
  30. package/soundfont/basic_soundfont/write_dls/lins.js +1 -1
  31. package/soundfont/basic_soundfont/write_dls/modulator_converter.js +9 -9
  32. package/soundfont/basic_soundfont/write_dls/rgn2.js +5 -5
  33. package/soundfont/basic_soundfont/write_dls/wave.js +2 -1
  34. package/soundfont/basic_soundfont/write_dls/write_dls.js +1 -1
  35. package/soundfont/basic_soundfont/write_dls/wsmp.js +2 -2
  36. package/soundfont/basic_soundfont/write_dls/wvpl.js +1 -1
  37. package/soundfont/basic_soundfont/write_sf2/ibag.js +2 -2
  38. package/soundfont/basic_soundfont/write_sf2/igen.js +20 -17
  39. package/soundfont/basic_soundfont/write_sf2/imod.js +3 -3
  40. package/soundfont/basic_soundfont/write_sf2/inst.js +2 -2
  41. package/soundfont/basic_soundfont/write_sf2/pbag.js +2 -2
  42. package/soundfont/basic_soundfont/write_sf2/pgen.js +21 -18
  43. package/soundfont/basic_soundfont/write_sf2/phdr.js +2 -2
  44. package/soundfont/basic_soundfont/write_sf2/pmod.js +2 -2
  45. package/soundfont/basic_soundfont/write_sf2/sdta.js +1 -1
  46. package/soundfont/basic_soundfont/write_sf2/shdr.js +1 -1
  47. package/soundfont/basic_soundfont/write_sf2/write.js +7 -6
  48. package/soundfont/dls/articulator_converter.js +1 -0
  49. package/soundfont/dls/dls_destinations.js +3 -3
  50. package/soundfont/dls/dls_preset.js +1 -1
  51. package/soundfont/dls/dls_soundfont.js +7 -7
  52. package/soundfont/dls/read_instrument.js +1 -1
  53. package/soundfont/dls/read_region.js +1 -0
  54. package/soundfont/dls/read_samples.js +4 -5
  55. package/soundfont/load_soundfont.js +1 -1
  56. package/soundfont/read_sf2/presets.js +1 -1
  57. package/soundfont/read_sf2/soundfont.js +5 -5
  58. package/synthetizer/audio_effects/effects_config.js +3 -3
  59. package/synthetizer/audio_effects/fancy_chorus.js +7 -7
  60. package/synthetizer/audio_effects/reverb.js +5 -5
  61. package/synthetizer/audio_effects/reverb_as_binary.js +15 -0
  62. package/synthetizer/audio_effects/reverb_buffer.min.js +1 -0
  63. package/synthetizer/synth_constants.js +14 -0
  64. package/synthetizer/synthetizer.js +1 -17
  65. package/synthetizer/worklet_processor.min.js +14 -12
  66. package/synthetizer/worklet_system/main_processor.js +2 -2
  67. package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +1 -1
  68. package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +1 -1
  69. package/synthetizer/worklet_system/worklet_methods/create_worklet_channel.js +2 -1
  70. package/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js +2 -2
  71. package/synthetizer/worklet_system/worklet_processor.js +1 -1
  72. package/soundfont/basic_soundfont/write_sf2/soundfont_trimmer.js +0 -185
  73. package/synthetizer/audio_effects/impulse_response_2.flac +0 -0
  74. package/synthetizer/worklet_system/minify_processor.sh +0 -4
package/README.md CHANGED
@@ -111,7 +111,7 @@ Supported formats list:
111
111
  - **First note detection:** Skip unnecessary silence at the start by jumping to the first note!
112
112
  - **Lyrics support:** Both regular MIDI and .kar files!
113
113
  - **[Write MIDI files from scratch](https://github.com/spessasus/SpessaSynth/wiki/Creating-MIDI-Files)**
114
- - **Easy saving:** Save with just [one function!](https://github.com/spessasus/SpessaSynth/wiki/Writing-MIDI-Files#writemidifile)
114
+ - **Easy saving:** Save with just [one function!](https://github.com/spessasus/SpessaSynth/wiki/Writing-MIDI-Files#writemidi)
115
115
 
116
116
  #### Read and write [RMID files with embedded SF2 soundfonts](https://github.com/spessasus/sf2-rmidi-specification#readme)
117
117
  - **[Level 4](https://github.com/spessasus/sf2-rmidi-specification#level-4) compliance:** Reads and writes *everything!*
@@ -8,7 +8,7 @@ import { SpessaSynthInfo } from "../utils/loggin.js";
8
8
  * https://www.g200kg.com/en/docs/webmidilink/
9
9
  */
10
10
 
11
- export class WebMidiLinkHandler
11
+ export class WebMIDILinkHandler
12
12
  {
13
13
  /**
14
14
  * @param synth {Synthetizer} the synth to play to
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Import modules
2
2
  import { loadSoundFont } from "./soundfont/load_soundfont.js";
3
- import { BasicSoundFont } from "./soundfont/basic_soundfont/basic_soundfont.js";
3
+ import { BasicSoundBank } from "./soundfont/basic_soundfont/basic_soundfont.js";
4
4
  import { BasicSample } from "./soundfont/basic_soundfont/basic_sample.js";
5
5
  import { BasicInstrumentZone } from "./soundfont/basic_soundfont/basic_zones.js";
6
6
  import { BasicInstrument } from "./soundfont/basic_soundfont/basic_instrument.js";
@@ -9,40 +9,20 @@ import { Modulator } from "./soundfont/basic_soundfont/modulator.js";
9
9
  import { BasicPresetZone } from "./soundfont/basic_soundfont/basic_zones.js";
10
10
  import { BasicPreset } from "./soundfont/basic_soundfont/basic_preset.js";
11
11
  import { BasicMIDI } from "./midi_parser/basic_midi.js";
12
- import { MidiMessage } from "./midi_parser/midi_message.js";
12
+ import { MIDIMessage } from "./midi_parser/midi_message.js";
13
13
  import { MIDI } from './midi_parser/midi_loader.js';
14
14
  import { RMIDINFOChunks } from "./midi_parser/rmidi_writer.js";
15
- import { MIDIticksToSeconds } from './midi_parser/basic_midi.js';
16
15
  import { MIDIBuilder } from "./midi_parser/midi_builder.js";
17
16
  import { Synthetizer, VOICE_CAP, DEFAULT_PERCUSSION } from './synthetizer/synthetizer.js';
18
17
  import { Sequencer } from './sequencer/sequencer.js';
19
18
  import { IndexedByteArray } from './utils/indexed_array.js';
20
- import { writeMIDIFile } from './midi_parser/midi_writer.js';
21
- import { writeRMIDI } from './midi_parser/rmidi_writer.js'
22
- import { applySnapshotToMIDI, modifyMIDI } from './midi_parser/midi_editor.js';
23
19
  import { audioBufferToWav } from './utils/buffer_to_wav.js';
24
- import {
25
- SpessaSynthInfo,
26
- SpessaSynthWarn,
27
- SpessaSynthGroupCollapsed,
28
- SpessaSynthGroupEnd,
29
- SpessaSynthTable,
30
- SpessaSynthLogging,
31
- SpessaSynthGroup
32
- } from './utils/loggin.js';
20
+ import { SpessaSynthLogging } from './utils/loggin.js';
33
21
  import { midiControllers, messageTypes } from './midi_parser/midi_message.js';
34
22
  import { MIDIDeviceHandler} from "./external_midi/midi_handler.js";
35
- import { WebMidiLinkHandler} from "./external_midi/web_midi_link.js";
36
- import { formatTime, formatTitle, consoleColors, arrayToHexString } from './utils/other.js';
37
- import { readBytesAsUintBigEndian } from './utils/byte_functions/big_endian.js';
38
- import { readBytesAsString } from "./utils/byte_functions/string.js";
39
- import { readLittleEndian } from "./utils/byte_functions/little_endian.js";
40
- import { readVariableLengthQuantity } from "./utils/byte_functions/variable_length_quantity.js";
23
+ import { WebMIDILinkHandler} from "./external_midi/web_midi_link.js";
41
24
  import { modulatorSources } from "./soundfont/basic_soundfont/modulator.js";
42
- import { NON_CC_INDEX_OFFSET } from "./synthetizer/worklet_system/worklet_utilities/controller_tables.js";
43
- import { ALL_CHANNELS_OR_DIFFERENT_ACTION } from './synthetizer/worklet_system/message_protocol/worklet_message.js';
44
25
  import { DEFAULT_SYNTH_CONFIG } from "./synthetizer/audio_effects/effects_config.js";
45
- import { trimSoundfont} from "./soundfont/basic_soundfont/write_sf2/soundfont_trimmer.js";
46
26
  import { WORKLET_URL_ABSOLUTE } from './synthetizer/worklet_url.js';
47
27
  import { SynthesizerSnapshot} from "./synthetizer/worklet_system/snapshot/synthesizer_snapshot.js";
48
28
  import { ChannelSnapshot } from "./synthetizer/worklet_system/snapshot/channel_snapshot.js";
@@ -59,7 +39,7 @@ export {
59
39
  DEFAULT_SYNTH_CONFIG,
60
40
 
61
41
  // SoundFont
62
- BasicSoundFont,
42
+ BasicSoundBank,
63
43
  BasicSample,
64
44
  BasicInstrumentZone,
65
45
  BasicInstrument,
@@ -68,46 +48,22 @@ export {
68
48
  Generator,
69
49
  Modulator,
70
50
  loadSoundFont,
71
- trimSoundfont,
72
51
  modulatorSources,
73
52
 
74
53
  // MIDI
75
54
  MIDI,
76
55
  BasicMIDI,
77
56
  MIDIBuilder,
78
- IndexedByteArray,
79
- MidiMessage,
80
- writeMIDIFile,
81
- writeRMIDI,
82
- applySnapshotToMIDI,
83
- modifyMIDI,
84
- MIDIticksToSeconds,
57
+ MIDIMessage,
85
58
  RMIDINFOChunks,
86
59
 
87
60
  // Utilities
61
+ IndexedByteArray,
88
62
  audioBufferToWav,
89
63
  SpessaSynthLogging,
90
- SpessaSynthGroup,
91
- SpessaSynthTable,
92
- SpessaSynthGroupEnd,
93
- SpessaSynthInfo,
94
- SpessaSynthWarn,
95
- SpessaSynthGroupCollapsed,
96
64
  midiControllers,
97
65
  messageTypes,
98
66
  MIDIDeviceHandler,
99
- WebMidiLinkHandler,
100
- arrayToHexString,
101
- consoleColors,
102
- formatTitle,
103
- formatTime,
104
-
105
- readBytesAsUintBigEndian,
106
- readBytesAsString,
107
- readLittleEndian,
108
- readVariableLengthQuantity,
109
-
110
- NON_CC_INDEX_OFFSET,
111
- ALL_CHANNELS_OR_DIFFERENT_ACTION,
67
+ WebMIDILinkHandler,
112
68
  WORKLET_URL_ABSOLUTE
113
69
  };
@@ -4,13 +4,17 @@ import { messageTypes } from "./midi_message.js";
4
4
  import { readBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js";
5
5
  import { SpessaSynthGroup, SpessaSynthGroupEnd, SpessaSynthInfo } from "../utils/loggin.js";
6
6
  import { consoleColors, formatTitle, sanitizeKarLyrics } from "../utils/other.js";
7
+ import { writeMIDI } from "./midi_writer.js";
8
+ import { applySnapshotToMIDI, modifyMIDI } from "./midi_editor.js";
9
+ import { writeRMIDI } from "./rmidi_writer.js";
10
+ import { getUsedProgramsAndKeys } from "./used_keys_loaded.js";
7
11
 
8
12
  /**
9
13
  * BasicMIDI is the base of a complete MIDI file, used by the sequencer internally.
10
14
  * BasicMIDI is not available on the main thread, as it contains the actual track data which can be large.
11
15
  * It can be accessed by calling getMIDI() on the Sequencer.
12
16
  */
13
- export class BasicMIDI extends MIDISequenceData
17
+ class BasicMIDI extends MIDISequenceData
14
18
  {
15
19
 
16
20
  /**
@@ -21,8 +25,8 @@ export class BasicMIDI extends MIDISequenceData
21
25
 
22
26
  /**
23
27
  * The actual track data of the MIDI file, represented as an array of tracks.
24
- * Tracks are arrays of MidiMessage objects.
25
- * @type {MidiMessage[][]}
28
+ * Tracks are arrays of MIDIMessage objects.
29
+ * @type {MIDIMessage[][]}
26
30
  */
27
31
  tracks = [];
28
32
 
@@ -560,7 +564,7 @@ export class BasicMIDI extends MIDISequenceData
560
564
  * The total playback time, in seconds
561
565
  * @type {number}
562
566
  */
563
- this.duration = MIDIticksToSeconds(this.lastVoiceEventTick, this);
567
+ this.duration = this.MIDIticksToSeconds(this.lastVoiceEventTick);
564
568
 
565
569
  SpessaSynthInfo("%cSuccess!", consoleColors.recognized);
566
570
  SpessaSynthGroupEnd();
@@ -581,29 +585,10 @@ export class BasicMIDI extends MIDISequenceData
581
585
  }
582
586
  }
583
587
 
584
- /**
585
- * Converts ticks to time in seconds
586
- * @param ticks {number} time in MIDI ticks
587
- * @param mid {BasicMIDI|MidiData} the MIDI
588
- * @returns {number} time in seconds
589
- */
590
- export function MIDIticksToSeconds(ticks, mid)
591
- {
592
- let totalSeconds = 0;
593
-
594
- while (ticks > 0)
595
- {
596
- // tempo changes are reversed, so the first element is the last tempo change
597
- // and the last element is the first tempo change
598
- // (always at tick 0 and tempo 120)
599
- // find the last tempo change that has occurred
600
- let tempo = mid.tempoChanges.find(v => v.ticks < ticks);
601
-
602
- // calculate the difference and tempo time
603
- let timeSinceLastTempo = ticks - tempo.ticks;
604
- totalSeconds += (timeSinceLastTempo * 60) / (tempo.tempo * mid.timeDivision);
605
- ticks -= timeSinceLastTempo;
606
- }
607
-
608
- return totalSeconds;
609
- }
588
+ BasicMIDI.prototype.writeMIDI = writeMIDI;
589
+ BasicMIDI.prototype.modifyMIDI = modifyMIDI;
590
+ BasicMIDI.prototype.applySnapshotToMIDI = applySnapshotToMIDI;
591
+ BasicMIDI.prototype.writeRMIDI = writeRMIDI;
592
+ BasicMIDI.prototype.getUsedProgramsAndKeys = getUsedProgramsAndKeys;
593
+
594
+ export { BasicMIDI };
@@ -1,5 +1,5 @@
1
1
  import { BasicMIDI } from "./basic_midi.js";
2
- import { messageTypes, MidiMessage } from "./midi_message.js";
2
+ import { messageTypes, MIDIMessage } from "./midi_message.js";
3
3
  import { IndexedByteArray } from "../utils/indexed_array.js";
4
4
  import { SpessaSynthWarn } from "../utils/loggin.js";
5
5
 
@@ -59,7 +59,7 @@ export class MIDIBuilder extends BasicMIDI
59
59
  }
60
60
  this.tracks.push([]);
61
61
  this.tracks[this.tracksAmount - 1].push(
62
- new MidiMessage(0, messageTypes.endOfTrack, new IndexedByteArray(0))
62
+ new MIDIMessage(0, messageTypes.endOfTrack, new IndexedByteArray(0))
63
63
  );
64
64
  this.addEvent(0, this.tracksAmount - 1, messageTypes.trackName, this.encoder.encode(name));
65
65
  this.addEvent(0, this.tracksAmount - 1, messageTypes.midiPort, [port]);
@@ -83,15 +83,15 @@ export class MIDIBuilder extends BasicMIDI
83
83
  SpessaSynthWarn("The EndOfTrack is added automatically. Ignoring!");
84
84
  return;
85
85
  }
86
- // remove end of track
86
+ // remove the end of track
87
87
  this.tracks[track].pop();
88
- this.tracks[track].push(new MidiMessage(
88
+ this.tracks[track].push(new MIDIMessage(
89
89
  ticks,
90
90
  event,
91
91
  new IndexedByteArray(eventData)
92
92
  ));
93
- // add end of track
94
- this.tracks[track].push(new MidiMessage(
93
+ // add the end of track
94
+ this.tracks[track].push(new MIDIMessage(
95
95
  ticks,
96
96
  messageTypes.endOfTrack,
97
97
  new IndexedByteArray(0)
@@ -5,7 +5,7 @@ import { MIDISequenceData } from "./midi_sequence.js";
5
5
  * Use getMIDI() to get the actual sequence.
6
6
  * This class contains all properties that MIDI does, except for tracks and the embedded soundfont.
7
7
  */
8
- export class MidiData extends MIDISequenceData
8
+ export class MIDIData extends MIDISequenceData
9
9
  {
10
10
 
11
11
  /**
@@ -53,7 +53,7 @@ export class MidiData extends MIDISequenceData
53
53
 
54
54
  /**
55
55
  * Temporary MIDI data used when the MIDI is not loaded.
56
- * @type {MidiData}
56
+ * @type {MIDIData}
57
57
  */
58
58
  export const DUMMY_MIDI_DATA = {
59
59
  duration: 99999,
@@ -1,18 +1,18 @@
1
- import { messageTypes, midiControllers, MidiMessage } from "./midi_message.js";
1
+ import { messageTypes, midiControllers, MIDIMessage } from "./midi_message.js";
2
2
  import { IndexedByteArray } from "../utils/indexed_array.js";
3
3
  import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from "../utils/loggin.js";
4
4
  import { consoleColors } from "../utils/other.js";
5
- import { DEFAULT_PERCUSSION } from "../synthetizer/synthetizer.js";
6
5
 
7
6
  import { customControllers } from "../synthetizer/worklet_system/worklet_utilities/controller_tables.js";
7
+ import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
8
8
 
9
9
  /**
10
10
  * @param ticks {number}
11
- * @returns {MidiMessage}
11
+ * @returns {MIDIMessage}
12
12
  */
13
13
  export function getGsOn(ticks)
14
14
  {
15
- return new MidiMessage(
15
+ return new MIDIMessage(
16
16
  ticks,
17
17
  messageTypes.systemExclusive,
18
18
  new IndexedByteArray([
@@ -35,11 +35,11 @@ export function getGsOn(ticks)
35
35
  * @param cc {number}
36
36
  * @param value {number}
37
37
  * @param ticks {number}
38
- * @returns {MidiMessage}
38
+ * @returns {MIDIMessage}
39
39
  */
40
40
  function getControllerChange(channel, cc, value, ticks)
41
41
  {
42
- return new MidiMessage(
42
+ return new MIDIMessage(
43
43
  ticks,
44
44
  messageTypes.controllerChange | (channel % 16),
45
45
  new IndexedByteArray([cc, value])
@@ -49,7 +49,7 @@ function getControllerChange(channel, cc, value, ticks)
49
49
  /**
50
50
  * @param channel {number}
51
51
  * @param ticks {number}
52
- * @returns {MidiMessage}
52
+ * @returns {MIDIMessage}
53
53
  */
54
54
  function getDrumChange(channel, ticks)
55
55
  {
@@ -70,7 +70,7 @@ function getDrumChange(channel, ticks)
70
70
  const sum = 0x40 + chanAddress + 0x15 + 0x01;
71
71
  const checksum = 128 - (sum % 128);
72
72
  // add system exclusive to enable drums
73
- return new MidiMessage(
73
+ return new MIDIMessage(
74
74
  ticks,
75
75
  messageTypes.systemExclusive,
76
76
  new IndexedByteArray([
@@ -110,20 +110,20 @@ function getDrumChange(channel, ticks)
110
110
  * Allows easy editing of the file by removing channels, changing programs,
111
111
  * changing controllers and transposing channels. Note that this modifies the MIDI in-place.
112
112
  *
113
- * @param {BasicMIDI} midi - The MIDI file to modify.
113
+ * @this {BasicMIDI}
114
114
  * @param {DesiredProgramChange[]} desiredProgramChanges - The programs to set on given channels.
115
115
  * @param {DesiredControllerChange[]} desiredControllerChanges - The controllers to set on given channels.
116
116
  * @param {number[]} desiredChannelsToClear - The channels to remove from the sequence.
117
117
  * @param {DesiredChanneltranspose[]} desiredChannelsToTranspose - The channels to transpose.
118
118
  */
119
119
  export function modifyMIDI(
120
- midi,
121
120
  desiredProgramChanges = [],
122
121
  desiredControllerChanges = [],
123
122
  desiredChannelsToClear = [],
124
123
  desiredChannelsToTranspose = []
125
124
  )
126
125
  {
126
+ const midi = this;
127
127
  SpessaSynthGroupCollapsed("%cApplying changes to the MIDI file...", consoleColors.info);
128
128
 
129
129
  /**
@@ -252,7 +252,7 @@ export function modifyMIDI(
252
252
  };
253
253
 
254
254
  /**
255
- * @param e {MidiMessage}
255
+ * @param e {MIDIMessage}
256
256
  * @param offset{number}
257
257
  */
258
258
  const addEventBefore = (e, offset = 0) =>
@@ -352,7 +352,7 @@ export function modifyMIDI(
352
352
  // the output event order is: drums -> lsb -> msb -> program change
353
353
 
354
354
  // add program change
355
- const programChange = new MidiMessage(
355
+ const programChange = new MIDIMessage(
356
356
  e.ticks,
357
357
  messageTypes.programChange | midiChannel,
358
358
  new IndexedByteArray([
@@ -527,10 +527,10 @@ export function modifyMIDI(
527
527
 
528
528
  /**
529
529
  * Modifies the sequence according to the locked presets and controllers in the given snapshot
530
- * @param midi {BasicMIDI}
530
+ * @this {BasicMIDI}
531
531
  * @param snapshot {SynthesizerSnapshot}
532
532
  */
533
- export function applySnapshotToMIDI(midi, snapshot)
533
+ export function applySnapshotToMIDI(snapshot)
534
534
  {
535
535
  /**
536
536
  * @type {{
@@ -600,5 +600,5 @@ export function applySnapshotToMIDI(midi, snapshot)
600
600
  });
601
601
  });
602
602
  });
603
- modifyMIDI(midi, programChanges, controllerChanges, channelsToClear, channelsToTranspose);
603
+ this.modifyMIDI(programChanges, controllerChanges, channelsToClear, channelsToTranspose);
604
604
  }
@@ -1,4 +1,4 @@
1
- import { dataBytesAmount, getChannel, MidiMessage } from "./midi_message.js";
1
+ import { dataBytesAmount, getChannel, MIDIMessage } from "./midi_message.js";
2
2
  import { IndexedByteArray } from "../utils/indexed_array.js";
3
3
  import { consoleColors } from "../utils/other.js";
4
4
  import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo, SpessaSynthWarn } from "../utils/loggin.js";
@@ -172,7 +172,7 @@ class MIDI extends BasicMIDI
172
172
  for (let i = 0; i < this.tracksAmount; i++)
173
173
  {
174
174
  /**
175
- * @type {MidiMessage[]}
175
+ * @type {MIDIMessage[]}
176
176
  */
177
177
  const track = [];
178
178
  const trackChunk = this.readMIDIChunk(fileByteArray);
@@ -262,7 +262,7 @@ class MIDI extends BasicMIDI
262
262
  trackChunk.data.currentIndex,
263
263
  trackChunk.data.currentIndex + eventDataLength
264
264
  ), 0);
265
- const event = new MidiMessage(totalTicks, statusByte, eventData);
265
+ const event = new MIDIMessage(totalTicks, statusByte, eventData);
266
266
  track.push(event);
267
267
  // advance the track chunk
268
268
  trackChunk.data.currentIndex += eventDataLength;
@@ -5,7 +5,7 @@ import { IndexedByteArray } from "../utils/indexed_array.js";
5
5
  * purpose: contains enums for midi events and controllers and functions to parse them
6
6
  */
7
7
 
8
- export class MidiMessage
8
+ export class MIDIMessage
9
9
  {
10
10
  /**
11
11
  * Absolute number of MIDI ticks from the start of the track.
@@ -249,6 +249,6 @@ export const dataBytesAmount = {
249
249
  0xA: 2, // note at
250
250
  0xB: 2, // cc change
251
251
  0xC: 1, // pg change
252
- 0xD: 1, // channel aftertouch
252
+ 0xD: 1, // channel after touch
253
253
  0xE: 2 // pitch wheel
254
254
  };
@@ -2,10 +2,10 @@
2
2
  * This is the base type for MIDI files. It contains all the "metadata" and information.
3
3
  * It extends to:
4
4
  * - BasicMIDI, which contains the actual track data of the MIDI file. Essentially the MIDI file itself.
5
- * - MidiData, which contains all properties that MIDI does, except for tracks and the embedded soundfont.
6
- * MidiData is the "shell" of the file which is available on the main thread at all times, containing the metadata.
5
+ * - MIDIData, which contains all properties that MIDI does, except for tracks and the embedded soundfont.
6
+ * MIDIData is the "shell" of the file which is available on the main thread at all times, containing the metadata.
7
7
  */
8
- export class MIDISequenceData
8
+ class MIDISequenceData
9
9
  {
10
10
  /**
11
11
  * The time division of the sequence, representing the number of ticks per beat.
@@ -143,4 +143,32 @@ export class MIDISequenceData
143
143
  * @type {boolean}
144
144
  */
145
145
  isKaraokeFile = false;
146
- }
146
+
147
+ /**
148
+ * Converts ticks to time in seconds
149
+ * @param ticks {number} time in MIDI ticks
150
+ * @returns {number} time in seconds
151
+ */
152
+ MIDIticksToSeconds(ticks)
153
+ {
154
+ let totalSeconds = 0;
155
+
156
+ while (ticks > 0)
157
+ {
158
+ // tempo changes are reversed, so the first element is the last tempo change
159
+ // and the last element is the first tempo change
160
+ // (always at tick 0 and tempo 120)
161
+ // find the last tempo change that has occurred
162
+ let tempo = this.tempoChanges.find(v => v.ticks < ticks);
163
+
164
+ // calculate the difference and tempo time
165
+ let timeSinceLastTempo = ticks - tempo.ticks;
166
+ totalSeconds += (timeSinceLastTempo * 60) / (tempo.tempo * this.timeDivision);
167
+ ticks -= timeSinceLastTempo;
168
+ }
169
+
170
+ return totalSeconds;
171
+ }
172
+ }
173
+
174
+ export { MIDISequenceData };
@@ -3,12 +3,12 @@ import { writeVariableLengthQuantity } from "../utils/byte_functions/variable_le
3
3
  import { writeBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js";
4
4
 
5
5
  /**
6
- * Exports the midi as a .mid file
7
- * @param midi {BasicMIDI} the midi to export
8
- * @returns {Uint8Array} the binary .mid file data
6
+ * Exports the midi as a standard MIDI file
7
+ * @this {BasicMIDI}
9
8
  */
10
- export function writeMIDIFile(midi)
9
+ export function writeMIDI()
11
10
  {
11
+ const midi = this;
12
12
  if (!midi.tracks)
13
13
  {
14
14
  throw new Error("MIDI has no tracks!");
@@ -24,7 +24,7 @@ export function writeMIDIFile(midi)
24
24
  let runningByte = undefined;
25
25
  for (const event of track)
26
26
  {
27
- // ticks stored in MIDI are absolute, but .mid wants relative. Convert them here.
27
+ // Ticks stored in MIDI are absolute, but SMF wants relative. Convert them here.
28
28
  const deltaTicks = event.ticks - currentTick;
29
29
  /**
30
30
  * @type {number[]}
@@ -33,7 +33,7 @@ export function writeMIDIFile(midi)
33
33
  // determine the message
34
34
  if (event.messageStatusByte <= messageTypes.keySignature || event.messageStatusByte === messageTypes.sequenceSpecific)
35
35
  {
36
- // this is a meta message
36
+ // this is a meta-message
37
37
  // syntax is FF<type><length><data>
38
38
  messageData = [0xff, event.messageStatusByte, ...writeVariableLengthQuantity(event.messageData.length), ...event.messageData];
39
39
  }
@@ -49,7 +49,7 @@ export function writeMIDIFile(midi)
49
49
  messageData = [];
50
50
  if (runningByte !== event.messageStatusByte)
51
51
  {
52
- // running byte was not the byte we want. Add the byte here.
52
+ // Running byte was not the byte we want. Add the byte here.
53
53
  runningByte = event.messageStatusByte;
54
54
  // add the status byte to the midi
55
55
  messageData.push(event.messageStatusByte);
@@ -59,7 +59,7 @@ export function writeMIDIFile(midi)
59
59
  }
60
60
  // write VLQ
61
61
  binaryTrack.push(...writeVariableLengthQuantity(deltaTicks));
62
- // write message
62
+ // write the message
63
63
  binaryTrack.push(...messageData);
64
64
  currentTick += deltaTicks;
65
65
  }
@@ -1,13 +1,12 @@
1
1
  import { combineArrays, IndexedByteArray } from "../utils/indexed_array.js";
2
- import { writeMIDIFile } from "./midi_writer.js";
3
2
  import { writeRIFFOddSize } from "../soundfont/basic_soundfont/riff_chunk.js";
4
3
  import { getStringBytes, getStringBytesZero } from "../utils/byte_functions/string.js";
5
- import { messageTypes, midiControllers, MidiMessage } from "./midi_message.js";
6
- import { DEFAULT_PERCUSSION } from "../synthetizer/synthetizer.js";
4
+ import { messageTypes, midiControllers, MIDIMessage } from "./midi_message.js";
7
5
  import { getGsOn } from "./midi_editor.js";
8
6
  import { SpessaSynthGroup, SpessaSynthGroupEnd, SpessaSynthInfo } from "../utils/loggin.js";
9
7
  import { consoleColors } from "../utils/other.js";
10
8
  import { writeLittleEndian } from "../utils/byte_functions/little_endian.js";
9
+ import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
11
10
 
12
11
  /**
13
12
  * @enum {string}
@@ -48,9 +47,9 @@ const DEFAULT_COPYRIGHT = "Created using SpessaSynth";
48
47
 
49
48
  /**
50
49
  * Writes an RMIDI file
50
+ * @this {BasicMIDI}
51
51
  * @param soundfontBinary {Uint8Array}
52
- * @param mid {BasicMIDI}
53
- * @param soundfont {BasicSoundFont}
52
+ * @param soundfont {BasicSoundBank}
54
53
  * @param bankOffset {number} the bank offset for RMIDI
55
54
  * @param encoding {string} the encoding of the RMIDI info chunk
56
55
  * @param metadata {RMIDMetadata} the metadata of the file. Optional. If provided, the encoding is forced to utf-8/
@@ -59,7 +58,6 @@ const DEFAULT_COPYRIGHT = "Created using SpessaSynth";
59
58
  */
60
59
  export function writeRMIDI(
61
60
  soundfontBinary,
62
- mid,
63
61
  soundfont,
64
62
  bankOffset = 0,
65
63
  encoding = "Shift_JIS",
@@ -67,6 +65,7 @@ export function writeRMIDI(
67
65
  correctBankOffset = true
68
66
  )
69
67
  {
68
+ const mid = this;
70
69
  SpessaSynthGroup("%cWriting the RMIDI File...", consoleColors.info);
71
70
  SpessaSynthInfo(
72
71
  `%cConfiguration: Bank offset: %c${bankOffset}%c, encoding: %c${encoding}`,
@@ -79,14 +78,14 @@ export function writeRMIDI(
79
78
  SpessaSynthInfo("Initial bank offset", mid.bankOffset);
80
79
  if (correctBankOffset)
81
80
  {
82
- // add offset to bank.
81
+ // Add the offset to the bank.
83
82
  // See https://github.com/spessasus/sf2-rmidi-specification#readme
84
- // also fix presets that don't exists
85
- // since midiplayer6 doesn't seem to default to 0 when nonextistent...
83
+ // also fix presets that don't exist
84
+ // since midi player6 doesn't seem to default to 0 when non-existent...
86
85
  let system = "gm";
87
86
  /**
88
87
  * The unwanted system messages such as gm/gm2 on
89
- * @type {{tNum: number, e: MidiMessage}[]}
88
+ * @type {{tNum: number, e: MIDIMessage}[]}
90
89
  */
91
90
  let unwantedSystems = [];
92
91
  /**
@@ -115,14 +114,14 @@ export function writeRMIDI(
115
114
  return index;
116
115
  }
117
116
 
118
- // it copies midiPorts everywhere else, but here 0 works so DO NOT CHANGE!!!!!!!
117
+ // it copies midiPorts everywhere else, but here 0 works so DO NOT CHANGE!
119
118
  const ports = Array(mid.tracks.length).fill(0);
120
119
  const channelsAmount = 16 + mid.midiPortChannelOffsets.reduce((max, cur) => cur > max ? cur : max);
121
120
  /**
122
121
  * @type {{
123
122
  * program: number,
124
123
  * drums: boolean,
125
- * lastBank: MidiMessage,
124
+ * lastBank: MIDIMessage,
126
125
  * hasBankSelect: boolean
127
126
  * }[]}
128
127
  */
@@ -222,7 +221,7 @@ export function writeRMIDI(
222
221
  {
223
222
  if (soundfont.presets.findIndex(p => p.program === e.messageData[0] && p.bank === 128) === -1)
224
223
  {
225
- // doesn't exist. pick any preset that has the 128 bank.
224
+ // doesn't exist. pick any preset that has bank 128.
226
225
  e.messageData[0] = soundfont.presets.find(p => p.bank === 128)?.program || 0;
227
226
  }
228
227
  }
@@ -230,7 +229,7 @@ export function writeRMIDI(
230
229
  {
231
230
  if (soundfont.presets.findIndex(p => p.program === e.messageData[0] && p.bank !== 128) === -1)
232
231
  {
233
- // doesn't exist. pick any preset that does not have the 128 bank.
232
+ // doesn't exist. pick any preset that does not have bank 128.
234
233
  e.messageData[0] = soundfont.presets.find(p => p.bank !== 128)?.program || 0;
235
234
  }
236
235
  }
@@ -244,7 +243,7 @@ export function writeRMIDI(
244
243
  }
245
244
  if (system === "xg" && channel.drums)
246
245
  {
247
- // drums override: set bank to 127
246
+ // drums override: set the bank to 127
248
247
  channelsInfo[chNum].lastBank.messageData[1] = 127;
249
248
  }
250
249
 
@@ -262,7 +261,7 @@ export function writeRMIDI(
262
261
  }
263
262
  else
264
263
  {
265
- // there is a preset with this bank. add offset. For drums add the normal offset.
264
+ // There is a preset with this bank. Add offset. For drums add the normal offset.
266
265
  let drumBank = system === "xg" ? 127 : 0;
267
266
  const newBank = (bank === 128 ? drumBank : realBank) + bankOffset;
268
267
  channel.lastBank.messageData[1] = newBank;
@@ -298,7 +297,7 @@ export function writeRMIDI(
298
297
  {
299
298
  return;
300
299
  }
301
- // find first program change (for the given channel)
300
+ // find the first program change (for the given channel)
302
301
  const midiChannel = ch % 16;
303
302
  const status = messageTypes.programChange | midiChannel;
304
303
  // find track with this channel being used
@@ -325,7 +324,7 @@ export function writeRMIDI(
325
324
  }
326
325
  const programTicks = track[programIndex].ticks;
327
326
  const targetProgram = soundfont.getPreset(0, 0).program;
328
- track.splice(programIndex, 0, new MidiMessage(
327
+ track.splice(programIndex, 0, new MIDIMessage(
329
328
  programTicks,
330
329
  messageTypes.programChange | midiChannel,
331
330
  new IndexedByteArray([targetProgram])
@@ -342,7 +341,7 @@ export function writeRMIDI(
342
341
  0,
343
342
  has.program
344
343
  )?.bank + bankOffset) || bankOffset;
345
- track.splice(indexToAdd, 0, new MidiMessage(
344
+ track.splice(indexToAdd, 0, new MIDIMessage(
346
345
  ticks,
347
346
  messageTypes.controllerChange | midiChannel,
348
347
  new IndexedByteArray([midiControllers.bankSelect, targetBank])
@@ -364,9 +363,9 @@ export function writeRMIDI(
364
363
  mid.tracks[0].splice(index, 0, getGsOn(0));
365
364
  }
366
365
  }
367
- const newMid = new IndexedByteArray(writeMIDIFile(mid).buffer);
366
+ const newMid = new IndexedByteArray(mid.writeMIDI().buffer);
368
367
 
369
- // infodata for RMID
368
+ // info data for RMID
370
369
  /**
371
370
  * @type {Uint8Array[]}
372
371
  */