Rust를 사용한 오디오 시각화
코드를 확인하고 싶다면 이 작은 프로젝트를 위해 만든 저장소를 확인할 수 있습니다.
매니플레임 / 믹비즈
Rust로 만든 간단한 실시간 오디오 시각화 애플리케이션입니다.
믹비즈
Rust로 만든 간단한 실시간 오디오 시각화 앱입니다.
이것은 (rust) portaudio 및 three(-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용으로 download 및 compile 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에 대해 한두 가지 알고 있다면 제시된 코드를 개선할 수 있는 방법을 알려주십시오!
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에 대해 한두 가지 알고 있다면 제시된 코드를 개선할 수 있는 방법을 알려주십시오!
Reference
이 문제에 관하여(Rust를 사용한 오디오 시각화), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/maniflames/audio-visualization-with-rust-4nhg텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)