0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Strudel】#2 作曲環境を作ったので何か作る

0
Last updated at Posted at 2026-04-11

前回の続きです

おさらいと現状

前回やったこと

  • 型定義それっぽく書いた
  • ローカルのテキストをブラウザのREPLにpushする機能を作成

暇なときに追加した機能

  • ブラウザのランタイムエラーを受信してログを受信←New!

前提知識など

ちなみに作曲経験はほとんどありません。ゲームのループBGMをMIDIで打った程度です。
ただ、分野的にはプロシージャル生成に近いですし、音声処理の分野です。ここに限って言えばエンジニアの十八番です。
参入障壁の低さを公式が謳っているくらいなので、これで事故ったら僕のセンスが壊滅的ということでしょう。

image.png

それに、僕のターミナルにはLLMが住んでいます。きっといい感じにしてくれるでしょう。


😡「結局AI頼りかよ!!!AIで曲作るとか最低!!!」

いや、僕が書いているのは乱数などでリアルタイムに変化しうる「アルゴリズム」なので✋

※もちろん、既存の楽曲と露骨に同じものを自作として公開することは避けましょう。作るプロセスもパフォーマンスなので、完全に頼りきるのは面白みにかけます。

実際、僕が魅力に感じるポイントの一つです。
プログラムゆえに、最近のゲームでよくあるインタラクティブミュージック(オープンワールドでシームレスに曲調が変わったりするアレ)を更に動的にした仕掛けをJavaScriptのエコシステムに実装できるポテンシャルがいいですね。状態を変数で受け取るコードにしておけばいいので。
効果音への応用も考えられますね。単なる攻撃SEでも、強度、属性に応じて適切なエフェクトがかかるコードを記述すれば労力を下げられます。


基礎については他の方の記事がわかりやすいです。

パターンを記述するためのmini記法(note("c2 a2 eb2")など)はドキュメントに詳しく書いてあるので、意識するべきJavaScriptとの違いに触れていきます。主にラベルと文字列リテラルの扱いに違いがあります。

例えばダブルクォート・テンプレート文字列はm(mini, offset)に変換されます。
mはmini記法パーサーに渡す関数です。第二引数が挿入され、これはハイライト機能のために使用されます。

n("c3 e3 g3")

// トランスパイル後
// 第二引数はoffsetで、トランスパイル前のコードのダブルクォート開始インデックス。
n(m("c3 e3 g3",2))

ラベルはp(patternName)のメソッドチェーンに変換されます。
おそらくplayの略です。

bass: note("c3 e3")

// トランスパイル後
note("c3 e3").p('bass')       

ダブルクォート文字列は強制的にminiパーサーに渡されるので、JavaScriptの文字列として扱う場合はシングルクォート文字列にする必要があります。

作るべ

とりあえずいい感じにしてみましょう。時代はバイブです。

from-scratch sessionで調べるといくつかゼロから作るパフォーマンス動画がヒットします。
リモートワークの時代、他人のコーディングが見られる機会は貴重です。

先人たちの動画はハイレベルで参考になりませんでしたが、ドラムパターンから作ると良さそうですね。

本家REPLの初期状態もドラムですし、ここから発展させよという啓示を感じます。
これはそのまま使いましょう。

$: s("[bd <hh oh>]*2").bank("tr909").dec(0.4);

せっかくフィルタが豊富なので、シンプルなノコギリ波と方形波を軸に作ってみましょう。シンプルな波形はゲーム音で頻出なので僕の耳には馴染みます。

個人的にWebページや作業中の環境音に応用したいので、思い切ってテンポを落とします。
そこにノコギリ波のベースをいい感じにのせます。そのまま使うと主張が強いので、ローパスフィルタを、あと気分でリバーブを入れます。

setcpm(60 / 2);

drum: s("[bd <hh oh>]").bank("tr909").dec(0.4);

bass: chord("<Am F C G>")
  .anchor("a2")
  .voicing()
  .s("saw")
  .lpf(380)
  .gain(0.3)
  .room(0.5)
  .roomsize(6);

僕は飽き性なので音にランダム性がほしいです。
2オクターブ高いアクセントを50%で鳴らします。

setcpm(60 / 2);

drum: s("[bd <hh oh>]").bank("tr909").dec(0.4);

bass: chord("<Am F C G>")
  .s("saw")
  .anchor("a2")
  .voicing()
  .lpf(380)
  .gain(0.3)
  .room(0.5)
  .roomsize(6);

accent: chord("<Am F C G>")
  .s("square")
  .anchor("a4")
  .voicing()
  .struct("[1? [1 0]?]*2") 
  .lpf(1600)
  .lpq(1.5)
  .gain(0.1)
  .room(0.65)
  .roomsize(5);

レトロ風ローグライクゲームのBGMでありそうな感じの曲になりました。
場面に合わせてテンポ・音が鳴る確率を調整することでインタラクティブになりそうですね。

REPLのUIでsliderなどが使用できるので簡単に実験できて面白いです。

ここから聴けます

0
2
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
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?