generated from lks/eframe_template_android
Almost working audio fft input for waterfall
This commit is contained in:
83
src/app/audio_fft.rs
Normal file
83
src/app/audio_fft.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use cpal::{
|
||||
self,
|
||||
traits::{DeviceTrait, HostTrait},
|
||||
BufferSize, StreamConfig,
|
||||
};
|
||||
use realfft::RealFftPlanner;
|
||||
use std::sync::mpsc;
|
||||
|
||||
pub struct AudioFFT {
|
||||
pub stream: cpal::Stream,
|
||||
pub output_len: usize,
|
||||
}
|
||||
|
||||
impl AudioFFT {
|
||||
pub fn new(size: usize) -> Result<(Self, mpsc::Receiver<Vec<u8>>)> {
|
||||
let output_len = size / 2 + 1;
|
||||
|
||||
// Create mpsc queue
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
// Setup fft use f32 for now
|
||||
let mut fft_planner = RealFftPlanner::<f32>::new();
|
||||
let fft = fft_planner.plan_fft_forward(size);
|
||||
|
||||
// Setup audio input
|
||||
let host = cpal::default_host();
|
||||
let device = host
|
||||
.default_input_device()
|
||||
.ok_or(anyhow!("No input audio device found"))?;
|
||||
// Basic config that 'should' be suppoted by most devices
|
||||
let config = StreamConfig {
|
||||
channels: 1,
|
||||
sample_rate: cpal::SampleRate(44100),
|
||||
buffer_size: BufferSize::Default,
|
||||
};
|
||||
|
||||
let mut fft_in: Vec<f32> = Vec::with_capacity(size);
|
||||
let mut fft_out = fft.make_output_vec();
|
||||
let mut fft_scratch = fft.make_scratch_vec();
|
||||
let stream = device.build_input_stream(
|
||||
&config,
|
||||
move |mut data: &[f32], _: &cpal::InputCallbackInfo| {
|
||||
while data.fill_vec(&mut fft_in, size).is_ok() {
|
||||
assert_eq!(size, fft_in.len());
|
||||
fft.process_with_scratch(&mut fft_in, &mut fft_out, &mut fft_scratch)
|
||||
.unwrap();
|
||||
fft_in.clear();
|
||||
let output: Vec<u8> = fft_out.iter().map(|c| (c.arg() * 255.0) as u8).collect();
|
||||
assert_eq!(output_len, output.len());
|
||||
tx.send(output).unwrap();
|
||||
}
|
||||
},
|
||||
move |err| log::error!("Audio Thread Error: {err}"),
|
||||
None,
|
||||
)?;
|
||||
|
||||
Ok((Self { stream, output_len }, rx))
|
||||
}
|
||||
}
|
||||
|
||||
trait FillVec {
|
||||
/// Takes elements from self and inserts them into out_vec
|
||||
/// Returns Ok if out_vec is filled to size
|
||||
/// Returns Err when out_vec is not fully filled (self will be empty)
|
||||
fn fill_vec(&mut self, out_vec: &mut Vec<f32>, size: usize) -> Result<()>;
|
||||
}
|
||||
impl FillVec for &[f32] {
|
||||
fn fill_vec(&mut self, out_vec: &mut Vec<f32>, size: usize) -> Result<()> {
|
||||
let have = self.len();
|
||||
if have == 0 {
|
||||
anyhow::bail!("Self empty");
|
||||
}
|
||||
let need = size - out_vec.len();
|
||||
let can_move = need.min(have);
|
||||
out_vec.extend_from_slice(&self[..can_move]);
|
||||
*self = &self[can_move..];
|
||||
match out_vec.len() == size {
|
||||
true => Ok(()),
|
||||
false => Err(anyhow!("out_vec not full")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,7 +185,6 @@ impl Waterfall {
|
||||
gl.tex_parameter_i32(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST as i32);
|
||||
check_for_gl_errors(&gl, "Set texture params");
|
||||
|
||||
//gl.tex_storage_2d(glow::TEXTURE_2D, 1, glow::R8, 300, 300);
|
||||
gl.tex_image_2d(
|
||||
glow::TEXTURE_2D,
|
||||
0,
|
||||
@@ -195,7 +194,8 @@ impl Waterfall {
|
||||
0,
|
||||
glow::RED,
|
||||
glow::UNSIGNED_BYTE,
|
||||
Some(&buffer),
|
||||
//Some(&buffer), // This segfaults with large buffers
|
||||
None,
|
||||
);
|
||||
check_for_gl_errors(&gl, "Initializing Texture");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user