ADX2ロボット機能でブロックの長さ調整

#はじめに

前回はADX2ロボット機能を使って[MIDIを読みこんでみた](https://qiita.com/tatmos/items/738181ed9b346a8f32e4)

今回は
ブロックの長さを調整する時に、BPMとビート数で指定できると良いなと思った時があったので、
ロボット機能でできないか試してみる。

最初は、スクリプトだけで試し、後半はPySimpleGUI でUIつきで操作できるようにしてみる。


#スクリプト
``` ChangeBlockLength.py
# --Description:[tatmos]ブロックの長さを指定した長さに変更する
import sys
import cri.atomcraft.project
import cri.atomcraft.project as acproject
import cri.atomcraft.debug as acdebug

# --BeginUserVariable
"""
BLOCK_LENGTH:
  type:number
  comment:ブロックの長さ
"""
BLOCK_LENGTH = 1000
# --EndUserVariable

#選択中のブロックを得る
selected_Block = acproject.get_selected_objects("Block")["data"]
if not selected_Block :
    acdebug.warning("長さを変更するブロックを選択してください。")
    sys.exit()

#指定した長さに変更する
acproject.set_value(selected_Block[0], "BlockEndPositionMs", BLOCK_LENGTH)
```

#実行前に長さを指定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/5213faec-3b53-70d8-3ec9-5f5f8c1639ba.png)


#これで変更はできるが
BPMとビート数で、時間計算するといったUIをつけたいけど、
メニューからの選択系（ローカル動作）だとUIがうまく動かないらしいので、
リモートで設定できるように改良してみる。

#リモート対応版スクリプト
まず、シンプルにリモート化するとこんな感じ。
接続と終了で処理を挟む。

``` ChangeBlockLengthRemote.py
import sys
import cri.atomcraft.project
import cri.atomcraft.project as acproject
import cri.atomcraft.criatomcraft_api_lib as acconnect
import cri.atomcraft.debug as acdebug
# 初期化
result = acconnect.initialize()
if result != 0:
    print("initialize error")
    sys.exit()
# 接続の実行
result = acconnect.connect("127.0.0.1", 9000)
if result != 0:
    print("connection failed")
    acconnect.finalize()
    sys.exit()

# UI
import PySimpleGUI as sg



# コマンドの送信

BLOCK_LENGTH = 1000

#選択中のブロックを得る
selected_Block = acproject.get_selected_objects("Block")["data"]
if not selected_Block :
    acdebug.warning("長さを変更するブロックを選択してください。")
    sys.exit()

#指定した長さに変更する
acproject.set_value(selected_Block[0], "BlockEndPositionMs", BLOCK_LENGTH)

# 接続の終了
acconnect.disconnect()
# 終了処理
acconnect.finalize()
```

これで、リモート経由で動作させて長さが1000になるのを確認した。



#リモートでUI付きのもの

UIを表示するのに、PySimpleGUI を使ってみた。

ちょっと値取得関連がまだうまく動いていないけど（PySimpleGUI の表示更新、表示内容取得のタイミングが悪いのか）
計算結果のLengthを表示したいけど、うまくいかない・・・

とりあえず2回くらいボタン押すと変化するようにはなった
実用上はまぁ、問題ないかな。

実行している様子の動画：
https://twitter.com/tatmos/status/1356933633123127296?s=20
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/ADX2?src=hash&amp;ref_src=twsrc%5Etfw">#ADX2</a> ロボット機能を使ってブロックの長さを変更してみる動画 <a href="https://t.co/nbbyPJiiii">pic.twitter.com/nbbyPJiiii</a></p>&mdash; tatmos (@tatmos) <a href="https://twitter.com/tatmos/status/1356933633123127296?ref_src=twsrc%5Etfw">February 3, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96808/e9bdf732-65c3-6540-d74c-0f8473b0fbcb.png)

Craft上で変更したいBlockを選択し、
ダイアログでBPMとビート数を入れて、
Submitを押すと、ブロックの長さが変化します。

#リモートでUI付きスクリプト

``` ChangeBlockLengthRemoteUI.py
import sys
import cri.atomcraft.project
import cri.atomcraft.project as acproject
import cri.atomcraft.criatomcraft_api_lib as acconnect
import cri.atomcraft.debug as acdebug
# 初期化
result = acconnect.initialize()
if result != 0:
    print("initialize error")
    sys.exit()
# 接続の実行
result = acconnect.connect("127.0.0.1", 9000)
if result != 0:
    print("connection failed")
    acconnect.finalize()
    sys.exit()

# UI

import PySimpleGUI as sg

BLOCK_LENGTH = 1000

layout = [
          [sg.Text('BPM', size=(15, 1)), sg.InputText("120",key='Input-BPM'), sg.Button('Set', key='-SetBPM-')],
          [sg.Text('BeatNum', size=(15, 1)), sg.InputText("4",key='Input-BeatNum'),sg.Button('Set', key='-SetBeatNum-')],
          [sg.Submit(), sg.Cancel()],
          [sg.Text("Length: {0}".format(BLOCK_LENGTH),key='Text-Length')]]

window = sg.Window('ChangeBlockLength', layout=layout)



def caliculateLength():
	global BLOCK_LENGTH
	event, values = window.read() 
	bpm = str(values["Input-BPM"])
	beatNum = str(values["Input-BeatNum"])
	
	BLOCK_LENGTH = 1000 * 60/ float(bpm) * float(beatNum)

def setBpm_func():
	pass

def setBeatNum_func():
	pass

def submit_func():
	# コマンドの送信
	global BLOCK_LENGTH

	#選択中のブロックを得る
	selected_Block = acproject.get_selected_objects("Block")["data"]
	if not selected_Block :
	    acdebug.warning("長さを変更するブロックを選択してください。")
	    sys.exit()

	#指定した長さに変更する
	preBlockLength = acproject.get_value(selected_Block[0], "BlockEndPositionMs")["data"]
	acproject.set_value(selected_Block[0], "BlockEndPositionMs", BLOCK_LENGTH)
	acdebug.log("ブロックの中さを変更。\"{0}\" {1} to {2}".format(acproject.get_object_path(selected_Block[0])["data"] ,preBlockLength,BLOCK_LENGTH))

# 使いたい関数とkeyをセットにして辞書にするだけ
handler = {
    '-SetBPM-': setBpm_func,
    '-SetBeatNum-': setBeatNum_func,
    'Submit': submit_func,
}


while True:
    event, value = window.read()
    caliculateLength()

    if event in [None, 'Cancel']:
        break
    
    function = handler[event]  # handlerからeventに応じた関数を呼び出す
    function()
    window['Text-Length'].update("Length: {0}".format(BLOCK_LENGTH))

window.close()


# 接続の終了
acconnect.disconnect()
# 終了処理
acconnect.finalize()
```