or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdconvenience-functions.mddevice-management.mdindex.mdstream-processing.mdutilities.md

utilities.mddocs/

0

# Utilities and Error Handling

1

2

Utility functions and exception classes for audio processing workflows and error management. These components provide additional functionality and robust error handling for sounddevice applications.

3

4

## Capabilities

5

6

### Utility Functions

7

8

Helper functions for timing operations and library information retrieval.

9

10

```python { .api }

11

def sleep(msec):

12

"""

13

Sleep for specified milliseconds using PortAudio's timing.

14

15

Parameters:

16

- msec (float): Number of milliseconds to sleep

17

18

Returns:

19

None

20

21

Notes:

22

Uses PortAudio's Pa_Sleep() function which may provide more accurate

23

timing than Python's time.sleep() on some platforms.

24

"""

25

26

def get_portaudio_version():

27

"""

28

Get PortAudio library version information.

29

30

Returns:

31

tuple: (version_text, version_number)

32

- version_text (str): Human-readable version string

33

- version_number (int): Numeric version identifier

34

"""

35

```

36

37

### Exception Classes

38

39

Specialized exception classes for handling audio-related errors and controlling stream behavior.

40

41

```python { .api }

42

class PortAudioError(Exception):

43

"""

44

Exception raised for PortAudio-related errors.

45

46

This exception is raised when PortAudio functions return error codes,

47

typically indicating hardware problems, invalid parameters, or

48

system resource issues.

49

50

Attributes:

51

- args[0] (str): Error message describing the specific problem

52

"""

53

54

class CallbackStop(Exception):

55

"""

56

Exception to signal stream callback should stop gracefully.

57

58

Raise this exception from within an audio callback function to

59

stop the stream in a controlled manner. The stream will finish

60

processing the current buffer and then stop.

61

"""

62

63

class CallbackAbort(Exception):

64

"""

65

Exception to signal stream callback should abort immediately.

66

67

Raise this exception from within an audio callback function to

68

abort the stream immediately. This may cause audio dropouts but

69

stops the stream as quickly as possible.

70

"""

71

72

class DeviceList(tuple):

73

"""

74

Special tuple subclass containing device information.

75

76

A list-like object that behaves like a tuple but provides special

77

string representation for interactive display of device information.

78

Each element is a device information dictionary.

79

"""

80

81

class CallbackFlags:

82

"""

83

Status flags for stream callback functions.

84

85

Provides information about buffer over-/underruns and stream conditions

86

that occurred during callback processing.

87

88

Attributes:

89

- input_underflow (bool): Input buffer underflow occurred

90

- input_overflow (bool): Input buffer overflow occurred

91

- output_underflow (bool): Output buffer underflow occurred

92

- output_overflow (bool): Output buffer overflow occurred

93

- priming_output (bool): Stream is priming output buffers

94

"""

95

96

def __init__(self, flags=0x0): ...

97

98

input_underflow: bool

99

input_overflow: bool

100

output_underflow: bool

101

output_overflow: bool

102

priming_output: bool

103

```

104

105

## Usage Examples

106

107

### Precise Timing with sleep()

108

109

```python

110

import sounddevice as sd

111

import time

112

113

def compare_sleep_methods():

114

"""Compare sounddevice.sleep() with time.sleep() precision."""

115

116

# Test sounddevice.sleep()

117

start = time.time()

118

sd.sleep(100) # Sleep for 100 milliseconds

119

sd_duration = time.time() - start

120

121

# Test time.sleep()

122

start = time.time()

123

time.sleep(0.1) # Sleep for 0.1 seconds (100 milliseconds)

124

time_duration = time.time() - start

125

126

print(f"sounddevice.sleep(100): {sd_duration:.6f} seconds")

127

print(f"time.sleep(0.1): {time_duration:.6f} seconds")

128

print(f"Target: 0.100000 seconds")

129

130

compare_sleep_methods()

131

```

132

133

### Using sleep() for Audio Synchronization

134

135

```python

136

import sounddevice as sd

137

import numpy as np

138

139

def play_with_gaps():

140

"""Play audio segments with precise gaps between them."""

141

142

# Generate short audio beeps

143

duration = 0.2 # 200ms beeps

144

samplerate = 44100

145

frequency = 800 # 800 Hz

146

147

t = np.linspace(0, duration, int(samplerate * duration))

148

beep = 0.3 * np.sin(2 * np.pi * frequency * t)

149

150

# Play 5 beeps with 300ms gaps

151

for i in range(5):

152

print(f"Playing beep {i+1}")

153

sd.play(beep, samplerate=samplerate, blocking=True)

154

155

if i < 4: # Don't sleep after the last beep

156

print("Waiting...")

157

sd.sleep(300) # 300ms gap using PortAudio timing

158

159

play_with_gaps()

160

```

161

162

### Getting Library Version Information

163

164

```python

165

import sounddevice as sd

166

167

def show_version_info():

168

"""Display sounddevice and PortAudio version information."""

169

170

# Get sounddevice version

171

print(f"sounddevice version: {sd.__version__}")

172

173

# Get PortAudio version

174

version_text, version_number = sd.get_portaudio_version()

175

print(f"PortAudio version: {version_text}")

176

print(f"PortAudio version number: {version_number}")

177

178

# Decode version number (example for PortAudio v19.7.0)

179

major = (version_number >> 16) & 0xFF

180

minor = (version_number >> 8) & 0xFF

181

patch = version_number & 0xFF

182

print(f"PortAudio version breakdown: {major}.{minor}.{patch}")

183

184

show_version_info()

185

```

186

187

### Exception Handling in Audio Operations

188

189

```python

190

import sounddevice as sd

191

import numpy as np

192

193

def safe_audio_operation():

194

"""Demonstrate proper exception handling for audio operations."""

195

196

try:

197

# Attempt to use a specific device that might not exist

198

devices = sd.query_devices()

199

print("Available devices:")

200

for i, device in enumerate(devices):

201

print(f" {i}: {device['name']}")

202

203

# Try to record from device index 99 (likely doesn't exist)

204

recording = sd.rec(frames=44100, samplerate=44100, device=99)

205

sd.wait()

206

207

except sd.PortAudioError as e:

208

print(f"PortAudio error occurred: {e}")

209

print("Falling back to default device...")

210

211

# Fallback to default device

212

try:

213

recording = sd.rec(frames=44100, samplerate=44100)

214

sd.wait()

215

print("Recording successful with default device")

216

217

except sd.PortAudioError as e2:

218

print(f"Even default device failed: {e2}")

219

return None

220

221

except Exception as e:

222

print(f"Unexpected error: {e}")

223

return None

224

225

return recording

226

227

# Test safe audio operation

228

result = safe_audio_operation()

229

if result is not None:

230

print(f"Recording completed: {len(result)} samples")

231

```

232

233

### Using Callback Exceptions for Stream Control

234

235

```python

236

import sounddevice as sd

237

import numpy as np

238

import time

239

240

def controlled_callback_demo():

241

"""Demonstrate using callback exceptions to control stream behavior."""

242

243

start_time = time.time()

244

max_duration = 5.0 # Maximum recording duration

245

246

recorded_data = []

247

248

def recording_callback(indata, frames, time, status):

249

"""Callback that stops after maximum duration."""

250

if status:

251

print(f"Status: {status}")

252

253

# Store the recorded data

254

recorded_data.append(indata.copy())

255

256

# Check if we should stop

257

elapsed = time.time() - start_time

258

if elapsed > max_duration:

259

print("Maximum duration reached, stopping gracefully...")

260

raise sd.CallbackStop()

261

262

# Check for some error condition (example: too loud input)

263

rms = np.sqrt(np.mean(indata**2))

264

if rms > 0.8: # If input is too loud

265

print("Input too loud, aborting immediately!")

266

raise sd.CallbackAbort()

267

268

try:

269

with sd.InputStream(callback=recording_callback, channels=1):

270

print("Recording started. Speak normally (will stop after 5 seconds)")

271

print("Speak loudly to trigger abort condition")

272

273

# Keep the stream alive

274

while True:

275

sd.sleep(100)

276

277

except sd.CallbackStop:

278

print("Stream stopped gracefully via CallbackStop")

279

except sd.CallbackAbort:

280

print("Stream aborted immediately via CallbackAbort")

281

except KeyboardInterrupt:

282

print("Recording interrupted by user")

283

284

if recorded_data:

285

total_samples = sum(len(block) for block in recorded_data)

286

print(f"Recorded {total_samples} samples in {len(recorded_data)} blocks")

287

288

return recorded_data

289

290

# Run the controlled callback demonstration

291

recorded_blocks = controlled_callback_demo()

292

```

293

294

### Comprehensive Error Handling Strategy

295

296

```python

297

import sounddevice as sd

298

import numpy as np

299

import logging

300

301

# Configure logging for audio errors

302

logging.basicConfig(level=logging.INFO)

303

logger = logging.getLogger(__name__)

304

305

class AudioManager:

306

"""Audio manager with comprehensive error handling."""

307

308

def __init__(self):

309

self.fallback_devices = []

310

self._discover_fallback_devices()

311

312

def _discover_fallback_devices(self):

313

"""Discover available devices for fallback scenarios."""

314

try:

315

devices = sd.query_devices()

316

# Find devices that support both input and output

317

for i, device in enumerate(devices):

318

if (device['max_input_channels'] > 0 and

319

device['max_output_channels'] > 0):

320

self.fallback_devices.append(i)

321

logger.info(f"Found {len(self.fallback_devices)} fallback devices")

322

except sd.PortAudioError as e:

323

logger.error(f"Failed to query devices: {e}")

324

325

def safe_record(self, duration=5.0, samplerate=44100, device=None):

326

"""Record audio with automatic fallback on errors."""

327

328

devices_to_try = [device] if device is not None else [None]

329

devices_to_try.extend(self.fallback_devices)

330

331

for attempt, dev in enumerate(devices_to_try):

332

try:

333

logger.info(f"Attempt {attempt + 1}: Using device {dev}")

334

335

recording = sd.rec(

336

frames=int(duration * samplerate),

337

samplerate=samplerate,

338

channels=1,

339

device=dev

340

)

341

sd.wait()

342

343

logger.info("Recording successful")

344

return recording

345

346

except sd.PortAudioError as e:

347

logger.warning(f"Device {dev} failed: {e}")

348

continue

349

except Exception as e:

350

logger.error(f"Unexpected error with device {dev}: {e}")

351

continue

352

353

logger.error("All devices failed")

354

return None

355

356

def get_system_info(self):

357

"""Get comprehensive system audio information."""

358

info = {}

359

360

try:

361

# PortAudio version

362

version_text, version_number = sd.get_portaudio_version()

363

info['portaudio_version'] = version_text

364

info['portaudio_version_number'] = version_number

365

366

# sounddevice version

367

info['sounddevice_version'] = sd.__version__

368

369

# Device count

370

devices = sd.query_devices()

371

info['device_count'] = len(devices)

372

info['input_devices'] = sum(1 for d in devices if d['max_input_channels'] > 0)

373

info['output_devices'] = sum(1 for d in devices if d['max_output_channels'] > 0)

374

375

# Host APIs

376

hostapis = sd.query_hostapis()

377

info['hostapi_count'] = len(hostapis)

378

info['hostapi_names'] = [api['name'] for api in hostapis]

379

380

except Exception as e:

381

logger.error(f"Failed to get system info: {e}")

382

info['error'] = str(e)

383

384

return info

385

386

# Usage example

387

audio_manager = AudioManager()

388

389

# Get system information

390

system_info = audio_manager.get_system_info()

391

print("System Audio Information:")

392

for key, value in system_info.items():

393

print(f" {key}: {value}")

394

395

# Safe recording with fallback

396

recording = audio_manager.safe_record(duration=3.0)

397

if recording is not None:

398

print(f"Successfully recorded {len(recording)} samples")

399

else:

400

print("Recording failed on all available devices")

401

```

402

403

## Error Code Reference

404

405

Common PortAudioError conditions and their meanings:

406

407

- **"Invalid device"**: Specified device index or name not found

408

- **"Invalid sample rate"**: Sample rate not supported by device

409

- **"Invalid number of channels"**: Channel count not supported

410

- **"Device unavailable"**: Device is busy or not accessible

411

- **"Insufficient memory"**: Not enough memory for audio buffers

412

- **"Host API not found"**: Requested host API not available

413

- **"Stream is not active"**: Attempted operation on inactive stream

414

- **"Invalid latency"**: Specified latency not achievable

415

416

## Callback Exception Guidelines

417

418

When using callback exceptions:

419

420

- **CallbackStop**: Use for graceful shutdown (normal completion, user request)

421

- **CallbackAbort**: Use for emergency shutdown (hardware failure, critical error)

422

- Handle exceptions outside the callback to avoid audio dropouts

423

- Log callback exceptions for debugging purposes

424

- Implement fallback strategies when callbacks fail