// Copyright The pipewire-rs Contributors. // SPDX-License-Identifier: MIT //! This file is a rustic interpretation of the the [PipeWire Tutorial 4][tut] //! //! tut: https://docs.pipewire.org/page_tutorial4.html use pipewire as pw; use pw::{properties::properties, spa}; use spa::pod::Pod; pub const DEFAULT_RATE: u32 = 44100; pub const DEFAULT_CHANNELS: u32 = 2; pub const DEFAULT_VOLUME: f64 = 0.7; pub const PI_2: f64 = std::f64::consts::PI + std::f64::consts::PI; pub const CHAN_SIZE: usize = std::mem::size_of::(); pub fn main() -> Result<(), pw::Error> { pw::init(); let mainloop = pw::main_loop::MainLoop::new(None)?; let context = pw::context::Context::new(&mainloop)?; let core = context.connect(None)?; let data: f64 = 0.0; let stream = pw::stream::Stream::new( &core, "audio-src", properties! { *pw::keys::MEDIA_TYPE => "Audio", *pw::keys::MEDIA_ROLE => "Music", *pw::keys::MEDIA_CATEGORY => "Playback", }, )?; let _listener = stream .add_local_listener_with_user_data(data) .process(|stream, acc| match stream.dequeue_buffer() { None => println!("No buffer received"), Some(mut buffer) => { let datas = buffer.datas_mut(); let stride = CHAN_SIZE * DEFAULT_CHANNELS as usize; let data = &mut datas[0]; let n_frames = if let Some(slice) = data.data() { let n_frames = slice.len() / stride; for i in 0..n_frames { *acc += PI_2 * 440.0 / DEFAULT_RATE as f64; if *acc >= PI_2 { *acc -= PI_2 } let val = (f64::sin(*acc) * DEFAULT_VOLUME * 16767.0) as i16; for c in 0..DEFAULT_CHANNELS { let start = i * stride + (c as usize * CHAN_SIZE); let end = start + CHAN_SIZE; let chan = &mut slice[start..end]; chan.copy_from_slice(&i16::to_le_bytes(val)); } } n_frames } else { 0 }; let chunk = data.chunk_mut(); *chunk.offset_mut() = 0; *chunk.stride_mut() = stride as _; *chunk.size_mut() = (stride * n_frames) as _; } }) .register()?; let mut audio_info = spa::param::audio::AudioInfoRaw::new(); audio_info.set_format(spa::param::audio::AudioFormat::S16LE); audio_info.set_rate(DEFAULT_RATE); audio_info.set_channels(DEFAULT_CHANNELS); let values: Vec = pw::spa::pod::serialize::PodSerializer::serialize( std::io::Cursor::new(Vec::new()), &pw::spa::pod::Value::Object(pw::spa::pod::Object { type_: spa_sys::SPA_TYPE_OBJECT_Format, id: spa_sys::SPA_PARAM_EnumFormat, properties: audio_info.into(), }), ) .unwrap() .0 .into_inner(); let mut params = [Pod::from_bytes(&values).unwrap()]; stream.connect( spa::utils::Direction::Output, None, pw::stream::StreamFlags::AUTOCONNECT | pw::stream::StreamFlags::MAP_BUFFERS | pw::stream::StreamFlags::RT_PROCESS, &mut params, )?; mainloop.run(); Ok(()) }