誰でもそれっぽくピアノが弾けるツールを作った
はじめに
私はこの記事に書いたとおり音楽理論に沿ってランダムに曲を生成するという試みを行っていました。その活動の中で、基礎的な音楽理論は割と単純(コード構成音を鳴らせばそれなりに良い音楽になる)でありアルゴリズムとして書き出せば通常音楽家が実践的に体に叩き込む鍵盤上での指の形や指運びをかなり単純化でき、学習ステップをスキップできるのではというアイデアが思い浮かびました。実はこのアイデア自体は古くからありOmnichordや最近だとINSTACHORDが有名です。同様な機器はコードの種類ボタンを片手で抑え、それによりもう片方の手側のボタンにコード構成音が単一音づつ割り当てられるという仕組みです。なので一つの楽器を演奏するのに両手が塞がるタイミングが多いです。今回開発したシステムはこのコード種類の指定を事前にユーザに任意順で拍数含めてテキストファイルに書かせ、それを時間経過で遷移ループする方式としました。これにより両手の占有率を下げることに成功し、より複雑な操作や演奏構成ができるようになりました。更に同期信号を出し他の機器と協調できることでより曲としての構成が強固にできました。
使い方はこの動画にまとめました(9:51あたりの演奏は評判がいいです)。以降の説明のイメージを持っておくためにご視聴をおすすめします。この記事では内部仕様について解説します。
Githubページ
以下にて無償公開しています。Windows,Mac,Linux向けに実行形式ファイルもReleaseに公開しています。
演奏動画
以下は私がchordMapperを使って演奏したものです。私はここ数ヶ月で音楽理論の基礎をこの開発のために勉強した程度で、通常のピアノの運指(瞬時にコードを鳴らせるなど)はできず曲を演奏できるレベルにはありません。その程度のスキルでも即興でまあまあいい感じに音楽奏でられているのではと思います、、、どうですかね?
以下は四分の五拍子でミクソリディアン音階という割と込み入った構成です。複雑な構成でも概要だけ知ってテキストに名称だけ書いてしまえばなんとなく弾けてしまいます。流石に四分の五拍子はリズム感が難しく20分ほど練習しました。それでも即興で弾いています。
分散和音
chordMapperが上記動画のような即興弾きを可能としている裏には分散和音という理論があります。それはコードを階段の手すりのように捉え、その手すりさえ握っていればどんな(制限内の)音を鳴らしても美しく聞こえるというものです。例えば一番簡単な三和音であるCメジャーを例にすると、その構成音であるドミソは同時ではなくとも、別タイミングで鳴らしてもメロディーとして良く聞こえます。この動画が分かりやすいです。
当システムではあるコードが指定された区間では、分散和音に含まれない音は存在できないように鍵盤と出る音のマッピングを変えます。これによりすべての鍵盤が理論的に美しいメロディを出せるようになります。
コードマッピング方式
上記の分散和音の理論のもとで、コードは以下のように鍵盤上に割り当てられます。現在は4和音のみ設定ファイルで指定できます。コード+Gなどと書いているのは通過音です。通過音は和音構成音の他にも弾いて良い音であり、これにも音楽理論による規定がありますがここでは割愛します。当システムでは理論に沿って自動で通過音を選定します。これで4つのコード構成音と1つの通過音の計5音が1オクターブ分の"鍵盤ブロック"に割り当てられることとなります。鍵盤ブロックは4つの白鍵単位となっています。この4つの鍵盤ブロック中の黒鍵の左から1番めにルート音を割り当て、それ以外の3音+通過音を音の高さ順に並び替えて4つの白鍵に割り当てます。黒鍵の2番目にはテキストで指定した四和音の内の三和音を割り当て、黒鍵の3番目には四和音からルートの音を取った3音を割り当てています。なので黒鍵の1番目と3番目を同時に弾けば四和音が鳴ります。
以下はシステムが音楽理論に沿って自動対応していることですが、少し深い内容なので興味がある方は調査されてください。
- 設定ファイルで指定する複数コード中の各コードは1番目のコードとの音高差が少なくなるように配置されています。これは転置によるヴォイシングというものです。
- 5音の内でルートだけ1番目の黒鍵に割り当てています。これはルートレスヴォイシングを意図しています。ルートはベース専用の音源に任せることでメロディは白鍵を適当に叩けば良いというスタイルが取りたかったのです。またヴォイシングによってはルートと他4音とで音がぶつかる場合があるので物理的障壁をもうける意図もあります。
スレッド構成
リズムをキープしつつ、任意のタイミングで入力が来るなどするため複数のスレッドを実行する必要がありました。おそらくDAWなど音楽関係のプログラムであれば同程度の量のスレッドを必要とするでしょう。コードのベースは当プロジェクトでも利用しているEuterpeaのサンプルコードです。
スレッド名 | コード上関数名 | 役割 |
---|---|---|
メイン | mainLoop | 各スレッドを作成しUIからの終了要求を待つ |
UI | tuiMain | TUI画面表示とPCのキー入力処理 |
コード進行 | clockLoop | 時間経過によるコードマッピング変更,MIDI音源への同期信号生成,録音した打鍵の再生 |
MIDI入力 | midiInRec | MIDI入力からコードマッピングし出力バッファに追加,録音有効時の打鍵情報の保存 |
MIDI出力 | midiOutRec | 出力バッファから実際のMIDI出力 |
制御入力処理 | controlReceiver | MIDIキーボード上のタッチパッドからなどの入力によるコード進行セットの変更,打鍵録音機能の有効化と録音再生の停止/再開など |
ソフト実装面の考察
設計から関数型
コードマップそれ自体が数学的に純粋な意味での関数であり、実際にtype ChordKeyMap = (Key -> Maybe [Key])
として定義しており関数であっても値としてコード上で扱っています。これは関数型ならではの手法といいますか物の考え方のように思えます。即ちこれは実装時点からの優位性ではなく、設計時点から関数オブジェクトとしてコードマップを捉えることが自然に適合した課題です。関数型的思考を持たなければ、関数オブジェクトの代わりに構成音を常に保持し単一入力毎に同一引数の関数入力をするような設計となっていたでしょう。どちらの設計でも用意する関数としては同一の[Key] -> Key -> Maybe [Key]
なのですが、関数型的思考ではカリー化により[Key] -> (Key -> Maybe [Key])
として捉え設計及び実装の単純化ができます。ラムダ式など関数型手法を実装している手続き型言語は多いので、そういった言語の現場でも設計時点から生きる思考だと思います。ChatGPTに聞いたところReaderTを使えば良いとのことですがまだ上手い使い方を想像できていません。別記事にでも書いてみます。
引数地獄
共有変数を多く(全体で20数個)扱う必要があり、実装当初はそれを数個単位で関数にそのまま渡すfunc::TVar a -> TVar b -> TVar c... -> IO ()
などしており得にスレッドのメイン関数で引数の数が膨大になりました。これをfunc::(x -> IO y)->... -> IO ()
としてカリー化した関数lilFunc:: TVar a -> TVar b -> TVar c...-> (x -> IO y)
を関数オブジェクトとして引数渡しするように構成を修正しました。これにより関数の役割分担が明確になり記述量削減ができました。CやPythonなどでは共有変数はグローバルにできるためこういう苦労がないのかなと思います。TVarやMVarが提供しているロックをかける機構だったりが使え楽できるので、引数が多くなるのはしょうがないか、、、なにか私の知らない手法で楽ができるのかもしれませんが。そもそも共有変数が無駄に多い気がするので見直しが必要かもしれません。
演算子ハイ
<$>,<*>,>=>
といった演算子が必要な箇所があり楽しめました。特に初期の勉強以来ご無沙汰だったクライスリ合成>=>
が利用できたのは面白かったです。当プログラムではgetData::a->IO b
,addBuf::b->IO ()
をgetData>=>addBuf
として指定したデータをそのままバッファリングするa->IO()
を構成するという使い方をしました。これを知らなければdo記法やラムダ式で記述してしまいがちだと思いますので、豊かな記述を行うためにもControl.Monadなどの読み込みは重要だと実感しました。
音楽面の考察
ピアノ初心者なりには良い演奏ができている自負があるのですが、
私のYoutubeチャンネルを見てのとおり良い評価は受けていません。これがシステムの限界なのか私のセンスが悪いだけなのかまだわかっていないので、一度すごく時間をかけた渾身の曲を作る必要があるかと思います。私のセンスは磨くしかないとして、
コード構成音と通過音のみ弾ける方式が曲をつまらなくしている可能性もあります。また現時点では即興できる音源が打鍵録音機能を合わせて2つしかないので、それもつまらないかもしれません。となるとエレクトーンのようにフットスイッチを使う構成にしないと行けないのでしょうか?ベース音1音程度であれば行けるような気はしますが演奏の手軽さから離れてしまいますね、、、
また将来は普通にピアノが弾けるようになりたい人が、このシステムを使うことで悪影響とならないかが心配です。もしかしたら作曲時のコンセプトのようなものを作りやすい構成だったりするかもしれないので、そういう使い方で売り出すのもありかもしれません。
今後
現在はコード弾きのみですが、メロディックマイナー、ブルーススケールなども弾けるようにしたいです。また、MIDIキーボードを複数台にして即興セッションできるようにしても楽しいと思います。折角コードが時間経過で流れているので、アルペジオ機能も実装したいです。
Discussion