Open Sound Control server and client implementations in pure Python for networked music and multimedia applications
—
Core OSC message and bundle construction, parsing, and manipulation capabilities. This module provides the fundamental building blocks for creating and processing OSC messages and bundles with full support for all standard OSC data types.
Build OSC messages with typed arguments using the builder pattern or convenience functions.
class OscMessageBuilder:
"""Builds arbitrary OSC messages with typed arguments."""
def __init__(self, address: Optional[str] = None):
"""Initialize builder with optional OSC address pattern."""
@property
def address(self) -> Optional[str]:
"""Get or set the OSC address pattern."""
@address.setter
def address(self, value: str):
"""Set the OSC address pattern."""
@property
def args(self) -> List[Tuple[str, Any]]:
"""Returns list of (type, value) argument tuples."""
def add_arg(self, arg_value: ArgValue, arg_type: Optional[str] = None):
"""Add typed argument to message.
Parameters:
- arg_value: The argument value (auto-typed if arg_type is None)
- arg_type: Explicit OSC type character ('i', 'f', 's', 'b', etc.)
"""
def build(self) -> OscMessage:
"""Build OscMessage from current state.
Returns:
OscMessage instance ready for transmission
Raises:
BuildError: If message cannot be built (missing address, etc.)
"""
def build_msg(address: str, value: ArgValue = "") -> OscMessage:
"""Convenience function to quickly build OSC message.
Parameters:
- address: OSC address pattern string
- value: Single argument value or list of values
Returns:
OscMessage ready for transmission
"""Parse OSC messages from incoming datagram bytes with automatic type detection.
class OscMessage:
"""Representation of a parsed OSC message."""
def __init__(self, dgram: bytes):
"""Initialize from datagram bytes.
Parameters:
- dgram: Raw OSC message datagram
Raises:
ParseError: If datagram cannot be parsed as OSC message
"""
@property
def address(self) -> str:
"""Returns OSC address pattern string."""
@property
def params(self) -> List[Any]:
"""Returns list of message parameters."""
@property
def size(self) -> int:
"""Returns datagram length in bytes."""
@property
def dgram(self) -> bytes:
"""Returns original datagram bytes."""
@staticmethod
def dgram_is_message(dgram: bytes) -> bool:
"""Check if datagram is an OSC message.
Parameters:
- dgram: Datagram bytes to check
Returns:
True if dgram contains OSC message
"""
def __iter__(self):
"""Iterator over message parameters."""
def __str__(self) -> str:
"""String representation of message."""Create OSC bundles that group messages and sub-bundles with shared timestamps for synchronized execution.
class OscBundleBuilder:
"""Builds OSC bundles with timed message groups."""
def __init__(self, timestamp: Union[int, float]):
"""Initialize bundle with timestamp.
Parameters:
- timestamp: Execution time (seconds since epoch) or IMMEDIATELY constant
"""
def add_content(self, content: Union[OscMessage, OscBundle]):
"""Add message or sub-bundle to bundle.
Parameters:
- content: OscMessage or OscBundle to include
"""
def build(self) -> OscBundle:
"""Build OscBundle from current state.
Returns:
OscBundle ready for transmission
Raises:
BuildError: If bundle cannot be built
"""
# Bundle timing constant
IMMEDIATELY: int # Special timestamp value for immediate executionParse OSC bundles from datagram bytes and access contained messages and sub-bundles.
class OscBundle:
"""Representation of a parsed OSC bundle."""
def __init__(self, dgram: bytes):
"""Initialize from datagram bytes.
Parameters:
- dgram: Raw OSC bundle datagram
Raises:
ParseError: If datagram cannot be parsed as OSC bundle
"""
@property
def timestamp(self) -> float:
"""Returns bundle timestamp as seconds since epoch."""
@property
def num_contents(self) -> int:
"""Returns number of contained elements."""
@property
def size(self) -> int:
"""Returns datagram length in bytes."""
@property
def dgram(self) -> bytes:
"""Returns original datagram bytes."""
def content(self, index: int) -> Union[OscMessage, OscBundle]:
"""Returns bundle content at given index.
Parameters:
- index: Zero-based index of content element
Returns:
OscMessage or OscBundle at the specified index
"""
@staticmethod
def dgram_is_bundle(dgram: bytes) -> bool:
"""Check if datagram is an OSC bundle.
Parameters:
- dgram: Datagram bytes to check
Returns:
True if dgram contains OSC bundle
"""
def __iter__(self):
"""Iterator over bundle contents."""Handle complete OSC transmissions that may contain messages or bundles with timing information.
class OscPacket:
"""Represents complete OSC transmission unit."""
def __init__(self, dgram: bytes):
"""Initialize from UDP datagram bytes.
Parameters:
- dgram: Complete OSC packet datagram
Raises:
ParseError: If packet cannot be parsed
"""
@property
def messages(self) -> List[TimedMessage]:
"""Returns time-sorted list of messages for execution.
Returns:
List of TimedMessage objects with execution timestamps
"""
class TimedMessage:
"""Message with execution timestamp."""
time: float # Execution timestamp (seconds since epoch)
message: OscMessage # The actual OSC messageType constants for explicit argument typing in message builders.
# Numeric types
ARG_TYPE_INT: str = "i" # 32-bit integer
ARG_TYPE_INT64: str = "h" # 64-bit integer
ARG_TYPE_FLOAT: str = "f" # 32-bit float
ARG_TYPE_DOUBLE: str = "d" # 64-bit double
# Data types
ARG_TYPE_STRING: str = "s" # UTF-8 string
ARG_TYPE_BLOB: str = "b" # Binary data
# Special types
ARG_TYPE_RGBA: str = "r" # RGBA color (32-bit)
ARG_TYPE_MIDI: str = "m" # MIDI packet (4 bytes)
# Boolean/null types
ARG_TYPE_TRUE: str = "T" # Boolean true
ARG_TYPE_FALSE: str = "F" # Boolean false
ARG_TYPE_NIL: str = "N" # Null/nil value
# Array delimiters
ARG_TYPE_ARRAY_START: str = "[" # Array start marker
ARG_TYPE_ARRAY_STOP: str = "]" # Array end markerfrom pythonosc.osc_message_builder import OscMessageBuilder, ARG_TYPE_FLOAT, ARG_TYPE_STRING
# Build message with explicit types
builder = OscMessageBuilder("/synth/params")
builder.add_arg(440.0, ARG_TYPE_FLOAT)
builder.add_arg("sine", ARG_TYPE_STRING)
builder.add_arg([1, 2, 3]) # Auto-typed as array
message = builder.build()
# Quick message building
from pythonosc.osc_message_builder import build_msg
msg = build_msg("/filter", 0.75)from pythonosc.osc_bundle_builder import OscBundleBuilder, IMMEDIATELY
import time
# Create bundle for immediate execution
bundle_builder = OscBundleBuilder(IMMEDIATELY)
bundle_builder.add_content(build_msg("/note/on", [60, 127]))
bundle_builder.add_content(build_msg("/note/on", [64, 127]))
bundle_builder.add_content(build_msg("/note/on", [67, 127]))
chord_bundle = bundle_builder.build()
# Create bundle for future execution
future_time = time.time() + 2.0 # 2 seconds from now
delayed_builder = OscBundleBuilder(future_time)
delayed_builder.add_content(build_msg("/note/off", [60, 64, 67]))
delayed_bundle = delayed_builder.build()from pythonosc.osc_message import OscMessage
from pythonosc.osc_bundle import OscBundle
from pythonosc.osc_packet import OscPacket
# Parse raw datagram
def handle_datagram(dgram_bytes):
if OscMessage.dgram_is_message(dgram_bytes):
message = OscMessage(dgram_bytes)
print(f"Message: {message.address} {message.params}")
elif OscBundle.dgram_is_bundle(dgram_bytes):
bundle = OscBundle(dgram_bytes)
print(f"Bundle with {bundle.num_contents} items at {bundle.timestamp}")
for i in range(bundle.num_contents):
content = bundle.content(i)
if isinstance(content, OscMessage):
print(f" Message: {content.address} {content.params}")
# Or use packet for unified handling
packet = OscPacket(dgram_bytes)
for timed_msg in packet.messages:
print(f"Execute at {timed_msg.time}: {timed_msg.message}")from typing import Union, List, Tuple, Any, Optional
ArgValue = Union[str, bytes, bool, int, float, MidiPacket, list]
MidiPacket = Tuple[int, int, int, int] # (port_id, status_byte, data1, data2)
class ParseError(Exception):
"""Raised when datagram parsing fails."""
class BuildError(Exception):
"""Raised when message/bundle building fails."""Install with Tessl CLI
npx tessl i tessl/pypi-python-osc