fix
13404089107
7 天以前 a90bcdf047a8baf02aeec81221aeeb49db523cde
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
(function (window) {  
    //兼容  
    window.URL = window.URL || window.webkitURL;  
    //请求麦克风
    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;  
  
    var Recorder = function (stream, wsock) {  
        //创建一个音频环境对象  
        audioContext = window.AudioContext || window.webkitAudioContext;  
        var context = new audioContext();  
        
        config =  {};  
        config.channelCount = 1;
        config.numberOfInputChannels = config.channelCount;
        config.numberOfOutputChannels = config.channelCount;
        config.sampleBits = config.sampleBits || 16;      //采样数位 8, 16  
        //config.sampleRate = config.sampleRate || (context.sampleRate / 6);   //采样率(1/6 44100)
        config.sampleRate = config.sampleRate || 8000;   //采样率16K
        //创建缓存,用来缓存声音  
        config.bufferSize = 8192;
        var ws = wsock;
        //将声音输入这个对像  
        var audioInput = context.createMediaStreamSource(stream);  
          
        //设置音量节点  
        var volume = context.createGain();
        audioInput.connect(volume);  
  
        // 创建声音的缓存节点,createScriptProcessor方法的  
        // 第二个和第三个参数指的是输入和输出都是声道数。
        var recorder = context.createScriptProcessor(config.bufferSize, config.channelCount, config.channelCount); 
         
        //用来储存读出的麦克风数据,和压缩这些数据,将这些数据转换为WAV文件的格式
        var audioData = {  
            size: 0          //录音文件长度  
            , buffer: []     //录音缓存  
            , inputSampleRate: context.sampleRate    //输入采样率  
            , inputSampleBits: 16                    //输入采样数位 8, 16  
            , outputSampleRate: config.sampleRate    //输出采样率  
            , oututSampleBits: config.sampleBits     //输出采样数位 8, 16  
            , input: function (data) {  
                this.buffer = [];
                this.size = 0;
                this.buffer.push(new Float32Array(data));  //Float32Array
                this.size += data.length;  
            }  
            , getRawData: function () { //合并压缩  
                //合并  
                var data = new Float32Array(this.size);  
                var offset = 0;  
                for (var i = 0; i < this.buffer.length; i++) {
                    data.set(this.buffer[i], offset);  
                    offset += this.buffer[i].length;  
                }  
                //压缩
                var getRawDataion = parseInt(this.inputSampleRate / this.outputSampleRate);  
                var length = data.length / getRawDataion;  
                var result = new Float32Array(length);  
                var index = 0, j = 0;  
                while (index < length) {  
                    result[index] = data[j];  
                    j += getRawDataion;  
                    index++;  
                }  
                return result;
            }             
            
            ,closeContext:function(){
                context.close();   //关闭AudioContext否则录音多次会报错。
            }
            ,getPureWavData: function(offset) {
                var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits)
                var bytes = this.getRawData();  
                var dataLength = bytes.length * (sampleBits / 8);  
                var buffer = new ArrayBuffer(dataLength);  
                var data = new DataView(buffer);
                data = this.reshapeWavData(sampleBits, offset, bytes, data);                
//                var wavd = new Int8Array(data.buffer.byteLength);
//                var pos = 0;
//                for (var i = 0; i < data.buffer.byteLength; i++, pos++) {
//                    wavd[i] = data.getInt8(pos);
//                }                //                return wavd;
                
                //  return new Blob([data], { type: 'audio/wav' });
                return data;
 
            }            
            ,reshapeWavData: function(sampleBits, offset, iBytes, oData) {
                if (sampleBits === 8) {  
                    for (var i = 0; i < iBytes.length; i++, offset++) {  
                        var s = Math.max(-1, Math.min(1, iBytes[i]));  
                        var val = s < 0 ? s * 0x8000 : s * 0x7FFF;  
                        val = parseInt(255 / (65535 / (val + 32768)));  
                        oData.setInt8(offset, val, true);  
                    }  
                } else {  
                    for (var i = 0; i < iBytes.length; i++, offset += 2) {  
                        var s = Math.max(-1, Math.min(1, iBytes[i]));  
                        oData.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);  
                    }  
                } 
                return oData;
            }
             
        };  
  
        //开始录音  
        this.start = function () {
            audioInput.connect(recorder);  
            recorder.connect(context.destination);
        };  
  
        //停止  
        this.stop = function () {  
            recorder.disconnect();             
        
            audioData.closeContext();
        }
  
        
        var $bo=$("#inbo");
        var $change=$("#change");
        var width=$bo.width();
        //音频采集  
        recorder.onaudioprocess = function (e) {
            audioData.input(e.inputBuffer.getChannelData(0));
            
            var data = audioData.getPureWavData(0);
            if (ws != null)
                ws.send(data);
            
            
        };  
  
    };  
    //抛出异常  
    Recorder.throwError = function (message) {  
        throw new function () { this.toString = function () { return message; };};  
    };  
    //是否支持录音  
    Recorder.canRecording = (navigator.getUserMedia != null);  
    //获取录音机  
    Recorder.get = function (callback, wsock) {  
        if (callback) {  
            if (navigator.getUserMedia) {  
                navigator.getUserMedia(  
                    { audio: true } //只启用音频  A
                    , function (stream) {  //stream这个参数是麦克风的输入流,将这个流传递给Recorder
                        var rec = new Recorder(stream, wsock);  
                        callback(rec);  
                    }  
                    , function (error) {  
                        switch (error.code || error.name) {  
                            case 'PERMISSION_DENIED':  
                            case 'PermissionDeniedError':  
                                Recorder.throwError('用户拒绝提供信息。');  
                                break;  
                            case 'NOT_SUPPORTED_ERROR':  
                            case 'NotSupportedError':  
                                Recorder.throwError('浏览器不支持硬件设备。');  
                                break;  
                            case 'MANDATORY_UNSATISFIED_ERROR':  
                            case 'MandatoryUnsatisfiedError':  
                                Recorder.throwError('无法发现指定的硬件设备。');  
                                break;  
                            default:  
                                Recorder.throwError('无法打开麦克风。异常信息:' + (error.code || error.name));  
                                break;  
                        }  
                    });  
            } else {  
                Recorder.throwErr('当前浏览器不支持录音功能。'); return;  
            }  
        }  
    };  
    window.Recorder = Recorder;  
  
})(window);