mp3lame_encoder/lib.rs
1//!High level wrapper over [mp3lame-sys](https://crates.io/crates/mp3lame-sys)
2//!
3//!## Example
4//!
5//!```rust
6//!use mp3lame_encoder::{Builder, Id3Tag, DualPcm, FlushNoGap};
7//!
8//!let id3tag = Id3Tag {
9//! title: b"My title",
10//! artist: &[],
11//! album: b"My album",
12//! album_art: &[],
13//! year: b"Current year",
14//! comment: b"Just my comment",
15//!};
16//!
17//!//Methods prefixed with `with_*` return Self for convenience
18//!let mut mp3_encoder = Builder::new().expect("Create LAME builder")
19//! .with_num_channels(2).expect("set channels")
20//! .with_sample_rate(44_100).expect("set sample rate")
21//! .with_brate(mp3lame_encoder::Bitrate::Kbps192).expect("set brate")
22//! .with_quality(mp3lame_encoder::Quality::Best).expect("set quality")
23//! .with_vbr_mode(mp3lame_encoder::VbrMode::Mtrh).expect("set VBR")
24//! .with_vbr_quality(mp3lame_encoder::Quality::Best).expect("set VBR quality")
25//! .with_to_write_vbr_tag(true).expect("set to write VBR tag")
26//! .with_id3_tag(id3tag).expect("set tags")
27//! .build().expect("To initialize LAME encoder");
28//!
29//!//use actual PCM data
30//!let input = DualPcm {
31//! left: &[0u16, 1024],
32//! right: &[0u16, 1024],
33//!};
34//!
35//!let mut mp3_out_buffer = Vec::new();
36//!mp3_out_buffer.reserve(mp3lame_encoder::max_required_buffer_size(input.left.len()));
37//!let encoded_size = mp3_encoder.encode(input, mp3_out_buffer.spare_capacity_mut()).expect("To encode");
38//!unsafe {
39//! mp3_out_buffer.set_len(mp3_out_buffer.len().wrapping_add(encoded_size));
40//!}
41//!
42//!let encoded_size = mp3_encoder.flush::<FlushNoGap>(mp3_out_buffer.spare_capacity_mut()).expect("to flush");
43//!unsafe {
44//! mp3_out_buffer.set_len(mp3_out_buffer.len().wrapping_add(encoded_size));
45//!}
46//!//At this point your mp3_out_buffer should have full MP3 data, ready to be written on file system or whatever
47//!
48//!if mp3_encoder.lame_tag_size() > 0 {
49//! let id3v2_tag_boundary = mp3_encoder.id3v2_tag_size();
50//! assert_eq!(id3v2_tag_boundary, 158);
51//! let mut lame_tag = [core::mem::MaybeUninit::uninit(); 1024];
52//! assert!(lame_tag.len() >= mp3_encoder.lame_tag_size(), "Increase buffer size");
53//! let lame_tag_size = mp3_encoder.lame_tag_encode(&mut lame_tag).expect("to write lame tag");
54//! assert_eq!(mp3_encoder.lame_tag_size(), lame_tag_size.get());
55//!
56//! //If you need VBR tag then you need to write mp3 file in following order
57//! //- id3v2 tag
58//! //- VBR tag
59//! //- actual mp3 content
60//! let chunks_to_write = [
61//! &mp3_out_buffer[..id3v2_tag_boundary],
62//! unsafe {
63//! core::slice::from_raw_parts(lame_tag.as_ptr() as *const u8, lame_tag_size.get())
64//! },
65//! &mp3_out_buffer[id3v2_tag_boundary..],
66//! ];
67//!} else {
68//! let chunks_to_write = [
69//! &mp3_out_buffer[..]
70//! ];
71//!}
72//!```
73
74#![no_std]
75#![warn(missing_docs)]
76#![allow(clippy::style)]
77#![allow(clippy::missing_safety_doc)]
78#![cfg_attr(rustfmt, rustfmt_skip)]
79
80#[cfg(feature = "std")]
81extern crate std;
82
83extern crate alloc;
84
85pub use mp3lame_sys as ffi;
86
87use alloc::vec::Vec;
88use core::mem::{self, MaybeUninit};
89use core::num::{NonZeroU32, NonZeroUsize};
90use core::ptr::{self, NonNull};
91use core::{cmp, fmt};
92use core::ffi::c_int;
93
94mod input;
95pub use input::*;
96
97///Maximum size of album art
98pub const MAX_ALBUM_ART_SIZE: usize = 128 * 1024;
99
100///Calculates maximum required size for specified number of samples.
101///
102///Note that actual requirement may vary depending on encoder parameters,
103///but this size should be generally enough for encoding given number of samples
104pub const fn max_required_buffer_size(sample_number: usize) -> usize {
105 //add 25% sample number + mp3 frame size 7200
106 let mut sample_extra_size = sample_number / 4;
107 if (sample_number % 4) > 0 {
108 sample_extra_size = sample_extra_size.wrapping_add(1);
109 }
110 sample_number.wrapping_add(sample_extra_size).wrapping_add(7200)
111}
112
113#[derive(Debug, Copy, Clone, Eq, PartialEq)]
114///ID3 setter errors
115pub enum Id3TagError {
116 ///Specified buffer exceed limit of 128kb
117 AlbumArtOverflow,
118}
119
120#[derive(Debug, Copy, Clone, Eq, PartialEq)]
121///Encoder builder errors
122pub enum BuildError {
123 ///Generic error, indicates invalid input or state
124 Generic,
125 ///Failed to allocate memory
126 NoMem,
127 ///Invalid brate
128 BadBRate,
129 ///Invalid sample frequency
130 BadSampleFreq,
131 ///Internal error
132 InternalError,
133 ///Other errors, most likely unexpected.
134 Other(c_int),
135}
136
137impl BuildError {
138 #[inline(always)]
139 fn from_c_int(code: c_int) -> Result<(), Self> {
140 if code >= 0 {
141 return Ok(())
142 }
143
144 match code {
145 -1 => Err(Self::Generic),
146 -10 => Err(Self::NoMem),
147 -11 => Err(Self::BadBRate),
148 -12 => Err(Self::BadSampleFreq),
149 -13 => Err(Self::InternalError),
150 _ => Err(Self::Other(code)),
151 }
152 }
153}
154
155#[cfg(feature = "std")]
156impl std::error::Error for BuildError {
157}
158
159impl fmt::Display for BuildError {
160 #[inline]
161 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
162 match self {
163 Self::Generic => fmt.write_str("error"),
164 Self::NoMem => fmt.write_str("alloc failure"),
165 Self::BadBRate => fmt.write_str("bad bitrate"),
166 Self::BadSampleFreq => fmt.write_str("bad sample frequency"),
167 Self::InternalError => fmt.write_str("internal error"),
168 Self::Other(code) => fmt.write_fmt(format_args!("error code={code}")),
169 }
170 }
171}
172
173#[derive(Debug, Copy, Clone, Eq, PartialEq)]
174///Encoder errors
175pub enum EncodeError {
176 ///Indicates output buffer is insufficient.
177 ///
178 ///Consider using [max_required_buffer_size] to determine required
179 ///space to alloc.
180 BufferTooSmall,
181 ///Failed to allocate memory
182 NoMem,
183 ///Invalid encoder state
184 ///
185 ///Should not happen if encoder created through builder
186 InvalidState,
187 ///Psycho acoustic problems, whatever it means.
188 PsychoAcoustic,
189 ///Other errors, most likely unexpected.
190 Other(c_int),
191}
192
193impl EncodeError {
194 #[inline(always)]
195 fn from_c_int(code: c_int) -> Result<usize, Self> {
196 if code >= 0 {
197 return Ok(code as usize)
198 }
199
200 match code {
201 -1 => Err(Self::BufferTooSmall),
202 -2 => Err(Self::NoMem),
203 -3 => Err(Self::InvalidState),
204 -4 => Err(Self::PsychoAcoustic),
205 _ => Err(Self::Other(code)),
206 }
207 }
208}
209
210#[cfg(feature = "std")]
211impl std::error::Error for EncodeError {
212}
213
214impl fmt::Display for EncodeError {
215 #[inline]
216 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
217 match self {
218 Self::BufferTooSmall => fmt.write_str("output buffer is insufficient for encoder output"),
219 Self::NoMem => fmt.write_str("alloc failure"),
220 Self::InvalidState => fmt.write_str("attempt to use uninitialized encoder"),
221 Self::PsychoAcoustic => fmt.write_str("psycho acoustic problems"),
222 Self::Other(code) => fmt.write_fmt(format_args!("error code={code}")),
223 }
224 }
225}
226
227
228///Enumeration of valid values for `set_brate`
229#[derive(Copy, Clone)]
230#[repr(u16)]
231pub enum Bitrate {
232 ///8_000
233 Kbps8 = 8,
234 ///16_000
235 Kbps16 = 16,
236 ///24_000
237 Kbps24 = 24,
238 ///32_000
239 Kbps32 = 32,
240 ///40_000
241 Kbps40 = 40,
242 ///48_000
243 Kbps48 = 48,
244 ///64_000
245 Kbps64 = 64,
246 ///80_000
247 Kbps80 = 80,
248 ///96_000
249 Kbps96 = 96,
250 ///112_000
251 Kbps112 = 112,
252 ///128_000
253 Kbps128 = 128,
254 ///160_000
255 Kbps160 = 160,
256 ///192_000
257 Kbps192 = 192,
258 ///224_000
259 Kbps224 = 224,
260 ///256_000
261 Kbps256 = 256,
262 ///320_000
263 Kbps320 = 320,
264}
265
266///Alias to `Bitrate` with incorrect spelling
267pub use Bitrate as Birtate;
268
269#[derive(Copy, Clone)]
270#[repr(u8)]
271///Possible VBR types
272pub enum VbrMode {
273 ///Off.
274 Off = ffi::vbr_mode::vbr_off as u8,
275 ///MT.
276 Mt = ffi::vbr_mode::vbr_mt as u8,
277 ///RH.
278 Rh = ffi::vbr_mode::vbr_rh as u8,
279 ///ABR.
280 Abr = ffi::vbr_mode::vbr_abr as u8,
281 ///MTRH.
282 Mtrh = ffi::vbr_mode::vbr_mtrh as u8,
283}
284
285impl Default for VbrMode {
286 #[inline(always)]
287 fn default() -> Self {
288 Self::Mtrh
289 }
290}
291
292#[derive(Copy, Clone)]
293#[repr(u8)]
294///Possible modes for encoder
295pub enum Mode {
296 ///Mono.
297 Mono = ffi::MPEG_mode::MONO as u8,
298 ///Stereo.
299 Stereo = ffi::MPEG_mode::STEREO as u8,
300 ///Joint stereo.
301 JointStereo = ffi::MPEG_mode::JOINT_STEREO as u8,
302 ///Dual channel
303 ///
304 ///Unsupported so far.
305 DaulChannel = ffi::MPEG_mode::DUAL_CHANNEL as u8,
306 ///Not set.
307 NotSet = ffi::MPEG_mode::NOT_SET as u8,
308}
309
310///Possible quality parameter.
311///From best(0) to worst(9)
312#[derive(Copy, Clone)]
313#[repr(u8)]
314pub enum Quality {
315 ///Best possible quality
316 Best = 0,
317 ///Second best
318 SecondBest = 1,
319 ///Close to best
320 NearBest = 2,
321 ///Very nice
322 VeryNice = 3,
323 ///Nice
324 Nice = 4,
325 ///Good
326 Good = 5,
327 ///Decent
328 Decent = 6,
329 ///Okayish
330 Ok = 7,
331 ///Almost worst
332 SecondWorst = 8,
333 ///Worst
334 Worst = 9,
335}
336
337#[derive(Copy, Clone)]
338///ID3 tag as raw bytes.
339///
340///Use empty slice for `None`
341///
342///At the current moment, only up to 250 characters will be copied.
343pub struct Id3Tag<'a> {
344 ///Track's Title
345 pub title: &'a [u8],
346 ///Artist name
347 pub artist: &'a [u8],
348 ///Album name
349 pub album: &'a [u8],
350 ///Album art
351 ///
352 ///Must be image data.
353 ///
354 ///Allowed formats: PNG, JPG, GIF
355 ///
356 ///Maximum size is defined by constant MAX_ALBUM_ART_SIZE
357 ///When setting this metadata, make sure allocate at least MAX_ALBUM_ART_SIZE
358 pub album_art: &'a [u8],
359 ///Year
360 pub year: &'a [u8],
361 ///Comment
362 pub comment: &'a [u8],
363}
364
365impl Id3Tag<'_> {
366 #[inline(always)]
367 ///Returns true if any is set
368 pub const fn is_any_set(&self) -> bool {
369 !self.title.is_empty() || !self.artist.is_empty() || !self.album.is_empty() || !self.album_art.is_empty() || !self.year.is_empty() || !self.comment.is_empty()
370 }
371}
372
373///Builder of C LAME encoder.
374pub struct Builder {
375 inner: NonNull<ffi::lame_global_flags>,
376}
377
378impl Builder {
379 #[inline]
380 ///Creates new encoder with default parameters: J-Stereo, 44.1khz 128kbps CBR mp3 file at quality 5
381 ///
382 ///Returns `None` if unable to allocate struct.
383 pub fn new() -> Option<Self> {
384 let ptr = unsafe {
385 ffi::lame_init()
386 };
387
388 NonNull::new(ptr).map(|inner| Self {
389 inner
390 })
391 }
392
393 #[inline(always)]
394 ///Get access to underlying LAME structure, without dropping ownership.
395 ///
396 ///User must guarantee not to close or dealloc this pointer
397 pub unsafe fn as_ptr(&mut self) -> *mut ffi::lame_global_flags {
398 self.ptr()
399 }
400
401 #[inline(always)]
402 fn ptr(&mut self) -> *mut ffi::lame_global_flags {
403 self.inner.as_ptr()
404 }
405
406 #[inline]
407 ///Sets output sample rate.
408 ///
409 ///The default `None` allows LAME to pick the best value.
410 ///The supported values are:
411 /// - MPEG1: `32_000`, `44_100` and `48_000`
412 /// - MPEG2: `16_000`, `22_050` and `24_000`
413 /// - MPEG2.5: `8_000`, `11_025` and `12_000`
414 ///
415 ///Returns `Ok(())` the requested value is supported and was set successfully.
416 pub fn set_output_sample_rate(&mut self, rate: Option<NonZeroU32>) -> Result<(), BuildError> {
417 let rate = rate.map_or(0, NonZeroU32::get).try_into().unwrap_or(c_int::MAX);
418
419 let res = unsafe {
420 ffi::lame_set_out_samplerate(self.ptr(), rate)
421 };
422
423 BuildError::from_c_int(res)
424 }
425
426 #[inline]
427 ///Sets output sample rate using the builder pattern.
428 ///
429 ///The default `None` allows LAME to pick the best value.
430 ///The supported values are:
431 /// - MPEG1: `32_000`, `44_100` and `48_000`
432 /// - MPEG2: `16_000`, `22_050` and `24_000`
433 /// - MPEG2.5: `8_000`, `11_025` and `12_000`
434 ///
435 ///Returns `Ok(())` the requested value is supported and was set successfully.
436 pub fn with_output_sample_rate(mut self, rate: Option<NonZeroU32>) -> Result<Self, BuildError> {
437 self.set_output_sample_rate(rate)?;
438 Ok(self)
439 }
440
441 #[inline]
442 ///Sets input sample rate.
443 ///
444 ///Defaults to 44_100.
445 ///
446 ///Returns whether it is supported or not.
447 pub fn set_sample_rate(&mut self, rate: u32) -> Result<(), BuildError> {
448 let res = unsafe {
449 ffi::lame_set_in_samplerate(self.ptr(), rate.try_into().unwrap_or(c_int::MAX))
450 };
451
452 BuildError::from_c_int(res)
453 }
454
455 #[inline]
456 ///Sets input sample rate using the builder pattern.
457 ///
458 ///Defaults to 44_100.
459 ///
460 ///Returns an error if it is not supported.
461 pub fn with_sample_rate(mut self, rate: u32) -> Result<Self, BuildError> {
462 self.set_sample_rate(rate)?;
463 Ok(self)
464 }
465
466 #[inline]
467 ///Sets number of channels.
468 ///
469 ///Defaults to 2.
470 ///
471 ///Returns whether it is supported or not.
472 pub fn set_num_channels(&mut self, num: u8) -> Result<(), BuildError> {
473 let res = unsafe {
474 ffi::lame_set_num_channels(self.ptr(), num as _)
475 };
476
477 BuildError::from_c_int(res)
478 }
479
480 #[inline]
481 ///Sets sample rate using the builder pattern.
482 ///
483 ///Defaults to 2.
484 ///
485 ///Returns an error if it is not supported.
486 pub fn with_num_channels(mut self, num: u8) -> Result<Self, BuildError> {
487 self.set_num_channels(num)?;
488 Ok(self)
489 }
490
491 #[inline]
492 ///Sets bitrate (as kbps).
493 ///
494 ///Defaults to compression ratio of 11.
495 ///
496 ///Returns whether it is supported or not.
497 pub fn set_brate(&mut self, brate: Bitrate) -> Result<(), BuildError> {
498 let res = unsafe {
499 ffi::lame_set_brate(self.ptr(), brate as _)
500 };
501
502 BuildError::from_c_int(res)
503 }
504
505 #[inline]
506 ///Sets bitrate (as kbps) using the builder pattern.
507 ///
508 ///Defaults to compression ratio of 11.
509 ///
510 ///Returns an error if it is not supported.
511 pub fn with_brate(mut self, brate: Bitrate) -> Result<Self, BuildError> {
512 self.set_brate(brate)?;
513 Ok(self)
514 }
515
516 #[inline]
517 ///Sets MPEG mode.
518 ///
519 ///Default is picked by LAME depending on compression ration and input channels.
520 ///
521 ///Returns whether it is supported or not.
522 pub fn set_mode(&mut self, mode: Mode) -> Result<(), BuildError> {
523 let res = unsafe {
524 ffi::lame_set_mode(self.ptr(), mode as _)
525 };
526
527 BuildError::from_c_int(res)
528 }
529
530 #[inline]
531 ///Sets MPEG mode using the builder pattern.
532 ///
533 ///Default is picked by LAME depending on compression ration and input channels.
534 ///
535 ///Returns an error if it is not supported.
536 pub fn with_mode(mut self, mode: Mode) -> Result<Self, BuildError> {
537 self.set_mode(mode)?;
538 Ok(self)
539 }
540
541 #[inline]
542 ///Sets quality.
543 ///
544 ///Default is good one(5)
545 ///
546 ///Returns whether it is supported or not.
547 pub fn set_quality(&mut self, quality: Quality) -> Result<(), BuildError> {
548 let res = unsafe {
549 ffi::lame_set_quality(self.ptr(), quality as _)
550 };
551
552 BuildError::from_c_int(res)
553 }
554
555 #[inline]
556 ///Sets quality using the builder pattern.
557 ///
558 ///Default is good one(5)
559 ///
560 ///Returns an error if it is not supported.
561 pub fn with_quality(mut self, quality: Quality) -> Result<Self, BuildError> {
562 self.set_quality(quality)?;
563 Ok(self)
564 }
565
566 #[inline]
567 ///Sets VBR quality.
568 ///
569 ///Returns whether it is supported or not.
570 pub fn set_vbr_quality(&mut self, quality: Quality) -> Result<(), BuildError> {
571 let res = unsafe {
572 ffi::lame_set_VBR_q(self.ptr(), quality as _)
573 };
574
575 BuildError::from_c_int(res)
576 }
577
578 #[inline]
579 ///Sets VBR quality using the builder pattern.
580 ///
581 ///Returns an error if it is not supported.
582 pub fn with_vbr_quality(mut self, quality: Quality) -> Result<Self, BuildError> {
583 self.set_vbr_quality(quality)?;
584 Ok(self)
585 }
586
587 #[inline]
588 ///Sets whether to write VBR tag.
589 ///
590 ///Default is true.
591 ///
592 ///Returns whether it is supported or not.
593 pub fn set_to_write_vbr_tag(&mut self, value: bool) -> Result<(), BuildError> {
594 let res = unsafe {
595 ffi::lame_set_bWriteVbrTag(self.ptr(), value as _)
596 };
597
598 BuildError::from_c_int(res)
599 }
600
601 #[inline]
602 ///Sets whether to write VBR tag using the builder pattern.
603 ///
604 ///Default is true.
605 ///
606 ///Returns an error if it is not supported.
607 pub fn with_to_write_vbr_tag(mut self, value: bool) -> Result<Self, BuildError> {
608 self.set_to_write_vbr_tag(value)?;
609 Ok(self)
610 }
611
612 #[inline]
613 ///Sets VBR mode.
614 ///
615 ///Default is off (i.e. CBR)
616 ///
617 ///Returns whether it is supported or not.
618 pub fn set_vbr_mode(&mut self, value: VbrMode) -> Result<(), BuildError> {
619 let res = unsafe {
620 ffi::lame_set_VBR(self.ptr(), value as _)
621 };
622
623 BuildError::from_c_int(res)
624 }
625
626 #[inline]
627 ///Sets VBR mode using the bulder pattern.
628 ///
629 ///Default is off (i.e. CBR)
630 ///
631 ///Returns an error if it is not supported.
632 pub fn with_vbr_mode(mut self, value: VbrMode) -> Result<Self, BuildError> {
633 self.set_vbr_mode(value)?;
634 Ok(self)
635 }
636
637 #[inline]
638 ///Sets id3tag tag.
639 ///
640 ///If [FlushGap] is used, then `v1` will not be added.
641 ///But `v2` is always added at the beginning.
642 ///
643 ///Returns whether it is supported or not.
644 pub fn set_id3_tag(&mut self, value: Id3Tag<'_>) -> Result<(), Id3TagError> {
645 if !value.is_any_set() {
646 return Ok(());
647 }
648
649 const MAX_BUFFER: usize = 250;
650 let mut buffer = [0u8; MAX_BUFFER + 1];
651
652 unsafe {
653 ffi::id3tag_init(self.ptr());
654 ffi::id3tag_add_v2(self.ptr());
655
656 if !value.album_art.is_empty() {
657 let size = value.album_art.len();
658 if size > MAX_ALBUM_ART_SIZE {
659 return Err(Id3TagError::AlbumArtOverflow);
660 }
661 let ptr = value.album_art.as_ptr();
662 ffi::id3tag_set_albumart(self.ptr(), ptr as _, size);
663 }
664
665 if !value.title.is_empty() {
666 let size = cmp::min(MAX_BUFFER, value.title.len());
667 ptr::copy_nonoverlapping(value.title.as_ptr(), buffer.as_mut_ptr(), size);
668 buffer[size] = 0;
669 ffi::id3tag_set_title(self.ptr(), buffer.as_ptr() as _);
670 }
671
672 if !value.album.is_empty() {
673 let size = cmp::min(MAX_BUFFER, value.album.len());
674 ptr::copy_nonoverlapping(value.album.as_ptr(), buffer.as_mut_ptr(), size);
675 buffer[size] = 0;
676 ffi::id3tag_set_album(self.ptr(), buffer.as_ptr() as _);
677 }
678
679 if !value.artist.is_empty() {
680 let size = cmp::min(MAX_BUFFER, value.artist.len());
681 ptr::copy_nonoverlapping(value.artist.as_ptr(), buffer.as_mut_ptr(), size);
682 buffer[size] = 0;
683 ffi::id3tag_set_artist(self.ptr(), buffer.as_ptr() as _);
684 }
685
686 if !value.year.is_empty() {
687 let size = cmp::min(MAX_BUFFER, value.year.len());
688 ptr::copy_nonoverlapping(value.year.as_ptr(), buffer.as_mut_ptr(), size);
689 buffer[size] = 0;
690 ffi::id3tag_set_year(self.ptr(), buffer.as_ptr() as _);
691 }
692
693 if !value.comment.is_empty() {
694 let size = cmp::min(MAX_BUFFER, value.comment.len());
695 ptr::copy_nonoverlapping(value.comment.as_ptr(), buffer.as_mut_ptr(), size);
696 buffer[size] = 0;
697 ffi::id3tag_set_comment(self.ptr(), buffer.as_ptr() as _);
698 }
699 }
700
701 Ok(())
702 }
703
704 #[inline]
705 ///Sets id3tag tag using the builder pattern.
706 ///
707 ///If [FlushGap] is used, then `v1` will not be added.
708 ///
709 ///Returns an error if it is not supported.
710 pub fn with_id3_tag(mut self, value: Id3Tag<'_>) -> Result<Self, Id3TagError> {
711 self.set_id3_tag(value)?;
712 Ok(self)
713 }
714
715 #[inline]
716 ///Attempts to initialize encoder with specified parameters.
717 ///
718 ///Returns `None` if parameters are invalid or incompatible.
719 pub fn build(mut self) -> Result<Encoder, BuildError> {
720 let res = unsafe {
721 ffi::lame_init_params(self.ptr())
722 };
723
724 match BuildError::from_c_int(res) {
725 Ok(()) => {
726 let inner = self.inner;
727 mem::forget(self);
728 Ok(Encoder { inner })
729 },
730 Err(error) => Err(error),
731 }
732 }
733}
734
735impl Drop for Builder {
736 #[inline]
737 fn drop(&mut self) {
738 unsafe {
739 ffi::lame_close(self.ptr())
740 };
741 }
742}
743
744///LAME Encoder.
745pub struct Encoder {
746 inner: NonNull<ffi::lame_global_flags>,
747}
748
749impl Encoder {
750 #[inline(always)]
751 fn ptr(&self) -> *mut ffi::lame_global_flags {
752 self.inner.as_ptr()
753 }
754
755 #[inline]
756 ///Returns sample rate.
757 pub fn sample_rate(&self) -> u32 {
758 unsafe {
759 ffi::lame_get_in_samplerate(self.ptr()) as u32
760 }
761 }
762
763 #[inline]
764 ///Returns number of channels.
765 pub fn num_channels(&self) -> u8 {
766 unsafe {
767 ffi::lame_get_num_channels(self.ptr()) as u8
768 }
769 }
770
771 #[inline]
772 ///Returns indication whether encoder is configured to write LAME tag
773 pub fn is_lame_tag_written(&self) -> bool {
774 unsafe {
775 ffi::lame_get_bWriteVbrTag(self.ptr()) != 0
776 }
777 }
778
779 #[inline]
780 ///Returns size of ths [Id3Tag] written, if any
781 pub fn id3v2_tag_size(&self) -> usize {
782 unsafe {
783 ffi::lame_get_id3v2_tag(self.ptr(), ptr::null_mut(), 0)
784 }
785 }
786
787 #[inline]
788 ///Retrieves size of the lame tag
789 pub fn lame_tag_size(&self) -> usize {
790 unsafe {
791 ffi::lame_get_lametag_frame(self.ptr(), ptr::null_mut(), 0)
792 }
793 }
794
795 #[inline]
796 ///Attempts to write lame tag into `output` returning number of written bytes in case of success
797 ///
798 ///Note that if you write [Id3Tag] you must not write this tag at the start.
799 ///Instead you must insert Lame Tag after [Id3Tag] position.
800 ///
801 ///To determine where [Id3Tag] ends in output stream, you can use [Encoder::id3v2_tag_size]
802 ///which returns full size of [Id3Tag] metadata written (therefore indicating where actual mp3
803 ///content is starting)
804 pub fn lame_tag_encode(&self, output: &mut [MaybeUninit<u8>]) -> Option<NonZeroUsize> {
805 //lame_get_lametag_frame() returns full required size in case `output` is not sufficient,
806 //so first manually check buffer size
807 if output.len() < self.lame_tag_size() {
808 None
809 } else {
810 NonZeroUsize::new(unsafe {
811 ffi::lame_get_lametag_frame(self.ptr(), output.as_mut_ptr() as _, output.len())
812 })
813 }
814 }
815
816 #[inline]
817 ///Attempts to write lame tag into `output` returning number of written bytes in case of success
818 ///
819 ///Refer to [Encoder::lame_tag_encode] for details
820 pub fn lame_tag_encode_to_vec(&self, output: &mut Vec<u8>) -> Option<NonZeroUsize> {
821 let original_len = output.len();
822 match self.lame_tag_encode(output.spare_capacity_mut()) {
823 Some(written) => {
824 unsafe {
825 output.set_len(original_len.saturating_add(written.get()));
826 }
827 Some(written)
828 },
829 None => None
830 }
831 }
832
833 #[inline]
834 ///Attempts to encode PCM data, writing whatever available onto `output` buffer
835 ///
836 ///### Arguments:
837 ///
838 /// - `input` - Data input. Can be [MonoPcm], [DualPcm] or [InterleavedPcm]
839 /// - `output` - Output buffer to write into.
840 ///
841 ///### Result:
842 ///On success, returns number of bytes written (can be 0).
843 ///Otherwise returns error indicating potential issue.
844 pub fn encode(&mut self, input: impl EncoderInput, output: &mut [MaybeUninit<u8>]) -> Result<usize, EncodeError> {
845 let output_len = output.len();
846 let output_buf = output.as_mut_ptr();
847
848 let result = input.encode(self, output_buf as _, output_len);
849
850 EncodeError::from_c_int(result)
851 }
852
853 #[inline(always)]
854 ///Attempts to encode PCM data, writing whatever available onto `output` buffer
855 ///
856 ///`output` size is adjusted on success only
857 ///
858 ///Refer for details to `encode()`
859 pub fn encode_to_vec(&mut self, input: impl EncoderInput, output: &mut Vec<u8>) -> Result<usize, EncodeError> {
860 let original_len = output.len();
861 match self.encode(input, output.spare_capacity_mut()) {
862 Ok(written) => {
863 unsafe {
864 output.set_len(original_len.saturating_add(written));
865 }
866 Ok(written)
867 },
868 Err(error) => Err(error),
869 }
870 }
871
872 #[inline]
873 ///Attempts flush all data, writing whatever available onto `output` buffer
874 ///Padding with 0 to complete MP3
875 ///
876 ///### Type:
877 ///
878 ///- [FlushNoGap] - performs flush, using ancillary data to fill gaps;
879 ///- [FlushGap] - performs flush, padding with 0;
880 ///
881 ///### Arguments:
882 ///
883 /// - `output` - Output buffer to write into. As it is final action, you need at least 7200 bytes to hold at MP3 data.
884 ///
885 ///### Result:
886 ///On success, returns number of bytes written (can be 0).
887 ///Otherwise returns error indicating potential issue.
888 pub fn flush<T: EncoderFlush>(&mut self, output: &mut [MaybeUninit<u8>]) -> Result<usize, EncodeError> {
889 let output_len = output.len();
890 let output_buf = output.as_mut_ptr();
891
892 let result = T::flush(self, output_buf as _, output_len);
893
894 EncodeError::from_c_int(result)
895 }
896
897 #[inline(always)]
898 ///Attempts flush all data, writing whatever available onto `output` buffer.
899 ///
900 ///`output` size is adjusted on success only
901 ///
902 ///Refer for details to `flush()`
903 pub fn flush_to_vec<T: EncoderFlush>(&mut self, output: &mut Vec<u8>) -> Result<usize, EncodeError> {
904 let original_len = output.len();
905 match self.flush::<T>(output.spare_capacity_mut()) {
906 Ok(written) => {
907 unsafe {
908 output.set_len(original_len.saturating_add(written));
909 }
910 Ok(written)
911 },
912 Err(error) => Err(error),
913 }
914 }
915}
916
917impl Drop for Encoder {
918 #[inline]
919 fn drop(&mut self) {
920 unsafe {
921 ffi::lame_close(self.ptr())
922 };
923 }
924}
925
926/// According to LAME 3.99.5 HACKING, it is thread-safe.
927unsafe impl Send for Encoder {}
928/// According to LAME 3.99.5 HACKING, it is thread-safe.
929unsafe impl Sync for Encoder {}
930
931///Creates default encoder with 192kbps bitrate and best possible quality.
932pub fn encoder() -> Result<Encoder, BuildError> {
933 match Builder::new() {
934 Some(mut builder) => {
935 builder.set_brate(Bitrate::Kbps192)?;
936 builder.set_quality(Quality::Best)?;
937 builder.build()
938 },
939 None => Err(BuildError::NoMem)
940 }
941}