diff --git a/out.txt b/out.txt new file mode 100644 index 0000000..0e0a395 Binary files /dev/null and b/out.txt differ diff --git a/s.txt b/s.txt new file mode 100644 index 0000000..ac54994 --- /dev/null +++ b/s.txt @@ -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 l’apparence 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. diff --git a/sine.wav b/sine.wav new file mode 100644 index 0000000..1319c3e Binary files /dev/null and b/sine.wav differ diff --git a/src/bfsk.rs b/src/bfsk.rs index ed4a830..c288f66 100644 --- a/src/bfsk.rs +++ b/src/bfsk.rs @@ -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> { @@ -50,9 +54,59 @@ where pub struct BFSKDem { samples_per_bit: u32, deviation: f32, - // State sample_index: u32, + + fft: Box, } -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; + } +} diff --git a/src/fft.rs b/src/fft.rs index a1b126c..e808e8a 100644 --- a/src/fft.rs +++ b/src/fft.rs @@ -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 { diff --git a/src/fft/dft.rs b/src/fft/dft.rs index 8a2fc2d..6965caf 100644 --- a/src/fft/dft.rs +++ b/src/fft/dft.rs @@ -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 - } } diff --git a/src/fft/mixed_radix.rs b/src/fft/mixed_radix.rs index e48fe9c..dece321 100644 --- a/src/fft/mixed_radix.rs +++ b/src/fft/mixed_radix.rs @@ -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 diff --git a/src/fft/rader.rs b/src/fft/rader.rs index 61f080e..0ffd6c8 100644 --- a/src/fft/rader.rs +++ b/src/fft/rader.rs @@ -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, conv_fft: Box, @@ -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 = (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)); + } } } diff --git a/src/fft/radix2.rs b/src/fft/radix2.rs index f01f992..6570fca 100644 --- a/src/fft/radix2.rs +++ b/src/fft/radix2.rs @@ -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 diff --git a/src/fft/windows.rs b/src/fft/windows.rs index 1893288..9e7882b 100644 --- a/src/fft/windows.rs +++ b/src/fft/windows.rs @@ -1,4 +1,4 @@ -pub fn rectanguar(t: f32) -> f32 { +pub fn rectangular(t: f32) -> f32 { 1. } diff --git a/src/main.rs b/src/main.rs index b83667c..1ab4f8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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(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 = 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(); + } }