Convert blob to WAV file without loosing data or compressing

15,051

As I can see in the metadatas of the file you've linked in the comment. You use a codec (here Opus) that compresses the file.

I see two solutions:

  • Reduce the compressing ratio of the codec if you can.
  • Use WAV container with an uncompressed configuration (for example PCM)

as you described me the problem in the chat I think it's more of the second solution. I don't use mediarecorder myself but I've found this method to check if a mime type works with it.

MediaRecorder.isTypeSupported("audio/wav;codecs=MS_PCM")

then you I suggest you change the mime type when creating the Blob to

new Blob(chunks, { 'type' : 'audio/wav; codecs=MS_PCM' });

or

new Blob(chunks, { 'type' : 'audio/wav; codecs=0' });
Share:
15,051
Emanuela Colta
Author by

Emanuela Colta

I am a junior full-stack web developer at a software enterprise in Romania (Mobiversal), specialized in mobile applications.Experience in: JavaScript,Node.js - Express, Angular 4 Python 2.7+ - Flask C# HTML,CSS, jQuery Firebase, PostgreSQL, MySQL

Updated on June 07, 2022

Comments

  • Emanuela Colta
    Emanuela Colta almost 2 years

    I am working on recording speeches and converting them to downloadable WAV files. I am using Angular6 and MediaRecorder. Firstly I obtain the blob and from the blob, I get the .wav file. The problem is that the WAV file (which can be played and sounds good) loss much of its properties during the process and is not a valid WAV. It keeps being a WebM file. For further processing, I need really valid and high-quality WAV files. In the end, I get files of ~20KB, instead of bigger files of ~300KB.

    My code looks like this:

    //convert Blob to File. Pass the blob and the file title as arguments
    var blobToFile = (theBlob: Blob, fileName:string): File => {
      var b: any = theBlob;
      //Add properties to the blob
      b.lastModifiedDate = new Date();
      b.name = fileName;
      return <File>theBlob;
    }
    
    var browser = <any>navigator;  
    var headers = new Headers();  
    var audioCtx = new AudioContext();
    var chunks =[];
    var constraints = { audio: true, video: false }; 
    
    var promisifiedOldGUM = function(constraints, successCallback, errorCallback) {
      var getUserMedia = (browser.getUserMedia || browser.webkitGetUserMedia || browser.mozGetUserMedia || browser.msGetUserMedia);
    
      // Some browsers just don't implement it - return a rejected promise with an error to keep a consistent interface
      if(!getUserMedia) {
        return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
      }
    
      // Otherwise, wrap the call to the old navigator.getUserMedia with a Promise
      return new Promise(function(successCallback, errorCallback) {
        getUserMedia.call(browser, constraints, successCallback, errorCallback);
      });
    
    }
    if (browser.mediaDevices.getUserMedia) {
      browser.mediaDevices.getUserMedia(constraints).then((stream) => { 
      this.mediaRecorder = new MediaRecorder(stream);        
    
    this.mediaRecorder.onstop  = function(){
            var last_bit= chunks[chunks.length-1];
            var blob = new Blob([last_bit], { 'type' : 'audio/wav' });
            var audioURL = window.URL.createObjectURL(blob);
            //convert Blob to file 
            var file = blobToFile(blob, "my-recording.wav"); 
    
            var link = document.createElement("a");
            link.href = audioURL;
            link.download = 'audio_recording_' + new Date().getTime() + '.wav';
            link.innerHTML = "download file";
            document.body.appendChild(link);  
    };     
    

    And the MediaRecorder setup in typings.d.ts file looks like this:

    declare class MediaRecorder extends EventTarget {
    
    // readonly mimeType: string;
    readonly MimeType: 'audio/x-wav';  // 'audio/vnd.wav';
    readonly state: 'inactive' | 'recording' | 'paused';
    readonly stream: MediaStream;
    ignoreMutedMedia: boolean;
    videoBitsPerSecond: number;
    audioBitsPerSecond: 16000//number;
    
    ondataavailable: (event : MediaRecorderDataAvailableEvent) => void;
    onerror: (event: MediaRecorderErrorEvent) => void;
    onpause: () => void;
    onresume: () => void;
    onstart: () => void;
    onstop: () => void;
    
    constructor(stream: MediaStream);
    
    start();
    
    stop();
    
    resume();
    
    pause();
    
    isTypeSupported(type: string): boolean;
    
    requestData();
    
    
    addEventListener<K extends keyof MediaRecorderEventMap>(type: K, listener: (this: MediaStream, ev: MediaRecorderEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
    
    addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
    
    removeEventListener<K extends keyof MediaRecorderEventMap>(type: K, listener: (this: MediaStream, ev: MediaRecorderEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
    
    removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
    
    } 
    

    I don't know how can change the blobToFile() function in order to preserve the recording quality during conversion. If you can help me with this, I would appreciate very much.

  • Emanuela Colta
    Emanuela Colta over 5 years
    Thank you! I didn't set it anywhere. But as I can see from the detailed information provided here get-metadata.com/result/10525caf-9757-4b4d-910e-abf60b62655b‌​, the Audio Sample Rate is 48000Hz and Audio Bits Per Sample is 32
  • Emanuela Colta
    Emanuela Colta over 5 years
    Actually, the detailed analysis provided here get-metadata.com/result/10525caf-9757-4b4d-910e-abf60b62655b is identifying that the Audio Bits Per Sample is 32 and, still for Streams, the Bits Per Sample is 0. I think the problem is here.
  • Emanuela Colta
    Emanuela Colta over 5 years
    Yes, I did. They sound well. Listen to it here: drive.google.com/file/d/1zMGkgkA5EW7iCYSL7Kv7mePnEpMokJ0N/…
  • Emanuela Colta
    Emanuela Colta over 5 years
    I don't have an initial audio file. I just record my voice. Based on the recording I get a blob and based on the blob, I generate the audio file.
  • JSmith
    JSmith over 5 years
  • Emanuela Colta
    Emanuela Colta over 5 years
    not yet. I am considering switching from MediaRecorder to Recorder.js
  • Emanuela Colta
    Emanuela Colta over 5 years
    I am opening it now
  • Armin Hierstetter
    Armin Hierstetter over 3 years
    This does not work. And I can not see how it potentially could: How would the blob call know it needs to convert from webm? I tried this and the file created was still in webm format. That's the reason why you could listen to it. It still was a valid sound file, but it was a webm, not a wave.