fix(audio,ios): correct ios_voice_unit imports + iOS stop() field access (rc.8+61)

iOS build of chanora_audio (target aarch64-apple-ios) failed with
four compilation errors after commit 2 landed. Root causes were
all simple symbol-path / cfg-gating mistakes from the skeleton
commit; the underlying design is unchanged.

1. ios_voice_unit.rs: wrong import path for OutPacket. The
   chanora_protocol crate re-exports it at the crate root
   (`pub use ...::OutPacket` in lib.rs line 52), not from a
   `voice` submodule (which doesn't exist).
   - use chanora_protocol::voice::OutPacket;
   + use chanora_protocol::OutPacket;

2. ios_voice_unit.rs: LinearPcmFlags lives in
   `coreaudio::audio_unit::audio_format`, not in
   `stream_format` (the doc page lists it under StreamFormat but
   the actual module path is the upstream Apple naming).
   - use coreaudio::audio_unit::stream_format::LinearPcmFlags;
   + use coreaudio::audio_unit::audio_format::LinearPcmFlags;

3. ios_voice_unit.rs: `Ordering` import unused (commit 1
   skeleton callbacks don't load atomics yet — that comes in
   commits 3 + 4). Remove from the std::sync::atomic import to
   silence the unused_imports warning.

4. engine.rs::AudioEngine::stop(): the existing body unconditionally
   touched self._input_stream and self._output_stream, but commit
   2 cfg-gated those fields away on iOS (and added an iOS-only
   _ios_voice_unit field in their place). Split the field drop
   logic with the same target_os = "ios" cfg so each platform
   only touches the fields it actually has.

Also clean up two pre-existing warnings exposed by the iOS cfg
gating:

5. engine.rs: `tracing::{error, warn}` were imported
   unconditionally but are only used inside cpal log lines.
   Cfg-gate the import to not(target_os = "ios").
6. engine.rs: `AudioData`, `CodecType`, `OutAudio` from
   chanora_protocol are only referenced in the Opus encoder feed
   inside CaptureState — cpal-side only. Cfg-gate to
   not(target_os = "ios"); keep `InboundVoice` + `OutPacket`
   on the unconditional path because the inbound forwarder + (in
   commit 3) the iOS capture pipeline both reference them.

Also fix a stray duplicate `#[cfg(not(target_os = "ios"))]`
attribute that landed on line 18 in commit 2.

Build verify (Linux host): cargo check -p chanora_audio clean
in 0.53s. iOS-side check pending on Mac.

Build counter bumped 60 -> 61 — the About dialog will display
v1.0.0-rc.8+61 so the user can confirm the build under test
matches this commit.
This commit is contained in:
EdisonJwa
2026-05-17 00:53:39 +08:00
parent 97a6ba6b55
commit 5de6eccc0c
3 changed files with 37 additions and 15 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0-rc.8+60
version: 1.0.0-rc.8+61
environment:
sdk: ^3.11.5
+32 -8
View File
@@ -13,9 +13,14 @@ use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
#[cfg(not(target_os = "ios"))]
use cpal::{SampleFormat, SizedSample};
use tokio::sync::mpsc;
use tracing::{debug, error, info, warn};
use tracing::{debug, info};
// `error!` and `warn!` are used only inside the cpal capture /
// playback paths (`build_input_stream`, `build_output_stream`,
// `try_open_capture` log lines). Cfg-gate the imports too so iOS
// builds don't carry an unused-imports warning.
#[cfg(not(target_os = "ios"))]
use tracing::{error, warn};
#[cfg(not(target_os = "ios"))]
use audiopus::coder::Encoder as OpusEncoder;
#[cfg(not(target_os = "ios"))]
@@ -26,9 +31,16 @@ use audiopus::{
use tsclientlib::audio::AudioHandler;
use chanora_protocol::{
AudioData, CodecType, InboundVoice, OutAudio, OutPacket,
};
// `AudioData`, `CodecType`, `OutAudio` are referenced only by the
// cpal capture pipeline's Opus encode path (`CaptureState::encode_and_send`).
// `InboundVoice` + `OutPacket` are used by every platform — the
// inbound forwarder task pumps `InboundVoice` into AudioHandler on
// iOS too, and `OutPacket` flows out of the capture pipeline once
// commit 3 lands. Cfg-gate the cpal-only ones to keep iOS warnings
// clean.
#[cfg(not(target_os = "ios"))]
use chanora_protocol::{AudioData, CodecType, OutAudio};
use chanora_protocol::{InboundVoice, OutPacket};
use crate::AudioError;
@@ -641,12 +653,24 @@ impl AudioEngine {
// The platform PTT backend is no longer owned by the
// engine (SDD-088); its lifecycle is managed by
// `chanora_core::ptt::PttController`. The engine only
// needs to abort its watchdog and drop the cpal streams.
// needs to abort its watchdog and drop the audio streams.
// Aborting the watchdog cancels its tokio task.
self.ptt_watchdog.take();
// Drop the streams, which stops their callback threads.
let _ = self._input_stream.lock().unwrap().take();
let _ = self._output_stream.lock().unwrap().take();
// Each platform has a slightly different backend; the
// common contract is that dropping the wrapper stops
// audio. iOS collapses input + output into one
// `IosVoiceUnit` (see `ios_voice_unit.rs`); every other
// platform has separate cpal input + cpal/SDL output.
#[cfg(not(target_os = "ios"))]
{
let _ = self._input_stream.lock().unwrap().take();
let _ = self._output_stream.lock().unwrap().take();
}
#[cfg(target_os = "ios")]
{
let _ = self._ios_voice_unit.lock().unwrap().take();
}
info!(target: "chanora_audio", "audio engine stopped");
}
+4 -6
View File
@@ -75,14 +75,12 @@
//! * AVAudioSession category / mode configuration — Swift owns the
//! session (it must be set up before Flutter loads).
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::atomic::{AtomicBool, AtomicU32};
use std::sync::{Arc, Mutex};
use coreaudio::audio_unit::audio_format::LinearPcmFlags;
use coreaudio::audio_unit::render_callback::{self, data};
use coreaudio::audio_unit::{
AudioUnit, Element, SampleFormat, Scope, StreamFormat,
};
use coreaudio::audio_unit::stream_format::LinearPcmFlags;
use coreaudio::audio_unit::{AudioUnit, Element, SampleFormat, Scope, StreamFormat};
use coreaudio::audio_unit::IOType;
use tokio::sync::mpsc;
use tracing::{info, warn};
@@ -90,7 +88,7 @@ use tsclientlib::audio::AudioHandler;
use crate::engine::SessionAudioId;
use crate::AudioError;
use chanora_protocol::voice::OutPacket;
use chanora_protocol::OutPacket;
/// 20 ms at 48 kHz mono — one Opus frame's worth of samples.
/// Aligning the AudioUnit IO buffer to this frame size keeps the