Init and test sending samples to pipewire stream

This commit is contained in:
Muaz Ahmad 2024-12-03 14:30:55 +05:00
parent f2cea03e27
commit 2c66db4205
4 changed files with 129 additions and 38 deletions

View file

@ -8,6 +8,8 @@ use std::{
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
use pipewire::spa::pod::Pod;
use crate::{config::Settings, player::PlayerEvent, utils::Error}; use crate::{config::Settings, player::PlayerEvent, utils::Error};
pub enum AudioEvent { pub enum AudioEvent {
@ -19,6 +21,8 @@ pub struct SoundManager {
error_chan: Sender<Error>, error_chan: Sender<Error>,
audio_controls: Receiver<AudioEvent>, audio_controls: Receiver<AudioEvent>,
player_chan: Sender<PlayerEvent>, player_chan: Sender<PlayerEvent>,
sample_in: Sender<Vec<u8>>,
pw_signal_in: pipewire::channel::Sender<Box<Vec<u8>>>,
} }
pub fn init( pub fn init(
@ -53,5 +57,5 @@ pub fn init(
Ok(audio_control_in) Ok(audio_control_in)
} }
mod pipewire; mod pw;
mod sound_mgr; mod sound_mgr;

View file

@ -1,32 +0,0 @@
use pipewire::{
context::Context, core::Core, keys, main_loop::MainLoop, properties::properties, stream::Stream,
};
use crate::utils::Error;
pub struct PipewireContext {
pub mainloop: MainLoop,
context: Context,
core: Core,
stream: Stream,
}
pub fn init() -> Result<PipewireContext, Error> {
let mainloop = MainLoop::new(None)?;
let context = Context::new(&mainloop)?;
let core = context.connect(None)?;
let props = properties! {
*keys::MEDIA_TYPE => "Audio",
*keys::MEDIA_ROLE => "Music",
*keys::MEDIA_CATEGORY => "Playback",
*keys::AUDIO_CHANNELS => "2",
};
let audio_source = Stream::new(&core, "Subtails", props)?;
Ok(PipewireContext {
mainloop,
context,
core,
stream: audio_source,
})
}

76
src/audio/pw.rs Normal file
View file

@ -0,0 +1,76 @@
use std::{process::exit, sync::Arc};
use pipewire::{
channel::Receiver,
context::Context,
core::Core,
keys,
main_loop::MainLoop,
properties::properties,
spa::{pod::Pod, utils::Direction},
stream::{Stream, StreamFlags},
};
use crate::utils::Error;
pub fn init(
samples: std::sync::mpsc::Receiver<Vec<u8>>,
pw_signal: Receiver<Box<Vec<u8>>>,
) -> Result<(), Error> {
let mainloop = MainLoop::new(None)?;
let context = Context::new(&mainloop)?;
let core = context.connect(None)?;
let props = properties! {
*keys::MEDIA_TYPE => "Audio",
*keys::MEDIA_ROLE => "Music",
*keys::MEDIA_CATEGORY => "Playback",
*keys::AUDIO_CHANNELS => "2",
};
let audio_source = Arc::new(Stream::new(&core, "Subtails", props)?);
let _listener = audio_source
.add_local_listener_with_user_data(0.0 as f64)
.process(move |stream, _| match stream.dequeue_buffer() {
None => {
eprintln!("Buffer dequeue failed!");
exit(1)
}
Some(mut buf) => {
let data = &mut (buf.datas_mut()[0]);
let n_iter = if let Some(sample_buf) = data.data() {
let next_samples = samples
.recv()
.expect("Sample sender disconnected unexpectedly");
if next_samples.len() > sample_buf.len() {
panic!("Buffer too small for given sample chunks");
}
let slice = &mut sample_buf[0..next_samples.len()];
slice.copy_from_slice(&next_samples);
slice.len()
} else {
0
};
let chunk = data.chunk_mut();
*chunk.offset_mut() = 0;
*chunk.size_mut() = n_iter as _;
}
})
.register()?;
let audio_source_ref = audio_source.clone();
let _receiver = pw_signal.attach(mainloop.loop_(), move |pod_bytes| {
let mut params = [Pod::from_bytes(&pod_bytes).unwrap()];
audio_source_ref.disconnect().unwrap();
audio_source_ref
.connect(
Direction::Output,
None,
StreamFlags::AUTOCONNECT | StreamFlags::MAP_BUFFERS | StreamFlags::ALLOC_BUFFERS,
&mut params,
)
.unwrap();
});
mainloop.run();
Ok(())
}

View file

@ -1,14 +1,24 @@
use std::{ use std::{
f64::consts,
io::Cursor,
sync::{ sync::{
mpsc::{Receiver, Sender}, mpsc::{channel, Receiver, Sender},
Arc, Arc,
}, },
thread, thread,
}; };
use pipewire::spa::{
param::audio::{AudioFormat, AudioInfoRaw, MAX_CHANNELS},
pod::{serialize::PodSerializer, Object, Pod, Value},
sys::{
SPA_PARAM_EnumFormat, SPA_TYPE_OBJECT_Format, SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR,
},
};
use crate::{config::Settings, player::PlayerEvent, utils::Error}; use crate::{config::Settings, player::PlayerEvent, utils::Error};
use super::{pipewire, AudioEvent, SoundManager}; use super::{pw, AudioEvent, SoundManager};
impl SoundManager { impl SoundManager {
pub fn init( pub fn init(
@ -18,30 +28,63 @@ impl SoundManager {
player_chan: Sender<PlayerEvent>, player_chan: Sender<PlayerEvent>,
) -> Result<SoundManager, Error> { ) -> Result<SoundManager, Error> {
let error_chan_clone = error_chan.clone(); let error_chan_clone = error_chan.clone();
let (sample_in, sample_out) = channel();
let (pw_signal_in, pw_signal_out) = pipewire::channel::channel();
thread::spawn(move || { thread::spawn(move || {
let pwire_context = match pipewire::init() { match pw::init(sample_out, pw_signal_out) {
Err(err) => { Err(err) => {
error_chan_clone.send(err).unwrap(); error_chan_clone.send(err).unwrap();
thread::park(); thread::park();
return; return;
} }
Ok(x) => x, Ok(_) => (),
}; };
pwire_context.mainloop.run();
}); });
let mut test_info = AudioInfoRaw::new();
test_info.set_format(AudioFormat::S16LE);
test_info.set_rate(44100);
test_info.set_channels(2);
let mut positions = [0; MAX_CHANNELS];
(positions[0], positions[1]) = (SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR);
test_info.set_position(positions);
let test_pod = PodSerializer::serialize(
Cursor::new(Vec::new()),
&Value::Object(Object {
type_: SPA_TYPE_OBJECT_Format,
id: SPA_PARAM_EnumFormat,
properties: test_info.into(),
}),
)
.unwrap()
.0
.into_inner();
pw_signal_in.send(Box::new(test_pod)).unwrap();
Ok(SoundManager { Ok(SoundManager {
settings, settings,
error_chan, error_chan,
audio_controls: audio_control_chan, audio_controls: audio_control_chan,
player_chan, player_chan,
sample_in,
pw_signal_in,
}) })
} }
pub fn begin(&mut self) -> Result<(), Error> { pub fn begin(&mut self) -> Result<(), Error> {
let mut test_tone: Vec<u8> = vec![0; 1764];
for i in 0..441 {
let t = i as f64 / 441.0 * consts::PI * 2.0;
let v = (f64::sin(t) * 16767.0) as i16;
test_tone[i * 4..i * 4 + 2].copy_from_slice(&i16::to_le_bytes(v));
test_tone[i * 4 + 2..i * 4 + 4].copy_from_slice(&i16::to_le_bytes(v));
}
loop { loop {
match self.audio_controls.recv()? { match self.audio_controls.recv()? {
AudioEvent::DecodeChunk(chunk) => (), AudioEvent::DecodeChunk(chunk) => (),
_ => unimplemented!(), _ => unimplemented!(),
} }
for i in 0..100 {
self.sample_in.send(test_tone.clone())?;
}
} }
} }
} }