Init and test sending samples to pipewire stream
This commit is contained in:
parent
f2cea03e27
commit
2c66db4205
4 changed files with 129 additions and 38 deletions
|
@ -8,6 +8,8 @@ use std::{
|
|||
|
||||
use std::sync::mpsc::Receiver;
|
||||
|
||||
use pipewire::spa::pod::Pod;
|
||||
|
||||
use crate::{config::Settings, player::PlayerEvent, utils::Error};
|
||||
|
||||
pub enum AudioEvent {
|
||||
|
@ -19,6 +21,8 @@ pub struct SoundManager {
|
|||
error_chan: Sender<Error>,
|
||||
audio_controls: Receiver<AudioEvent>,
|
||||
player_chan: Sender<PlayerEvent>,
|
||||
sample_in: Sender<Vec<u8>>,
|
||||
pw_signal_in: pipewire::channel::Sender<Box<Vec<u8>>>,
|
||||
}
|
||||
|
||||
pub fn init(
|
||||
|
@ -53,5 +57,5 @@ pub fn init(
|
|||
Ok(audio_control_in)
|
||||
}
|
||||
|
||||
mod pipewire;
|
||||
mod pw;
|
||||
mod sound_mgr;
|
||||
|
|
|
@ -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
76
src/audio/pw.rs
Normal 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(())
|
||||
}
|
|
@ -1,14 +1,24 @@
|
|||
use std::{
|
||||
f64::consts,
|
||||
io::Cursor,
|
||||
sync::{
|
||||
mpsc::{Receiver, Sender},
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
Arc,
|
||||
},
|
||||
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 super::{pipewire, AudioEvent, SoundManager};
|
||||
use super::{pw, AudioEvent, SoundManager};
|
||||
|
||||
impl SoundManager {
|
||||
pub fn init(
|
||||
|
@ -18,30 +28,63 @@ impl SoundManager {
|
|||
player_chan: Sender<PlayerEvent>,
|
||||
) -> Result<SoundManager, Error> {
|
||||
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 || {
|
||||
let pwire_context = match pipewire::init() {
|
||||
match pw::init(sample_out, pw_signal_out) {
|
||||
Err(err) => {
|
||||
error_chan_clone.send(err).unwrap();
|
||||
thread::park();
|
||||
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 {
|
||||
settings,
|
||||
error_chan,
|
||||
audio_controls: audio_control_chan,
|
||||
player_chan,
|
||||
sample_in,
|
||||
pw_signal_in,
|
||||
})
|
||||
}
|
||||
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 {
|
||||
match self.audio_controls.recv()? {
|
||||
AudioEvent::DecodeChunk(chunk) => (),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
for i in 0..100 {
|
||||
self.sample_in.send(test_tone.clone())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue