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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
#[cfg(feature = "flac")] use flac_sys::{FLAC__stream_encoder_new, FLAC__stream_encoder_get_state, FLAC__stream_encoder_get_verify_decoder_state, FLAC__stream_encoder_finish, FLAC__stream_encoder_process, FLAC__stream_encoder_process_interleaved}; #[cfg(feature = "libflac")] use libflac_sys::{FLAC__stream_encoder_new, FLAC__stream_encoder_get_state, FLAC__stream_encoder_get_verify_decoder_state, FLAC__stream_encoder_finish, FLAC__stream_encoder_process, FLAC__stream_encoder_process_interleaved}; use super::{StreamEncoderContainer, FlacEncoderConfig, FlacEncoderState}; use std::marker::PhantomData; use std::convert::TryFrom; use std::os::raw::c_uint; use std::{mem, ptr}; /// The [stream encoder](https://xiph.org/flac/api/group__flac__stream__encoder.html) can encode to native FLAC, /// and optionally Ogg FLAC (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files. /// /// The basic usage of this encoder is as follows: /// * The program creates an instance of an encoder using /// [`FlacEncoder::new()`](#method.new). /// * The program overrides the default settings using functions in /// [`FlacEncoderConfig`](struct.FlacEncoderConfig.html). At a minimum, the following /// functions should be called: /// * [`FlacEncoderConfig::channels()`](struct.FlacEncoderConfig.html#method.channels) /// * [`FlacEncoderConfig::bits_per_sample()`](struct.FlacEncoderConfig.html#method.bits_per_sample) /// * [`FlacEncoderConfig::sample_rate()`](struct.FlacEncoderConfig.html#method.sample_rate) /// * [`FlacEncoderConfig::ogg_serial_number()`](struct.FlacEncoderConfig.html#method.ogg_serial_number) /// (if encoding to Ogg FLAC) /// * [`FlacEncoderConfig::total_samples_estimate()`](struct.FlacEncoderConfig.html#method.total_samples_estimate) /// (if known) /// * If the application wants to control the compression level or set its own /// metadata, then the following should also be called: /// * [`FlacEncoderConfig::compression_level()`](struct.FlacEncoderConfig.html#method.compression_level) /// * [`FlacEncoderConfig::verify()`](struct.FlacEncoderConfig.html#method.verify) /// * [`FlacEncoderConfig::metadata()`](struct.FlacEncoderConfig.html#method.metadata) /// * The rest of the set functions should only be called if the client needs /// exact control over how the audio is compressed; thorough understanding /// of the FLAC format is necessary to achieve good results. /// * The program initializes the instance to validate the settings and /// prepare for encoding using /// * [`FlacEncoderConfig::init_write()`](struct.FlacEncoderConfig.html#method.init_write), or /// [`FlacEncoderConfig::init_file()`](struct.FlacEncoderConfig.html#method.init_file), or /// [`FlacEncoderConfig::init_stdout()`](struct.FlacEncoderConfig.html#method.init_stdout) for native FLAC /// * [`FlacEncoderConfig::init_write_ogg()`](struct.FlacEncoderConfig.html#method.init_write_ogg), or /// [`FlacEncoderConfig::init_file_ogg()`](struct.FlacEncoderConfig.html#method.init_file_ogg), or /// [`FlacEncoderConfig::init_stdout_ogg()`](struct.FlacEncoderConfig.html#method.init_stdout_ogg) for Ogg FLAC /// * The program calls [`FlacEncoder::process()`](#method.process) or /// [`FlacEncoder::process_interleaved()`](#method.process_interleaved) to encode data, which /// subsequently calls the callbacks when there is encoder data ready /// to be written. /// * The program finishes the encoding with [`FlacEncoder::finish()`](#method.finish), /// which causes the encoder to encode any data still in its input pipe, /// update the metadata with the final encoding statistics if output /// seeking is possible, and finally reset the encoder to the /// uninitialized state. /// Note: the stream is `finish()`ed when it's dropped, and any potential error is ignored. /// * The instance may be used again or deleted with /// [`FlacEncoder::delete()`](#method.delete). /// Note: the stream is `delete()`ed when it's dropped. /// /// In more detail, the stream encoder functions similarly to the /// stream decoder, but has fewer /// callbacks and more options. Typically the client will create a new /// instance by calling [`FlacEncoder::new()`](#method.new), then set the necessary /// parameters with functions on [`FlacEncoderConfig`](struct.FlacEncoderConfig.html), and initialize it by /// calling one of the [`FlacEncoderConfig::init_*()`](struct.FlacEncoderConfig.html#method.init_write) functions. /// /// Unlike the decoders, the stream encoder has many options that can /// affect the speed and compression ratio. When setting these parameters /// you should have some basic knowledge of the format (see the /// user-level documentation or the formal description). The functions on /// [`FlacEncoderConfig`](struct.FlacEncoderConfig.html) themselves do not validate the /// values as many are interdependent. The [`FlacEncoderConfig::init_*()`](struct.FlacEncoderConfig.html#method.init_write) /// functions will do this, so make sure to pay attention to the result /// returned by [`FlacEncoderConfig::init_*()`](struct.FlacEncoderConfig.html#method.init_write) to make sure that it is /// `Ok()`. Any parameters that are not set /// before [`FlacEncoderConfig::init_*()`](struct.FlacEncoderConfig.html#method.init_write) will take on the defaults from /// the constructor. /// /// There are three initialization functions for native FLAC, one for /// setting up the encoder to encode FLAC data to the client via /// a `Write` stream, and two for encoding directly to a file. /// /// For encoding via a `Write` stream, use [`FlacEncoderConfig::init_write()`](struct.FlacEncoderConfig.html#method.init_write). /// You must also supply a `std::io::Write` stream which will be called anytime /// there is raw encoded data to write. The client cannot seek the output due to /// [RFC 2035](https://github.com/rust-lang/rfcs/issues/2035), so the /// encoder cannot go back after encoding is finished to write back /// information that was collected while encoding, like seek point offsets, /// frame sizes, etc. /// /// For encoding directly to a file, use [`FlacEncoderConfig::init_file()`](struct.FlacEncoderConfig.html#method.init_file). /// Then you must only supply a UTF-8 filename; the encoder will handle all the callbacks /// internally. You may also supply a progress callback for periodic /// notification of the encoding progress. /// /// There are three similarly-named init functions for encoding to Ogg /// FLAC streams. /// /// The call to [`FlacEncoderConfig::init_*()`](struct.FlacEncoderConfig.html#method.init_write) currently will also immediately /// call write to the sink several times, once with the `fLaC` signature, /// and once for each encoded metadata block. Note that for Ogg FLAC /// encoding you will usually get at least twice the number of callbacks than /// with native FLAC, one for the Ogg page header and one for the page body. /// /// After initializing the instance, the client may feed audio data to the /// encoder in one of two ways: /// /// * Channel separate, through [`FlacEncoder::process()`](#method.process) - The client /// will pass an slice of buffer slices, one for each channel, to /// the encoder, each of the same length. The samples need not be /// block-aligned, but each channel should have the same number of samples. /// This function will allocate if the user supplies more than 8 channels. /// * Channel interleaved, through /// [`FlacEncoder::process_interleaved()`](#method.process_interleaved) - The client will pass a single /// slice to data that is channel-interleaved (i.e. `channel0_sample0`, /// `channel1_sample0`, ... , `channelN_sample0`, `channel0_sample1`, ...). /// Again, the samples need not be block-aligned but they must be /// sample-aligned, i.e. the first value should be `channel0_sample0` and /// the last value `channelN_sampleM`. /// /// Note that for either process call, each sample in the buffers should be a /// signed integer, right-justified to the resolution set by /// [`FlacEncoderConfig::bits_per_sample()`](struct.FlacEncoderConfig.html#method.bits_per_sample). /// For example, if the resolution is 16 bits per sample, the samples should all be in the range [-32768,32767]. /// /// When the client is finished encoding data, it calls /// [`FlacEncoder::finish()`](#method.finish), either explicitly or by dropping the encoder, /// which causes the encoder to encode any /// data still in its input pipe, and call the metadata callback with the /// final encoding statistics. Then the instance may be deleted with /// [`FlacEncoder::delete()`](#method.delete) by dropping the encoder, or initialized again to encode another /// stream. /// /// For programs that write their own metadata, but that do not know the /// actual metadata until after encoding, it is advantageous to instruct /// the encoder to write a PADDING block of the correct size, so that /// instead of rewriting the whole stream after encoding, the program can /// just overwrite the PADDING block. If only the maximum size of the /// metadata is known, the program can write a slightly larger padding /// block, then split it after encoding. /// /// Make sure you understand how lengths are calculated. All FLAC metadata /// blocks have a 4 byte header which contains the type and length. This /// length does not include the 4 bytes of the header. See the format page /// for the specification of metadata blocks and their lengths. /// /// **Note**:<br /> /// If you are writing the FLAC data to a file via callbacks, make sure it /// is open for update (e.g. mode "w+" for stdio streams). This is because /// after the first encoding pass, the encoder will try to seek back to the /// beginning of the stream, to the STREAMINFO block, to write some data /// there. (If using [`FlacEncoderConfig::init_file()`](struct.FlacEncoderConfig.html#method.init_file), the file is managed internally.) /// /// **Note**:<br /> /// [`FlacEncoder::finish()`](#method.finish) resets all settings to the constructor defaults. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] pub struct FlacEncoder<'out>(pub(super) StreamEncoderContainer, pub(super) PhantomData<&'out mut ()>); impl<'out> FlacEncoder<'out> { /// Create a new stream encoder, in a configuration wrapper, or `None` if one couldn't be allocated. #[allow(clippy::new_ret_no_self)] pub fn new() -> Option<FlacEncoderConfig> { let enc = unsafe { FLAC__stream_encoder_new() }; if !enc.is_null() { Some(FlacEncoderConfig(StreamEncoderContainer(enc))) } else { None } } /// Get the current encoder state. pub fn state(&self) -> FlacEncoderState { FlacEncoderState::try_from(unsafe { FLAC__stream_encoder_get_state((self.0).0) }).unwrap() } /// Get the state of the verify stream decoder. /// /// Useful when the stream encoder state is /// [`VerifyDecoderError`](enum.FlacEncoderState.html#variant.VerifyDecoderError). pub fn verify_decoder_state(&self) -> FlacEncoderState { FlacEncoderState::try_from(unsafe { FLAC__stream_encoder_get_verify_decoder_state((self.0).0) }).unwrap() } /// Submit data for encoding. /// /// This version allows you to supply the input data via a slice of /// slices, each slice consisting of the same amount of samples as the first one, /// representing one channel. The samples need not be block-aligned, /// but each channel should have the same number of samples. Each sample /// should be a signed integer, right-justified to the resolution set by /// [`FlacEncoderConfig::bits_per_sample()`](struct.FlacEncoderConfig.html#method.bits_per_sample). For example, if the /// resolution is 16 bits per sample, the samples should all be in the /// range [-32768,32767]. /// /// For applications where channel order is important, channels must /// follow the order as described in the /// [frame header](https://xiph.org/flac/format.html#frame_header). /// /// Requires encoder instance to be in OK state. pub fn process(&mut self, buffers: &[&[i32]]) -> Result<(), ()> { if buffers.len() <= 8 { let mut buffer = [ptr::null(); 8]; self.process_impl(&mut buffer, buffers) } else { let mut buffer = vec![ptr::null(); buffers.len()]; self.process_impl(&mut buffer, buffers) } } fn process_impl(&mut self, buffer: &mut [*const i32], buffers: &[&[i32]]) -> Result<(), ()> { let samples = buffers.iter().next().map(|b| b.len()).unwrap_or(0) as c_uint; for (pbfr, sbfr) in buffer.iter_mut().zip(buffers) { *pbfr = sbfr.as_ptr(); } if unsafe { FLAC__stream_encoder_process((self.0).0, buffer.as_ptr(), samples) } != 0 { Ok(()) } else { Err(()) } } /// Submit data for encoding. /// /// This version allows you to supply the input data where the channels /// are interleaved into a single array (i.e. `channel0_sample0`, /// `channel1_sample0`, ... , `channelN_sample0`, `channel0_sample1`, ...). /// The samples need not be block-aligned but they must be /// sample-aligned, i.e. the first value should be `channel0_sample0` /// and the last value `channelN_sampleM`. Each sample should be a signed /// integer, right-justified to the resolution set by /// [`FlacEncoderConfig::bits_per_sample()`](struct.FlacEncoderConfig.html#method.bits_per_sample). /// For example, if the resolution is 16 bits per sample, the samples should all be in the /// range [-32768,32767]. /// /// For applications where channel order is important, channels must /// follow the order as described in the /// [frame header](https://xiph.org/flac/format.html#frame_header). /// /// Requires encoder instance to be in OK state. pub fn process_interleaved(&mut self, buffer: &[i32], samples_per_channel: u32) -> Result<(), ()> { if unsafe { FLAC__stream_encoder_process_interleaved((self.0).0, buffer.as_ptr(), samples_per_channel) } != 0 { Ok(()) } else { Err(()) } } /// Finish the encoding process. /// /// Flushes the encoding buffer, releases resources, resets the encoder /// settings to their defaults, and returns the encoder state to /// `FLAC__STREAM_ENCODER_UNINITIALIZED`. Note that this can generate /// one or more write callbacks before returning, and will generate /// a metadata callback. /// /// Note that in the course of processing the last frame, errors can /// occur, so the caller should be sure to check the return value to /// ensure the file was encoded properly. /// /// In the event of a prematurely-terminated encode, it is not strictly /// necessary to call this immediately before [`FlacEncoder::delete()`](#method.delete) /// but it is good practice to match every [`FlacEncoderConfig::init_*()`](struct.FlacEncoderConfig.html#method.init_write) /// with a [`FlacEncoder::finish()`](#method.finish). /// /// This is also called by `drop()`. /// /// Returns `Err(self)` if an error occurred processing the last frame, or, if verify /// mode is set (see [`FlacEncoderConfig::verify()`](struct.FlacEncoderConfig.html#method.verify)), there was a /// verify mismatch; else the config wrapper. /// /// If `Err()`, caller should check the state with [`state()`](#method.state) for more information about the error. pub fn finish(mut self) -> Result<FlacEncoderConfig, FlacEncoder<'out>> { if unsafe { FLAC__stream_encoder_finish((self.0).0) } != 0 { Ok(FlacEncoderConfig(mem::replace(&mut self.0, StreamEncoderContainer(ptr::null_mut())))) } else { Err(self) } } } impl<'out> Drop for FlacEncoder<'out> { fn drop(&mut self) { if !(self.0).0.is_null() { unsafe { FLAC__stream_encoder_finish((self.0).0) }; } } }