Skip to content

Commit 78cb00a

Browse files
authored
Merge pull request #421 from YoYoGames/gmfr-1115-afx-ptrax-develop
Audio effect parameter tracks
2 parents c668b30 + 1397cb6 commit 78cb00a

File tree

16 files changed

+1118
-284
lines changed

16 files changed

+1118
-284
lines changed

scripts/functions/Function_Sequence.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ function sequence_keyframestore_new(_type)
140140
case eSTT_String:
141141
case eSTT_Real:
142142
case eSTT_Color:
143+
case eSTT_AudioEffect:
143144
case eSTT_Message:
144145
case eSTT_Moment:
145146
pKeyframeStore = new yyKeyframeStore(_type);
@@ -180,6 +181,7 @@ function sequence_keyframe_new(_type)
180181
case eSTT_String:
181182
case eSTT_Real:
182183
case eSTT_Color:
184+
case eSTT_AudioEffect:
183185
case eSTT_Message:
184186
case eSTT_Moment:
185187
pKeyframe = new yyKeyframe(_type);
@@ -220,6 +222,7 @@ function sequence_keyframedata_new(_type)
220222
case eSTT_String: pKey = new yyStringTrackKey(); break;
221223
case eSTT_Real: pKey = new yyRealTrackKey(); break;
222224
case eSTT_Color: pKey = new yyColorTrackKey(); break;
225+
case eSTT_AudioEffect: pKey = new yyAudioEffectTrackKey(); break;
223226
case eSTT_Message: pKey = new yyMessageEventTrackKey(); break;
224227
case eSTT_Moment: pKey = new yyMomentEventTrackKey(); break;
225228
default: yyError("Unsupported keyframe type"); break;
@@ -253,9 +256,10 @@ function sequence_track_new(_type)
253256
case eSTT_Audio: pTrack = new yySequenceAudioTrack(); break;
254257
case eSTT_Text: pTrack = new yySequenceTextTrack(); break;
255258
case eSTT_Real: pTrack = new yySequenceRealTrack(); break;
256-
case eSTT_Color: pTrack = new yySequenceColorTrack(); break;
259+
case eSTT_Color: pTrack = new yySequenceColourTrack(); break;
257260
case eSTT_Bool: pTrack = new yySequenceBoolTrack(); break;
258261
case eSTT_String: pTrack = new yySequenceStringTrack(); break;
262+
case eSTT_AudioEffect: pTrack = new yySequenceAudioEffectTrack(); break;
259263
case eSTT_Sequence: pTrack = new yySequenceSequenceTrack(); break;
260264
case eSTT_Particle: pTrack = new yySequenceParticleTrack(); break;
261265
case eSTT_ClipMask: pTrack = new yySequenceClipMaskTrack(); break;

scripts/sound/AudioBus.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,21 +92,21 @@ AudioBus.prototype.findPrevNode = function(_idx)
9292

9393
AudioBus.prototype.handleConnections = function(_idx, _newNodes)
9494
{
95-
const currentNode = this.nodes[_idx];
95+
const currentNodes = this.nodes[_idx];
9696

97-
if (currentNode === undefined && _newNodes === undefined)
97+
if (currentNodes === undefined && _newNodes === undefined)
9898
return; // No need to change anything
9999

100100
const prevNode = this.findPrevNode(_idx);
101101
const nextNode = this.findNextNode(_idx);
102102

103103
// Disconnect the previous node
104-
if (currentNode !== undefined)
104+
if (currentNodes !== undefined)
105105
{
106-
prevNode.disconnect(currentNode);
106+
prevNode.disconnect(currentNodes.input);
107107

108-
currentNode.disconnect();
109-
this.effects[_idx].removeNode(currentNode);
108+
currentNodes.output.disconnect();
109+
this.effects[_idx].removeNode(currentNodes.output);
110110
}
111111
else
112112
{
@@ -148,6 +148,19 @@ AudioBus.isNodeIndex = function(_prop)
148148
return false;
149149
};
150150

151+
AudioBus.prototype.getParamDescriptors = function() {
152+
return AudioBus.ParamDescriptors;
153+
};
154+
155+
AudioBus.prototype.getParamDescriptor = function(_idx) {
156+
return AudioBus.ParamDescriptors[_idx];
157+
};
158+
159+
AudioBus.ParamDescriptors = [
160+
{ name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 },
161+
{ name: "gain", integer: false, defaultValue: 1, minValue: 0, maxValue: Number.MAX_VALUE }
162+
];
163+
151164
function DummyAudioBus() {
152165
this.outputNode = Audio_CreateGainNode(g_WebAudioContext);
153166

scripts/sound/AudioEffect.js

Lines changed: 85 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function AudioEffectStruct(_type) {
4444
this.nodes = [];
4545

4646
this.type = _type;
47-
this.params = {};
47+
this.params = [];
4848

4949
// Define user-facing properties
5050
Object.defineProperties(this, {
@@ -60,42 +60,51 @@ function AudioEffectStruct(_type) {
6060
gmlbypass: {
6161
enumerable: true,
6262
get: () => {
63-
return this.params.bypass;
63+
return this.params[AudioEffectStruct.Index.Bypass];
6464
},
6565
set: (_state) => {
66-
this.setParam(AudioEffectStruct.paramDescriptors().bypass, _state);
66+
const val = this.setParam(AudioEffectStruct.Index.Bypass, _state);
6767

6868
this.nodes.forEach((_node) => {
6969
const bypass = _node.parameters.get("bypass");
70-
bypass.value = this.params.bypass;
70+
bypass.value = val;
7171
});
7272
}
7373
}
7474
});
7575
}
7676

77-
AudioEffectStruct.Create = function(_type, _params) {
77+
AudioEffectStruct.GetStructType = function(_type) {
7878
switch (_type)
7979
{
80-
case AudioEffect.Type.Bitcrusher: return new BitcrusherEffectStruct(_params);
81-
case AudioEffect.Type.Delay: return new DelayEffectStruct(_params);
82-
case AudioEffect.Type.Gain: return new GainEffectStruct(_params);
83-
case AudioEffect.Type.HPF2: return new HPF2EffectStruct(_params);
84-
case AudioEffect.Type.LPF2: return new LPF2EffectStruct(_params);
85-
case AudioEffect.Type.Reverb1: return new Reverb1EffectStruct(_params);
86-
case AudioEffect.Type.Tremolo: return new TremoloEffectStruct(_params);
87-
case AudioEffect.Type.PeakEQ: return new PeakEQEffectStruct(_params);
88-
case AudioEffect.Type.HiShelf: return new HiShelfEffectStruct(_params);
89-
case AudioEffect.Type.LoShelf: return new LoShelfEffectStruct(_params);
90-
case AudioEffect.Type.EQ: return new EQEffectStruct(_params);
91-
case AudioEffect.Type.Compressor: return new CompressorEffectStruct(_params);
92-
default: return null;
80+
case AudioEffect.Type.Bitcrusher: return BitcrusherEffectStruct;
81+
case AudioEffect.Type.Delay: return DelayEffectStruct;
82+
case AudioEffect.Type.Gain: return GainEffectStruct;
83+
case AudioEffect.Type.HPF2: return HPF2EffectStruct;
84+
case AudioEffect.Type.LPF2: return LPF2EffectStruct;
85+
case AudioEffect.Type.Reverb1: return Reverb1EffectStruct;
86+
case AudioEffect.Type.Tremolo: return TremoloEffectStruct;
87+
case AudioEffect.Type.PeakEQ: return PeakEQEffectStruct;
88+
case AudioEffect.Type.HiShelf: return HiShelfEffectStruct;
89+
case AudioEffect.Type.LoShelf: return LoShelfEffectStruct;
90+
case AudioEffect.Type.EQ: return EQEffectStruct;
91+
case AudioEffect.Type.Compressor: return CompressorEffectStruct;
92+
default: return undefined;
9393
}
9494
};
9595

96-
AudioEffectStruct.paramDescriptors = () => ({
97-
bypass: { name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }
98-
});
96+
AudioEffectStruct.Create = function(_type, _params) {
97+
const structType = AudioEffectStruct.GetStructType(_type);
98+
return (structType === undefined) ? undefined : new structType(_params);
99+
};
100+
101+
AudioEffectStruct.Index = {
102+
Bypass: 0
103+
};
104+
105+
AudioEffectStruct.ParamDescriptors = [
106+
{ name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 }
107+
];
99108

100109
AudioEffectStruct.prototype.addInstance = function() {
101110
const node = g_WorkletNodeManager.createEffect(this);
@@ -105,27 +114,40 @@ AudioEffectStruct.prototype.addInstance = function() {
105114
return ret;
106115
};
107116

108-
AudioEffectStruct.prototype.initParams = function(_params, _descriptors) {
109-
Object.values(_descriptors).forEach(_desc => {
110-
const val = (() => {
111-
if (_params === undefined || _params["gml" + _desc.name] === undefined) {
112-
return _desc.defaultValue;
113-
}
114-
115-
return _params["gml" + _desc.name];
116-
})();
117+
AudioEffectStruct.prototype.initParams = function(_params) {
118+
const descriptors = this.getParamDescriptors();
119+
120+
descriptors.forEach((_desc, _idx) => {
121+
let val = _desc.defaultValue;
122+
123+
if (_params !== undefined && _params["gml" + _desc.name] !== undefined) {
124+
val = _params["gml" + _desc.name];
125+
}
117126

118-
this.setParam(_desc, val);
127+
this.setParam(_idx, val);
119128
});
120129
};
121130

122-
AudioEffectStruct.prototype.setParam = function(_desc, _val) {
123-
_val = clamp(_val, _desc.minValue, _desc.maxValue);
131+
AudioEffectStruct.prototype.setParam = function(_idx, _val) {
132+
const structType = AudioEffectStruct.GetStructType(this.type);
133+
const desc = structType.ParamDescriptors[_idx];
134+
135+
_val = clamp(_val, desc.minValue, desc.maxValue);
124136

125-
if (_desc.integer === true)
137+
if (desc.integer === true)
126138
_val = ~~_val;
127139

128-
this.params[_desc.name] = _val;
140+
this.params[_idx] = _val;
141+
return _val;
142+
};
143+
144+
AudioEffectStruct.prototype.getParamDescriptors = function() {
145+
const structType = AudioEffectStruct.GetStructType(this.type);
146+
return structType.ParamDescriptors;
147+
};
148+
149+
AudioEffectStruct.prototype.getParamDescriptor = function(_idx) {
150+
return this.getParamDescriptors()[_idx];
129151
};
130152

131153
AudioEffectStruct.prototype.removeNode = function(_node) {
@@ -136,4 +158,32 @@ AudioEffectStruct.prototype.removeNode = function(_node) {
136158
this.nodes.splice(idx, 1);
137159
}
138160
};
161+
162+
AudioEffectStruct.prototype.updateFreqDesc = function(_desc) {
163+
if (this.isFilter() === false) {
164+
return _desc;
165+
}
166+
167+
if (_desc.name !== "cutoff" && _desc.name !== "freq") {
168+
return _desc;
169+
}
170+
171+
_desc.maxValue = g_WebAudioContext ? Math.min(g_WebAudioContext.sampleRate / 2, _desc.maxValue)
172+
: _desc.maxValue;
173+
_desc.defaultValue = Math.min(_desc.defaultValue, _desc.maxValue);
174+
return _desc;
175+
};
176+
177+
AudioEffectStruct.prototype.isFilter = function() {
178+
switch (this.type) {
179+
case AudioEffect.Type.HiShelf:
180+
case AudioEffect.Type.HPF2:
181+
case AudioEffect.Type.LoShelf:
182+
case AudioEffect.Type.LPF2:
183+
case AudioEffect.Type.PeakEQ:
184+
return true;
185+
default:
186+
return false;
187+
}
188+
};
139189
// @endif

scripts/sound/effects/Bitcrusher.js

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,74 +3,82 @@ function BitcrusherEffectStruct(_params) {
33
AudioEffectStruct.call(this, AudioEffect.Type.Bitcrusher);
44
Object.setPrototypeOf(this, AudioEffectStruct.prototype);
55

6-
this.initParams(_params, BitcrusherEffectStruct.paramDescriptors());
6+
this.initParams(_params);
77

88
// Define user-facing properties
99
Object.defineProperties(this, {
1010
gmlgain: {
1111
enumerable: true,
1212
get: () => {
13-
return this.params.gain;
13+
return this.params[BitcrusherEffectStruct.Index.Gain];
1414
},
1515
set: (_gain) => {
16-
this.setParam(BitcrusherEffectStruct.paramDescriptors().gain, _gain);
16+
const val = this.setParam(BitcrusherEffectStruct.Index.Gain, _gain);
1717

1818
this.nodes.forEach((_node) => {
1919
const gain = _node.parameters.get("gain");
20-
gain.setTargetAtTime(this.params.gain, 0, AudioEffect.PARAM_TIME_CONSTANT);
20+
gain.setTargetAtTime(val, 0, AudioEffect.PARAM_TIME_CONSTANT);
2121
});
2222
}
2323
},
2424
gmlfactor: {
2525
enumerable: true,
2626
get: () => {
27-
return this.params.factor;
27+
return this.params[BitcrusherEffectStruct.Index.Factor];
2828
},
2929
set: (_factor) => {
30-
this.setParam(BitcrusherEffectStruct.paramDescriptors().factor, _factor);
30+
const val = this.setParam(BitcrusherEffectStruct.Index.Factor, _factor);
3131

3232
this.nodes.forEach((_node) => {
3333
const factor = _node.parameters.get("factor");
34-
factor.value = this.params.factor;
34+
factor.value = val;
3535
});
3636
}
3737
},
3838
gmlresolution: {
3939
enumerable: true,
4040
get: () => {
41-
return this.params.resolution;
41+
return this.params[BitcrusherEffectStruct.Index.Resolution];
4242
},
4343
set: (_resolution) => {
44-
this.setParam(BitcrusherEffectStruct.paramDescriptors().resolution, _resolution);
44+
const val = this.setParam(BitcrusherEffectStruct.Index.Resolution, _resolution);
4545

4646
this.nodes.forEach((_node) => {
4747
const resolution = _node.parameters.get("resolution");
48-
resolution.value = this.params.resolution;
48+
resolution.value = val;
4949
});
5050
}
5151
},
5252
gmlmix: {
5353
enumerable: true,
5454
get: () => {
55-
return this.params.mix;
55+
return this.params[BitcrusherEffectStruct.Index.Mix];
5656
},
5757
set: (_mix) => {
58-
this.setParam(BitcrusherEffectStruct.paramDescriptors().mix, _mix);
58+
const val = this.setParam(BitcrusherEffectStruct.Index.Mix, _mix);
5959

6060
this.nodes.forEach((_node) => {
6161
const mix = _node.parameters.get("mix");
62-
mix.setTargetAtTime(this.params.mix, 0, AudioEffect.PARAM_TIME_CONSTANT);
62+
mix.setTargetAtTime(val, 0, AudioEffect.PARAM_TIME_CONSTANT);
6363
});
6464
}
6565
}
6666
});
6767
}
6868

69-
BitcrusherEffectStruct.paramDescriptors = () => ({
70-
bypass: AudioEffectStruct.paramDescriptors().bypass,
71-
gain: { name: "gain", integer: false, defaultValue: 1.0, minValue: 0.0, maxValue: Number.MAX_VALUE },
72-
factor: { name: "factor", integer: true, defaultValue: 20, minValue: 1, maxValue: 100 },
73-
resolution: { name: "resolution", integer: true, defaultValue: 8, minValue: 2, maxValue: 16 },
74-
mix: { name: "mix", integer: false, defaultValue: 0.8, minValue: 0.0, maxValue: 1.0 }
75-
});
69+
BitcrusherEffectStruct.Index = {
70+
Bypass: 0,
71+
Gain: 1,
72+
Factor: 2,
73+
Resolution: 3,
74+
Mix: 4
75+
};
76+
77+
BitcrusherEffectStruct.ParamDescriptors = [
78+
{ name: "bypass", integer: true, defaultValue: 0, minValue: 0, maxValue: 1 },
79+
{ name: "gain", integer: false, defaultValue: 1.0, minValue: 0.0, maxValue: Number.MAX_VALUE },
80+
{ name: "factor", integer: true, defaultValue: 20, minValue: 1, maxValue: 100 },
81+
{ name: "resolution", integer: true, defaultValue: 8, minValue: 2, maxValue: 16 },
82+
{ name: "mix", integer: false, defaultValue: 0.8, minValue: 0.0, maxValue: 1.0 }
83+
];
7684
// @endif

0 commit comments

Comments
 (0)