DAWにコードを入力するのはとても面倒。
コード(音楽的な)は3音~複数音による和音ですから当然その回数入力してやらないといけません。
また、コードにはメジャー、マイナーからはじまりもろもろ種類があります。
ピアノロールに打ち込むときに個人的にはどうもしっくりこない(コード主体の音楽をやってこなかったのもある)ので何か良い方法はないかとコードを入力するためのプラグインを導入してみたりもしたけれどいまいち便利ではありませんでした。
また、ギター譜などになるとそもそもコード名しか書いていないこともあり、コードになじみのない(理屈ではわかっている)人にとってはいいから楽譜に書いてくれ。となります。
ちょっとしたひらめき
どうにかしてコードを簡単に入力できないかと考えているうちにちょっとしたひらめきがありました。
midiのクリップに名前が付けられるのでそこにコード名などを入力してスクリプトで変換してやればいいんじゃないだろうか。と。
この時点ではReaScriptに何が出来るのかわかっていませんがluaは解ります。
じゃあAPIさえ分かればいけるのでは? と調査を開始しました。
試行錯誤
プロジェクトのなかのテイクの中のノート。のような階層であるとか、この辺りはSynthesizer Vなどでも気を付けないといけないところですがどういう概念で作られているDAWなのか探りながらやらないと要らない苦労をすることになるので柔軟に…。
引っかかりポイントとしてはテイクとクリップの開始時間のあたりです。
クリップ内であってもグローバル位置を持っていてそのグローバル位置を指定して作らないといけないなどは、言われてみればそうだな。という見落としがちな部分だったと思います。
- テイクにノートをつくる
- テイクの名前を取得する
この辺りが出来ればいけるだろうと弄っているとなかなかいい感じになったので一気に作業しました。
完成したスクリプト
-- 指定された take が MIDI かどうかを判定
function is_midi(take)
return take and reaper.TakeIsMIDI(take)
end
-- 指定された take 内のすべての MIDI ノートを削除
function clear_take(take)
if is_midi(take) then
local note_count = reaper.MIDI_CountEvts(take)
for idx = note_count - 1, 0, -1 do
reaper.MIDI_DeleteNote(take, idx)
end
reaper.MIDI_Sort(take)
end
end
-- 指定された位置にノートを1つ挿入
function make_note(take, start_pos, len, prop)
if is_midi(take) then
local pitch = prop.pitch or 60
local velocity = prop.velocity or 100
local chan = prop.chan or 0
local selected = prop.selected or true
local muted = prop.muted or false
local start_ppq = reaper.MIDI_GetPPQPosFromProjTime(take, start_pos)
local end_ppq = reaper.MIDI_GetPPQPosFromProjTime(take, start_pos+len)
reaper.MIDI_InsertNote(take, selected, muted, start_ppq, end_ppq, chan, pitch, velocity, false)
end
end
-- 文字列を区切り文字で分割する
function split(str, sep)
local result = {}
for match in (str .. sep):gmatch("(.-)" .. sep) do
table.insert(result, match)
end
return result
end
-- 音名テーブルとコード定義
local notes = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}
local chords = {
["M"] = {4, 7},
["m"] = {3, 7},
["7"] = {4, 7, 10},
["M7"] = {4, 7, 11},
["m7"] = {3, 7, 10},
["dim"] = {3, 6, 9},
["6"] = {4, 7, 9},
["aug"] = {4, 8},
["sus4"] = {5, 7},
}
local prj = reaper.EnumProjects(-1) -- 現在のプロジェクトを取得
local items = reaper.CountSelectedMediaItems(prj) -- 選択中のメディアアイテムの数を取得
-- 各選択アイテムに対して処理を実行
for idx = 0, items - 1 do
local item = reaper.GetSelectedMediaItem(prj, idx)
local take = reaper.GetActiveTake(item)
local retval, name = reaper.GetSetMediaItemTakeInfo_String(take, "P_NAME", "", false)
local item_pos = reaper.GetMediaItemInfo_Value(item, "D_POSITION")
local item_len = reaper.GetMediaItemInfo_Value(item, "D_LENGTH")
-- 既存のノートをクリア
clear_take(take)
-- 名前(例:"C M")を分割して要素(音名やコード)を取得
local elem = split(name, " ")
local note_num = 60 -- デフォルトのMIDIノート番号(中央C)
for i, e in pairs(elem) do
-- route key
for ni, n in pairs(notes) do
if e == n then
note_num = 59 + ni -- 59+C=60の中央Cを基準にして調整
end
end
-- ルート音を挿入
local prop = { pitch = note_num }
make_note(take, item_pos, item_len, prop)
-- コード名に一致したら対応するコード音を挿入
for ci, c in pairs(chords) do
if e == ci then
for ni, note in pairs(c) do
local prop = { pitch = note_num + note }
make_note(take, item_pos, item_len, prop)
end
end
end
end
-- 1アイテム分の処理完了
end
動作画面
普段luaのコードを出す必要はありませんが。
一応動かしてるんだぞっていうアピールのため。
midiクリップを選択してF2でアイテムプロパティを開き、名前に「C sus4」という感じでスペースで分けて入力してやります。1クリップに一種類だけです。
あとはスクリプトを実行すればそれぞれのクリップの長さ分のシーケンスが挿入されます。
これからの展開としてバッキング入力に変換できるようなものが出来れば便利かもしれません。
グロッサリー
クリップへの入力
「C sus4」など、基音とコードの種類を入力
基音
- C
- C#
- D
- D#
- E
- F
- F#
- G
- G#
- A
- A#
- B
コードの種類(他にも必要であればluaに追加してください)
- M Major
- m minor
- 7
- M7
- m7
- dim
- 6
- aug
- sus4