Skip to content Skip to sidebar Skip to footer

How To Record Directly From A Webpage Using Javascript

I am working on making a little music app. I want to be able to record the sounds made in the browser, without relying on the microphone. So far, everything I have seen about the M

Solution 1:

So far, everything I have seen about the MediaRecorder api suggests that it relies on the mic.

No, MediaRecorder API does rely on MediaStreams, but these MediaStreams don't have to be LocalMediaStreams (i.e from gUM):

You can get a MediaStream from MediaElement (<audio>, <video>)'s captureStream() method, if the media loaded complies with the same-origin policy.

But this will return one MediaStream per MediaElement, and in your case it's probably not the best solution.

Instead, make the great jump to the Web Audio API, which anyway is more suitable for such an application as a drum-pad. The Web Audio API does have a createMediaStreamDestination() method which will return a MediaStreamAudioDestinationNode, which will contain a MediaStream in its .stream property. Every other nodes you'll connect to this MediaStreamAudioDestinationNode will be aired in the MediaStream, and you'll be able to record it from a MediaRecorder.

Let's recycle this drum-kit demo to include a recorder:

(functionmyFirstDrumKit() {

  const db_url = 'https://dl.dropboxusercontent.com/s/'; // all our medias are stored on dropbox// we'll need to first load all the audiosfunctioninitAudios() {
    const promises = drum.parts.map(part => {
      returnfetch(db_url + part.audio_src) // fetch the file
        .then(resp => resp.arrayBuffer()) // as an arrayBuffer
        .then(buf => drum.a_ctx.decodeAudioData(buf)) // then decode its audio data
        .then(AudioBuf => {
          part.buf = AudioBuf; // store the audioBuffer (won't change)returnPromise.resolve(part); // done
        });
    });
    returnPromise.all(promises); // when all are loaded
  }

  functioninitImages() {
    // in this version we have only an static image,// but we could have multiple per parts, with the same logic as for audiosvar img = newImage();
    img.src = db_url + drum.bg_src;
    drum.bg = img;
    returnnewPromise((res, rej) => {
      img.onload = res;
      img.onerror = rej;
    });
  }

  let general_solo = false;
  let part_solo = false;

  const drum = {
    a_ctx: newAudioContext(),
    generate_sound: (part) => {
      // called each time we need to play a sourceconst source = drum.a_ctx.createBufferSource();
      source.buffer = part.buf;
      source.connect(drum.gain);
      // to keep only one playing at a time// simply store this sourceNode, and stop the previous oneif(general_solo){
        // stop all playing sources
        drum.parts.forEach(p => (p.source && p.source.stop(0)));
        }
      elseif (part_solo && part.source) {
        // stop only the one of this part
        part.source.stop(0);
      }
      // store the source
      part.source = source;
      source.start(0);
    },
    parts: [{
        name: 'hihat',
        x: 90,
        y: 116,
        w: 160,
        h: 70,
        audio_src: 'kbgd2jm7ezk3u3x/hihat.mp3'
      },
      {
        name: 'snare',
        x: 79,
        y: 192,
        w: 113,
        h: 58,
        audio_src: 'h2j6vm17r07jf03/snare.mp3'
      },
      {
        name: 'kick',
        x: 80,
        y: 250,
        w: 200,
        h: 230,
        audio_src: '1cdwpm3gca9mlo0/kick.mp3'
      },
      {
        name: 'tom',
        x: 290,
        y: 210,
        w: 110,
        h: 80,
        audio_src: 'h8pvqqol3ovyle8/tom.mp3'
      }
    ],
    bg_src: '0jkaeoxls18n3y5/_drumkit.jpg?dl=0',
///////////////////////// The recording part//////////////////////    record: functionrecord(e) {
    	const btn = document.getElementById('record');
    	const chunks = [];
    	// init a new MediaRecorder with our StreamNode's streamconst recorder = newMediaRecorder(drum.streamNode.stream);
    	// save every chunks
    	recorder.ondataavailable = e => chunks.push(e.data);
    	// once we're done recording
    	recorder.onstop = e => {
    		// export our recordingconst blob = newBlob(chunks);
    		const url = URL.createObjectURL(blob);
    		// here in an <audio> elementconst a = newAudio(url);
    		a.controls = true;
    		document.getElementById('records').appendChild(a);
    		// reset default click handler
    		btn.onclick = drum.record;
    		btn.textContent = 'record';
    	}
    	btn.onclick = function () {
    		recorder.stop();
    	};
    	// start recording
    	recorder.start();
    	btn.textContent = 'stop recording';
    }
  };
  drum.gain = drum.a_ctx.createGain();
  drum.gain.gain.value = .5;
  drum.gain.connect(drum.a_ctx.destination);
  // for recording
  drum.streamNode = drum.a_ctx.createMediaStreamDestination();
  drum.gain.connect(drum.streamNode);
  
  document.getElementById('record').onclick = drum.record;


///////////////Unrelated to current question////////////functioninitCanvas() {
    const c = drum.canvas = document.createElement('canvas');
    const ctx = drum.ctx = c.getContext('2d');
    c.width = drum.bg.width;
    c.height = drum.bg.height;
    ctx.drawImage(drum.bg, 0, 0);
    document.body.appendChild(c);
    addEvents(c);
  }

  constisHover = (x, y) =>
    (drum.parts.filter(p => (p.x < x && p.x + p.w > x && p.y < y && p.y + p.h > y))[0] || false);


  functionaddEvents(canvas) {
    let mouse_hovered = false;
    canvas.addEventListener('mousemove', e => {
      mouse_hovered = isHover(e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop)
      if (mouse_hovered) {
        canvas.style.cursor = 'pointer';
      } else {
        canvas.style.cursor = 'default';
      }
    })
    canvas.addEventListener('mousedown', e => {
      e.preventDefault();
      if (mouse_hovered) {
        drum.generate_sound(mouse_hovered);
      }
    });
    const checkboxes = document.querySelectorAll('input');
    checkboxes[0].onchange = function() {
      general_solo = this.checked;
      general_solo && (checkboxes[1].checked = part_solo = true);
    };
    checkboxes[1].onchange = function() {
      part_solo = this.checked;
      !part_solo && (checkboxes[0].checked = general_solo = false);
    };
  }
  Promise.all([initAudios(), initImages()])
    .then(initCanvas);

})()
label{float: right}
<buttonid="record">record</button><label>general solo<inputtype="checkbox"></label><br><label>part solo<inputtype="checkbox"></label><br><divid="records"></div>

Post a Comment for "How To Record Directly From A Webpage Using Javascript"