or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

audio-system.mdbackend-system.mdconfiguration.mdcore-controllers.mdextension-system.mdindex.mdmodels.md

audio-system.mddocs/

0

# Audio System

1

2

Mopidy's audio system provides a GStreamer-based audio pipeline with playback control, volume management, and support for various audio formats and output methods. The system handles audio streaming, format conversion, and hardware integration through a flexible architecture.

3

4

## Capabilities

5

6

### Audio Actor

7

8

The main audio system actor that manages the GStreamer pipeline and audio playback operations.

9

10

```python { .api }

11

class Audio(pykka.ThreadingActor):

12

"""

13

Main audio actor managing GStreamer-based audio pipeline.

14

15

Parameters:

16

- config: Audio configuration

17

- mixer: Audio mixer instance

18

"""

19

def __init__(self, config, mixer): ...

20

21

def set_uri(self, uri):

22

"""

23

Set URI for audio playback.

24

25

Parameters:

26

- uri (str): Audio URI to play

27

28

Returns:

29

- bool: True if URI was set successfully

30

"""

31

...

32

33

def get_uri(self):

34

"""

35

Get current audio URI.

36

37

Returns:

38

- str or None: Current audio URI

39

"""

40

...

41

42

def start_playback(self):

43

"""

44

Start audio playback.

45

46

Returns:

47

- bool: True if playback started successfully

48

"""

49

...

50

51

def pause_playback(self):

52

"""

53

Pause audio playback.

54

55

Returns:

56

- bool: True if playback was paused successfully

57

"""

58

...

59

60

def prepare_change(self):

61

"""

62

Prepare for URI change by pausing.

63

64

Returns:

65

- bool: True if prepared successfully

66

"""

67

...

68

69

def stop_playback(self):

70

"""

71

Stop audio playback and reset pipeline.

72

73

Returns:

74

- bool: True if playback was stopped successfully

75

"""

76

...

77

78

def get_position(self):

79

"""

80

Get current playback position.

81

82

Returns:

83

- int: Position in milliseconds

84

"""

85

...

86

87

def set_position(self, position):

88

"""

89

Seek to specific position.

90

91

Parameters:

92

- position (int): Position in milliseconds

93

94

Returns:

95

- bool: True if seek was successful

96

"""

97

...

98

99

def get_volume(self):

100

"""

101

Get current volume level.

102

103

Returns:

104

- int or None: Volume level (0-100)

105

"""

106

...

107

108

def set_volume(self, volume):

109

"""

110

Set volume level.

111

112

Parameters:

113

- volume (int): Volume level (0-100)

114

115

Returns:

116

- bool: True if volume was set successfully

117

"""

118

...

119

120

def get_mute(self):

121

"""

122

Get mute state.

123

124

Returns:

125

- bool or None: True if muted

126

"""

127

...

128

129

def set_mute(self, mute):

130

"""

131

Set mute state.

132

133

Parameters:

134

- mute (bool): True to mute, False to unmute

135

136

Returns:

137

- bool: True if mute state was changed successfully

138

"""

139

...

140

141

def set_metadata(self, track):

142

"""

143

Set track metadata for current playback.

144

145

Parameters:

146

- track (Track): Track metadata

147

"""

148

...

149

150

def get_appsrc(self):

151

"""

152

Get GStreamer appsrc element for custom audio sources.

153

154

Returns:

155

- GstElement or None: appsrc element

156

"""

157

...

158

```

159

160

Usage example:

161

```python

162

# Audio actor is typically managed by core system

163

audio_actor = Audio.start(config=audio_config, mixer=mixer_instance)

164

audio_ref = audio_actor.actor_ref

165

166

# Control playback

167

audio_ref.tell({"method": "set_uri", "uri": "file:///path/to/song.mp3"})

168

audio_ref.tell({"method": "start_playback"})

169

audio_ref.tell({"method": "set_volume", "volume": 75})

170

```

171

172

### Playback State Management

173

174

Constants and utilities for managing audio playback states.

175

176

```python { .api }

177

class PlaybackState:

178

"""Constants for audio playback states."""

179

STOPPED = "stopped"

180

PLAYING = "playing"

181

PAUSED = "paused"

182

183

@classmethod

184

def all_states(cls):

185

"""

186

Get all valid playback states.

187

188

Returns:

189

- list[str]: All playback state constants

190

"""

191

...

192

```

193

194

### Audio Utility Functions

195

196

Helper functions for audio processing, format detection, and stream management.

197

198

```python { .api }

199

def supported_uri_schemes():

200

"""

201

Get list of supported URI schemes for audio playback.

202

203

Returns:

204

- list[str]: Supported URI schemes (file, http, https, etc.)

205

"""

206

...

207

208

def calculate_duration(num_samples, sample_rate):

209

"""

210

Calculate duration from audio samples and sample rate.

211

212

Parameters:

213

- num_samples (int): Number of audio samples

214

- sample_rate (int): Sample rate in Hz

215

216

Returns:

217

- int: Duration in milliseconds

218

"""

219

...

220

221

def millisecond_to_clocktime(milliseconds):

222

"""

223

Convert milliseconds to GStreamer clocktime format.

224

225

Parameters:

226

- milliseconds (int): Time in milliseconds

227

228

Returns:

229

- int: Time in GStreamer clocktime format (nanoseconds)

230

"""

231

...

232

233

def create_buffer(data, timestamp=None, duration=None):

234

"""

235

Create GStreamer buffer from audio data.

236

237

Parameters:

238

- data (bytes): Audio data

239

- timestamp (int, optional): Buffer timestamp in nanoseconds

240

- duration (int, optional): Buffer duration in nanoseconds

241

242

Returns:

243

- GstBuffer: GStreamer buffer object

244

"""

245

...

246

```

247

248

Usage example:

249

```python

250

from mopidy.audio import calculate_duration, supported_uri_schemes

251

252

# Check supported formats

253

schemes = supported_uri_schemes()

254

print(f"Supported schemes: {schemes}")

255

256

# Calculate duration for audio data

257

samples = 44100 * 180 # 3 minutes at 44.1kHz

258

duration_ms = calculate_duration(samples, 44100)

259

print(f"Duration: {duration_ms}ms")

260

```

261

262

### Audio Event Listeners

263

264

Event notification system for audio state changes and pipeline events.

265

266

```python { .api }

267

class AudioListener:

268

"""Base class for audio event listeners."""

269

270

def reached_end_of_stream(self):

271

"""Called when audio stream reaches end."""

272

...

273

274

def position_changed(self, position):

275

"""

276

Called when playback position changes.

277

278

Parameters:

279

- position (int): New position in milliseconds

280

"""

281

...

282

283

def state_changed(self, old_state, new_state):

284

"""

285

Called when playback state changes.

286

287

Parameters:

288

- old_state (str): Previous playback state

289

- new_state (str): New playback state

290

"""

291

...

292

293

def stream_changed(self, uri):

294

"""

295

Called when audio stream URI changes.

296

297

Parameters:

298

- uri (str): New stream URI

299

"""

300

...

301

302

def tags_changed(self, tags):

303

"""

304

Called when stream tags/metadata change.

305

306

Parameters:

307

- tags (dict): Stream tags and metadata

308

"""

309

...

310

```

311

312

Usage example:

313

```python

314

class MyAudioListener(AudioListener):

315

def reached_end_of_stream(self):

316

print("Track finished playing")

317

318

def position_changed(self, position):

319

print(f"Playback position: {position}ms")

320

321

def state_changed(self, old_state, new_state):

322

print(f"State changed: {old_state} -> {new_state}")

323

324

# Register listener with audio system

325

listener = MyAudioListener()

326

# Registration depends on specific audio system setup

327

```

328

329

### Audio Configuration

330

331

Configuration schema and options for the audio system.

332

333

```python { .api }

334

# Audio configuration schema

335

_audio_schema = ConfigSchema("audio")

336

_audio_schema["mixer"] = String() # Mixer implementation to use

337

_audio_schema["mixer_volume"] = Integer(optional=True, minimum=0, maximum=100)

338

_audio_schema["output"] = String() # Audio output configuration

339

_audio_schema["buffer_time"] = Integer(optional=True, minimum=1) # Buffer time in milliseconds

340

341

# Example audio configuration

342

"""

343

[audio]

344

mixer = software

345

mixer_volume = 80

346

output = autoaudiosink

347

buffer_time = 200

348

"""

349

```

350

351

### GStreamer Pipeline Integration

352

353

Low-level GStreamer pipeline management and element handling.

354

355

```python { .api }

356

class GstElement:

357

"""Type hint for GStreamer elements."""

358

pass

359

360

def get_audio_element():

361

"""

362

Get main audio pipeline element.

363

364

Returns:

365

- GstElement: Main audio pipeline element

366

"""

367

...

368

369

def setup_output_element(config):

370

"""

371

Set up audio output element based on configuration.

372

373

Parameters:

374

- config (dict): Audio configuration

375

376

Returns:

377

- GstElement: Configured output element

378

"""

379

...

380

381

def setup_mixer_element(config):

382

"""

383

Set up audio mixer element based on configuration.

384

385

Parameters:

386

- config (dict): Audio configuration

387

388

Returns:

389

- GstElement: Configured mixer element

390

"""

391

...

392

```

393

394

### Audio Format Support

395

396

Information about supported audio formats, codecs, and containers.

397

398

```python { .api }

399

def get_supported_formats():

400

"""

401

Get list of supported audio formats.

402

403

Returns:

404

- list[str]: Supported audio format extensions

405

"""

406

...

407

408

def get_supported_codecs():

409

"""

410

Get list of supported audio codecs.

411

412

Returns:

413

- list[str]: Supported audio codec names

414

"""

415

...

416

417

def detect_format(uri):

418

"""

419

Detect audio format from URI or file.

420

421

Parameters:

422

- uri (str): Audio URI or file path

423

424

Returns:

425

- str or None: Detected format or None if unknown

426

"""

427

...

428

```

429

430

### Stream Management

431

432

Utilities for handling audio streams, including network streams and local files.

433

434

```python { .api }

435

def prepare_stream(uri, headers=None, timeout=None):

436

"""

437

Prepare audio stream for playback.

438

439

Parameters:

440

- uri (str): Stream URI

441

- headers (dict, optional): HTTP headers for network streams

442

- timeout (int, optional): Connection timeout in seconds

443

444

Returns:

445

- bool: True if stream was prepared successfully

446

"""

447

...

448

449

def get_stream_metadata(uri):

450

"""

451

Get metadata from audio stream.

452

453

Parameters:

454

- uri (str): Stream URI

455

456

Returns:

457

- dict: Stream metadata (title, artist, album, etc.)

458

"""

459

...

460

461

def validate_stream_uri(uri):

462

"""

463

Validate that URI can be played by audio system.

464

465

Parameters:

466

- uri (str): URI to validate

467

468

Returns:

469

- bool: True if URI is valid and playable

470

"""

471

...

472

```

473

474

Usage example:

475

```python

476

# Prepare and validate streams

477

uri = "http://stream.example.com/radio.mp3"

478

if validate_stream_uri(uri):

479

headers = {"User-Agent": "Mopidy/3.4.2"}

480

if prepare_stream(uri, headers=headers, timeout=30):

481

metadata = get_stream_metadata(uri)

482

print(f"Stream title: {metadata.get('title', 'Unknown')}")

483

```

484

485

### Audio Buffer Management

486

487

Low-level buffer management for custom audio sources and processing.

488

489

```python { .api }

490

def create_audio_buffer(data, sample_rate=44100, channels=2, format="S16LE"):

491

"""

492

Create audio buffer from raw audio data.

493

494

Parameters:

495

- data (bytes): Raw audio data

496

- sample_rate (int): Sample rate in Hz

497

- channels (int): Number of audio channels

498

- format (str): Audio format (S16LE, F32LE, etc.)

499

500

Returns:

501

- GstBuffer: GStreamer audio buffer

502

"""

503

...

504

505

def buffer_to_samples(buffer):

506

"""

507

Extract audio samples from GStreamer buffer.

508

509

Parameters:

510

- buffer (GstBuffer): GStreamer buffer

511

512

Returns:

513

- bytes: Raw audio sample data

514

"""

515

...

516

517

def get_buffer_info(buffer):

518

"""

519

Get information about audio buffer.

520

521

Parameters:

522

- buffer (GstBuffer): GStreamer buffer

523

524

Returns:

525

- dict: Buffer info (duration, timestamp, size, etc.)

526

"""

527

...

528

```

529

530

### Audio Device Management

531

532

Interface for discovering and managing audio devices.

533

534

```python { .api }

535

def get_audio_devices():

536

"""

537

Get list of available audio devices.

538

539

Returns:

540

- list[dict]: Available audio devices with properties

541

"""

542

...

543

544

def get_default_audio_device():

545

"""

546

Get default audio output device.

547

548

Returns:

549

- dict: Default device information

550

"""

551

...

552

553

def set_audio_device(device_id):

554

"""

555

Set audio output device.

556

557

Parameters:

558

- device_id (str): Device identifier

559

560

Returns:

561

- bool: True if device was set successfully

562

"""

563

...

564

```

565

566

## Audio System Architecture

567

568

### Pipeline Structure

569

570

The audio system uses GStreamer pipelines with the following general structure:

571

572

```

573

uridecodebin -> audioconvert -> audioresample -> volume -> output

574

```

575

576

Key components:

577

- **uridecodebin**: Automatically decodes audio from various URI schemes

578

- **audioconvert**: Converts between audio formats as needed

579

- **audioresample**: Resamples audio to match output requirements

580

- **volume**: Provides software volume control

581

- **output**: Routes audio to speakers, files, or other destinations

582

583

### Buffer Management

584

585

Audio data flows through the pipeline in discrete buffers, each containing:

586

- Raw audio samples

587

- Timestamp information

588

- Duration metadata

589

- Format specifications

590

591

### Event Handling

592

593

The audio system generates events for:

594

- Playback state changes (play, pause, stop)

595

- End of stream notifications

596

- Position updates

597

- Error conditions

598

- Metadata changes

599

600

### Thread Safety

601

602

The audio system uses Pykka actors to ensure thread-safe operation across multiple components while maintaining real-time audio performance requirements.

603

604

## Mixer System

605

606

Mopidy's mixer system provides audio volume and mute control across different audio backends and hardware configurations.

607

608

### Mixer Base Class

609

610

Base mixer implementation that backends can extend to provide volume control.

611

612

```python { .api }

613

class Mixer:

614

"""

615

Audio mixer API for volume and mute control.

616

617

Parameters:

618

- config (dict): Mopidy configuration

619

"""

620

621

name: str # Name of the mixer (matches extension name)

622

623

def __init__(self, config): ...

624

625

def get_volume(self):

626

"""

627

Get volume level on a linear scale from 0 to 100.

628

629

Returns:

630

- int or None: Volume level (0-100), None if unknown

631

"""

632

...

633

634

def set_volume(self, volume):

635

"""

636

Set volume level of the mixer.

637

638

Parameters:

639

- volume (int): Volume level (0-100)

640

641

Returns:

642

- bool: True if successful

643

"""

644

...

645

646

def get_mute(self):

647

"""

648

Get mute state of the mixer.

649

650

Returns:

651

- bool or None: True if muted, False if not, None if unknown

652

"""

653

...

654

655

def set_mute(self, mute):

656

"""

657

Mute or unmute the mixer.

658

659

Parameters:

660

- mute (bool): True to mute, False to unmute

661

662

Returns:

663

- bool: True if successful

664

"""

665

...

666

667

def trigger_volume_changed(self, volume):

668

"""

669

Send volume_changed event to all mixer listeners.

670

671

Should be called by subclasses when volume changes.

672

673

Parameters:

674

- volume (int): New volume level

675

"""

676

...

677

678

def trigger_mute_changed(self, mute):

679

"""

680

Send mute_changed event to all mixer listeners.

681

682

Should be called by subclasses when mute state changes.

683

684

Parameters:

685

- mute (bool): New mute state

686

"""

687

...

688

689

def ping(self):

690

"""

691

Check if the mixer actor is still alive.

692

693

Returns:

694

- bool: True if alive

695

"""

696

...

697

```

698

699

### Mixer Listener

700

701

Event listener interface for receiving mixer state change notifications.

702

703

```python { .api }

704

class MixerListener:

705

"""Listener interface for mixer events."""

706

707

def volume_changed(self, volume):

708

"""

709

Called after the volume has changed.

710

711

Parameters:

712

- volume (int): New volume level (0-100)

713

"""

714

...

715

716

def mute_changed(self, mute):

717

"""

718

Called after the mute state has changed.

719

720

Parameters:

721

- mute (bool): New mute state

722

"""

723

...

724

725

def send(event, **kwargs):

726

"""

727

Helper to send mixer listener events.

728

729

Parameters:

730

- event (str): Event name ('volume_changed' or 'mute_changed')

731

- **kwargs: Event parameters

732

"""

733

...

734

```

735

736

### Usage Examples

737

738

```python

739

# Custom mixer implementation

740

class MyMixer(Mixer):

741

name = 'mymixer'

742

743

def __init__(self, config):

744

super().__init__(config)

745

self._volume = 50

746

self._muted = False

747

748

def get_volume(self):

749

return self._volume

750

751

def set_volume(self, volume):

752

if 0 <= volume <= 100:

753

self._volume = volume

754

self.trigger_volume_changed(volume)

755

return True

756

return False

757

758

def get_mute(self):

759

return self._muted

760

761

def set_mute(self, mute):

762

self._muted = bool(mute)

763

self.trigger_mute_changed(self._muted)

764

return True

765

766

# Using mixer events

767

class MyMixerListener(MixerListener):

768

def volume_changed(self, volume):

769

print(f"Volume changed to {volume}%")

770

771

def mute_changed(self, mute):

772

print(f"Mute {'enabled' if mute else 'disabled'}")

773

```