cauldron 0.0.1

Pure Rust Audio Decoder
Documentation
use bitflags::bitflags;
use std::fmt;
use std::fs;
use std::io;

use super::io::{AudioReader, AudioSamplesIterator, Sample, SampleFormat};
use super::{codecs, errors, Result};

// import codecs package
use super::{wav, flac};

bitflags! {
    /// Channels is a bit mask of all channels contained in a signal.
    /// see https://trac.ffmpeg.org/wiki/AudioChannelManipulation for more info
    pub struct Channels: u32 {
        const FRONT_LEFT         = 0x0000_0001; // Mono Channel
        const FRONT_RIGHT        = 0x0000_0002; // Stereo channel
        const FRONT_CENTRE       = 0x0000_0004;
        const BACK_LEFT          = 0x0000_0008;
        const BACK_CENTRE        = 0x0000_0010;
        const BACK_RIGHT         = 0x0000_0020;
        const LFE1               = 0x0000_0040; // Low frequency channel 1.
        const FRONT_LEFT_CENTRE  = 0x0000_0080;
        const FRONT_RIGHT_CENTRE = 0x0000_0100;
        const BACK_LEFT_CENTRE   = 0x0000_0200;
        const BACK_RIGHT_CENTRE  = 0x0000_0400;
        const FRONT_LEFT_WIDE    = 0x0000_0800;
        const FRONT_RIGHT_WIDE   = 0x0000_1000;
        const FRONT_LEFT_HIGH    = 0x0000_2000;
        const FRONT_CENTRE_HIGH  = 0x0000_4000;
        const FRONT_RIGHT_HIGH   = 0x0000_8000;
        const LFE2               = 0x0001_0000;  // Low frequency channel 2
        const SIDE_LEFT          = 0x0002_0000;
        const SIDE_RIGHT         = 0x0004_0000;
        const TOP_CENTRE         = 0x0008_0000;
        const TOP_FRONT_LEFT     = 0x0010_0000;
        const TOP_FRONT_CENTRE   = 0x0020_0000;
        const TOP_FRONT_RIGHT    = 0x0040_0000;
        const TOP_BACK_LEFT      = 0x0080_0000;
        const TOP_BACK_CENTRE    = 0x0100_0000;
        const TOP_BACK_RIGHT     = 0x0200_0000;
    }
}

impl Channels {
    /// Gets the number of channels.
    pub fn count(self) -> usize {
        self.bits.count_ones() as usize
    }
}

impl fmt::Display for Channels {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#032b}", self.bits)
    }
}

/// `ChannelLayout` describes common audio channel configurations.
/// Run `ffmpeg -layouts` to see the layout mappings
#[derive(Copy, Clone, Debug)]
pub enum ChannelLayout {
    Mono,
    Stereo,
    TwoPointOne,
    ThreePointZero,
    Quad,
    FivePointZero,
    FivePointOne,
    SixPointOne,
    SixPointOneBack,
    SevenPointOne,
}

impl ChannelLayout {
    /// Converts a channel `ChannelLayout` into a `Channels` bit mask.
    pub fn into_channels(self) -> Channels {
        match self {
            ChannelLayout::Mono => Channels::FRONT_LEFT,
            ChannelLayout::Stereo => Channels::FRONT_LEFT | Channels::FRONT_RIGHT,
            ChannelLayout::TwoPointOne => {
                Channels::FRONT_LEFT | Channels::FRONT_RIGHT | Channels::LFE1
            }
            ChannelLayout::ThreePointZero => {
                Channels::FRONT_LEFT | Channels::FRONT_RIGHT | Channels::FRONT_CENTRE
            }
            ChannelLayout::Quad => {
                Channels::FRONT_LEFT
                    | Channels::FRONT_RIGHT | Channels::BACK_LEFT | Channels::BACK_RIGHT
            }
            ChannelLayout::FivePointZero => {
                Channels::FRONT_LEFT
                    | Channels::FRONT_RIGHT | Channels::FRONT_CENTRE
                    | Channels::BACK_LEFT | Channels::BACK_RIGHT
            }
            ChannelLayout::FivePointOne => {
                Channels::FRONT_LEFT
                    | Channels::FRONT_RIGHT | Channels::FRONT_CENTRE | Channels::LFE1
                    | Channels::BACK_LEFT | Channels::BACK_RIGHT
            }
            ChannelLayout::SixPointOne => {
                Channels::FRONT_LEFT
                    | Channels::FRONT_RIGHT | Channels::FRONT_CENTRE | Channels::LFE1
                    | Channels::BACK_CENTRE | Channels::SIDE_LEFT | Channels::SIDE_RIGHT
            }
            ChannelLayout::SixPointOneBack => {
                Channels::FRONT_LEFT
                    | Channels::FRONT_RIGHT | Channels::FRONT_CENTRE | Channels::LFE1
                    | Channels::BACK_CENTRE | Channels::BACK_LEFT | Channels::BACK_RIGHT
            }
            ChannelLayout::SevenPointOne => {
                Channels::FRONT_LEFT
                    | Channels::FRONT_RIGHT | Channels::FRONT_CENTRE | Channels::LFE1
                    | Channels::BACK_LEFT | Channels::BACK_RIGHT
                    | Channels::SIDE_LEFT | Channels::SIDE_RIGHT
            }
        }
    }
}

impl fmt::Display for ChannelLayout {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

/// AudioInfo stored in a container format's headers and metadata
#[derive(Clone, Copy)]
pub struct AudioInfo {
    pub codec: codecs::CodecType,

    /// describes the data encoding for an audio sample.
    pub audio_format: SampleFormat,

    /// The sample rate of the audio in Hz.
    pub sample_rate: u32,

    /// The length of the encoded stream in number of frames.
    pub total_samples: u64,

    /// The number of bits per one decoded audio sample.
    pub bits_per_sample: u32,

    /// A list of in-order channels.
    pub channels: Channels,

    /// The channel layout.
    pub channel_layout: ChannelLayout,
}

impl fmt::Display for AudioInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "| Codec:                 {}\n", self.codec)?;
        write!(f, "| Sample Format:         {:?}\n", self.audio_format)?;
        write!(f, "| Sample Rate:           {}\n", self.sample_rate)?;
        write!(f, "| Total Samples:         {}\n", self.total_samples)?;
        write!(f, "| Bits per Sample:       {}\n", self.bits_per_sample)?;
        write!(f, "| Channel(s):            {}\n", self.channels.count())?;
        write!(f, "| Channel Layout:        {:?}\n", self.channel_layout)?;

        Ok(())
    }
}

/// `AudioSegment` is returned to user to perform various operations and get
/// decoded stream, audio info or encode to different format.
pub struct AudioSegment {
    /// codec flag
    codec_flag: codecs::CodecFlag,

    /// audio info stored in a container format's headers and metadata
    info: AudioInfo,

    /// audio reader
    reader: Box<dyn AudioReader>,
}

impl AudioSegment {
    /// Constructs a new `AudioSegment`.
    ///
    /// # Example
    ///
    /// ```
    /// use cauldron::audio::AudioSegment;
    /// use cauldron::codecs::CodecFlag;
    ///
    /// match AudioSegment::read("./samples/wav/test-s24le.wav", CodecFlag::WAV) {
    ///   Ok(f)  => f,
    ///   Err(e) => panic!("Couldn't open example file: {}", e)
    /// };
    /// ```

    /// read audio file from file path and returns `AudioSegment`
    pub fn read(filename: &'static str, flag: codecs::CodecFlag) -> Result<AudioSegment> {
        let file = fs::File::open(filename).unwrap();
        let file_buffer = io::BufReader::new(file);

        let mut read_res = match flag {
            codecs::CodecFlag::WAV => wav::WavReader::new(file_buffer)?,
            codecs::CodecFlag::FLAC => flac::FlacReader::new(file_buffer)?,
            _ => return errors::unsupported_error("Codec flag not supported"),
        };

        Ok(AudioSegment {
            codec_flag: flag,
            info: read_res.read_header()?,
            reader: read_res,
        })
    }

    /// returns audio info as `AudioInfo`
    pub fn info(&self) -> AudioInfo {
        self.info
    }

    pub fn number_channels(&self) -> usize {
        self.info.channels.count()
    }

    /// Returns the duration of the audio file in seconds
    ///
    /// duration = (total_samples / no_channels) / sampling_rate
    pub fn duration(&self) -> f32 {
        self.info.total_samples as f32
            / (self.number_channels() as u32 * self.info.sample_rate) as f32
    }

    /// Returns bitrate of the audio in kbps
    pub fn bitrate(&self) -> u32 {
        (self.info.sample_rate / 1000) * self.info.bits_per_sample * self.number_channels() as u32
    }

    /// Returns an iterator on samples
    pub fn samples<'a, S: Sample + 'a>(&'a mut self) -> Box<dyn AudioSamplesIterator<S> + 'a> {
        return match self.codec_flag {
            codecs::CodecFlag::WAV => wav::WavSamplesIterator::new(&mut self.reader, &self.info),
            codecs::CodecFlag::FLAC => flac::FlacSamplesIterator::new(&mut self.reader, &self.info),
            _ => unreachable!(),
        };
    }
}

impl fmt::Display for AudioSegment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "AudioInfo:\n{}\n", self.info)?;
        write!(
            f,
            "duration: {}s, bitrate: {}kbps",
            self.duration(),
            self.bitrate()
        )?;
        Ok(())
    }
}

#[test]
fn read_wav() {
    let mut au_seg = AudioSegment::read(
        "samples/wav/test-16bit-44100Hz-mono.wav",
        codecs::CodecFlag::WAV,
    ).unwrap();

    assert_eq!(au_seg.number_channels(), 1);
    assert_eq!(au_seg.info().sample_rate, 44100);
    assert_eq!(au_seg.info().bits_per_sample, 16);
    assert_eq!(au_seg.info().audio_format, SampleFormat::U16);

    let samples: Vec<i16> = au_seg.samples().map(|r| r.unwrap()).collect();

    assert_eq!(&samples[..], &[2, -3, 5, -7]);
}