#はじめに
Unityにはデフォルト搭載のAudio Sourceに3DSound
(カメラと音源の座標から、どこから鳴っているかをシミュレートする機能)が存在します。
しかし、デフォルト3DSoundはあくまでもカメラ座標と音源座標しか反映されません。
(Audio屋ではないので詳しくは存じませんが)
人間の耳は同じ音でも前からなのか、後ろからなのか、上か下かを聞き分ける能力があります。
耳たぶの存在意義はまさにこのためであり、耳たぶが無いと音の方向が分からないらしい・・・?
なんでも、後方から鳴る音は少しくぐもって聞こえるとか・・・?
一言でいうと「ASMR」です。
Unityデフォルトの3DSoundはこの**「方向による音のくぐもり」**を再現できません。
そこで、様々なプラットフォームが「音のくぐもり」をシミュレートするための
Audio Spatializerプラグインを公開しています。
今回はそのうちからGitHub等で公開されているオープンソースの物をいくつか紹介します。
#TL;DR
・UnityでAudio Spatializerを使うのはそこそこ簡単
・Unity公式、Unity派生、Steam Audioなど結構存在する
・SOFAデータを読み込めるものが多い気がする
・現状、公式でiOS対応しているSpatializerは存在しなさそう
#各種プラグイン紹介
##Unity公式・Native Audio Plugin
https://github.com/Unity-Technologies/NativeAudioPlugins
我らがUnity公式が提供しているAudio関連プラグイン郡のNative Audio Pluginです。
この中の1機能としてSpatializerが存在します。
他にも使い勝手の良さそうな機能が多数ありますが、
このプラグイン紹介のみで1本記事が書けるレベルなので、
今回はSpatializerのみ扱います。
###特徴
Nativeの名の通り、プラグインの実体はC++のコードです。(C++から)逃げちゃダメだ
Unityで実際に使う際はDLLを呼んでくる形になりますが、
オープンソースなのでC++コードに自分で改変を加えて使うことももちろん可能です。
手元でリビルドすることも簡単でした。
Unity公式によるとこのプラグインはあくまでも試験的なもので、
エディタ・Windows Standalone(86/64)用のDLLしか存在しないようです。
ただ、ざっと実装を見た所Windows専用APIなども叩いていないようなので、
各プラットフォームごとにインターフェイスを用意してあげれば
AndroidやMacOSからでも利用できそうな気配がします。
HRTFのデータはKEMARダミーヘッドです。
MITが公開しているデータをそのまま活用しているようです。
(ただ、これに限りませんがHRTFデータのバイトオーダーがリトルだかビッグだか
OSによって読み込み結果が異なってくる箇所が存在しそうな記述をどっかで読んだ。
めちゃくちゃうろ覚え、勘違いだったらいいな)
###HRTF実装
実際に音源に対してエフェクトを掛けているスクリプトがPlugin_Spatializer.cpp
です。
実装を読んでみてもWindowsでないと動かないようなコードは見当たらないので、
iOS向けに呼び出しインターフェイスと、Xcodeを介してプラグインのリビルドをしてあげれば
動きそうな気配を感じます。
一番重要なコードが55行目辺りで、
extern float hrtfSrcData[];
40行くらい中略
public:
HRTFData()
{
float* p = hrtfSrcData;
for (int c = 0; c < 2; c++)
{
for (int e = 0; e < 14; e++)
{
中略
for (int a = 0; a < coeffs.numangles; a++)
{
中略
for (int n = 0; n < HRTFLEN; n++)
h[n + HRTFLEN].re = p[n];
中略
for (int n = 0; n < HRTFLEN * 2; n++)
{
中略
}
}
}
}
}
};
HRTFData
の中で位相をいじっていそうな気配がします。
C++はからっきしなので、それぞれのコードがどんな処理をしてるのかはまるで分かりませんが、
hrtfSrcDataの中身によって処理を走らせていることだけは分かります。
コードの先頭を見るとexternが付いており、
データそのものはhrtftable.cpp
と別ファイルになっています。
そのhrtftableですが、なんと文字列なのに8MBと超絶巨大ファイルです。
中身は5730行の配列データとなっており、GitHub上ですらRawDataを見ることができません。
こちらがそのコードです。(View raw押すとダウンロード始まるから非推奨)
ざっと読んでみた所、どうも1700行ごとくらいに1ブロックを構成しているようで
耳・音源の各角度が6,7度ずつに周波数補正の数値が入っているように見えます。
冒頭には配列がどのような順番で置かれているかの実装がコメントアウトで残されており
struct Channels // left and right ears
{
struct Elevations // 14 elevation angles from -90..40 degrees
{
float numangles; // small integer stored as float
float angles[numangles];
struct Coefficients
{
float impulse[HRTFLEN];
} coeffs[numangles];
} elevations[14];
} channels[2];
C++配列の読み方は分からないので推測になりますが、それぞれ
channels[2]=左右耳
elevations[14]=耳と音源の高低差
(角度のはずだけど、なんでfrom -90..40 degreesっていう微妙な数字なのかは不明)
coeffs[numangles]=係数(MATLAB側の用語らしい、角度の整数がfloat扱いで収められている?)
impulse[HRTFLEN]=周波数補正用floatの実体データ
こんな感じでしょうか。
これらが累乗で増えていくので、5700行とかいうクソデカ配列になるのだろうと思います。
恐らくオーディオエンジニアの方なら
それぞれどのようなデータがどの順番で並んでいるかが読めると思われますので、
任意のダミーヘッドマイクの数値で書き換えることが可能です。(現実的にやるかはともかく)
また、このhrtftable.cpp
を読んでいるPlugin_Spatializer.cpp
での参照は
public:
HRTFData()
{
float* p = hrtfSrcData;
メソッドの一番冒頭でextern配列をロードしているだけなので、ここの参照を書き換えれば
該当配列以外の外部ファイルから読んでくることも可能だと思われます。
詳しくは後述しますが、
この部分を書き換えてsofa形式のデータを読み込めるプラグインも存在します。
###Unityでの使い方
Unityからの呼び方は極めて簡単で、Edit/Project Settings/Audioから
Spatializer PluginドロップダウンをDemo Spatializerに指定するだけです。
(なんでそんな貧相な名前なのかは不明、テスト用途だから?)
ドロップダウンを指定するとAudio Sourceコンポーネントに新たにフラグが追加されます。
Spatialize がその音源をSpatializer経由で再生するかどうか、
Spatialize Post Effects は同様に
コンポーネントによるポストエフェクトをSpatializer経由させるかのフラグになります。
コンポーネントによるポストエフェクトとは
Audio Echo FilterコンポーネントやAudio Chorus Filterコンポーネントが該当します。
Audio Mixerによるポストエフェクトには発動しません。
恐らく処理順序が Audio Source > Spatializer Plugin > Audio Mixer になっているからだと思います。
Spatialize以外のパラメータに変化はありません。
3D Sound Settingsもほぼ関係ありません。既存3D Sound Settingsは音量調整のみに作用します。
(流石に完全2D音源はSpatializeも無効化されます。
BGMとかの聞こえ方を調整する際はおとなしくFilterコンポーネントを使いましょう。)
###懸念ポイント
このプラグインには既知の問題点が複数存在します。
####1.初期状態ではKEMAR以外のダミーヘッドが扱えない
問題点というか、試験用プラグインである以上仕方ない点ではありますが、
音の表現幅に限界があります。
KEMARは日本人平均の頭部形状と違うんでしょうか(よく知りません)
この点は次の紹介するプラグインを使うことで回避できます。
とりあえずSpatializerを使ってみたい!という方はこのプラグインが一番お手軽なはずなので、
表現幅を追求したい方は以降に紹介するプラグインに乗り換えていくのが良いと思います。
####2.Windows以外のOSに対応していない
こちらは恐らく頑張れば回避可能な点です。
公式提供の状態ではDLLの形で配布されているので、
DLLの対象となっているx86,x86-64環境でのみ動きます。
実装を読む限りOS機能を使った複雑な実装などはしていないように見えるので、
適切にインターフェイスを組んであげればAndroidやiOSでも動かせる気がします。
実際にNative Audio PluginをiOS対応させた方がいらっしゃるので、
この記事に従って実装していけばiOS系も、応用すればAndroidにも対応できるはずです。
(自分はMac環境がないので未検証です、ごめんね)
####3.Z軸回転に対応していない(恐らくX軸も)
これは公式ドキュメントでも案内されている既知の問題点です。
どうやらこのPluginではカメラ座標と音源座標からのみエフェクトを掛けているようで、
Unity空間Z軸回転(首をかしげる動作)によるズレは表現されません。
また、同様にX軸回転(うなづく動作)も表現されていないように感じられます。
(耳がガバガバなので分かりませんが、実装的にXも取ってないと思います。)
恐らくプラグインを噛ませる際にPosition情報のみを使い、
Rotation情報を参照していないからだと思います。
エフェクトを掛けている箇所は判明しているので、
Position情報を渡す前にRotationを計算してあげれば対処可能です。
(簡単に解決可能であると思われます。って公式ドキュメントにも書いてあるし)
そんなに問題か?
通常の平面ゲームではカメラがかしげる動きはほとんど実装されないはずなので
(そんな回転したら絶対酔う)さほど問題にはなりません。
しかし、VRゲームやARアプリケーションの場合は人間が操作する以上
ありとあらゆる首の動きが発生します。
当然かしげますし、うなづきます。
どのくらい聞こえ方が変わってくるのかはよく知りませんが、
VR/AR環境で使う際は注意が必要です。
##SOFAlizer-for-Unity
https://github.com/sofacoustics/SOFAlizer-for-Unity
SOFAフォーマットを策定したSofaconventionsが公式提供するSOFAlizer-for-Unityです。
前述のNative Audio Pluginで述べたSOFAデータを読み込めるプラグインがこちらです。
中身の実装としてはほとんどNative Audio Pluginと同じ、というかほぼForkプロジェクトで
Spatializer Pluginに関係ない部分のソースコードがそのままリポジトリに存在します。
(コミットメッセージがfirst commit, no changes towards SOFA yetになっててちょっとおもしろい)
###特徴
Native Audio Pluginの問題点1.で述べた、
KEMAR以外のダミーヘッドが使えない問題を解消できるプラグインで
SOFA形式であればどこから持ってきたものでも使うことができます。
特徴はほとんどNative Audio Pluginと同じで、実装はC++のオープンソースで
いくらでも自分で改良を加えることができます。
ただ、後述しますがこちらのプラグインを他OSに対応させることは少し厳しい気がします。
###HRTF実装
こちらの実装もPlugin_Spatializer.cpp
を読めば分かります。
が、Native Audio Pluginに比べて実装が大きく変わっており、
SOFAデータの仕様も理解しないと読めなさそうな気配を感じたので、完全理解は諦めました。
一応重要そうなメソッドとして
void LoadSOFAs(UnityAudioEffectState* state)
中略
void UnloadSOFAs(void)
といったメソッドが70行目、200行目に見えています。
せっかく規格が決まっているので、おとなしく提供されているものを使う
くらいの姿勢が良いと思います。(敗北宣言)
###Unityでの使い方
Native Audio Pluginと同様にSpatializer Pluginドロップダウンを指定するだけで
使えるようになります。
Audio Sourceに出現するフラグも全く同じです。
同じリポジトリから派生したプラグインなので、当然といえば当然ですね。
悔しいのでUnity側のC#実装も一応読んでおきましょう。
公式サンプルとしてあるコードでSpatializerUserParams.cs
というコードがあります。
Spatializer Pluginを導入することによってAudio Sourceクラスに追加されるメソッドで
SetSpatializerFloat
というものがあります。
これはDLL側に任意のデータを転送するためのメソッドで
第一引数がintでindex、第二引数がfloatでvalueです。
source.SetSpatializerFloat(0, DistanceAttn);
source.SetSpatializerFloat(1, FixedVolume);
source.SetSpatializerFloat(2, CustomRolloff);
source.SetSpatializerFloat(3, SOFASelector);
Update内にこういったコードが存在し、
どのSOFAデータを参照するかを最後の行で指定しているようです。
このプラグインでは同時に10個のSOFAデータを保持しておくことが可能で、
プロジェクトのRootフォルダ(Assetsと同じ階層、プロジェクト内ではないので注意)に
hrtfX.sofa
のXを0~9の数字に書き換えた名称で配置することでロード可能になります。
SetSpatializerFloat
で渡しているSOFASelector
はそのXの値のintです。
再ビルドをせず10個のSOFAデータを差し替えられる利点として考えられそうなのは、
例えば体験会などで複数人を対象に実行する際に
アプリケーションを終了せず次体験者に備えることができる、
などでしょうか。(ニッチすぎる需要)
ただ同時に、プロジェクト内階層に設置しないという点が問題点にもなります。
###懸念ポイント
こちらはNative Audio Pluginよりも根深そうな問題があります。
####1.Windows以外のOSに対応していない
Native Audio Pluginとは違い、こちらはゲーム起動後にコンソール画面が一枚開きます。
Plugin_Spatializer.cpp
の実装を読む限り、ただの実行ログのようですが、
Windows以外のOSに対応する際は
こういったデバッグ用出力コードを全て剥がさないといけません。
結構頻繁にfprintfメソッドが存在するので、注意して剥がさないとバグりそうな気配がします。
とはいえ、そのコンソール画面さえ封印できてしまえば他OSにも移植できそう、、、
そうは問屋が卸しません。
####2.SOFAデータがプロジェクト内配置ではない。
これが一番致命的かもしれません。
SOFAデータへの参照を取っている箇所を見つけられていないので推測になりますが、
UnityビルドからDLLに対してSOFAデータを渡せていないんじゃないかと考えています。
Spatializer Pluginを導入することで
SetSpatializerFloat
というメソッドが追加されると紹介しましたが、
ここで参照を渡しているのはあくまでもどのSOFAデータを使うかのintだけであり、
SOFAデータ実体ではありません。
恐らく参照番号のみをDLLに渡し、
DLL側から相対パス参照でSOFAデータにアクセスしているのではないでしょうか。
その実装の場合、もしインターフェイスを整えてiOS,Androidから呼べるようになったとしても
DLLがSOFAデータにアクセスする際に躓くことになるでしょう。
この問題は参考になりそうなブログも知見も何故か見つかっていないため、
Windows以外の環境で当プラグインを使うのは絶望的と言えます。
(需要がなさすぎるから?先駆者が少なすぎるから?)
####なので
以上の2点より、
このプラグインを実装を改めて他OSに対応させるという方法は取れないと考えてよいでしょう。
せっかくいいプラグインなのに、、、
issueでも投げてみます、、、?
ちなみにこれまた後述しますが、他プラグインのiOS対応issueは黙殺されています。そんな殺生な
##Steam Audio
我らが最大ゲームプラットフォーム、SteamによるAudio Pluginです。
公式対応プラットフォームはこれが最大級で、PC/MacOS/SteamOS/Linux/Androidと
モリモリの対応幅です。
開発環境サイドも強力で、Unity/UE4/FMOD Studio/C api(!?)と主流環境は網羅しています。
WwiseもComing Soonが付いており、いつか使えるようになる日が来るでしょう。
(ただ、このComing Soonかなり長い期間付いてる気がするんですが、、、)
リポジトリとしても活発で、2021/06/09時点の最終更新は2021/04/13と、
そこそこ生きているプロジェクトであると言えます。
(感覚がおかしい?上2つは年単位で過去ですよ)
さらにおもしろポイントとして、実装や使い方の議論がGitHubのissueより
SteamCommunityのスレッドのほうが活発です。
流石はお膝元。
###特徴
特徴はなんといっても、そのすさまじい機能量です。
今回はSpatializer機能にのみ絞って紹介しますが、
このプラグインだけで一本記事が書けるほど大量の機能があります。
使いこなすのも大変です。いつか書きたいね。
Spatializer機能にのみ絞ると言いましたが、
そのSpatializerの中にも今まで紹介してきた耳・音源の座標によるエフェクトや
空間反響・閉塞・大気減衰といった、リアリティあふれる音表現まで使うことができます。
その分実装選択肢と作業が増えるので大変です。(当然)
このプラグインではSOFAデータを読み込むことができます。
Windows以外に対応していなかった上記SOFAlizer Unityのシンプル上位互換ですね。
###HRTF実装
流石にこのクソデカプロジェクトの完全理解は無理です。
一応Spatialize用メソッドは読みましたが、これまた巨大でした。
条件分岐がとにかく多すぎる。
コードとしてはspatialize_effect.cpp
が該当します。
これの640行目がエフェクトを掛けているメソッドです。
/** Applies the Spatialize effect to audio flowing through an Audio Source.*/
void process(float* inBuffer,
float* outBuffer,
unsigned int numSamples,
int inChannels,
int outChannels,
int samplingRate,
int frameSize,
unsigned int flags,
UnityAudioSpatializerData* spatializerData)
{
中略
// Unity provides the world-space position of the source and the listener's transform matrix.
auto S = spatializerData->sourcematrix;
auto L = spatializerData->listenermatrix;
auto sourcePosition = convertVector(S[12], S[13], S[14]);
auto directionX = L[0] * S[12] + L[4] * S[13] + L[8] * S[14] + L[12];
auto directionY = L[1] * S[12] + L[5] * S[13] + L[9] * S[14] + L[13];
auto directionZ = L[2] * S[12] + L[6] * S[13] + L[10] * S[14] + L[14];
auto lengthSquared = directionX * directionX + directionY * directionY + directionZ * directionZ;
auto direction = (lengthSquared < 1e-4) ? IPLVector3{ .0f, 1.0f, .0f } : convertVector(directionX, directionY, directionZ);
auto sourceAhead = unitVector(convertVector(S[8], S[9], S[10]));
auto sourceUp = unitVector(convertVector(S[4], S[5], S[6]));
auto sourceDirectivity = IPLDirectivity{ dipoleWeight, dipolePower, nullptr, nullptr };
細かい検証はしていませんが、Steam Audioは前2プラグインとは違い、
頭のX軸Z軸回転まで含めて計算しているような気がします。
うなづきと首かしげに対応していないというのはやはり問題だったのでしょう。
こちらはVR対応を大々的に謳うプラグインなので、安心して使えますね。
これまでと同様にProjectSettingsのAudioからSpatializer Pluginのドロップダウンを
Steam Audio Spatializerに変更しておきましょう。
また、他のプラグインとは違いAmbisonic Decoder Pluginという物もSteamが提供していますが
こちらの効果はよく分かっていません。一応選択してはいますが、、、
Steam Audioを使う場合、
まず上部メニューから Window/Steam Audio を選択し、マネージャー管理画面を開きます。
手動でSteam Audio Managerコンポーネントを設置してはいけないそうです。
このウィンドウを開くと自動的にシーンにSteam Audio Managerが追加されます。
恐らくこのオブジェクトへの参照を維持するために、Window経由で開かせているのでしょう。
その上でAudio Sourceコンポーネントを持つオブジェクトに
Steam Audio Sourceコンポーネントを追加しましょう。
それぞれのパラメータをざっくりと説明しますと、
・Direct Binaural:HRTFデータに基づいた音で鳴らすかどうか
・HRTF Interpolation:複数の音が聞こえる際、それぞれを別計算するかどうか(未検証)
・Physics Based Attenuation:物理ベースの距離減衰をするかどうか
・Direct Sound Occlusion:閉塞の計算、密室とかで音の聞こえ方を変えたい場合に使う
・Air Absorption:大気減衰させるかどうか
・Direct Mix Level:エフェクトを掛ける前の音とミックスする度合い(Dry,Wetみたいな)
・Dipole Weight:下記
・Dipole Power:双極子音源のミックス度合い(音屋じゃないのでよく分からず)
・Reflections:音の反射
こんな感じでしょうか。
ただ単純にSpatializeだけを使うなら一番最初のDirect Binauralのみをアクティブにしておけば
問題ありません。
####一応
せっかくなので他のSteam Audioコンポーネントをざっくり紹介します。
#####Ambisonic Audio系
・Steam Audio Ambisonics Source:Ambisonicオーディオを使う場合に使用
Enable Binauralを有効にするとHRTFベースで、
無効にするとパンニングでAmbisonicオーディオを再生します。
・Steam Audio Geometry
・Steam Audio Material
MeshRendererを持つオブジェクトに対してGeometryコンポーネントを追加することで、
そのMeshが音を反射する壁であると宣言することができます。
(MeshRenderer持ちを子に持つ親オブジェクトでも大丈夫、その方が管理しやすそう)
Materialコンポーネントは上記とセットで使い、音を反射する壁の材質を設定することができます。
プリセットで木、岩、ガラス、レンガ、コンクリート、カーペット、金属といった主流な材質を
Customを選択すれば周波数ごとに減衰を設定できる任意材質まで選択できます。
実装選択肢が多すぎる。
・Steam Audio Dynamic Object
Geometry系はStaticオブジェクトに対して設定するものですが、
こちらは動的オブジェクトに対して設定するコンポーネントです。
Dynamic Objectコンポーネントと同じオブジェクトにGeometryコンポーネントも
貼り付けるのが正しい実装のようです。
またこちらはセットでRigidBodyコンポーネントも必要です。
使い方としては、部屋の壁にGeometryを、ドアにDynamicとGeometryを設定
といった感じになると思います。
更にこれらは貼り付けるだけでは発動せず、Steam Audio Windowから
Pre-Export Scene
やExport All Dynamic Objects
をして
予めどのオブジェクトがAmbisonic処理対象なのかを出力しなければなりません。
後ほど問題点で書きますが、使える場面が結構絞られる気がします。
#####Audio Bake系
・Steam Audio Listener
・Steam Audio Probe Box
・Steam Audio Baked Static Listener Node
半径やProbe Boxを指定して、範囲内の環境音に掛かるReverbエフェクトをベイクします。
未検証ですが、範囲内で鳴ったベイク対象音源に対して
一律にReverbエフェクトを掛けるものだと思います。
使い方としては洞窟の反響音などでしょうか。
###懸念ポイント
####1.iOSに対応していない
もうお馴染みですが、このプラグインもiOSには公式対応しておりません。
なんでこんなに徹底的に誰も対応しないんだ、iOSが何をした。
(浮かび上がる悪行の数々)(正直仕方ない気はする)
更におもしろポイントとして、
iOS対応を願うissueがGitHubに上がっていますが、
半年近く黙殺されています。
There is quite a clear lack of spatial audio tools for iOS unfortunately.
ここまで徹底的に対応プラグインがないとなると、
iOSでの音出力扱いがかなり特殊なんじゃないかと思ったりします。
iOSプラグイン開発者の方がいれば助けてくださいお願いします。
####2.動的生成メッシュには使いにくそう
こちらはさほど問題ではないというか、高レイヤーの悩みですね。
使い方のパートでAmbisonic Audio系を紹介しましたが、
StaticでもDynamicでも、一旦Sceneに存在するMeshやオブジェクトを
Steam Audio WindowからExportしなければなりません。
つまり、Scene内に既にMeshなりオブジェクトとして存在している必要があります。
何が問題かというと、近年流行りのAR/MR系の動的空間生成などには適用しにくいという点です。
iPadやiPhoneのLiDARから生成されるメッシュや、Hololensの空間検知でのメッシュは
Scene内に存在するオブジェクトではなく、アプリ実行中に動的生成されるものです。
部屋を動的取得して、その部屋に対する適切なAmbisonic Audioを設定するという
ロマンコンボが一切通用しないのは悲しいポイントです。
もちろんHRTFベースのポストエフェクトは掛けられるので、
プラグインまるごと使えなくなるような問題ではありません。
元々がStandaloneゲームやVRゲーム対象なので、高望みし過ぎといった所でしょうか。
懸念ポイントとしてはこの程度で、総じて使いやすく高機能でまとまったプラグインと言えます。
#まとめ
近年は「グラが良いゲームはとりあえず良い」みたいな風潮があります。
PVや動画などではゲームプレイ感は伝わりにくく、ぱっと見で分かるグラの良さで
人々の興味を引いてしまうのはよく分かります。
しかし、同様に音もダイレクトに人間の耳につながるもので
こちらもこだわっていく価値は十分にあると言えるでしょう。
(ただ曲が良いとかはよく聞くけど、音声処理が良いとかあんま話題に上がらない気がする)
今回はUnity向けのSpatializer Pluginを3本紹介しました。
プラットフォームは限られますが、StandaloneやAndroid向けは
少し手間を掛けるだけで音の聞こえ方を大きく改善させることができます。
(尤も、それを聞き分けられる人間はどれほどいるのかという問題はある。自分はよく分からん)
特にSteam Audioはとにかく機能が多く、VRゲーム開発にはうってつけですね。
この記事はふつーのUnityエンジニアが書いたもので、
本職のオーディオエンジニアからすると「何言ってんのか」みたいな
間違いや勘違いを堂々書いている可能性があります。
その際はぜひコメントして頂ければ、勉強した上で該当箇所を修正させていただきます。
(意訳:アプローチを教えて下さい)
ここまで読んでくださってありがとうございました。
ご意見・ご感想・修正要望・Twitterでの共有などなど
お待ちしております。