ush 0.1.0

Ultrasonic Shell - communicate between devices using ultrasonic sound waves
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# Audio Processing and Cross-Platform I/O

## Overview

The audio processing subsystem provides cross-platform abstraction for real-time audio input/output, enabling ultrasonic communication across Windows, macOS, and Linux. Built on the `cpal` library, it handles device enumeration, format negotiation, and streaming audio data.

## Audio System Architecture

```
┌─────────────────────────────────────┐
│         Application Layer           │  UshApp
├─────────────────────────────────────┤
│        Audio Abstraction            │  AudioManager
├─────────────────────────────────────┤
│         cpal Library                │  Cross-platform Audio
├─────────────────────────────────────┤
│       Platform-Specific APIs        │
├─────────────────┬───────────────────┤
│    CoreAudio    │      WASAPI      │  │     ALSA      │
│     (macOS)     │     (Windows)    │  │    (Linux)    │
└─────────────────┴───────────────────┘  └───────────────┘
```

## Platform Support

### Audio API Backends

The system leverages different native audio APIs depending on the platform¹:

**macOS - Core Audio**:
- Low-latency audio framework
- Built into the operating system
- Hardware abstraction layer (HAL)
- Support for 16-bit, 24-bit, and 32-bit sample formats

**Windows - WASAPI (Windows Audio Session API)**:
- Modern Windows audio architecture
- Introduced in Windows Vista
- Low-latency exclusive mode available
- Shared and exclusive audio modes

**Linux - ALSA (Advanced Linux Sound Architecture)**:
- Kernel-level audio framework
- Direct hardware access
- Plugin architecture for format conversion
- PulseAudio compatibility layer

## Audio Configuration

### Sample Rate Selection

The system defaults to 44.1 kHz sampling rate, chosen for several technical reasons:

```rust
const SAMPLE_RATE: u32 = 44100;

pub struct AudioConfig {
    pub sample_rate: u32,    // 44,100 Hz
    pub channels: u16,       // Mono (1 channel)
    pub buffer_size: usize,  // 4,096 samples
}
```

**Rationale for 44.1 kHz**:
- **Nyquist Theorem Compliance²**: Supports frequencies up to 22.05 kHz
- **Ultrasonic Coverage**: Adequate for 18-22 kHz frequency range
- **Hardware Compatibility**: Universally supported sample rate
- **Audio CD Standard**: Well-established in consumer audio equipment

### Channel Configuration

**Mono Operation** (1 channel):
- Simplified processing pipeline
- Reduced computational complexity
- Consistent cross-platform behavior
- Most built-in microphones are mono

### Buffer Size Optimization

```rust
const BUFFER_SIZE: usize = 4096; // ~93ms at 44.1kHz
```

**Trade-offs**:
- **Latency**: Larger buffers increase latency
- **Stability**: Larger buffers reduce dropouts
- **Processing Time**: Must complete within buffer duration
- **Memory Usage**: Larger buffers consume more RAM

**Buffer Duration Calculation**:
```
Duration = Buffer_Size / Sample_Rate
         = 4096 / 44100
         = 0.093 seconds (93 ms)
```

## Device Management

### Device Enumeration and Selection

```rust
impl AudioManager {
    fn get_input_device(&self) -> UshResult<Device> {
        self.host
            .default_input_device()
            .ok_or_else(|| UshError::Config {
                message: "No input device available".to_string(),
            })
    }
    
    fn get_output_device(&self) -> UshResult<Device> {
        self.host
            .default_output_device()
            .ok_or_else(|| UshError::Config {
                message: "No output device available".to_string(),
            })
    }
}
```

### Format Negotiation

The system automatically negotiates audio formats with available hardware:

```rust
fn get_supported_config(&self, device: &Device, is_input: bool) -> UshResult<SupportedStreamConfig> {
    let sample_rate = SampleRate(self.config.sample_rate);
    
    if is_input {
        for config in device.supported_input_configs()? {
            if config.channels() == self.config.channels
                && config.min_sample_rate() <= sample_rate
                && sample_rate <= config.max_sample_rate()
            {
                return Ok(config.with_sample_rate(sample_rate));
            }
        }
    }
    // Similar logic for output devices...
}
```

**Capability Matching**:
1. **Channel Count**: Must support mono (1 channel)
2. **Sample Rate**: Must support 44.1 kHz
3. **Sample Format**: Automatic conversion between formats
4. **Buffer Size**: Negotiate optimal buffer size

## Sample Format Handling

### Format Conversion

The system supports multiple sample formats with automatic conversion³:

```rust
fn fill_output_buffer<T>(
    data: &mut [T],
    samples: &Arc<Mutex<Vec<f32>>>,
    // ...
) where
    T: Sample + Send + FromSample<f32>,
{
    let sample_value = samples_lock[*index_lock];
    let converted_sample = T::from_sample(sample_value);
    
    for sample in frame.iter_mut() {
        *sample = converted_sample;
    }
}
```

**Supported Formats**:
- **I8**: 8-bit signed integer (-128 to 127)
- **I16**: 16-bit signed integer (-32,768 to 32,767)  
- **I32**: 32-bit signed integer
- **F32**: 32-bit floating point (-1.0 to 1.0)

**Conversion Process**:
```rust
// Float to Integer conversion
fn f32_to_i16(sample: f32) -> i16 {
    (sample.clamp(-1.0, 1.0) * i16::MAX as f32) as i16
}

// Integer to Float conversion  
fn i16_to_f32(sample: i16) -> f32 {
    sample as f32 / i16::MAX as f32
}
```

## Streaming Audio

### Input Stream Creation

```rust
pub fn create_input_stream(
    &self,
    mut callback: impl FnMut(&[f32]) + Send + 'static,
) -> UshResult<Stream> {
    let device = self.get_input_device()?;
    let config = self.get_supported_config(&device, true)?;
    
    let stream = match config.sample_format() {
        SampleFormat::F32 => device.build_input_stream(
            &config.into(),
            move |data: &[f32], _: &InputCallbackInfo| {
                callback(data);
            },
            |err| warn!("Input stream error: {}", err),
            None,
        )?,
        // Handle other formats...
    };
    
    Ok(stream)
}
```

**Input Processing Pipeline**:
1. **Hardware → Driver**: Audio samples from microphone
2. **Driver → cpal**: Platform-specific audio buffer
3. **cpal → Callback**: Format conversion and delivery
4. **Callback → Application**: User-defined processing

### Output Stream Creation

```rust
pub fn create_output_stream(
    &self,
    samples: Arc<Mutex<Vec<f32>>>,
    finished_tx: mpsc::UnboundedSender<()>,
) -> UshResult<Stream> {
    let device = self.get_output_device()?;
    let config = self.get_supported_config(&device, false)?;
    
    let sample_index = Arc::new(Mutex::new(0usize));
    let channels = config.channels() as usize;
    
    let stream = device.build_output_stream(
        &config.into(),
        move |data: &mut [T], _: &OutputCallbackInfo| {
            Self::fill_output_buffer(data, &samples, &sample_index, channels, &finished_tx);
        },
        |err| warn!("Output stream error: {}", err),
        None,
    )?;
    
    Ok(stream)
}
```

**Output Processing Pipeline**:
1. **Application**: Generate audio samples
2. **Shared Memory**: Thread-safe sample buffer
3. **Output Callback**: Fill hardware buffer
4. **Platform Audio**: Convert and send to speakers

## Real-Time Constraints

### Audio Thread Requirements

Audio callbacks operate under strict real-time constraints⁴:

**Critical Requirements**:
- **No blocking operations**: No file I/O, network, or long computations
- **Bounded execution time**: Must complete within buffer duration
- **No memory allocation**: Avoid heap allocation in callbacks
- **Lock-free when possible**: Minimize mutex usage

**Implementation Strategy**:
```rust
// Pre-allocate shared buffer
let samples = Arc::new(Mutex::new(Vec::with_capacity(MAX_SAMPLES)));

// Audio callback - minimal work
move |data: &[f32], _: &InputCallbackInfo| {
    if let Ok(mut buffer) = samples_clone.try_lock() {
        buffer.extend_from_slice(data);
    }
    // No other processing in callback
}
```

### Dropout Prevention

**Buffer Underrun/Overrun Prevention**:
- **Double Buffering**: Alternate between buffers
- **Circular Buffers**: Continuous data flow
- **Adaptive Buffer Size**: Adjust based on system performance
- **Priority Scheduling**: Real-time thread priority where available

## Threading Model

### Thread Architecture

```
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Main Thread   │    │  Audio Thread   │    │  Async Thread   │
│                 │    │  (OS managed)   │    │   (Tokio)       │
│ • UI/CLI        │◄──►│ • Input Callback│◄──►│ • Signal Proc.  │
│ • Control Logic │    │ • Output Callback    │ • Protocol      │
│ • Configuration │    │ • Real-time      │    │ • I/O           │
└─────────────────┘    └─────────────────┘    └─────────────────┘
```

### Thread Communication

**Shared State Management**:
```rust
// Thread-safe audio buffer
let audio_buffer = Arc::new(Mutex<Vec<f32>>>::new(Vec::new()));

// Message passing for control
let (control_tx, control_rx) = mpsc::unbounded_channel();

// Completion notification
let (finished_tx, finished_rx) = mpsc::unbounded_channel();
```

**Synchronization Patterns**:
- **Arc<Mutex<T>>**: Shared mutable state
- **mpsc channels**: Message passing
- **Atomic operations**: Lock-free primitives where applicable

## Error Handling and Recovery

### Common Audio Errors

**Device Errors**:
```rust
#[derive(Error, Debug)]
pub enum UshError {
    #[error("Audio device error: {0}")]
    AudioDevice(#[from] cpal::DevicesError),
    
    #[error("Audio format error: {0}")]
    AudioFormat(#[from] cpal::SupportedStreamConfigsError),
    
    #[error("Audio stream error: {0}")]
    Audio(#[from] cpal::StreamError),
}
```

**Recovery Strategies**:
1. **Device Disconnection**: Graceful degradation, user notification
2. **Format Mismatch**: Automatic fallback to supported formats
3. **Stream Failure**: Restart stream with different parameters
4. **Buffer Overflow**: Increase buffer size, reduce processing load

### Robustness Measures

**Stream Health Monitoring**:
```rust
// Error callback for stream monitoring
|err| {
    match err {
        StreamError::DeviceNotAvailable => {
            // Attempt device reconnection
            warn!("Audio device disconnected, attempting reconnection...");
        },
        StreamError::BackendSpecific { err } => {
            // Platform-specific error handling
            error!("Platform-specific audio error: {:?}", err);
        },
    }
}
```

## Performance Optimization

### CPU Usage Optimization

**Efficient Sample Processing**:
```rust
// Vectorized operations where possible
fn process_samples_simd(samples: &mut [f32]) {
    // Use SIMD instructions for bulk operations
    for chunk in samples.chunks_exact_mut(4) {
        // Process 4 samples simultaneously
    }
}
```

**Memory Layout Optimization**:
- **Cache-friendly access patterns**: Sequential memory access
- **Alignment**: Ensure proper memory alignment for SIMD
- **Buffer reuse**: Minimize allocation/deallocation

### Latency Minimization

**End-to-End Latency Components**⁵:
```
Total Latency = Input_Buffer + Processing + Output_Buffer + Hardware_Latency

Where:
- Input_Buffer: ~93ms (4096 samples at 44.1kHz)
- Processing: <10ms (signal processing time)  
- Output_Buffer: ~93ms (4096 samples at 44.1kHz)
- Hardware_Latency: ~5-20ms (device dependent)

Total: ~200-220ms typical
```

**Latency Reduction Techniques**:
- **Smaller buffer sizes**: Reduce buffering delay
- **Exclusive mode**: Bypass audio mixing (Windows)
- **Real-time scheduling**: Higher thread priority
- **Hardware acceleration**: Dedicated audio processors

## Testing and Validation

### Audio System Testing

**Loopback Testing**:
```rust
#[tokio::test]
async fn test_audio_loopback() -> UshResult<()> {
    let manager = AudioManager::new()?;
    
    // Generate test signal
    let test_freq = 1000.0; // 1 kHz sine wave
    let test_samples = generate_sine_wave(test_freq, SAMPLE_RATE, 1.0);
    
    // Record output through input (requires physical loopback)
    let recorded = record_playback(&manager, &test_samples).await?;
    
    // Verify signal integrity
    let correlation = cross_correlate(&test_samples, &recorded);
    assert!(correlation > 0.9, "Poor audio fidelity: {}", correlation);
    
    Ok(())
}
```

### Cross-Platform Validation

**Platform-Specific Tests**:
- **macOS**: Core Audio compatibility testing
- **Windows**: WASAPI exclusive mode testing  
- **Linux**: ALSA/PulseAudio compatibility
- **Hardware Variation**: Different audio interfaces

## Future Enhancements

### Advanced Audio Features

**Adaptive Buffer Sizing**:
```rust
fn adjust_buffer_size(current_latency: Duration, target_latency: Duration) -> usize {
    let ratio = target_latency.as_secs_f32() / current_latency.as_secs_f32();
    (BUFFER_SIZE as f32 * ratio).round() as usize
}
```

**Multi-Channel Support**:
- Stereo operation for increased data rate
- Channel-specific frequency allocation
- Spatial audio techniques

**Hardware Acceleration**:
- GPU-based FFT processing
- Dedicated DSP hardware utilization
- ASIO driver support (Windows)

## References

1. RustAudio Community. (2023). *cpal - Cross-Platform Audio Library*. https://github.com/RustAudio/cpal (Cross-platform audio abstraction)

2. Nyquist, H. (1928). "Certain topics in telegraph transmission theory." *Transactions of the American Institute of Electrical Engineers*, 47(2), 617-644. (Sampling theorem)

3. Smith, S. W. (2011). *Digital Signal Processing: A Practical Guide for Engineers and Scientists*. Newnes. (Sample format conversion)

4. Dannenberg, R. B. (1997). "Real-time scheduling and computer music." *Computer Music Journal*, 21(3), 25-40. (Real-time audio constraints)

5. Brandt, E., & Dannenberg, R. B. (1999). "Low-latency audio synthesis in Java." *Proceedings of the International Computer Music Conference*, 101-104. (Audio latency analysis)