0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

QLab5 Remote&CueViewer with touchOSC その2 〜touchOSCとLuaスクリプト

0
Last updated at Posted at 2026-01-08

ここまで

前回QLabのOSC通信を利用した表示ツールを作ろうとしたなかで、/listen/updateの仕様について知見を得ました。

/updateのほうがどうやら表示ツールとしては都合が良さそうだ、ということが開発を進めるなかで分かったのですが、今回は/listenを使って開発していた初期のtouchOSC側での実装と、実際に触ってみて分かった設計上の悩みについて書いていきます。

とりあえずつくってみた

最初に作ったテスト用のボタン群はこんな感じです。
スクリーンショット 2026-01-08 23.23.24.png

ボタンが4つとそれのラベル、そして取得データ表示用のラベルが1つです。

touchOSCの詳細は省きますが、はじめボタンのTriggerのANY/RISE/FALLに気づかず、二重送信されてしまい苦労しました。
(デフォルトのANYはpressとreleaseのどちらでも走る)

そして、表示用のラベルは、/reply/cue/selcted/durationを受けて、そのArgumentsがtextの中身になるように設定しました。
スクリーンショット 2026-01-08 23.24.57.png

データは取得できましたが、表示されるのはJSONデータであって、これでは意味をなしていません。

Lua script

ここで、scriptの登場です。touchOSCではLuaスクリプトを埋めこむことができます。
いきなりcallback関数ですが、まずは表示エリアでOSCを受け取ったらどうするかを書いてみます。

label4.script
function onReceiveOSC(message)
  local path = message[1] -- OSCアドレス
  local args = message[2] -- OSCメッセージ
  local t = json.toTable(args[1].value)
  self.values.text = t.data
end

これで、self(自分自身=このLabel)のvalues.text(テキストの値)を取得したJSONの[data]にすることができました。
ここでOSCのArgumentsを外しておかないと、スクリプトで書き換えられずにJSONデータが表示され続けてしまいます。
スクリーンショット 2026-01-08 23.35.22.png

関数を追加

せっかくのluaスクリプトですし、秒だけで表示するのも見にくいので、HH:MM:SS.sで表示できるようにしてみます。

label4.script
function onReceiveOSC(message)
  local path = message[1] -- OSCアドレス
  local args = message[2] -- OSCメッセージ
  local t = json.toTable(args[1].value)
- self.values.text = t.data
+self.values.text = formatTime(t.data)
end

+function formatTime(sec)
+  if type(sec) ~= "number" then
+    return "--:--:--.-"
+  end
+  local h = math.floor(sec / 3600)
+  local m = math.floor((sec % 3600) / 60)
+  local s = sec % 60
+  return string.format("%02d:%02d:%04.1f", h, m, s)
+end

formatTime(sec)という秒を引数にした関数をあらたに作って、HH:MM:SS.sを返すようにしました。数値以外が来た場合に備えて、未取得時は --:--:--.- を返すようにしています。
見やすくなりました。
スクリーンショット 2026-01-08 23.43.16.png

スクリプトをどこに書くか

いま、個別のLabel(表示対象)にスクリプトを書いてみたわけですが、これをすべてのLabelに対して行うのは手間がかかりますし、同じような処理を多くの箇所に書かなくてはいけないという最大の弱点があります。

touchOSCでは各コントローラそれぞれにスクリプトを書くことができますが、それ以外にドキュメントそのもの(Document Treeのトップ=ROOT)にも書くことができます。

たとえば、先ほどと同じ内容をrootに書き直すとこのようになります。

ROOT.script
function onReceiveOSC(message)
  local path = message[1]
  local args = message[2]
  
+  if path == "/reply/cue/selected/duration" then
    local t = json.toTable(args[1].value)
-    self.values.text = formatTime(t.data)
+    self.children.label4.values.text = formatTime(t.data)
  end
end


function formatTime(sec)
  if type(sec) ~= "number" then
    return "--:--:--.-"
  end

  local h = math.floor(sec / 3600)
  local m = math.floor((sec % 3600) / 60)
  local s = sec % 60

  return string.format("%02d:%02d:%04.1f", h, m, s)
end

取得アドレスによって場合分けし、テキストの書換先も自身ではなく、自身の子の[label4]を書き換えています。

このように場合分けを書き足していき、このような表示ができあがりました。
スクリーンショット 2026-01-09 0.02.29.png

  • 再生中のキュー
    • number
    • displayName
    • formatTime(duration)
  • 選択中のキュー
    • number
    • displayName
    • formatTime(duration)
      が表示できました。

再生バーを作りたい

せっかく再生状態までGETできたので、時間表示をつくってみます。
再生状態を示すバーは、FADERを横むきにして使います。
function update()にかくと毎フレームごとに実行されるので、getMillis()で時間を取得して、約0.1秒ごとに時間を進め、経過時間の割合を求めてフェーダーのxの値を更新します。
この時点では、「再生開始時刻」と「duration」が分かっていれば、表示側の時計で十分だろうと考えて、こういう実装を行いました。

local nowMillis = getMillis() - startMillis
local elapsedSec = nowMillis / 1000
local remainTime = runningDuration - elapsedSec
local progress = elapsedSec / runningDuration

self.children.runningTime.values.x = progress
self.children.elapsedTime.values.text = formatTime(elapsedSec)
self.children.remainTimeLabel.values.text ="-"..formatTime(remainTime)

スクリーンショット 2026-01-09 0.17.52.png
なんとなくそれらしく出来てきました。

ここまで作ってみて感じた限界

これまで/listenで得られる情報をもとに作り進めてきましたが、同時に弱点がたくさん明らかになりました。

再生中のキューが増えるとダメ

プログラム経験のある方なら既にお気づきかもしれませんが、この方法は1つのキューが別々で止まらずに動くときは問題なく動作しているように見えます。
しかしながら、再生・停止の状態しかわかららない上、複数が同時に走った場合OSCメッセージが同時にたくさんきます。
そうなると、表示されるものもその時次第ということになります。

また、停止すると表示を空にするという処理を実装していたのですが、そうすると、1と2が同時に走っていて1が先に停止した場合、停止イベントを受けて表示が空になってしまい、再生が続いているのに表示が消えてしまいました。

これは、表示側が「今どのキューが再生中なのか」という状態を持たず、イベント発生により送られるOSCだけに依存していたためです。

再生位置をずらすとダメ

再生中に再生位置を動かしても、イベントは通知されないため、表示側の時計に頼るシステムでは当たり前に無理でした。

ここまで作ってみて分かったのは、問題は touchOSC や Lua の書き方ではなく、

  • 表示側が「状態」を持っていないこと
  • イベントとローカル時計に依存していること
    そのものだった、という点です。

つまりこの構成のままでは求めているものは絶対に得られず、すなわち/listen ベースの設計を根本的に見直す必要があることが分かりました。

そして、QLab Remoteなどの挙動を調べた結果 /updateベースで設計するという方法をとり、いちから作り直すことにしました。

ちなみに

ここまでに費やした時間、約半日。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?