mirror of
https://github.com/kevin-DL/vue-audio-recorder.git
synced 2026-01-23 15:51:26 +00:00
Add existing code
This commit is contained in:
110
src/lib/recorder.js
Normal file
110
src/lib/recorder.js
Normal 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
22
src/lib/utils.js
Normal 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
59
src/lib/wav-encoder.js
Normal 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user