Skip to content

Commit

Permalink
fix: Fixed the issue of audio duration loss in recorded files
Browse files Browse the repository at this point in the history
fix: Fixed the issue with low recording volume on Safari browser
  • Loading branch information
Amery2010 committed Jun 26, 2024
1 parent 70ce327 commit 3efcf92
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 44 deletions.
35 changes: 4 additions & 31 deletions hooks/useAudio.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { useState, useEffect, useCallback } from 'react'

async function getAudioObjectURL(src: string): Promise<string> {
const blob = await fetch(src).then((resp) => resp.blob())
return URL.createObjectURL(blob)
}
import { useState, useEffect, useCallback, useMemo } from 'react'

function useAudio(url: string) {
const [audio] = useState<HTMLAudioElement>(new Audio())
const [playing, setPlaying] = useState<boolean>(false)
const [duration, setDuration] = useState<number>(0)
const [current, setCurrent] = useState<number>(0)
const audio = useMemo(() => new Audio(url), [url])

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

const init = useCallback(async () => {
const audioObjectURL = await getAudioObjectURL(url)
audio.src = audioObjectURL
audio.preload = 'auto'
}, [audio, url])

useEffect(() => {
playing ? audio.play() : audio.pause()
}, [audio, playing])
}, [audio])

useEffect(() => {
init()
let audioDuration = 0
audio.addEventListener('ended', () => setPlaying(false))
audio.addEventListener('loadeddata', () => {
if (audio.duration === Infinity) {
// HACK: Set a duration longer than the audio to get the actual duration of the audio
audio.currentTime = 1e1
} else {
setDuration(audio.duration)
audioDuration = audio.duration
}
setDuration(audio.duration)
})
audio.addEventListener('timeupdate', () => {
if (audioDuration === 0) {
audioDuration = audio.currentTime
setDuration(audioDuration)
setTimeout(() => {
audio.currentTime = 0
setCurrent(0)
}, 0)
}
setCurrent(audio.currentTime)
})
return () => {
audioDuration = 0
audio.removeEventListener('ended', () => setPlaying(false))
audio.removeEventListener('loadeddata', () => setDuration(0))
audio.removeEventListener('timeupdate', () => setCurrent(0))
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "talk-with-gemini",
"version": "0.12.1",
"version": "0.12.2",
"private": true,
"author": "Amery2010 <[email protected]>",
"license": "GPL-3.0-only",
Expand Down Expand Up @@ -40,6 +40,7 @@
"clipboard": "^2.0.11",
"clsx": "^2.1.1",
"crypto-js": "^4.2.0",
"fix-webm-duration": "^1.0.5",
"highlight.js": "^11.9.0",
"i18next": "^23.11.5",
"i18next-browser-languagedetector": "^7.2.1",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"package": {
"productName": "talk-with-gemini",
"version": "0.12.1"
"version": "0.12.2"
},
"tauri": {
"allowlist": {
Expand Down
33 changes: 22 additions & 11 deletions utils/Recorder.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import fixWebmDuration from 'fix-webm-duration'
import { isFunction } from 'lodash-es'

export interface AudioRecorderPayload {
Expand All @@ -16,10 +17,10 @@ export interface RecordMineType {
}

export class AudioRecorder {
public blob: Blob | null = null
public time: number = 0
public isRecording: boolean = false
public autoStop: boolean = false
private startTime: number = 0
protected audioContext: AudioContext
protected mediaRecorder: MediaRecorder | null = null
protected volumeThreshold: number = 30
Expand Down Expand Up @@ -82,7 +83,14 @@ export class AudioRecorder {
} else {
// 获取麦克风音频流
navigator.mediaDevices
.getUserMedia({ audio: true })
.getUserMedia({
audio: {
sampleSize: 16,
channelCount: 1,
noiseSuppression: false,
echoCancellation: false,
},
})
.then((stream) => {
this.recording(stream)
})
Expand Down Expand Up @@ -110,6 +118,15 @@ export class AudioRecorder {
// 将麦克风连接到分析器
microphone.connect(analyser)

const finishRecord = async () => {
const duration = Date.now() - this.startTime
const blob = new Blob(chunks, { type: mediaRecorderType.mineType })
const fixedBlob = await fixWebmDuration(blob, duration, { logger: false })
this.onFinish(fixedBlob)
this.startTime = 0
chunks = []
}

// 监听录音数据可用事件,将数据发送到服务器
mediaRecorder.addEventListener('dataavailable', (ev) => {
if (ev.data.size > 0) {
Expand All @@ -118,21 +135,15 @@ export class AudioRecorder {
})
mediaRecorder.addEventListener('start', () => {
this.isRecording = true
this.startTime = Date.now()
this.startTimer()
this.onStart()
})
mediaRecorder.addEventListener('pause', () => {
const blob = new Blob(chunks)
this.onFinish(blob)
this.blob = blob
chunks = []
finishRecord()
})
mediaRecorder.addEventListener('stop', () => {
const blob = new Blob(chunks)
this.onFinish(blob)
this.mediaRecorder = null
this.blob = blob
chunks = []
finishRecord()
stream.getTracks().forEach((track) => track.stop())
})

Expand Down

0 comments on commit 3efcf92

Please sign in to comment.