みかづきブログ・カスタム

基本的にはちょちょいのほいです。

WebAudioAPIとAudio MIDI 設定を使って、Macのスピーカとイヤホンから別々の音を再生する 🔈

通常Macにイヤホンを繋いだ場合、音源はイヤホンから再生されます。
しかし、Macにはデフォルトアプリとして「Audio MIDI 設定」が入っており、オーディオのインアウトを好きなようにカスタマイズできます。

support.apple.com

そして、Audio MIDI 設定とWebAudioAPIを組み合わせると、

  • Macのスピーカー(左)
  • Macのスピーカー(右)
  • イヤホン(左)
  • イヤホン(右)

の全てから異なる音源を再生することができます。

Macの設定

  • アプリケーション > その他 > Audio MIDI 設定を開く
  • 左下の+ボタンを押して危機セットを作成
  • MacBookのスピーカーとイヤホンの使用にチェックを入れる


  • 右下のスピーカーを構成...をクリック
  • 構成を4チャンネルに設定

これでOKです。

index.htmlの作成

<html>
  <head>
    <meta charset="UTF-8">
    <title>web-audio-api-4ch</title>
    <meta name="viewport" content="width=device-width">
  </head>
  <body>
    <button id="load">load</button>
    <span style="display: none;">
      <button id="a">a</button>
      <button id="b">b</button>
      <button id="c">c</button>
      <button id="d">d</button>
    </span>
    <script>
      document.getElementById('load').addEventListener('click', async () => {
        const channelCount = 4;
        const audioCtx = new AudioContext();

        if (audioCtx.destination.maxChannelCount < channelCount) {
          return alert(`${channelCount}チャンネル欲しいけど、${audioCtx.destination.maxChannelCount}チャンネルしかありません`);
        }

        const merger = audioCtx.createChannelMerger(channelCount);

        audioCtx.destination.channelCount = channelCount;

        const [
          controllerA,
          controllerB,
          controllerC,
          controllerD
        ] = await Promise.all([
          loadAudio('a.mp3', 0),
          loadAudio('b.mp3', 1),
          loadAudio('c.mp3', 2),
          loadAudio('d.mp3', 3)
        ]);

        document.getElementById('load').style.display = 'none';
        document.querySelector('span').style.display = 'inline';

        document.getElementById('a').addEventListener('click', () => {
          controllerA.start();
        });

        document.getElementById('b').addEventListener('click', () => {
          controllerB.start();
        });

        document.getElementById('c').addEventListener('click', () => {
          controllerC.start();
        });

        document.getElementById('d').addEventListener('click', () => {
          controllerD.start();
        });

        merger.connect(audioCtx.destination);

        async function loadAudio(src, channel) {
          return new Promise((resolve) => {
            const request = new XMLHttpRequest();

            request.open('GET', src, true);
            request.responseType = 'arraybuffer';

            request.onload = () => {
              const gain = audioCtx.createGain();

              gain.channelCount = 1;
              gain.connect(merger, 0, channel);

              audioCtx.decodeAudioData(request.response, (buffer) => {
                const source = audioCtx.createBufferSource();

                source.buffer = buffer;
                source.loop = true;

                source.connect(gain);

                resolve({
                  start: () => source.start()
                });
              });
            };

            request.send();
          })
        }
      });
    </script>
  </body>
</html>

こんな感じです。
index.htmlと同階層にa.mp3〜d.mp3を置く必要があります。
僕はこういう時に使うテスト用のmp3ファイルは、sayコマンドで作ったaiffファイルをffmpegでmp3化して作ってます

blog.kimizuka.org

DEMO

web-audio-api-4ch.netlify.app

loadボタンを押した後にa〜dボタンを押すことで、対応したmp3が対応したチャンネルから再生されます。

loadボタンを押した時に「4チャンネル欲しいけど、2チャンネルしかありません」と表示された場合はAudio MIDI 設定がうまく設定できていないので見返してみてください。