手机端浏览器音频自动播放

最近在尝试做一个文字转语音的功能,使用第三方的接口进行文字转语音,接口传入文字,返回音频文件,然后自动播放,这个流程在PC运行正常的,但是到了手机端,发现没有效果,因为手机端的浏览器默认不需要audio自动播放,需要明显的用户操作,比如点击才可以。

为了解决这个问题,方法也很简单,就是在用户之前的点击行为后,先创建几个audio,然后将将创建的audio缓存下来,直接播放再停止,那么这几个缓存的audio就可以突破浏览器自动播放的限制。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
export class AudioPlayer {
private audioQueue: Array<{ audio: HTMLAudioElement; chunkId: number }> = [];
private currentAudio: HTMLAudioElement | null = null;
private unlockedAudioQueue: HTMLAudioElement[] = [];
private nextChunkId = 0;

private statusChange: (playing: Boolean) => void;

constructor(statusChange: (playing: Boolean) => void) {
this.statusChange = statusChange;
}

public init() {
const count = 10;
if (this.unlockedAudioQueue.length < count) {
for (let i = this.unlockedAudioQueue.length; i < count; i++) {
const audio = new Audio();
audio.play();
audio.pause();
audio.currentTime = 0;
this.unlockedAudioQueue.push(audio);
}
}
console.log(this.unlockedAudioQueue);
}

public addAudioToQueue(audioUrl: string, chunkId: number) {
const newAudio = this.unlockedAudioQueue.shift();
newAudio.src = audioUrl;
this.audioQueue.push({ audio: newAudio, chunkId });
this.audioQueue = this.audioQueue.sort((a, b) => a.chunkId - b.chunkId);
console.log("addAudioToQueue", this.nextChunkId, chunkId, audioUrl);
if (!this.currentAudio) {
this.playNext();
}
}

public stop() {
if (this.currentAudio) {
this.currentAudio.pause();
this.currentAudio.src = null;
this.currentAudio.currentTime = 0;
}
this.audioQueue = [];
}

private playNext(retryCount = 0) {
console.log("playNext", this.audioQueue, this.nextChunkId, retryCount);
if (this.audioQueue.length > 0) {
if (this.audioQueue[0].chunkId !== this.nextChunkId && retryCount < 3) {
setTimeout(() => {
this.playNext(retryCount + 1);
}, 300);
return;
}
this.statusChange(true);
const audioQueueItem = this.audioQueue.shift();
this.currentAudio = audioQueueItem.audio;
this.nextChunkId = audioQueueItem.chunkId + 1;
if (this.currentAudio) {
this.currentAudio.onended = () => {
if (this.currentAudio) {
this.currentAudio.src = null;
this.currentAudio.pause();
this.currentAudio.currentTime = 0;
}

this.currentAudio = null;
this.playNext();
};
this.currentAudio?.play();
}
} else {
this.statusChange(false);
}
}
}

在用户前置点击的操作中,调用init,缓存10个audio,当真的需要播放音频的时候,再将音频blob传入,chunkId的逻辑是因为,我的需求中,一段长文字返回后一起识别延迟比较明显,所以就并行请求,但是为了播放的正常顺序,加一个id,大家可以根据自己需求来