Skip to content

Commit

Permalink
The Key Modifer Manager Update
Browse files Browse the repository at this point in the history
added a new page for it in the synth too!
cleanup the synth wiki page too
  • Loading branch information
spessasus committed Nov 2, 2024
1 parent a72eca5 commit c25779f
Show file tree
Hide file tree
Showing 30 changed files with 926 additions and 222 deletions.
3 changes: 2 additions & 1 deletion .idea/jsLibraryMappings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"name": "SpessaSynth",
"version": "3.22.1",
"version": "3.22.2",
"type": "module",
"scripts": {
"start": "node src/website/server/server.js"
"start": "node src/website/server/server.js",
"build": "src/website/minify_website.sh"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
smplStartOffsets,
smplEndOffsets,
options?.compress,
options?.compressionQuality || 0.5,
options?.compressionQuality ?? 0.5,
options.compressionFunction
);

Expand Down
73 changes: 73 additions & 0 deletions src/spessasynth_lib/synthetizer/key_modifier_manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { workletMessageType } from "./worklet_system/message_protocol/worklet_message.js";
import { KeyModifier, workletKeyModifierMessageType } from "./worklet_system/worklet_methods/worklet_key_modifier.js";

export class KeyModifierManager
{
/**
* @param synth {Synthetizer}
*/
constructor(synth)
{
this.synth = synth;
}

/**
* @private
* @param type {workletKeyModifierMessageType}
* @param data {any}
*/
_sendToWorklet(type, data)
{
this.synth.post({
messageType: workletMessageType.keyModifierManager,
messageData: [
type,
data
]
});
}

/**
* Modifies a single key
* @param channel {number} the channel affected. Usually 0-15
* @param midiNote {number} the MIDI note to change. 0-127
* @param options {{
* velocity: number|undefined,
* patch: {
* bank: number,
* program: number
* }|undefined
* }} the key's modifiers
*/
addModifier(channel, midiNote, options)
{
const velocity = options?.velocity || -1;
const program = options?.patch?.program ?? -1;
const bank = options?.patch?.bank ?? -1;
this._sendToWorklet(
workletKeyModifierMessageType.addMapping,
[channel, midiNote, new KeyModifier(velocity, bank, program)]
);
}

/**
* Deletes a key modifier
* @param channel {number} the channel affected. Usually 0-15
* @param midiNote {number} the MIDI note to change. 0-127
*/
deleteModifier(channel, midiNote)
{
this._sendToWorklet(
workletKeyModifierMessageType.deleteMapping,
[channel, midiNote]
);
}

/**
* Clears ALL Modifiers
*/
clearModifiers()
{
this._sendToWorklet(workletKeyModifierMessageType.clearMappings, undefined);
}
}
7 changes: 7 additions & 0 deletions src/spessasynth_lib/synthetizer/synthetizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { SpessaSynthInfo, SpessaSynthWarn } from "../utils/loggin.js";
import { DEFAULT_EFFECTS_CONFIG } from "./audio_effects/effects_config.js";
import { SoundfontManager } from "./synth_soundfont_manager.js";
import { channelConfiguration } from "./worklet_system/worklet_utilities/worklet_processor_channel.js";
import { KeyModifierManager } from "./key_modifier_manager.js";


/**
Expand Down Expand Up @@ -183,6 +184,12 @@ export class Synthetizer
*/
this.soundfontManager = new SoundfontManager(this);

/**
* The synth's key modifier manager
* @type {KeyModifierManager}
*/
this.keyModifierManager = new KeyModifierManager(this);

/**
* @type {function(SynthesizerSnapshot)}
* @private
Expand Down
20 changes: 10 additions & 10 deletions src/spessasynth_lib/synthetizer/worklet_processor.min.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
import { applySynthesizerSnapshot, sendSynthesizerSnapshot } from "./worklet_methods/snapshot.js";
import { WorkletSoundfontManager } from "./worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js";
import { interpolationTypes } from "./worklet_utilities/wavetable_oscillator.js";
import { WorkletKeyModifierManager } from "./worklet_methods/worklet_key_modifier.js";
import { getWorkletVoices } from "./worklet_utilities/worklet_voice.js";
import { panVoice } from "./worklet_utilities/stereo_panner.js";

Expand Down Expand Up @@ -150,7 +151,13 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
this.highPerformanceMode = false;

/**
* Overrides the main soundfont (embedded for example
* Handlese custom key overrides: velocity and preset
* @type {WorkletKeyModifierManager}
*/
this.keyModifierManager = new WorkletKeyModifierManager();

/**
* Overrides the main soundfont (embedded for example)
* @type {BasicSoundFont}
*/
this.overrideSoundfont = undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ export function handleMessage(message)
this.clearSoundFont(true, false);
break;

case workletMessageType.keyModifierManager:
this.keyModifierManager.handleMessage(data[0], data[1]);
break;

case workletMessageType.requestSynthesizerSnapshot:
this.sendSynthesizerSnapshot();
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* @property {number} sequencerSpecific - 23 -> [messageType<WorkletSequencerMessageType> messageData<any>] note: refer to sequencer_message.js
* @property {number} requestSynthesizerSnapshot - 24 -> (no data)
* @property {number} setLogLevel - 25 -> [enableInfo<boolean>, enableWarning<boolean>, enableGroup<boolean>, enableTable<boolean>]
* @property {number} keyModifier - 26 -> [messageType<workletKeyModifierMessageType> messageData<any>]
*/
export const workletMessageType = {
noteOff: 0,
Expand Down Expand Up @@ -54,7 +55,8 @@ export const workletMessageType = {
lockController: 22,
sequencerSpecific: 23,
requestSynthesizerSnapshot: 24,
setLogLevel: 25
setLogLevel: 25,
keyModifierManager: 26
};

/**
Expand Down Expand Up @@ -83,6 +85,7 @@ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
* |boolean
* |ArrayBuffer
* |{messageType: WorkletSequencerMessageType, messageData: any}
* |{messageType: workletKeyModifierMessageType, messageData: any}
* )
* }} WorkletMessage
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ export function noteOn(channel, midiNote, velocity, enableDebugging = false, sen
velocity = channelObject.velocityOverride;
}

// key velocity override
const keyVel = this.keyModifierManager.getVelocity(channel, midiNote);
if (keyVel > -1)
{
velocity = keyVel;
}

// get voices
const voices = this.getWorkletVoices(
channel,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
export class KeyModifier
{

/**
* The new override velocity. -1 means unchanged
* @type {number}
*/
velocity = -1;
/**
* The patch this key uses. -1 on either means default
* @type {{bank: number, program: number}}
*/
patch = { bank: -1, program: -1 };

/**
* @param velocity {number}
* @param bank {number}
* @param program {number}
*/
constructor(velocity = -1, bank = -1, program = -1)
{
this.velocity = velocity;
this.patch = {
bank: bank,
program: program
};
}
}

/**
* @enum {number}
*/
export const workletKeyModifierMessageType = {
addMapping: 0, // [channel<number, midiNote<number>, mapping<KeyModifier>]
deleteMapping: 1, // [channel<number, midiNote<number>]
clearMappings: 2 // <no data>
};

export class WorkletKeyModifierManager
{
/**
* The velocity override mappings for MIDI keys
* @type {KeyModifier[][]}
* @private
*/
_keyMappings = [];

/**
* @param type {workletKeyModifierMessageType}
* @param data {any}
*/
handleMessage(type, data)
{
switch (type)
{
default:
return;

case workletKeyModifierMessageType.addMapping:
this.addMapping(...data);
break;

case workletKeyModifierMessageType.clearMappings:
this.clearMappings();
break;

case workletKeyModifierMessageType.deleteMapping:
this.deleteMapping(...data);
}
}

/**
* @param channel {number}
* @param midiNote {number}
* @param mapping {KeyModifier}
*/
addMapping(channel, midiNote, mapping)
{
if (this._keyMappings[channel] === undefined)
{
this._keyMappings[channel] = [];
}
this._keyMappings[channel][midiNote] = mapping;
}

deleteMapping(channel, midiNote)
{
if (this._keyMappings[channel]?.[midiNote] === undefined)
{
return;
}
this._keyMappings[channel][midiNote] = undefined;
}

clearMappings()
{
this._keyMappings = [];
}

/**
* @param channel {number}
* @param midiNote {number}
* @returns {number} velocity, -1 if unchanged
*/
getVelocity(channel, midiNote)
{
const modifier = this._keyMappings[channel]?.[midiNote];
if (modifier)
{
return modifier.velocity;
}
return -1;
}

/**
* @param channel {number}
* @param midiNote {number}
* @returns {boolean}
*/
hasOverridePatch(channel, midiNote)
{
const bank = this._keyMappings[channel]?.[midiNote]?.patch?.bank;
return bank !== undefined && bank > 0;
}

/**
* @param channel {number}
* @param midiNote {number}
* @returns {{bank: number, program: number}} -1 if unchanged
*/
getPatch(channel, midiNote)
{
const modifier = this._keyMappings[channel]?.[midiNote];
if (modifier)
{
return modifier.patch;
}
throw new Error("No modifier.");
}

}
Loading

0 comments on commit c25779f

Please sign in to comment.