Cloud RunでLaravelのバッチとキューワーカーを動かしたときに困ったこと
この記事について
六間坂上 Advent Calendar 2024 に参加させていただきました。2(4)日目です。めっちゃおくれてすみません。。。。
はじめに
以前Cloud Run サービスでLaravelのバッチを動かしたのですが、なかなか思った通りの挙動にならず苦労したため、詰まりポイントを書きます。 本記事では以下バージョンの言語・ライブラリを利用しています。
- PHP 8.2.4(
php:8.2.4-fpmコンテナを使用) - Laravel 10.x
また、2024年11月末時点での情報を元に記載していますので、今後状況が変わる可能性があることにご留意ください。
早速状況が変わった
...と書いていたら、記事を書いている途中に料金の計算方法に変更が入り、早速一部が参考にならなくなりました...
まだ日本語には翻訳されていないようなのですが、「CPUを常に割り当てる」というオプションがなくなり、代わりに「課金」という項目が追加されています。
「リクエストベース」と「インスタンスベース」の2種類から選べるようになっていて、おそらく「インスタンスベース」が「CPUを常に割り当てる」に相当するものと思われます。
表記以外の違いはなさそうに見えますが...もし見落としていたらすいません。

Cloud Run について
コンテナをサーバーレスかつフルマネージドに実行できるGCPのコンピューティングサービスの一つです。
Webサーバーやバッチ処理など、デプロイするコンテナによって色々と動かすことができます。ランタイムに依存しないし、インスタンス管理も不要なのでなかなか便利です。
このCloud Runですが、最近一般公開(GA)されたCloud Run ジョブも「Cloud Run」の一部という扱いになっています。
ややこしいので、この記事では特に注釈がない限り「Cloud Run サービス」「Cloud Run ジョブ」とわけて記載します。(Cloud Run 関数...? なんですかそれは)
さて、そのCloud Run サービスですが、ライフサイクルはざっくりと
- HTTPリクエストなどのイベントに応じてCPUが割り当てられ、コンテナが起動
- 起動プローブ(ヘルスチェック)が通ったコンテナにリクエストがルーティングされ、コンテナはリクエストを処理
- その後リクエストがなければインスタンスはアイドル状態となり待機
- さらにしばらくアクセスがなければシャットダウン
というフローで動きます。
そのため、もし同じコンテナでリクエスト以外の処理(例えば、リクエストで非同期ジョブを追加し、バックグラウンドでキューワーカーを動かす、など)を動かそうとした場合、 レスポンス応答後のアイドル状態はCPUが割り当てられないため、処理は進まなくなってしまいます。
しかし、Cloud Runサービスに用意されている「CPUを常に割り当てる」オプションをオンにすると、アイドル状態でもCPUが割り当てられるようになります。
さらに、「インスタンスの最小数」を1以上にすることでリクエストの有無にかかわらずコンテナを1つは維持してくれるので、バックグラウンドタスクが安定して動かせるようになります。
※ 2024/12/22 現在、上記日本語ドキュメントは存在していますが、英語版ではBilling Settingsページにリダイレクトされてしまいます...。 近い内に日本語ドキュメントも削除されるものと思われますので、一応Wayback Machineのリンクも貼っておきます。 https://web.archive.org/web/20241008085342/https://cloud.google.com/run/docs/configuring/cpu-allocation?hl=ja
さて、これならLaravelのバッチ(cronでphp artisan schedule:run)やキューワーカーを動かせるなと思って意気揚々とsupervisorを設定し動かしたところ...
困ったこと
① Graceful Shutdownの実装が必要
あるところに、Google Cloud Storage(以下GCSと書きます)に作成された不要なファイルを削除し、データベースにも削除済フラグを立てる、というバッチがありました。
超省略していますが、次のような実装がされていました。(ツッコミどころが多いとは思いますが、サンプルということで...)
<? public function handle(): int { // DBから削除候補のファイルデータを検索し、 $storageFile = \App\Models\StorageFile::where('can_delete', true)->first(); // 存在していなければ終了 if (is_null($storageFile)) { return; } \DB::transaction(function () use ($storageFile) { // 存在していそうだったら、専有ロックを掛けて重複処理を抑止 $storageFile = \App\Models\StorageFile::where('id', $storageFile->id)->lockForUpdate()->first(); if (is_null($storageFile)) { return; } // ここまで来たらファイルは存在しているはずなので、Google Cloud Storageから削除 \Storage::delete($storageFile->path); // ストレージから削除できたらDB側も削除済にする $storageFile->delete(); }); }
これで数日は問題なく動いていたのですが、ある日\Storage::delete($storageFile->path);の部分で404 Not FoundエラーがGCSから返ってきて止まってしまいました。
GCSの削除時刻を見ると、バッチでエラーが発生した時刻の数分前。
調査したところ、GCSでのファイル削除日時付近のMySQLのログに、Aborted connection {id} to db: '{DB名}' ...的なログが残っていたことから、
MySQLのコネクションが途中で切断されている = 何らかの理由で処理途中にコンテナがdownしてしまっている
だろう、という予測が立てられました。
ここで改めてCloud Runのドキュメントをみると、以下のように書いてあります。
Cloud Run サービスの場合、アイドル状態のインスタンスは、最小数のインスタンスを介してウォーム状態を維持するインスタンスを含め、いつでもシャットダウンできます。リクエストを処理しているインスタンスをシャットダウンする必要がある場合、新しい受信リクエストは他のインスタンスに転送され、現在処理されているリクエストに完了までの時間が与えられます。
(中略)
インスタンスをシャットダウンする前に、Cloud Run から SIGTERM シグナルがインスタンス内のすべてのコンテナに送信されます。これは、Cloud Run が SIGKILL シグナルを送信してから 10 秒後に実際のシャットダウンが開始することを表しています。この期間中、インスタンスに CPU が割り当てられ、課金されます。
このことから、おそらく今回の事象はバッチ実行中のインスタンスがシャットダウン対象になってしまい、普通に処理中にシャットダウンされてしまったのではないかと考えています。
試しに最小インスタンス数をいくつか増やしてから減らすと、減らした台数分MySQLの切断ログが記録されたためこの説が有力そうです。
今回の事象を防ぐためには、SIGTERMシグナルを常に待ち受けておいて、受信したら10秒以内に終了させないと、強制終了扱いになってしまうわけです。外部との通信などでバッチがいつ終わるかわからない処理をしている場合、これはなかなか難しい...
また、シャットダウンされる予定のインスタンスには、通常のHTTPリクエストはルーティングされなくなるため、Webサーバーだけの運用であれば気が付かないわけです。
② バッチもジョブも重複して動いてしまう
Cloud Runサービス上でコンテナはそれぞれ独立して動いているので、当然全部のコンテナが同じことをしようとします。
HTTPリクエストはうまい具合にCloud Run側でルーティングしてくれますが、バッチやキューワーカーはそうはいかないので、それぞれ対策が必要です。
バッチサーバーでの対策
これについては解説記事がたくさんあるので詳細はググってもらえればと思いますが、独立して動かしたいバッチ処理のScheduleで、onOneServer()メソッドを呼び出す必要があります。
またLaravelのドキュメントにも書いてありますが、デフォルトのキャッシュドライバであるfileを使っている場合はこれだけでは意味がないため、キャッシュドライバをインスタンスで共有であるdatabase, redis, memcached, dynamodbのどれかに変更する必要があります。
キャッシュドライバを入れてなかったり、他の箇所ではファイルキャッシュを使いたいという場合には、$schedule->useCache()を使うと影響範囲が絞れて良さそうです。
これで、その時間に実行される処理は1つのコンテナだけで実行されるようになります。
ただonOneServerではバッチの実行時間がもし長引いてしまったときに次の時分で実行されるバッチ処理を止めるということはしないので、
もし絶対に重複して実行してほしくないという場合にはwithoutOverlapping()関数も一緒に呼んであげると良いでしょう。
キューワーカー(ジョブ)実行での対策
各コンテナでキューワーカーを動かしていると、全く同じジョブが2つ追加されてしまい、それを複数のキューワーカーで並行して動かしてしまう、ということがあります。
専有ロックなどで頑張っても良いのですが、ジョブクラスの定義にいくつか追加することである程度対策することもできます。
まず、最低1回は処理されればよい(2回同じジョブが実行されなくても良い)という場合はshouldBeUniqueinterfaceをimplementsすれば良いです。
このinterfaceはキューに追加する時点で同じジョブがすでに追加されていた場合、ジョブをdispatchしても「何も変更しない」という動作になります。実際にジョブが追加されたかどうかはわからないので注意してください。
さらに、これもキャッシュを使うのでバッチと同じくfileキャッシュを使っている場合はuniqueVia()メソッドをオーバーライドしてdatabaseなど共有のキャッシュを参照してあげる必要があります。
もしdispatchされた数だけ処理されないと困る、という場合は、withoutOverlapping Middleware を使う必要があります。
shouldBeUniqueがdispatch時にチェックするのに対し、こちらはジョブの実行時に重複実行されているかをチェックします。もし実行中であれば、一回ジョブは失敗したものとみなし再キューイングされる、という動作です。ジョブのリトライ回数等と相談して追加してあげるのが良さそうです。
当然このMiddlewareもキャッシュを参照するのですが、こちらはMiddlewareだけキャッシュドライバを変更する、という仕組みが現状無さそうです(見落としていたらすみません...)
それぞれLockのLaravel実装はあるのでそれを使うようにWithoutOverlappingクラスを継承して作れなくはない、かなと思います。
③ スケーリングが難しくなる
現在、Cloud Runサービスのスケーリングは以下を利用していると記載されています。
既存インスタンスの 1 分間の平均 CPU 使用率(スケジュール設定されたインスタンスの CPU 使用率を 60% に維持するため)。
1 分間でのリクエストの最大同時実行数と比較した現在の同時実行数。
インスタンスの最大数の設定
インスタンスの最小数の設定
Webサーバーがとしてコンテナを運用する場合、アクセス数が増える→CPU使用率が上がる→スケールアウトする
と、普通に動いてくれます。
しかし、そこにバッチサーバーやキューワーカーが乗って負荷をかけると、アクセスはないのにCPU使用率は上がるため、設定によってはスケールアウトしてしまう可能性があります。
Cloud Runサービスは細かいスケーリング指標をこちらでカスタマイズすることができない(最小・最大インスタンス数と同時実行数のみ)ため、インフラ側で制御することが難しいです。
もし大量にスケールアウトしてしまう場合、もともとのコンテナのスペックを上げておいたり、負荷がかからないようなバッチにしたり、キューワーカ数を少なめにしておく...とかしかないです。
スケールしなくてもよいのであれば、最大インスタンス数を1にすればよいですね。
蛇足(AWS App Runner では)
AWSにもApp Runnerという近いサービスが存在しているので動かしてみたのですが、実際使ってみると挙動だったり細かい仕様が違う部分が多くて面白いです。
例えば、
- CPUの常時割り当て:Cloud Runはできるが、App Runnerにそのような設定項目はない(できなさそう?)
- 1インスタンスの生存時間:Cloud Run(長くても1週間くらい)と比較して、App Runnerはかなり長い
- ストレージ:Cloud Runのインメモリなのでメモリサイズ依存、App Runnerは3GBのストレージ
などなど。
生存時間などは体感的なものなので実際違ってたらすみません。
個人的にはApp Runnerを使うメリットとしてストレージがあるのがデカいなと思っています。
Cloud Runは大きいファイルを扱おうとするとメモリ使いつくしてコンテナが落ちてしまうのです...メモリはスケーリング指標にもなっていないため。
あと、CPUの常時割り当てがないApp Runnerでバッチやジョブを動かすと、(動きはするのですが)リクエストを処理しているときだけしかバッチが動かないという仕組みになっているため、数十ミリ秒くらいで終わるバッチが普通に5分かかったりします。
ヘルスチェックの頻度にもよるかもしれないですし、リクエストが多ければあまり気にならないのかもしれないですね。
おわりに
結局、①の時点でハードルが高かったので、私のWebサーバーのバッチはCloud Runサービスでの実行は諦め、Cloud Runジョブを使うようにしました。
バッチサーバ、キューワーカをそれぞれ同じコンテナで別のジョブにし、Cloud Run ジョブをCloud Schedulerから毎分起動するような構成にしたところトラブルはなくなりました。
あと、バッチサーバは基本すぐ処理終わるので、料金も若干安くなった気がします。
キューワーカのスケーリングだけは難しいですが...実行時間を長くし、--stop-when-emptyオプションを使ってほんのりスケーリングするようにして凌いでいます。
うちではこんな構成にしているぞ~だったり、もっとスマートな(あるいは安い)方法があれば教えてください。毎月若干無料枠を出てしまってるので...
ここまで読んでいただきありがとうございました。それではまた次の記事で~
Laravelで作る箱庭諸島
はじめに
この記事について
2023年に箱庭諸島っぽいゲームを作ったので記録します。
箱庭諸島について知らない人はググってください。20年以上昔に開発されたCGIゲームですが、まだ運営されているサーバーが結構あります。
元はperlスクリプトで書かれたものを、PHP(Laravel)とVue、GCPなど使えるもの使ってフルスクラッチで作るとこうなった、というものです。
いいからソースコード見せろって人はGithubにあるのでそちらをどうぞ。

つくるもの
冒頭で箱庭諸島っぽいものと書いた通り、箱庭諸島とはいくつか仕様が違うものを作りました。
というのも、2007年頃にサービス終了した「しまにてぃ」というSNSがあり、これに乗っかっていた箱庭諸島ライクなゲームがすこぶる楽しかったので、これを再現したいなーと思ったのが事の発端だからです。
※今調べると、これはみんなのあいらんど*1というシェアウェアであったようで、この開発元は箱庭諸島の開発者でもあるとくおかさんとのこと。
使ったもの
ざっくり以下のフレームワーク/ライブラリを使いました。
バックエンド
フロントエンド
- Node 18.15
- Vite 4.3
- Vue 3.3
- Tailwind CSS 3.3
CSSフレームワークは最初はBulmaを使っていたのですが、途中から友人がTailwindにリプレイス&いろいろ実装してくれました。ありがたい...
インフラ
開発
コードを書き出すとキリがないので、かいつまみつつ書いていきます。
設計
実際は開発と設計を行き来しながら作業してます...
リソース設計
あまりRESTからはみ出ないように...と意識して画面や遷移を書くだけ書きました。後から結構変わってしまいましたが...

テーブル設計
設計するならとER図を作ろうとしたのですが、途中で面倒になったのでテーブルだけ書いて終わりました。

島の地形など、細かいデータはあまりSQLで検索するものでもないかな...ということjsonカラムにしました。
開発環境整備
php-fpmとnodeのDockerコンテナをそれぞれ用意し、それぞれバックエンドとフロントエンドの開発サーバーを動かすようにしました。
Makefileにコマンドをまとめています。
実装
コントローラ
島の新規登録や変更等、基本的なCRUDについてはコントローラをそれぞれ作成し実装しました。
特に変わったことはしていない...と思います。
以下は島新規登録画面のコントローラです。
ゲーム自体の処理
LaravelのModelはORマッパーでよく使われるため、ゲームのロジックを入れてしまうとクエリと混在して大変なことになりそう...ということで、Entityディレクトリ以下にゲームロジックを分離して、Modelとの相互変換だけをModelに書くようにしました。(いつもはロジックはサービスクラスに書いているのですが、肥大化しそうだったので...)
以下は森セルの実装ですが、毎ターン木が増えたり外の島から見た時に木かミサイル基地か違いが分からないようにしたりといった処理が入っています。
ターンの更新処理は1つのartisanコマンドにまとめました。超長くなってしまったのですが、処理速度は許容範囲内だったので同時登録島数を制限する方針でヨシとしました。
cronで定期実行するイメージです。
怪獣や災害とかの仕様はwikiと記憶を頼りに大体それっぽくなるように実装しています。
DBへの保存と読み込み
前述のとおり、ユーザーからの入力コマンドや島の地形情報などは全部jsonにして保存しています。 nullableのカラムを大量に用意することも考えたのですが、アップデートの度にalterするのは嫌だなと思ったので...
あと普通にゲームであればシリアライズなどもありなのかな?と思ったのですが、あまりWebサービスでデータをシリアライズして保存...とかすると脆弱性になりそうなのでやめました。
DBのjson ↔ Modelクラス ↔ Entity となる形で、変換を集約してなんとかしました。
よく見るとjson_decode()で取得したobjectを...get_object_vars()で展開してコンストラクタに突っ込んでます。なんとかなってなさそう。
フロントの処理
基本はバックエンドから情報を渡し、Vueで島のステータスやセルや開発計画などを整形して表示しています。フロント部分は全部友人が作ってくれたのであまり詳しく書けません...
作った機能
OAuthログイン
従来の箱庭ではユーザー名とPWを入れてログインという形式が主ですが、セキュリティを考えるのがやや面倒だったのでGoogle様のOAuthログインを使いました。 メールアドレスなどは保持せず、ユーザーの識別子だけ保存・利用しています。
あとから友人にYahooのログインも追加してもらいました。
ほかの島のプレビュー
ミサイルをほかの島に打つ際、座標指定が楽になるようにウィンドウ内で開けます。(フロントの表示は友人が超頑張ってくれました)

実績
ターン賞や繁栄賞といった実績がもらえるようにしています。

ログ
島に起こった出来事をログとして保存して表示するようにしています。
jsonで色やリンクなどを保存し、フロントでパースしてます。ちょっとイケてないのですが、v-htmlを使うとxssの危険があるので...

掲示板
掲示板です。島を持っている人しか投稿できないようにしており、極秘通信は資金が必要にしています。

テーマ切り替え
友人が作ってくれました。ダークテーマとホワイトテーマで切り替えができます。

レスポンシブ対応
これも友人が作ってくれました。別件で私もTailwind CSSを使いましたが、レスポンシブ対応が超楽です。

デプロイ
GCPの用意
ローカル環境で一通り作ったところでデプロイをしていきます。GCPのプロジェクトを作り、APIを有効化していきます。 最終的に使ったのは、
- Cloud Run
- Cloud Run Jobs
- Cloud Schedular
- Cloud Run Jobsとあわせて定時のターン処理で使います
- Cloud Build
- Artifact Registry
- Secret Manager
- Cloud SQL
- のちに費用が高かったので適当なレンタルサーバーに乗せ換えました
- Cloud Function(第一世代)とCloud Pub/Sub
- ビルド結果をDiscordへ通知するのに使いました
あたりです。Cloud Runは512MB RAMでも十分動いたのでそれでよさそうです。無料枠からは足が出てしまいますが、900円くらいです。

クラウド破産は怖いので念のため請求アラートも設定しました。(これくらいなら財布がやられる前にサーバーが落ちそうではあります)
当時の箱庭諸島の記事を見ると、重いのでレンタルサーバーへの設置は禁止といろいろなサイトに書かれており、サーバー性能のインフレを感じます。
CI/CDパイプラインの整備
Githubへの特定ブランチへのpushをトリガーに、CloudBuildが走るよう設定します。
CloudBuild構成ファイルを作成し、ビルドとmigrate、イメージのpushとリビジョンの切り替えなどを書いていきます。
イメージのキャッシュはビルド時間が早くなりそうだったのですが、Artifact Registryの料金が意外とお高かったのでやめました。
UT実行環境の整備
ここまでほぼUTを書いておらず、さすがにまずいと思ったので手始めにGithub Actionsをつかってテストが実行されるようにします。
ほぼテンプレートのまま動いたのでありがたいです。README.mdにバッチも付けてそれっぽくなりました。
現在もUTは書けていません...
完成
大体実装できて動いたので完成です。公開するならドメインをとってとるなどしたいのですが、とりあえず身内だけで遊んでいます。
おわりに
詰まったところ
Cloud Run
Cloud Runを使い始めたころ、特定のページにアクセスすると504エラーとなりインスタンスが応答しなくなってしまう現象に悩まされました。
1000文字くらいのJSONをBladeに渡すところで落ちていることがわかり、結局のところCloud Run第一世代だったのが原因だったのですが、これに気が付けずすごい時間がかかってしまいました...。
感想
モダンな環境でCGIゲームを再現でき、またインフラからフロントまで一貫でつくる経験がなかったのでいい経験になりました。
サ終したサービスを再現するという1つの目標が達成できたので個人的には満足しています。
ぞぎなど一部機能がないですが、気が向いたらまた作ります。
最後になりますが、フロントの実装&技術相談にのってくれたF氏、潜水艦など一部ドット絵を打ってくれたM氏、ありがとう。好きなだけ遊んでください。
HALion Sonic SE 卒業式
Unagi DTM Advent Calendar 2020 11日目です。
この記事は
- DTM を始めてから HALion Sonic SE を卒業するまでの経緯を述べます
- いいところとか物足りなかったとことか書きます
はじめに
私のおじいさんがくれた初めての DAW それは Cubase 7 Artist で私は高三でした その付属音源は軽くてライブラリが豊富で こんな素晴らしい音源をもらえる私は きっと特別な存在なのだと感じました 今では私がおじいさん 孫にあげるのはもちろん Cubase なぜなら彼もまた特別な存在だからです
DTM を始めた時、すでに音源を持ってたって人はそういないんじゃないでしょうか。もし持ってたらあなたは変なオタクか親が DTMer でしょう。
かくいう私も iPhone の Music Studio から DTM をはじめた身なので、初めて Cubase を購入した時には音源が必要ということすら知らず。 その後 2 年くらいは付属音源の HALion Sonic SE をメインに曲を作っていました。
その頃作った曲。オケは HALion Sonic SE と MT Power Drum Kit だったはず...
その後 HALion Sonic を当時 1 万くらいで買い、Komplete、BFD3 と、後は専用音源沼にズブズブです。これらの話も今度書きたいですね。
HALion Sonic SE て何
あの Steinberg 社が開発した総合音源です。生楽器からシンセまで収録されています。 内部的には 6 種類に細分化された音源と、それらを読み込むプレイヤーがセットになってます。 ざっくりと、Flux と TRIP がシンセで、それ以外が生楽器とかです。

上位版には HALion Sonic 3(約 3 万円)と、HALion 6(約 4 万円)があります。
HALion Sonic は SE と比べて圧倒的に音源が増えています。MOTIF 開発チームが手掛けているとのことですが、MOTIF 持ってないので音の違いはわからんです...すみません。(MOXF8、欲しい...)
HALion は色々機能が豊富な ソフトサンプラーとのこと。HALion Sonic も付いてきます。持ってない & よくわかってないので解説は他の人に任せます。
入学式(よかったとこ)
無料
タダですよタダ。
Cubase をインストールするときにチェックを入れとくと勝手に入ってきますが、当然無料で配布されています。
合作するときとかこれ使って仮打ちするのにいいです。VST 以外の形式もあるので、とりあえず全員入れてくれや。
サウンドライブラリーの豊富さ
総合音源なので当然っちゃ当然ですが、結構サウンドライブラリー(プリセット)があります。ざっと数えてみましたが 1000 種類くらいはあります。(HALion Sonic 2 は 3000 種類くらいありました)
そのわりに Character(属性)とレーティングでフィルタリングができるので音源が探しやすいです。

結構楽器の種類とかで絞り込める音源はあるのですが、"Analog"とか"Dark"とかの属性で絞り込めるのはありがたいです。シンセとかプリセット名だけでは何もわからんので、"Clean"な"Lead"みたいな属性を指定して調べるとサクサク使えます。音源をまたいで検索できるのはありがたいですね。
軽い
Steinberg が作ってるだけあってこいつが原因で落ちることがほぼないです。えらい。
環境にもよると思いますが、秒で読み込めます。ループ再生にしておいて、下キーを押す➝ Enter キーを押すだけで試聴してけるので、音色総当たり戦法が楽です。
動作に必要な HDD の容量は 30GB って書いてありましたが、実際にインストールされてそうなフォルダを探してみると 100MB くらいでした。はたして。
卒業式(ものたりんとこ)
フリー音源という先入観はあったのかもしれませんが、いい曲ができてくると専用音源に差し替えたくなるのがDTMer。 音源がそろってきた3年目くらいから相対的に使わなくなってきました。
生楽器
シンセとかはプリセットに任せてごり押しできるのですが、それが通じないのが生楽器。 ストリングスやベースをリアルに打ち込もうとすると奏法切り替えでキースイッチがほぼ必須みたいなところがあるんですが、 HALion Sonic SE では対応している音源がそこまでないんですね...
音によりますが安っぽい感じがします(ギターとか)。個人の感想です。 音色自体の編集も変えれる項目がそこまで多くはなかったので、音作りはやりづらい気がします。うまい人はこれで十分なんでしょうか?

The Grandeur を初めて使った時に蓋の閉じ具合が調整できるの、ちょっと感動してました。
ドラム
ドラム音源のグルーブのプリセットがなくて、気合で手打ちするかループ素材を使うかの二択だったので、こっちはすぐフリー音源に乗り換えてしまいました。 MediaBay から召喚できる MIDI Loop を使えばそれなりにいけることを知ったのはだいぶ後でした。わかりにくくないですか?
ドラムキットのスネアだけ変更、みたいなこともできないので、どちらにせよあまり使い勝手がよくない気がします。BFD3 万歳。
まとめ
うまい、安い、早い。
曲の仮打ちには十分ですし、ライブラリが多い分シンセのバリエーションを増やす点で悪くないんじゃないでしょうか。 卒業後買ってしまった HALion Sonic 2 はシンセだけで 1500 種類くらいあり、選ぶだけで楽しいのでまだまだ現役です。 あんま音作りに凝りすぎて思いついたメロ忘れるのは勿体ないですし。
ただ生楽器を凝ろうとすると相応の苦労が発生するため、物足りなくなってきたらアップグレードするか別の音源に乗り換えると精神衛生上大変良いとされています。 Steinberg に魂を売りつつある身としては、HALion ちょっと使ってみたいですね。
それでは。
VSTプログラミング feat.MATLAB
Unagi DTM Advent Calendar 2020 6日目です。
この記事は
はじめに
VSTって何さ
Steinberg's Virtual Studio Technology のことで音源やエフェクタのプラグイン形式の一つです。DTMerはこれに金を溶かす現象が報告されています。
高いんですけど、ないと明らかに曲がしょぼい。DTMerのジレンマですね。
なら自分で作るしかないじゃない。こう考えた人は私だけではないはず...
VST を作る(C++ 挫折編)
天下の Steinberg 様なので、 VST を作るために必要な SDK は公開してくれてます。が。
数年前に VST2 制作に挑んだときは Visual Studio でしかビルドができず、情報も少なく、技術力も作りたいものもなかったのであっさり投げました。
今は主力が VST3 に切り替わり、こちらにて公開されています。 日本語のドキュメントは皆無ですが、C++でVST作り 様の解説が非常に丁寧です。(ここ VST2 の時から解説をしてくれてます。神。)
また再挑戦しようかなと思っていたところ、今年の春くらいに「MATLAB で VST が作れる」という話で盛り上がっているところを Twitter で見かけました。
ちょっと調べてみたら VSTi は難しそうなのですが、エフェクタならかんたんに作れそうです。やってみましょう。
VST を作る(MATLAB編)
そもそも MATLAB って?
- Mathworks 社が開発している科学技術計算言語
- Python っぽい書き方をするけれど違う部分も結構ある
- 配列の index が1から始まるとか
*や/演算子は行列演算なので、.*や./を使わないといけないとか
- 行列計算や機械学習などは公式ライブラリが手厚いので簡単に動かせる
- Simulink と併用すれば組み込み系との連携も強い
- 基本年ごとのサブスクリプション制で有料
- 学校によってはライセンス購入しているかも…?
ざっとこんな感じです。有料なのがネックなのですが、環境構築がなくて楽ですね。
体験版でもいけます(30日の制限あり)が、生成に必要なツールボックスが MATLAB Online では対応していないため、ダウンロードを行う必要があります。体験版登録した後、ライセンスセンター(https://jp.mathworks.com/licensecenter/trials)にアクセスして取得しましょう。
実装
コピペ編
早速やってみましょう。
せっかく公式がドキュメント書いてくれてるのでそれをパクってきたほうが楽そうです。
MATLAB を起動し、スクリプトの名前を stereoWidth.m にし、公式サンプルのコードを張っ付けます。
コンソールで
>> audioTestBench stereoWidth
と入力すると、MATLAB 上でテストできます。

これまたコンソールで
>> generateAudioPlugin stereoWidth
と入力すると、stereoWidth.dllが生成されるので、こいつを読み込んでみます。

もう VST ができてしまいました。ちょろい。
コーディング量としてはかなり少なめなので、直感的にわかります。
自作編
もう少し踏み込んだ内容で自作VSTとか書きたいですが、諸事情により今度にして、簡単なフェーダーを実装します。(本当はフィルタとか作ろうとしたんですけど忘れてて正しい解説できる気がしなかったのでまた今度やります)
フェーダー、要はボリュームつまみです。 オーディオを入力してつまみが0dBだとそのまま出力、マイナス方向にしていくと音量が小さくなってくあれです。 -96dB から +6dB 操作できればいいでしょうか。(-96dB って 16bit 符号付整数の値で表せる最小の数らしいので)
ここを参考にさせてもらいました。
入力を としたとき、dB を求める式は
です。電気関係のときは20が10になるっぽいですが。
なので、逆に dB から入力を求めるときはでしょうか。(ここらへん調べながら書いたのですごく怪しいです。間違ってたら教えてください。)
完成したものがこちらになります。
classdef Fader< audioPlugin properties Volume = 1; end properties (Constant) PluginInterface = audioPluginInterface( ... audioPluginParameter('Volume', 'Mapping', {'lin', -96.0, 6.0}) ... ); end methods function out = process(plugin,in) out = in .* (10^(plugin.Volume/20)); end end end
パラメータと出力のところだけ変えました。 ただ、このままだとオーバーフローの値を考慮していないですね。どうなるか調べてなかったので鼓膜割れたらごめんなさい。
Fader.mで保存し、出力すると、

できますね。よしよし。
できないこと
今回のような用途であればいいんですが、いろいろと制約があるようです。
- Audio Test Bench 上ならできるMIDI 入出力ですが、VST出力するとできないようです(VSTiが作れないやん...)
- 画像とか使う場所や大きさに結構制限がありそう
- アニメーションGIF入れたら Audio Test Bench では動き回りましたが、VST 出力したら地蔵になりました
- Web系の通信もできないようです
VSTi、作りたかったなぁ...
いろいろやりたくなってくると C++ のが自由なのですが、環境構築とかのお手軽さという点では MATLAB よかったです。信号処理用の関数も超大量にありますし。
練習もかねて、遊んでみてはいかがでしょうか。
本文中で参考にしたサイト
#cubase_otita ので 10 Pro にアップグレードした
まえがき
- 当記事は Unagi DTM Advent Calendar 2020 の記事です。
- ※個人の感想です。また、内容の正確性については保証できません。ご了承ください。
この記事は
あらすじ
DTM 全然わからん頃に Cubase 7 Artist を買って、気がついたら5年ほど使っていました。
しかし、
#cubase_otita pic.twitter.com/fa4ew2bA4Q
— テイクノン🍆 (@mjtakenon) 2017年9月3日
このように、いい感じの時に落ち。たまにプロジェクトファイルごと飛んでいってしまうのでした。
その度に我々は #cubase_otita で呟いて、上書き保存をこまめにしなかった自身の行いを反省するのです。
今思うと節々に重くしていた原因が思い当たるのですが、去年の私は「アップグレードはすべてを解決してくれる」くらいに考えていたため、Cubase 30th アニバーサリーセールにあわせてノリでアップグレードを決意しました。(Cubase シリーズ、30 年も続いているの...?)
ついでに VariAudio も使いたかったので、奮発して Pro エディションにしました。黒色かっこいい。
ということで、1 年ほど Cubase 10 Pro を使った感想と、個人的に便利だと思ったところをまとめました。
よかったとこ
チョット落ちにくくなった
VSTBridge が 2016 年に開発終了していたため、Cubase 9 から 32bit プラグインが全部ブラックリストに。実質使えなくなりました。
VocalShifter の VST プラグインなど、代用効かないものもそこそこありましたが、時代の流れなら仕方ないということですっぱり諦めました。
すると、なんということでしょう。Cubase が落ちなくなりました。お前だったのか。
まあ考えてみれば 32bit プラグインを仮想環境で動かしてるようなものなので不安定ですよねということ。
ただ全くというわけではなく、1 日 1 回くらい落ちます。BFD3 や BIAS AMP を読み込んだときの挙動が怪しいので、今度再インストールしてみたいと思います。
また、シンセ兼 MIDI キー として YAMAHA MX61 を使用しているのですが、Cubase 起動後に電源を投入するとまず認識されませんでした。これは MX61 のリファレンスマニュアルにも書いてあるので仕様ですね。
これが Cubase をアップグレードしたところ、後から電源を入れても認識されるようになりました。シンセが不慮の電源断で Cubase まで道連れにすることが多かったので、これは非常にありがたいです。
VariAudio 3(Cubase 10 Pro)
Cubase 版の Melodyne で、オーディオを解析してピッチやタイミングの補正をすることができます。 ギターを録音して、ちょっとだけタイミングがずれているとかであれば波形を切り貼りしてできるのですが、音の長さが違うとそうはいかないので、こいつが大活躍します。
ただ 200% の長さにしたりすると補間処理で違和感がでるので、使いすぎると崩壊します。 インプレイスレンダリングとのコンボで、 VOCALOID トラックの補正が最高に楽。

今までオーディオミックスダウンでソロで書き出し、VocalShifter でピッチ補正をして、読み込んでからエフェクト掛け... なんてことをやってましたが、こいつで全てが解決しました。Cubase 最高。
ただ、ピッチを手書きする機能はないので、あとから抑揚を変えたいっていうのはできません。VocalShiter はいいぞ。
以前から Pro エディションには付属していた VariAudio ですが、バージョン 3 になって機能が追加されています。 例えばオーディオアライメント、これは多重録音したオーディオのタイミングをいい感じにあわせてくれます。

まだあまり使えてませんが、いつか録音したギターで使ってみたいですね...
インプレイスレンダリング(Cubase 8 で追加)
選択した範囲のインストゥルメントトラックだけをオーディオミックスダウンして置き換えてくれる機能です。 今までは逆再生ピアノを作るにも、
- ソロで書き出し
- エクスプローラから読み込み
- トリミングしてエフェクト適用
って手順だったので、だいぶ楽になりました。

あとから VOCALOID のここだけ修正したいとかいうときにも真価を発揮します。
Insert エフェクトは適用してから書き出すか、書き出し後のトラックにコピーするかを選ぶことができますし、置き換えた後、元のトラックはミュートされるなどアフターケアもバッチリ。優勝です。
ダイレクトオフラインプロセシング(Cubase Pro 9.5 で追加)
私はギターが壊滅的に下手なので、BPM 半分で録って倍速に...ってズルでもしないと一向に曲ができません。以前はオーディオに複数処理をかけたらレンダリングされ、後から変更はできませんでした。
なんと、この機能を使えばいつでも取り消せます。どうやら元のデータを保持しておき、処理に変更が加わったらミックスダウンするようです。

サブ効果としてCPU 負荷が軽減されたり、プラグインを持っていない他の人でもエフェクトが適用された状態の音が聴けたり。あまりメインで使いませんが、Insert エフェクトと使い分けられたら強いんだろうなーと思っています。
Mix Console のスナップショット(Cubase 10 で追加)
曲の仕上げで、フェーダーなど変えて Mix が崩壊することがあります。(ありません?) ここは Ctrl+Z も使えないので、何度も地獄を見ることになりました。
Cubase 10 からはスナップショットをとっておくことで、Mix Console だけをいつでも戻すことができます。 これで心置きなく前衛的な Mix に挑戦できますね。Steinberg わかってんじゃん。
オーディオミックスダウン
.mp3 ファイルの書き出しは Cubase 7 Artist には対応しておらず、「Steinberg MP3 Upgrade Patch」なるものを購入する必要がありました。お値段 ¥2690- と、絶妙。 当時これをケチった私は .wav ファイルをいちいち変換しなくてはならず、結構めんどい思いをしました。(Cubase 7 Pro・Cubase 8 以降はデフォルトで付属しています)
最近よく Discord を利用しますが、Nitro に加入しないとファイルサイズ制限が厳しいので、手軽に .mp3 に変換できるのは非常にありがたいですね。
他にも、Pro エディションではトラック毎に分けての書き出しができます。今は使っていませんが、他の DAW を使っている人と合作するときに便利かなーと思っていたりしてます。
UIの変更(Cubase 9 で追加)
プロジェクトの中でウィンドウが固定配置できるようになりました。 今までは MIDI を編集しようとすると新規のウィンドウが開き、Enter でウィンドウを閉じるって操作でした。あまりにも斬新。
一生画面内を漂っていたかと思われたトランスポートパネルも、同じく固定できるようになりました。 最近はショートカットを覚えて消してたのですが、やはり固定位置にいると実家のような安心感があります。
AeroSnap を使うと...
Windows 8 から、AeroSnap というウィンドウサイズを変えてくれるやつがあります。NO AEROSNAP NO LIFE.
これを Cubase でやると下のタスクバーと被ってしまうという問題が報告されていて、ちょっと困ってしまいました。実用上はほぼ問題ないですが、見た目が🤔
Cubase 10 pro issues with Windows 10 and taskbar on top - www.steinberg.net
まとめ
主観で特に嬉しかったものを書いてみましたが、まだ Cubase の機能で使いこなせてないものは山ほどありそうです...。もし、これ便利だよーという機能があれば是非ご教授ください。
しかし、こうやって挙げて調べてみると、Cubase 10 Pro でいきなり良くなったというより、それぞれのバージョンで少しずつ改善されてってるという感じがしますね。
今後も 1 ユーザーとして、より良い DAW を作ってくれるよう応援したいと思います。
Cubase 11 について
11 月 11 日に Cubase 11 が発表されましたね。日付狙ってたんでしょうか。
本記事の趣旨とはちょっと外れますが、よさげな機能がたくさんあったのでちょっと紹介します。 他のサイトのほうが余程詳しいと思うので参考程度に。
MIDI CCの変更
今まで曲線ツールでピッチベンドなどのコントロールチェンジを書いても、曲線を近似した値をいれてくれるだけでした。 これが 11 からなんと、ちゃんとカーブがかけるようになったとのこと。だいぶスッキリしそうです。
Imager, Dynamic EQ の追加
Ozone いらんのでは...? じき Master Assistant 機能も搭載されそうな勢いですね...
サンプラートラックの追加
LFOやスライスが操作できるようになったみたいです。 強化されたらしい音源と Squasher と併せて EDM を作れということでしょうか。
その他
- 中間グレードの Cubase Artist 11 に付属するようになったとのこと。あれ、私が Pro 買った理由の半分がなくなってしまった...?
- コード等、グローバルトラックが MIDI シーケンサーに表示できるようになりました。
- スケールの色がシーケンス上に表示されるようになりました。逆にスケールのサジェストもしてくれるとのこと。賢い。
これ以上書くと買ってしまいそうなので、この辺にしておきます。それではまた次の記事で。