nandi/jolt-nativepublic Fork 0
b1758f55a558c64297729c4c64d80d20e80773e7
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Lift freeq's AV media plane out of sleek 90f8b89 · on b1758f55a558c64297729c4c64d80d20e80773e7 · nandi · 19d ago
UPGRADING.md · 247 lines · 9.9 KBmarkdown
Blame HistoryOpen raw

Upgrading from v0.17 to v0.18

This guide covers breaking changes requiring code updates. See CHANGELOG.md for the complete list of changes and improvements.

Breaking Changes Checklist

  • Add wildcard arms to exhaustive match expressions on cpal error enums
  • Optionally handle the new DeviceBusy variant for retryable device errors
  • Change build_*_stream call sites to pass StreamConfig by value (drop the &)
  • For custom hosts, change DeviceTrait implementations to accept StreamConfig by value.

1. Error enums are now #[non_exhaustive]

What changed: Public error enums in cpal are now marked #[non_exhaustive].

// Before (v0.17)
match device.default_output_config() {
    Ok(config) => config,
    Err(DefaultStreamConfigError::DeviceNotAvailable) => panic!("device gone"),
    Err(DefaultStreamConfigError::StreamTypeNotSupported) => panic!("unsupported"),
    Err(DefaultStreamConfigError::BackendSpecific { err }) => panic!("{err}"),
}

// After (v0.18)
loop {
    match device.default_output_config() {
        Ok(config) => break config,
        Err(DefaultStreamConfigError::DeviceBusy) => {
            std::thread::sleep(std::time::Duration::from_millis(100));
        }
        Err(DefaultStreamConfigError::DeviceNotAvailable) => panic!("device gone"),
        Err(DefaultStreamConfigError::StreamTypeNotSupported) => panic!("unsupported"),
        Err(DefaultStreamConfigError::BackendSpecific { err }) => panic!("{err}"),
        Err(_) => panic!("unknown error"),
    }
}

Why: This lets cpal add new variants in future minor releases without a SemVer-breaking change.

2. New DeviceBusy variant

What changed: On ALSA, EBUSY/EAGAIN errors from device open calls now produce DeviceBusy instead of DeviceNotAvailable. This may be added to other hosts in the future.

Why: Unlike DeviceNotAvailable (device is gone), DeviceBusy signals a transient condition. Retrying after a short delay may succeed, as shown in the example above.

3. StreamConfig is now passed by value

What changed: StreamConfig now implements Copy, and all DeviceTrait stream-building methods accept it by value.

// Before (v0.17)
let stream = device.build_output_stream(&config, data_fn, err_fn, None)?;

// After (v0.18)
let stream = device.build_output_stream(config, data_fn, err_fn, None)?;

Impact: Remove the & at every build_*_stream call site. Because StreamConfig is Copy, you can reuse the same binding across multiple calls without cloning.

If you implement DeviceTrait on your own type (via the custom feature), update your build_input_stream_raw and build_output_stream_raw signatures from config: &StreamConfig to config: StreamConfig. Any config.clone() calls before move closures can also be removed.


Upgrading from v0.16 to v0.17

Breaking Changes Checklist

  • Replace SampleRate(n) with plain n values
  • Update windows crate to >= 0.59, <= 0.62 (Windows only)
  • Update alsa crate to 0.11 (Linux only)
  • Remove wee_alloc feature from Wasm builds (if used)
  • Wrap CoreAudio streams in Arc if you were cloning them (macOS only)
  • Handle BuildStreamError::StreamConfigNotSupported for BufferSize::Fixed (JACK, strict validation)
  • Update device name matching if using ALSA (Linux only)

Recommended migrations:

  • Replace deprecated device.name() calls with device.description() or device.id()

1. SampleRate is now a u32 type alias

What changed: SampleRate changed from a struct to a u32 type alias.

// Before (v0.16)
use cpal::SampleRate;
let config = StreamConfig {
    channels: 2,
    sample_rate: SampleRate(44100),
    buffer_size: BufferSize::Default,
};

// After (v0.17)
let config = StreamConfig {
    channels: 2,
    sample_rate: 44100,
    buffer_size: BufferSize::Default,
};

Impact: Remove SampleRate() constructor calls. The type is now just u32, so use integer literals or variables directly.

2. Device::name() deprecated (soft deprecation)

What changed: Device::name() is deprecated in favor of id() and description().

// Old (still works but shows deprecation warning)
let name = device.name()?;

// New: For user-facing display
let desc = device.description()?;
println!("Device: {}", desc);  // or desc.name() for just the name

// New: For stable identification and persistence
let id = device.id()?;
let id_string = id.to_string();  // Save this
// Later...
let device = host.device_by_id(&id_string.parse()?)?;

Impact: Deprecation warnings only. The old API still works in v0.17. Update when convenient to prepare for future versions.

Why: Separates stable device identification (id()) from human-readable names (description()).

3. CoreAudio Stream no longer Clone (macOS)

What changed: On macOS, Stream no longer implements Clone. Use Arc instead.

// Before (v0.16) - macOS only
let stream = device.build_output_stream(&config, data_fn, err_fn, None)?;
let stream_clone = stream.clone();

// After (v0.17) - all platforms
let stream = Arc::new(device.build_output_stream(&config, data_fn, err_fn, None)?);
let stream_clone = Arc::clone(&stream);

Why: Removed as part of making Stream implement Send on macOS.

4. BufferSize behavior changes

BufferSize::Default now uses host defaults

What changed: BufferSize::Default now defers to the audio host/device defaults instead of applying cpal's opinionated defaults.

Impact: Buffer sizes may differ from v0.16, affecting latency characteristics:

  • Latency will vary based on host/device defaults (which may be lower, higher, or similar)
  • May underrun or have different latency depending on what the host chooses
  • Better integration with system audio configuration: cpal now respects configured settings instead of imposing its own buffers. For example, on ALSA, PipeWire quantum settings (via the pipewire-alsa device) are now honored instead of being overridden.

Migration: If you experience underruns, fast-forwarding behavior or need specific latency, use BufferSize::Fixed(size) instead of relying on possibly misconfigured system defaults.

Platform-specific notes:

  • ALSA: Previously used cpal's hardcoded 25ms periods / 100ms buffer, now uses device defaults
  • All platforms: Default buffer sizes now match what the host audio system expects

BufferSize::Fixed validation changes

What changed: Several backends now have different validation behavior for BufferSize::Fixed:

  • ALSA: Now uses set_buffer_size_near() for improved hardware compatibility with devices requiring byte-alignment, power-of-two sizes, or other alignment constraints (was: exact size via set_buffer_size(), which would reject unsupported sizes)
  • JACK: Must exactly match server buffer size (was: silently ignored)
  • Emscripten/WebAudio: Validates min/max range
  • ASIO: Stricter lower bound validation
// Handle validation errors
let mut config = StreamConfig {
    channels: 2,
    sample_rate: 44100,
    buffer_size: BufferSize::Fixed(512),
};

match device.build_output_stream(&config, data_fn, err_fn, None) {
    Ok(stream) => { /* success */ },
    Err(BuildStreamError::StreamConfigNotSupported) => {
        config.buffer_size = BufferSize::Default;  // Fallback
        device.build_output_stream(&config, data_fn, err_fn, None)?
    },
    Err(e) => return Err(e),
}

JACK users: Use BufferSize::Default to automatically match the server's configured size.

5. Dependency updates

Update these dependencies if you use them directly:

[dependencies]
cpal = "0.17"

# Platform-specific (if used directly):
alsa = "0.11"  # Linux only
windows = { version = ">=0.59, <=0.62" }  # Windows only
audio_thread_priority = "0.34"  # All platforms

6. ALSA device enumeration changed (Linux)

What changed: Device enumeration now returns all devices from aplay -L. v0.16 had a regression that only returned card names, missing all device variants.

  • v0.16: Only card names ("Loopback", "HDA Intel PCH")
  • v0.17: All aplay -L devices (default, hw:CARD=X,DEV=Y, plughw:, front:, surround51:, etc.)

Impact: Many more devices will be enumerated. Device names/IDs will be much more detailed. Update any code that matches specific ALSA device names.

7. Wasm wee_alloc feature removed

What changed: The optional wee_alloc feature was removed for security reasons.

# Before (v0.16)
cpal = { version = "0.16", features = ["wasm-bindgen", "wee_alloc"] }

# After (v0.17)
cpal = { version = "0.17", features = ["wasm-bindgen"] }

Notable Non-Breaking Improvements

v0.17 also includes significant improvements that don't require code changes:

  • Stable device IDs: New device.id() returns persistent device identifiers that survive reboots/reconnections. Use host.device_by_id() to reliably select saved devices.
  • Streams are Send+Sync everywhere: All platforms now support moving/sharing streams across threads
  • 24-bit sample formats: Added I24/U24 support on ALSA, CoreAudio, WASAPI, ASIO
  • Custom host support: Implement your own Host/Device/Stream for proprietary platforms
  • Predictable buffer sizes: CoreAudio and AAudio now ensure consistent callback buffer sizes
  • Expanded sample rate support: ALSA supports 12, 24, 352.8, 384, 705.6, and 768 kHz
  • WASAPI advanced interop: Exposed IMMDevice for Windows COM interop scenarios
  • Platform improvements: macOS loopback recording (14.6+), improved ALSA audio callback performance, improved timestamp accuracy, iOS AVAudioSession integration, JACK on all platforms

See CHANGELOG.md for complete details and examples/ for updated usage patterns.


Getting Help

  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
# Upgrading from v0.17 to v0.18

This guide covers breaking changes requiring code updates. See [CHANGELOG.md](CHANGELOG.md) for the complete list of changes and improvements.

## Breaking Changes Checklist

- [ ] Add wildcard arms to exhaustive `match` expressions on cpal error enums
- [ ] Optionally handle the new `DeviceBusy` variant for retryable device errors
- [ ] Change `build_*_stream` call sites to pass `StreamConfig` by value (drop the `&`)
- [ ] For custom hosts, change `DeviceTrait` implementations to accept `StreamConfig` by value.

## 1. Error enums are now `#[non_exhaustive]`

**What changed:** Public error enums in `cpal` are now marked `#[non_exhaustive]`.

```rust
// Before (v0.17)
match device.default_output_config() {
    Ok(config) => config,
    Err(DefaultStreamConfigError::DeviceNotAvailable) => panic!("device gone"),
    Err(DefaultStreamConfigError::StreamTypeNotSupported) => panic!("unsupported"),
    Err(DefaultStreamConfigError::BackendSpecific { err }) => panic!("{err}"),
}

// After (v0.18)
loop {
    match device.default_output_config() {
        Ok(config) => break config,
        Err(DefaultStreamConfigError::DeviceBusy) => {
            std::thread::sleep(std::time::Duration::from_millis(100));
        }
        Err(DefaultStreamConfigError::DeviceNotAvailable) => panic!("device gone"),
        Err(DefaultStreamConfigError::StreamTypeNotSupported) => panic!("unsupported"),
        Err(DefaultStreamConfigError::BackendSpecific { err }) => panic!("{err}"),
        Err(_) => panic!("unknown error"),
    }
}
```

**Why:** This lets cpal add new variants in future minor releases without a SemVer-breaking change.

## 2. New `DeviceBusy` variant

**What changed:** On ALSA, `EBUSY`/`EAGAIN` errors from device open calls now produce `DeviceBusy` instead of `DeviceNotAvailable`. This may be added to other hosts in the future.

**Why:** Unlike `DeviceNotAvailable` (device is gone), `DeviceBusy` signals a transient condition. Retrying after a short delay may succeed, as shown in the example above.

## 3. `StreamConfig` is now passed by value

**What changed:** `StreamConfig` now implements `Copy`, and all `DeviceTrait` stream-building methods accept it by value.

```rust
// Before (v0.17)
let stream = device.build_output_stream(&config, data_fn, err_fn, None)?;

// After (v0.18)
let stream = device.build_output_stream(config, data_fn, err_fn, None)?;
```

**Impact:** Remove the `&` at every `build_*_stream` call site. Because `StreamConfig` is `Copy`, you can reuse the same binding across multiple calls without cloning.

If you implement `DeviceTrait` on your own type (via the `custom` feature), update your `build_input_stream_raw` and `build_output_stream_raw` signatures from `config: &StreamConfig` to `config: StreamConfig`. Any `config.clone()` calls before `move` closures can also be removed.

---

# Upgrading from v0.16 to v0.17

## Breaking Changes Checklist

- [ ] Replace `SampleRate(n)` with plain `n` values
- [ ] Update `windows` crate to >= 0.59, <= 0.62 (Windows only)
- [ ] Update `alsa` crate to 0.11 (Linux only)
- [ ] Remove `wee_alloc` feature from Wasm builds (if used)
- [ ] Wrap CoreAudio streams in `Arc` if you were cloning them (macOS only)
- [ ] Handle `BuildStreamError::StreamConfigNotSupported` for `BufferSize::Fixed` (JACK, strict validation)
- [ ] Update device name matching if using ALSA (Linux only)

**Recommended migrations:**
- [ ] Replace deprecated `device.name()` calls with `device.description()` or `device.id()`

---

## 1. SampleRate is now a u32 type alias

**What changed:** `SampleRate` changed from a struct to a `u32` type alias.

```rust
// Before (v0.16)
use cpal::SampleRate;
let config = StreamConfig {
    channels: 2,
    sample_rate: SampleRate(44100),
    buffer_size: BufferSize::Default,
};

// After (v0.17)
let config = StreamConfig {
    channels: 2,
    sample_rate: 44100,
    buffer_size: BufferSize::Default,
};
```

**Impact:** Remove `SampleRate()` constructor calls. The type is now just `u32`, so use integer literals or variables directly.

## 2. Device::name() deprecated (soft deprecation)

**What changed:** `Device::name()` is deprecated in favor of `id()` and `description()`.

```rust
// Old (still works but shows deprecation warning)
let name = device.name()?;

// New: For user-facing display
let desc = device.description()?;
println!("Device: {}", desc);  // or desc.name() for just the name

// New: For stable identification and persistence
let id = device.id()?;
let id_string = id.to_string();  // Save this
// Later...
let device = host.device_by_id(&id_string.parse()?)?;
```

**Impact:** Deprecation warnings only. The old API still works in v0.17. Update when convenient to prepare for future versions.

**Why:** Separates stable device identification (`id()`) from human-readable names (`description()`).

## 3. CoreAudio Stream no longer Clone (macOS)

**What changed:** On macOS, `Stream` no longer implements `Clone`. Use `Arc` instead.

```rust
// Before (v0.16) - macOS only
let stream = device.build_output_stream(&config, data_fn, err_fn, None)?;
let stream_clone = stream.clone();

// After (v0.17) - all platforms
let stream = Arc::new(device.build_output_stream(&config, data_fn, err_fn, None)?);
let stream_clone = Arc::clone(&stream);
```

**Why:** Removed as part of making `Stream` implement `Send` on macOS.

## 4. BufferSize behavior changes

### BufferSize::Default now uses host defaults

**What changed:** `BufferSize::Default` now defers to the audio host/device defaults instead of applying cpal's opinionated defaults.

**Impact:** Buffer sizes may differ from v0.16, affecting latency characteristics:
- **Latency will vary** based on host/device defaults (which may be lower, higher, or similar)
- **May underrun or have different latency** depending on what the host chooses
- **Better integration** with system audio configuration: cpal now respects configured settings instead of imposing its own buffers. For example, on ALSA, PipeWire quantum settings (via the pipewire-alsa device) are now honored instead of being overridden.

**Migration:** If you experience underruns, fast-forwarding behavior or need specific latency, use `BufferSize::Fixed(size)` instead of relying on possibly misconfigured system defaults.

**Platform-specific notes:**
- **ALSA:** Previously used cpal's hardcoded 25ms periods / 100ms buffer, now uses device defaults
- **All platforms:** Default buffer sizes now match what the host audio system expects

### BufferSize::Fixed validation changes

**What changed:** Several backends now have different validation behavior for `BufferSize::Fixed`:

- **ALSA:** Now uses `set_buffer_size_near()` for improved hardware compatibility with devices requiring byte-alignment, power-of-two sizes, or other alignment constraints (was: exact size via `set_buffer_size()`, which would reject unsupported sizes)
- **JACK:** Must exactly match server buffer size (was: silently ignored)
- **Emscripten/WebAudio:** Validates min/max range
- **ASIO:** Stricter lower bound validation

```rust
// Handle validation errors
let mut config = StreamConfig {
    channels: 2,
    sample_rate: 44100,
    buffer_size: BufferSize::Fixed(512),
};

match device.build_output_stream(&config, data_fn, err_fn, None) {
    Ok(stream) => { /* success */ },
    Err(BuildStreamError::StreamConfigNotSupported) => {
        config.buffer_size = BufferSize::Default;  // Fallback
        device.build_output_stream(&config, data_fn, err_fn, None)?
    },
    Err(e) => return Err(e),
}
```

**JACK users:** Use `BufferSize::Default` to automatically match the server's configured size.

## 5. Dependency updates

Update these dependencies if you use them directly:

```toml
[dependencies]
cpal = "0.17"

# Platform-specific (if used directly):
alsa = "0.11"  # Linux only
windows = { version = ">=0.59, <=0.62" }  # Windows only
audio_thread_priority = "0.34"  # All platforms
```

## 6. ALSA device enumeration changed (Linux)

**What changed:** Device enumeration now returns all devices from `aplay -L`. v0.16 had a regression that only returned card names, missing all device variants.

* v0.16: Only card names ("Loopback", "HDA Intel PCH")
* v0.17: All aplay -L devices (default, hw:CARD=X,DEV=Y, plughw:, front:, surround51:, etc.)

**Impact:** Many more devices will be enumerated. Device names/IDs will be much more detailed. Update any code that matches specific ALSA device names.

## 7. Wasm wee_alloc feature removed

**What changed:** The optional `wee_alloc` feature was removed for security reasons.

```toml
# Before (v0.16)
cpal = { version = "0.16", features = ["wasm-bindgen", "wee_alloc"] }

# After (v0.17)
cpal = { version = "0.17", features = ["wasm-bindgen"] }
```

## Notable Non-Breaking Improvements

v0.17 also includes significant improvements that don't require code changes:

- **Stable device IDs:** New `device.id()` returns persistent device identifiers that survive reboots/reconnections. Use `host.device_by_id()` to reliably select saved devices.
- **Streams are Send+Sync everywhere:** All platforms now support moving/sharing streams across threads
- **24-bit sample formats:** Added `I24`/`U24` support on ALSA, CoreAudio, WASAPI, ASIO
- **Custom host support:** Implement your own `Host`/`Device`/`Stream` for proprietary platforms
- **Predictable buffer sizes:** CoreAudio and AAudio now ensure consistent callback buffer sizes
- **Expanded sample rate support:** ALSA supports 12, 24, 352.8, 384, 705.6, and 768 kHz
- **WASAPI advanced interop:** Exposed `IMMDevice` for Windows COM interop scenarios
- **Platform improvements:** macOS loopback recording (14.6+), improved ALSA audio callback performance, improved timestamp accuracy, iOS AVAudioSession integration, JACK on all platforms

See [CHANGELOG.md](CHANGELOG.md) for complete details and [examples/](examples/) for updated usage patterns.

---

## Getting Help

- Full details: [CHANGELOG.md](CHANGELOG.md)
- Examples: [examples/](examples/)
- Issues: https://github.com/RustAudio/cpal/issues