1+ ( function ( window ) {
2+ //兼容
3+ window . URL = window . URL || window . webkitURL ;
4+ navigator . getUserMedia = navigator . getUserMedia || navigator . webkitGetUserMedia || navigator . mozGetUserMedia || navigator . msGetUserMedia ;
5+
6+
7+ var HZRecorder = function ( stream , config ) {
8+ config = config || { } ;
9+ config . sampleBits = config . sampleBits || 16 ; //采样数位 8, 16
10+ config . sampleRate = config . sampleRate || ( 8000 ) ; //采样率(1/6 44100)
11+
12+
13+ var context = new AudioContext ( ) ;
14+ var audioInput = context . createMediaStreamSource ( stream ) ;
15+ var recorder = context . createScriptProcessor ( 4096 , 1 , 1 ) ;
16+
17+
18+ var audioData = {
19+ size : 0 //录音文件长度
20+ , buffer : [ ] //录音缓存
21+ , inputSampleRate : context . sampleRate //输入采样率
22+ , inputSampleBits : 16 //输入采样数位 8, 16
23+ , outputSampleRate : config . sampleRate //输出采样率
24+ , oututSampleBits : config . sampleBits //输出采样数位 8, 16
25+ , input : function ( data ) {
26+ this . buffer . push ( new Float32Array ( data ) ) ;
27+ this . size += data . length ;
28+ }
29+ , compress : function ( ) { //合并压缩
30+ //合并
31+ var data = new Float32Array ( this . size ) ;
32+ var offset = 0 ;
33+ for ( var i = 0 ; i < this . buffer . length ; i ++ ) {
34+ data . set ( this . buffer [ i ] , offset ) ;
35+ offset += this . buffer [ i ] . length ;
36+ }
37+ //压缩
38+ var compression = parseInt ( this . inputSampleRate / this . outputSampleRate ) ;
39+ var length = data . length / compression ;
40+ var result = new Float32Array ( length ) ;
41+ var index = 0 , j = 0 ;
42+ while ( index < length ) {
43+ result [ index ] = data [ j ] ;
44+ j += compression ;
45+ index ++ ;
46+ }
47+ return result ;
48+ }
49+ , encodeWAV : function ( ) {
50+ var sampleRate = Math . min ( this . inputSampleRate , this . outputSampleRate ) ;
51+ var sampleBits = Math . min ( this . inputSampleBits , this . oututSampleBits ) ;
52+ var bytes = this . compress ( ) ;
53+ var dataLength = bytes . length * ( sampleBits / 8 ) ;
54+ var buffer = new ArrayBuffer ( 44 + dataLength ) ;
55+ var data = new DataView ( buffer ) ;
56+
57+
58+ var channelCount = 1 ; //单声道
59+ var offset = 0 ;
60+
61+
62+ var writeString = function ( str ) {
63+ for ( var i = 0 ; i < str . length ; i ++ ) {
64+ data . setUint8 ( offset + i , str . charCodeAt ( i ) ) ;
65+ }
66+ }
67+
68+ // 资源交换文件标识符
69+ writeString ( 'RIFF' ) ; offset += 4 ;
70+ // 下个地址开始到文件尾总字节数,即文件大小-8
71+ data . setUint32 ( offset , 36 + dataLength , true ) ; offset += 4 ;
72+ // WAV文件标志
73+ writeString ( 'WAVE' ) ; offset += 4 ;
74+ // 波形格式标志
75+ writeString ( 'fmt ' ) ; offset += 4 ;
76+ // 过滤字节,一般为 0x10 = 16
77+ data . setUint32 ( offset , 16 , true ) ; offset += 4 ;
78+ // 格式类别 (PCM形式采样数据)
79+ data . setUint16 ( offset , 1 , true ) ; offset += 2 ;
80+ // 通道数
81+ data . setUint16 ( offset , channelCount , true ) ; offset += 2 ;
82+ // 采样率,每秒样本数,表示每个通道的播放速度
83+ data . setUint32 ( offset , sampleRate , true ) ; offset += 4 ;
84+ // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8
85+ data . setUint32 ( offset , channelCount * sampleRate * ( sampleBits / 8 ) , true ) ; offset += 4 ;
86+ // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8
87+ data . setUint16 ( offset , channelCount * ( sampleBits / 8 ) , true ) ; offset += 2 ;
88+ // 每样本数据位数
89+ data . setUint16 ( offset , sampleBits , true ) ; offset += 2 ;
90+ // 数据标识符
91+ writeString ( 'data' ) ; offset += 4 ;
92+ // 采样数据总数,即数据总大小-44
93+ data . setUint32 ( offset , dataLength , true ) ; offset += 4 ;
94+ // 写入采样数据
95+ if ( sampleBits === 8 ) {
96+ for ( var i = 0 ; i < bytes . length ; i ++ , offset ++ ) {
97+ var s = Math . max ( - 1 , Math . min ( 1 , bytes [ i ] ) ) ;
98+ var val = s < 0 ? s * 0x8000 : s * 0x7FFF ;
99+ val = parseInt ( 255 / ( 65535 / ( val + 32768 ) ) ) ;
100+ data . setInt8 ( offset , val , true ) ;
101+ }
102+ } else {
103+ for ( var i = 0 ; i < bytes . length ; i ++ , offset += 2 ) {
104+ var s = Math . max ( - 1 , Math . min ( 1 , bytes [ i ] ) ) ;
105+ data . setInt16 ( offset , s < 0 ? s * 0x8000 : s * 0x7FFF , true ) ;
106+ }
107+ }
108+
109+
110+ return new Blob ( [ data ] , { type : 'audio/wav' } ) ;
111+ }
112+ } ;
113+
114+
115+ //开始录音
116+ this . start = function ( ) {
117+ audioInput . connect ( recorder ) ;
118+ recorder . connect ( context . destination ) ;
119+ }
120+
121+
122+ //停止
123+ this . stop = function ( ) {
124+ recorder . disconnect ( ) ;
125+ }
126+
127+
128+ //获取音频文件
129+ this . getBlob = function ( ) {
130+ this . stop ( ) ;
131+ return audioData . encodeWAV ( ) ;
132+ }
133+
134+
135+ //回放
136+ this . play = function ( audio ) {
137+ audio . src = window . URL . createObjectURL ( this . getBlob ( ) ) ;
138+ }
139+
140+
141+ //上传
142+ this . upload = function ( url , callback ) {
143+ var fd = new FormData ( ) ;
144+ fd . append ( "audioData" , this . getBlob ( ) ) ;
145+ var xhr = new XMLHttpRequest ( ) ;
146+ if ( callback ) {
147+ xhr . upload . addEventListener ( "progress" , function ( e ) {
148+ callback ( 'uploading' , e ) ;
149+ } , false ) ;
150+ xhr . addEventListener ( "load" , function ( e ) {
151+ callback ( 'ok' , e ) ;
152+ } , false ) ;
153+ xhr . addEventListener ( "error" , function ( e ) {
154+ callback ( 'error' , e ) ;
155+ } , false ) ;
156+ xhr . addEventListener ( "abort" , function ( e ) {
157+ callback ( 'cancel' , e ) ;
158+ } , false ) ;
159+ }
160+ xhr . open ( "POST" , url ) ;
161+ xhr . send ( fd ) ;
162+ }
163+
164+
165+ //音频采集
166+ recorder . onaudioprocess = function ( e ) {
167+ audioData . input ( e . inputBuffer . getChannelData ( 0 ) ) ;
168+ //record(e.inputBuffer.getChannelData(0));
169+ }
170+
171+
172+ } ;
173+ //抛出异常
174+ HZRecorder . throwError = function ( message ) {
175+ alert ( message ) ;
176+ throw new function ( ) { this . toString = function ( ) { return message ; } }
177+ }
178+ //是否支持录音
179+ HZRecorder . canRecording = ( navigator . getUserMedia != null ) ;
180+ //获取录音机
181+ HZRecorder . get = function ( callback , config ) {
182+ if ( callback ) {
183+ if ( navigator . getUserMedia ) {
184+ navigator . getUserMedia (
185+ { audio : true } //只启用音频
186+ , function ( stream ) {
187+ var rec = new HZRecorder ( stream , config ) ;
188+ callback ( rec ) ;
189+ }
190+ , function ( error ) {
191+ switch ( error . code || error . name ) {
192+ case 'PERMISSION_DENIED' :
193+ case 'PermissionDeniedError' :
194+ HZRecorder . throwError ( '用户拒绝提供信息。' ) ;
195+ break ;
196+ case 'NOT_SUPPORTED_ERROR' :
197+ case 'NotSupportedError' :
198+ HZRecorder . throwError ( '浏览器不支持硬件设备。' ) ;
199+ break ;
200+ case 'MANDATORY_UNSATISFIED_ERROR' :
201+ case 'MandatoryUnsatisfiedError' :
202+ HZRecorder . throwError ( '无法发现指定的硬件设备。' ) ;
203+ break ;
204+ default :
205+ HZRecorder . throwError ( '无法打开麦克风。异常信息:' + ( error . code || error . name ) ) ;
206+ break ;
207+ }
208+ } ) ;
209+ } else {
210+ HZRecorder . throwErr ( '当前浏览器不支持录音功能。' ) ; return ;
211+ }
212+ }
213+ }
214+
215+
216+ window . HZRecorder = HZRecorder ;
217+
218+
219+ } ) ( window ) ;
0 commit comments