音声が複数個あっても順に再生したい!!!
非同期処理って難しいですね…
テキストを音声化して再生するといった動作を実装したのですがそこでハマったことをメモとして書き出していきます。
概要
- 音楽ファイルを用意する
- 音楽ファイルを DataURI Scheme 形式(base64) にエンコードする
- await を使って順に再生するように実装する
1. 音楽ファイルを用意する
適当な音楽ファイルを用意して読み込みます。
File API とか使えば読み込めるかと思います。
今回はサーバーにテキストを送ると音声データが返ってくる仕様だったので読み込む動作は割愛します。
2. 音楽ファイルを DataURI Scheme 形式(base64) にエンコードする
ローカルから読み込む場合は、File API の URL.createObjectURL()
使えばエンコードできると思います。
今回は送られてきた音声データを base64 にエンコードして返してくれるように自作 API を実装しました。
3. await を使って順に再生するように実装する
ここでループ回して await 使って音声再生… で終わると思っていたのですが、音声がすべて同時に再生されてしまい、うまくいきませんでした。
そこで Promise オブジェクトを作成し、音声の再生終了後までループが止まるように実装したところ、うまく動かすことができました。
for (const d of data) {
const targetSoundBase64 = d.base64;
// 音声の再生が終わるまでループを回さないように止めておく
await new Promise((resolve) => {
const sound = new Audio("data:audio/wav;base64," + targetSoundBase64);
sound.play();
sound.addEventListener('ended', async () => {
// 音声終了後にいきなり次の音声が再生されてると違和感がすごいのでちょっとスリープかける
await sleep(100);
resolve();
}, {once: true});
});
}
最初は音声の秒数を取得してその曲が終わるタイミングで次の曲が再生されるように setTimeout を使おうかと考えていたのですが、こっちのほうがいい感じのコードになるかなと思われます。
まとめ
テキストチャットの音声化のような複数の音声データを順に再生するような状況になった場合、このように実装すればいいのではないでしょうか。
非同期処理、わかるようでわからず難しいなーと感じました。
もっと勉強頑張ります😇