Rust를 사용한 오디오 시각화

28725 단어 rustaudio
올해 초부터 나는 녹에 대해 더 많이 배우기로 결심했습니다. 오디오로 무언가를 하고 싶었지만 어디서부터 시작해야 할지 몰랐습니다. 고맙게도 Justin Wernick은 Rusty Mic을 만들고 그것에 대해 blogpost을 썼습니다. 나는 다른 그래픽 라이브러리로 나만의 간단한 '마이크 시각화'를 구축하기로 결정했습니다. 개선할 수 있는 부분이 있으면 알려주세요. 배우고 싶습니다!

코드를 확인하고 싶다면 이 작은 프로젝트를 위해 만든 저장소를 확인할 수 있습니다.


매니플레임 / 믹비즈


Rust로 만든 간단한 실시간 오디오 시각화 애플리케이션입니다.





믹비즈


Rust로 만든 간단한 실시간 오디오 시각화 앱입니다.
이것은 (rust) portaudiothree(-rs) 으로 구현된 작고 단순한 것입니다.

시작하기


환경 설정


모든 OS에 대해 이 프로젝트를 컴파일할 수 있어야 합니다. 현재 이 문서는 MacOS에서 환경을 설정하는 방법에 중점을 둡니다.
환경을 설정하려면 다음 명령을 실행하십시오(모든 사항이 적용되는 것은 아님).
# Clone this repository
git clone https://github.com/maniflames/MicViz.git

# Install Rust with homebrew
brew install rust

# Install portaudio with homebrew
brew install portaudio

# Unpack MacOS SDK headers 
open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg

컴파일 및 실행

Now that you're all set you can run this:

# Navigate to project
cd MicViz

# Compile and run with cargo
cargo run

Enjoy 😁




종속성

For this project I'll be using two crates: portaudio for working with audio and three. So go ahead and put the following in your Cargo.toml:

[dependencies]
portaudio = "0.7.0"
three = "0.4.0"


일반적으로 이 작업만 수행하면 되지만 이 경우에는 상황이 약간 다릅니다. portaudio 크레이트는 portaudio의 실제 설치에 대한 Rust 바인딩으로 구성되어 있으므로 직접 설치해야 합니다. Windows, Linux 및 MacOS용으로 downloadcompile portaudio를 사용할 수 있습니다. 그러나 Mac에서 개발하는 경우 pkg-config를 사용하여 homebrew를 통해 설치하는 것이 더 쉬울 것입니다.

brew install pkg-config
brew install portaudio


오디오 입력 읽기



먼저 컴퓨터의 기본 마이크에서 입력을 읽어 봅시다. Portaudio의 비 차단 입력 스트림으로 이를 달성하기 위해. 코드를 한 줄씩 주석 처리하여 가능한 한 명확하게 하려고 노력할 것입니다.

use portaudio;
use std::sync::mpsc::*;

fn main() {

    // Construct a portaudio instance that will connect to a native audio API
    let pa = portaudio::PortAudio::new().expect("Unable to init PortAudio"); 
    // Collect information about the default microphone
    let mic_index = pa.default_input_device().expect("Unable to get default device");
    let mic = pa.device_info(mic_index).expect("unable to get mic info");

    // Set parameters for the stream settings.
    // We pass which mic should be used, how many channels are used,
    // whether all the values of all the channels should be passed in a 
    // single audiobuffer and the latency that should be considered 
    let input_params = portaudio::StreamParameters::<f32>::new( mic_index, 1, true, mic.default_low_input_latency);

    // Settings for an inputstream.
    // Here we pass the stream parameters we set before,
    // the sample rate of the mic and the amount values we want to receive
    let input_settings = portaudio::InputStreamSettings::new(input_params, mic.default_sample_rate, 256);

    // Creating a channel so we can receive audio values asynchronously
    let (sender, receiver) = channel(); 

    // A callback function that should be as short as possible so we send all the info to a different thread
    let callback = move |portaudio::InputStreamCallbackArgs {buffer, .. }| {
        match sender.send(buffer) {
            Ok(_) => portaudio::Continue, 
            Err(_) => portaudio::Complete
        }
    };

    // Creating & starting the input stream with our settings & callback
    let mut stream = pa.open_non_blocking_stream(input_settings, callback).expect("Unable to create stream"); 
    stream.start().expect("Unable to start stream");

    //Printing values every time we receive new ones while the stream is active
    while stream.is_active().unwrap() {
       while let Ok(buffer) = receiver.try_recv() {
            println!("{:?}", buffer); 
       }
    }
}



이것을 실행하면 다음과 같아야 합니다.

오디오 시각화



자세히 보면 마이크에서 반환되는 모든 것이 -1.0에서 1.0 사이의 숫자를 포함하는 slice임을 알 수 있습니다. 이 숫자는 음파가 시간이 지남에 따라 마이크 내에서 움직이는 부분을 조작하는 방법을 나타냅니다. 이 변화를 시각화하는 다양한 방법이 있지만 간단하게 유지하기 위해 시간에 발생하는 모든 변화에 대해 선을 그려 보겠습니다. 새로운 변경 사항을 받으면 단순히 선을 제거하고 새 선을 그려서 선을 애니메이션으로 만들 것입니다.

use portaudio;
use std::sync::mpsc::*;
use three;

//struct for storing the application state
#[derive(Debug)]
struct State {
    sound_values: Vec<f32>,
    scene_meshes: Vec<three::Mesh>
}

fn main() {
    // Receiving audio input 
    let pa = portaudio::PortAudio::new().expect("Unable to init PortAudio"); 
    let mic_index = pa.default_input_device().expect("Unable to get default device");
    let mic = pa.device_info(mic_index).expect("unable to get mic info");

    let input_params = portaudio::StreamParameters::<f32>::new(mic_index, 1, true, mic.default_low_input_latency);
    let input_settings = portaudio::InputStreamSettings::new(input_params, mic.default_sample_rate, 256);

    let (sender, receiver) = channel();

    let callback = move |portaudio::InputStreamCallbackArgs {buffer, .. }| {
        match sender.send(buffer) {
            Ok(_) => portaudio::Continue, 
            Err(_) => portaudio::Complete
        }
    };

    let mut stream = pa.open_non_blocking_stream(input_settings, callback).expect("Unable to create stream"); 
    stream.start().expect("Unable to start stream"); 

    // Create a full screen window with a black background
    let mut builder = three::Window::builder("My Mic"); 
    builder.fullscreen(true); 
    let mut win = builder.build(); 
    win.scene.background = three::Background::Color(0x000000);

    // Create a variable that will contain the state off the app
    let mut state = State {
        sound_values: Vec::new(),
        scene_meshes: Vec::new()
    };

    // Create a camera that will be put in the scene on location 0.0, 0.0
    let camera = win.factory.orthographic_camera([0.0, 0.0], 1.0, -1.0 .. 1.0); 

    //Animation loop that will run until you press ESC or exit the program
    while win.update() && !win.input.hit(three::KEY_ESCAPE) {
        // Put new lines in the scene temporarily save them in the state
        update_lines(&mut win, &mut state);
        // Show the lines
        win.render(&camera);
        // Remove all lines from the scene and the state
        remove_lines(&mut win, &mut state);

        //Update state
        while let Ok(buffer) = receiver.try_recv() {
            update_sound_values(&buffer, &mut state); 
       }
    }
}

// Pass new samples into the state by overriding the vector
fn update_sound_values(samples: &[f32], state: &mut State) {
   state.sound_values = samples.to_vec(); 
}

// Put new lines in the scene temporarily save them in the state
fn update_lines(win: &mut three::window::Window, state: &mut State) {
    for (index, y_position) in state.sound_values.iter().enumerate() {

        // calculate the x position of the line by calculating a normalized x position between 0.0 and 1.0 (i / num_samples)
        // With the scale variable the size of the visualization can be changed. 
        let i = index as f32; 
        let num_samples = state.sound_values.len() as f32; 
        let scale = 3.0; 
        let x_position = (i / (num_samples / scale)) - (0.5 * scale);

        // create the geometry for a line with the calculated positions
        // three is a 3D graphics library so we pass the x, y, z values
        let geometry = three::Geometry::with_vertices(vec![
            [x_position, y_position.clone(), 0.0].into(),
            [x_position, -y_position.clone(), 0.0].into()
        ]);

        // create material so for our line, in this case white line material
        let material = three::material::Line {
            color: 0xFFFFFF,
        };

        // create a 3D object from the geometry and the material
        let mesh = win.factory.mesh(geometry, material);

        // Put the line in the scene and store it in the state
        win.scene.add(&mesh); 
        state.scene_meshes.push(mesh); 
    }
}

// Remove all lines from the scene and the state
fn remove_lines(win: &mut three::window::Window, state: &mut State) {
    for mesh in &state.scene_meshes {
        win.scene.remove(&mesh); 
    }

    state.scene_meshes.clear(); 
}


스크립트를 실행하면 다음과 같아야 합니다.



MicViz를 즐기세요. 시간이 있다면 여러분의 버전을 자랑하세요 😄👀

그리고 다시 한 번 Rust에 대해 한두 가지 알고 있다면 제시된 코드를 개선할 수 있는 방법을 알려주십시오!

좋은 웹페이지 즐겨찾기