Play and record chords in Tone.js

32 Views Asked by At

I am trying to code a web page in which an input is given a list of frequencies separated by spaces, and then play these frequencies with Tone.js.

First, I had this piece of code that only play notes one by one and works as expected:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Play and Record Notes</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.26/Tone.js"></script>
</head>
<body>
    <h1>Play and Record Notes</h1>
    <label for="frequenciesInput">Enter frequencies (separated by spaces):</label><br>
    <input type="text" id="frequenciesInput"><br>
    <button id="playButton">Play</button>

    <audio controls style="display: none;"></audio>

    <script>
        let playButton = document.getElementById('playButton');
        let inputNotes = document.getElementById('frequenciesInput');
        let started = false;

        playButton.addEventListener('click', () => {
            if (started) return;
            started = true;
            const audio = document.querySelector('audio');
            const synth = new Tone.Synth();
            const actx  = Tone.context;
            const dest  = actx.createMediaStreamDestination();
            const recorder = new MediaRecorder(dest.stream);

            synth.connect(dest);
            synth.toMaster();

            const chunks = [];

            const notes = inputNotes.value.split(' ').map(n => parseInt(n));

            let note = 0;
            Tone.Transport.cancel();
            Tone.Transport.scheduleRepeat(time => {
                if (note === 0) recorder.start();
                if (note > notes.length) {
                synth.triggerRelease(time)
                recorder.stop();
                Tone.Transport.stop();
                } else synth.triggerAttack(notes[note], time);
                note++;
            }, '4n');

            recorder.ondataavailable = evt => chunks.push(evt.data);
            recorder.onstop = evt => {
                let blob = new Blob(chunks, { type: 'audio/ogg; codecs=opus' });
                audio.src = URL.createObjectURL(blob);
                audio.style.display = 'block';
            };

            Tone.Transport.start();
            started = false;
        });
    </script>
</body>
</html>

Then I added a checkbox for playing notes in chord (i.e. simultaneously) with Tone.PolySynth :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Play and Record Notes</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.26/Tone.js"></script>
</head>
<body>
    <h1>Play and Record Notes</h1>
    <label for="frequenciesInput">Enter frequencies (separated by spaces):</label><br>
    <input type="text" id="frequenciesInput"><br>
    <input type="checkbox" id="playSimultaneously">
    <label for="playSimultaneously">Play all notes simultaneously</label><br>
    <button id="playButton">Play</button>

    <audio controls style="display: none;"></audio>

    <script>
        let playButton = document.getElementById('playButton');
        let inputNotes = document.getElementById('frequenciesInput');
        let checkboxSim = document.getElementById('playSimultaneously');
        let started = false;

        playButton.addEventListener('click', () => {
            if (started) return;
            started = true;
            const audio = document.querySelector('audio');
            const synth = new Tone.Synth();
            const polySynth = new Tone.PolySynth()
            const actx  = Tone.context;
            const dest  = actx.createMediaStreamDestination();
            const recorder = new MediaRecorder(dest.stream);

            synth.connect(dest);
            synth.toMaster();

            polySynth.connect(dest);
            polySynth.toMaster();

            const chunks = [];

            const notes = inputNotes.value.split(' ').map(n => parseInt(n));

            if (!checkboxSim.checked) {
                let note = 0;
                Tone.Transport.cancel();
                Tone.Transport.scheduleRepeat(time => {
                    if (note === 0) recorder.start();
                    if (note >= notes.length) {
                        synth.triggerRelease(time)
                        recorder.stop();
                        Tone.Transport.stop();
                    } else synth.triggerAttack(notes[note], time);
                    note++;
                }, '4n');
            }
            else {
            Tone.Transport.cancel();
                Tone.Transport.scheduleRepeat(time => {
                    recorder.start();
                    polySynth.triggerAttack(notes, time);
                    polySynth.triggerRelease(time);
                    recorder.stop();
                    Tone.Transport.stop();
                }, '4n');
            }

            recorder.ondataavailable = evt => chunks.push(evt.data);
            recorder.onstop = evt => {
                let blob = new Blob(chunks, { type: 'audio/ogg; codecs=opus' });
                audio.src = URL.createObjectURL(blob);
                audio.style.display = 'block';
            };

            Tone.Transport.start();
            started = false;
        });
    </script>
</body>
</html>

However, when I activate the 'Play all notes simultaneously' option, the chord is played but I get an error message in the console that says: net::ERR_REQUEST_RANGE_NOT_SATISFIABLE. What have I done wrong?

0

There are 0 best solutions below