Rust Rodio get a list of OutputDevices - audio

I'm new to the rust and I've been playing around with the Rodio audio library.
I can play an audio file on the default audio output device like this:
use std::fs::File;
use std::io::BufReader;
use rodio::{OutputStream, Sink};
fn main() {
let (_stream, stream_handle) = OutputStream::try_default().unwrap();
let sink = Sink::try_new(&stream_handle).unwrap();
let file = File::open("m.mp3").unwrap();
let source = rodio::Decoder::new(BufReader::new(file)).unwrap();
sink.append(source);
loop {}
}
I can see that Rodio provides a method to set the audio output device for a stream try_from_device(&Device) but I can't figure out how to get a list of available audio output devices and provide an arbitrary one to this function.
---- UPDATE ----
Based on E_net4's answer I made two simple functions to list host devices and create an OutputStream for a specific device and then use it anywhere I need to play an audio file on that device like this:
use std::fs::File;
use std::io::BufReader;
use rodio::*;
use rodio::cpal::traits::{HostTrait,DeviceTrait};
fn listHostDevices(){
let host = cpal::default_host();
let devices = host.output_devices().unwrap();
for device in devices{
let dev:rodio::Device = device.into();
let devName:String=dev.name().unwrap();
println!(" # Device : {}", devName);
}
}
fn getOutputStream(device_name:&str) -> (OutputStream,OutputStreamHandle) {
let host = cpal::default_host();
let devices = host.output_devices().unwrap();
let ( mut _stream, mut stream_handle) = OutputStream::try_default().unwrap();
for device in devices{
let dev:rodio::Device = device.into();
let devName:String=dev.name().unwrap();
if devName==device_name {
println!("Device found: {}", devName);
( _stream, stream_handle) = OutputStream::try_from_device(&dev).unwrap();
}
}
return (_stream,stream_handle);
}
And then I use the functions like this:
fn main() {
listHostDevices();
let (_stream, stream_handle) = getOutputStream("Speakers (Realtek(R) Audio)");
let sink = Sink::try_new(&stream_handle).unwrap();
let file = File::open("m.mp3").unwrap();
let source = rodio::Decoder::new(BufReader::new(file)).unwrap();
sink.append(source);
loop {}
}

rodio uses cpal as the underlying audio library. This is where the concepts of host and device come from. Use the re-exported cpal module from rodio to get the system host and obtain a list of output devices.
use rodio::cpal;
let host = cpal::default_host();
let devices = host.output_devices()?;
for device in devices {
// use device
}
The device values obtained will implement DeviceTrait, but rodio works with the dynamically polymorphic type rodio::Device instead. Fortunately, we can easily convert what we have via From or Into.
let device: rodio::Device = device.into();
// ...
stream.try_from_device(&device)?;

Related

Reading the DS18B20 temperature sensor with this Rust function

sorry, i'm a complete newbie to Rust. I try to read the temp from the sensor mentioned above on a Raspberry Pi using the code provided on this site: https://github.com/fuchsnj/ds18b20
Actually, i want to call the function
get_temperature
but i have no idea how to declare the parameters, especially delay and one_wire_bus.
I was able to resolve all the 'namespaces' or name bindings (sorry, coming from C++) but got stuck with the parameters. Can someone give me an example how to call and use this function like this:
use ds18b20::{Resolution, Ds18b20};
use embedded_hal::blocking::delay::{DelayUs, DelayMs};
use embedded_hal::digital::v2::{OutputPin, InputPin};
use one_wire_bus::{self, OneWire, OneWireResult};
use core::fmt::Debug;
use std::io::Write;
fn main() {
let mut delay = ?????;
let mut one_wire_bus = ?????;
let mut tx = ?????; //&mut Vec::new();
let temp = get_temperature(delay, tx, one_wire_bus);
...
//do something whit the temp
...
}
This is the implementation of the function from the website
fn get_temperature<P, E>(
delay: &mut (impl DelayUs<u16> + DelayMs<u16>),
tx: &mut impl Write,
one_wire_bus: &mut OneWire<P>,
) -> OneWireResult<(), E>
where
P: OutputPin<Error=E> + InputPin<Error=E>,
E: Debug
{
// initiate a temperature measurement for all connected devices
ds18b20::start_simultaneous_temp_measurement(one_wire_bus, delay)?;
// wait until the measurement is done. This depends on the resolution you specified
// If you don't know the resolution, you can obtain it from reading the sensor data,
// or just wait the longest time, which is the 12-bit resolution (750ms)
Resolution::Bits12.delay_for_measurement_time(delay);
// iterate over all the devices, and report their temperature
let mut search_state = None;
loop {
if let Some((device_address, state)) = one_wire_bus.device_search(search_state.as_ref(), false, delay)? {
search_state = Some(state);
if device_address.family_code() != ds18b20::FAMILY_CODE {
// skip other devices
continue;
}
// You will generally create the sensor once, and save it for later
let sensor = Ds18b20::new(device_address)?;
// contains the read temperature, as well as config info such as the resolution used
let sensor_data = sensor.read_data(one_wire_bus, delay)?;
writeln!(tx, "Device at {:?} is {}°C", device_address, sensor_data.temperature);
} else {
break;
}
}
Ok(())
}

Peripheral Initialisation of GPIO Output with stm32f1xx_hal on bluepill development board

I would like to initialize a basic output GPIO pin on my blue pill board. I am using Rust and the stm32f1xx_hal crate. I want to create a struct Peripherals which holds the handle to the output in the following way:
use cortex_m_rt;
use stm32f1xx_hal::{
pac,
prelude::*,
gpio,
afio,
serial::{Serial, Config},
};
use crate::pac::{USART1};
type GpioOutput = gpio::gpioc::PC13<gpio::Output<gpio::PushPull>>;
pub struct Peripherals{
led: Option<GpioOutput>
}
impl Peripherals {
fn init() -> Peripherals {
let dp = pac::Peripherals::take().unwrap();
let cp = cortex_m::Peripherals::take().unwrap();
// set clock frequency to internal 8mhz oscillator
let mut rcc = dp.RCC.constrain();
let mut flash = dp.FLASH.constrain();
let clocks = rcc.cfgr.sysclk(8.mhz()).freeze(&mut flash.acr);
// access PGIOC registers
let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
return Peripherals{
led: Peripherals::init_led(&mut gpioc)
}
}
fn init_led(gpioc: &mut gpio::gpioc::Parts) -> Option<GpioOutput> {
let led = &gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
return Some(led);
}
}
This code does not work, since init_led returns Option<&GpioOutput>. Now I am wondering if it makes sense to use a lifetime parameter in the Peripherals struct and store a reference to the GpioOutput within the struct. Or is it more sensible to store the unreferenced value - and how would I implement either of these options?
The only solution which seems to work is moving the init_led code to the scope of the init function:
return Peripherals{
led: Some(gpioc.pc13.into_push_pull_output(&mut gpioc.crh))
}
But i would like to seperate that code within its own function. How can i do that?
Ok, i figured out a way in case someone else is having the same problem:
pub fn init() -> Peripherals {
let dp = pac::Peripherals::take().unwrap();
let cp = cortex_m::Peripherals::take().unwrap();
// set clock frequency to internal 8mhz oscillator
let rcc = dp.RCC.constrain();
let mut flash = dp.FLASH.constrain();
// access PGIOC and PGIOB registers and prepare the alternate function I/O registers
let mut apb2 = rcc.apb2;
let gpioc = dp.GPIOC.split(&mut apb2);
let clocks = rcc.cfgr.sysclk(8.mhz()).freeze(&mut flash.acr);
return Peripherals{
led: Peripherals::init_led(gpioc)
}
}
fn init_led(mut gpioc: stm32f1xx_hal::gpio::gpioc::Parts) -> Option<GpioOutput> {
let led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
return Some(led);
}
I am just wondering if this is the correct way to do it or will it create extra overhead, because i am passing gpioc by value instead of by reference in the init_led function?

How can i controll a 8x8 led-matrix display Max7219 with a raspberrypi in rust?

I want to manually control every single dot on 4 together chained 8*8 led-matrices controlled by the max7219 microcontroller via the SPI interface.
I already hooked up the clock, master-output/slave-input and ChipSelect signal to my osciloscope and everything seems to work the way it should be.
But I am only able to get the display kind of working by sending random data to it and I do not know how that data gets encoded.
Here is the code
use rand::Rng;
use std::io;
use std::io::prelude::*;
use spidev::{Spidev, SpidevOptions, SpidevTransfer, SpiModeFlags};
// Read the state of GPIO4 on a raspberry pi. /dev/gpiochip0
// maps to the driver for the SoC (builtin) GPIO controller.
fn main() -> Result<(), gpio_cdev::Error> {
let mut spi = create_spi().unwrap();
write_spi(&mut spi);
Ok(())
}
fn write_spi(spi: &mut Spidev) -> io::Result<()> {
let mut rng = rand::thread_rng();
loop {
let mut tx_buf = [0u8; 8];
for i in 0..8 {
tx_buf[i] = rng.gen_range(0..255);
}
spi.write(&tx_buf);
}
Ok(())
}
fn create_spi() -> io::Result<Spidev> {
let mut spi = Spidev::open("/dev/spidev0.0")?;
let options = SpidevOptions::new()
.bits_per_word(8)
.max_speed_hz(10_000)
.mode(SpiModeFlags::SPI_MODE_0)
.build();
spi.configure(&options)?;
Ok(spi)
}
What data must be sent to get it working?

Unable to record audio with cpal and encode with opus. Always creates improper file

I have been working for several hours trying to simply record audio from my microphone and encode it with opus so I can ultimately stream it and play it elsewhere. Currently, I am struggling with trying to get it to simply play a file. I based this pretty heavily based upon the cpal examples as well as the libopus c example. Currently, it just outputs a nonsense file that VLC cant even read. However, if I print the raw bytes as they are encoded, I can definitely tell something is happening. I have also tried messing with endianess, but it did not work at all. I have also been using rubato to resample the raw output into a way the opus can use.
fn main() -> Result<(), anyhow::Error> {
let mut encoder = opus::Encoder::new(48000, opus::Channels::Stereo, opus::Application::Voip).unwrap();
let mut resampler = rubato::FftFixedInOut::<f32>::new(44100, 48000, 896, 2);
let host = cpal::default_host();
let device = host.default_input_device().unwrap();
println!("Input device: {}", device.name()?);
let config = device
.default_input_config()
.expect("Failed to get default input config");
println!("Default input config: {:?}", config);
println!("Begin recording...");
let err_fn = move |err| {
eprintln!("an error occurred on stream: {}", err);
};
let sample_format = config.sample_format();
// let socket = std::net::UdpSocket::bind("192.168.1.82:1337")?;
const PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/recorded.pcm");
let socket = std::fs::File::create(PATH).unwrap();
let mut socket = BufWriter::new(socket);
let stream = device
.build_input_stream_raw(
&config.into(),
sample_format,
move |data, _: &_| write_input_data_f32(data, &mut encoder, &mut resampler, &mut socket),
err_fn,
)
.unwrap();
stream.play()?;
std::thread::sleep(std::time::Duration::from_secs(10));
drop(stream);
Ok(())
}
type ResamplerHandle = rubato::FftFixedInOut<f32>;
// type SocketHandle = std::net::UdpSocket;
type SocketHandle = BufWriter<std::fs::File>;
fn write_input_data_f32(
input: &Data,
encoder: &mut Encoder,
resampler: &mut ResamplerHandle,
socket: &mut SocketHandle,
) {
let mut inp = input.as_slice::<f32>().unwrap().to_vec();
inp.truncate(resampler.nbr_frames_needed());
if inp.len() < resampler.nbr_frames_needed() {
inp.append(&mut vec![0f32; resampler.nbr_frames_needed() - inp.len()]);
}
let mut wave_out = resampler.process(&vec![Vec::from(inp); 2]).unwrap();//[0].to_owned();
use itertools::interleave;
let v1 = wave_out[0].to_owned();
let v2 = wave_out[1].to_owned();
let v = interleave(v1.chunks(1), v2.chunks(1)).flatten().copied().collect::<Vec<f32>>();
let buff = encoder.encode_vec_float(v.as_slice(), 960).unwrap();
use std::io::Write;
socket.write(&buff);
}
It looks like you're writing raw audio frames to a file, which most likely is not what you are looking for. Most audio files aren't just raw data, they use an audio container with a header and other features. For Opus, you most likely want to use an Ogg container for simply saving audio to a file (as opposed to e.g. streaming).
The issue, I found, is a misuderstanding with how opus was working. It seems to work distinctly in chunks and not a stream of input. So I went ahead and started streaming them over the network and when I put in the exact output chunks it worked perfectly!

Is it possible to compile a Vulkano shader at runtime?

I've been using Vulkano in order to get some simple 3D graphics going on. Generally, I like to write my GLSL shaders in text and restart my program, or even changing shaders while the program is running. The examples given in Vulkano appear to use a macro to convert the GLSL to some form of SPIR-V based shader with Rust functions attached, but the GLSL is actually compiled into the binary (even when using a path to a file).
I've managed to get the crate shaderc to build my SPIR-V on the fly:
let mut f = File::open("src/grafx/vert.glsl")
.expect("Can't find file src/bin/runtime-shader/vert.glsl
This example needs to be run from the root of the example crate.");
let mut source = String::new();
f.read_to_string(&mut source);
//let source = "#version 310 es\n void EP() {}";
let mut compiler = shaderc::Compiler::new().unwrap();
let mut options = shaderc::CompileOptions::new().unwrap();
options.add_macro_definition("EP", Some("main"));
let binary_result = compiler.compile_into_spirv(
&source, shaderc::ShaderKind::Vertex,
"shader.glsl", "main", Some(&options)).unwrap();
assert_eq!(Some(&0x07230203), binary_result.as_binary().first());
let text_result = compiler.compile_into_spirv_assembly(
&source, shaderc::ShaderKind::Vertex,
"shader.glsl", "main", Some(&options)).unwrap();
assert!(text_result.as_text().starts_with("; SPIR-V\n"));
//println!("Compiled Vertex Shader: {}", text_result.as_text());
let vert_spirv = {
unsafe { ShaderModule::new(device.clone(), binary_result.as_binary_u8()) }.unwrap()
};
vert_spirv
So far, so good, we have a ShaderModule which seems to be the first step. However, we we actually need is a GraphicsEntryPoint which we can then put into our GraphicsPipeline. Apparently, GraphicsPipeline is where we string together our shaders, triangles and depth maps and all that lovely stuff.
Trouble is, I've no idea what is going on with the code that performs this feat:
pub fn shade_vertex <'a, S> (vert_spirv: &'a Arc<ShaderModule>) ->
GraphicsEntryPoint<'a, S, VertInput, VertOutput, VertLayout> {
let tn = unsafe {
vert_spirv.graphics_entry_point(
CStr::from_bytes_with_nul_unchecked(b"main\0"),
VertInput,
VertOutput,
VertLayout(ShaderStages { vertex: true, ..ShaderStages::none() }),
GraphicsShaderType::Vertex
)
};
tn
}
Specifically, what is VertInput and VertOutput? I've copied them from the example.
This is the closest example I could find that deals with loading Shaders on the fly. It looks like Input and Output are looking for entry points into the SPIR-V or something but I've no idea what to do with that. I'm hoping there is a function somewhere in the existing macro that will just take care of this for me. I've gotten this far but I seem a little stuck.
Has anyone else tried loading shaders at runtime?
I'm using wgpu, I've made my device, render_pipeline multithreaded like this:
let rx = Arc::new(Mutex::new(rx));
let window = Arc::new(Mutex::new(window));
let fs = Arc::new(Mutex::new(fs));
let fs_module = Arc::new(Mutex::new(fs_module));
let render_pipeline = Arc::new(Mutex::new(render_pipeline));
let device = Arc::new(Mutex::new(device));
used notify to listen to change events:
notify = "4.0.15"
use notify::{RecommendedWatcher, Watcher, RecursiveMode};
//mainxx
let (tx, rx) = mpsc::channel();
let mut watcher: RecommendedWatcher =
Watcher::new(tx, Duration::from_millis(500)).unwrap();
log::info!("Starting watcher on {:?}", *FRAG_SHADER_PATH);
watcher.watch((*FRAG_SHADER_PATH).clone(), RecursiveMode::NonRecursive).unwrap();
Then spawn a thread that listens to changes:
thread::spawn(move || {
log::info!("Shader watcher thread spawned");
loop {
if let Ok(notify::DebouncedEvent::Write(..)) = rx.lock().unwrap().recv() {
log::info!("Write event in fragment shader");
window.lock().unwrap().set_title("Loading shader.frag...");
*fs.lock().unwrap() = load_fs().unwrap();
*fs_module.lock().unwrap() = load_fs_module(Arc::clone(&device), &Arc::clone(&fs).lock().unwrap());
*render_pipeline.lock().unwrap() = create_render_pipeline_multithreaded(Arc::clone(&device), Arc::clone(&fs_module));
render.lock().unwrap().deref_mut()();
window.lock().unwrap().set_title(TITLE);
};
}
});
where load_fs is a closure that uses glsl_to_spirv:
let load_fs = move || -> Result<Vec<u32>, std::io::Error> {
log::info!("Loading fragment shader");
let mut buffer = String::new();
let mut f = File::open(&*FRAG_SHADER_PATH)?;
f.read_to_string(&mut buffer)?;
// Load fragment shader
wgpu::read_spirv(
glsl_to_spirv::compile(
&buffer,
glsl_to_spirv::ShaderType::Fragment
).expect("Compilation failed")
)
};
There is an updated example for this in the vulkano repository.
I followed that and the example for shaderc-rs to get to this:
fn compile_to_spirv(src: &str, kind: shaderc::ShaderKind, entry_point_name: &str) -> Vec<u32> {
let mut f = File::open(src).unwrap_or_else(|_| panic!("Could not open file {}", src));
let mut glsl = String::new();
f.read_to_string(&mut glsl)
.unwrap_or_else(|_| panic!("Could not read file {} to string", src));
let compiler = shaderc::Compiler::new().unwrap();
let mut options = shaderc::CompileOptions::new().unwrap();
options.add_macro_definition("EP", Some(entry_point_name));
compiler
.compile_into_spirv(&glsl, kind, src, entry_point_name, Some(&options))
.expect("Could not compile glsl shader to spriv")
.as_binary()
.to_vec()
}
let vs = {
unsafe {
ShaderModule::from_words(
device.clone(),
&compile_to_spirv(
"shaders/triangle/vs.glsl",
shaderc::ShaderKind::Vertex,
"main",
),
)
}
.unwrap()
};
After this, vs can be used as in the example.

Resources