Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Schampijer <simon@schampijer.de>2007-06-05 12:00:02 (GMT)
committer Simon Schampijer <simon@schampijer.de>2007-06-05 12:00:02 (GMT)
commiteb66dce18886cadf4cf4c7806f05ef4c703cd4db (patch)
treeee4a1940496c14d5741929b6e7a48f3b53149d04
parent25706e1dc449b353710dafd5ef387f0edbb60e22 (diff)
- changes in the osc api:
- osc messages are typesafe now (path and types of argumens are verified) - some method/class name changes - added documentation - adopted the game server to use the new osc api
-rwxr-xr-xmemosono.py77
-rwxr-xr-xosc/oscapi.py163
-rwxr-xr-xosc/osccore.py405
3 files changed, 611 insertions, 34 deletions
diff --git a/memosono.py b/memosono.py
index 367c7dd..c91c4ce 100755
--- a/memosono.py
+++ b/memosono.py
@@ -22,8 +22,6 @@ import gobject
import gtk, pygtk
import os
import socket
-from osc.oscAPI import *
-from osc.OSC import *
import logging
import random
import copy
@@ -31,19 +29,24 @@ import time
import errno
import gc
-
from sugar.activity import activity
+from osc.oscapi import OscApi
class Server:
def __init__(self, _MEMO, port):
- self.oscapi = OscApi()
- self.port = 0
- self.oscrecv, self.port = self.oscapi.createListener('127.0.0.1', port)
+
+ isserver = 0
+ while isserver == 0:
+ self.port = port
+ self.oscapi = OscApi(self.port)
+ isserver = self.oscapi.isserver
+ port+=1
+
logging.debug(" Memosono-Server has port "+str(self.port) )
- gobject.io_add_watch(self.oscrecv, gobject.IO_IN, self._handle_query)
- self.oscapi.bind(self._tile, '/MEMO/tile')
- self.oscapi.bind(self._connect, '/MEMO/connect')
+ gobject.io_add_watch(self.oscapi.iosock, gobject.IO_IN, self._handle_query)
+ self.oscapi.addmethod('/MEMO/tile', 'is', self._tile)
+ self.oscapi.addmethod('/MEMO/connect', 'i', self._connect)
self.compkey = ''
self.key = ''
self.tile = 0
@@ -61,7 +64,7 @@ class Server:
def _handle_query(self, source, condition):
data, address = source.recvfrom(1024)
- self.oscapi.recvhandler(data, address)
+ self.oscapi.handlemsg(data, address)
return True
# OSC-METHODS:
@@ -77,12 +80,12 @@ class Server:
for i in self.addresses:
if msg[1][0] == self.addresses[i][0]:
if msg[1][1] != self.addresses[i][1]:
- self.oscapi.sendMsg("/MEMO/tile", [self.tile, self.key],
- self.addresses[i][0], self.addresses[i][1])
+ self.oscapi.send((self.addresses[i][0], self.addresses[i][1]), "/MEMO/tile",
+ [self.tile, self.key])
else:
## logging.debug(" Send the stuff ")
- self.oscapi.sendMsg("/MEMO/tile", [self.tile, self.key],
- self.addresses[i][0], self.addresses[i][1])
+ self.oscapi.send((self.addresses[i][0], self.addresses[i][1]), "/MEMO/tile",
+ [self.tile, self.key])
# match
if self.compkey != '':
if self.compkey == self.key:
@@ -100,14 +103,14 @@ class Server:
self.currentplayer+=1
i = 0
for i in self.addresses:
- self.oscapi.sendMsg("/MEMO/game/next",[self.players[self.currentplayer],
- self.players[self.lastplayer]],
- self.addresses[i][0], self.addresses[i][1])
+ self.oscapi.send((self.addresses[i][0], self.addresses[i][1]),"/MEMO/game/next",
+ [self.players[self.currentplayer],
+ self.players[self.lastplayer]])
i = 0
for i in self.addresses:
- self.oscapi.sendMsg("/MEMO/game/match", [self.match, self.players[self.lastplayer],
- self.comtile, self.tile, self.count/self.numpairs],
- self.addresses[i][0], self.addresses[i][1])
+ self.oscapi.send((self.addresses[i][0], self.addresses[i][1]), "/MEMO/game/match",
+ [self.match, self.players[self.lastplayer],
+ self.comtile, self.tile, self.count/self.numpairs])
self.compkey = ''
self.comptile = 0
else:
@@ -144,20 +147,25 @@ class Controler(gobject.GObject):
gobject.GObject.__init__(self)
self._MEMO = _MEMO
self.sound = 0
- # OSC-communication
- self.oscapi = OscApi()
- self.port = 0
self.replyaddr = (('127.0.0.1', port))
- self.serveraddr = (('127.0.0.1', port))
- self.oscrecv, self.port = self.oscapi.createListener('127.0.0.1', port+1)
+ self.serveraddr = (('127.0.0.1', port))
+ port+=1
+ # OSC-communication
+ isserver = 0
+ while isserver == 0:
+ self.port = port
+ self.oscapi = OscApi(self.port)
+ isserver = self.oscapi.isserver
+ port+=1
+
logging.debug(" Memosono-Client has port "+str(self.port) )
- self.oscapi.sendMsg("/MEMO/connect", [self.port], self.serveraddr[0], self.serveraddr[1])
- gobject.io_add_watch(self.oscrecv, gobject.IO_IN, self._handle_query)
- self.oscapi.bind(self._addplayer, '/MEMO/addplayer')
- self.oscapi.bind(self._game_init, '/MEMO/init')
- self.oscapi.bind(self._tile, '/MEMO/tile')
- self.oscapi.bind(self._game_match, '/MEMO/game/match')
- self.oscapi.bind(self._game_next, '/MEMO/game/next')
+ self.oscapi.send((self.serveraddr[0], self.serveraddr[1]), "/MEMO/connect", [self.port])
+ gobject.io_add_watch(self.oscapi.iosock, gobject.IO_IN, self._handle_query)
+ self.oscapi.addmethod('/MEMO/addplayer', '', self._addplayer)
+ self.oscapi.addmethod('/MEMO/init', 'sis', self._game_init)
+ self.oscapi.addmethod('/MEMO/tile', 'i', self._tile)
+ self.oscapi.addmethod('/MEMO/game/match', 'isiii', self._game_match)
+ self.oscapi.addmethod('/MEMO/game/next', 'ss', self._game_next)
self.block = 0
self.count = 0
@@ -192,7 +200,7 @@ class Controler(gobject.GObject):
def _handle_query(self, source, condition):
data, self.replyaddr = source.recvfrom(1024)
- self.oscapi.recvhandler(data, self.replyaddr)
+ self.oscapi.handlemsg(data, self.replyaddr)
return True
# SLOTS:
@@ -228,7 +236,7 @@ class Controler(gobject.GObject):
if requesttype == 0:
logging.debug(" send to server ")
- self.oscapi.sendMsg("/MEMO/tile", [tile_number, pic], self.serveraddr[0], self.serveraddr[1])
+ self.oscapi.send((self.serveraddr[0], self.serveraddr[1]), "/MEMO/tile", [tile_number, pic])
if self.count == 2:
self.block = 1
self.count = 0
@@ -546,6 +554,7 @@ class MemosonoActivity(activity.Activity):
self.gamename = 'drumgit'
self.set_title("Memosono - "+self.gamename)
+ logging.debug(" ---------------------Memosono start-----------------------. ")
# set path
_MEMO = {}
diff --git a/osc/oscapi.py b/osc/oscapi.py
new file mode 100755
index 0000000..bc26ddb
--- /dev/null
+++ b/osc/oscapi.py
@@ -0,0 +1,163 @@
+""" This file is based on simpleOSC 0.2 by Daniel Holth.
+This file has been modified by Simon Schampijer.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+
+import socket
+import errno
+import logging
+import sys
+
+import osccore
+
+
+class OscApi(object):
+ def __init__(self, port=None, host=None):
+ """create the send/receive socket and the callback manager
+ bind the socket to a name (host and port)
+
+ Keyword arguments:
+ port -- the port
+ if no port is specified the socket will not be bound to an address and
+ will only be used for sending
+ host -- the host address, can be a dotted decimal address (e.g. '192.168.0.100')
+ or a hostname (e.g. 'machine', 'localhost')
+ if you do not specify a hostname the socket will listen on all the
+ devices for incoming messages
+ """
+
+ self.manager = 0
+ self.iosock = 0
+ self.iosock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.manager = osccore.CallbackManager()
+
+ self.isserver = 0
+
+ if port is not None:
+ if host is None:
+ host = ""
+ try:
+ self.iosock.bind((host, port))
+ self.isserver = 1
+ except socket.error:
+ if errno.EADDRINUSE:
+ logging.error("Port " +str(port)+ " in use." +
+ " Maybe another oscapi is still or already running?" +
+ " oscapi can only be used for sending.")
+ self.iosock.close()
+ self.iosock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.iosock.setblocking(0)
+
+
+ def addmethod(self, path, types, func):
+ '''adds an osc method to the listener
+
+ Keyword arguments:
+ func -- the callback function which gets called when the appropriate osc message is received
+ path -- the path (identifier) of the osc message
+
+ '''
+ self.manager.add(func, path, types)
+
+
+ def handlemsg(self, data, addr):
+ '''give the information read from the socket to the osc message dispatcher
+
+ Keyword arguments:
+ data -- the data read from the socket
+ addr -- address the message is received from
+ '''
+ self.manager.handle(data, addr)
+
+
+ def _create_binarymsg(self, path, data):
+ """create an OSC message in binary format
+
+ Keyword arguments:
+ path -- the path (identifier) of the osc message
+ data -- the data of the message
+
+ return: the osc message in binary format
+ """
+ m = osccore.OSCMessage()
+ m.setAddress(path)
+
+ if len(data) != 0:
+ for x in data:
+ m.append(x)
+
+ return m.getBinary()
+
+
+ def send(self, to, path, data):
+ """send an osc message to the address specified
+
+ Keyword arguments:
+ to -- address of receiver in the form (ipaddr, bundle)
+ path -- the path of the osc message
+ data -- the data of the message
+ """
+ ### resolve host? [a,b,host] = socket.gethostbyaddr(to[0])
+ msg = self._create_binarymsg(path, data)
+ try:
+ self.iosock.sendto(msg, (to[0],to[1]))
+ except socket.error:
+ cla, exc, trbk = sys.exc_info()
+ try:
+ excArgs = exc.__dict__["args"]
+ except KeyError:
+ excArgs = "<no args>"
+ logging.error('error '+str(excArgs))
+
+ def createbundle(self):
+ """create the header of a bundle of OSC messages"""
+ b = osccore.OSCMessage()
+ b.setAddress("")
+ b.append("#bundle")
+ b.append(0)
+ b.append(0)
+ return b
+
+ def appendbundle(self, bundle, path, data):
+ """append osc message to the bundle
+
+ Keyword arguments:
+ bundle -- the bundle to which you want to append a message
+ path -- the path of the OSC message
+ data -- the data of the message
+ """
+ msg = self._create_binarymsg(path, data)
+ bundle.append(msg, 'b')
+
+
+ def sendbundle(self, to, bundle):
+ """send bundle to the address specified
+
+ Keyword arguments:
+ to -- address of receiver in the form (ipaddr, bundle)
+ bundle -- the bundle to send
+ """
+ try:
+ self.iosock.sendto(bundle.message, to)
+ except socket.error:
+ cla, exc, trbk = sys.exc_info()
+ try:
+ excArgs = exc.__dict__["args"]
+ except KeyError:
+ excArgs = "<no args>"
+ logging.error('error '+str(excArgs))
+
diff --git a/osc/osccore.py b/osc/osccore.py
new file mode 100755
index 0000000..4cab0b5
--- /dev/null
+++ b/osc/osccore.py
@@ -0,0 +1,405 @@
+#!/usr/bin/python
+#
+# Open SoundControl for Python
+# Copyright (C) 2002 Daniel Holth, Clinton McChesney
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# For questions regarding this module contact
+# Daniel Holth <dholth@stetson.edu> or visit
+# http://www.stetson.edu/~ProctoLogic/
+#
+# Changelog:
+# 15 Nov. 2001:
+# Removed dependency on Python 2.0 features.
+# - dwh
+# 13 Feb. 2002:
+# Added a generic callback handler.
+# - dwh
+# 29 Mai 2007:
+# Added type checking of messanges.
+# - erikos
+
+import socket
+import struct
+import math
+import sys
+import string
+import pprint
+
+
+def hexDump(bytes):
+ """Useful utility; prints the string in hexadecimal"""
+ for i in range(len(bytes)):
+ sys.stdout.write("%2x " % (ord(bytes[i])))
+ if (i+1) % 8 == 0:
+ print repr(bytes[i-7:i+1])
+
+ if(len(bytes) % 8 != 0):
+ print string.rjust("", 11), repr(bytes[i-len(bytes)%8:i+1])
+
+
+class OSCMessage:
+ """Builds typetagged OSC messages."""
+ def __init__(self):
+ self.address = ""
+ self.typetags = ","
+ self.message = ""
+
+ def setAddress(self, address):
+ self.address = address
+
+ def setMessage(self, message):
+ self.message = message
+
+ def setTypetags(self, typetags):
+ self.typetags = typetags
+
+ def clear(self):
+ self.address = ""
+ self.clearData()
+
+ def clearData(self):
+ self.typetags = ","
+ self.message = ""
+
+ def append(self, argument, typehint = None):
+ """Appends data to the message,
+ updating the typetags based on
+ the argument's type.
+ If the argument is a blob (counted string)
+ pass in 'b' as typehint."""
+
+ if typehint == 'b':
+ binary = OSCBlob(argument)
+ else:
+ binary = OSCArgument(argument)
+
+ self.typetags = self.typetags + binary[0]
+ self.rawAppend(binary[1])
+
+ def rawAppend(self, data):
+ """Appends raw data to the message. Use append()."""
+ self.message = self.message + data
+
+ def getBinary(self):
+ """Returns the binary message (so far) with typetags."""
+ address = OSCArgument(self.address)[1]
+ typetags = OSCArgument(self.typetags)[1]
+ return address + typetags + self.message
+
+ def __repr__(self):
+ return self.getBinary()
+
+def readString(data):
+ length = string.find(data,"\0")
+ nextData = int(math.ceil((length+1) / 4.0) * 4)
+ return (data[0:length], data[nextData:])
+
+
+def readBlob(data):
+ length = struct.unpack(">i", data[0:4])[0]
+ nextData = int(math.ceil((length) / 4.0) * 4) + 4
+ return (data[4:length+4], data[nextData:])
+
+
+def readInt(data):
+ if(len(data)<4):
+ print "Error: too few bytes for int", data, len(data)
+ rest = data
+ integer = 0
+ else:
+ integer = struct.unpack(">i", data[0:4])[0]
+ rest = data[4:]
+
+ return (integer, rest)
+
+
+
+def readLong(data):
+ """Tries to interpret the next 8 bytes of the data
+ as a 64-bit signed integer."""
+ high, low = struct.unpack(">ll", data[0:8])
+ big = (long(high) << 32) + low
+ rest = data[8:]
+ return (big, rest)
+
+
+
+def readFloat(data):
+ if(len(data)<4):
+ print "Error: too few bytes for float", data, len(data)
+ rest = data
+ float = 0
+ else:
+ float = struct.unpack(">f", data[0:4])[0]
+ rest = data[4:]
+
+ return (float, rest)
+
+
+def OSCBlob(next):
+ """Convert a string into an OSC Blob,
+ returning a (typetag, data) tuple."""
+
+ if type(next) == type(""):
+ length = len(next)
+ padded = math.ceil((len(next)) / 4.0) * 4
+ binary = struct.pack(">i%ds" % (padded), length, next)
+ tag = 'b'
+ else:
+ tag = ''
+ binary = ''
+
+ return (tag, binary)
+
+
+def OSCArgument(next):
+ """Convert some Python types to their
+ OSC binary representations, returning a
+ (typetag, data) tuple."""
+
+ if type(next) == type(""):
+ OSCstringLength = math.ceil((len(next)+1) / 4.0) * 4
+ binary = struct.pack(">%ds" % (OSCstringLength), next)
+ tag = "s"
+ elif type(next) == type(42.5):
+ binary = struct.pack(">f", next)
+ tag = "f"
+ elif type(next) == type(13):
+ binary = struct.pack(">i", next)
+ tag = "i"
+ else:
+ binary = ""
+ tag = ""
+
+ return (tag, binary)
+
+
+def parseArgs(args):
+ """Given a list of strings, produces a list
+ where those strings have been parsed (where
+ possible) as floats or integers."""
+ parsed = []
+ for arg in args:
+ print arg
+ arg = arg.strip()
+ interpretation = None
+ try:
+ interpretation = float(arg)
+ if string.find(arg, ".") == -1:
+ interpretation = int(interpretation)
+ except:
+ # Oh - it was a string.
+ interpretation = arg
+ pass
+ parsed.append(interpretation)
+ return parsed
+
+
+
+def decodeOSC(data):
+ """Converts a typetagged OSC message to a Python list."""
+ table = {"i":readInt, "f":readFloat, "s":readString, "b":readBlob}
+ decoded = []
+ address, rest = readString(data)
+ typetags = ""
+
+ if address == "#bundle":
+ time, rest = readLong(rest)
+# decoded.append(address)
+# decoded.append(time)
+ while len(rest)>0:
+ length, rest = readInt(rest)
+ decoded.append(decodeOSC(rest[:length]))
+ rest = rest[length:]
+
+ elif len(rest) > 0:
+ typetags, rest = readString(rest)
+ decoded.append(address)
+ decoded.append(typetags)
+ if typetags[0] == ",":
+ for tag in typetags[1:]:
+ value, rest = table[tag](rest)
+ decoded.append(value)
+ else:
+ print "Oops, typetag lacks the magic ,"
+
+ return decoded
+
+
+class CallbackManager:
+ """This utility class maps OSC addresses to callables.
+
+ The CallbackManager calls its callbacks with a list
+ of decoded OSC arguments, including the address and
+ the typetags as the first two arguments."""
+
+ def __init__(self):
+ self.callbacks = {}
+ self.add(self.unbundler, "#bundle", '')
+
+ def handle(self, data, source = None):
+ """Given OSC data, tries to call the callback with the
+ right address."""
+ decoded = decodeOSC(data)
+ self.dispatch(decoded, source)
+
+ def dispatch(self, message, source = None):
+ """Sends decoded OSC data to an appropriate callback"""
+ try:
+ if type(message[0]) == str :
+ # got a single message
+ address = message[0]
+
+ if address in self.callbacks:
+ types = message[1].split(',')[1]
+ if(types == self.callbacks[address][1]):
+ self.callbacks[address][0](message, source)
+ else:
+ print '[error] path %s exist on osc server but...'%address
+ print ' the types received [%s] do not match with [%s].'%(types,self.callbacks[address][1])
+ print ' received message: %s'%(message)
+ else:
+ print '[error] path %s does not exist on osc server.'%address
+
+ elif type(message[0]) == list :
+ # smells like nested messages
+ for msg in message :
+ self.dispatch(msg, source)
+
+ except KeyError, e:
+ # address not found
+ print 'address %s not found ' % address
+ pprint.pprint(message)
+ except IndexError, e:
+ print 'got malformed OSC message'
+ pass
+ except None, e:
+ print "Exception in", address, "callback :", e
+
+ return
+
+ def add(self, callback, name, types):
+ """Adds a callback to our set of callbacks,
+ or removes the callback with name if callback
+ is None."""
+ if callback == None:
+ del self.callbacks[name]
+ else:
+ self.callbacks[name] = [callback, types]
+
+ def unbundler(self, messages):
+ """Dispatch the messages in a decoded bundle."""
+ # first two elements are #bundle and the time tag, rest are messages.
+ for message in messages[2:]:
+ self.dispatch(message)
+
+
+if __name__ == "__main__":
+ hexDump("Welcome to the OSC testing program.")
+ print
+ message = OSCMessage()
+ message.setAddress("/foo/play")
+ message.append(44)
+ message.append(11)
+ message.append(4.5)
+ message.append("the white cliffs of dover")
+ hexDump(message.getBinary())
+
+ print "Making and unmaking a message.."
+
+ strings = OSCMessage()
+ strings.append("Mary had a little lamb")
+ strings.append("its fleece was white as snow")
+ strings.append("and everywhere that Mary went,")
+ strings.append("the lamb was sure to go.")
+ strings.append(14.5)
+ strings.append(14.5)
+ strings.append(-400)
+
+ raw = strings.getBinary()
+
+ hexDump(raw)
+
+ print "Retrieving arguments..."
+ data = raw
+ for i in range(6):
+ text, data = readString(data)
+ print text
+
+ number, data = readFloat(data)
+ print number
+
+ number, data = readFloat(data)
+ print number
+
+ number, data = readInt(data)
+ print number
+
+ hexDump(raw)
+ print decodeOSC(raw)
+ print decodeOSC(message.getBinary())
+
+ print "Testing Blob types."
+
+ blob = OSCMessage()
+ blob.append("","b")
+ blob.append("b","b")
+ blob.append("bl","b")
+ blob.append("blo","b")
+ blob.append("blob","b")
+ blob.append("blobs","b")
+ blob.append(42)
+
+ hexDump(blob.getBinary())
+
+ print decodeOSC(blob.getBinary())
+
+ def printingCallback(*stuff):
+ sys.stdout.write("Got: ")
+ for i in stuff:
+ sys.stdout.write(str(i) + " ")
+ sys.stdout.write("\n")
+
+ print "Testing the callback manager."
+
+ c = CallbackManager()
+ c.add(printingCallback, "/print")
+
+ c.handle(message.getBinary())
+ message.setAddress("/print")
+ c.handle(message.getBinary())
+
+ print1 = OSCMessage()
+ print1.setAddress("/print")
+ print1.append("Hey man, that's cool.")
+ print1.append(42)
+ print1.append(3.1415926)
+
+ c.handle(print1.getBinary())
+
+ bundle = OSCMessage()
+ bundle.setAddress("")
+ bundle.append("#bundle")
+ bundle.append(0)
+ bundle.append(0)
+ bundle.append(print1.getBinary(), 'b')
+ bundle.append(print1.getBinary(), 'b')
+
+ bundlebinary = bundle.message
+
+ print "sending a bundle to the callback manager"
+ c.handle(bundlebinary)