or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-types.mdindex.mdmessage-handling.mdservers.mdtcp-networking.mdudp-networking.md

data-types.mddocs/

0

# Data Types and Parsing

1

2

Low-level OSC data type encoding/decoding, NTP timestamp handling, and SLIP protocol implementation. These modules provide the foundation for OSC message formatting and reliable TCP communication.

3

4

## Capabilities

5

6

### OSC Type Conversion

7

8

Functions for converting between Python types and OSC binary format with full support for all standard OSC data types.

9

10

```python { .api }

11

# String handling

12

def write_string(val: str) -> bytes:

13

"""Convert Python string to OSC string format.

14

15

Parameters:

16

- val: Python string to encode

17

18

Returns:

19

OSC-formatted string bytes (UTF-8 encoded, null-terminated, padded to 4-byte boundary)

20

21

Raises:

22

BuildError: If string cannot be encoded

23

"""

24

25

def get_string(dgram: bytes, start_index: int) -> Tuple[str, int]:

26

"""Parse OSC string from datagram.

27

28

Parameters:

29

- dgram: Datagram bytes containing OSC string

30

- start_index: Starting position in datagram

31

32

Returns:

33

Tuple of (parsed_string, next_index)

34

35

Raises:

36

ParseError: If string cannot be parsed

37

"""

38

39

# Integer handling

40

def write_int(val: int) -> bytes:

41

"""Convert Python int to OSC int32 format.

42

43

Parameters:

44

- val: 32-bit integer value

45

46

Returns:

47

4-byte big-endian integer representation

48

49

Raises:

50

BuildError: If value exceeds int32 range

51

"""

52

53

def get_int(dgram: bytes, start_index: int) -> Tuple[int, int]:

54

"""Parse OSC int32 from datagram.

55

56

Parameters:

57

- dgram: Datagram bytes containing OSC int32

58

- start_index: Starting position in datagram

59

60

Returns:

61

Tuple of (parsed_int, next_index)

62

63

Raises:

64

ParseError: If integer cannot be parsed

65

"""

66

67

def write_int64(val: int) -> bytes:

68

"""Convert Python int to OSC int64 format.

69

70

Parameters:

71

- val: 64-bit integer value

72

73

Returns:

74

8-byte big-endian integer representation

75

"""

76

77

def get_int64(dgram: bytes, start_index: int) -> Tuple[int, int]:

78

"""Parse OSC int64 from datagram.

79

80

Parameters:

81

- dgram: Datagram bytes containing OSC int64

82

- start_index: Starting position in datagram

83

84

Returns:

85

Tuple of (parsed_int64, next_index)

86

"""

87

88

def get_uint64(dgram: bytes, start_index: int) -> Tuple[int, int]:

89

"""Parse OSC uint64 from datagram.

90

91

Parameters:

92

- dgram: Datagram bytes containing OSC uint64

93

- start_index: Starting position in datagram

94

95

Returns:

96

Tuple of (parsed_uint64, next_index)

97

"""

98

99

# Float handling

100

def write_float(val: float) -> bytes:

101

"""Convert Python float to OSC float32 format.

102

103

Parameters:

104

- val: Float value for 32-bit representation

105

106

Returns:

107

4-byte IEEE 754 single-precision representation

108

"""

109

110

def get_float(dgram: bytes, start_index: int) -> Tuple[float, int]:

111

"""Parse OSC float32 from datagram.

112

113

Parameters:

114

- dgram: Datagram bytes containing OSC float32

115

- start_index: Starting position in datagram

116

117

Returns:

118

Tuple of (parsed_float, next_index)

119

"""

120

121

def write_double(val: float) -> bytes:

122

"""Convert Python float to OSC double/float64 format.

123

124

Parameters:

125

- val: Float value for 64-bit representation

126

127

Returns:

128

8-byte IEEE 754 double-precision representation

129

"""

130

131

def get_double(dgram: bytes, start_index: int) -> Tuple[float, int]:

132

"""Parse OSC double/float64 from datagram.

133

134

Parameters:

135

- dgram: Datagram bytes containing OSC double

136

- start_index: Starting position in datagram

137

138

Returns:

139

Tuple of (parsed_double, next_index)

140

"""

141

142

# Binary data handling

143

def write_blob(val: bytes) -> bytes:

144

"""Convert Python bytes to OSC blob format.

145

146

Parameters:

147

- val: Binary data to encode

148

149

Returns:

150

OSC blob with 4-byte size prefix and padded data

151

"""

152

153

def get_blob(dgram: bytes, start_index: int) -> Tuple[bytes, int]:

154

"""Parse OSC blob from datagram.

155

156

Parameters:

157

- dgram: Datagram bytes containing OSC blob

158

- start_index: Starting position in datagram

159

160

Returns:

161

Tuple of (parsed_bytes, next_index)

162

"""

163

164

# Special type handling

165

def write_rgba(val: Tuple[int, int, int, int]) -> bytes:

166

"""Convert RGBA tuple to OSC RGBA color format.

167

168

Parameters:

169

- val: (red, green, blue, alpha) tuple with values 0-255

170

171

Returns:

172

4-byte RGBA color representation

173

"""

174

175

def get_rgba(dgram: bytes, start_index: int) -> Tuple[Tuple[int, int, int, int], int]:

176

"""Parse OSC RGBA color from datagram.

177

178

Parameters:

179

- dgram: Datagram bytes containing OSC RGBA

180

- start_index: Starting position in datagram

181

182

Returns:

183

Tuple of ((r, g, b, a), next_index)

184

"""

185

186

def write_midi(val: MidiPacket) -> bytes:

187

"""Convert MIDI packet to OSC MIDI format.

188

189

Parameters:

190

- val: (port_id, status_byte, data1, data2) MIDI packet

191

192

Returns:

193

4-byte OSC MIDI representation

194

"""

195

196

def get_midi(dgram: bytes, start_index: int) -> Tuple[MidiPacket, int]:

197

"""Parse OSC MIDI message from datagram.

198

199

Parameters:

200

- dgram: Datagram bytes containing OSC MIDI

201

- start_index: Starting position in datagram

202

203

Returns:

204

Tuple of (midi_packet, next_index)

205

"""

206

```

207

208

### Timestamp Handling

209

210

OSC timetag conversion functions for precise timing control with NTP timestamp format.

211

212

```python { .api }

213

def write_date(system_time: Union[float, int]) -> bytes:

214

"""Convert system time to OSC timetag format.

215

216

Parameters:

217

- system_time: System time in seconds since epoch, or IMMEDIATELY constant

218

219

Returns:

220

8-byte NTP timestamp for OSC timetag

221

"""

222

223

def get_date(dgram: bytes, start_index: int) -> Tuple[float, int]:

224

"""Parse OSC timetag from datagram as system time.

225

226

Parameters:

227

- dgram: Datagram bytes containing OSC timetag

228

- start_index: Starting position in datagram

229

230

Returns:

231

Tuple of (system_time, next_index)

232

"""

233

234

def get_timetag(dgram: bytes, start_index: int) -> Tuple[datetime.datetime, int]:

235

"""Parse OSC timetag from datagram as datetime object.

236

237

Parameters:

238

- dgram: Datagram bytes containing OSC timetag

239

- start_index: Starting position in datagram

240

241

Returns:

242

Tuple of (datetime_object, next_index)

243

"""

244

245

# Special timing constant

246

IMMEDIATELY: int = 0 # Special value for immediate execution

247

```

248

249

### NTP Timestamp Functions

250

251

Low-level NTP timestamp manipulation for precise timing operations.

252

253

```python { .api }

254

def parse_timestamp(timestamp: int) -> Timestamp:

255

"""Parse NTP timestamp into seconds and fraction components.

256

257

Parameters:

258

- timestamp: 64-bit NTP timestamp

259

260

Returns:

261

Timestamp namedtuple with seconds and fraction fields

262

"""

263

264

def ntp_to_system_time(timestamp: bytes) -> float:

265

"""Convert NTP timestamp bytes to system time.

266

267

Parameters:

268

- timestamp: 8-byte NTP timestamp

269

270

Returns:

271

System time in seconds since Unix epoch

272

273

Raises:

274

NtpError: If timestamp cannot be converted

275

"""

276

277

def system_time_to_ntp(seconds: float) -> bytes:

278

"""Convert system time to NTP timestamp bytes.

279

280

Parameters:

281

- seconds: System time in seconds since Unix epoch

282

283

Returns:

284

8-byte NTP timestamp representation

285

"""

286

287

def ntp_time_to_system_epoch(seconds: float) -> float:

288

"""Convert NTP epoch time to system epoch time.

289

290

Parameters:

291

- seconds: Seconds since NTP epoch (1900-01-01)

292

293

Returns:

294

Seconds since Unix epoch (1970-01-01)

295

"""

296

297

def system_time_to_ntp_epoch(seconds: float) -> float:

298

"""Convert system epoch time to NTP epoch time.

299

300

Parameters:

301

- seconds: Seconds since Unix epoch

302

303

Returns:

304

Seconds since NTP epoch

305

"""

306

307

class Timestamp:

308

"""NTP timestamp representation."""

309

seconds: int # Integer seconds component

310

fraction: int # Fractional seconds component (32-bit)

311

312

# NTP constants

313

IMMEDIATELY: bytes # Special NTP timestamp for immediate execution

314

```

315

316

### SLIP Protocol

317

318

Serial Line Internet Protocol implementation for reliable TCP OSC communication (OSC 1.1).

319

320

```python { .api }

321

def encode(msg: bytes) -> bytes:

322

"""Encode message bytes into SLIP packet format.

323

324

Parameters:

325

- msg: Message bytes to encode

326

327

Returns:

328

SLIP-encoded packet with proper framing and escaping

329

"""

330

331

def decode(packet: bytes) -> bytes:

332

"""Decode SLIP packet to retrieve original message.

333

334

Parameters:

335

- packet: SLIP-encoded packet bytes

336

337

Returns:

338

Original message bytes

339

340

Raises:

341

ProtocolError: If packet contains invalid SLIP sequences

342

"""

343

344

def is_valid(packet: bytes) -> bool:

345

"""Check if packet conforms to SLIP specification.

346

347

Parameters:

348

- packet: Packet bytes to validate

349

350

Returns:

351

True if packet is valid SLIP format

352

"""

353

354

# SLIP protocol constants

355

END: bytes = b"\xc0" # Frame delimiter

356

ESC: bytes = b"\xdb" # Escape character

357

ESC_END: bytes = b"\xdc" # Escaped END

358

ESC_ESC: bytes = b"\xdd" # Escaped ESC

359

END_END: bytes = b"\xc0\xc0" # Double END sequence

360

```

361

362

## Usage Examples

363

364

### Manual Message Construction

365

366

```python

367

from pythonosc.parsing import osc_types

368

369

# Build message datagram manually

370

address_bytes = osc_types.write_string("/synth/freq")

371

type_tag_bytes = osc_types.write_string(",f") # One float argument

372

freq_bytes = osc_types.write_float(440.0)

373

374

# Combine into complete message

375

message_dgram = address_bytes + type_tag_bytes + freq_bytes

376

print(f"Message size: {len(message_dgram)} bytes")

377

```

378

379

### Parsing Custom Data Types

380

381

```python

382

from pythonosc.parsing import osc_types

383

384

def parse_custom_message(dgram):

385

"""Parse a message with known structure."""

386

index = 0

387

388

# Parse address

389

address, index = osc_types.get_string(dgram, index)

390

print(f"Address: {address}")

391

392

# Parse type tag

393

type_tag, index = osc_types.get_string(dgram, index)

394

print(f"Type tag: {type_tag}")

395

396

# Parse arguments based on type tag

397

args = []

398

for arg_type in type_tag[1:]: # Skip comma

399

if arg_type == 'i':

400

value, index = osc_types.get_int(dgram, index)

401

elif arg_type == 'f':

402

value, index = osc_types.get_float(dgram, index)

403

elif arg_type == 's':

404

value, index = osc_types.get_string(dgram, index)

405

elif arg_type == 'b':

406

value, index = osc_types.get_blob(dgram, index)

407

elif arg_type == 'd':

408

value, index = osc_types.get_double(dgram, index)

409

elif arg_type == 'h':

410

value, index = osc_types.get_int64(dgram, index)

411

elif arg_type == 'm':

412

value, index = osc_types.get_midi(dgram, index)

413

elif arg_type == 'r':

414

value, index = osc_types.get_rgba(dgram, index)

415

else:

416

print(f"Unknown type: {arg_type}")

417

continue

418

args.append(value)

419

420

return address, args

421

422

# Test with sample datagram

423

sample_dgram = (osc_types.write_string("/test") +

424

osc_types.write_string(",ifs") +

425

osc_types.write_int(42) +

426

osc_types.write_float(3.14) +

427

osc_types.write_string("hello"))

428

429

address, args = parse_custom_message(sample_dgram)

430

print(f"Parsed: {address} -> {args}")

431

```

432

433

### Precise Timing with NTP

434

435

```python

436

from pythonosc.parsing import ntp, osc_types

437

import time

438

439

# Current time as NTP timestamp

440

current_time = time.time()

441

ntp_timestamp = ntp.system_time_to_ntp(current_time)

442

print(f"NTP timestamp: {ntp_timestamp.hex()}")

443

444

# Schedule for 2 seconds in the future

445

future_time = current_time + 2.0

446

future_ntp = ntp.system_time_to_ntp(future_time)

447

448

# Create timetag for bundle

449

timetag_bytes = osc_types.write_date(future_time)

450

print(f"Future timetag: {timetag_bytes.hex()}")

451

452

# Parse timestamp components

453

timestamp_int = int.from_bytes(future_ntp, 'big')

454

parsed = ntp.parse_timestamp(timestamp_int)

455

print(f"Seconds: {parsed.seconds}, Fraction: {parsed.fraction}")

456

```

457

458

### SLIP Encoding for TCP

459

460

```python

461

from pythonosc import slip

462

from pythonosc.parsing import osc_types

463

464

# Create OSC message

465

message_data = (osc_types.write_string("/tcp/test") +

466

osc_types.write_string(",s") +

467

osc_types.write_string("TCP message"))

468

469

# Encode for TCP transmission (OSC 1.1)

470

slip_packet = slip.encode(message_data)

471

print(f"SLIP packet: {slip_packet.hex()}")

472

473

# Validate packet

474

is_valid = slip.is_valid(slip_packet)

475

print(f"Valid SLIP packet: {is_valid}")

476

477

# Decode received packet

478

try:

479

decoded_message = slip.decode(slip_packet)

480

print(f"Decoded message length: {len(decoded_message)}")

481

print(f"Original matches decoded: {message_data == decoded_message}")

482

except slip.ProtocolError as e:

483

print(f"SLIP decode error: {e}")

484

```

485

486

### Working with MIDI Data

487

488

```python

489

from pythonosc.parsing import osc_types

490

491

# Create MIDI note on message

492

midi_packet = (0, 0x90, 60, 127) # Channel 1, Note On, Middle C, Velocity 127

493

midi_bytes = osc_types.write_midi(midi_packet)

494

495

# Create OSC message with MIDI data

496

message_dgram = (osc_types.write_string("/midi/note") +

497

osc_types.write_string(",m") +

498

midi_bytes)

499

500

# Parse MIDI data back

501

index = len(osc_types.write_string("/midi/note") + osc_types.write_string(",m"))

502

parsed_midi, _ = osc_types.get_midi(message_dgram, index)

503

504

print(f"Original MIDI: {midi_packet}")

505

print(f"Parsed MIDI: {parsed_midi}")

506

print(f"Port: {parsed_midi[0]}, Status: 0x{parsed_midi[1]:02x}, Data1: {parsed_midi[2]}, Data2: {parsed_midi[3]}")

507

```

508

509

### Color Data Handling

510

511

```python

512

from pythonosc.parsing import osc_types

513

514

# Create RGBA color (red with 50% alpha)

515

color = (255, 0, 0, 128)

516

color_bytes = osc_types.write_rgba(color)

517

518

# Create message with color

519

message_dgram = (osc_types.write_string("/light/color") +

520

osc_types.write_string(",r") +

521

color_bytes)

522

523

# Parse color back

524

index = len(osc_types.write_string("/light/color") + osc_types.write_string(",r"))

525

parsed_color, _ = osc_types.get_rgba(message_dgram, index)

526

527

print(f"Original color: RGBA{color}")

528

print(f"Parsed color: RGBA{parsed_color}")

529

```

530

531

### Binary Data (Blobs)

532

533

```python

534

from pythonosc.parsing import osc_types

535

import struct

536

537

# Create binary data (e.g., audio samples)

538

audio_samples = struct.pack('>10f', *[0.1 * i for i in range(10)])

539

blob_bytes = osc_types.write_blob(audio_samples)

540

541

# Create message with blob

542

message_dgram = (osc_types.write_string("/audio/samples") +

543

osc_types.write_string(",b") +

544

blob_bytes)

545

546

# Parse blob back

547

index = len(osc_types.write_string("/audio/samples") + osc_types.write_string(",b"))

548

parsed_blob, _ = osc_types.get_blob(message_dgram, index)

549

550

# Unpack audio samples

551

parsed_samples = struct.unpack('>10f', parsed_blob)

552

print(f"Blob size: {len(parsed_blob)} bytes")

553

print(f"Audio samples: {parsed_samples}")

554

```

555

556

### Performance Optimization

557

558

```python

559

from pythonosc.parsing import osc_types

560

import itertools

561

562

# Pre-compute common type tags for performance

563

common_types = {

564

'f': osc_types.write_string(",f"),

565

'i': osc_types.write_string(",i"),

566

's': osc_types.write_string(",s"),

567

'ff': osc_types.write_string(",ff"),

568

'ifs': osc_types.write_string(",ifs"),

569

}

570

571

def fast_build_message(address, args):

572

"""Optimized message building for known patterns."""

573

address_bytes = osc_types.write_string(address)

574

575

# Determine type pattern

576

type_pattern = ''.join('f' if isinstance(arg, float) else

577

'i' if isinstance(arg, int) else

578

's' if isinstance(arg, str) else 'x'

579

for arg in args)

580

581

# Use pre-computed type tag if available

582

if type_pattern in common_types:

583

type_tag_bytes = common_types[type_pattern]

584

else:

585

type_tag_bytes = osc_types.write_string(',' + type_pattern)

586

587

# Build argument bytes

588

arg_bytes = b''

589

for arg in args:

590

if isinstance(arg, float):

591

arg_bytes += osc_types.write_float(arg)

592

elif isinstance(arg, int):

593

arg_bytes += osc_types.write_int(arg)

594

elif isinstance(arg, str):

595

arg_bytes += osc_types.write_string(arg)

596

597

return address_bytes + type_tag_bytes + arg_bytes

598

599

# Test optimized building

600

fast_msg = fast_build_message("/fast/test", [440.0, 127, "hello"])

601

print(f"Fast message size: {len(fast_msg)} bytes")

602

```

603

604

## Types and Exceptions

605

606

```python { .api }

607

from typing import Tuple, Union

608

from datetime import datetime

609

610

MidiPacket = Tuple[int, int, int, int] # (port_id, status_byte, data1, data2)

611

612

class ParseError(Exception):

613

"""Raised when OSC data parsing fails."""

614

615

class BuildError(Exception):

616

"""Raised when OSC data building fails."""

617

618

class ProtocolError(ValueError):

619

"""Raised when SLIP protocol error occurs."""

620

621

class NtpError(Exception):

622

"""Raised when NTP timestamp conversion fails."""

623

```