synfx_dsp/
rand.rs

1// Copyright (c) 2021-2022 Weird Constructor <weirdconstructor@gmail.com>
2// This file is a part of synfx-dsp. Released under GPL-3.0-or-later.
3// See README.md and COPYING for details.
4
5//! Random number generators and utilities.
6/// Be aware that some might need some initialization function!
7use std::cell::RefCell;
8
9/// A wavetable filled entirely with white noise.
10/// Don't forget to call [init_white_noise_tab] before using it.
11static mut WHITE_NOISE_TAB: [f64; 1024] = [0.0; 1024];
12
13#[allow(rustdoc::private_intra_doc_links)]
14/// Initializes [WHITE_NOISE_TAB]
15pub fn init_white_noise_tab() {
16    let mut rng = RandGen::new();
17    unsafe {
18        for i in 0..WHITE_NOISE_TAB.len() {
19            WHITE_NOISE_TAB[i as usize] = rng.next_open01();
20        }
21    }
22}
23
24#[derive(Debug, Copy, Clone, PartialEq)]
25/// Random number generator based on xoroshiro128.
26/// Requires two internal state variables.
27/// You may prefer [SplitMix64] or [Rng] which only use one `u64` as state.
28pub struct RandGen {
29    r: [u64; 2],
30}
31
32// Taken from xoroshiro128 crate under MIT License
33// Implemented by Matthew Scharley (Copyright 2016)
34// https://github.com/mscharley/rust-xoroshiro128
35/// Given the mutable `state` generates the next pseudo random number.
36pub fn next_xoroshiro128(state: &mut [u64; 2]) -> u64 {
37    let s0: u64 = state[0];
38    let mut s1: u64 = state[1];
39    let result: u64 = s0.wrapping_add(s1);
40
41    s1 ^= s0;
42    state[0] = s0.rotate_left(55) ^ s1 ^ (s1 << 14); // a, b
43    state[1] = s1.rotate_left(36); // c
44
45    result
46}
47
48// Taken from rand::distributions
49// Licensed under the Apache License, Version 2.0
50// Copyright 2018 Developers of the Rand project.
51/// Maps any `u64` to a `f64` in the open interval `[0.0, 1.0)`.
52pub fn u64_to_open01(u: u64) -> f64 {
53    use core::f64::EPSILON;
54    let float_size = std::mem::size_of::<f64>() as u32 * 8;
55    let fraction = u >> (float_size - 52);
56    let exponent_bits: u64 = (1023 as u64) << 52;
57    f64::from_bits(fraction | exponent_bits) - (1.0 - EPSILON / 2.0)
58}
59
60impl RandGen {
61    pub fn new() -> Self {
62        RandGen { r: [0x193a6754a8a7d469, 0x97830e05113ba7bb] }
63    }
64
65    /// Next random unsigned 64bit integer.
66    pub fn next(&mut self) -> u64 {
67        next_xoroshiro128(&mut self.r)
68    }
69
70    /// Next random float between `[0.0, 1.0)`.
71    pub fn next_open01(&mut self) -> f64 {
72        u64_to_open01(self.next())
73    }
74}
75
76#[derive(Debug, Copy, Clone)]
77/// Random number generator based on [SplitMix64].
78/// Requires two internal state variables. You may prefer [SplitMix64] or [Rng].
79pub struct Rng {
80    sm: SplitMix64,
81}
82
83impl Rng {
84    pub fn new() -> Self {
85        Self { sm: SplitMix64::new(0x193a67f4a8a6d769) }
86    }
87
88    pub fn seed(&mut self, seed: u64) {
89        self.sm = SplitMix64::new(seed);
90    }
91
92    #[inline]
93    pub fn next(&mut self) -> f32 {
94        self.sm.next_open01() as f32
95    }
96
97    #[inline]
98    pub fn next_u64(&mut self) -> u64 {
99        self.sm.next_u64()
100    }
101}
102
103thread_local! {
104    static GLOBAL_RNG: RefCell<Rng> = RefCell::new(Rng::new());
105}
106
107#[inline]
108pub fn rand_01() -> f32 {
109    GLOBAL_RNG.with(|r| r.borrow_mut().next())
110}
111
112#[inline]
113pub fn rand_u64() -> u64 {
114    GLOBAL_RNG.with(|r| r.borrow_mut().next_u64())
115}
116
117// Copyright 2018 Developers of the Rand project.
118//
119// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
120// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
121// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
122// option. This file may not be copied, modified, or distributed
123// except according to those terms.
124//- splitmix64 (http://xoroshiro.di.unimi.it/splitmix64.c)
125//
126/// A splitmix64 random number generator.
127///
128/// The splitmix algorithm is not suitable for cryptographic purposes, but is
129/// very fast and has a 64 bit state.
130///
131/// The algorithm used here is translated from [the `splitmix64.c`
132/// reference source code](http://xoshiro.di.unimi.it/splitmix64.c) by
133/// Sebastiano Vigna. For `next_u32`, a more efficient mixing function taken
134/// from [`dsiutils`](http://dsiutils.di.unimi.it/) is used.
135#[derive(Debug, Copy, Clone)]
136pub struct SplitMix64(pub u64);
137
138/// Internal random constant for [SplitMix64].
139const PHI: u64 = 0x9e3779b97f4a7c15;
140
141impl SplitMix64 {
142    pub fn new(seed: u64) -> Self {
143        Self(seed)
144    }
145    pub fn new_from_i64(seed: i64) -> Self {
146        Self::new(u64::from_be_bytes(seed.to_be_bytes()))
147    }
148
149    pub fn new_time_seed() -> Self {
150        use std::time::SystemTime;
151
152        match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
153            Ok(n) => Self::new(n.as_secs() as u64),
154            Err(_) => Self::new(123456789),
155        }
156    }
157
158    #[inline]
159    pub fn next_u64(&mut self) -> u64 {
160        self.0 = self.0.wrapping_add(PHI);
161        let mut z = self.0;
162        z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
163        z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
164        z ^ (z >> 31)
165    }
166
167    #[inline]
168    pub fn next_i64(&mut self) -> i64 {
169        i64::from_be_bytes(self.next_u64().to_be_bytes())
170    }
171
172    #[inline]
173    pub fn next_open01(&mut self) -> f64 {
174        u64_to_open01(self.next_u64())
175    }
176}