-2

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.

  • The function that needs to be bound to the correct context is one that's calling `createKey`. In this case, that function is anonymous - `this.noteFreq.forEach(function(keys, idx)`. You have a few options, but just make it an arrow function and be done with it. – Brian Thompson Nov 18 '21 at 19:32
  • Does this answer your question? [How to access the correct \`this\` inside a callback](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – Brian Thompson Nov 18 '21 at 19:34

1 Answers1

-1

Your error is probably due to the function keyword in your forEach loops. There you lose your this scope. You can fix it by using for of or an arrow function.

this.noteFreq.forEach((keys, idx) => {
    ...
    keyList.forEach((key) => { 
        ...
    }
}

Also, your code looks more like a normal ES6 class, and not a React class. In React, you don't use document.createElement and element.appendChild. Instead, you should use the render() function to render the document.

Kokodoko
  • 26,167
  • 33
  • 120
  • 197
  • Thank you this worked! I do use render as well but I was having trouble and tried ES6 until I could fix the problem. I can now reformat it in the render() function. – Antoine Lévesque-Roy Nov 18 '21 at 19:45