ADX2 別プロセスで原音再生

#はじめに
[ADX2スクリプトで原音再生](https://qiita.com/tatmos/items/86cadb3c3b4daa5ca9fb)の改良版の紹介。

環境：CRI AtomCraft 3.45.00

#スクリプトの流れ
UI系は外部プロセスにしないとCraftごと落ちてしまうことがわかっていて、
バッチみたいな処理にしか利用できていないのが現状なのだけど、

- 情報収集（選択キューのマテリアルを探し元ファイルパスを得る）
- 解析　　（今回はとくになし）
- 実行　　（音を再生する）

という流れの、
情報収集だけCraft上で動作させて、
解析、実行を別プロセスにしておくと、
Craftを解放できるはず


##別プロセスのメリット
- 中断ができる（プロセスを止めることができる）
- 非同期実行にできる(重い解析とかでCraftを待たせなくすることができる）
- Craft上でのスクリプトの実行の制限から解放される
    - UIが表示できる（解析結果表示など）
    - スクリプト分割時の更新など（Craftは一度importしてしまうとCraft再起動するまで外部ファイルの再読み込みをしないため）

##別プロセスのデメリット
- ファイルが増える
- デバッグや、結果の収集、インタラクティブなやりとり（プロセス間通信）に少し工夫が必要になる。
（Craftはサーバー機能でもスクリプト動かせるので駆使すればできなくはないが）

## 改善点
###長い音の再生をキャンセルできない問題
前回の再生の場合、同じプロセスで再生をしてしまうため、
曲のような長い音を再生した場合、再生が終わるまで待たないといけない状態でした。

そこで、再生処理を別プロセスに投げてしまうことで、キャンセルできるようにします。

キューを選択し、PlayWav.pyを実行すると
マテリアルのフルパスを得ることができ、
subprocessを利用して
Z_ExtAudioPlay.pyを実行します。
引数の波形を再生します。

また、再生しているウィンドウを閉じることで、中断することができます。


# プロセスを起動するスクリプト
``` PlayWav.py
# --Description:[tatmos][Preview]選択したオブジェクトの原音を再生

import subprocess
from time import sleep
import cri.atomcraft.project as acproject
import cri.atomcraft.debug as acdebug

#--------

#選択中のマテリアルまたはウェーブフォームリージョンまたはキューを得る
selectedMaterials = acproject.get_selected_objects("Material")["data"]
selectedWaveformRegions = acproject.get_selected_objects("WaveformRegion")["data"]
selectedCues = acproject.get_selected_objects("Cue")["data"]

selectedMaterial = None

if len(selectedMaterials) > 0:
    selectedMaterial = selectedMaterials[0]
if len(selectedCues) > 0:
    selectedWaveformRegions = acproject.find_objects(selectedCues[0],"WaveformRegion")["data"]
if len(selectedWaveformRegions) > 0:
    selectedMaterial = acproject.get_value(selectedWaveformRegions[0], "LinkMaterial")["data"]

if not selectedMaterial:
    acdebug.warning("再生するマテリアルまたはウェーブフォームリージョンまたはキューを選択してください。")
    sys.exit()

print(acproject.get_value(selectedMaterial, "Name")["data"])

input_path = acproject.get_value(selectedMaterial, "SrcFileAbsolutePath")["data"]

if not input_path:
    acdebug.warning("マテリアルのパスがみつかりません。")
    sys.exit()

print("Path:" + str(input_path))

#別プロセスで再生

command = ["python","C:/MyDearest/github/ADX2Robot/local/Z_ExtAudioPlay.py",input_path]
proc = subprocess.Popen(command) 
proc.communicate()
```
# 再生をするスクリプト
``` Z_ExtAudioPlay.py
import pyaudio
import wave
import time
import sys

args = sys.argv
input_path = args[1]

p = pyaudio.PyAudio()

#--------
#AUDIOのDEVICEを列挙
#hostAPICount = p.get_host_api_count()
#for cnt in range(hostAPICount):
#    print(a.get_host_api_info_by_index(cnt))

#音を鳴らすDEVICEを指定
DEVICE_INDEX = 0

#--------
#ファイルを開く
wf = wave.open(input_path, "rb")

def callback(in_data, frame_count, time_info, status):
    data = wf.readframes(frame_count)
    return (data, pyaudio.paContinue)

#ストリームの作成
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                channels=wf.getnchannels(),
                rate=wf.getframerate(),
                input_device_index = DEVICE_INDEX,
                output=True,
                stream_callback=callback)

#再生
stream.start_stream()
print("再生中 ... " + input_path)

#再生終わりまで待つ
while stream.is_active():
    time.sleep(0.1)

print("done.")
stream.stop_stream()
stream.close()
p.terminate()
wf.close()
```