オーディオデータの波形描画

LEFT CHANNEL
RIGHT CHANNEL
LOOP
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
(function() {
 
    var onDOMContentLoaded = function() {
 
        window.AudioContext = window.AudioContext || window.webkitAudioContext;
 
        try {
            // Create the instance of AudioContext
            var context = new AudioContext();
        } catch (error) {
            window.alert(error.message + ' : Please use Chrome or Safari.');
            return;
        }
 
        // for the instance of AudioBufferSourceNode
        var source = null;
 
        // for legacy browsers
        context.createGain = context.createGain || context.createGainNode;
 
        // Create the instance of GainNode
        var gain = context.createGain();
 
        var drawAudio = function(canvas, data, sampleRate) {
            var canvasContext = canvas.getContext('2d');
 
            var width  = canvas.width;
            var height = canvas.height;
 
            var paddingTop    = 20;
            var paddingBottom = 20;
            var paddingLeft   = 30;
            var paddingRight  = 30;
 
            var innerWidth  = width  - paddingLeft - paddingRight;
            var innerHeight = height - paddingTop  - paddingBottom;
            var innerBottom = height - paddingBottom;
 
            // Sampling period
            var period = 1 / sampleRate;
 
            // This value is the number of samples during 50 msec
            var n50msec = Math.floor(50 * Math.pow(10, -3) * sampleRate);
 
            // Clear previous data
            canvasContext.clearRect(0, 0, width, height);
 
            // Draw audio wave
            canvasContext.beginPath();
 
            for (var i = 0, len = data.length; i < len; i++) {
                // 50 msec ?
                if ((i % n50msec) === 0) {
                    var x = Math.floor((i / len) * innerWidth) + paddingLeft;
                    var y = Math.floor(((1 - data[i]) / 2) * innerHeight) + paddingTop;
 
                    if (i === 0) {
                        canvasContext.moveTo(x, y);
                    } else {
                        canvasContext.lineTo(x, y);
                    }
                }
            }
 
            canvasContext.strokeStyle = 'rgba(0, 0, 255, 1.0)';
            canvasContext.lineWidth   = 0.5;
            canvasContext.lineCap     = 'round';
            canvasContext.lineJoin    = 'miter';
            canvasContext.stroke();
        };
 
        // Trigger 'ended' event
        var trigger = function() {
            var event = document.createEvent('Event');
            event.initEvent('ended', true, true);
 
            if (source instanceof AudioBufferSourceNode) {
                source.dispatchEvent(event);
            }
        };
 
        // This funciton is executed after getting ArrayBuffer of audio data
        var startAudio = function(arrayBuffer) {
 
            // The 2nd argument for decodeAudioData
            var successCallback = function(audioBuffer) {
                // The 1st argument (audioBuffer) is the instance of AudioBuffer
 
                // Get audio binary data for drawing wave
                var channelLs = new Float32Array(audioBuffer.length);
                var channelRs = new Float32Array(audioBuffer.length);
 
                console.log('numberOfChannels : ' + audioBuffer.numberOfChannels);
 
                // Stereo ?
                if (audioBuffer.numberOfChannels > 1) {
                    // Stereo
                    channelLs.set(audioBuffer.getChannelData(0));
                    channelRs.set(audioBuffer.getChannelData(1));
 
                    drawAudio(document.querySelectorAll('canvas')[0], channelLs, audioBuffer.sampleRate);
                    drawAudio(document.querySelectorAll('canvas')[1], channelRs, audioBuffer.sampleRate);
                } else if (audioBuffer.numberOfChannels > 0) {
                    // Monaural
                    channelLs.set(audioBuffer.getChannelData(0));
                    drawAudio(document.querySelectorAll('canvas')[0], channelLs, audioBuffer.sampleRate);
                } else {
                    window.alert('The number of channels is invalid.');
                    return;
                }
 
                // If there is previous AudioBufferSourceNode, program stops previous audio
                if ((source instanceof AudioBufferSourceNode) && (source.buffer instanceof AudioBuffer)) {
                    // Execute onended event handler
                    trigger();
                    source = null;
                }
 
                // Create the instance of AudioBufferSourceNode
                source = context.createBufferSource();
 
                // for legacy browsers
                source.start = source.start || source.noteOn;
                source.stop  = source.stop  || source.noteOff;
 
                // Set the instance of AudioBuffer
                source.buffer = audioBuffer;
 
                // Set parameters
                source.playbackRate.value = document.getElementById('range-playback-rate').valueAsNumber;
                source.loop               = document.querySelector('[type="checkbox"]').checked;
 
                // AudioBufferSourceNode (Input) -> GainNode (Volume) -> AudioDestinationNode (Output)
                source.connect(gain);
                gain.connect(context.destination);
 
                // Start audio
                source.start(0);
 
                // Set Callback
                source.onended = function(event) {
                    // Remove event handler
                    source.onended     = null;
                    document.onkeydown = null;
 
                    // Stop audio
                    source.stop(0);
 
                    console.log('STOP by "on' + event.type + '" event handler !!');
 
                    // Audio is not started !!
                    // It is necessary to create the instance of AudioBufferSourceNode again
 
                    // Cannot replay
                    // source.start(0);
                };
 
                // Stop audio
                document.onkeydown = function(event) {
                    // Space ?
                    if (event.keyCode !== 32) {
                        return;
                    }
 
                    // Execute onended event handler
                    trigger();
 
                    return false;
                };
            };
 
            // The 3rd argument for decodeAudioData
            var errorCallback = function(error) {
                if (error instanceof Error) {
                    window.alert(error.message);
                } else {
                    window.alert('Error : "decodeAudioData" method.');
                }
            };
 
            // Create the instance of AudioBuffer (Asynchronously)
            context.decodeAudioData(arrayBuffer, successCallback, errorCallback);
        };
 
        /*
         * File Uploader
         */
 
        document.getElementById('file-upload-audio').addEventListener('change', function(event) {
            var uploader     = this;
            var progressArea = document.getElementById('progress-file-upload-audio');
 
            // Get the instance of File (extends Blob)
            var file = event.target.files[0];
 
            if (!(file instanceof File)) {
                window.alert('Please upload file.');
            } else if (file.type.indexOf('audio') === -1) {
                window.alert('Please upload audio file.');
            } else {
                // Create the instance of FileReader
                var reader = new FileReader();
 
                reader.onprogress = function(event) {
                    if (event.lengthComputable && (event.total > 0)) {
                        var rate = Math.floor((event.loaded / event.total) * 100);
                        progressArea.textContent = rate + ' %';
                    }
                };
 
                reader.onerror = function() {
                    window.alert('FileReader Error : Error code is ' + reader.error.code);
                    uploader.value = '';
                };
 
                // Success read
                reader.onload = function() {
                    var arrayBuffer = reader.result;  // Get ArrayBuffer
 
                    startAudio(arrayBuffer);
 
                    uploader.value           = '';
                    progressArea.textContent = file.name;
                };
 
                // Read the instance of File
                reader.readAsArrayBuffer(file);
            }
        }, false);
 
        // Control Volume
        document.getElementById('range-volume').addEventListener('input', function() {
            var min = gain.gain.minValue || 0;
            var max = gain.gain.maxValue || 1;
 
            if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) {
                gain.gain.value = this.valueAsNumber;
                document.getElementById('output-volume').textContent = this.value;
            }
        }, false);
 
        // Control playbackRate
        document.getElementById('range-playback-rate').addEventListener('input', function() {
            if (source instanceof AudioBufferSourceNode) {
                var min = source.playbackRate.minValue || 0;
                var max = source.playbackRate.maxValue || 1024;
 
                if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) {
                    source.playbackRate.value = this.valueAsNumber;
                }
            }
 
            document.getElementById('output-playback-rate').textContent = this.value;
        }, false);
 
        // Toggle loop
        document.querySelector('[type="checkbox"]').addEventListener(EventWrapper.CLICK, function() {
            if (source instanceof AudioBufferSourceNode) {
                source.loop = this.checked;
            }
        }, false);
    };
 
    if ((document.readyState === 'interactive') || (document.readyState === 'complete')) {
        onDOMContentLoaded();
    } else {
        document.addEventListener('DOMContentLoaded', onDOMContentLoaded, true);
    }
 
})();
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
function EventWrapper(){
}
 
(function(){
    var click = '';
    var start = '';
    var move  = '';
    var end   = '';
 
    // Touch Panel ?
    if (/iPhone|iPad|iPod|Android/.test(navigator.userAgent)) {
        click = 'click';
        start = 'touchstart';
        move  = 'touchmove';
        end   = 'touchend';
    } else {
        click = 'click';
        start = 'mousedown';
        move  = 'mousemove';
        end   = 'mouseup';
    }
 
    EventWrapper.CLICK = click;
    EventWrapper.START = start;
    EventWrapper.MOVE  = move;
    EventWrapper.END   = end;
})();