add stopping state, handle dma drain, improve cli readability (state) by moving state to shared
This commit is contained in:
@@ -3,9 +3,13 @@ name = "shared"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
std = []
|
||||
serde = ["dep:serde"]
|
||||
|
||||
[dependencies]
|
||||
bytemuck = { version = "1.25.0", features = ["derive"] }
|
||||
defmt = "1.1.1"
|
||||
deku = { version = "0.20.3", default-features = false }
|
||||
num_enum = { version = "0.7.6", default-features = false }
|
||||
serde = { version = "1.0.228", optional = true, features = ["derive"] }
|
||||
usbd-hid = { version = "0.10.0" }
|
||||
|
||||
+124
-2
@@ -1,14 +1,120 @@
|
||||
#![no_std]
|
||||
|
||||
use bytemuck::NoUninit;
|
||||
use num_enum::TryFromPrimitive;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Copy, Debug, NoUninit, Eq, PartialEq, TryFromPrimitive)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[repr(u8)]
|
||||
pub enum AudioState {
|
||||
/// Knowingly stopped, ie. AltSetting=0. DAC muted, I2S disabled.
|
||||
///
|
||||
/// AltSetting = 1 -> ARMED
|
||||
Stopped = 0,
|
||||
/// Waiting for data. DAC muted, I2S running sending 0s (FIFO not serviced).
|
||||
///
|
||||
/// USB OUT data packet -> ARMED
|
||||
/// AltSetting = 0 -> STOPPED
|
||||
Armed = 1,
|
||||
/// Filling the buffer before playback starts. Feedback does not run,
|
||||
/// playout does not start draining the queue. Gets us better feedback
|
||||
/// behaviour and a full buffer without a feedback rate spike at startup.
|
||||
///
|
||||
/// queue reaches <QUEUE_RUNNING_UP> -> RUNNING
|
||||
/// AltSetting = 0 -> STOPPED
|
||||
///
|
||||
Prefill = 2,
|
||||
/// Normal running state. Start servicing FIFO and begin playing out from the buffer.
|
||||
///
|
||||
/// queue reaches <QUEUE_RUNNING_DOWN> -> DRAINING
|
||||
/// AltSetting = 0 -> DRAINING
|
||||
Running = 3,
|
||||
/// The queue is low. We will continue playout.
|
||||
///
|
||||
/// queue is empty && altSetting 1 -> NODATA
|
||||
/// queue is empty && altSetting 0 -> STOPPED
|
||||
/// queue reaches <QUEUE_RUNNING_UP> && altSetting 1 -> RUNNING
|
||||
LowData = 4,
|
||||
/// There is no data in the queue. We will count underflows for a while, send 0s, and hope the host comes back.
|
||||
///
|
||||
/// countdown reaches DATA_TIMEOUT -> STOPPED
|
||||
/// AltSetting = 0 -> STOPPED
|
||||
NoData = 5,
|
||||
/// The host has asked us to stop (altSetting 0), but we need to play out the remaining buffer
|
||||
///
|
||||
/// queue is empty -> STOPPED
|
||||
Stopping = 6,
|
||||
}
|
||||
|
||||
impl Default for AudioState {
|
||||
fn default() -> Self {
|
||||
AudioState::Stopped
|
||||
}
|
||||
}
|
||||
impl defmt::Format for AudioState {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
defmt::write!(
|
||||
fmt,
|
||||
"{}",
|
||||
match self {
|
||||
Self::Stopped => "Stopped",
|
||||
Self::Armed => "Armed",
|
||||
Self::Prefill => "Prefill",
|
||||
Self::Running => "Running",
|
||||
Self::LowData => "Draining",
|
||||
Self::NoData => "NoData",
|
||||
Self::Stopping => "Stopping",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for AudioState {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::Stopped => "Stopped",
|
||||
Self::Armed => "Armed",
|
||||
Self::Prefill => "Prefill",
|
||||
Self::Running => "Running",
|
||||
Self::LowData => "Draining",
|
||||
Self::NoData => "NoData",
|
||||
Self::Stopping => "Stopping",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct AudioTelemetrySnapshot {
|
||||
pub state: AudioState,
|
||||
pub average_buffer_fill: u16,
|
||||
pub frame_count: u32,
|
||||
pub dac_underflow_count: u16,
|
||||
pub usb_underflow_count: u16,
|
||||
pub dac_overflow_count: u16,
|
||||
pub p: i32,
|
||||
pub i: i32,
|
||||
pub fb: i32,
|
||||
}
|
||||
|
||||
pub mod hid {
|
||||
use deku::DekuRead;
|
||||
use usbd_hid::descriptor::generator_prelude::*;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use usbd_hid::descriptor::generator_prelude::*;
|
||||
|
||||
use crate::{AudioState, AudioTelemetrySnapshot};
|
||||
|
||||
#[derive(DekuRead)]
|
||||
#[deku(endian = "little")]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[gen_hid_descriptor(
|
||||
(collection = APPLICATION, usage_page = VENDOR_DEFINED_START, usage = 0x01, ) = {
|
||||
state=input;
|
||||
@@ -37,4 +143,20 @@ pub mod hid {
|
||||
pub fb: i32,
|
||||
pub integrator: i32,
|
||||
}
|
||||
|
||||
impl From<AudioTelemetryReport> for AudioTelemetrySnapshot {
|
||||
fn from(value: AudioTelemetryReport) -> Self {
|
||||
AudioTelemetrySnapshot {
|
||||
state: AudioState::try_from(value.state).expect("Invalid AudioState"),
|
||||
average_buffer_fill: value.average_buffer_fill,
|
||||
frame_count: i32::cast_unsigned(value.frame_count), // on firmware side is usize == u32
|
||||
dac_underflow_count: value.dac_underflow_count,
|
||||
usb_underflow_count: value.usb_underflow_count,
|
||||
dac_overflow_count: value.dac_overflow_count,
|
||||
p: value.p,
|
||||
i: value.i,
|
||||
fb: value.fb,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user