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
use std::error::Error;
use std::fmt::Debug;
use std::path::PathBuf;

use super::WriteBlock;
use crate::FileInfo;

/// The return status of writing to a file.
#[derive(Debug, Clone, Copy)]
pub enum WriteStatus {
    /// Written ok.
    Ok,
    /// Written ok, but the file has (or is about to) reach
    /// the maximum file size for this codec. A new file
    /// will be created to hold more data.
    ///
    /// This returns the total number of files.
    /// (Including the one created with this stream and the new one that
    /// is being created right now).
    ReachedMaxSize { num_files: u32 },
}

/// A type that encodes a file in a write stream.
pub trait Encoder: Sized + 'static {
    /// The data type of a single sample. (i.e. `f32`)
    type T: Copy + Clone + Default + Send;

    /// Any additional options for creating a file with this encoder.
    type AdditionalOpts: Send + Default + Debug;

    /// Any additional information on the file.
    type FileParams: Clone + Send;

    /// The error type while opening the file.
    type OpenError: Error + Send;

    /// The error type when a fatal error occurs.
    type FatalError: Error + Send;

    /// The default number of frames in a write block.
    const DEFAULT_BLOCK_SIZE: usize;

    /// The default number of write blocks. This must be sufficiently large to
    /// ensure there are enough write blocks for the client in the worst case
    /// write latency scenerio.
    const DEFAULT_NUM_WRITE_BLOCKS: usize;

    /// Open the file for writing.
    ///
    /// * `file` - The path of the file to open.
    /// * `num_channels` - The number of audio channels in the file.
    /// * `sample_rate` - The sample rate of the audio data.
    /// * `block_size` - The block size to use.
    /// * `max_num_write_blocks` - The number of write blocks this stream is using.
    /// * `additional_opts` - Any additional encoder-specific options.
    fn new(
        file: PathBuf,
        num_channels: u16,
        sample_rate: u32,
        block_size: usize,
        num_write_blocks: usize,
        additional_opts: Self::AdditionalOpts,
    ) -> Result<(Self, FileInfo<Self::FileParams>), Self::OpenError>;

    /// Write a block of data to the file.
    ///
    /// If the write was successful, return `WriteStatus::Ok`.
    ///
    /// If the codec has a maximum file size (i.e. 4GB for WAV), then keep track of
    /// how many bytes were written. Once the file is full (or about full), finish the
    /// file, close it, and create a new file with the characters "_XXX" appended to
    /// the file name (i.e. "_001" for the first file, "_002" for the second, etc.)
    /// This helper function `num_files_to_file_name_extension()` can be used to find
    /// this extension.
    ///
    /// ## Unsafe
    /// This is marked as "unsafe" because a `data_block` may be uninitialized, causing
    /// undefined behavior if unwritten data from the block is read. Please use the value
    /// from `write_block.num_frames()` to know how many frames in the block are valid.
    /// (valid frames are from `[0..num_frames]`)
    unsafe fn encode(
        &mut self,
        write_block: &WriteBlock<Self::T>,
    ) -> Result<WriteStatus, Self::FatalError>;

    /// Finish up the file and then close it.
    fn finish_file(&mut self) -> Result<(), Self::FatalError>;

    /// Delete all created files. Do not start over.
    fn discard_file(&mut self) -> Result<(), Self::FatalError>;

    /// Delete all created files and start over from the beginning.
    fn discard_and_restart(&mut self) -> Result<(), Self::FatalError>;
}

/// Converts the current total number of files created (including the one created
/// with this stream and the new one that is being created right now) to the extension
/// to append to the end of the file name.
///
/// This extension is in the format "_XXX". (i.e. "_001", "_002", etc.)
pub fn num_files_to_file_name_extension(num_files: u32) -> String {
    if num_files <= 1 {
        return String::from("");
    }

    let extension_num = num_files - 1;
    if extension_num < 10 {
        format!("_00{}", extension_num)
    } else if extension_num < 100 {
        format!("_0{}", extension_num)
    } else {
        format!("_{}", extension_num)
    }
}