1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Node.jsで音を作りたい!サウンドプログラミングもどきをやってみよう

Last updated at Posted at 2024-12-29

お久しぶりです。Qiitaへの投稿は3年ぶりらしい。信じられないけど、確かに実感はある。

作曲好きなんです、唐突ですが。
音が好きなんです。

プログラミングも一応できないことはないんです、多分。

というわけで、Node.jsで基本的な部分をやります。

あ、非常に雑に記事を書いてるので間違ってることとかあるかもしれません。誤りを見つけた方はコメント欄やTwitterのDMなどで指摘していただけると良いかと思います。なお、本当に適当に書いているかつ勉強もあんまりしていないので分かりやすく書いていただけると僕は泣いて喜びます。

対象者

  • なんとなくシンセサイザの仕組みがわかる方
  • なんとなくNode.jsを使うことができる方
  • なんとなくJavaScriptが書ける方

使うライブラリ

  • node-web-audio-api:有志の方々がWeb Audio APIをNodejs用に移植したバージョンです。
  • noteFrequency:自作したやつです。入力された音程("A4"などの文字列)から440のような周波数の数列が返されます。Google Geminiに書いてもらったから、自作と言えるべきかわからんけど
    • コードは後述します。

あと、npmNode.jsは大前提として必要です。

以上

node-web-audio-apiについて

前述の通りこれは有志の方々がNode.jsでは動作しないWeb Audio APIをNode.jsで使うために移植したもので、使い方は元々のWeb Audio APIと近いらしいです。
ここではあまり詳しく説明しないので、使い方を知りたい方はWeb Audio APIの使い方を調べると良いかと思います。

Web Audio API の使用 - Web API | MDN

やりましょう

ライブラリの導入

とりあえずライブラリを導入しましょう。

% npm init
% npm i node-web-audio-api

1行目の時のやつはいろいろ出てくると思うのですがEnterぽちぽちしてください。

あ、package.jsonのscriptsの中のtestが既定では"echo \"Error: no test specified\" && exit 1"となっていますが、ここを"node ."に書き換えておくことでnpm testで実行できるので少し便利かもしれません。

コードについて

こちらがindex.js

index.js
// node-web-audio-apiの読み込み
const {
  AudioContext,
  OscillatorNode,
  GainNode
} = require('node-web-audio-api');
const audioContext = new AudioContext();

// noteFrequencyの読み込み
const noteFrequency = require('./noteFrequency.js');

// 音を鳴らす関数
function ring() {
  const now = audioContext.currentTime;

  // 音階を指定
  const frequency = noteFrequency('A4');

  // オシレータの生成
  const osc = new OscillatorNode(audioContext, {
    type: 'triangle', // 波形の設定
    frequency: frequency // 鳴らす音の周波数の設定
  });

  // エンベロープを作成・設定
  const env = new GainNode(
    audioContext,
    {
      gain: 0
    }
  );
  env.gain
    .setValueAtTime(0, now)
    .linearRampToValueAtTime(0.1, now + 0.02)
    .exponentialRampToValueAtTime(0.01, now + 1);

  // エンベロープをaudioContextに接続する
  env.connect(audioContext.destination);
  
  
  // エンベロープを先ほど生成したオシレータに接続する
  osc.connect(env);

  // 音を鳴らす(オシレータ起動)
  osc.start(now);
  
  // 音を止める(オシレータ停止)
  osc.stop(now + 1);
}

// 音を鳴らす関数を実行
ring();

フィルタやモジュレータ、エフェクトなどは省略します。というか、まだやり方知りません。すみません(汗

コードの説明についてはほとんどすることはありませんが、どうやらaudioContextというのが音を全部管理する?機能を持つようで、そこにオシレータなどいろいろな機能を接続することで動作させているようです。

続きましてnoteFrequencyです。上のindex.jsと同じディレクトリに配置してください。

noteFrequency.js
module.exports = (note) => {
  const notes = [
    "C",
    "C#",
    "D",
    "D#",
    "E",
    "F",
    "F#",
    "G",
    "G#",
    "A",
    "A#",
    "B",
  ];
  const octave = parseInt(note.slice(-1));
  const noteName = note.slice(0, -1).toUpperCase();

  if (isNaN(octave) || octave < 0 || octave > 8 || !notes.includes(noteName)) {
    return null;
  }

  const noteIndex = notes.indexOf(noteName);
  const a4Frequency = 440; // A4の周波数 (Hz)
  const semitonesFromA4 = (octave - 4) * 12 + (noteIndex - notes.indexOf("A"));

  const frequency = a4Frequency * Math.pow(2, semitonesFromA4 / 12);
  return frequency;
};

これをAIが一撃で出力したなんて信じられないよな・・・と思いつつ、全く問題なく動いてるので本当にすごいです。

実行しよう

これを実行してみると、ラの音(440Hz)が一度だけ鳴ります。

ちなみに音の高さを決めている箇所はindex.jsの22行目なんですが、コードの通り定数frequencyを参照していて、これが先ほど掲載したnoteFrequency.jsの関数に"A4"の音程を渡して440Hzを返してもらっています。
つまり、const frequency = noteFrequency('A4')のA4の部分を変えることで音程を変えられます。音程は、「AからGのアルファベット+任意の数字(大抵0から7くらい)」という形式で表表記されるので、参考にしてください。ちなみにAからGというのは順に「ラシドレミファソ」です。いろいろ試してみると良いでしょう。

ここからどうする?

この状態だと非常に変化がなく面白くない。というわけで、その場の気温やカメラからの入力、マイクで拾った音や乱数を入力してランダムに音を生成するなど工夫はまだまだできそうです。


以上です。眠いです。おやすみ。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?