web錄音——上傳錄音文件


捕獲麥克風

 

一、  前言   

  公司項目需要實現web錄音,剛剛好接手此功能,由於之前未接觸過,在網上找了些資料做對比

  1. )   https://www.cnblogs.com/starcrm/p/5109253.html  
  2. )  https://www.cnblogs.com/shihuc/p/9703508.html
  3. )   https://www.jb51.net/html5/611409.html
  4. )    https://xiaohuazheng.github.io/2018/10/03/getusermedia/等等

  寫本博客的目為了溫故而知新把學習過程記錄下來,以備后查。已經有幾年沒做過B/S 的項目,對HTML ,jqGrid,layui忘記的差不多了也不太熟所以看到不懂的語句就百度查 。

還是直接進入主題把,我們先了解下Form​Data 對象的使用:https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/Using_FormData_Objects 通過HTML表單創建FormData對象

1 var formData = new FormData();
2   formData.append("audioData", this.getBlob());
3   var xhr = new XMLHttpRequest();
4   xhr.open("POST", url);
5   xhr.send(formData);
View Code

 

在后台處理上傳代碼如下就可以,實例這里我用的一般處理程序,項目中就沒有用一並處理程序實現,項目是mvc架構這里不多說

 

  • 文件體積大如何處理?

    使用網上下載的DEMO,錄制保存后10秒文件就到達2M數據偏大怎么壓縮處理錄屏數據開始嘗試讀取每一段代碼,差不多花費了一天時間了解字節數據格式化成wav的格式的過程對比了幾篇文章   https://www.cnblogs.com/ranson7zop/p/7657874.html   https://blog.csdn.net/mlkiller/article/details/12567139

壓縮從上面三處着手處理,於是把雙聲道改為了單聲道,在錄音的只記錄一個聲道

 

雙聲道變為單聲道,數據直接縮小一半了

  • 繼續壓縮體積

除了聲道以外,還有一個可以縮減的地方就是采樣位數與采樣率 采樣位數默認是16位的,我們改成8位 又可以減少一半了,采樣率是44100 直接44100/6又減少一半

8和16的取值范圍不一樣

 

最終代碼整理以后 代碼

  1 /*!
  2  * zengzp
  3  * Date: 2019-06-04  
  4  */
  5 (function (window) {
  6     //兼容
  7     window.URL = window.URL || window.webkitURL;
  8     navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
  9 
 10     var HZRecorder = function (stream, config) {
 11         config = config || {};
 12         config.sampleBits = config.sampleBits || 8;      //采樣數位 8, 16
 13         config.sampleRate = config.sampleRate || (44100 / 6);   //采樣率(1/6 44100)
 14 
 15         //var context = new (window.webkitAudioContext || window.AudioContext)();
 16         //var audioInput = context.createMediaStreamSource(stream);
 17         //var createScript = context.createScriptProcessor || context.createJavaScriptNode;
 18         //var recorder = createScript.apply(context, [4096, 1, 1]);
 19 
 20         //創建一個音頻環境對象  
 21         audioContext = window.AudioContext || window.webkitAudioContext;
 22         var context = new audioContext();
 23 
 24         //將聲音輸入這個對像  
 25         var audioInput = context.createMediaStreamSource(stream);
 26 
 27         //設置音量節點  
 28         var volume = context.createGain();
 29         audioInput.connect(volume);
 30 
 31         //創建緩存,用來緩存聲音  
 32         var bufferSize = 4096;
 33 
 34         // 創建聲音的緩存節點,createScriptProcessor方法的  
 35         // 第二個和第三個參數指的是輸入和輸出都是雙聲道。  
 36         var recorder = context.createScriptProcessor(bufferSize, 2, 2);
 37 
 38      
 39 
 40 
 41         var audioData = {
 42             size: 0          //錄音文件長度
 43             , buffer: []     //錄音緩存
 44             , inputSampleRate: context.sampleRate    //輸入采樣率
 45             , inputSampleBits: 16       //輸入采樣數位 8, 16
 46             , outputSampleRate: config.sampleRate    //輸出采樣率
 47             , oututSampleBits: config.sampleBits       //輸出采樣數位 8, 16
 48             , input: function (data) {
 49                 this.buffer.push(new Float32Array(data));
 50                 this.size += data.length;
 51             }
 52             , compress: function () { //合並壓縮
 53                 //合並
 54                 var data = new Float32Array(this.size);
 55                 var offset = 0;
 56                 for (var i = 0; i < this.buffer.length; i++) {
 57                     data.set(this.buffer[i], offset);
 58                     offset += this.buffer[i].length;
 59                 }
 60                 //壓縮
 61                 var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
 62                 var length = data.length / compression;
 63                 var result = new Float32Array(length);
 64                 var index = 0, j = 0;
 65                 while (index < length) {
 66                     result[index] = data[j];
 67                     j += compression;
 68                     index++;
 69                 }
 70                 return result;
 71             }
 72             , encodeWAV: function () {
 73                 var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
 74                 var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
 75                 var bytes = this.compress();
 76                 var dataLength = bytes.length * (sampleBits / 8);
 77                 var buffer = new ArrayBuffer(44 + dataLength);
 78                 var data = new DataView(buffer);
 79 
 80                 var channelCount = 1;//單聲道
 81                 var offset = 0;
 82 
 83                 var writeString = function (str) {
 84                     for (var i = 0; i < str.length; i++) {
 85                         data.setUint8(offset + i, str.charCodeAt(i));
 86                     }
 87                 }
 88 
 89                 // 資源交換文件標識符 
 90                 writeString('RIFF'); offset += 4;
 91                 // 下個地址開始到文件尾總字節數,即文件大小-8 
 92                 data.setUint32(offset, 36 + dataLength, true); offset += 4;
 93                 // WAV文件標志
 94                 writeString('WAVE'); offset += 4;
 95                 // 波形格式標志 
 96                 writeString('fmt '); offset += 4;
 97                 // 過濾字節,一般為 0x10 = 16 
 98                 data.setUint32(offset, 16, true); offset += 4;
 99                 // 格式類別 (PCM形式采樣數據) 
100                 data.setUint16(offset, 1, true); offset += 2;
101                 // 通道數 
102                 data.setUint16(offset, channelCount, true); offset += 2;
103                 // 采樣率,每秒樣本數,表示每個通道的播放速度 
104                 data.setUint32(offset, sampleRate, true); offset += 4;
105                 // 波形數據傳輸率 (每秒平均字節數) 單聲道×每秒數據位數×每樣本數據位/8 
106                 data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;
107                 // 快數據調整數 采樣一次占用字節數 單聲道×每樣本的數據位數/8 
108                 data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
109                 // 每樣本數據位數 
110                 data.setUint16(offset, sampleBits, true); offset += 2;
111                 // 數據標識符 
112                 writeString('data'); offset += 4;
113                 // 采樣數據總數,即數據總大小-44 
114                 data.setUint32(offset, dataLength, true); offset += 4;
115                 // 寫入采樣數據 
116                 if (sampleBits === 8) {
117                     for (var i = 0; i < bytes.length; i++ , offset++) {
118                         var s = Math.max(-1, Math.min(1, bytes[i]));
119                         var val = s < 0 ? s * 0x8000 : s * 0x7FFF;
120                         val = parseInt(255 / (65535 / (val + 32768)));
121                         data.setInt8(offset, val, true);
122                     }
123                 } else {
124                     for (var i = 0; i < bytes.length; i++ , offset += 2) {
125                         var s = Math.max(-1, Math.min(1, bytes[i]));
126                         data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
127                     }
128                 }
129 
130                 return new Blob([data], { type: 'audio/wav' });
131             }
132         };
133 
134         //開始錄音
135         this.start = function () {
136             audioInput.connect(recorder);
137             recorder.connect(context.destination);
138         }
139 
140         //停止
141         this.stop = function () {
142             recorder.disconnect();
143         }
144 
145         //獲取音頻文件
146         this.getBlob = function () {
147             this.stop();
148             return audioData.encodeWAV();
149             //return audioData.finish();
150 
151         }
152 
153         //回放
154         this.play = function (audio) {
155             audio.src = window.URL.createObjectURL(this.getBlob());
156         }
157 
158         //上傳
159         this.upload = function (url, callback) {
160             var formData = new FormData();
161             formData.append("audioData", this.getBlob());
162             var xhr = new XMLHttpRequest();
163             if (callback) {
164                 xhr.upload.addEventListener("progress", function (e) {
165                     callback('uploading', e);
166                 }, false);
167                 xhr.addEventListener("load", function (e) {
168                     callback('ok', e);
169                 }, false);
170                 xhr.addEventListener("error", function (e) {
171                     callback('error', e);
172                 }, false);
173                 xhr.addEventListener("abort", function (e) {
174                     callback('cancel', e);
175                 }, false);
176             }
177             xhr.open("POST", url);
178             xhr.send(formData);
179         }
180 
181         //音頻采集
182         recorder.onaudioprocess = function (e) {
183             audioData.input(e.inputBuffer.getChannelData(0));
184             //record(e.inputBuffer.getChannelData(0));
185         }
186 
187     };
188     //拋出異常
189     HZRecorder.throwError = function (message) {
190         alert(message);
191         throw new function () { this.toString = function () { return message; } }
192     }
193     //是否支持錄音
194     HZRecorder.canRecording = (navigator.getUserMedia != null);
195     //獲取錄音機
196     HZRecorder.get = function (callback, config) {
197         if (callback) {
198             navigator.mediaDevices
199                 .getUserMedia({ audio: true })
200                 .then(function (stream) {
201                     let rec = new HZRecorder(stream, config);
202                     callback(rec);
203                 })
204                 .catch(function (error) {
205                     HZRecorder.throwError('無法錄音,請檢查設備狀態');
206                 });
207         }
208     }
209 
210     window.HZRecorder = HZRecorder;
211 
212 })(window);
HZRecorder.js

 自己寫了個Demo做測試我們先看下運行效果

demo用VS2017開發 看下Demo結構

源碼下載:HTML網頁錄音+c#服務器接受音頻

                 錄音Demo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM