Advertisement
Guest User

Rust gtk+portaudio

a guest
Feb 13th, 2016
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 10.22 KB | None | 0 0
  1. extern crate chrono;
  2. extern crate gtk;
  3. extern crate gdk;
  4. extern crate portaudio;
  5.  
  6. use gtk::traits::*;
  7. use gtk::signal::Inhibit;
  8. use std::thread;
  9. use std::collections::VecDeque;
  10. use chrono::UTC;
  11. use std::time::Duration;
  12. use std::sync::mpsc::{Sender, Receiver};
  13. use std::sync::mpsc;
  14. use std::sync::Arc;
  15. use std::sync::Mutex;
  16.  
  17. const SAMPLE_RATE: i64 = 44_100;
  18. const CHANNELS: i32 = 2;
  19. const FRAMES: u32 = 1024;
  20. const INTERLEAVED: bool = true;
  21. const LATENCY: portaudio::Time = 0.0; // Ignored by PortAudio::is_*_format_supported.
  22.  
  23. struct IPData {
  24.     terminate: bool
  25. }
  26.  
  27. impl IPData {
  28.     pub fn new() -> IPData {
  29.         IPData {
  30.             terminate: false
  31.         }
  32.     }
  33. }
  34.  
  35.  
  36. fn main() {
  37.     // During construction, call unwrap() on all Results, since there's no
  38.     // alternative to printing an error message and stopping anyway.
  39.     let pa = portaudio::PortAudio::new().unwrap();
  40.  
  41.     // Share ip_data between the main event loop and the audio thread
  42.     let ip_data = Arc::new(Mutex::new(IPData::new()));
  43.  
  44.     // GTK initialization
  45.     if gtk::init().is_err() {
  46.         println!("Failed to initialize GTK.");
  47.         return;
  48.     }
  49.  
  50.     // Construct window,
  51.     let (window, avg_level_bar, peak_level_bar) = create_main_window(&pa);
  52.     window.show_all();
  53.     // Make close window set flag which terminates all loops
  54.     let ip_clone = ip_data.clone();
  55.     window.connect_delete_event(move |_, _| {
  56.         ip_clone.lock().unwrap().terminate = true;
  57.         Inhibit(false)
  58.     });
  59.  
  60.     // Set up communication channel between audio thread and main loop
  61.     let (tx, rx): (Sender<(f32,f32)>, Receiver<(f32,f32)>) = mpsc::channel();
  62.  
  63.     let ip_clone = ip_data.clone();
  64.     let analysis_thread = thread::spawn(move || {
  65.         match start_analysis_thread(pa, &ip_clone, tx) {
  66.             Err(err) => {
  67.                 println!("PortAudio error: {}", err);
  68.             },
  69.             _ => ()
  70.         }
  71.     });
  72.  
  73.     // Show peak audio signal with a decay instead of changing it abruptly
  74.     let mut peak_decay: f32 = 0.0;
  75.  
  76.     // Main event loop.
  77.     'eventloop: while !ip_data.lock().unwrap().terminate {
  78.        while gtk::events_pending() {
  79.            if !gtk::main_iteration() {
  80.                break 'eventloop; // never happens as no-one calls the quit function
  81.             }
  82.         }
  83.         // Receive power and peak value from the other thread. This takes around
  84.         // 23ms, which is an acceptable delay for an event loop.
  85.         let (power, peak) = rx.recv().unwrap();
  86.         if peak > peak_decay {
  87.             peak_decay = peak;
  88.         } else {
  89.             peak_decay = (3.0 * peak_decay + peak) / 4.0;
  90.         }
  91.         avg_level_bar.set_value(power as f64);
  92.         peak_level_bar.set_value(peak_decay as f64);
  93.     }
  94.  
  95.     // Wait for termination of audio thread
  96.     analysis_thread.join().unwrap();
  97. }
  98.  
  99. // Creates the gtk window with a few elements and returns a handle to the
  100. // window and two elements that will be updated from the main loop
  101. fn create_main_window(pa: &portaudio::PortAudio) -> (gtk::Window, gtk::LevelBar, gtk::LevelBar) {
  102.     let window = gtk::Window::new(gtk::WindowType::Toplevel).unwrap();
  103.  
  104.     window.set_title("Realtime FFT Analyzer");
  105.     window.set_default_size(350, 70);
  106.     window.set_border_width(10);
  107.     window.set_window_position(gtk::WindowPosition::Center);
  108.    
  109.     let avg_level_bar = gtk::LevelBar::new_for_interval(0., 100.).unwrap();
  110.     avg_level_bar.set_value(0.);
  111.     let peak_level_bar = gtk::LevelBar::new_for_interval(0., 100.).unwrap();
  112.     peak_level_bar.set_value(0.);
  113.     let scale_low = gtk::Scale::new_with_range(gtk::Orientation::Horizontal, 0., 2000., 10.).unwrap();
  114.     let scale_high = gtk::Scale::new_with_range(gtk::Orientation::Horizontal, 0., 2000., 10.).unwrap();
  115.  
  116.     let _box = gtk::Box::new(gtk::Orientation::Vertical, 10).unwrap();
  117.     _box.set_border_width(5);
  118.     _box.add(&create_input_interface_combo_box(&pa));
  119.     _box.add(&avg_level_bar);
  120.     _box.add(&peak_level_bar);
  121.     _box.add(&scale_low);
  122.     _box.add(&scale_high);
  123.     window.add(&_box);
  124.     (window, avg_level_bar, peak_level_bar)
  125. }
  126.  
  127. // Creates combo box with all audio inputs (ignoring half duplex output only
  128. // interfaces) and selects the system default in the list.
  129. fn create_input_interface_combo_box(pa: &portaudio::PortAudio) -> gtk::ComboBoxText {
  130.     let default_device_index = pa.default_input_device().unwrap();
  131.     let interface_list = gtk::ComboBoxText::new().unwrap();
  132.     let mut def_dev_idx: i32 = -1;
  133.     let mut menu_index: i32 = 0;
  134.  
  135.     for device in pa.devices().unwrap() {
  136.         let (idx, info) = device.unwrap();
  137.         let in_channels = info.max_input_channels;
  138.         let input_params = portaudio::StreamParameters::<i16>::new(idx, in_channels, INTERLEAVED, LATENCY);
  139.         let out_channels = info.max_output_channels;
  140.         let output_params = portaudio::StreamParameters::<i16>::new(idx, out_channels, INTERLEAVED, LATENCY);
  141.         let default_sample_rate = info.default_sample_rate;
  142.         if pa.is_input_format_supported(input_params, default_sample_rate).is_ok() ||
  143.            pa.is_duplex_format_supported(input_params, output_params, default_sample_rate).is_ok() {
  144.             interface_list.append(info.name, info.name);
  145.             if idx == default_device_index {
  146.                 def_dev_idx = menu_index;
  147.             }
  148.             menu_index += 1;
  149.         }
  150.     }
  151.     interface_list.set_active(def_dev_idx);
  152.     interface_list
  153. }
  154.  
  155. // Open a stream on the default input device, and keep reading until the flag
  156. // in ip_data tells us to stop. For every received buffer, compute some form
  157. // of running average of the power and the current peak and send them to the
  158. // main loop.
  159. // Note: the loop sleeps after receiving a full buffer, since the blocking
  160. // stream doesn't block in reality.
  161. fn start_analysis_thread(pa: portaudio::PortAudio, ip_data: &Arc<Mutex<IPData>>, tx: Sender<(f32,f32)>) -> Result<(), portaudio::Error> {
  162.  
  163.     let def_input = try!(pa.default_input_device());
  164.     let input_info = try!(pa.device_info(def_input));
  165.  
  166.     // Construct the input stream parameters.
  167.     let latency = input_info.default_low_input_latency;
  168.     let input_params = portaudio::StreamParameters::<f32>::new(def_input, CHANNELS, INTERLEAVED, latency);
  169.  
  170.     // Construct the settings with which we'll open our duplex stream.
  171.     let settings = portaudio::InputStreamSettings::new(input_params, SAMPLE_RATE as f64, FRAMES);
  172.  
  173.     let mut stream = try!(pa.open_blocking_stream(settings));
  174.  
  175.     // We don't buffer yet.
  176.     // let mut buffer: VecDeque<f32> = VecDeque::with_capacity(FRAMES as usize * CHANNELS as usize);
  177.  
  178.     // We'll use this function to wait for read/write availability.
  179.     fn wait_for_stream<F>(f: F, name: &str) -> u32
  180.         where F: Fn() -> Result<portaudio::StreamAvailable, portaudio::error::Error>
  181.     {
  182.         'waiting_for_stream: loop {
  183.            match f() {
  184.                Ok(available) => match available {
  185.                    portaudio::StreamAvailable::Frames(frames) => return frames as u32,
  186.                    portaudio::StreamAvailable::InputOverflowed => println!("Input stream has overflowed"),
  187.                    portaudio::StreamAvailable::OutputUnderflowed => println!("Output stream has underflowed"),
  188.                },
  189.                Err(err) => panic!("An error occurred while waiting for the {} stream: {}", name, err),
  190.            }
  191.        }
  192.    };
  193.  
  194.    let interval = chrono::Duration::microseconds((1000000 as i64 * FRAMES as i64 + SAMPLE_RATE - 1) / SAMPLE_RATE);
  195.    let mut next = UTC::now() + interval + interval;
  196.    let mut do_sleep: bool = true;
  197.  
  198.    let mut ring_buffer: VecDeque<f32> = VecDeque::with_capacity(25);
  199.    let mut running_sum: f32 = 0.0;
  200.  
  201.    // let max_noise_power: f32 = -9.5424; // average over power of all possible values from (-32768..+32767)/32768 in dB
  202.    // let max_per_buffer: f32 = (FRAMES as f32).log10() * 20.0 + max_noise_power;
  203.    
  204.    try!(stream.start());
  205.  
  206.    'stream: while !ip_data.lock().unwrap().terminate {
  207.  
  208.         if do_sleep {
  209.             // If the sleep flag is set, sleep until the time indicated by
  210.             // next. This is needed since the blocking stream doesn't block.
  211.             let wait_in_ms = (next - UTC::now()).num_milliseconds() as u64;
  212.             thread::sleep(Duration::from_millis(wait_in_ms + 1));
  213.             do_sleep = false;
  214.         }
  215.        
  216.         // How many frames are available on the input stream?
  217.         let in_frames = wait_for_stream(|| stream.read_available(), "Read");
  218.  
  219.         // If there are frames available, let's take them and add them to our buffer.
  220.         if in_frames > 0 {
  221.  
  222.             let input_samples = try!(stream.read(in_frames));
  223.             // buffer.extend(input_samples.into_iter());
  224.             let mut peak: f32 = 0.0;
  225.             // Compute the average of all x[i]^2
  226.             let energy = input_samples.into_iter().
  227.                 fold(0.0, |acc_power, &sample| {
  228.                     let abs = sample.abs();
  229.                     if abs > peak {
  230.                         peak = abs;
  231.                     };
  232.                     sample * sample + acc_power
  233.                 });
  234.             let power: f32 = energy / FRAMES as f32;
  235.  
  236.             // Use a ring buffer to compute the running average over 25 buffers
  237.             // (about .5s with the current constants).
  238.             // TODO: Doesn't seem to work as intended.
  239.             if ring_buffer.len() == 25 {
  240.                 running_sum -= ring_buffer.pop_front().unwrap_or(0.0);
  241.             }
  242.             running_sum += power;
  243.             ring_buffer.push_back(power);
  244.  
  245.             // Convert to dB, but add some large number to make sure it's
  246.             // visible.
  247.             let db: f32 = 120.0 + (running_sum / ring_buffer.len() as f32).log10() * 20.0; // - max_per_buffer ??
  248.  
  249.             // Send values to main loop so they will be shown in the UI
  250.             match tx.send((db, 100.0 * peak)) {
  251.                 Ok(()) => (),
  252.                 Err(_) => {
  253.                     break;
  254.                 }
  255.             }
  256.             // buffer.clear();
  257.  
  258.             next = UTC::now() + interval;
  259.             do_sleep = true;
  260.            
  261.         }
  262.  
  263.     }
  264.  
  265.     try!(stream.stop());
  266.     Ok(())
  267. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement