@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
201 lines (185 loc) • 5.96 kB
text/typescript
/**
* Accumulates attributes based on audio analyser nodes
*
*
*/
import {TypedSopNode} from './_Base';
import {CoreGroup} from '../../../core/geometry/Group';
import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig';
import {AUDIO_ANALYSER_NODES, NodeContext} from '../../poly/NodeContext';
import {InputCloneMode} from '../../poly/InputCloneMode';
import {BufferAttribute} from 'three';
import {NodePathParam} from '../../params/NodePath';
import {BooleanParam} from '../../params/Boolean';
import {StringParam} from '../../params/String';
import {isBooleanTrue} from '../../../core/Type';
import {BaseAnalyserAudioNode} from '../audio/_BaseAnalyser';
import {CoreObjectType, ObjectContent} from '../../../core/geometry/ObjectContent';
import {corePointClassFactory} from '../../../core/geometry/CoreObjectFactory';
interface ParamSet {
active: BooleanParam;
attribName: StringParam;
node: NodePathParam;
}
interface ParamSetByIndex {
'0': ParamSet;
'1': ParamSet;
'2': ParamSet;
}
type ParaSetIndex = keyof ParamSetByIndex;
class AttribAudioAnalyserSopParamsConfig extends NodeParamsConfig {
/** @param the point or object index this applies to */
index = ParamConfig.INTEGER('$F', {
range: [0, 100],
rangeLocked: [true, false],
});
/** @param values size */
valuesSize = ParamConfig.INTEGER(8);
/** @param sets if audioNode0 is read */
active0 = ParamConfig.BOOLEAN(0);
/** @param attribute name */
attrib0 = ParamConfig.STRING('audioNode0', {visibleIf: {active0: 1}});
/** @param audio node to read data from */
audioNode0 = ParamConfig.NODE_PATH('', {
nodeSelection: {
context: NodeContext.AUDIO,
types: AUDIO_ANALYSER_NODES,
},
visibleIf: {active0: 1},
});
/** @param sets if audioNode0 is read */
active1 = ParamConfig.BOOLEAN(0);
/** @param attribute name */
attrib1 = ParamConfig.STRING('audioNode0', {visibleIf: {active1: 1}});
/** @param audio node to read data from */
audioNode1 = ParamConfig.NODE_PATH('', {
nodeSelection: {
context: NodeContext.AUDIO,
types: AUDIO_ANALYSER_NODES,
},
visibleIf: {active1: 1},
});
/** @param sets if audioNode0 is read */
active2 = ParamConfig.BOOLEAN(0);
/** @param attribute name */
attrib2 = ParamConfig.STRING('audioNode0', {visibleIf: {active2: 1}});
/** @param audio node to read data from */
audioNode2 = ParamConfig.NODE_PATH('', {
nodeSelection: {
context: NodeContext.AUDIO,
types: AUDIO_ANALYSER_NODES,
},
visibleIf: {active2: 1},
});
}
const ParamsConfig = new AttribAudioAnalyserSopParamsConfig();
export class AttribAudioAnalyserSopNode extends TypedSopNode<AttribAudioAnalyserSopParamsConfig> {
override paramsConfig = ParamsConfig;
static override type() {
return 'attribAudioAnalyser';
}
override initializeNode() {
this.io.inputs.setCount(1);
this.io.inputs.initInputsClonedState(InputCloneMode.NEVER);
}
// private _attribIndex: number = 0;
override cook(inputCoreGroups: CoreGroup[]) {
this._initParamsByIndex();
this._registerOnTickHook();
const coreGroup = inputCoreGroups[0];
// if (this.scene().frame() == TimeController.START_FRAME) {
// this._attribIndex = 0;
// } else {
// this._attribIndex += 1;
// }
const objects = coreGroup.allObjects();
for (const object of objects) {
this._updateGeometry(object);
}
this.setCoreGroup(coreGroup);
}
override dispose() {
this._unRegisterOnTickHook();
}
private _updateGeometry<T extends CoreObjectType>(object: ObjectContent<T>) {
this._updateWithParamSet(object, '0');
this._updateWithParamSet(object, '1');
this._updateWithParamSet(object, '2');
}
private _updateWithParamSet<T extends CoreObjectType>(object: ObjectContent<T>, paramSetIndex: ParaSetIndex) {
if (!this._paramSetByIndex) {
return;
}
const corePointClass = corePointClassFactory(object);
const paramSet = this._paramSetByIndex[paramSetIndex];
if (!isBooleanTrue(paramSet.active.value)) {
return;
}
const attribName = paramSet.attribName.value;
let attrib = corePointClass.attribute(object, attribName) as BufferAttribute | undefined;
// create attrib if needed
if (!attrib) {
// const coreGeometry = new CoreGeometry(geometry);
corePointClass.addNumericAttribute(object, attribName, 1, 0);
attrib = corePointClass.attribute(object, attribName) as BufferAttribute;
}
// update buffer
const audioNode = paramSet.node.value.nodeWithContext(NodeContext.AUDIO);
const audioAnalyserNode = audioNode as BaseAnalyserAudioNode<any>;
if (!audioAnalyserNode.getAnalyserValue) {
return;
}
const values = audioAnalyserNode.getAnalyserValue();
if (!values) {
return;
}
const array = attrib.array;
const valuesSize = this.pv.valuesSize;
const analyserValuesCount = values.length;
const index = this.pv.index;
const offset = index * valuesSize;
for (let i = 0; i < valuesSize; i++) {
const value = i < analyserValuesCount ? values[i] : values[0];
array[offset + i] = value;
}
attrib.needsUpdate = true;
}
/*
* INIT
*/
private _paramSetByIndex: ParamSetByIndex | undefined;
private _initParamsByIndex() {
this._paramSetByIndex = this._paramSetByIndex || {
'0': {
active: this.p.active0,
attribName: this.p.attrib0,
node: this.p.audioNode0,
},
'1': {
active: this.p.active1,
attribName: this.p.attrib1,
node: this.p.audioNode1,
},
'2': {
active: this.p.active2,
attribName: this.p.attrib2,
node: this.p.audioNode2,
},
};
}
/*
* REGISTER TICK CALLBACK
*/
private async _registerOnTickHook() {
if (this.scene().registeredBeforeTickCallbacks().has(this._tickCallbackName())) {
return;
}
this.scene().registerOnBeforeTick(this._tickCallbackName(), () => this.setDirty());
}
private async _unRegisterOnTickHook() {
this.scene().unRegisterOnBeforeTick(this._tickCallbackName());
}
private _tickCallbackName() {
return `cop/audioAnalyserNode-${this.graphNodeId()}`;
}
}