Skip to content

Instantly share code, notes, and snippets.

@Synergyst
Created February 29, 2024 07:28
Show Gist options
  • Save Synergyst/301bbc0a5890dfa2e0e5aad40651f981 to your computer and use it in GitHub Desktop.
Save Synergyst/301bbc0a5890dfa2e0e5aad40651f981 to your computer and use it in GitHub Desktop.
dtmf_test
extern crate rustfft;
extern crate num_complex;
use std::f32;
use std::{thread, time::Duration};
use std::collections::HashMap;
use std::process::Command;
use jack::{AsyncClient, Client, ClosureProcessHandler, ProcessScope};
fn main() {
println!("Starting..");
// Initialize JACK client
let (client, _status) = Client::new("dtmf_decoder", jack::ClientOptions::NO_START_SERVER).expect("Unable to create JACK client");
println!("Initialized JACK client..");
// Register ports
let spec = jack::AudioIn::default();
let in_port = client.register_port("in", spec).expect("Failed to register input port");
println!("Registered JACK port..");
// Define the processing callback
let process_callback = ClosureProcessHandler::new(move |_client: &Client, scope: &ProcessScope| -> jack::Control {
// Access audio samples from in_port
let in_buffer = in_port.as_slice(scope) as &[f32];
let sample_rate = 48000 as f32;
let dtmf_freqs: HashMap<char, (f32, f32)> = {
let mut map = HashMap::new();
map.insert('1', (697.0, 1209.0));
map.insert('2', (697.0, 1336.0));
map.insert('3', (697.0, 1477.0));
map.insert('A', (697.0, 1633.0));
map.insert('4', (770.0, 1209.0));
map.insert('5', (770.0, 1336.0));
map.insert('6', (770.0, 1477.0));
map.insert('B', (770.0, 1633.0));
map.insert('7', (852.0, 1209.0));
map.insert('8', (852.0, 1336.0));
map.insert('9', (852.0, 1477.0));
map.insert('C', (852.0, 1633.0));
map.insert('*', (941.0, 1209.0));
map.insert('0', (941.0, 1336.0));
map.insert('#', (941.0, 1477.0));
map.insert('D', (941.0, 1633.0));
map
};
let mut highest1: f32 = 0.0;
let mut highest2: f32 = 0.0;
let mut char1: char = ' ';
let mut char2: char = ' ';
for (key, (freq1, freq2)) in &dtmf_freqs {
let val1 = goertzel_threshold(in_buffer, sample_rate, *freq1, 10.0);
let val2 = goertzel_threshold(in_buffer, sample_rate, *freq2, 10.0);
//println!("magnitude: {:?} / {:?}", val1, val2);
if val1 < 15.0 || val2 < 15.0 {
continue;
}
if val1 > highest1 {
highest1 = val1;
char1 = *key;
}
if val2 > highest2 {
highest2 = val2;
char2 = *key;
}
}
if char1 != ' ' && char2 != ' ' {
//println!("{:?} / {:?}", char1, char2);
println!("First tone: {} (magnitude: {})", char1, highest1);
println!("Second tone: {} (magnitude: {})", char2, highest2);
}
jack::Control::Continue
},);
let _active_client = AsyncClient::new(client, (), process_callback).expect("Failed to create async client"); // Activate the client
println!("Press Enter to quit...");
let command1 = "jack_connect";
let command2 = "killall";
let args1 = ["system:capture_1", "dtmf_decoder:in"];
let args2 = ["-9", command1];
let _child_process2 = Command::new(command2).args(&args2).spawn().expect("Failed to spawn command2");
thread::sleep(Duration::from_millis(1000));
let _child_process1 = Command::new(command1).args(&args1).spawn().expect("Failed to spawn command1");
let mut user_input = String::new();
std::io::stdin().read_line(&mut user_input).unwrap();
}
pub fn goertzel(input: &[f32], sample_rate: f32, target_freq: f32) -> f32 {
let num_samples = input.len() as f32;
let target_k = (target_freq * num_samples / sample_rate).round() as usize;
let mut s_prev = 0.0;
let mut s_prev2 = 0.0;
let coeff = 2.0 * f32::cos(2.0 * f32::consts::PI * target_k as f32 / num_samples);
for &sample in input {
let s = sample + coeff * s_prev - s_prev2;
s_prev2 = s_prev;
s_prev = s;
}
let magnitude = s_prev2.powi(2) + s_prev.powi(2) - coeff * s_prev * s_prev2;
magnitude.sqrt()
}
pub fn goertzel_threshold(input: &[f32], sample_rate: f32, target_freq: f32, threshold: f32) -> f32 {
let num_samples = input.len() as f32;
let target_k = (target_freq * num_samples / sample_rate).round() as usize;
let mut s_prev = 0.0;
let mut s_prev2 = 0.0;
let coeff = 2.0 * f32::cos(2.0 * f32::consts::PI * target_k as f32 / num_samples);
for &sample in input {
let s = sample + coeff * s_prev - s_prev2;
s_prev2 = s_prev;
s_prev = s;
}
let magnitude = s_prev2.powi(2) + s_prev.powi(2) - coeff * s_prev * s_prev2;
// Calculate frequencies within the threshold range
let lower_freq = (target_freq - threshold).max(0.0);
let upper_freq = target_freq + threshold;
if target_freq - threshold <= 0.0 {
return magnitude.sqrt(); // Return magnitude as is if lower bound is 0 Hz
}
// Calculate corresponding k values for the threshold frequencies
let lower_k = (lower_freq * num_samples / sample_rate).round() as usize;
let upper_k = (upper_freq * num_samples / sample_rate).round() as usize;
// Sum magnitudes within the threshold range
let mut threshold_magnitude = 0.0;
for k in lower_k..=upper_k {
let coeff = 2.0 * f32::cos(2.0 * f32::consts::PI * k as f32 / num_samples);
threshold_magnitude += 2.0 * coeff * s_prev * s_prev2;
}
(magnitude + threshold_magnitude).sqrt()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment