この記事は Ableton Live を JavaScript でハックする方法を紹介したシリーズの、三番目記事です。このシリーズでは読者は Ableton Live 9 suite を持っていて、JavaScript にある程度慣れ親しんでいることを前提としています。

This is the 2nd of a series of articles about hacking on Ableton Live with JavaScript. These articles assume you own Ableton Live 9 Suite and are comfortable coding JavaScript.

この記事では Live API が活用できる様々な Live object にアクセスする方法と、Live object のプロパティや子オブジェクトを調査していく方法、それからオブジェクトを変更する方法を学んでいきます。

In this article we'll learn how to access the different Live objects available to the Live API, how to examine their properties and child objects, and how to make changes to the objects.

この記事は過去の記事を読んでいることを前提としていますので、わからないことがあれば過去の記事も参照するようにしてください。

This article builds upon the previous articles, so refer back to them if you're having trouble here.

Live API Documentation

Live API の全てを取り上げることはできませんので、ここで取り上げることができなかったことについての情報が必要な場合には、Max のドキュメンテーションを読むようにしてください。

I can't cover everything about the Live API in these articles. When you need more information, Max's documentation is a good resource:

Live API Overview のページは、Max object について特に取り上げており、JavaScript によるものではありません。どちらもその概念は共通してはいますが、私たちの記事では live.object や live.path や live.observer といった Max オブジェクトは一切使いません。Max object ではなく、全てを JavaScript LiveAPI オブジェクトを通して操作していきます。

Note this page focuses on Max objects, not JavaScript. The concepts are the same, but we won't be using any Max objects like live.object, live.path, or live.observer. Instead, we do all this via the JavaScript LiveAPI object.

Getting Started

まずは二番目の記事で作成した log() 関数をコピペしましょう。これを使って Live API を詳しく見ていきます。この記事以降は log() 関数がスクリプトに含まれているものとして進めていきます。このコードについてはこれ以降説明しませんので、常にコードの先頭にこの関数を配置してください。

First let's paste in our log() function that we built in article #2: Logging & Debugging. This will help us explore the Live API. The rest of this article assumes the log() function is in your script. I won't repeat this code again, so pretend it's at the top of all the code examples.

log 関数
function log() {
  for(var i=0,len=arguments.length; i<len; i++) {
    var message = arguments[i];
    if(message && message.toString) {
      var s = message.toString();
      if(s.indexOf("[object ") >= 0) {
        s = JSON.stringify(message);
      }
      post(s);
    }
    else if(message === null) {
      post("<null>");
    }
    else {
      post(message);
    }
  }
  post("\n");
}
 
log("___________________________________________________");
log("Reload:", new Date);

準備はできましたか?それで LiveAPI オブジェクトを作成して、そのプロパティをいくつか見ていきましょう。

Ready? Let's create a LiveAPI object and take a look at some of its properties:

LiveAPI オブジェクトの作成
var liveObject = new LiveAPI();
 
log("path:", liveObject.path);
log("id:", liveObject.id);
log("children:", liveObject.children);
log(liveObject.info);

すると Max ウィンドウには次のように表示されます。

The Max window shows:

Max ウィンドウへの表示結果
path:
id:  0
children:  this_device,control_surfaces,live_app,live_set
"No object"

うーん…path は空ですし、明らかに "オブジェクトはありません" と表示されてしまっています。でも children はいくつかあるようなので、ここには期待が持てそうですね。(すぐにこの部分をみていきます)

Hmmm... The path is empty and we're apparently looking at "No object". But it has some children, which seems promising (more on that soon).

object が何もない理由は、まだ Live API に Live object を結びつけなかったからです。Live API に Live object を結びつけるためには、LiveAPI コンストラクタに path を与えます。

The reason we have no object is because we haven't actually connected this LiveAPI to an actual Live object yet. We can do that by giving a path to the LiveAPI constructor:

LiveAPI に path を与える
var liveObject = new LiveAPI("live_set master_track");
 
log("path:", liveObject.path);
log("id:", liveObject.id);
log("children:", liveObject.children);

今回は log に liveObject.info を表示しませんでしたが、これは後ほど取り上げます。

I've omitted liveObject.info this time, we'll come back to that.

今回は次のように表示されはずです。(ID だけは多分異なります。)

This time we should see something like this (your id may be different):

出力結果
path:  "live_set master_track"
id:  4
children:  canonical_parent,clip_slots,devices,mixer_device,view

Live Paths

今見てきたように、Live が持つ object に接続するためには、LiveAPI に object への path を与えます。では、この path はどのように決めるのでしょうか?

As we just saw, to connect to an object in Live we need to give the LiveAPI a path to the object. How do we determine the path?

Live Object Model ダイアグラム を詳しく見ていきましょう。root object から辿っていって目的の object まで、object から object を辿っていきます。この目的の object までの道筋がそのまま path になります。では root object とは何でしょうか。liveObject の子要素を覚えていますでしょうか?"No object" と表示された時の件です。実は子要素として表示されていた liveapp、liveset、controlsurfaces、thisdevice が root object なのです。

Examine the Live Object Model diagram. We form paths by starting from a root object and following the arrows on the diagram from object to object. What's a root object? Remember the children of the liveObject when we had "No object"? Those are the root objects: liveapp, liveset, controlsurfaces, and thisdevice.

Live Object Model ダイアグラムを辿って行く道筋を、スペース区切りの文字列にしたものが、path です。例えば "liveset mastertrack" という path をつくったとしたら、これは root object である liveset から出発して(ダイアグラムの上方左側にある)、mastertrack 矢印を辿って Track オブジェクトに到着します。("Track" と書いてあるボックスです)

As we follow the arrows around the Live Object Model diagram, we build a space separated string. That's the path. When we did "liveset mastertrack", this corresponds to starting at the liveset root object (towards the upper left of the diagram), and following the mastertrack arrow down to a Track object (represented by the box that says "Track").

ではもっと複雑な path をつくってみましょう。ダイアグラムの矢印をどんどん辿っていきます。

Let's try a more complex path. Follow along with the arrows in the diagram.

より長い path を作る
var path = "live_set master_track mixer_device volume";
var liveObject = new LiveAPI(path);
 
log("path:", liveObject.path);
log("id:", liveObject.id);
log("children:", liveObject.children);
path:  "live_set master_track mixer_device volume"
id:  2
children:  canonical_parent

いけるところまで矢印を辿って進んでいきましたが、まだ子要素に "canonical_parent" がありますね。名前からもわかると思いますが、これは現在のオブジェクトからリーチできる親オブジェクトです。ですので "child" という名称は誤解を与える名称かもしれません。children というのは、リーチできるオブジェクトのことだと考えてください。

Here we've followed arrows in the diagram as far as we can, but there's still a child called "canonical_parent". As you might guess from the name, this is a parent object from which you can reach this object. So the term "child" is misleading here. Think of children as paths we can follow to reach other objects.

Live Objects

さて path を使って Live object にアクセスできるようになりました。次に Live object の info プロパティをみていきましょう。ここから多くのことが理解できます。

Now we can access Live objects via paths. We can learn a lot about the different Live objects by looking at their info property.

info プロパティを見る
var liveObject = new LiveAPI("live_set");
log(liveObject.info);
id 4
type Song
description This class represents a Live set.
children cue_points CuePoint
children return_tracks Track
children scenes Scene
children tracks Track
children visible_tracks Track
child master_track Track
child view View
property appointed_device Device
property arrangement_overdub bool
property back_to_arranger bool
property can_jump_to_next_cue bool
property can_jump_to_prev_cue bool
property can_redo bool
property can_undo bool
property clip_trigger_quantization int
property current_song_time float
property exclusive_arm bool
property exclusive_solo bool
property groove_amount float
property is_playing bool
property last_event_time float
property loop bool
property loop_length float
property loop_start float
property metronome bool
property midi_recording_quantization int
property nudge_down bool
property nudge_up bool
property overdub bool
property punch_in bool
property punch_out bool
property re_enable_automation_enabled bool
property record_mode bool
property select_on_launch bool
property session_automation_record bool
property session_record bool
property session_record_status int
property signature_denominator int
property signature_numerator int
property song_length float
property swing_amount float
property tempo float
function capture_and_insert_scene
function continue_playing
function create_audio_track
function create_midi_track
function create_return_track
function create_scene
function delete_scene
function delete_track
function duplicate_scene
function duplicate_track
function get_beats_loop_length
function get_beats_loop_start
function get_current_beats_song_time
function get_current_smpte_song_time
function is_cue_point_selected
function jump_by
function jump_to_next_cue
function jump_to_prev_cue
function play_selection
function re_enable_automation
function redo
function scrub_by
function set_or_delete_cue
function start_playing
function stop_all_clips
function stop_playing
function tap_tempo
function trigger_session_record
function undo
done

リストの冒頭には一般的な情報がありますね。id, type, description です。この object の type は Song です。Live Object Model ダイアグラム の Song オブジェクトの四角をクリックすると、Song オブジェクトの説明に移動します。ここで Song オブジェクトのより詳細な情報を得ることができます。

First there's some general information: id, type, and description. Note the type of this object is a Song. If you click the Song box back in the Live Object Model diagram, it will jump to the reference for the Song object, which provides more detailed information.

一般情報の後には、このオブジェクトに所属しているものの長いリストが続きます。これらは 3 つのカテゴリーに分類することができます。つまり children, properties, functions です。ではこれを一つづつみていきましょう。

After the general info, there's a long list of the things in the object. They fall into 3 categories: children, properties, and functions. Let's take a closer look at each.

Live Object Children

いままでみてきたことでわかるように、Live object と Live path は密接な関係にあります。

As we've seen, Live object children and Live paths go hand in hand.

Live object の子要素は Live Object Model リファレンスをみて確認するとか、もしくは LiveAPI object の children もしくは info プロパティを次のように確認することで。log(liveObject.children); log(liveObject.info);

We can determine the children of a Live object by consulting the Live Object Model reference, or by looking at a LiveAPI object's children or info properties: log(liveObject.children); log(liveObject.info);

Live path は Live object の階層を子要素から子要素へと辿って行くことで形成できます。ただし child が "canonicalparent" の場合のように、child が親要素を指す場合もあります。つまり Live object の階層を上がることも下がることもできるのです。例えば "liveset mastertrack canonicalparent" も "live_set" どちらも同じく Song オブジェクトを示しています。

We form a Live path by going from one child to the next in the hierarchy of Live objects. Sometimes a child is actually a parent in the case of "canonicalparent", so we can move up and down the object hierarchy. For example, the paths "liveset mastertrack canonicalparent" and "live_set" will both give you the Song object.

子要素の表示には二つの形式があります。一つは単一の子要素の場合で、もう一つは複数の子要素の場合です。先ほどのセクションで live_set path を与えた Live object の liveObject.info を log した場合にも、このどちらの形式の子要素も表示されています。

Children come in two forms: single child and children list. You'll see both forms in the info property, such as when we logged liveObject.info for the live_set path in the previous section:

子要素には単数の場合と複数の場合がある
children cue_points CuePoint
children return_tracks Track
children scenes Scene
children tracks Track
children visible_tracks Track
child master_track Track
child view View

子要素が複数の場合には、さらに下に path があることが暗示されています。その場合にはどの子要素にアクセスすればいいのか Live に伝える必要があります。子要素のリストの index を与えます。これはプログラミング言語に特有な 0 から始まる index です。ではいくつか例をみてみましょう。

Children lists have additional implications for Live paths. We need to tell Live which child in the list we want to access. This is done by providing an index into the list, counting from 0 as is typical in programming languages. Let's take a look at some examples:

子要素が複数ある場合に配列の index を与えてアクセスする
// the first track:
new LiveAPI("live_set tracks 0");
 
// the second track:
new LiveAPI("live_set tracks 1");
 
// the second clip slot in the third track:
new LiveAPI("live_set tracks 2 clip_slots 1")
 
// the first control surface:
new LiveAPI("control_surfaces 0");

Live Object Model reference diagram をよくみてみると、矢印にいくつかの種類があることがわかります。List タイプの矢印の場合には、子要素が複数あることを示しており、その場合には index を与えることで目的のオブジェクトにアクセスすることができます。また "canonical relations" 矢印もあることに着目してください。この矢印は "canonical_parent" を使って遡る場合のルートを示しています。

Back in the Live Object Model reference diagram, take a closer look at the different types of arrows. Arrows come in single and list types. List type arrows are children lists where we need to provide an index to access a particular object. Also note the "canonical relations" are in bold. These indicate which arrows to follow backwards when going up to a "canonical_parent".

Live Object Properties

Live object のプロパティは、その Live object の状態を保持しています。このプロパティを通して Live の現在の状態を確認したり、もしくはその状態を変更することができます。

Live object properties store the state of each Live object. They allow us to look at the current state of Live and change that state.

liveObject.get() メソッドを使ってオブジェクトのプロパティを詳しくみることができます。

We can examine the object's properties with the liveObject.get() method:

.get メソッドでプロパティを詳しくみる
var liveObject = new LiveAPI("live_set");
log("tempo:", liveObject.get("tempo") );

オブジェクトのプロパティの値を変更するには liveObject.set() メソッドを使用します。

And we can change the object's properties with the liveObject.set() method:

.set() メソッドでプロパティの状態を変更する
var liveObject = new LiveAPI("live_set");
liveObject.set("tempo", 80);

tempo の変更はこのスクリプトを実行した後におこなわれます。また、この変更は undoable = アンドゥーができる変更です。api.set() で行われる変更は基本的に全てアンドゥーできるはずです。

Note the tempo has changed in Live after you run this script. Also note this change is performed as an undoable operation. I believe you can always undo changes made by api.set().

Live Object Functions

プロパティの値を set することで変更するだけではなく、Live object の関数を呼び出して Live の状態を変更したり様々な機能を実行することができます。そのために使うのが liveObject.call() です。次の例はシンプルな liveObject.call() の例です。トランスポートがストップ状態で実行するようにしてください。

Besides setting properties, we can call functions on Live objects in order to make changes to Live and trigger various features. For this we use liveObject.call(). Here's a simple example. Make sure the transport is stopped and try the following script:

liveObject.call() を使う
var liveObject = new LiveAPI("live_set");
liveObject.call("start_playing");

関数は一般的にそうですが、これによって引き起こされる変更はアンドゥー可能であったりそうでなかったりします。この挙動に関しては Live そのものの挙動に依拠しています。トランスポートの再生ボタンをクリックは、アンドゥーできないものなので、Song の start_playing 関数の実行もアンドゥーできません。

Depending on the nature of the function, it's result may or may not be undoable. This should reflect typical Live behavior. In this case, clicking play on the transport is not undoable, so neither is the Song's start_playing function.

Live の GUI をクリックしたりマウスで操作できる機能は LiveAPI の関数からも実行できます。

Many things that can be clicked on and interacted with the mouse in Live's GUI, can also be triggered with functions in the LiveAPI.

この Live object 関数を呼び出すコード例は引数を何も受け取っていないシンプルなものですが、今後引数を受け取る関数を呼び出す方法も紹介していきます。

This is one of the simplest examples of calling a Live object function, because it has no parameters. In future articles we'll see how to call functions that take parameters.

this_device

thisdevice という root object があります。これは Live object Model ダイアグラムにはのっていません。これは特殊な path で、現在実行している JavaScript を保持している Max for Live デバイスを示します。thisdevice path を用いる場合、canonicalparent が特に便利です。なぜなら thisdevice で参照できる現在の Max for Live デバイスから遡って、このデバイスが所属するトラックを参照することができるからです。現在の Max for Live デバイスから相対的に Live object にアクセスすることができます。

You may have noticed the root object thisdevice does not appear on the Live Object Model diagram. It's a special path for the Max for Live Device object that contains our JavaScript code. The canonicalparent is particular useful here, because we can start from our Max for Live device and go upwards to the containing track. From there we can interact with Live objects relative to the current Max for Live device.

this_device を使う
var liveObject = new LiveAPI("this_device");
log("current Max for Live device path:", liveObject.path);
 
liveObject = new LiveAPI("this_device canonical_parent");
log("current Max for Live device's parent:", liveObject.path);
log 結果
current Max for Live device path:  "live_set tracks 0 devices 0"
current Max for Live device's parent:  "live_set tracks 0"

つまり今回は最初のトラックに Max for Live デバイスが存在していることがわかります。

In this case, the Max for Live device was on the first track.

Next Steps

様々な Live object のプロパティと関数を見てきました。この記事で学習した手法を使ってあなたが興味のある Live API を探求してみてください。

There are a lot of different Live objects with various properties and function. Use what we've learned in this article to explore the parts of the Live API that interest you.

次の記事では Clip オブジェクトを取り上げ、MIDI clip 内の note をどのようにして操作をするかということを学んでいきます。MIDI クリップに興味がないとしてもその過程を通じ、この記事で学んだ基礎的な Live API の技術をより実践的な形で使用する良い機会です。引数を受け取る関数を呼び出したり、Live API から受け取った生のデータをパースして、カスタム JavaScript オブジェクトに渡して処理をさせたりしていきます。

In the next article, we'll focus on the Clip object and learn how to manipulate the notes in a MIDI clip. Even if you are not particularly interested in MIDI clips, it's a good opportunity to start applying the basic Live API techniques from this article in a more practical situation. For example, we'll call functions that take parameters, and deal with parsing raw data from the Live API into our own custom JavaScript objects.