feat(voice): real-time mic input level metering at 30 Hz (#25)
* feat(voice): add real-time mic input level metering at 30 Hz
Expose input RMS from the audio engine through the bridge as a
dedicated Rust→Dart Stream<double>, replacing the binary on/off
indicator with a proportional dBFS level meter.
Rust side:
- chanora_audio: add set_input_dbfs/input_dbfs accessors to
SharedAudioProcessingStats; restructure CaptureState::ingest()
to compute dBFS from mono buffer before the PTT guard so the
meter shows mic activity even when not transmitting.
- chanora_core: widen audio_stats() return to include f32 input
level.
- chanora_bridge: add input_level: f32 to BridgeAudioStats and
new input_level_stream(sink: StreamSink<f32>) that pushes at
~30 Hz via tokio interval task.
- Update frb_generated.rs serialization for the new field.
Flutter side:
- VoiceLevelMeter: accept optional double level (dBFS), map
-60..0 dBFS to 0..1 fill fraction, animate with
TweenAnimationBuilder for smooth transitions.
- voice_compact.dart: subscribe to inputLevelStream in the voice
details sheet for 30 Hz meter updates, keeping 250 ms poll for
TX/RX counters.
- voice_bar.dart: accept optional inputLevel from the stream.
- main.dart: subscribe to inputLevelStream, pass to VoiceBar.
* chore: sync Flutter build config and dependency updates
- Add Flutter migrator flags to gradle.properties (builtInKotlin, newDsl)
- Add FlutterGeneratedPluginSwiftPackage to iOS/macOS Xcode projects
- Update meta 1.17→1.18, test_api 0.7.10→0.7.11
- Rebuild chanora_bridge framework for macOS
- Update Podfile.lock for iOS and macOS
* fix(voice): correct meter animation, pre-gain dBFS, stream lifecycle, and protocol warnings
B1: Convert VoiceLevelMeter to StatefulWidget tracking previous fill
as Tween begin so the meter animates smoothly instead of resetting
to zero on every frame.
B2: Compute dBFS from pre-gain mono samples in CaptureState::ingest()
so the level meter reflects raw mic input, matching mobile paths.
B4: End input_level_stream after 10 consecutive session errors instead
of emitting -120 dBFS forever when the session is gone.
Also fixes all 13 clippy warnings in chanora_protocol: collapsed
nested if-let patterns, replaced .ok() + Some matching with Ok, used
? operator, and introduced EventChannels struct to reduce the four
helper functions below the 7-argument threshold.
* fix(voice): use MissedTickBehavior::Skip for level meter stream and align dBFS doc
Set MissedTickBehavior::Skip on the input_level_stream tokio interval
so slow audio_stats() calls skip missed ticks instead of bursting,
preventing CPU spikes on the UI meter thread.
Align VoiceLevelMeter class doc: the mapping floors at -60 dBFS
(via dbfsToFraction), not the full -120 range.
This commit is contained in:
@@ -1,69 +1,32 @@
|
||||
PODS:
|
||||
- audio_session (0.0.1):
|
||||
- Flutter
|
||||
- chanora_bridge (1.0.0)
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_foreground_task (0.0.1):
|
||||
- Flutter
|
||||
- haptic_kit (1.0.0):
|
||||
- Flutter
|
||||
- package_info_plus (0.4.5):
|
||||
- Flutter
|
||||
- share_plus (0.0.1):
|
||||
- Flutter
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
||||
- chanora_bridge (from `.`)
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_foreground_task (from `.symlinks/plugins/flutter_foreground_task/ios`)
|
||||
- haptic_kit (from `.symlinks/plugins/haptic_kit/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
audio_session:
|
||||
:path: ".symlinks/plugins/audio_session/ios"
|
||||
chanora_bridge:
|
||||
:path: "."
|
||||
connectivity_plus:
|
||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_foreground_task:
|
||||
:path: ".symlinks/plugins/flutter_foreground_task/ios"
|
||||
haptic_kit:
|
||||
:path: ".symlinks/plugins/haptic_kit/ios"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
shared_preferences_foundation:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0
|
||||
chanora_bridge: 2ed7c2ba427fab135dd9eab66c507b09cfee113a
|
||||
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||
flutter_foreground_task: a159d2c2173b33699ddb3e6c2a067045d7cebb89
|
||||
haptic_kit: b22c4fbb2aa7b0d66f2891f81a9e950ad2de5758
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||
|
||||
PODFILE CHECKSUM: e2123068539aeb66d53dc1612b383d13f489ede2
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
FD3C80659716BF7A0C95C7AF /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 1937FD83C5CC909094CDC137 /* PrivacyInfo.xcprivacy */; };
|
||||
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -72,6 +73,7 @@
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
C10A61C706CAF223682AC397 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
E469085D9AE850FF6BD35704 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -79,6 +81,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
|
||||
8C5000042DD0000000000001 /* SileroCoreML in Frameworks */,
|
||||
1E3B5BCCA481234F14E64D44 /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
@@ -128,6 +131,7 @@
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||
@@ -216,6 +220,7 @@
|
||||
);
|
||||
name = Runner;
|
||||
packageProductDependencies = (
|
||||
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
|
||||
8C5000032DD0000000000001 /* SileroCoreML */,
|
||||
);
|
||||
productName = Runner;
|
||||
@@ -252,6 +257,7 @@
|
||||
);
|
||||
mainGroup = 97C146E51CF9000F007C117D;
|
||||
packageReferences = (
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
|
||||
8C5000022DD0000000000001 /* XCLocalSwiftPackageReference "silero-coreml" */,
|
||||
);
|
||||
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||
@@ -760,6 +766,10 @@
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
relativePath = ../../../silero-coreml;
|
||||
};
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
|
||||
};
|
||||
/* End XCLocalSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
@@ -768,6 +778,10 @@
|
||||
package = 8C5000022DD0000000000001 /* XCLocalSwiftPackageReference "silero-coreml" */;
|
||||
productName = SileroCoreML;
|
||||
};
|
||||
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = FlutterGeneratedPluginSwiftPackage;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||
|
||||
@@ -5,6 +5,24 @@
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<PreActions>
|
||||
<ExecutionAction
|
||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||
<ActionContent
|
||||
title = "Run Prepare Flutter Framework Script"
|
||||
scriptText = "/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare ">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
</ActionContent>
|
||||
</ExecutionAction>
|
||||
</PreActions>
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
|
||||
Reference in New Issue
Block a user