initial commit

This commit is contained in:
2025-09-17 20:33:25 +02:00
commit 20cac4cb60
6 changed files with 176 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

16
Cargo.lock generated Normal file
View 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
View File

@ -0,0 +1,7 @@
[package]
name = "rdsp"
version = "0.1.0"
edition = "2024"
[dependencies]
hound = "3.5.1"

BIN
a.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
sine.wav Normal file

Binary file not shown.

152
src/main.rs Normal file
View 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();
}
}