REAPERで音の長さを変えるスクリプト


#はじめに

20年ほど昔Logicでステップ入力していて、
TABキーで音の長さを伸ばせる機能があって、
もしかしてREAPERのスクリプトなら似たような事ができるかもと思い試してみたメモ。
![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/d7dafab1-c9c0-c064-11bb-04675e329a0d.gif)
#スクリプト
``` tatAddLength.py
RPR_ClearConsole()

midieditor = RPR_MIDIEditor_GetActive()
take = RPR_MIDIEditor_GetTake( midieditor )

notecntOut = 0
ccevtcntOut = 0
textsyxevtcntOut = 0

( retval, take, notecntOut, ccevtcntOut, textsyxevtcntOut ) = RPR_MIDI_CountEvts(take, notecntOut, ccevtcntOut, textsyxevtcntOut )

noteidx = notecntOut-1  #last note
selectedOut = True
mutedOut = True
startppqposOut = 0
endppqposOut = 0
chanOut = 0
pitchOut = 0
velOut = 0

( retval, take, noteidx, selectedOut, mutedOut, startppqposOut, endppqposOut, chanOut, pitchOut, velOut ) = RPR_MIDI_GetNote(take, noteidx, selectedOut, mutedOut, startppqposOut, endppqposOut, chanOut, pitchOut, velOut )

retval = RPR_MIDI_SetNote( take, noteidx, True, mutedOut, startppqposOut, endppqposOut+240, chanOut, pitchOut, velOut, False )

adjust = 0.125 * 120/RPR_TimeMap2_GetDividedBpmAtTime(0,0.125) # Tempo
RPR_MoveEditCursor(adjust, False)
```

#なかなかうまくいかない点

##音を伸ばす単位とカーソルを動かす単位が違う？

- 音の長さを変える
- 
```.py
retval = RPR_MIDI_SetNote( take, noteidx, True, mutedOut, startppqposOut, endppqposOut+240, chanOut, pitchOut, velOut, False )

```


SetNoteで指定するのはQNとよばれるMIDI分解能の数値。
16分音符がメインなので、決め打ちで240足している。

**※QNはテンポに依存せず音楽的、楽譜的に扱える。反面実時間に直すにはグローバルテンポを参照する必要がある。**

960 = 4分音符
480 = 8分音符
240 = 16分音符


- カーソルを移動する

```.py
adjust = 0.125 * 120/RPR_TimeMap2_GetDividedBpmAtTime(0,0.125) # Tempo
RPR_MoveEditCursor(adjust, False)
```

MoveEditCursorは・・・　これよくわからない。

試行錯誤して、BPM120の時1を入れると1ビート進むという感じがする。
時間なのかと思ってQNTotTimeとかそれっぽいの試してもなんか違う・・・

RPR_TimeMap2_GetDividedBpmAtTime(0,0.125)
がテンポと同じ値を返すので、120から比率だして、
これで一拍の長さがとれたので、1/4倍して16分音符の長さにしている。
**※ココあっているか自信ない**

## キー設定
おそらくTABキーはシステム的にビューを切り替えるのが標準なので、ショートカットを割り当てるのはよくない感じがする。（割り当ててもうまく動かない）
ので、今回は左下の方のキーのXを使うことにした。
打ち間違えたときなど、Ctr-Zもよく使う。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/69b97abb-f9bf-037f-c138-4dd4f65d2c79.png)

あと、Cキーで16分音符だけ進めるようにした。
休符を入れているイメージ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/20ce493d-e236-425a-f660-b0b8dae0c698.png)

**※Xキーもウィンドウ閉じるとかとバッティングしていて、うまく動かないときもある・・・謎。**

##ちょっと違うけどだいたい満足

Logicの時はノートオフが来る前の（長さが未確定な）音を伸ばすとかだったので、和音とかもいけていたのだが、
REAPERでは記録されている最後のノートだけ伸ばすことにしている。

# Grid対応

- 16分音符固定にしているけど、ここは変えられても良いかなぁとか。グリッドに合わせるとか。

```.py
( grid, take,  swingOutOptional,  noteLenOutOptional) = RPR_MIDI_GetGrid(take, 0, 0)

retval = RPR_MIDI_SetNote( take, noteidx, True, mutedOut, startppqposOut, endppqposOut+960*grid, chanOut, pitchOut, velOut, False )

adjust = grid * 60/RPR_TimeMap2_GetDividedBpmAtTime(0,0.25) # Tempo
```
こんな感じだろうか


```tatAddLengthGrid.py
RPR_ClearConsole()

midieditor = RPR_MIDIEditor_GetActive()
take = RPR_MIDIEditor_GetTake( midieditor )

notecntOut = 0
ccevtcntOut = 0
textsyxevtcntOut = 0

( retval, take, notecntOut, ccevtcntOut, textsyxevtcntOut ) = RPR_MIDI_CountEvts(take, notecntOut, ccevtcntOut, textsyxevtcntOut )

noteidx = notecntOut-1  #last note
selectedOut = True
mutedOut = True
startppqposOut = 0
endppqposOut = 0
chanOut = 0
pitchOut = 0
velOut = 0

( retval, take, noteidx, selectedOut, mutedOut, startppqposOut, endppqposOut, chanOut, pitchOut, velOut ) = RPR_MIDI_GetNote(take, noteidx, selectedOut, mutedOut, startppqposOut, endppqposOut, chanOut, pitchOut, velOut )


( grid, take,  swingOutOptional,  noteLenOutOptional) = RPR_MIDI_GetGrid(take, 0, 0)

retval = RPR_MIDI_SetNote( take, noteidx, True, mutedOut, startppqposOut, endppqposOut+960*grid, chanOut, pitchOut, velOut, False )

adjust = grid * 60/RPR_TimeMap2_GetDividedBpmAtTime(0,0.25) # Tempo
RPR_MoveEditCursor(adjust, False)
```

#ToDo:
- ペダルで伸ばせるようにする（これはMIDIだけど、モードが異なるとき誤爆してしまうのをどうするのか　ステップ入力時のみ機能させたい感じ）
- 戻しつつ消すみたいな動作とか
- 和音対応（これは最後のノートと同じスタートのノートに適用すればできそう）