I am trying to make a simple synthesizer with React and the Web Audio API and having trouble with some methods. Here is my code:
import * as React from "react"
class Synth extends React.Component {
constructor(props) {
super(props);
this.state = {value: 0.5};
this.handleChange = this.handleChange.bind(this);
this.setup = this.setup.bind(this);
this.createKey = this.createKey.bind(this);
this.componentDidMount = this.componentDidMount.bind(this);
this.audioContext = null;
this.oscList = [];
this.mainGainNode = null;
this.wavePicker = document.querySelector("select[name='waveform']");
this.volumeControl = document.querySelector("input[name='volume']");
this.noteFreq = null;
this.customWaveform = null;
this.sineTerms = null;
this.cosineTerms = null;
}
componentDidMount(){
this.setup();
}
handleChange(event) {
this.setState({
value: event.target.value
});
}
createNoteTable(){
let noteFreq = [];
for (let i=0; i< 9; i++) {
noteFreq[i] = [];
}
noteFreq[3]["C"] = 130.81;
noteFreq[3]["C#"] = 138.59;
noteFreq[3]["D"] = 146.83;
noteFreq[3]["D#"] = 155.56;
noteFreq[3]["E"] = 164.81;
noteFreq[3]["F"] = 174.61;
noteFreq[3]["F#"] = 185.00;
noteFreq[3]["G"] = 196.00;
noteFreq[3]["G#"] = 207.65;
//etc...
return noteFreq;
}
createKey(note, octave, freq){
console.log("createKey() is firing");
let keyElement = document.createElement("li");
switch (freq) {
case 130.81:
keyElement.className = "white c1"
break;
case 146.83:
keyElement.className = "black cs1"
break;
case 164.81:
keyElement.className = "white c1"
break;
case 174.61:
keyElement.className = "white d1"
break;
//etc...
default:
break;
}
keyElement.dataset["freq"] = freq;
keyElement.dataset["note"] = note;
keyElement.dataset["octave"] = octave;
keyElement.addEventListener("mousedown", this.notePressed, false);
keyElement.addEventListener("mouseup", this.noteReleased, false);
keyElement.addEventListener("mouseover", this.notePressed, false);
keyElement.addEventListener("mouseleave", this.noteReleased, false);
return keyElement;
}
setup(){
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
this.noteFreq = this.createNoteTable();
this.mainGainNode = this.audioContext.createGain();
this.mainGainNode.connect(this.audioContext.destination);
this.mainGainNode.gain.value = this.state.value;
this.noteFreq.forEach(function(keys, idx) {
let keyList = Object.entries(keys);
let octaveElem = document.createElement("div");
keyList.forEach(function(key){
console.log("key[0] = " + key[0]);
console.log("idx = " + idx);
console.log("key[1] = " + key[1]);
try {
octaveElem.appendChild(this.createKey(key[0], idx, key[1]));
} catch(error){
console.log("Cannot create key... " + error);
}
});
});
this.sineTerms = new Float32Array([0, 0, 1, 0, 1]);
this.cosineTerms = new Float32Array(this.sineTerms.length);
this.customWaveform = this.audioContext.createPeriodicWave(this.cosineTerms, this.sineTerms);
for (let i=0; i<9; i++) {
this.oscList[i] = {};
}
}
then I have the notePressed() and noteReleased() functions but these seem to work fine.
The problem is when this.createKey() is called I get this error : TypeError: Cannot read properties of undefined (reading 'createKey')
As you can see, I tried binding pretty much every method I have to see if it would help but it didn't. Any help will be greatly appreciated.
Your error is probably due to the
functionkeyword in yourforEachloops. There you lose yourthisscope. You can fix it by usingfor ofor an arrow function.Also, your code looks more like a normal ES6 class, and not a React class. In React, you don't use
document.createElementandelement.appendChild. Instead, you should use therender()function to render the document.