ステレオサウンドテストページをVue 3/Nuxtで実装
React JSXで書いていたステレオテストページを、Vue 3 / Nuxt対応に変換した作業ログ。
背景
もともとstereo-test.jsxというReact向けのプロトタイプがあり、ヘッドホンの左右チャンネルに別々の音を流して分離を確認するページだった。これをNuxtのページとして正式に組み込みたかった。
主な変換ポイントは以下の通り。
- JSXの構文をVueの
<template>に書き換え useState/useRefをref()に置き換えuseEffectのクリーンアップをonUnmountedに対応- Canvas描画コンポーネントを別ファイルに切り出し
StereoWaveCanvasコンポーネント
波形アニメーションを描画するCanvasコンポーネントをStereoWaveCanvas.vueとして新規作成した。Nuxtのauto-import機能で、components/に置くだけでページ側から<StereoWaveCanvas />で使える。
コンポーネントのpropsは2つだけ。
const props = defineProps({
isActive: { type: Boolean, default: false },
color: { type: String, default: '#fff' },
})
isActiveがfalseのときは薄い水平線を描画し、trueになるとrequestAnimationFrameでサイン波のアニメーションが始まる。watchでpropsの変化を検知してアニメーションの開始/停止を切り替える仕組み。
描画のポイントは、複数の周波数のサイン波を重ね合わせて「それっぽい」波形にしているところ。
const a = Math.sin(t * Math.PI) * (h * 0.35) *
(0.6 + 0.25 * Math.sin(phase + t * 9) + 0.15 * Math.sin(phase * 1.4 + t * 14))
実際の音声波形ではなく、あくまで視覚的なフィードバック用の装飾アニメーション。
stereo-test.vue テストページ
メインのテストページでは、左右チャンネルそれぞれに音源を割り当てて再生する。
音源の種類
Web Audio APIのAudioBufferを直接生成している。外部ファイルは使わず、全てコード内で波形を計算する方式。
| 音源 | 内容 |
|---|---|
| 440Hz Tone | A音のサイン波(ビブラート付き) |
| 523Hz Tone | C音 + 倍音 |
| Click | 120BPMのメトロノーム |
| Pink Noise | フィルタードノイズ |
| Sweep | 200Hz - 2000Hzのスイープ |
| Melody | C-E-Gのアルペジオ |
ステレオパンニングの実装
StereoPannerNodeで左右の振り分けを制御する。L/RそれぞれにGainNode -> StereoPannerNodeのチェーンを作り、モード切替でpan.valueとgain.valueを操作する。
const lP = ctx.createStereoPanner(); lP.pan.value = -1 // 左
const rP = ctx.createStereoPanner(); rP.pan.value = 1 // 右
lG.connect(lP).connect(ctx.destination)
rG.connect(rP).connect(ctx.destination)
4つのモード(L/R Split, Mix, Left only, Right only)とバランススライダーの組み合わせで、再生中に動的にパンニングを変更できる。
index.vueへの導線追加
トップページの「テスト・デモ」セクションに「サウンド」カテゴリを新設し、ステレオテストへのナビカードを追加した。
<h3 class="demo-category-label">サウンド</h3>
<div class="nav-cards demo-cards">
<NuxtLink to="/stereo-test" class="nav-card demo-card">
...ステレオ出し分けテスト...
</NuxtLink>
</div>
今後サウンド関連のデモページが増えたときに、このカテゴリにぶら下げていける構成。
Chrome DevTools MCPでの動作確認
実装後の確認にChrome DevTools MCPを使った。確認できたのは以下。
- ページが正常にレンダリングされること
- 左右のサンプル選択UIが表示されていること
- 再生中に波形アニメーションとパルスドットが表示されること
- モード切替やバランススライダーのUI表示
音声の左右分離は実際にヘッドホンをつけて耳で確認する必要があり、MCPだけでは音の出力までは検証できない。UIの表示崩れや要素の欠落がないことの確認に使った。
元JSXファイルの削除
Vue移植が完了したため、元のstereo-test.jsxを削除して整理した。Nuxtのページとしてpages/stereo-test.vueが正式な置き場所になる。
振り返り
React -> Vueの変換で手間がかかるのは、JSXのインライン条件分岐(三項演算子の連鎖)をv-if / v-forに書き直すところ。逆に、Vueの<style scoped>でスタイルを閉じ込められるのはありがたい。
Web Audio APIでバッファを直接生成する方式は、音声ファイルの管理が不要でデプロイが楽。ただし音源の種類を増やすとgenerateBuffer関数が肥大化するので、音源定義を別ファイルに切り出す余地はある。