Repository navigation
Own native recording and continuous capture in the module #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
e96e9de
feat(bus): own native recording and bounded audio outputs
senamakel 39ead47
feat(capture): own bounded continuous streams and shutdown
senamakel 570735a
fix: retain native capture ownership through cancellation and shutdown
senamakel ee09645
Add hotkey lifecycle bus contract
senamakel 7ac8f6c
Own native hotkey lifecycle in module
senamakel 5276937
Document module hotkey lifecycle
senamakel 2f8e150
Preserve hotkey batch ack across reset
senamakel 78c43a5
Share hotkey vocabulary with the bus
senamakel c22a867
Recover ended Windows hotkey owners
senamakel b007cc6
docs(tinyvoice): document module-owned capture and hotkey lifecycles
senamakel ba3cee8
test(tinyvoice): validate formatted hotkey lifecycle coverage
senamakel 3fc932b
fix(module): satisfy stable clippy for module workspace
senamakel b2e43a6
fix(tinyvoice): harden hotkey replay and startup races
senamakel 649756c
fix(tinyvoice): wait for native hotkey startup retries
senamakel 294e348
test(tinyvoice): clarify tap repeat semantics
senamakel 82d92da
fix(tinyvoice): close capture and native hotkey gaps
senamakel 1ed9aca
fix(windows): register hook from its owning module
senamakel 20b930c
fix(tinyvoice): verify platform hotkey lifecycle gates
senamakel 887dc4a
fix(windows): reap failed hook cleanup off the drop path
senamakel 77bd00b
fix: preserve hotkey feed continuity and shutdown cleanup
senamakel d64ed33
fix: clean up lost XRecord contexts and map navigation keys
senamakel 5b98a8b
docs: list TinyVoice hotkey bus methods
senamakel 5c5bfca
fix: reset hotkey state at every sequence discontinuity
senamakel 40fad03
fix: retain uncertain XRecord cleanup for retry
senamakel 02f6a27
style: format hotkey regression tests
senamakel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| //! Native capture lifecycle vocabulary; no devices or audio processing live here. | ||
| use serde::{Deserialize, Serialize}; | ||
| /// Permission decision obtained by the host through the computer module. | ||
| #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] | ||
| #[serde(rename_all = "snake_case")] | ||
| pub enum MicrophonePermission { | ||
| /// Capture may proceed. | ||
| Granted, | ||
| /// Capture is refused, including unknown or unsupported permission states. | ||
| #[default] | ||
| Denied, | ||
| } | ||
| /// Opaque module-owned recording or audio output handle. | ||
| #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||
| #[serde(transparent)] | ||
| pub struct CaptureHandle(pub String); | ||
|
senamakel marked this conversation as resolved.
|
||
| /// Reserve or start capture using an explicit permission decision. | ||
| #[derive(Debug, Clone, Default, Serialize, Deserialize)] | ||
| pub struct RecordingStartRequest { | ||
|
senamakel marked this conversation as resolved.
|
||
| /// Permission granted through the computer module. | ||
| pub permission: MicrophonePermission, | ||
|
senamakel marked this conversation as resolved.
|
||
| /// Module-generated reservation obtained before starting native setup. | ||
| #[serde(default)] | ||
| pub handle: Option<CaptureHandle>, | ||
| } | ||
| /// Finish capture and run the module's existing preparation pipeline. | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct RecordingFinishRequest { | ||
| /// Recording lease. | ||
| pub handle: CaptureHandle, | ||
| /// Existing silence-gate threshold; zero disables gating. | ||
| pub gate_threshold: f32, | ||
| } | ||
| /// Prepared WAV held inside the module for bounded reads. | ||
| #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub struct AudioOutput { | ||
| /// Caller-known recording lease, released with `ReleaseAudioOutput`. | ||
| pub handle: CaptureHandle, | ||
| /// WAV bytes available. | ||
| pub length: usize, | ||
| } | ||
| /// Read a bounded slice of a held audio output. | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct ReadAudioRequest { | ||
| /// Output lease. | ||
| pub handle: CaptureHandle, | ||
| /// Byte offset. | ||
| pub offset: usize, | ||
| /// Maximum bytes to read. | ||
| pub length: usize, | ||
| } | ||
| /// Capture failure for product presentation, never a telemetry payload. | ||
| #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| #[serde(tag = "code", content = "detail", rename_all = "snake_case")] | ||
| pub enum CaptureError { | ||
| /// Permission was not explicitly granted. | ||
| PermissionDenied, | ||
| /// Startup was canceled before its resource was delivered. | ||
| Cancelled, | ||
| /// Capture shutdown has closed this module instance. | ||
| Closed, | ||
| /// A recording or finishing operation already owns the device slot. | ||
| Busy, | ||
| /// The recording or output handle is unknown or released. | ||
| UnknownHandle, | ||
| /// Output storage or read bounds were exceeded. | ||
| LimitExceeded, | ||
| /// Invalid processing parameters. | ||
| InvalidParameters, | ||
| /// Device or processing failure; contains product-facing native detail. | ||
| Device(String), | ||
| } | ||
| /// Typed terminal result; provider detail must not enter telemetry. | ||
| pub type CaptureResult<T> = Result<T, CaptureError>; | ||
|
senamakel marked this conversation as resolved.
senamakel marked this conversation as resolved.
|
||
|
|
||
| #[cfg(test)] | ||
| #[path = "mod_tests.rs"] | ||
| mod tests; | ||
|
|
||
| /// Native device format, reported once on stream startup. | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub struct CaptureFormat { | ||
| /// Native sample rate. | ||
| pub source_rate: u32, | ||
| /// Interleaved channel count. | ||
| pub channels: u16, | ||
| } | ||
| /// Bounded native callback buffer, processed through module audio operations. | ||
| #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | ||
| pub struct RawChunk { | ||
|
senamakel marked this conversation as resolved.
|
||
| /// Interleaved native f32 samples. | ||
| pub samples: Vec<f32>, | ||
| } | ||
| /// Opaque continuous capture lease and its native format. | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct CaptureStream { | ||
| /// Lease consumed by `CaptureStop`. | ||
| pub handle: CaptureHandle, | ||
| /// Native device format. | ||
| pub format: CaptureFormat, | ||
| } | ||
| /// Bounded batch read request. | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct CapturePollRequest { | ||
|
senamakel marked this conversation as resolved.
|
||
| /// Continuous capture lease. | ||
| pub handle: CaptureHandle, | ||
| /// At most two callback buffers per call. | ||
| pub max_chunks: usize, | ||
|
senamakel marked this conversation as resolved.
|
||
| } | ||
| /// Ordered bounded chunks, with terminal channel status. | ||
| #[derive(Debug, Clone, Default, Serialize, Deserialize)] | ||
| pub struct CaptureBatch { | ||
| /// Retained native chunks in capture order. | ||
| pub chunks: Vec<RawChunk>, | ||
| /// Native capture ended; stop releases the lease and reports its result. | ||
| pub closed: bool, | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| //! Capture vocabulary preserves explicit permission and opaque lease shapes. | ||
| use super::*; | ||
| #[test] | ||
| fn permission_defaults_to_denial_and_handle_is_opaque() -> Result<(), serde_json::Error> { | ||
| assert_eq!( | ||
| MicrophonePermission::default(), | ||
| MicrophonePermission::Denied | ||
| ); | ||
| assert_eq!( | ||
| serde_json::to_value(MicrophonePermission::Granted)?, | ||
| serde_json::json!("granted") | ||
| ); | ||
| let handle = CaptureHandle("opaque".into()); | ||
| assert_eq!(serde_json::to_value(&handle)?, serde_json::json!("opaque")); | ||
| assert_eq!( | ||
| serde_json::from_value::<CaptureHandle>(serde_json::json!("opaque"))?, | ||
| handle | ||
| ); | ||
| Ok(()) | ||
| } | ||
| #[test] | ||
| fn native_faults_are_structured_product_results() -> Result<(), serde_json::Error> { | ||
| let fault = CaptureError::Device("fixture".into()); | ||
| let wire = serde_json::to_value(&fault)?; | ||
| assert_eq!( | ||
| wire, | ||
| serde_json::json!({"code":"device","detail":"fixture"}) | ||
| ); | ||
| assert_eq!(serde_json::from_value::<CaptureError>(wire)?, fault); | ||
| Ok(()) | ||
| } | ||
|
|
||
| #[test] | ||
| fn continuous_format_and_batches_preserve_native_samples() -> Result<(), serde_json::Error> { | ||
| let stream = CaptureStream { | ||
| handle: CaptureHandle("lease".into()), | ||
| format: CaptureFormat { | ||
| source_rate: 48_000, | ||
| channels: 2, | ||
| }, | ||
| }; | ||
| let wire = serde_json::to_value(&stream)?; | ||
| let decoded: CaptureStream = serde_json::from_value(wire)?; | ||
| assert_eq!(decoded.format, stream.format); | ||
| let batch = CaptureBatch { | ||
| chunks: vec![RawChunk { | ||
| samples: vec![0.25, -0.25], | ||
| }], | ||
| closed: false, | ||
| }; | ||
| let decoded: CaptureBatch = serde_json::from_value(serde_json::to_value(&batch)?)?; | ||
| assert_eq!(decoded.chunks, batch.chunks); | ||
| assert!(!decoded.closed); | ||
| Ok(()) | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.