Skip to content

Commit 3efcf92

Browse files
committed
fix: Fixed the issue of audio duration loss in recorded files
fix: Fixed the issue with low recording volume on Safari browser
1 parent 70ce327 commit 3efcf92

File tree

5 files changed

+37
-44
lines changed

5 files changed

+37
-44
lines changed

hooks/useAudio.ts

+4-31
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1-
import { useState, useEffect, useCallback } from 'react'
2-
3-
async function getAudioObjectURL(src: string): Promise<string> {
4-
const blob = await fetch(src).then((resp) => resp.blob())
5-
return URL.createObjectURL(blob)
6-
}
1+
import { useState, useEffect, useCallback, useMemo } from 'react'
72

83
function useAudio(url: string) {
9-
const [audio] = useState<HTMLAudioElement>(new Audio())
104
const [playing, setPlaying] = useState<boolean>(false)
115
const [duration, setDuration] = useState<number>(0)
126
const [current, setCurrent] = useState<number>(0)
7+
const audio = useMemo(() => new Audio(url), [url])
138

149
const toggle = useCallback(() => {
1510
if (!playing) {
@@ -19,41 +14,19 @@ function useAudio(url: string) {
1914
}, [audio, playing])
2015

2116
const init = useCallback(async () => {
22-
const audioObjectURL = await getAudioObjectURL(url)
23-
audio.src = audioObjectURL
2417
audio.preload = 'auto'
25-
}, [audio, url])
26-
27-
useEffect(() => {
28-
playing ? audio.play() : audio.pause()
29-
}, [audio, playing])
18+
}, [audio])
3019

3120
useEffect(() => {
3221
init()
33-
let audioDuration = 0
3422
audio.addEventListener('ended', () => setPlaying(false))
3523
audio.addEventListener('loadeddata', () => {
36-
if (audio.duration === Infinity) {
37-
// HACK: Set a duration longer than the audio to get the actual duration of the audio
38-
audio.currentTime = 1e1
39-
} else {
40-
setDuration(audio.duration)
41-
audioDuration = audio.duration
42-
}
24+
setDuration(audio.duration)
4325
})
4426
audio.addEventListener('timeupdate', () => {
45-
if (audioDuration === 0) {
46-
audioDuration = audio.currentTime
47-
setDuration(audioDuration)
48-
setTimeout(() => {
49-
audio.currentTime = 0
50-
setCurrent(0)
51-
}, 0)
52-
}
5327
setCurrent(audio.currentTime)
5428
})
5529
return () => {
56-
audioDuration = 0
5730
audio.removeEventListener('ended', () => setPlaying(false))
5831
audio.removeEventListener('loadeddata', () => setDuration(0))
5932
audio.removeEventListener('timeupdate', () => setCurrent(0))

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "talk-with-gemini",
3-
"version": "0.12.1",
3+
"version": "0.12.2",
44
"private": true,
55
"author": "Amery2010 <[email protected]>",
66
"license": "GPL-3.0-only",
@@ -40,6 +40,7 @@
4040
"clipboard": "^2.0.11",
4141
"clsx": "^2.1.1",
4242
"crypto-js": "^4.2.0",
43+
"fix-webm-duration": "^1.0.5",
4344
"highlight.js": "^11.9.0",
4445
"i18next": "^23.11.5",
4546
"i18next-browser-languagedetector": "^7.2.1",

pnpm-lock.yaml

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/tauri.conf.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"package": {
1010
"productName": "talk-with-gemini",
11-
"version": "0.12.1"
11+
"version": "0.12.2"
1212
},
1313
"tauri": {
1414
"allowlist": {

utils/Recorder.ts

+22-11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fixWebmDuration from 'fix-webm-duration'
12
import { isFunction } from 'lodash-es'
23

34
export interface AudioRecorderPayload {
@@ -16,10 +17,10 @@ export interface RecordMineType {
1617
}
1718

1819
export class AudioRecorder {
19-
public blob: Blob | null = null
2020
public time: number = 0
2121
public isRecording: boolean = false
2222
public autoStop: boolean = false
23+
private startTime: number = 0
2324
protected audioContext: AudioContext
2425
protected mediaRecorder: MediaRecorder | null = null
2526
protected volumeThreshold: number = 30
@@ -82,7 +83,14 @@ export class AudioRecorder {
8283
} else {
8384
// 获取麦克风音频流
8485
navigator.mediaDevices
85-
.getUserMedia({ audio: true })
86+
.getUserMedia({
87+
audio: {
88+
sampleSize: 16,
89+
channelCount: 1,
90+
noiseSuppression: false,
91+
echoCancellation: false,
92+
},
93+
})
8694
.then((stream) => {
8795
this.recording(stream)
8896
})
@@ -110,6 +118,15 @@ export class AudioRecorder {
110118
// 将麦克风连接到分析器
111119
microphone.connect(analyser)
112120

121+
const finishRecord = async () => {
122+
const duration = Date.now() - this.startTime
123+
const blob = new Blob(chunks, { type: mediaRecorderType.mineType })
124+
const fixedBlob = await fixWebmDuration(blob, duration, { logger: false })
125+
this.onFinish(fixedBlob)
126+
this.startTime = 0
127+
chunks = []
128+
}
129+
113130
// 监听录音数据可用事件,将数据发送到服务器
114131
mediaRecorder.addEventListener('dataavailable', (ev) => {
115132
if (ev.data.size > 0) {
@@ -118,21 +135,15 @@ export class AudioRecorder {
118135
})
119136
mediaRecorder.addEventListener('start', () => {
120137
this.isRecording = true
138+
this.startTime = Date.now()
121139
this.startTimer()
122140
this.onStart()
123141
})
124142
mediaRecorder.addEventListener('pause', () => {
125-
const blob = new Blob(chunks)
126-
this.onFinish(blob)
127-
this.blob = blob
128-
chunks = []
143+
finishRecord()
129144
})
130145
mediaRecorder.addEventListener('stop', () => {
131-
const blob = new Blob(chunks)
132-
this.onFinish(blob)
133-
this.mediaRecorder = null
134-
this.blob = blob
135-
chunks = []
146+
finishRecord()
136147
stream.getTracks().forEach((track) => track.stop())
137148
})
138149

0 commit comments

Comments
 (0)