123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- <template>
- <div class="voice-recorder">
- <button
- @mousedown="startRecording"
- @mouseup="stopRecording"
- @touchstart="startRecording"
- @touchend="stopRecording"
- :disabled="isRecording"
- >
- {{ isRecording ? '录音中...' : '按住说话' }}
- </button>
- <div class="volume-indicator" :style="{ width: volume + '%' }"></div>
- <div v-if="transcript" class="transcript">
- <h3>识别结果:</h3>
- <p>{{ transcript }}</p>
- </div>
- <div v-if="error" class="error">{{ error }}</div>
- </div>
- </template>
- <script>
- import { ref } from 'vue';
- export default {
- name: 'VoiceRecorder',
- setup() {
- const isRecording = ref(false);
- const volume = ref(0);
- const transcript = ref('');
- const error = ref('');
- let mediaStream = null;
- let audioContext = null;
- let analyser = null;
- let microphone = null;
- let scriptProcessor = null;
- let recognition = null;
- const startRecording = async () => {
- try {
-
- transcript.value = '';
- error.value = '';
- isRecording.value = true;
- volume.value = 0;
-
- mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
-
- audioContext = new (window.AudioContext || window.webkitAudioContext)();
- analyser = audioContext.createAnalyser();
- microphone = audioContext.createMediaStreamSource(mediaStream);
- scriptProcessor = audioContext.createScriptProcessor(2048, 1, 1);
- analyser.smoothingTimeConstant = 0.8;
- analyser.fftSize = 1024;
- microphone.connect(analyser);
- analyser.connect(scriptProcessor);
- scriptProcessor.connect(audioContext.destination);
-
- scriptProcessor.onaudioprocess = () => {
- const array = new Uint8Array(analyser.frequencyBinCount);
- analyser.getByteFrequencyData(array);
- let values = 0;
- const length = array.length;
- for (let i = 0; i < length; i++) {
- values += array[i];
- }
- const average = values / length;
- volume.value = Math.min(100, Math.max(0, average * 0.5));
- };
-
- if ('webkitSpeechRecognition' in window) {
- recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
- recognition.continuous = true;
- recognition.interimResults = true;
- recognition.onresult = (event) => {
- let interimTranscript = '';
- let finalTranscript = '';
- for (let i = event.resultIndex; i < event.results.length; i++) {
- const transcript = event.results[i][0].transcript;
- if (event.results[i].isFinal) {
- finalTranscript += transcript;
- } else {
- interimTranscript += transcript;
- }
- }
- transcript.value = finalTranscript || interimTranscript;
- };
- recognition.onerror = (event) => {
- console.log(event)
- error.value = `识别错误: ${event.error}`;
- };
- recognition.start();
- } else {
- error.value = '您的浏览器不支持语音识别';
- }
- } catch (err) {
- error.value = `错误: ${err.message}`;
- isRecording.value = false;
- }
- };
- const stopRecording = () => {
- isRecording.value = false;
- volume.value = 0;
-
- if (scriptProcessor) {
- scriptProcessor.disconnect();
- scriptProcessor = null;
- }
- if (microphone) {
- microphone.disconnect();
- microphone = null;
- }
- if (analyser) {
- analyser.disconnect();
- analyser = null;
- }
-
- if (mediaStream) {
- mediaStream.getTracks().forEach(track => track.stop());
- mediaStream = null;
- }
-
- if (recognition) {
- recognition.stop();
- recognition = null;
- }
-
- if (audioContext && audioContext.state !== 'closed') {
- audioContext.close();
- audioContext = null;
- }
- };
- return {
- isRecording,
- volume,
- transcript,
- error,
- startRecording,
- stopRecording
- };
- }
- };
- </script>
- <style scoped>
- .voice-recorder {
- max-width: 500px;
- margin: 0 auto;
- padding: 20px;
- text-align: center;
- }
- button {
- padding: 12px 24px;
- font-size: 16px;
- background-color: #4CAF50;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- transition: background-color 0.3s;
- }
- button:disabled {
- background-color: #cccccc;
- cursor: not-allowed;
- }
- button:hover:not(:disabled) {
- background-color: #45a049;
- }
- .volume-indicator {
- height: 10px;
- background-color: #4CAF50;
- margin: 15px 0;
- transition: width 0.1s;
- border-radius: 5px;
- }
- .transcript {
- margin-top: 20px;
- padding: 15px;
- background-color: #f5f5f5;
- border-radius: 4px;
- text-align: left;
- }
- .error {
- color: #f44336;
- margin-top: 15px;
- }
- </style>
|