feat: rms + peak meters

This commit is contained in:
2026-08-01 12:45:58 +02:00
parent 27821c1866
commit e2fee36cdb
5 changed files with 26 additions and 39 deletions
+1 -1
View File
@@ -1868,7 +1868,7 @@ Sine Wave:
|---|---|---|---|
| `Byte 1-36` | 12 meter channels | 3 bytes × 12 | [Enc §8.1 Meter Level](#81-meter-level) |
| `Byte 37` | Input clip indicator | 1 byte bitmask | Same OR bitmask pattern as [Enc §4.1](#41-matrix-input-mask) |
| `Byte 38` | Output clip indicator | 1 byte bitmask | Same OR bitmask pattern as [Enc §4.1](#41-matrix-input-mask) |
| `Byte 38` | Output limit indicator | 1 byte bitmask | Same OR bitmask pattern as [Enc §4.1](#41-matrix-input-mask) |
**Example — Query**
+21 -32
View File
@@ -1,10 +1,7 @@
use crate::{
common::errors::ProtocolError,
dsp408::{
protocol::{
constants::{METERS_HIGH_STEP, METERS_LOW_STEP, METERS_SPLIT, METERS_SPLIT_LEVEL_DB},
frame::Frame,
},
protocol::frame::Frame,
types::{
Acknowledgement, AuthenticationResult, ConfigChunk, CurrentPreset, DeviceFlags,
DeviceInfo, DeviceResponseType, HandshakeAck, Meter, Meters, ModifiedPreset,
@@ -166,43 +163,35 @@ pub fn map_response(frame: &Frame) -> Result<DeviceResponseType, ProtocolError>
// Command `0x40` (Get Level Meters)
if cmd == 0x40 {
fn float_to_db(v: f32) -> f32 {
40.0 * v.abs().max(1e-12).log10()
}
if payload.len() != 38 {
return Err(ProtocolError::new("Not enough bytes for meter levels"));
}
// Bytes 0..36 = 12 × 3-byte meter levels
let levels: Vec<f32> = (0..36)
let channels: Vec<Meter> = (0..36)
.step_by(3)
.map(|i| decode_float16(payload[i], payload[i + 1]).unwrap_or_default())
.map(|f| {
if f < METERS_SPLIT {
METERS_SPLIT_LEVEL_DB + (f - METERS_SPLIT) / METERS_LOW_STEP
} else {
METERS_SPLIT_LEVEL_DB + (f - METERS_SPLIT) / METERS_HIGH_STEP
}
})
.collect();
// Byte 36 = input clip bitmask
let input_clip_mask = payload[36];
// Byte 37 = output clip bitmask
let output_clip_mask = payload[37];
let channels = levels
.into_iter()
.enumerate()
.map(|(index, level)| {
.map(|(index, i)| {
let rms = decode_float16(payload[i], payload[i + 1]).unwrap_or_default();
let peak = decode_float16(payload[i + 2], payload[i + 1]).unwrap_or_default();
let rms_db = float_to_db(rms);
let peak_db = float_to_db(peak);
let clip = if index < 4 {
// InA..InD
input_clip_mask & (1 << index) != 0
payload[36] & (1 << index) != 0
} else {
// Out1..Out8
let output_index = index - 4;
output_clip_mask & (1 << output_index) != 0
payload[37] & (1 << (index - 4)) != 0
};
Meter { level, clip }
Meter {
rms: rms_db,
peak: peak_db,
clip,
}
})
.collect();
@@ -438,7 +427,7 @@ mod tests {
assert_eq!(m.channels.len(), 12);
for channel in m.channels {
assert!(channel.level.is_finite());
assert!(channel.rms.is_finite());
}
}
other => panic!("unexpected {:?}", other),
-5
View File
@@ -77,8 +77,3 @@ pub fn output_link_mask(channel: u8) -> Option<u8> {
_ => None,
}
}
pub const METERS_SPLIT: f32 = 1.0;
pub const METERS_SPLIT_LEVEL_DB: f32 = -18.0;
pub const METERS_LOW_STEP: f32 = 0.0104;
pub const METERS_HIGH_STEP: f32 = 0.0208;
+2 -1
View File
@@ -221,7 +221,8 @@ pub struct AuthenticationResult {
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Meter {
pub level: f32,
pub rms: f32,
pub peak: f32,
pub clip: bool,
}
+2
View File
@@ -32,3 +32,5 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
// 10020100 27 40 263df7 263dfd 263dfd 263dfd 063d8d 063d63 263dcf 263dfd 263df6 263dcf 263dcf 263dcf 0300 10038b