0

I am trying to create an app with the electron-react boilerplate where i can create audio files.

I found Tone.js, which allows me to play sounds but loading the sample files seems close to impossible. Is there a way to give the audio data to Tone?

I am using Sampler but i dont know how to create the URLs to transmit the data

4
  • Is this a repost? I swear I saw a question just like this recently...
    – Chris
    Commented Jul 3 at 11:36
  • Hi, yes. The question got marked as asking for recommendations (which i was not, but while re-reading my post i realised i formulated it like i did) and i deleted it and made sure to formulate my question better. I already deleted the old post Commented Jul 3 at 16:05
  • In the future, please don't do that: "Do not post your question a second time, as it will be closed as a duplicate of your first question, and may attract downvotes. Also, do not delete your question and re-ask it, as your previous question will be undeleted and your new one closed as a duplicate." Instead, please edit the existing question to make it on-topic.
    – Chris
    Commented Jul 3 at 16:24
  • In the pop-up where it said the question was closed, it said i can delete it and post another question that follows the guidelines. And that's what i did Commented Jul 4 at 5:12

2 Answers 2

0

Looking at the implementation of Player.load it can handle ArrayBuffers and luckily, using an <input type="file"> can be used to get an ArrayBuffer with a FileReader

Here is some sample code that assumes that there's a file input with id "local-file" in your HTML and you have Tone.js loaded at this point:

document.querySelector('#local-file').addEventListener('change', async function() {
  await Tone.start();
  // create a FileReader to process the chosen file
  const fileReader = new FileReader();
  // when it's read into an ArrayBuffer, we can access that in the result property of the event target
  fileReader.onload = async (event) => {
    // create a Tone.js Player connected to the audio output
    const player = new Tone.Player().toDestination();
    // create a ToneAudioBuffer from the ArrayBuffer with file contents. event.target.result is the ArrayBuffer with the file content
    const buffer = await player.context.decodeAudioData(event.target.result); 
    // add the buffer to our player    
    player.buffer = buffer;
    // once we're ready, start the player.
    Tone.loaded().then(() => {
      player.start();
    });
  }
  // read the selected file into an ArrayBuffer
  fileReader.readAsArrayBuffer(this.files[0]);  
});
4
  • So the idea is that i have my source code in src folder, and my audio files in another folder. Would this work if i were to use fileReader.readAsArrayBuffer("path/to/file.mp3")? Or maybe by reading the file to a Buffer and passing the buffer to the file reader? Commented Jul 3 at 8:30
  • Ah, I misinterpreted the original question. I assumed you were trying to have users select a file and play it back. Works roughly like that: 1. Read the data with fs.readFile or fs.readFileSync etc. with encoding set to buffer 2. Turn the buffer into an ArrayBuffer 3. Pass ArrayBuffer into Tone.Player constructor
    – geekonaut
    Commented Jul 3 at 10:22
  • I tried following those steps with the following code: let buffer = fs.readFileSync(rscPath + "/samples/piano/A4.mp3") const view = new Uint8Array(buffer).buffer; console.log(view) let player = new Tone.Player(view).toDestination(); Tone.loaded().then(() => { player.start(); }) but i just get the following error: Debug.js:17 Uncaught (in promise) RangeError: Value must be within [0, Infinity], got: NaN Commented Jul 3 at 15:36
  • That doesn't work, because readFileSync defaults to reading the data as a string. Use fs.readFileSync(rscPath + '/samples/piano/A4.mp3', {encoding: 'buffer') instead.
    – geekonaut
    Commented Jul 3 at 16:26
0

I ended up using a mix of the above answer and what i saw in the documentation:

let fileDataA4 = fs.readFileSync('path/to/A4.mp3');

// create a Tone.js Player connected to the audio output
const player = new Tone.Player().toDestination();

let arrayBufferA4 = await new Blob([fileDataA4]).arrayBuffer();

// create a ToneAudioBuffer from the ArrayBuffer with loaded buffer
const bufferA4 = await player.context.decodeAudioData(arrayBufferA4);
                

let fileDataA5 = fs.readFileSync("path/to/A5.mp3");

let arrayBufferA5 = await new Blob([fileDataA5]).arrayBuffer();

const bufferA5 = await player.context.decodeAudioData(arrayBufferA5);
                
let sampler = new Tone.Sampler({
        urls: {
            "A4" : bufferA4,
            "A5" : bufferA5
        }
}).toDestination();
                
Tone.loaded().then(() => {
    sampler.triggerAttackRelease("C4", "1n")
})

This way, you only need to load 2 mp3 files, the sampler does the rest for you

Not the answer you're looking for? Browse other questions tagged or ask your own question.