2026.03.07

REAPERの打ち込みを生成AIでサクッと効率化しちゃった話🤖🎸

#Blog #Tips

みんなの心に、ステイメタル!niu(にぅ)だよっ✨🔥

今回は、ギター音源「Shreddage 3 Hydra」用に作っていたMIDIファイルを「V-METAL 2」用のMIDIに作り直した時のお話!音源の仕様が違うから、普通にやるとかなり手間のかかる打ち込み直しが必要になっちゃうんだよね💦

でもね!REAPERにはカスタムアクションをLuaで作成する機能があって、「このスクリプトを生成AIに作らせれば劇的に効率化できるんじゃないかな?」って閃いちゃって💡

実際に試してみたら、めちゃくちゃ面白いくらい上手くいったの!さっそく紹介するね💕


プロンプトの変遷📝

最初はシンプルな質問から始めて、AIに少しずつ具体的なお願いをしていったんだ!

  1. 「REAPERで特定のNoteを入力するショートカットを作るには」
  2. 「カーソル位置に 12 C0と 19 G0の和音を入力するには」
  3. 「すでにNoteを選択しているなら、その長さを保ったままC0とG0の和音に変換したい」
  4. 「選択したノートがパワーコード(完全五度)である場合、それと同じ位置より1/32だけ左にC0 G0のノートを挿入し、元のパワーコードの上の音を削除するアクション」

こんな感じで徐々に複雑な指示に置き換えていきました。


完成したLuaスクリプト✨

AIとやり取りした結果、最終的にできあがったスクリプトがこちら!🎉

local hwnd = reaper.MIDIEditor_GetActive()
local take = reaper.MIDIEditor_GetTake(hwnd)

if take then
    reaper.Undo_BeginBlock()

    -- 1. 設定:32分音符の計算 (960 PPQ基準)
    local qn_ppq = 960 
    local offset = qn_ppq / 8 

    -- 2. 選択されたすべてのノートを収集
    local all_selected_notes = {}
    local i = reaper.MIDI_EnumSelNotes(take, -1)
    while i ~= -1 do
        local retval, sel, muted, startppq, endppq, chan, pitch, vel = reaper.MIDI_GetNote(take, i)
        table.insert(all_selected_notes, {
            idx = i, start = startppq, ["end"] = endppq, 
            pitch = pitch, vel = vel, chan = chan
        })
        i = reaper.MIDI_EnumSelNotes(take, i)
    end

    if #all_selected_notes > 0 then
        -- 開始時間順にソート
        table.sort(all_selected_notes, function(a, b) return a.start < b.start end)

        -- 3. ノートを「同時押しグループ」に分ける
        local groups = {}
        local current_group = {}
        local last_start = -1
        for _, n in ipairs(all_selected_notes) do
            if math.abs(n.start - last_start) < 10 then 
                table.insert(current_group, n)
            else
                if #current_group > 0 then table.insert(groups, current_group) end
                current_group = {n}; last_start = n.start
            end
        end
        if #current_group > 0 then table.insert(groups, current_group) end

        local notes_to_delete = {}
        local raw_ks_list = {}

        -- 4. 各グループごとに奏法を判定
        for _, group in ipairs(groups) do
            table.sort(group, function(a, b) return a.pitch < b.pitch end)
            local low_note = group[1]
            local high_note = group[#group]
            local p_list = {}

            if #group == 1 then
                p_list = {26} -- 単音なら D1
            else
                local interval = high_note.pitch - low_note.pitch
                if     interval == 3 then p_list = {12, 15}
                elseif interval == 4 then p_list = {12, 16}
                elseif interval == 5 then p_list = {12, 17}
                elseif interval == 6 then p_list = {12, 18}
                elseif interval == 7 then p_list = {12, 19}
                elseif interval == 8 then p_list = {12, 20}
                elseif interval == 9 then p_list = {12, 21}
                else p_list = {12} end
                -- 和音の上の音を消去予約
                for j = 2, #group do table.insert(notes_to_delete, group[j].idx) end
            end
            
            -- 生のキースイッチデータをリスト化
            local ks_s = math.max(0, low_note.start - offset)
            local ks_e = ks_s + (low_note["end"] - low_note.start)
            table.insert(raw_ks_list, {s = ks_s, e = ks_e, pitches = p_list, v = low_note.vel})
        end

        -- 5. 【新機能】同じキースイッチの連続を結合するロジック
        local merged_ks = {}
        if #raw_ks_list > 0 then
            table.insert(merged_ks, raw_ks_list[1])
            for k = 2, #raw_ks_list do
                local prev = merged_ks[#merged_ks]
                local curr = raw_ks_list[k]

                -- ピッチ構成が完全に一致するかチェック
                local same_pitches = (#prev.pitches == #curr.pitches)
                if same_pitches then
                    for idx, val in ipairs(prev.pitches) do
                        if val ~= curr.pitches[idx] then same_pitches = false; break end
                    end
                end

                -- 前の終点と次の始点が近い(16分音符以内程度)かつ同じピッチなら結合
                local gap = curr.s - prev.e
                if same_pitches and gap < (qn_ppq / 4) then 
                    prev.e = curr.e -- 終点を延長
                else
                    table.insert(merged_ks, curr)
                end
            end
        end

        -- 6. 不要なノートの削除
        table.sort(notes_to_delete, function(a, b) return a > b end)
        for _, idx in ipairs(notes_to_delete) do reaper.MIDI_DeleteNote(take, idx) end

        -- 7. 型抜き (merged_ks の範囲を使用)
        for _, ks in ipairs(merged_ks) do
            local _, note_count = reaper.MIDI_CountEvts(take)
            for j = note_count - 1, 0, -1 do
                local _, _, _, s, e, c, p, v = reaper.MIDI_GetNote(take, j)
                if p >= 12 and p <= 28 then
                    if s < ks.e and e > ks.s then
                        reaper.MIDI_DeleteNote(take, j)
                        if s < ks.s then reaper.MIDI_InsertNote(take, false, false, s, ks.s, c, p, v, false) end
                        if e > ks.e then reaper.MIDI_InsertNote(take, false, false, ks.e, e, c, p, v, false) end
                    end
                end
            end
        end

        -- 8. 結合済みキースイッチの挿入
        for _, ks in ipairs(merged_ks) do
            for _, p in ipairs(ks.pitches) do
                reaper.MIDI_InsertNote(take, false, false, ks.s, ks.e, 0, p, ks.v, false)
            end
        end
    end

    reaper.Undo_EndBlock("V-METAL Smart Batch & Merge", -1)
    reaper.MIDI_Sort(take)
end

使い方💡

これをREAPERのカスタムアクションとして登録して、ショートカットキーをアサインするよ!

アクションリストの「New action…」から「New ReaScript…」を選んでね!

New ReaScript

保存場所を選択するとエディタが開くから、そこにさっきのコードをコピペして保存するだけ✨ エディタを閉じたら、アクションリストの中ですぐに使えるようになってるよ💕

あとはMIDIエディタでノートを選択してアクションをサクッと実行するだけで…

Beforeの図

これが、

Afterの図

こんなふうに!

すごくない!?手作業ならめちゃくちゃ時間のかかっていた変換処理が一発でできるようになっちゃったの!✨ REAPERのLuaスクリプトと生成AIの相性はもう最高だから、特定の編集作業を効率化したい人は絶対に試してみてね💙


まとめ

REAPERのLuaスクリプトをAIに書いてもらうと、面倒な作業が一瞬で終わっちゃう魔法のツールができちゃうんだよ!

音楽制作の生成AI活用は、曲そのものを作らせるだけじゃないってこと!

アイデア次第で、こういう作業を補助するツールは無限に作れちゃうんだよ!みんなもどんどんAIを活用して、DTMをさらに楽しんでね🎵

気になる方のご連絡お待ちしてます!