Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • redox-os/audiod
  • 4lDO2/audiod
  • bjorn3/audiod
3 results
Show changes
Commits on Source (7)
...@@ -6,6 +6,7 @@ version = 3 ...@@ -6,6 +6,7 @@ version = 3
name = "audiod" name = "audiod"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"redox-daemon",
"redox_syscall", "redox_syscall",
] ]
...@@ -15,11 +16,27 @@ version = "1.3.2" ...@@ -15,11 +16,27 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "libc"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "redox-daemon"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21e31c834277709c7ff3eb74959fe62be4b45b1189ba9d41fd3744cd3a9c554f"
dependencies = [
"libc",
"redox_syscall",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.12" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
...@@ -5,4 +5,5 @@ authors = ["Jeremy Soller <jackpot51@gmail.com>"] ...@@ -5,4 +5,5 @@ authors = ["Jeremy Soller <jackpot51@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
redox-daemon = "0.1"
redox_syscall = "0.2.12" redox_syscall = "0.2.12"
...@@ -3,10 +3,13 @@ extern crate syscall; ...@@ -3,10 +3,13 @@ extern crate syscall;
use std::{fs, io, mem, process, slice, thread}; use std::{fs, io, mem, process, slice, thread};
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use syscall::data::{Packet, SigAction}; use syscall::data::{Packet, SigAction};
use syscall::flag::{CloneFlags, SigActionFlags, O_CLOEXEC, SIGUSR1}; use syscall::flag::{SigActionFlags, SIGUSR1};
use syscall::scheme::SchemeBlockMut; use syscall::scheme::SchemeBlockMut;
use redox_daemon::Daemon;
use self::scheme::AudioScheme; use self::scheme::AudioScheme;
mod scheme; mod scheme;
...@@ -17,7 +20,7 @@ fn from_syscall_error(error: syscall::Error) -> io::Error { ...@@ -17,7 +20,7 @@ fn from_syscall_error(error: syscall::Error) -> io::Error {
extern "C" fn sigusr_handler(_sig: usize) {} extern "C" fn sigusr_handler(_sig: usize) {}
fn thread(scheme: Arc<Mutex<AudioScheme>>, pid: usize, mut hda_file: fs::File) -> io::Result<()> { fn thread(scheme: Arc<Mutex<AudioScheme>>, pid: usize, mut hw_file: fs::File) -> io::Result<()> {
// Enter null namespace // Enter null namespace
syscall::setrens(0, 0).map_err(from_syscall_error)?; syscall::setrens(0, 0).map_err(from_syscall_error)?;
...@@ -33,12 +36,12 @@ fn thread(scheme: Arc<Mutex<AudioScheme>>, pid: usize, mut hda_file: fs::File) - ...@@ -33,12 +36,12 @@ fn thread(scheme: Arc<Mutex<AudioScheme>>, pid: usize, mut hda_file: fs::File) -
// Wake up the scheme thread // Wake up the scheme thread
syscall::kill(pid, SIGUSR1).map_err(from_syscall_error)?; syscall::kill(pid, SIGUSR1).map_err(from_syscall_error)?;
hda_file.write(&buffer_u8)?; hw_file.write(&buffer_u8)?;
} }
} }
fn daemon(pipe_fd: usize) -> io::Result<()> { fn daemon(daemon: Daemon) -> io::Result<()> {
// Handle signals from the hda thread // Handle signals from the hw thread
syscall::sigaction(SIGUSR1, Some(&SigAction { syscall::sigaction(SIGUSR1, Some(&SigAction {
sa_handler: Some(sigusr_handler), sa_handler: Some(sigusr_handler),
sa_mask: [0; 2], sa_mask: [0; 2],
...@@ -47,24 +50,23 @@ fn daemon(pipe_fd: usize) -> io::Result<()> { ...@@ -47,24 +50,23 @@ fn daemon(pipe_fd: usize) -> io::Result<()> {
let pid = syscall::getpid().map_err(from_syscall_error)?; let pid = syscall::getpid().map_err(from_syscall_error)?;
let hda_file = fs::OpenOptions::new().write(true).open("hda:")?; let hw_file = fs::OpenOptions::new().write(true).open("audiohw:")?;
let mut scheme_file = fs::OpenOptions::new().create(true).read(true).write(true).open(":audio")?; let mut scheme_file = fs::OpenOptions::new().create(true).read(true).write(true).open(":audio")?;
// The scheme is now ready to accept requests, notify the original process
syscall::write(pipe_fd, &[1]).map_err(from_syscall_error)?;
let _ = syscall::close(pipe_fd);
let scheme = Arc::new(Mutex::new(AudioScheme::new())); let scheme = Arc::new(Mutex::new(AudioScheme::new()));
// Spawn a thread to mix and send audio data // Spawn a thread to mix and send audio data
let scheme_thread = scheme.clone(); let scheme_thread = scheme.clone();
let _thread = thread::spawn(move || thread(scheme_thread, pid, hda_file)); let _thread = thread::spawn(move || thread(scheme_thread, pid, hw_file));
// Enter the null namespace - done after thread is created so // Enter the null namespace - done after thread is created so
// memory: can be accessed for stack allocation // memory: can be accessed for stack allocation
syscall::setrens(0, 0).map_err(from_syscall_error)?; syscall::setrens(0, 0).map_err(from_syscall_error)?;
// The scheme is now ready to accept requests, notify the original process
daemon.ready().map_err(from_syscall_error)?;
let mut todo = Vec::new(); let mut todo = Vec::new();
loop { loop {
let mut packet = Packet::default(); let mut packet = Packet::default();
...@@ -99,26 +101,18 @@ fn daemon(pipe_fd: usize) -> io::Result<()> { ...@@ -99,26 +101,18 @@ fn daemon(pipe_fd: usize) -> io::Result<()> {
} }
} }
fn audiod() -> io::Result<()> {
let mut pipe = [0; 2];
syscall::pipe2(&mut pipe, O_CLOEXEC).map_err(from_syscall_error)?;
// Daemonize
if unsafe { syscall::clone(CloneFlags::empty()) }.map_err(from_syscall_error)? == 0 {
let _ = syscall::close(pipe[0]);
return daemon(pipe[1]);
} else {
let _ = syscall::close(pipe[1]);
syscall::read(pipe[0], &mut [0]).map_err(from_syscall_error)?;
let _ = syscall::close(pipe[0]);
Ok(())
}
}
fn main() { fn main() {
if let Err(err) = audiod() { if let Err(err) = Daemon::new(|x| {
match daemon(x) {
Ok(()) => {
process::exit(0);
},
Err(err) => {
eprintln!("audiod: {}", err);
process::exit(1);
}
}
}) {
eprintln!("audiod: {}", err); eprintln!("audiod: {}", err);
process::exit(1); process::exit(1);
} }
......
use std::collections::{BTreeMap, VecDeque}; use std::collections::{BTreeMap, VecDeque};
use syscall::error::{EBADF, EWOULDBLOCK, Error, Result}; use std::str;
use syscall::error::{EBADF, EINVAL, ENOENT, EWOULDBLOCK, Error, Result};
use syscall::flag::O_NONBLOCK; use syscall::flag::O_NONBLOCK;
use syscall::scheme::SchemeBlockMut; use syscall::scheme::SchemeBlockMut;
// The strict buffer size of the hda: driver // The strict buffer size of the audiohw: driver
const HDA_BUFFER_SIZE: usize = 512; const HW_BUFFER_SIZE: usize = 512;
// The desired buffer size of each handle // The desired buffer size of each handle
const HANDLE_BUFFER_SIZE: usize = 4096; const HANDLE_BUFFER_SIZE: usize = 4096;
struct Handle { enum Handle {
flags: usize, Audio {
buffer: VecDeque<(i16, i16)>, flags: usize,
buffer: VecDeque<(i16, i16)>,
},
//TODO: move volume to audiohw:?
Volume {
flags: usize,
offset: usize,
}
} }
pub struct AudioScheme { pub struct AudioScheme {
next_id: usize, next_id: usize,
handles: BTreeMap<usize, Handle> handles: BTreeMap<usize, Handle>,
volume: i32,
} }
impl AudioScheme { impl AudioScheme {
pub fn new() -> Self { pub fn new() -> Self {
AudioScheme { AudioScheme {
next_id: 0, next_id: 0,
handles: BTreeMap::new() handles: BTreeMap::new(),
volume: 50,
} }
} }
pub fn buffer(&mut self) -> [(i16, i16); HDA_BUFFER_SIZE] { pub fn buffer(&mut self) -> [(i16, i16); HW_BUFFER_SIZE] {
let mut buffer = [(0i16, 0i16); HDA_BUFFER_SIZE]; let mut mix_buffer = [(0i16, 0i16); HW_BUFFER_SIZE];
// Multiply each sample by the cube of volume divided by 100
// This mimics natural perception of loudness
let volume_factor = ((self.volume as f32) / 100.0).powi(3);
for (_id, handle) in self.handles.iter_mut() { for (_id, handle) in self.handles.iter_mut() {
let mut i = 0; match handle {
while i < buffer.len() { Handle::Audio { flags: _, ref mut buffer } => {
if let Some(sample) = handle.buffer.pop_front() { let mut i = 0;
buffer[i].0 = buffer[i].0.saturating_add(sample.0); while i < mix_buffer.len() {
buffer[i].1 = buffer[i].1.saturating_add(sample.1); if let Some(sample) = buffer.pop_front() {
} else { let left = (sample.0 as f32 * volume_factor) as i16;
break; let right = (sample.1 as f32 * volume_factor) as i16;
} mix_buffer[i].0 = mix_buffer[i].0.saturating_add(left);
i += 1; mix_buffer[i].1 = mix_buffer[i].1.saturating_add(right);
} else {
break;
}
i += 1;
}
},
_ => (),
} }
} }
buffer mix_buffer
} }
} }
impl SchemeBlockMut for AudioScheme { impl SchemeBlockMut for AudioScheme {
fn open(&mut self, _path: &str, flags: usize, _uid: u32, _gid: u32) -> Result<Option<usize>> { fn open(&mut self, path: &str, flags: usize, _uid: u32, _gid: u32) -> Result<Option<usize>> {
self.next_id += 1; let handle = match path.trim_matches('/') {
let id = self.next_id; "" => Handle::Audio {
flags,
buffer: VecDeque::new()
},
"volume" => Handle::Volume {
flags,
offset: 0,
},
_ => return Err(Error::new(ENOENT)),
};
self.handles.insert(id, Handle { let id = self.next_id;
flags, self.next_id += 1;
buffer: VecDeque::new() self.handles.insert(id, handle);
});
Ok(Some(id)) Ok(Some(id))
} }
fn write(&mut self, id: usize, buf: &[u8]) -> Result<Option<usize>> { fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; //TODO: check flags for readable
match self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::Audio { flags: _, buffer: _ } => {
//TODO: audio input?
Err(Error::new(EBADF))
},
Handle::Volume { flags: _, ref mut offset } => {
//TODO: should we allocate every time?
let string = format!("{}", self.volume);
let bytes = string.as_bytes();
if handle.buffer.len() >= HANDLE_BUFFER_SIZE { let mut i = 0;
if handle.flags & O_NONBLOCK > 0 { while i < buf.len() && *offset + i < bytes.len() {
Err(Error::new(EWOULDBLOCK)) buf[i] = bytes[*offset + i];
} else { i += 1;
Ok(None) }
}
} else { *offset += i;
let mut i = 0; Ok(Some(i))
while i + 4 <= buf.len() {
handle.buffer.push_back((
(buf[i] as i16) | ((buf[i + 1] as i16) << 8),
(buf[i + 2] as i16) | ((buf[i + 3] as i16) << 8)
));
i += 4;
} }
}
}
fn write(&mut self, id: usize, buf: &[u8]) -> Result<Option<usize>> {
//TODO: check flags for writable
match self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::Audio { ref flags, ref mut buffer } => {
if buffer.len() >= HANDLE_BUFFER_SIZE {
if flags & O_NONBLOCK > 0 {
Err(Error::new(EWOULDBLOCK))
} else {
Ok(None)
}
} else {
let mut i = 0;
while i + 4 <= buf.len() {
buffer.push_back((
(buf[i] as i16) | ((buf[i + 1] as i16) << 8),
(buf[i + 2] as i16) | ((buf[i + 3] as i16) << 8)
));
Ok(Some(i)) i += 4;
}
Ok(Some(i))
}
},
Handle::Volume { flags: _, ref mut offset } => {
//TODO: support other offsets?
if *offset == 0 {
let value = str::from_utf8(buf)
.map_err(|_| Error::new(EINVAL))?
.trim()
.parse::<i32>()
.map_err(|_| Error::new(EINVAL))?;
if value >= 0 && value <= 100 {
self.volume = value;
*offset += buf.len();
Ok(Some(buf.len()))
} else {
Err(Error::new(EINVAL))
}
} else {
// EOF
Ok(Some(0))
}
}
} }
} }
} }