spessasynth_core 4.3.4 → 4.3.6
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/dist/index.d.ts +43 -5
- package/dist/index.js +98 -34
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.d.ts
CHANGED
|
@@ -1663,7 +1663,8 @@ interface ChannelMIDIParameter {
|
|
|
1663
1663
|
/**
|
|
1664
1664
|
* If the channel is in the poly mode.
|
|
1665
1665
|
* - `true` - POLY ON - regular playback.
|
|
1666
|
-
* - `false` - MONO ON - one note per channel,
|
|
1666
|
+
* - `false` - MONO ON - one note per channel,
|
|
1667
|
+
* highest still pressed note is restored after releasing the currently playing one.
|
|
1667
1668
|
*/
|
|
1668
1669
|
polyMode: boolean;
|
|
1669
1670
|
/**
|
|
@@ -2045,6 +2046,20 @@ declare class MIDIChannel {
|
|
|
2045
2046
|
*/
|
|
2046
2047
|
protected readonly octaveTuning: Int8Array;
|
|
2047
2048
|
protected readonly _midiParameters: Readonly<ChannelMIDIParameter>;
|
|
2049
|
+
/**
|
|
2050
|
+
* Note On message tracking, for grouping voices for specific Note On messages.
|
|
2051
|
+
* Used for overlapping Note Ons.
|
|
2052
|
+
* MIDI note: current note on ID
|
|
2053
|
+
* @protected
|
|
2054
|
+
*/
|
|
2055
|
+
protected readonly noteOnID: number[];
|
|
2056
|
+
/**
|
|
2057
|
+
* Note Off message tracking, for grouping voices for specific Note On messages.
|
|
2058
|
+
* Used for overlapping Note Ons.
|
|
2059
|
+
* MIDI note: current note on ID
|
|
2060
|
+
* @protected
|
|
2061
|
+
*/
|
|
2062
|
+
protected readonly noteOffID: number[];
|
|
2048
2063
|
/**
|
|
2049
2064
|
* If the last Parameter was RPN.
|
|
2050
2065
|
* If false then the last parameter was NRPN.
|
|
@@ -2080,21 +2095,39 @@ declare class MIDIChannel {
|
|
|
2080
2095
|
*/
|
|
2081
2096
|
protected currentGain: number;
|
|
2082
2097
|
/**
|
|
2083
|
-
* The last pressed note on this channel.
|
|
2098
|
+
* The last pressed note on this channel for portamento tracking.
|
|
2084
2099
|
* -1 means none.
|
|
2085
2100
|
* This is not a `ChannelMIDIParameter` and is strictly internal,
|
|
2086
2101
|
* mostly because we don't want to send events for every note on message.
|
|
2087
2102
|
* It can be set with Portamento Control CC anyway.
|
|
2088
2103
|
* @protected
|
|
2089
2104
|
*/
|
|
2090
|
-
protected
|
|
2105
|
+
protected lastPortamentoNote: number;
|
|
2091
2106
|
/**
|
|
2092
2107
|
* If the portamento should be executed once regardless of Portamento on/off.
|
|
2093
2108
|
* Adhering to the MIDI spec, CC#84 ignores on/off.
|
|
2094
|
-
* This is also not a `ChannelMIDIParameter` for the same reason as `
|
|
2109
|
+
* This is also not a `ChannelMIDIParameter` for the same reason as `lastPortamentoNote`
|
|
2095
2110
|
* @protected
|
|
2096
2111
|
*/
|
|
2097
2112
|
protected portamentoForce: boolean;
|
|
2113
|
+
/**
|
|
2114
|
+
* The last pressed note on this channel in mono mode.
|
|
2115
|
+
* Used for tracking and releasing this note on a new Note On event.
|
|
2116
|
+
* -1 means none.
|
|
2117
|
+
* @protected
|
|
2118
|
+
*/
|
|
2119
|
+
protected lastMonoNote: number;
|
|
2120
|
+
/**
|
|
2121
|
+
* The last pressed note's velocity on this channel in mono mode.
|
|
2122
|
+
* @protected
|
|
2123
|
+
*/
|
|
2124
|
+
protected lastMonoVelocity: number;
|
|
2125
|
+
/**
|
|
2126
|
+
* For Mono Mode restoring notes.
|
|
2127
|
+
* playingNotes[midiNote]
|
|
2128
|
+
* @protected
|
|
2129
|
+
*/
|
|
2130
|
+
protected readonly playingNotes: boolean[];
|
|
2098
2131
|
protected readonly generators: ChannelGenerators;
|
|
2099
2132
|
protected readonly computeModulator: (voice: Voice, pitchWheel: number, modulatorIndex: number) => number;
|
|
2100
2133
|
protected readonly computeModulators: (voice: Voice, sourceUsesCC?: 0 | 1 | -1 | undefined, sourceIndex?: number | undefined) => void;
|
|
@@ -3070,6 +3103,11 @@ declare class Voice {
|
|
|
3070
3103
|
* MIDI channel number of the voice.
|
|
3071
3104
|
*/
|
|
3072
3105
|
channel: number;
|
|
3106
|
+
/**
|
|
3107
|
+
* Grouping voices for specific Note On messages.
|
|
3108
|
+
* Used for overlapping Note Ons.
|
|
3109
|
+
*/
|
|
3110
|
+
noteID: number;
|
|
3073
3111
|
/**
|
|
3074
3112
|
* MIDI note number of the voice.
|
|
3075
3113
|
* Direct number from the Note On message and is
|
|
@@ -3183,7 +3221,7 @@ declare class Voice {
|
|
|
3183
3221
|
* @param minNoteLength minimum note length in seconds
|
|
3184
3222
|
*/
|
|
3185
3223
|
releaseVoice(currentTime: number, minNoteLength?: number): void;
|
|
3186
|
-
setup(currentTime: number, channel: number, midiNote: number): void;
|
|
3224
|
+
setup(currentTime: number, channel: number, midiNote: number, noteID: number): void;
|
|
3187
3225
|
}
|
|
3188
3226
|
//#endregion
|
|
3189
3227
|
//#region src/soundbank/basic_soundbank/modulator_source.d.ts
|
package/dist/index.js
CHANGED
|
@@ -2585,13 +2585,13 @@ function getNoteTimesInternal(midi, minDrumLength = 0) {
|
|
|
2585
2585
|
const unfinishedNotes = [];
|
|
2586
2586
|
for (let i = 0; i < 16; i++) unfinishedNotes.push(/* @__PURE__ */ new Map());
|
|
2587
2587
|
const noteOff = (midiNote, channel) => {
|
|
2588
|
-
const
|
|
2589
|
-
|
|
2588
|
+
const noteIndexes = unfinishedNotes[channel].get(midiNote);
|
|
2589
|
+
if (noteIndexes === void 0) return;
|
|
2590
|
+
const noteIndex = noteIndexes.shift();
|
|
2590
2591
|
if (noteIndex === void 0) return;
|
|
2591
2592
|
const note = noteTimes[channel][noteIndex];
|
|
2592
2593
|
const time = elapsedTime - note.start;
|
|
2593
2594
|
note.length = channel === 9 ? Math.max(time, minDrumLength) : time;
|
|
2594
|
-
ch.delete(midiNote);
|
|
2595
2595
|
unfinished--;
|
|
2596
2596
|
};
|
|
2597
2597
|
const { timeline, tracks } = midi;
|
|
@@ -2606,7 +2606,6 @@ function getNoteTimesInternal(midi, minDrumLength = 0) {
|
|
|
2606
2606
|
const velocity = event.data[1];
|
|
2607
2607
|
if (velocity === 0) noteOff(midiNote, channel);
|
|
2608
2608
|
else {
|
|
2609
|
-
noteOff(midiNote, channel);
|
|
2610
2609
|
const noteTime = {
|
|
2611
2610
|
midiNote,
|
|
2612
2611
|
start: elapsedTime,
|
|
@@ -2615,14 +2614,17 @@ function getNoteTimesInternal(midi, minDrumLength = 0) {
|
|
|
2615
2614
|
};
|
|
2616
2615
|
const times = noteTimes[channel];
|
|
2617
2616
|
times.push(noteTime);
|
|
2618
|
-
unfinishedNotes[channel]
|
|
2617
|
+
const unfinishedChannel = unfinishedNotes[channel];
|
|
2618
|
+
if (!unfinishedChannel.has(midiNote)) unfinishedChannel.set(midiNote, []);
|
|
2619
|
+
unfinishedNotes[channel].get(midiNote)?.push(times.length - 1);
|
|
2619
2620
|
unfinished++;
|
|
2620
2621
|
}
|
|
2621
2622
|
} else if (event.statusByte === 81) oneTickToSeconds = 60 / (getTempo(event) * midi.timeDivision);
|
|
2622
2623
|
if (++i >= timeline.length) break;
|
|
2623
|
-
|
|
2624
|
+
const nextTimeline = timeline[i];
|
|
2625
|
+
elapsedTime += oneTickToSeconds * (tracks[nextTimeline.tr].events[nextTimeline.ev].ticks - event.ticks);
|
|
2624
2626
|
}
|
|
2625
|
-
if (unfinished > 0) for (let channel = 0; channel < unfinishedNotes.length; channel++) for (const
|
|
2627
|
+
if (unfinished > 0) for (let channel = 0; channel < unfinishedNotes.length; channel++) for (const noteIndexes of unfinishedNotes[channel].values()) for (const noteIndex of noteIndexes) {
|
|
2626
2628
|
const note = noteTimes[channel][noteIndex];
|
|
2627
2629
|
note.length = elapsedTime - note.start;
|
|
2628
2630
|
}
|
|
@@ -4490,20 +4492,31 @@ function processEventInternal(event, trackIndex) {
|
|
|
4490
4492
|
channel += offset;
|
|
4491
4493
|
switch (status) {
|
|
4492
4494
|
case MIDIMessageTypes.noteOn: {
|
|
4495
|
+
let playingNotes = this.playingNotes[channel];
|
|
4496
|
+
if (!playingNotes) {
|
|
4497
|
+
while (this.playingNotes.length <= channel) this.playingNotes.push(/* @__PURE__ */ new Map());
|
|
4498
|
+
playingNotes = this.playingNotes[channel];
|
|
4499
|
+
}
|
|
4493
4500
|
const velocity = event.data[1];
|
|
4494
4501
|
if (velocity > 0) {
|
|
4495
4502
|
this.synth.noteOn(channel, event.data[0], velocity);
|
|
4496
|
-
|
|
4503
|
+
playingNotes.set(event.data[0], velocity);
|
|
4497
4504
|
} else {
|
|
4498
4505
|
this.synth.noteOff(channel, event.data[0]);
|
|
4499
|
-
|
|
4506
|
+
playingNotes.delete(event.data[0]);
|
|
4500
4507
|
}
|
|
4501
4508
|
break;
|
|
4502
4509
|
}
|
|
4503
|
-
case MIDIMessageTypes.noteOff:
|
|
4510
|
+
case MIDIMessageTypes.noteOff: {
|
|
4511
|
+
let playingNotes = this.playingNotes[channel];
|
|
4512
|
+
if (!playingNotes) {
|
|
4513
|
+
while (this.playingNotes.length <= channel) this.playingNotes.push(/* @__PURE__ */ new Map());
|
|
4514
|
+
playingNotes = this.playingNotes[channel];
|
|
4515
|
+
}
|
|
4504
4516
|
this.synth.noteOff(channel, event.data[0]);
|
|
4505
|
-
|
|
4517
|
+
playingNotes.delete(event.data[0]);
|
|
4506
4518
|
break;
|
|
4519
|
+
}
|
|
4507
4520
|
case MIDIMessageTypes.pitchWheel:
|
|
4508
4521
|
this.synth.pitchWheel(channel, event.data[1] << 7 | event.data[0]);
|
|
4509
4522
|
break;
|
|
@@ -4654,7 +4667,7 @@ function loadNewSequenceInternal(parsedMIDI) {
|
|
|
4654
4667
|
//#endregion
|
|
4655
4668
|
//#region src/synthesizer/audio_engine/channel/reset.ts
|
|
4656
4669
|
function resetPortamento() {
|
|
4657
|
-
this.
|
|
4670
|
+
this.lastPortamentoNote = this.channelSystem === "xg" ? 60 : -1;
|
|
4658
4671
|
}
|
|
4659
4672
|
/**
|
|
4660
4673
|
* An array with the default MIDI controller values.
|
|
@@ -4722,6 +4735,7 @@ function resetChannelInternal(sendCCEvents = true) {
|
|
|
4722
4735
|
this.resetGeneratorOffsets();
|
|
4723
4736
|
this.dynamicModulators.resetModulators();
|
|
4724
4737
|
this.sf2NRPNGeneratorLSB = 0;
|
|
4738
|
+
this.playingNotes.fill(false);
|
|
4725
4739
|
this.lastParameterIsRegistered = true;
|
|
4726
4740
|
this._midiControllers[MIDIControllers.nonRegisteredParameterLSB] = 0;
|
|
4727
4741
|
this._midiControllers[MIDIControllers.nonRegisteredParameterMSB] = 0;
|
|
@@ -5041,7 +5055,7 @@ var SpessaSynthSequencer = class {
|
|
|
5041
5055
|
constructor(spessasynthProcessor) {
|
|
5042
5056
|
this.synth = spessasynthProcessor;
|
|
5043
5057
|
this.absoluteStartTime = this.synth.currentTime;
|
|
5044
|
-
|
|
5058
|
+
this.playingNotes = this.synth.midiChannels.map(() => /* @__PURE__ */ new Map());
|
|
5045
5059
|
}
|
|
5046
5060
|
_midiData;
|
|
5047
5061
|
/**
|
|
@@ -7378,6 +7392,11 @@ var Voice = class {
|
|
|
7378
7392
|
*/
|
|
7379
7393
|
channel = 0;
|
|
7380
7394
|
/**
|
|
7395
|
+
* Grouping voices for specific Note On messages.
|
|
7396
|
+
* Used for overlapping Note Ons.
|
|
7397
|
+
*/
|
|
7398
|
+
noteID = 0;
|
|
7399
|
+
/**
|
|
7381
7400
|
* MIDI note number of the voice.
|
|
7382
7401
|
* Direct number from the Note On message and is
|
|
7383
7402
|
* used for Note Off and external parameters:
|
|
@@ -7500,7 +7519,7 @@ var Voice = class {
|
|
|
7500
7519
|
this.releaseStartTime = currentTime;
|
|
7501
7520
|
if (this.releaseStartTime - this.startTime < minNoteLength) this.releaseStartTime = this.startTime + minNoteLength;
|
|
7502
7521
|
}
|
|
7503
|
-
setup(currentTime, channel, midiNote) {
|
|
7522
|
+
setup(currentTime, channel, midiNote, noteID) {
|
|
7504
7523
|
this.isActive = true;
|
|
7505
7524
|
this.isInRelease = false;
|
|
7506
7525
|
this.hasRendered = false;
|
|
@@ -7515,6 +7534,7 @@ var Voice = class {
|
|
|
7515
7534
|
this.startTime = currentTime;
|
|
7516
7535
|
this.channel = channel;
|
|
7517
7536
|
this.midiNote = midiNote;
|
|
7537
|
+
this.noteID = noteID;
|
|
7518
7538
|
}
|
|
7519
7539
|
};
|
|
7520
7540
|
//#endregion
|
|
@@ -12159,7 +12179,7 @@ function controllerChange(controller, value, sendEvent = true) {
|
|
|
12159
12179
|
}
|
|
12160
12180
|
break;
|
|
12161
12181
|
case MIDIControllers.portamentoControl:
|
|
12162
|
-
this.
|
|
12182
|
+
this.lastPortamentoNote = value;
|
|
12163
12183
|
this.portamentoForce = true;
|
|
12164
12184
|
break;
|
|
12165
12185
|
default:
|
|
@@ -12572,8 +12592,9 @@ const clamp = (num, min, max) => Math.max(min, Math.min(max, num));
|
|
|
12572
12592
|
* Sends a "MIDI Note on" message and starts a note.
|
|
12573
12593
|
* @param midiNote The MIDI note number (0-127).
|
|
12574
12594
|
* @param velocity The velocity of the note (0-127). If less than 1, it will send a note off instead.
|
|
12595
|
+
* @param emit If the note on should be updated and emitted (non-internal)
|
|
12575
12596
|
*/
|
|
12576
|
-
function noteOn(midiNote, velocity) {
|
|
12597
|
+
function noteOn(midiNote, velocity, emit = true) {
|
|
12577
12598
|
if (velocity < 1) {
|
|
12578
12599
|
this.noteOff(midiNote);
|
|
12579
12600
|
return;
|
|
@@ -12590,7 +12611,7 @@ function noteOn(midiNote, velocity) {
|
|
|
12590
12611
|
const keyVel = this.synthCore.keyModifierManager.getVelocity(this.channel, midiNote);
|
|
12591
12612
|
if (keyVel > -1) velocity = keyVel;
|
|
12592
12613
|
let voiceGain = this.synthCore.keyModifierManager.getGain(this.channel, midiNote);
|
|
12593
|
-
const previousNote = this.
|
|
12614
|
+
const previousNote = this.lastPortamentoNote;
|
|
12594
12615
|
const portamentoEnabled = this.portamentoForce || this._midiControllers[MIDIControllers.portamentoOnOff] >= 8192;
|
|
12595
12616
|
const portamentoTime = this._midiControllers[MIDIControllers.portamentoTime] >> 7;
|
|
12596
12617
|
const canApplyPortamento = portamentoEnabled && !this._drumChannel && previousNote >= 0 && previousNote !== midiNote && portamentoTime > 0;
|
|
@@ -12602,15 +12623,12 @@ function noteOn(midiNote, velocity) {
|
|
|
12602
12623
|
portaTime = portamentoTimeToSeconds(portamentoTime, keyDistance);
|
|
12603
12624
|
this.portamentoForce = false;
|
|
12604
12625
|
}
|
|
12605
|
-
this.
|
|
12626
|
+
this.lastPortamentoNote = midiNote;
|
|
12627
|
+
this.playingNotes[midiNote] = true;
|
|
12606
12628
|
if (!this._midiParameters.polyMode) {
|
|
12607
|
-
|
|
12608
|
-
|
|
12609
|
-
|
|
12610
|
-
v.exclusiveRelease(this.synthCore.currentTime, 0);
|
|
12611
|
-
if (++vc >= this._voiceCount) break;
|
|
12612
|
-
}
|
|
12613
|
-
}
|
|
12629
|
+
if (this.lastMonoNote >= 0 && this.lastMonoNote !== midiNote) this.killNote(this.lastMonoNote);
|
|
12630
|
+
this.lastMonoNote = midiNote;
|
|
12631
|
+
this.lastMonoVelocity = velocity;
|
|
12614
12632
|
}
|
|
12615
12633
|
const voices = this.synthCore.getVoices(this.channel, soundBankNote, velocity);
|
|
12616
12634
|
let panOverride = 0;
|
|
@@ -12636,10 +12654,11 @@ function noteOn(midiNote, velocity) {
|
|
|
12636
12654
|
delaySend = p.delayGain;
|
|
12637
12655
|
if (voiceGain === 1) voiceGain = p.gain;
|
|
12638
12656
|
}
|
|
12657
|
+
const noteID = emit ? this.noteOnID[midiNote]++ : this.noteOnID[midiNote];
|
|
12639
12658
|
for (const cached of voices) {
|
|
12640
12659
|
const voice = this.synthCore.assignVoice();
|
|
12641
12660
|
const now = this.synthCore.currentTime;
|
|
12642
|
-
voice.setup(now, this.channel, midiNote);
|
|
12661
|
+
voice.setup(now, this.channel, midiNote, noteID);
|
|
12643
12662
|
voice.wavetable = voice.oscillators[this._systemParameters.interpolationType ?? this.synthCore.systemParameters.interpolationType];
|
|
12644
12663
|
voice.targetKey = cached.targetKey;
|
|
12645
12664
|
voice.velocity = cached.velocity;
|
|
@@ -12712,7 +12731,7 @@ function noteOn(midiNote, velocity) {
|
|
|
12712
12731
|
voice.currentPan = Math.max(-500, Math.min(500, panOverride || voice.modulatedGenerators[GeneratorTypes.pan]));
|
|
12713
12732
|
}
|
|
12714
12733
|
this._voiceCount += voices.length;
|
|
12715
|
-
this.synthCore.callEvent("noteOn", {
|
|
12734
|
+
if (emit) this.synthCore.callEvent("noteOn", {
|
|
12716
12735
|
midiNote,
|
|
12717
12736
|
channel: this.channel,
|
|
12718
12737
|
velocity
|
|
@@ -12736,10 +12755,14 @@ function noteOff(midiNote) {
|
|
|
12736
12755
|
});
|
|
12737
12756
|
return;
|
|
12738
12757
|
}
|
|
12739
|
-
|
|
12758
|
+
this.playingNotes[midiNote] = false;
|
|
12759
|
+
const mono = !this._midiParameters.polyMode;
|
|
12760
|
+
const sustain = this._midiControllers[MIDIControllers.sustainPedal] >= 8192 && !mono;
|
|
12740
12761
|
let vc = 0;
|
|
12762
|
+
const noteID = this.noteOffID[midiNote];
|
|
12763
|
+
if (noteID < this.noteOnID[midiNote]) this.noteOffID[midiNote]++;
|
|
12741
12764
|
if (this._voiceCount > 0) {
|
|
12742
|
-
for (const v of this.synthCore.voices) if (v.channel === this.channel && v.isActive && v.midiNote === midiNote && !v.isInRelease) {
|
|
12765
|
+
for (const v of this.synthCore.voices) if (v.channel === this.channel && v.isActive && v.midiNote === midiNote && v.noteID === noteID && !v.isInRelease) {
|
|
12743
12766
|
if (sustain) v.isHeld = true;
|
|
12744
12767
|
else v.releaseVoice(this.synthCore.currentTime);
|
|
12745
12768
|
if (++vc >= this._voiceCount) break;
|
|
@@ -12749,6 +12772,11 @@ function noteOff(midiNote) {
|
|
|
12749
12772
|
midiNote,
|
|
12750
12773
|
channel: this.channel
|
|
12751
12774
|
});
|
|
12775
|
+
if (mono) {
|
|
12776
|
+
const highest = this.playingNotes.lastIndexOf(true);
|
|
12777
|
+
if (highest === -1) this.lastMonoNote = -1;
|
|
12778
|
+
else if (this.lastMonoNote === midiNote) this.noteOn(highest, this.lastMonoVelocity, false);
|
|
12779
|
+
}
|
|
12752
12780
|
}
|
|
12753
12781
|
//#endregion
|
|
12754
12782
|
//#region src/synthesizer/audio_engine/channel/program_change.ts
|
|
@@ -13270,6 +13298,20 @@ var MIDIChannel = class {
|
|
|
13270
13298
|
*/
|
|
13271
13299
|
_systemParameters = { ...DEFAULT_CHANNEL_SYSTEM_PARAMETERS };
|
|
13272
13300
|
/**
|
|
13301
|
+
* Note On message tracking, for grouping voices for specific Note On messages.
|
|
13302
|
+
* Used for overlapping Note Ons.
|
|
13303
|
+
* MIDI note: current note on ID
|
|
13304
|
+
* @protected
|
|
13305
|
+
*/
|
|
13306
|
+
noteOnID = new Array(128).fill(0);
|
|
13307
|
+
/**
|
|
13308
|
+
* Note Off message tracking, for grouping voices for specific Note On messages.
|
|
13309
|
+
* Used for overlapping Note Ons.
|
|
13310
|
+
* MIDI note: current note on ID
|
|
13311
|
+
* @protected
|
|
13312
|
+
*/
|
|
13313
|
+
noteOffID = new Array(128).fill(0);
|
|
13314
|
+
/**
|
|
13273
13315
|
* If the last Parameter was RPN.
|
|
13274
13316
|
* If false then the last parameter was NRPN.
|
|
13275
13317
|
* @protected
|
|
@@ -13304,21 +13346,39 @@ var MIDIChannel = class {
|
|
|
13304
13346
|
*/
|
|
13305
13347
|
currentGain = 0;
|
|
13306
13348
|
/**
|
|
13307
|
-
* The last pressed note on this channel.
|
|
13349
|
+
* The last pressed note on this channel for portamento tracking.
|
|
13308
13350
|
* -1 means none.
|
|
13309
13351
|
* This is not a `ChannelMIDIParameter` and is strictly internal,
|
|
13310
13352
|
* mostly because we don't want to send events for every note on message.
|
|
13311
13353
|
* It can be set with Portamento Control CC anyway.
|
|
13312
13354
|
* @protected
|
|
13313
13355
|
*/
|
|
13314
|
-
|
|
13356
|
+
lastPortamentoNote = -1;
|
|
13315
13357
|
/**
|
|
13316
13358
|
* If the portamento should be executed once regardless of Portamento on/off.
|
|
13317
13359
|
* Adhering to the MIDI spec, CC#84 ignores on/off.
|
|
13318
|
-
* This is also not a `ChannelMIDIParameter` for the same reason as `
|
|
13360
|
+
* This is also not a `ChannelMIDIParameter` for the same reason as `lastPortamentoNote`
|
|
13319
13361
|
* @protected
|
|
13320
13362
|
*/
|
|
13321
13363
|
portamentoForce = false;
|
|
13364
|
+
/**
|
|
13365
|
+
* The last pressed note on this channel in mono mode.
|
|
13366
|
+
* Used for tracking and releasing this note on a new Note On event.
|
|
13367
|
+
* -1 means none.
|
|
13368
|
+
* @protected
|
|
13369
|
+
*/
|
|
13370
|
+
lastMonoNote = -1;
|
|
13371
|
+
/**
|
|
13372
|
+
* The last pressed note's velocity on this channel in mono mode.
|
|
13373
|
+
* @protected
|
|
13374
|
+
*/
|
|
13375
|
+
lastMonoVelocity = 0;
|
|
13376
|
+
/**
|
|
13377
|
+
* For Mono Mode restoring notes.
|
|
13378
|
+
* playingNotes[midiNote]
|
|
13379
|
+
* @protected
|
|
13380
|
+
*/
|
|
13381
|
+
playingNotes = new Array(128).fill(false);
|
|
13322
13382
|
generators = {
|
|
13323
13383
|
offsets: new Int16Array(GENERATORS_AMOUNT),
|
|
13324
13384
|
offsetsEnabled: false,
|
|
@@ -13431,6 +13491,9 @@ var MIDIChannel = class {
|
|
|
13431
13491
|
* @param force If true, stops all notes immediately, otherwise applies release time.
|
|
13432
13492
|
*/
|
|
13433
13493
|
stopAllNotes(force = false) {
|
|
13494
|
+
this.noteOnID.fill(0);
|
|
13495
|
+
this.noteOffID.fill(0);
|
|
13496
|
+
this.playingNotes.fill(false);
|
|
13434
13497
|
if (force) {
|
|
13435
13498
|
let vc = 0;
|
|
13436
13499
|
if (this._voiceCount > 0) {
|
|
@@ -13633,6 +13696,8 @@ var MIDIChannel = class {
|
|
|
13633
13696
|
*/
|
|
13634
13697
|
killNote(midiNote, releaseTime = -12e3) {
|
|
13635
13698
|
let vc = 0;
|
|
13699
|
+
this.noteOffID[midiNote] = 0;
|
|
13700
|
+
this.noteOnID[midiNote] = 0;
|
|
13636
13701
|
if (this._voiceCount > 0) {
|
|
13637
13702
|
for (const v of this.synthCore.voices) if (v.channel === this.channel && v.isActive && v.midiNote === midiNote) {
|
|
13638
13703
|
v.overrideReleaseVolEnv = releaseTime;
|
|
@@ -13663,7 +13728,7 @@ var MIDIChannel = class {
|
|
|
13663
13728
|
* @param midiNote
|
|
13664
13729
|
*/
|
|
13665
13730
|
setLastNote(midiNote) {
|
|
13666
|
-
this.
|
|
13731
|
+
this.lastPortamentoNote = midiNote;
|
|
13667
13732
|
}
|
|
13668
13733
|
/**
|
|
13669
13734
|
* @internal
|
|
@@ -20467,7 +20532,6 @@ var SpessaSynthProcessor = class {
|
|
|
20467
20532
|
applySnapshot(snapshot) {
|
|
20468
20533
|
this.savedSnapshot = snapshot;
|
|
20469
20534
|
applySnapshot$1.call(this.synthCore, snapshot);
|
|
20470
|
-
this.reset();
|
|
20471
20535
|
}
|
|
20472
20536
|
/**
|
|
20473
20537
|
* Gets a synthesizer snapshot from this processor instance.
|