1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
/*! Blocks for the Au file format.
The format is very simple, and is documented on
<https://en.wikipedia.org/wiki/Au_file_format>.
The benefit .au has over .wav is that .au can be written as a stream,
without seeking back to the file header to update data sizes.
It's also much simpler.
*/
use anyhow::Result;
use crate::block::{Block, BlockRet};
use crate::stream::{new_streamp, Streamp};
use crate::{Error, Float};
/// Au support several encodings. This code currently only one.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Encoding {
/// 16 bit linear PCM.
PCM16 = 3,
}
/** Au encoder block.
This block takes a stream of floats between -1 and 1, and writes them
as the bytes of an .au file.
```
use rustradio::graph::Graph;
use rustradio::blocks::{AuEncode, VectorSource, FileSink};
use rustradio::au::Encoding;
use rustradio::file_sink::Mode;
use rustradio::Complex;
let src = VectorSource::new(
vec![10.0, 0.0, -20.0, 0.0, 100.0, -100.0],
false,
);
let src_out = src.out();
let au = AuEncode::new(src_out, Encoding::PCM16, 48000, 1);
let au_out = au.out();
let sink = FileSink::new(au_out, "/dev/null", Mode::Overwrite)?;
let mut g = Graph::new();
g.add(Box::new(src));
g.add(Box::new(au));
g.add(Box::new(sink));
g.run()?;
# Ok::<(), anyhow::Error>(())
```
*/
pub struct AuEncode {
header: Option<Vec<u8>>,
encoding: Encoding,
src: Streamp<Float>,
dst: Streamp<u8>,
}
impl AuEncode {
/// Create new Au encoder block.
///
/// * `encoding`: currently only `Encoding::PCM16` is implemented.
/// * `bitrate`: E.g. 48000,
/// * `channels`: Currently only mono (1) is implemented.
pub fn new(src: Streamp<Float>, encoding: Encoding, bitrate: u32, channels: u32) -> Self {
assert_eq!(channels, 1, "only mono supported at the moment");
let mut v = Vec::with_capacity(28);
// Magic
v.extend(0x2e736e64u32.to_be_bytes());
// Data offset.
v.extend(28u32.to_be_bytes());
// Size, or all ones if unknown.
v.extend(0xffffffffu32.to_be_bytes());
// Mode.
v.extend((encoding as u32).to_be_bytes());
// Bitrate.
v.extend(bitrate.to_be_bytes());
// Channels.
v.extend(channels.to_be_bytes());
// Minimum annotation field.
v.extend(&[0, 0, 0, 0]);
Self {
header: Some(v),
encoding,
src,
dst: new_streamp(),
}
}
/// Return the output stream.
pub fn out(&self) -> Streamp<u8> {
self.dst.clone()
}
}
impl Block for AuEncode {
fn block_name(&self) -> &'static str {
"AuEncode"
}
fn work(&mut self) -> Result<BlockRet, Error> {
let mut o = self.dst.lock().unwrap();
if let Some(h) = &self.header {
o.write(h.iter().copied());
self.header = None;
}
assert_eq!(self.encoding, Encoding::PCM16);
type S = i16;
let scale = S::MAX as Float;
let mut i = self.src.lock().unwrap();
let mut v = Vec::with_capacity(i.available() * std::mem::size_of::<S>());
i.iter().for_each(|x: &Float| {
v.extend(((*x * scale) as S).to_be_bytes());
});
i.clear();
if v.is_empty() {
Ok(BlockRet::Noop)
} else {
o.write_slice(&v);
Ok(BlockRet::Ok)
}
}
}