initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
||||
16
Cargo.lock
generated
Normal file
16
Cargo.lock
generated
Normal file
@ -0,0 +1,16 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "hound"
|
||||
version = "3.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f"
|
||||
|
||||
[[package]]
|
||||
name = "rdsp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hound",
|
||||
]
|
||||
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "rdsp"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
hound = "3.5.1"
|
||||
152
src/main.rs
Normal file
152
src/main.rs
Normal file
@ -0,0 +1,152 @@
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
fs::File,
|
||||
io::Read,
|
||||
ops::{Add, Div, Mul, Sub},
|
||||
};
|
||||
|
||||
fn map<T>(input: T, in_min: T, in_max: T, out_min: T, out_max: T) -> T
|
||||
where
|
||||
T: Clone + Add<Output = T> + Mul<Output = T> + Sub<Output = T> + Div<Output = T>,
|
||||
{
|
||||
((input - in_min.clone()) / (in_max - in_min)) * (out_max - out_min.clone()) + out_min
|
||||
}
|
||||
|
||||
struct Nco {
|
||||
// Phase of NCO
|
||||
theta: u32, // 0 <=> 0, 0xFFFFFFFF <=> 2pi
|
||||
dtheta: u32, // Dtheta = freq : f = dtheta/dt
|
||||
}
|
||||
|
||||
impl Nco {
|
||||
pub fn new(freq: f32) -> Self {
|
||||
let mut nco = Nco {
|
||||
theta: 0,
|
||||
dtheta: 0,
|
||||
};
|
||||
nco.set_frequency(freq);
|
||||
nco
|
||||
}
|
||||
|
||||
// Sets freq, freq in radian per sample
|
||||
pub fn set_frequency(&mut self, freq: f32) {
|
||||
if freq < 0.0 {
|
||||
self.dtheta = map(2. * PI - freq, 0., 2. * PI, 0., 0xFFFFFFFFu32 as f32).floor() as u32;
|
||||
} else {
|
||||
self.dtheta = map(freq, 0., 2. * PI, 0., 0xFFFFFFFFu32 as f32).floor() as u32;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjusts freq, freq in radian per sample
|
||||
pub fn adjust_frequency(&mut self, freq_off: f32) {
|
||||
self.set_frequency(self.get_frequency() + freq_off);
|
||||
}
|
||||
|
||||
pub fn get_frequency(&self) -> f32 {
|
||||
map(self.dtheta as f32, 0., 0xFFFFFFFFu32 as f32, 0., 2. * PI)
|
||||
}
|
||||
|
||||
pub fn step(&mut self) {
|
||||
let bef = self.theta;
|
||||
self.theta = self.theta.overflowing_add(self.dtheta).0;
|
||||
println!("{}", bef.overflowing_sub(self.theta).0);
|
||||
}
|
||||
|
||||
pub fn step_n(&mut self, n: u32) {
|
||||
self.theta = self
|
||||
.theta
|
||||
.overflowing_add(self.dtheta.overflowing_mul(n).0)
|
||||
.0;
|
||||
}
|
||||
|
||||
pub fn sin(&self) -> f32 {
|
||||
map(self.theta as f32, 0., 0xFFFFFFFFu32 as f32, 0., 2. * PI).sin()
|
||||
}
|
||||
|
||||
pub fn cos(&self) -> f32 {
|
||||
map(self.theta as f32, 0., 0xFFFFFFFFu32 as f32, 0., 2. * PI).cos()
|
||||
}
|
||||
}
|
||||
|
||||
struct BFSKMod<'a, T: Iterator<Item = bool>> {
|
||||
samples_per_bit: u32,
|
||||
bandwidth: f32,
|
||||
bit_stream: &'a mut T,
|
||||
|
||||
// State
|
||||
oscillator: Nco,
|
||||
sample_index: u32,
|
||||
}
|
||||
|
||||
impl<'a, T> BFSKMod<'a, T>
|
||||
where
|
||||
T: Iterator<Item = bool>,
|
||||
{
|
||||
pub fn new(samples_per_bit: u32, bandwidth: f32, bit_stream: &'a mut T) -> Self {
|
||||
BFSKMod {
|
||||
samples_per_bit,
|
||||
bandwidth,
|
||||
oscillator: Nco::new(0.0),
|
||||
bit_stream,
|
||||
sample_index: samples_per_bit,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step_modulate(&mut self) -> Option<f32> {
|
||||
if self.sample_index == self.samples_per_bit {
|
||||
self.sample_index = 0;
|
||||
let bit = self.bit_stream.next()?;
|
||||
|
||||
let frequency = if bit { self.bandwidth } else { -self.bandwidth };
|
||||
self.oscillator.set_frequency(frequency);
|
||||
}
|
||||
|
||||
self.sample_index += 1;
|
||||
self.oscillator.step();
|
||||
Some(self.oscillator.sin())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let sample_rate = 44100;
|
||||
let mut frequency = 500.0; //HZ
|
||||
|
||||
/*
|
||||
let path = "a.jpg";
|
||||
let file = File::open(path).unwrap();
|
||||
let mut bit_stream = file.bytes().flat_map(|byte| {
|
||||
let byte = byte.unwrap();
|
||||
[
|
||||
byte & 1 == 1,
|
||||
(byte >> 1) & 1 == 1,
|
||||
(byte >> 2) & 1 == 1,
|
||||
(byte >> 3) & 1 == 1,
|
||||
(byte >> 4) & 1 == 1,
|
||||
(byte >> 5) & 1 == 1,
|
||||
(byte >> 6) & 1 == 1,
|
||||
(byte >> 7) & 1 == 1,
|
||||
]
|
||||
});
|
||||
*/
|
||||
|
||||
let mut bit_stream = (0..22000).map(|_| false).chain((0..22000).map(|_| false));
|
||||
|
||||
let mut bfsk = BFSKMod::new(
|
||||
20,
|
||||
2. * PI * (frequency / sample_rate as f32),
|
||||
&mut bit_stream,
|
||||
);
|
||||
|
||||
let spec = hound::WavSpec {
|
||||
channels: 1,
|
||||
sample_rate,
|
||||
bits_per_sample: 16,
|
||||
sample_format: hound::SampleFormat::Int,
|
||||
};
|
||||
let mut writer = hound::WavWriter::create("sine.wav", spec).unwrap();
|
||||
|
||||
while let Some(sample) = bfsk.step_modulate() {
|
||||
let amplitude = i16::MAX as f32;
|
||||
writer.write_sample((amplitude * sample) as i16).unwrap();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user