begin cleaning ffts

This commit is contained in:
2025-09-24 19:24:40 +02:00
parent bd7ae2b19e
commit 3cc4144747
11 changed files with 152 additions and 99 deletions

BIN
out.txt Normal file

Binary file not shown.

17
s.txt Normal file
View File

@ -0,0 +1,17 @@
Skibidi Toilet[1],[2] est une web-série[3] machinima, créée par Alexey Gerasimov et diffusée sur sa chaîne YouTube DaFuq!?Boom! Produite à l'aide de Source Filmmaker, la série suit une guerre fictive entre des toilettes à tête humaine et des personnages humanoïdes dotés d'appareils électroniques à la place de la tête.
Après la publication du premier court métrage en février 2023, Skibidi Toilet devient un mème Internet viral sur divers réseaux sociaux, en particulier au sein de la génération Alpha. Les critiques ont vu dans cette série la première incursion de la génération Alpha dans la culture Internet, en concurrence avec la génération Z, plus âgée.
Synopsis
Skibidi Toilet raconte les événements d'une guerre fictive entre des hommes à tête de caméra, de haut-parleur ou de télévision, et les Skibidi Toilets, une race extraterrestre prenant lapparence de toilettes avec des têtes d'hommes ou de femmes et n'ayant que comme langage le « Skibidi ». Dirigés par le G-Toilet (personnage inspiré du G-Man dans la série de jeux vidéos Half-Life[4]), ils ont comme ambition de conquérir la Terre, en éliminant toute opposition[5],[6]. G-Toilet, qui est sous les ordres des Astro-Toilets, sa race d'origine qui se trouve dans l'espace, doit conquérir la terre avec l'armée qu'il a créée sur Terre, finira par perdre sa confiance aux Astros dû à sa défaite à l'épisode 57 et à sa dispute à l'épisode 60 avec les deux soldats d'élite des Astros. À partir de l'épisode 74, les Astros ont commencé à envahir la Terre. Les Skibidi Toilets et l'alliance n'ont d'autre choix que de s'allier pour vaincre les Astros.
Production
Illustration de Skibidi Toilet.
Alexey Gerasimov (russe : Алексей Герасимов, né en 1997 ou 1998[4]), crée sa chaîne YouTube DaFuq!?Boom! le 6 juin 2016. Lors d'une interview du site web Cartoon Brew, Alexey révèle qu'il est enseignant en animation en autodidacte depuis neuf ans[7]. Il utilise le logiciel Source Filmmaker et explique : « Cela me permet de travailler plus facilement et plus rapidement avec les ressources dont j'ai besoin pour l'animation, la réalisation, l'écriture et le montage moi-même, bien que j'aie une personne qui m'aide à obtenir les actifs dont j'ai besoin[7]. »
Vers la fin de l'année 2022, deux mèmes apparaissent sur Internet, le premier sur l'État américain d'Ohio, la présentant comme un état qui serait infesté de phénomènes paranormaux où il ne faudrait absolument ne pas y mettre les pieds ; le deuxième sur un remix de la musique Give It to Me de Timbaland avec Nelly Furtado et Justin Timberlake en featuring.
Dorkiopork, ami d'Alexey Gerasimov et animateur de vidéos drôles et de web-séries sur Source Filmmaker est déjà connu sur YouTube[5]. Dorkiopork a donné l'idée d'associer les deux mèmes pour créer la série Skibidi Toilet. C'est le 8 février 2023 qu'il sort leur fameux short nommé « Skibidi Toilet in Ohio », faisant référence au mème sur l'Ohio et donc aux phénomènes paranormaux qui s'y passent (selon le mème) puis en utilisant le remix de Give It to Me.
La vidéo connaît un succès retentissant aux États-Unis et peu de temps après, dans le monde entier. DaFuq!?Boom! s'empresse alors de créer d'autres vidéos sur le sujet. Après 8 shorts créés, il décide de faire de Skibidi Toilet une réelle web-série à partir du 9e épisode. Par la suite, Dorkiopork abandonne le projet pour se concentrer sur sa santé mentale, tandis que DaFuq!?Boom! continue de produire les animations.

BIN
sine.wav Normal file

Binary file not shown.

View File

@ -1,6 +1,10 @@
// 2-FSK Modulator
use crate::complex::Complex;
use std::f32::consts::PI;
use crate::complex::{Complex, Complex32};
use crate::fft::{self, DFT, windows};
use crate::map;
use crate::nco::Nco;
pub struct BFSKMod<'a, T: Iterator<Item = bool>> {
@ -50,9 +54,59 @@ where
pub struct BFSKDem {
samples_per_bit: u32,
deviation: f32,
// State
sample_index: u32,
fft: Box<dyn DFT>,
}
impl BFSKDem {}
impl BFSKDem {
pub fn new(samples_per_bit: u32, deviation: f32) -> Self {
BFSKDem {
samples_per_bit,
deviation,
sample_index: 0,
fft: fft::create_fft(samples_per_bit as usize, fft::FFTDirection::Forward),
}
}
pub fn demod(&mut self, baseband: &[Complex32]) -> bool {
assert!(baseband.len() >= self.samples_per_bit as usize);
self.fft
.get_input()
.iter_mut()
.enumerate()
.for_each(|(i, x)| *x = baseband[i]);
self.fft.execute(windows::rectanguar);
let bin_id = map(
self.deviation,
0.,
PI,
0.,
(self.samples_per_bit / 2) as f32,
)
.floor() as i32;
let bin_width = 5;
let mut positive_energy = 0.0;
for i in (bin_id - bin_width)..(bin_id + bin_width) {
if i >= 0 && i < self.samples_per_bit as i32 {
positive_energy += self.fft.get_output()[i as usize].mag();
}
}
let mut negative_energy = 0.0;
for i in (self.samples_per_bit as i32 - bin_id - bin_width)
..(self.samples_per_bit as i32 - bin_id + bin_width)
{
if i >= 0 && i < self.samples_per_bit as i32 {
negative_energy += self.fft.get_output()[i as usize].mag();
}
}
return positive_energy < negative_energy;
}
}

View File

@ -5,7 +5,7 @@ pub mod rader2;
pub mod radix2;
pub mod windows;
use std::iter::Map;
use std::{iter::Map, process::Output};
use crate::{
complex::Complex32,
@ -32,10 +32,7 @@ pub trait DFT {
where
Self: Sized;
fn get_input(&mut self) -> &mut [Complex32];
fn get_output(&self) -> &[Complex32];
fn execute(&mut self, window: fn(f32) -> f32);
fn execute(&mut self, input: &[Complex32], output: &mut [Complex32], window: fn(f32) -> f32);
}
pub trait DFTWindow {

View File

@ -3,8 +3,6 @@ use crate::fft::{DFT, FFTDirection};
use std::f32::consts::PI;
pub struct NaiveDFT {
output_buffer: Box<[Complex32]>,
input_buffer: Box<[Complex32]>,
direction: FFTDirection,
size: usize,
}
@ -14,18 +12,13 @@ impl DFT for NaiveDFT {
where
Self: Sized,
{
NaiveDFT {
output_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
input_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
direction,
size,
}
NaiveDFT { direction, size }
}
fn execute(&mut self, window: fn(f32) -> f32) {
for (freq, out) in self.output_buffer.iter_mut().enumerate() {
fn execute(&mut self, input: &[Complex32], output: &mut [Complex32], window: fn(f32) -> f32) {
for (freq, out) in output.iter_mut().enumerate() {
*out = Complex32::zero();
for (i, inp) in self.input_buffer.iter().enumerate() {
for (i, inp) in input.iter().enumerate() {
*out = *out
+ ((*inp
* Complex32::cexp(
@ -35,12 +28,4 @@ impl DFT for NaiveDFT {
}
}
}
fn get_input(&mut self) -> &mut [Complex32] {
&mut self.input_buffer
}
fn get_output(&self) -> &[Complex32] {
&self.output_buffer
}
}

View File

@ -8,8 +8,6 @@ use crate::{
};
pub struct MixedRadixFFT {
input_buffer: Box<[Complex32]>,
output_buffer: Box<[Complex32]>,
size: usize,
p: usize,
@ -33,8 +31,6 @@ impl DFT for MixedRadixFFT {
//let pfft = Box::new(NaiveDFT::create(p, direction));
MixedRadixFFT {
input_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
output_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
size,
twiddle_factors: compute_twiddle_factors(size, direction),
qfft,
@ -46,7 +42,7 @@ impl DFT for MixedRadixFFT {
}
}
fn execute(&mut self, window: fn(f32) -> f32) {
fn execute(&mut self, input: &[Complex32], output: &mut [Complex32], window: fn(f32) -> f32) {
// Perform p ffts of size q
for k0 in 0..self.p {
// Copy samples into input buffer

View File

@ -9,11 +9,9 @@ use crate::{
};
pub struct RaderFFT {
input_buffer: Box<[Complex32]>,
output_buffer: Box<[Complex32]>,
permutations: Box<[usize]>,
convolution_op: Box<[Complex32]>,
staging_buffer: Box<[Complex32]>,
inv_fft: Box<dyn DFT>,
conv_fft: Box<dyn DFT>,
@ -31,23 +29,20 @@ impl DFT for RaderFFT {
let mut conv_fft = create_fft(size - 1, FFTDirection::Forward);
//let mut conv_fft = create_fft(size - 1);
conv_fft
.get_input()
.iter_mut()
.enumerate()
.for_each(|(i, x)| {
*x = Complex32::cexp(
let mut convolution_op = vec![Complex32::zero(); size - 1];
let conv_fft_input: Vec<Complex32> = (0..(size - 1))
.map(|i| {
Complex32::cexp(
-2. * direction.sign() * PI * (permutations[i] as f32) / (size as f32),
)
});
conv_fft.execute(windows::rectanguar);
})
.collect();
conv_fft.execute(&conv_fft_input, &mut convolution_op, windows::rectangular);
RaderFFT {
input_buffer: vec![Complex32::zero(); size].into(),
output_buffer: vec![Complex32::zero(); size].into(),
permutations,
convolution_op: conv_fft.get_output().iter().copied().collect(),
convolution_op: convolution_op.into(),
staging_buffer: vec![Complex32::zero(); size - 1].into(),
inv_fft: create_fft(size - 1, FFTDirection::Inverse),
conv_fft,
@ -55,41 +50,33 @@ impl DFT for RaderFFT {
}
}
fn execute(&mut self, window: fn(f32) -> f32) {
fn execute(&mut self, input: &[Complex32], output: &mut [Complex32], window: fn(f32) -> f32) {
// Compute fft of input signal
for i in 0..(self.size - 1) {
let k = self.permutations[self.size - 1 - i - 1];
self.conv_fft.get_input()[i] = self.input_buffer[k];
self.staging_buffer[i] = input[k] * window(k as f32 / (self.size as f32));
}
self.conv_fft.execute(windows::rectanguar);
self.conv_fft
.execute(&self.staging_buffer, output, windows::rectangular);
for i in 0..(self.size - 1) {
self.output_buffer[i] = self.conv_fft.get_output()[i] * self.convolution_op[i];
self.staging_buffer[i] = output[i] * self.convolution_op[i];
}
for i in 0..(self.size - 1) {
//self.conv_fft.get_input()[i] = self.output_buffer[i];
self.inv_fft.get_input()[i] = self.output_buffer[i];
}
self.inv_fft.execute(windows::rectanguar);
self.inv_fft
.execute(&self.staging_buffer, output, windows::rectangular);
for i in 0..(self.size - 1) {
let k = self.permutations[i];
self.output_buffer[k] =
(self.inv_fft.get_output()[i] / (self.size - 1) as f32) + self.input_buffer[0];
self.staging_buffer[k - 1] = output[i];
}
self.output_buffer[0] = self.input_buffer.iter().copied().sum();
}
fn get_input(&mut self) -> &mut [Complex32] {
&mut self.input_buffer
}
fn get_output(&self) -> &[Complex32] {
&self.output_buffer
output[0] = input[0] * window(0.0);
for i in 0..(self.size - 1) {
output[i + 1] = (self.staging_buffer[i] / (self.size - 1) as f32) + input[0];
output[0] = output[0] + (input[i + 1] * window((i + 1) as f32 / self.size as f32));
}
}
}

View File

@ -5,8 +5,6 @@ use crate::fft::{DFT, FFTDirection};
use std::f32::consts::PI;
pub struct Radix2FFT {
output_buffer: Box<[Complex32]>,
input_buffer: Box<[Complex32]>,
direction: FFTDirection,
size: usize,
length: usize,
@ -20,19 +18,17 @@ impl DFT for Radix2FFT {
}
Radix2FFT {
output_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
input_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
size: size.ilog2() as usize,
direction,
length: size,
}
}
fn execute(&mut self, window: fn(f32) -> f32) {
fn execute(&mut self, input: &[Complex32], output: &mut [Complex32], window: fn(f32) -> f32) {
// Reorder samples
for (i, x) in self.output_buffer.iter_mut().enumerate() {
for (i, x) in output.iter_mut().enumerate() {
let k = reverse_bits(i, self.size as u32);
*x = self.input_buffer[k] * window(k as f32 / self.size as f32);
*x = input[k] * window(k as f32 / self.size as f32);
}
for step in 1..(self.size + 1) {
@ -41,24 +37,16 @@ impl DFT for Radix2FFT {
for s in (0..(self.length / pol_length)).map(|i| i * pol_length) {
for i in 0..mid_point {
// Compute current polynomial at each unit root
let a = self.output_buffer[s + i];
let b = self.output_buffer[s + i + mid_point];
let a = output[s + i];
let b = output[s + i + mid_point];
let angle = -2. * self.direction.sign() * PI * (i as f32) / (pol_length as f32);
let phasor = Complex32::cexp(angle);
self.output_buffer[i + s] = a + phasor * b;
self.output_buffer[i + s + mid_point] = a - phasor * b;
output[i + s] = a + phasor * b;
output[i + s + mid_point] = a - phasor * b;
}
}
}
}
fn get_input(&mut self) -> &mut [Complex32] {
&mut self.input_buffer
}
fn get_output(&self) -> &[Complex32] {
&self.output_buffer
}
}
// Utilities

View File

@ -1,4 +1,4 @@
pub fn rectanguar(t: f32) -> f32 {
pub fn rectangular(t: f32) -> f32 {
1.
}

View File

@ -17,15 +17,7 @@ use fft::rader;
use nco::Nco;
use plotters::prelude::*;
use crate::fft::{
DFT, FFTDirection, create_fft,
dft::NaiveDFT,
mixed_radix::MixedRadixFFT,
prime_factors,
rader::{RaderFFT, compute_prime_primitive_root, exp_mod},
radix2::Radix2FFT,
windows,
};
use crate::bfsk::BFSKDem;
// Utilities
fn map<T>(input: T, in_min: T, in_max: T, out_min: T, out_max: T) -> T
@ -35,14 +27,16 @@ where
((input - in_min.clone()) / (in_max - in_min)) * (out_max - out_min.clone()) + out_min
}
fn main() {}
fn main() {
modulate();
}
fn modulate() {
let sample_rate = 44100;
let mut frequency = 2000.0; //HZ
let mut bandwidth = 500.0; //HZ
let path = "a.jpg";
let path = "s.txt";
let file = File::open(path).unwrap();
let mut bit_stream = file.bytes().flat_map(|byte| {
let byte = byte.unwrap();
@ -80,15 +74,50 @@ fn modulate() {
let prev = Complex::new(0., 0.);
let alpha = 1.0 - (-2.0 * PI * ((1.5 * 0.5 * bandwidth) / sample_rate as f32));
let mut output_samples = vec![];
while let Some(sample) = bfsk.step_modulate() {
let amplitude = i16::MAX as f32;
let c_sample = lo.cexp() * sample;
let filtered = prev + (c_sample - prev) * alpha;
output_samples.push(filtered);
writer
.write_sample((amplitude * c_sample.re) as i16)
.unwrap();
lo.step();
}
writer.finalize().unwrap();
let mut of = File::create("out.txt").unwrap();
let mut bits = vec![];
let mut lodem = Nco::new(-2. * PI * (frequency / sample_rate as f32));
let mut demod = BFSKDem::new(
sample_rate / baud_rate,
PI * (bandwidth / sample_rate as f32),
);
for chunk in output_samples.chunks((sample_rate / baud_rate) as usize) {
let base_chunk: Vec<Complex32> = chunk
.iter()
.map(|x| {
lodem.step();
*x * lodem.cexp()
})
.collect();
let bit = demod.demod(base_chunk.as_slice());
bits.push(bit);
println!("{:?}", bit)
}
for b in bits.chunks(8) {
of.write_all(&[(b[0] as u8)
| ((b[0] as u8) << 1)
| ((b[0] as u8) << 2)
| ((b[0] as u8) << 3)
| ((b[0] as u8) << 4)
| ((b[0] as u8) << 5)
| ((b[0] as u8) << 6)
| ((b[0] as u8) << 7)])
.unwrap();
}
}