Coverage Report

Created: 2026-05-16 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/dasp_sample-0.11.0/src/lib.rs
Line
Count
Source
1
//! Use the [**Sample**](./trait.Sample.html) trait to remain generic over sample types, easily
2
//! access sample type conversions, apply basic audio operations and more.
3
//!
4
//! The **Sample** trait is the core abstraction throughout dasp on which most other abstractions
5
//! are based.
6
7
#![cfg_attr(not(feature = "std"), no_std)]
8
#![cfg_attr(not(feature = "std"), feature(core_intrinsics))]
9
10
#[cfg(not(feature = "std"))]
11
extern crate alloc;
12
13
pub use conv::{Duplex, FromSample, ToSample};
14
pub use types::{I24, I48, U24, U48};
15
16
pub mod conv;
17
mod ops;
18
pub mod types;
19
20
/// A trait for working generically across different **Sample** format types.
21
///
22
/// Provides methods for converting to and from any type that implements the
23
/// [`FromSample`](./trait.FromSample.html) trait and provides methods for performing signal
24
/// amplitude addition and multiplication.
25
///
26
/// # Example
27
///
28
/// ```rust
29
/// use dasp_sample::{I24, Sample};
30
///
31
/// fn main() {
32
///     assert_eq!((-1.0).to_sample::<u8>(), 0);
33
///     assert_eq!(0.0.to_sample::<u8>(), 128);
34
///     assert_eq!(0i32.to_sample::<u32>(), 2_147_483_648);
35
///     assert_eq!(I24::new(0).unwrap(), Sample::from_sample(0.0));
36
///     assert_eq!(0.0, Sample::EQUILIBRIUM);
37
/// }
38
/// ```
39
pub trait Sample: Copy + Clone + PartialOrd + PartialEq {
40
    /// When summing two samples of a signal together, it is necessary for both samples to be
41
    /// represented in some signed format. This associated `Addition` type represents the format to
42
    /// which `Self` should be converted for optimal `Addition` performance.
43
    ///
44
    /// For example, u32's optimal `Addition` type would be i32, u8's would be i8, f32's would be
45
    /// f32, etc.
46
    ///
47
    /// Specifying this as an associated type allows us to automatically determine the optimal,
48
    /// lossless Addition format type for summing any two unique `Sample` types together.
49
    ///
50
    /// As a user of the `sample` crate, you will never need to be concerned with this type unless
51
    /// you are defining your own unique `Sample` type(s).
52
    type Signed: SignedSample + Duplex<Self>;
53
54
    /// When multiplying two samples of a signal together, it is necessary for both samples to be
55
    /// represented in some signed, floating-point format. This associated `Multiplication` type
56
    /// represents the format to which `Self` should be converted for optimal `Multiplication`
57
    /// performance.
58
    ///
59
    /// For example, u32's optimal `Multiplication` type would be f32, u64's would be f64, i8's
60
    /// would be f32, etc.
61
    ///
62
    /// Specifying this as an associated type allows us to automatically determine the optimal,
63
    /// lossless Multiplication format type for multiplying any two unique `Sample` types together.
64
    ///
65
    /// As a user of the `sample` crate, you will never need to be concerned with this type unless
66
    /// you are defining your own unique `Sample` type(s).
67
    type Float: FloatSample + Duplex<Self>;
68
69
    /// The equilibrium value for the wave that this `Sample` type represents. This is normally the
70
    /// value that is equal distance from both the min and max ranges of the sample.
71
    ///
72
    /// # Example
73
    ///
74
    /// ```rust
75
    /// use dasp_sample::Sample;
76
    ///
77
    /// fn main() {
78
    ///     assert_eq!(0.0, f32::EQUILIBRIUM);
79
    ///     assert_eq!(0, i32::EQUILIBRIUM);
80
    ///     assert_eq!(128, u8::EQUILIBRIUM);
81
    ///     assert_eq!(32_768_u16, Sample::EQUILIBRIUM);
82
    /// }
83
    /// ```
84
    ///
85
    /// **Note:** This will likely be changed to an "associated const" if the feature lands.
86
    const EQUILIBRIUM: Self;
87
88
    /// The multiplicative identity of the signal.
89
    ///
90
    /// In other words: A value which when used to scale/multiply the amplitude or frequency of a
91
    /// signal, returns the same signal.
92
    ///
93
    /// This is useful as a default, non-affecting amplitude or frequency multiplier.
94
    ///
95
    /// # Example
96
    ///
97
    /// ```rust
98
    /// use dasp_sample::{Sample, U48};
99
    ///
100
    /// fn main() {
101
    ///     assert_eq!(1.0, f32::IDENTITY);
102
    ///     assert_eq!(1.0, i8::IDENTITY);
103
    ///     assert_eq!(1.0, u8::IDENTITY);
104
    ///     assert_eq!(1.0, U48::IDENTITY);
105
    /// }
106
    /// ```
107
    const IDENTITY: Self::Float = <Self::Float as FloatSample>::IDENTITY;
108
109
    /// Convert `self` to any type that implements `FromSample<Self>`.
110
    ///
111
    /// Find more details on type-specific conversion ranges and caveats in the `conv` module.
112
    ///
113
    /// # Example
114
    ///
115
    /// ```rust
116
    /// use dasp_sample::Sample;
117
    ///
118
    /// fn main() {
119
    ///     assert_eq!(0.0.to_sample::<i32>(), 0);
120
    ///     assert_eq!(0.0.to_sample::<u8>(), 128);
121
    ///     assert_eq!((-1.0).to_sample::<u8>(), 0);
122
    /// }
123
    /// ```
124
    #[inline]
125
0
    fn to_sample<S>(self) -> S
126
0
    where
127
0
        Self: ToSample<S>,
128
    {
129
0
        self.to_sample_()
130
0
    }
131
132
    /// Create a `Self` from any type that implements `ToSample<Self>`.
133
    ///
134
    /// Find more details on type-specific conversion ranges and caveats in the `conv` module.
135
    ///
136
    /// # Example
137
    ///
138
    /// ```rust
139
    /// use dasp_sample::{Sample, I24};
140
    ///
141
    /// fn main() {
142
    ///     assert_eq!(f32::from_sample(128_u8), 0.0);
143
    ///     assert_eq!(i8::from_sample(-1.0), -128);
144
    ///     assert_eq!(I24::from_sample(0.0), I24::new(0).unwrap());
145
    /// }
146
    /// ```
147
148
    #[inline]
149
0
    fn from_sample<S>(s: S) -> Self
150
0
    where
151
0
        Self: FromSample<S>,
152
    {
153
0
        FromSample::from_sample_(s)
154
0
    }
155
156
    /// Converts `self` to the equivalent `Sample` in the associated `Signed` format.
157
    ///
158
    /// This is a simple wrapper around `Sample::to_sample` which may provide extra convenience in
159
    /// some cases, particularly for assisting type inference.
160
    ///
161
    /// # Example
162
    ///
163
    /// ```rust
164
    /// use dasp_sample::Sample;
165
    ///
166
    /// fn main() {
167
    ///     assert_eq!(128_u8.to_signed_sample(), 0i8);
168
    /// }
169
    /// ```
170
0
    fn to_signed_sample(self) -> Self::Signed {
171
0
        self.to_sample()
172
0
    }
173
174
    /// Converts `self` to the equivalent `Sample` in the associated `Float` format.
175
    ///
176
    /// This is a simple wrapper around `Sample::to_sample` which may provide extra convenience in
177
    /// some cases, particularly for assisting type inference.
178
    ///
179
    /// # Example
180
    ///
181
    /// ```rust
182
    /// use dasp_sample::Sample;
183
    ///
184
    /// fn main() {
185
    ///     assert_eq!(128_u8.to_float_sample(), 0.0);
186
    /// }
187
    /// ```
188
0
    fn to_float_sample(self) -> Self::Float {
189
0
        self.to_sample()
190
0
    }
191
192
    /// Adds (or "offsets") the amplitude of the `Sample` by the given signed amplitude.
193
    ///
194
    /// `Self` will be converted to `Self::Signed`, the addition will occur and then the result
195
    /// will be converted back to `Self`. These conversions allow us to correctly handle the
196
    /// addition of unsigned signal formats.
197
    ///
198
    /// # Example
199
    ///
200
    /// ```rust
201
    /// use dasp_sample::Sample;
202
    ///
203
    /// fn main() {
204
    ///     assert_eq!(0.25.add_amp(0.5), 0.75);
205
    ///     assert_eq!(192u8.add_amp(-128), 64);
206
    /// }
207
    /// ```
208
    #[inline]
209
0
    fn add_amp(self, amp: Self::Signed) -> Self {
210
0
        let self_s = self.to_signed_sample();
211
0
        (self_s + amp).to_sample()
212
0
    }
213
214
    /// Multiplies (or "scales") the amplitude of the `Sample` by the given float amplitude.
215
    ///
216
    /// - `amp` > 1.0 amplifies the sample.
217
    /// - `amp` < 1.0 attenuates the sample.
218
    /// - `amp` == 1.0 yields the same sample.
219
    /// - `amp` == 0.0 yields the `Sample::EQUILIBRIUM`.
220
    ///
221
    /// `Self` will be converted to `Self::Float`, the multiplication will occur and then the
222
    /// result will be converted back to `Self`. These conversions allow us to correctly handle the
223
    /// multiplication of integral signal formats.
224
    ///
225
    /// # Example
226
    ///
227
    /// ```rust
228
    /// use dasp_sample::Sample;
229
    ///
230
    /// fn main() {
231
    ///     assert_eq!(64_i8.mul_amp(0.5), 32);
232
    ///     assert_eq!(0.5.mul_amp(-2.0), -1.0);
233
    ///     assert_eq!(64_u8.mul_amp(0.0), 128);
234
    /// }
235
    /// ```
236
    #[inline]
237
0
    fn mul_amp(self, amp: Self::Float) -> Self {
238
0
        let self_f = self.to_float_sample();
239
0
        (self_f * amp).to_sample()
240
0
    }
241
}
242
243
/// A macro used to simplify the implementation of `Sample`.
244
macro_rules! impl_sample {
245
    ($($T:ty:
246
       Signed: $Addition:ty,
247
       Float: $Modulation:ty,
248
       EQUILIBRIUM: $EQUILIBRIUM:expr),*) =>
249
    {
250
        $(
251
            impl Sample for $T {
252
                type Signed = $Addition;
253
                type Float = $Modulation;
254
                const EQUILIBRIUM: Self = $EQUILIBRIUM;
255
            }
256
        )*
257
    }
258
}
259
260
// Expands to `Sample` implementations for all of the following types.
261
impl_sample! {
262
    i8:  Signed: i8,  Float: f32, EQUILIBRIUM: 0,
263
    i16: Signed: i16, Float: f32, EQUILIBRIUM: 0,
264
    I24: Signed: I24, Float: f32, EQUILIBRIUM: types::i24::EQUILIBRIUM,
265
    i32: Signed: i32, Float: f32, EQUILIBRIUM: 0,
266
    I48: Signed: I48, Float: f64, EQUILIBRIUM: types::i48::EQUILIBRIUM,
267
    i64: Signed: i64, Float: f64, EQUILIBRIUM: 0,
268
    u8:  Signed: i8,  Float: f32, EQUILIBRIUM: 128,
269
    u16: Signed: i16, Float: f32, EQUILIBRIUM: 32_768,
270
    U24: Signed: i32, Float: f32, EQUILIBRIUM: types::u24::EQUILIBRIUM,
271
    u32: Signed: i32, Float: f32, EQUILIBRIUM: 2_147_483_648,
272
    U48: Signed: i64, Float: f64, EQUILIBRIUM: types::u48::EQUILIBRIUM,
273
    u64: Signed: i64, Float: f64, EQUILIBRIUM: 9_223_372_036_854_775_808,
274
    f32: Signed: f32, Float: f32, EQUILIBRIUM: 0.0,
275
    f64: Signed: f64, Float: f64, EQUILIBRIUM: 0.0
276
}
277
278
/// Integral and floating-point **Sample** format types whose equilibrium is at 0.
279
///
280
/// **Sample**s often need to be converted to some mutual **SignedSample** type for signal
281
/// addition.
282
pub trait SignedSample:
283
    Sample<Signed = Self>
284
    + core::ops::Add<Output = Self>
285
    + core::ops::Sub<Output = Self>
286
    + core::ops::Neg<Output = Self>
287
{
288
}
289
macro_rules! impl_signed_sample { ($($T:ty)*) => { $( impl SignedSample for $T {} )* } }
290
impl_signed_sample!(i8 i16 I24 i32 I48 i64 f32 f64);
291
292
/// Sample format types represented as floating point numbers.
293
///
294
/// **Sample**s often need to be converted to some mutual **FloatSample** type for signal scaling
295
/// and modulation.
296
pub trait FloatSample:
297
    Sample<Signed = Self, Float = Self>
298
    + SignedSample
299
    + core::ops::Mul<Output = Self>
300
    + core::ops::Div<Output = Self>
301
    + Duplex<f32>
302
    + Duplex<f64>
303
{
304
    /// Represents the multiplicative identity of the floating point signal.
305
    const IDENTITY: Self;
306
    /// Calculate the square root of `Self`.
307
    fn sample_sqrt(self) -> Self;
308
}
309
310
impl FloatSample for f32 {
311
    const IDENTITY: Self = 1.0;
312
    #[inline]
313
0
    fn sample_sqrt(self) -> Self {
314
0
        ops::f32::sqrt(self)
315
0
    }
316
}
317
318
impl FloatSample for f64 {
319
    const IDENTITY: Self = 1.0;
320
    #[inline]
321
0
    fn sample_sqrt(self) -> Self {
322
0
        ops::f64::sqrt(self)
323
0
    }
324
}