« 5月18日:サンプリング&プレイバック | メイン | 6月15日:パフォーマンスについて考える »
複数のコンピュータ間をつなぐネットワークは、メディアアートの表現にとって様々な可能性を秘めた、欠かすことのできない重要な要素である。コンピュータネットワークの技術は、日々進歩しており、それを用いた表現もまた刻々と進化している。コンピュータ音楽の分野でも、通信技術の進歩に伴って、ネットワーク上での音楽表現やコミュニケーションを実現するために様々な研究が行われており、その成果を利用することが可能となってきている。
今週の授業では、コンピュータネットワークを利用して、複数のコンピューター同士で音響合成のためのパラメータを双方向にやり取りする手法の例として、Open Sound Control(OSC)というプロトコル(データをやり取りするための規則)を取り上げる。OSCの基本的な考え方を解説した上で、Max/MSP、Flash、SuperColliderでのインプリメンテーションについて解説する。
Open Sound Control(以降OSCと表記)は、カルフォルニア州立大学バークレー校のCNMAT(Center for New Music and Audio Technorogies)を中心に研究開発が進められている、コンピュータ音楽のための通信プロトコル(規則)である。OSCは現在のコンピュータネットワーク技術に対応して、複数のコンピュータやシンセサイザー、また様々なマルチメディアのデバイス間でコミュニケーションすることを目指している。
OSCホームページ
http://cnmat.cnmat.berkeley.edu/OSC/
これまでコンピュータと電子楽器間を相互接続するプロトコルとして、MIDI(Musical Instrument Digital Interface)が標準的に用いられてきた。しかしながらコンピュータや電子デバイス、ネットワーク技術の進歩につれて、MIDIは時代遅れのものになりつつある。
OSCではこれまでのMIDIの欠点を踏まえた上でプロトコルの設計がなされている。
本日のサンプルは以下からダウンロードしてください。




Flashと他のOSCを使用可能なアプリケーション・言語とを、OSC経由でコミュニケーションするには、floscというJAVAで書かれたサーバーを経由して使います。floscは、flash5以降に標準装備されているXMLSocketを利用してFlashとOSCアプリとのゲートウェイ(異なるコンピューターのネットワークを接続する装置)のような役割を果たします。

/**
* flosc - Flash OpenSound Control
* Ben Chun, 2002
*
* Credits:
* design and structure based on MoockComm by Colin Moock.
* server based on CommServer by Derek Clayton.
* (see TcpServer.java and TcpClient.java for more information.)
*
*/
stop ();
// *** general init
var incomingUpdated = false; // flag to scroll the "incoming" window
incoming = ""; // clear the text field to start out
// attach the scroll manager movie
// (see its child movie's enterframe event for scroll code)
attachMovie("processScroll", "processScroll", 0);
// turn off ugly yellow highlight on buttons
_focusrect = 0;
// 新たなソケットを生成し、サーバ接続を要求
function connect () {
mySocket = new XMLSocket();
mySocket.onConnect = handleConnect;
mySocket.onClose = handleClose;
mySocket.onXML = handleIncoming;
if (!mySocket.connect(IPaddress, port)) gotoAndStop("connectionFailed");
}
//サーバから切り離す
function disconnect () {
mySocket.close();
mySocket.connected = false;
incoming = "";
gotoAndStop(2);
}
// XMLエンコードされたOSCパケットを扱うための関数
function handleIncoming (xmlIn) {
// USEFUL DEBUG - display the raw xml data in the output window
// incoming += xmlIn.toString() +"¥n";
// parse out the packet information
e = xmlIn.firstChild;
if (e != null && e.nodeName == "OSCPACKET") {
packet = new OSCPacket(e.attributes.address, e.attributes.port,
e.attributes.time, xmlIn);
displayPacketHeaders(packet);
parseMessages(xmlIn);
}
// tell the text field manager it's time to scroll
incomingUpdated = true;
lastScrollPos = incoming.scroll;
}
//接続が成功したときに返答するための関数
function handleConnect (succeeded) {
if(succeeded) {
incoming += "Connected to " + IPaddress +
" on port " + port + "¥n";
mySocket.connected = true;
} else {
mySocket.connected = false;
}
gotoAndPlay("connecting");
}
//サーバ接続がとぎれた際に呼ばれる関数
function handleClose () {
incoming += ("The server at " + IPaddress + " has terminated the connection.¥n");
incomingUpdated = true;
mySocket.connected = false;
numClients = 0;
}
//OSCパケット生成
function OSCPacket(address, port, time, xmlData) {
this.address = address;
this.port = port;
this.time = time;
this.xmlData = xmlData;
}
//OSCパケットの情報をテキストフィールドに表持するための関数
function displayPacketHeaders(packet) {
incoming += "** OSC Packet from " + packet.address +
", port " + packet.port +
" for time " + packet.time + "¥n";
}
//XMLエンコードされたOSCパケットからメッセージを取り出すための関数
function parseMessages(node) {
if (node.nodeName == "MESSAGE") {
incoming += "Message name: " + node.attributes.NAME + "¥n";
// loop over the arguments of the message
for (var child = node.firstChild; child != null; child=child.nextSibling) {
if (child.nodeName == "ARGUMENT") {
incoming += "¥tArg type " + child.attributes.TYPE;
incoming += ", value " + child.attributes.VALUE + "¥n";
}
}
}
else { // look recursively for a message node
for (var child = node.firstChild; child != null; child=child.nextSibling) {
parseMessages(child);
}
}
}
//XMLエンコードされたOSCメッセージを生成する
function sendOSC(name, arg, destAddr, destPort) {
xmlOut = new XML();
osc = xmlOut.createElement("OSCPACKET");
osc.attributes.TIME = 0;
osc.attributes.PORT = destPort;
osc.attributes.ADDRESS = destAddr;
message = xmlOut.createElement("MESSAGE");
message.attributes.NAME = name;
argument = xmlOut.createElement("ARGUMENT");
// NOTE : the server expects all strings to be encoded
// with the escape function.
argument.attributes.VALUE = escape(arg);
argument.attributes.TYPE = "s";
// NOTE : to send more than one argument, just create
// more elements and appendChild them to the message.
// the same goes for multiple messages in a packet.
message.appendChild(argument);
osc.appendChild(message);
xmlOut.appendChild(osc);
if (mySocket && mySocket.connected) {
mySocket.send(xmlOut);
incoming += "Sent XML-encoded OSC destined for "
+ destAddr
+ ", port "
+ destPort
+ "¥n";
}
incomingUpdated = true;
}
on (release) {
sendOSC(_root.msgName, _root.msgArg, destAddr, destPort);
}


onClipEvent (load) {
// パラメータの初期化
thrust = 1;
decay = .97;
maxSpeed = 15;
}
onClipEvent (enterFrame) {
// 右回転
if (Key.isDown(Key.RIGHT)) {
_rotation += 10;
}
// 左回転
if (Key.isDown(Key.LEFT)) {
_rotation -= 10;
}
// ↑が押されたら、移動軌道を再計算
if (Key.isDown(Key.UP)) {
//三角関数を利用して、移動スピードと軌道を計算
xSpeed += thrust*Math.sin(_rotation*(Math.PI/180));
ySpeed += thrust*Math.cos(_rotation*(Math.PI/180));
flames._visible = 1;
} else {
// ↑が押されていなければ、惰性で進む
xSpeed *= decay;
ySpeed *= decay;
flames._visible = 0;
}
// 常にプラス方向に推進する
speed = Math.sqrt((xSpeed*xSpeed)+(ySpeed*ySpeed));
// スピードを最大値以内におさめる
if (speed>maxSpeed) {
xSpeed *= maxSpeed/speed;
ySpeed *= maxSpeed/speed;
}
//
// 計算されたX,Yの移動スピードで物体を移動
_y -= ySpeed;
_x += xSpeed;
//
// 画面からはみ出したら、反対側から出現
if (_y<0) {
_y = 400;
}
if (_y>400) {
_y = 0;
}
if (_x<0) {
_x = 400;
}
if (_x>400) {
_x = 0;
}
// ムービーのX,Y座標をもとに、OSCメッセージを送信
_root.sendOSC("/xpos", _x*2+100, _root.destAddr, _root.destPort);
_root.sendOSC("/ypos", _y*2+100, _root.destAddr, _root.destPort);
}
SCのポート57110にOSCメッセージを送信

Max/MSPからOSCメッセージを受け取り、音を鳴らす
(
SynthDef("double-osc", { arg freq1 = 440, freq2 = 440, am = 0.5;
var osc1, osc2;
osc1 = SinOsc.ar(freq1,0,amp);
osc2 = SinOsc.ar(freq2,0,amp);
Out.ar(0, [osc1, osc2]);
}).writeDefFile;
s.sendSynthDef("double-osc");
)
サンプルフォルダ内の、赤松政行氏による、「SuperCollider3におけるネットワーキング」を参照してください。(※フォルダ"dspss2004-aka"内)。
投稿者 Atsushi Tadokoro : May 31, 2004 11:16 PM