spessasynth_lib 3.20.28 → 3.20.29
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/@types/midi_parser/midi_message.d.ts +1 -0
- package/midi_parser/midi_loader.js +4 -2
- package/midi_parser/midi_message.js +1 -0
- package/package.json +1 -1
- package/sequencer/worklet_sequencer/events.js +67 -0
- package/sequencer/worklet_sequencer/play.js +29 -33
- package/sequencer/worklet_sequencer/process_event.js +4 -1
- package/sequencer/worklet_sequencer/worklet_sequencer.js +24 -5
- package/synthetizer/worklet_processor.min.js +8 -8
- package/synthetizer/worklet_system/worklet_methods/reset_controllers.js +3 -2
- package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +35 -3
|
@@ -59,6 +59,7 @@ export namespace messageTypes {
|
|
|
59
59
|
export let lyric: number;
|
|
60
60
|
export let marker: number;
|
|
61
61
|
export let cuePoint: number;
|
|
62
|
+
export let programName: number;
|
|
62
63
|
export let midiChannelPrefix: number;
|
|
63
64
|
export let midiPort: number;
|
|
64
65
|
export let endOfTrack: number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { dataBytesAmount, getChannel, messageTypes, MidiMessage } from './midi_message.js'
|
|
2
2
|
import { IndexedByteArray } from '../utils/indexed_array.js'
|
|
3
|
-
import {
|
|
3
|
+
import { consoleColors, formatTitle } from '../utils/other.js'
|
|
4
4
|
import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo, SpessaSynthWarn } from '../utils/loggin.js'
|
|
5
5
|
import { readRIFFChunk } from '../soundfont/basic_soundfont/riff_chunk.js'
|
|
6
6
|
import { readVariableLengthQuantity } from '../utils/byte_functions/variable_length_quantity.js'
|
|
@@ -10,6 +10,8 @@ import { readLittleEndian } from '../utils/byte_functions/little_endian.js'
|
|
|
10
10
|
import { RMIDINFOChunks } from './rmidi_writer.js'
|
|
11
11
|
import { BasicMIDI, MIDIticksToSeconds } from './basic_midi.js'
|
|
12
12
|
|
|
13
|
+
|
|
14
|
+
const GS_TEXT_HEADER = new Uint8Array([0x41, 0x10, 0x45, 0x12, 0x10, 0x00, 0x00]);
|
|
13
15
|
/**
|
|
14
16
|
* midi_loader.js
|
|
15
17
|
* purpose: parses a midi file for the seqyencer, including things like marker or CC 2/4 loop detection, copyright detection etc.
|
|
@@ -366,7 +368,7 @@ class MIDI extends BasicMIDI
|
|
|
366
368
|
// since this is a sysex message
|
|
367
369
|
// check for embedded copyright (roland SC display sysex) http://www.bandtrax.com.au/sysex.htm
|
|
368
370
|
// header goes like this: 41 10 45 12 10 00 00
|
|
369
|
-
if(
|
|
371
|
+
if(eventData.slice(0, 7).every((n, i) => GS_TEXT_HEADER[i] === n))
|
|
370
372
|
{
|
|
371
373
|
/**
|
|
372
374
|
* @type {IndexedByteArray}
|
package/package.json
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { returnMessageType } from '../../synthetizer/worklet_system/message_protocol/worklet_message.js'
|
|
2
2
|
import { WorkletSequencerMessageType, WorkletSequencerReturnMessageType } from './sequencer_message.js'
|
|
3
|
+
import { messageTypes, midiControllers } from '../../midi_parser/midi_message.js'
|
|
4
|
+
import { MIDI_CHANNEL_COUNT } from '../../synthetizer/synthetizer.js'
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* @param messageType {WorkletSequencerMessageType}
|
|
@@ -94,4 +96,69 @@ export function post(messageType, messageData = undefined)
|
|
|
94
96
|
export function sendMIDIMessage(message)
|
|
95
97
|
{
|
|
96
98
|
this.post(WorkletSequencerReturnMessageType.midiEvent, message);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @this {WorkletSequencer}
|
|
103
|
+
* @param channel {number}
|
|
104
|
+
* @param type {number}
|
|
105
|
+
* @param value {number}
|
|
106
|
+
*/
|
|
107
|
+
export function sendMIDICC(channel, type, value)
|
|
108
|
+
{
|
|
109
|
+
channel %= 16;
|
|
110
|
+
if(!this.sendMIDIMessages)
|
|
111
|
+
{
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
this.sendMIDIMessage([messageTypes.controllerChange | channel, type, value])
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @this {WorkletSequencer}
|
|
119
|
+
* @param channel {number}
|
|
120
|
+
* @param program {number}
|
|
121
|
+
*/
|
|
122
|
+
export function sendMIDIProgramChange(channel, program)
|
|
123
|
+
{
|
|
124
|
+
channel %= 16;
|
|
125
|
+
if(!this.sendMIDIMessages)
|
|
126
|
+
{
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
this.sendMIDIMessage([messageTypes.programChange | channel, program]);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Sets the pitch of the given channel
|
|
134
|
+
* @this {WorkletSequencer}
|
|
135
|
+
* @param channel {number} usually 0-15: the channel to change pitch
|
|
136
|
+
* @param MSB {number} SECOND byte of the MIDI pitchWheel message
|
|
137
|
+
* @param LSB {number} FIRST byte of the MIDI pitchWheel message
|
|
138
|
+
*/
|
|
139
|
+
export function sendMIDIPitchWheel(channel, MSB, LSB)
|
|
140
|
+
{
|
|
141
|
+
channel %= 16;
|
|
142
|
+
if(!this.sendMIDIMessages)
|
|
143
|
+
{
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
this.sendMIDIMessage([messageTypes.pitchBend | channel, LSB, MSB]);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @this {WorkletSequencer}
|
|
151
|
+
*/
|
|
152
|
+
export function sendMIDIReset()
|
|
153
|
+
{
|
|
154
|
+
if(!this.sendMIDIMessages)
|
|
155
|
+
{
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
this.sendMIDIMessage([messageTypes.reset]);
|
|
159
|
+
for(let ch = 0; ch < MIDI_CHANNEL_COUNT; ch++)
|
|
160
|
+
{
|
|
161
|
+
this.sendMIDIMessage([messageTypes.controllerChange | ch, midiControllers.allSoundOff, 0]);
|
|
162
|
+
this.sendMIDIMessage([messageTypes.controllerChange | ch, midiControllers.resetAllControllers, 0]);
|
|
163
|
+
}
|
|
97
164
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { getEvent, messageTypes, midiControllers } from '../../midi_parser/midi_message.js'
|
|
2
2
|
import { WorkletSequencerReturnMessageType } from './sequencer_message.js'
|
|
3
|
-
import { MIDI_CHANNEL_COUNT } from '../../synthetizer/synthetizer.js'
|
|
4
3
|
import { MIDIticksToSeconds } from '../../midi_parser/basic_midi.js'
|
|
5
4
|
|
|
6
5
|
|
|
@@ -27,15 +26,8 @@ export function _playTo(time, ticks = undefined)
|
|
|
27
26
|
this.oneTickToSeconds = 60 / (120 * this.midiData.timeDivision);
|
|
28
27
|
// reset
|
|
29
28
|
this.synth.resetAllControllers();
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this.sendMIDIMessage([messageTypes.reset]);
|
|
33
|
-
for(let ch = 0; ch < MIDI_CHANNEL_COUNT; ch++)
|
|
34
|
-
{
|
|
35
|
-
this.sendMIDIMessage([messageTypes.controllerChange | ch, midiControllers.resetAllControllers, 0]);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
this._resetTimers()
|
|
29
|
+
this.sendMIDIReset();
|
|
30
|
+
this._resetTimers();
|
|
39
31
|
|
|
40
32
|
const channelsToSave = this.synth.workletProcessorChannels.length;
|
|
41
33
|
/**
|
|
@@ -131,19 +123,19 @@ export function _playTo(time, ticks = undefined)
|
|
|
131
123
|
const controllerNumber = event.messageData[0];
|
|
132
124
|
if(isCCNonSkippable(controllerNumber))
|
|
133
125
|
{
|
|
126
|
+
let ccV = event.messageData[1];
|
|
127
|
+
if(controllerNumber === midiControllers.bankSelect)
|
|
128
|
+
{
|
|
129
|
+
// add the bank to saved
|
|
130
|
+
programs[channel].bank = ccV;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
134
133
|
if(this.sendMIDIMessages)
|
|
135
134
|
{
|
|
136
|
-
this.
|
|
135
|
+
this.sendMIDICC(channel, controllerNumber, ccV);
|
|
137
136
|
}
|
|
138
137
|
else
|
|
139
138
|
{
|
|
140
|
-
let ccV = event.messageData[1];
|
|
141
|
-
if(controllerNumber === midiControllers.bankSelect)
|
|
142
|
-
{
|
|
143
|
-
// add the bank to saved
|
|
144
|
-
programs[channel].bank = ccV;
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
139
|
this.synth.controllerChange(channel, controllerNumber, ccV);
|
|
148
140
|
}
|
|
149
141
|
}
|
|
@@ -177,25 +169,29 @@ export function _playTo(time, ticks = undefined)
|
|
|
177
169
|
// restoring saved controllers
|
|
178
170
|
if(this.sendMIDIMessages)
|
|
179
171
|
{
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
172
|
+
for (let channelNumber = 0; channelNumber < channelsToSave; channelNumber++)
|
|
173
|
+
{
|
|
174
|
+
// restore pitch bends
|
|
175
|
+
if(pitchBends[channelNumber] !== undefined)
|
|
176
|
+
{
|
|
177
|
+
this.sendMIDIPitchWheel(channelNumber, pitchBends[channelNumber] >> 7, pitchBends[channelNumber] & 0x7F);
|
|
178
|
+
}
|
|
179
|
+
if(savedControllers[channelNumber] !== undefined)
|
|
180
|
+
{
|
|
181
|
+
// every controller that has changed
|
|
182
|
+
savedControllers[channelNumber].forEach((value, index) => {
|
|
183
|
+
if(value !== defaultControllerArray[index] && !isCCNonSkippable(index))
|
|
184
|
+
{
|
|
185
|
+
this.sendMIDICC(channelNumber, index, value);
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
}
|
|
193
189
|
// restore programs
|
|
194
190
|
if(programs[channelNumber].program >= 0 && programs[channelNumber].actualBank >= 0)
|
|
195
191
|
{
|
|
196
192
|
const bank = programs[channelNumber].actualBank;
|
|
197
|
-
this.
|
|
198
|
-
this.
|
|
193
|
+
this.sendMIDICC(channelNumber, midiControllers.bankSelect, bank);
|
|
194
|
+
this.sendMIDIProgramChange(channelNumber, programs[channelNumber].program);
|
|
199
195
|
}
|
|
200
196
|
}
|
|
201
197
|
}
|
|
@@ -101,6 +101,7 @@ export function _processEvent(event, trackIndex)
|
|
|
101
101
|
case messageTypes.songPosition:
|
|
102
102
|
case messageTypes.activeSensing:
|
|
103
103
|
case messageTypes.keySignature:
|
|
104
|
+
case messageTypes.sequenceNumber:
|
|
104
105
|
break;
|
|
105
106
|
|
|
106
107
|
case messageTypes.text:
|
|
@@ -110,6 +111,7 @@ export function _processEvent(event, trackIndex)
|
|
|
110
111
|
case messageTypes.marker:
|
|
111
112
|
case messageTypes.cuePoint:
|
|
112
113
|
case messageTypes.instrumentName:
|
|
114
|
+
case messageTypes.programName:
|
|
113
115
|
this.post(WorkletSequencerReturnMessageType.textEvent, [event.messageData, statusByteData.status])
|
|
114
116
|
break;
|
|
115
117
|
|
|
@@ -139,7 +141,8 @@ export function _processEvent(event, trackIndex)
|
|
|
139
141
|
*/
|
|
140
142
|
export function _addNewMidiPort()
|
|
141
143
|
{
|
|
142
|
-
for (let i = 0; i < 16; i++)
|
|
144
|
+
for (let i = 0; i < 16; i++)
|
|
145
|
+
{
|
|
143
146
|
this.synth.createWorkletChannel(true);
|
|
144
147
|
if(i === DEFAULT_PERCUSSION)
|
|
145
148
|
{
|
|
@@ -4,7 +4,15 @@ import { _findFirstEventIndex, _processTick } from './process_tick.js'
|
|
|
4
4
|
import { assignMIDIPort, loadNewSequence, loadNewSongList, nextSong, previousSong } from './song_control.js'
|
|
5
5
|
import { _playTo, _recalculateStartTime, play, setTimeTicks } from './play.js'
|
|
6
6
|
import { messageTypes, midiControllers } from '../../midi_parser/midi_message.js'
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
post,
|
|
9
|
+
processMessage,
|
|
10
|
+
sendMIDICC,
|
|
11
|
+
sendMIDIMessage,
|
|
12
|
+
sendMIDIPitchWheel,
|
|
13
|
+
sendMIDIProgramChange,
|
|
14
|
+
sendMIDIReset,
|
|
15
|
+
} from './events.js'
|
|
8
16
|
import { SpessaSynthWarn } from '../../utils/loggin.js'
|
|
9
17
|
import { MIDI_CHANNEL_COUNT } from '../../synthetizer/synthetizer.js'
|
|
10
18
|
|
|
@@ -178,16 +186,21 @@ class WorkletSequencer
|
|
|
178
186
|
{
|
|
179
187
|
this.clearProcessHandler()
|
|
180
188
|
// disable sustain
|
|
181
|
-
for (let i = 0; i < 16; i++)
|
|
189
|
+
for (let i = 0; i < 16; i++)
|
|
190
|
+
{
|
|
182
191
|
this.synth.controllerChange(i, midiControllers.sustainPedal, 0);
|
|
183
192
|
}
|
|
184
193
|
this.synth.stopAllChannels();
|
|
185
194
|
if(this.sendMIDIMessages)
|
|
186
195
|
{
|
|
196
|
+
for(let note of this.playingNotes)
|
|
197
|
+
{
|
|
198
|
+
this.sendMIDIMessage([messageTypes.noteOff | (note.channel % 16), note.midiNote]);
|
|
199
|
+
}
|
|
187
200
|
for (let c = 0; c < MIDI_CHANNEL_COUNT; c++)
|
|
188
201
|
{
|
|
189
|
-
this.
|
|
190
|
-
this.
|
|
202
|
+
this.sendMIDICC(c, midiControllers.allNotesOff, 0);
|
|
203
|
+
this.sendMIDICC(c, midiControllers.allSoundOff, 0);
|
|
191
204
|
}
|
|
192
205
|
}
|
|
193
206
|
}
|
|
@@ -218,9 +231,15 @@ class WorkletSequencer
|
|
|
218
231
|
}
|
|
219
232
|
}
|
|
220
233
|
|
|
221
|
-
|
|
234
|
+
// Web MIDI sending
|
|
222
235
|
WorkletSequencer.prototype.sendMIDIMessage = sendMIDIMessage;
|
|
236
|
+
WorkletSequencer.prototype.sendMIDIReset = sendMIDIReset;
|
|
237
|
+
WorkletSequencer.prototype.sendMIDICC = sendMIDICC;
|
|
238
|
+
WorkletSequencer.prototype.sendMIDIProgramChange = sendMIDIProgramChange;
|
|
239
|
+
WorkletSequencer.prototype.sendMIDIPitchWheel = sendMIDIPitchWheel;
|
|
223
240
|
WorkletSequencer.prototype.assignMIDIPort = assignMIDIPort;
|
|
241
|
+
|
|
242
|
+
WorkletSequencer.prototype.post = post;
|
|
224
243
|
WorkletSequencer.prototype.processMessage = processMessage;
|
|
225
244
|
|
|
226
245
|
WorkletSequencer.prototype._processEvent = _processEvent;
|