Skip to content

Commit 366344b

Browse files
committed
Fixed a bug.
1 parent 2f29ed1 commit 366344b

File tree

3 files changed

+273
-0
lines changed

3 files changed

+273
-0
lines changed
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
<template>
2+
<div>
3+
<b-alert :show="!supported">{{ $t('audio-support-msg')}}</b-alert>
4+
<div v-if="supported">
5+
<b-button v-if="!isRecording && !hasRecording" @click="record" variant="danger">
6+
{{ $t('start-button') }}
7+
</b-button>
8+
<div v-if="isRecording" class="container-fluid">
9+
<div class="pids-wrapper">
10+
<div class="pid"></div>
11+
<div class="pid"></div>
12+
<div class="pid"></div>
13+
<div class="pid"></div>
14+
<div class="pid"></div>
15+
<div class="pid"></div>
16+
<div class="pid"></div>
17+
<div class="pid"></div>
18+
<div class="pid"></div>
19+
<div class="pid"></div>
20+
</div>
21+
</div>
22+
<!-- <b-button v-if="isRecording" @click="stop">stop</b-button>-->
23+
<div v-if="isRecording">
24+
<small>{{timeRemaining}} {{ $t('x-seconds-left') }}</small>
25+
</div>
26+
<b-button variant="success" v-if="hasRecording && !isPlaying" @click="play" ref="play">
27+
<span> {{ $t('play-button') }} </span>
28+
</b-button>
29+
<b-button variant="secondary"
30+
v-if="hasRecording && isPlaying" @click="pause" ref="play">
31+
<span> {{ $t('pause-button') }} </span>
32+
</b-button>
33+
34+
<div v-if="hasRecording" class="mt-2">
35+
<a href="" @click="reset">{{ $t('redo-recording') }}</a>
36+
</div>
37+
<div v-if="hasRecording" class="mt-2">
38+
<a href="" @click="saveAndContinue">{{ $t('continue') }}</a>
39+
</div>
40+
</div>
41+
</div>
42+
</template>
43+
44+
<style>
45+
.pids-wrapper{
46+
width: 100%;
47+
background-color: white;
48+
}
49+
.pid{
50+
width: calc(8% - 10px);
51+
height: 10px;
52+
display: inline-block;
53+
margin: 5px;
54+
}
55+
</style>
56+
57+
<script>
58+
import _ from 'lodash';
59+
60+
61+
const MediaStreamRecorder = require('msr');
62+
63+
export default {
64+
name: 'audioRecord',
65+
props: {
66+
init: {
67+
type: [String, Blob],
68+
},
69+
mode: {
70+
type: String,
71+
default: 'audioRecord',
72+
},
73+
constraints: {
74+
type: Object,
75+
},
76+
},
77+
data() {
78+
return {
79+
recording: {},
80+
isRecording: false,
81+
hasRecording: false,
82+
audioCtx: {},
83+
audioConstraints: { audio: true, video: true },
84+
// chunks: [],
85+
mediaRecorder: {},
86+
supported: null,
87+
interval: {},
88+
timeRemaining: null,
89+
isPlaying: false,
90+
selectedImage: null,
91+
hasError: false,
92+
};
93+
},
94+
computed: {
95+
recordingTime() {
96+
return this.constraints['http://schema.org/maxValue'][0]['@value'];
97+
},
98+
},
99+
methods: {
100+
record() {
101+
this.isRecording = true;
102+
this.mediaRecorder.start(this.recordingTime);
103+
this.interval = setInterval(this.countdown, 1000);
104+
},
105+
countdown() {
106+
if (this.timeRemaining <= 0) {
107+
clearInterval(this.interval);
108+
} else {
109+
this.timeRemaining -= 1;
110+
}
111+
},
112+
play() {
113+
this.recording.play();
114+
this.isPlaying = true;
115+
},
116+
pause() {
117+
this.recording.pause();
118+
this.endPlay();
119+
},
120+
endPlay() {
121+
this.isPlaying = false;
122+
},
123+
saveAndContinue(e) {
124+
e.preventDefault();
125+
this.$emit('valueChanged', this.recording.blob);
126+
},
127+
stop() {
128+
this.mediaRecorder.stop();
129+
this.hasRecording = true;
130+
this.isRecording = false;
131+
clearInterval(this.interval);
132+
this.timeRemaining = this.recordingTime / 1000;
133+
// this.$emit('valueChanged', this.recording.blob);
134+
},
135+
reset(e) {
136+
e.preventDefault();
137+
this.hasRecording = false;
138+
this.isRecording = false;
139+
navigator.mediaDevices.getUserMedia(this.audioConstraints).then(this.initialize, this.error);
140+
},
141+
initialize(audioStream) {
142+
this.mediaRecorder = new MediaStreamRecorder(audioStream);
143+
this.mediaRecorder.mimeType = 'audio/wav'; // check this line for audio/wav
144+
this.timeRemaining = this.recordingTime / 1000;
145+
window.mediaRecorder = this.mediaRecorder;
146+
const self = this;
147+
this.mediaRecorder.ondataavailable = (e) => {
148+
const blobURL = URL.createObjectURL(e);
149+
self.recording.src = blobURL;
150+
self.recording.blob = e;
151+
self.stop();
152+
};
153+
154+
// check audio level
155+
const analyser = this.audioCtx.createAnalyser();
156+
const microphone = this.audioCtx.createMediaStreamSource(audioStream);
157+
const scriptNode = this.audioCtx.createScriptProcessor(2048, 1, 1);
158+
159+
analyser.smoothingTimeConstant = 0.8;
160+
analyser.fftSize = 1024;
161+
162+
microphone.connect(analyser);
163+
analyser.connect(scriptNode);
164+
scriptNode.connect(this.audioCtx.destination);
165+
scriptNode.onaudioprocess = () => {
166+
const array = new Uint8Array(analyser.frequencyBinCount);
167+
analyser.getByteFrequencyData(array);
168+
let values = 0;
169+
170+
const length = array.length;
171+
for (let i = 0; i < length; i += 1) {
172+
values += (array[i]);
173+
}
174+
const average = values / length;
175+
176+
// console.log(77, Math.round(average));
177+
const allPids = document.getElementsByClassName('pid');
178+
const amoutOfPids = Math.round(average / 10);
179+
for (let i = 0; i < allPids.length; i += 1) {
180+
allPids[i].style.backgroundColor = '#e6e7e8';
181+
}
182+
183+
if (amoutOfPids <= allPids.length) {
184+
for (let i = 0; i < amoutOfPids; i += 1) {
185+
allPids[i].style.backgroundColor = '#69ce2b';
186+
}
187+
}
188+
};
189+
},
190+
error() {
191+
console.log(191);
192+
this.hasError = true;
193+
// const notification = 'Uh-oh! Voice input needs fixing.<br><br>' + '<img src="../../../assets/Check-Mark.svg" alt="Nature" class="responsive"><br>' + 'Please change your browser\'s microphone permissions in order to answer these questions.';
194+
// const options = {
195+
// html: true,
196+
// };
197+
// this.$dialog.alert(notification, options);
198+
},
199+
},
200+
watch: {
201+
init() {
202+
if (this.init === 'skip' || this.init === 'dontKnow') {
203+
this.hasRecording = false;
204+
}
205+
},
206+
},
207+
mounted() {
208+
this.recording = new Audio();
209+
this.recording.onended = this.endPlay;
210+
211+
// Older browsers might not implement mediaDevices at all, so we set an empty object first
212+
if (navigator.mediaDevices === undefined) {
213+
navigator.mediaDevices = {};
214+
}
215+
216+
// Some browsers partially implement mediaDevices. We can't just assign an object
217+
// with getUserMedia as it would overwrite existing properties.
218+
// Here, we will just add the getUserMedia property if it's missing.
219+
if (navigator.mediaDevices.getUserMedia === undefined) {
220+
navigator.mediaDevices.getUserMedia = (constraints) => {
221+
// First get ahold of the legacy getUserMedia, if present
222+
const getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia
223+
|| navigator.msGetUserMedia;
224+
225+
// Some browsers just don't implement it - return a rejected promise with an error
226+
// to keep a consistent interface
227+
if (!getUserMedia) {
228+
this.supported = false;
229+
return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
230+
}
231+
232+
// Otherwise, wrap the call to the old navigator.getUserMedia with a Promise
233+
return new Promise(((resolve, reject) => {
234+
getUserMedia.call(navigator, constraints, resolve, reject);
235+
}));
236+
};
237+
}
238+
239+
// set up forked web audio context, for multiple browsers
240+
// window. is needed otherwise Safari explodes
241+
const AudioContext = window.AudioContext || window.webkitAudioContext;
242+
this.audioCtx = new AudioContext();
243+
if (navigator.mediaDevices.getUserMedia) {
244+
this.supported = true;
245+
navigator.mediaDevices.getUserMedia(this.audioConstraints).then(this.initialize, this.error);
246+
if (this.init) {
247+
if (_.isString(this.init)) {
248+
if (this.init.startsWith('blob')) {
249+
this.recording.src = this.init;
250+
this.hasRecording = true;
251+
} else {
252+
this.hasRecording = false;
253+
}
254+
} else if (this.init instanceof Blob) {
255+
const blobURL = URL.createObjectURL(this.init);
256+
this.recording.src = blobURL;
257+
this.recording.blob = this.init;
258+
this.hasRecording = true;
259+
} else {
260+
this.hasRecording = false;
261+
}
262+
}
263+
// console.log('getUserMedia API supported');
264+
} else {
265+
this.supported = false;
266+
// console.log(259, 'Getusermedia API is not supported on this browser');
267+
}
268+
},
269+
};
270+
</script>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// eslint-disable-next-line
2+
export { default } from './VideoCheck.vue';

src/components/Inputs/WebAudioRecord/Audio.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
navigator.mediaDevices.enumerateDevices().then((devices) => {
104104
const audioInputDevices = devices.filter((device) => device.kind === 'audioinput');
105105
this.devices = audioInputDevices.map((device) => device.label || `Microphone ${device.deviceId}`);
106+
this.tempDeviceName = devices[0]
106107
}).catch((err) => {
107108
console.error("Error enumerating devices:", err);
108109
});

0 commit comments

Comments
 (0)