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