Add existing code

This commit is contained in:
Gennady Grishkovtsov
2018-07-23 12:00:58 +03:00
parent ff7cb590c5
commit 95a9700d50
22 changed files with 10295 additions and 2 deletions

110
src/lib/recorder.js Normal file
View File

@@ -0,0 +1,110 @@
import WavEncoder from './wav-encoder'
import { convertTimeMMSS } from './utils'
export default class {
constructor (options = {}) {
this.afterStop = options.afterStop
this.micFailed = options.micFailed
this.bufferSize = 4096
this.records = []
this.samples = []
this.isPause = false
this.isRecording = false
this.duration = 0
this.volume = 0
this._duration = 0
}
start () {
navigator.mediaDevices.getUserMedia({audio: true})
.then(this._micCaptured.bind(this))
.catch(this._micError.bind(this))
this.isPause = false
this.isRecording = true
}
stop () {
this.stream.getTracks().forEach((track) => track.stop())
this.input.disconnect()
this.processor.disconnect()
this.context.close()
let encoder = new WavEncoder({
bufferSize: this.bufferSize,
sampleRate: this.context.sampleRate,
samples: this.samples
})
let audioBlob = encoder.getData()
let audioUrl = URL.createObjectURL(audioBlob)
this.samples = []
this.records.push({
blob: audioBlob,
url: audioUrl,
duration: convertTimeMMSS(this.duration)
})
this.isPause = false
this.isRecording = false
this._duration = 0
this.duration = 0
if (this.afterStop) {
this.afterStop()
}
}
pause () {
this.stream.getTracks().forEach((track) => track.stop())
this.input.disconnect()
this.processor.disconnect()
this.context.close()
this._duration = this.duration
this.isPause = true
}
recordList () {
return this.records
}
lastRecord () {
return this.records.slice(-1)
}
_micCaptured (stream) {
this.context = new AudioContext()
this.input = this.context.createMediaStreamSource(stream)
this.processor = this.context.createScriptProcessor(this.bufferSize, 1, 1)
this.duration = this._duration
this.stream = stream
this.processor.onaudioprocess = (ev) => {
let sample = ev.inputBuffer.getChannelData(0)
let sum = 0.0
for (let i = 0; i < sample.length; ++i) {
sum += sample[i] * sample[i]
}
this.duration = parseFloat(this._duration) + parseFloat(this.context.currentTime.toFixed(2))
this.volume = Math.sqrt(sum / sample.length).toFixed(2)
this.samples.push(new Float32Array(sample))
}
this.input.connect(this.processor)
this.processor.connect(this.context.destination)
}
_micError (error) {
if (this.micFailed) {
this.micFailed(error)
}
}
}

22
src/lib/utils.js Normal file
View File

@@ -0,0 +1,22 @@
export function calculateLineHeadPosition (ev, element) {
let progressWidth = element.getBoundingClientRect().width
let leftPosition = ev.target.getBoundingClientRect().left
let pos = (ev.clientX - leftPosition) / progressWidth
try {
if (!ev.target.className.match(/^ar\-line\-control/)) {
return
}
} catch (err) {
return
}
pos = pos < 0 ? 0 : pos
pos = pos > 1 ? 1 : pos
return pos
}
export function convertTimeMMSS (seconds) {
return new Date(seconds * 1000).toISOString().substr(14, 5)
}

59
src/lib/wav-encoder.js Normal file
View File

@@ -0,0 +1,59 @@
export default class {
constructor (options) {
this.bufferSize = options.bufferSize || 4096
this.sampleRate = options.sampleRate
this.samples = options.samples
}
getData () {
this._joinSamples()
let buffer = new ArrayBuffer(44 + this.samples.length * 2)
let view = new DataView(buffer)
this._writeString(view, 0, 'RIFF') // RIFF identifier
view.setUint32(4, 36 + this.samples.length * 2, true) // RIFF chunk length
this._writeString(view, 8, 'WAVE') // RIFF type
this._writeString(view, 12, 'fmt ') // format chunk identifier
view.setUint32(16, 16, true) // format chunk length
view.setUint16(20, 1, true) // sample format (raw)
view.setUint16(22, 1, true) // channel count
view.setUint32(24, this.sampleRate, true) // sample rate
view.setUint32(28, this.sampleRate * 4, true) // byte rate (sample rate * block align)
view.setUint16(32, 4, true) // block align (channel count * bytes per sample)
view.setUint16(34, 16, true) // bits per sample
this._writeString(view, 36, 'data') // data chunk identifier
view.setUint32(40, this.samples.length * 2, true) // data chunk length
this._floatTo16BitPCM(view, 44, this.samples)
return new Blob([view], {type: 'audio/wav'})
}
_floatTo16BitPCM (output, offset, input) {
for (let i = 0; i < input.length; i++, offset += 2) {
let s = Math.max(-1, Math.min(1, input[i]))
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true)
}
}
_joinSamples () {
let recordLength = this.samples.length * this.bufferSize
let joinedSamples = new Float64Array(recordLength)
let offset = 0
for (let i = 0; i < this.samples.length; i++) {
let sample = this.samples[i]
joinedSamples.set(sample, offset)
offset += sample.length
}
this.samples = joinedSamples
}
_writeString (view, offset, string) {
for (let i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i))
}
}
}