Sonic Pi による音の出力を Node.js のプログラム(osc.js を利用)から制御: OSC over UDP による通信


内容はタイトルのとおりで、最近書いていた以下の記事の流れの続きです。

Sonic Pi について

Sonic Pi とは?

以下の公式サイトの説明によると、Sonic Pi はプログラムを書いて音作りなどができるオープンソースのソフトになります。
そして、Windows用・Mac用・Raspberry Pi用のものが提供されています。

●Sonic Pi - The Live Coding Music Synth for Everyone
 https://sonic-pi.net/

今回、自分は Sonic Pi の Mac版を使って進めていきます。

Sonic Pi で OSC通信を使う(受信をする)

Sonic Pi は別アプリ・プログラムからの通信をトリガーにして音を鳴らすことができます。

プログラム

以下のページを見てみると、OSC での通信をトリガーにシンプルな音を鳴らすプログラムは、以下の簡単なもので実現できるようです。
 ●yoppa org – OSC通信 – ProcessingでSonic Piをコントロール
  https://yoppa.org/fms_music17/8679.html

上記のプログラムをもとに、少し改変をして以下のようなプログラムにしてみました。

live_loop :trigger do
  use_real_time
  n = sync "/osc*/trigger/synth"
  play n
end

元のプログラムの 3行目と 4行目は、それぞれ n = sync "/osc*/trigger/synth"play n という内容に変更しています。
2つ目のほうは音の鳴らし方をシンプルにしただけなのです。1つ目の /osc*/ と変更した部分については、この後に補足を書くことにします。

待ち受けをするポート番号について

「Sonic Pi が待ち受けをするポート番号はどうなるのだろう?」と思ってメニュー等を見てみたら、Mac版の上部のメニューに「入出力 ⇒ Incoming OSC Port」と言う]項目があり、デフォルトで「4560番ポート」が指定されていました。

この後の Node.js のプログラムで必要になるので、念のため確認をしておいてください。

Node.js のプログラムで OSC通信

冒頭でも紹介した以下の記事で使ったプログラムを元に、今回使うプログラムを作ります。

●osc.js で OSC Data Monitor に OSC over UDP でデータを送る(Node.js の keypress との組み合わせ) - Qiita
 https://qiita.com/youtoy/items/740686a8d2ffb0c6b8ba

先にプログラムを載せておきます。
プログラムを実行するためには、前回の記事と同様にパッケージのインストールが必要になりますが、今回は keypress も追加した $ npm install osc keypress というコマンドででパッケージのインストールを行ってください。

const osc = require("osc");
const keypress = require("keypress");

var udpPort = new osc.UDPPort({
  localAddress: "0.0.0.0",
  localPort: 8001,
  remoteAddress: "0.0.0.0",
  remotePort: 4560,
  metadata: true,
});

udpPort.on("message", function (oscMsg, timeTag, info) {
  console.log("An OSC message just arrived!", oscMsg);
  console.log("Remote info is: ", info);
});

udpPort.on("ready", function () {
  console.log("ready");

  keypress(process.stdin);
  process.stdin.on("keypress", (ch, key) => {
    if ((key && key.ctrl && key.name === "c") || (key && key.name === "q")) {
      process.exit();
    }
    switch (key.name) {
      case "e":
        sendValue(70, key.name);
        break;
      case "r":
        sendValue(75, key.name);
        break;
      case "t":
        sendValue(80, key.name);
        break;
      case "y":
        sendValue(85, key.name);
        break;
      default:
        break;
    }
  });

  process.stdin.setRawMode(true);
  process.stdin.resume();
});

function sendValue(inputValue, inputText) {
  udpPort.send({
    address: "/trigger/synth",
    args: [
      {
        type: "f",
        value: inputValue,
      },
    ],
  });
  console.log(`key.name: ${inputText}`);
}

udpPort.open();

remotePort: 4560 という部分は、Sonic Pi が OSC over UDP を待ち受けるポート番号で、アプリのメニューから確認した値になります。

上記を実行した後にキーボードの「e、r、t、y」のどれかを押すと、キーに対応した数値が OSC通信で Sonic Pi に送られます。
そして、Sonic Pi は、受けとった数値を使い「play n」の部分を実行するので、キーが押された際に音が鳴ります。

なお今回は使わないですが、上記には 8001番ポートでの待ち受けの処理も含まれています(※ これも前回の記事と同様)。

Sonic Pi 側のプログラムの補足

上で Sonic Pi のプログラムに関する話を書いた中で、「 n = sync "/osc*/trigger/synth" 」という指定をしていた部分の補足をここで書きます。

これは、Node.js で「 address: "/trigger/synth" 」として数値を送った場合に、Sonic Pi側が受けとる内容に依存して変更したものです。
具体的には、上記の Node.js のプログラムを実行して OSC通信を行うと、Sonic Pi側の「Cue」の部分で以下の内容が表示がされます。

これを見ると、元のプログラムのとおりに /osc/... と書いていると、 /osc:【IPアドレス】:【ポート番号】/... という内容になっている部分に合致せず、音が鳴りません。
(当初はこの部分に気づけず、音がならない状態になってしまって、その原因がなかなか分かりませんでした...)

この話は、Sonic Pi で音が鳴らない状態になり、デバッグ用の情報がどこかに出てないか見ていたときに気がつくことができました。

上記の 2つを実際に動作させた時の様子

上記のプログラムを両方実行してからキーボードのキーを押して、Node.js のプログラムから Sonic Pi への OSC通信を行って音を鳴らした時の様子を動画で示します。

おわりに

今回、Node.js で作ったプログラム(ライブラリに osc.js を利用)から Sonic Pi に OSC で通信をして、キー入力に連動して音を出力する仕組みを動作させることができました。

この後は、Sonic Pi にデータを送るプログラムをいろいろ変えてみたり、Sonic Pi側の音を出すプログラムに手を入れて、音の鳴らし方のバリエーションを増やしていこうと思います。

【追記】 Sonic Pi側のプログラムの別事例

複数で待ち受け

以下の記事から、OSC の通信の待ち受けを 2種類にする場合の例をメモしておきます。

●レーザーハープをつくってみる | hiyLab
 https://hiylab.net/20200429-laser-harp/

# レーザー1を遮られた時に送信されるメッセージを受信して音を鳴らす。
live_loop :laser_1 do
  use_real_time
  sync "/osc/trigger/laser_1"
  play 60 # Cの音を鳴らす
end

# レーザー2を遮られた時に送信されるメッセージを受信して音を鳴らす。
live_loop :laser_2 do
  use_real_time
  sync "/osc/trigger/laser_2"
  play 64 # Eの音を鳴らす
end