Skip to content

Instantly share code, notes, and snippets.

@carlows
Last active September 24, 2016 23:23
Show Gist options
  • Save carlows/3be56dc007be81ca05f563c4c909e3f4 to your computer and use it in GitHub Desktop.
Save carlows/3be56dc007be81ca05f563c4c909e3f4 to your computer and use it in GitHub Desktop.
Voice recording for the web spec

Goals

  • Entender como funciona voice recording en el navegador
  • Ser capacez de pedir al usuario el acceso al microfono de su PC
  • Grabar la voz del usuario
  • Ser capacez de hacer playback de lo que grabó el usuario

Hackaton results

Luego de algo de tiempo logré hacer setup de un react component que nos permite cumplir con todos los goals que tenemos, comencé utilizando create-react-app para hacer el quickstart de la aplicacion más rápido.

Para pedirle al usuario el acceso a video/audio utilizamos el siguiente metodo:

if (navigator.getUserMedia) {
  navigator.getUserMedia({ audio: true }, this.getAudio, this.handleError);
}

Si el usuario nos da acceso, podemos tener acceso al stream en tiempo real de lo que sea que se escucha en su microfono. Nuestro problema es que tenemos un raw streamMedia que tendriamos que convertir a WAV (o MP3, ogg, etc) manualmente.

Ya que no es algo que quisieramos hacer nosotros, encontré recorderjs una libreria que nos permite convertir el audio en ese stream media que obtenemos al formato que queramos.

this.recorder.exportWAV((stream) => {
  control.src = window.URL.createObjectURL(stream);
});

La implementación del componente completo se ve así:

import React, { Component } from 'react';
import classNames from 'classnames';
import Recorder from './recorder.js';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      recording: false,
      error: false
    };

    this.toggleRecord = this.toggleRecord.bind(this);
    this.handleError = this.handleError.bind(this);
    this.getAudio = this.getAudio.bind(this);
  }

  toggleRecord() {
    const { recording } = this.state;

    if (recording) {
      this.stopRecording();
    } else {
      this.startRecording();
    }

    this.setState({ recording: !recording });
  }

  startRecording() {
    if (navigator.getUserMedia) {
      navigator.getUserMedia({ audio: true }, this.getAudio, this.handleError);
    }
  }

  getAudio(response) {
    let context = new AudioContext();
    let mediaStreamSource = context.createMediaStreamSource(response);
    this.mediaStream = response;
    this.recorder = new Recorder(mediaStreamSource);
    this.recorder.record();
  }

  handleError(error) {
    this.setState({ error: true });
  }

  stopRecording() {
    const control = this.refs.control;

    this.mediaStream.getAudioTracks()[0].stop();

    this.recorder.stop();
    this.recorder.exportWAV((stream) => {
      control.src = window.URL.createObjectURL(stream);
    });
  }

  render() {
    const { recording, error } = this.state;

    const btnText = recording ? 'STOP' : 'RECORD';
    const classesBtn = classNames({ 'recording': recording });

    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Voice Recording Demo</h2>
        </div>
        <div className="App-content">
          <div>
            <audio ref='control' controls autoPlay></audio>
          </div>
          <div>
            <button disabled={error} className={classesBtn} onClick={this.toggleRecord}>{btnText}</button>
          </div>
        </div>
      </div>
    );
  }
}

export default App;

Un hint para un próximo avance en esto es que la función exportWAV nos da un blob de la data que tenemos en el Stream, y podemos usarla para guardarla en el servidor

recorder.stop();
recorder.exportWAV(function(audio) {
    var xhr = new XMLHttpRequest();
    xhr.open("POST", url, true);
    xhr.setRequestHeader("content-type", "audio/wav");
    xhr.onload = function(e) {
        // Handle the response.
    }
    xhr.send(audio);
});

Demo screencast:

https://www.opentest.co/share/8ea3ee8082ad11e69dae210557c9fab3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment