1#![no_std]
57#![warn(missing_docs)]
58#![allow(clippy::style)]
59#![allow(clippy::missing_safety_doc)]
60#![cfg_attr(rustfmt, rustfmt_skip)]
61
62#[cfg(feature = "std")]
63extern crate std;
64
65extern crate alloc;
66
67pub use mp3lame_sys as ffi;
68
69use alloc::vec::Vec;
70use core::mem::{self, MaybeUninit};
71use core::num::NonZeroU32;
72use core::ptr::{self, NonNull};
73use core::{cmp, fmt};
74use core::ffi::c_int;
75
76mod input;
77pub use input::*;
78
79pub const MAX_ALBUM_ART_SIZE: usize = 128 * 1024;
81
82pub const fn max_required_buffer_size(sample_number: usize) -> usize {
87 let mut sample_extra_size = sample_number / 4;
89 if (sample_number % 4) > 0 {
90 sample_extra_size = sample_extra_size.wrapping_add(1);
91 }
92 sample_number.wrapping_add(sample_extra_size).wrapping_add(7200)
93}
94
95#[derive(Debug, Copy, Clone, Eq, PartialEq)]
96pub enum Id3TagError {
98 AlbumArtOverflow,
100}
101
102#[derive(Debug, Copy, Clone, Eq, PartialEq)]
103pub enum BuildError {
105 Generic,
107 NoMem,
109 BadBRate,
111 BadSampleFreq,
113 InternalError,
115 Other(c_int),
117}
118
119impl BuildError {
120 #[inline(always)]
121 fn from_c_int(code: c_int) -> Result<(), Self> {
122 if code >= 0 {
123 return Ok(())
124 }
125
126 match code {
127 -1 => Err(Self::Generic),
128 -10 => Err(Self::NoMem),
129 -11 => Err(Self::BadBRate),
130 -12 => Err(Self::BadSampleFreq),
131 -13 => Err(Self::InternalError),
132 _ => Err(Self::Other(code)),
133 }
134 }
135}
136
137#[cfg(feature = "std")]
138impl std::error::Error for BuildError {
139}
140
141impl fmt::Display for BuildError {
142 #[inline]
143 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
144 match self {
145 Self::Generic => fmt.write_str("error"),
146 Self::NoMem => fmt.write_str("alloc failure"),
147 Self::BadBRate => fmt.write_str("bad bitrate"),
148 Self::BadSampleFreq => fmt.write_str("bad sample frequency"),
149 Self::InternalError => fmt.write_str("internal error"),
150 Self::Other(code) => fmt.write_fmt(format_args!("error code={code}")),
151 }
152 }
153}
154
155#[derive(Debug, Copy, Clone, Eq, PartialEq)]
156pub enum EncodeError {
158 BufferTooSmall,
163 NoMem,
165 InvalidState,
169 PsychoAcoustic,
171 Other(c_int),
173}
174
175impl EncodeError {
176 #[inline(always)]
177 fn from_c_int(code: c_int) -> Result<usize, Self> {
178 if code >= 0 {
179 return Ok(code as usize)
180 }
181
182 match code {
183 -1 => Err(Self::BufferTooSmall),
184 -2 => Err(Self::NoMem),
185 -3 => Err(Self::InvalidState),
186 -4 => Err(Self::PsychoAcoustic),
187 _ => Err(Self::Other(code)),
188 }
189 }
190}
191
192#[cfg(feature = "std")]
193impl std::error::Error for EncodeError {
194}
195
196impl fmt::Display for EncodeError {
197 #[inline]
198 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
199 match self {
200 Self::BufferTooSmall => fmt.write_str("output buffer is insufficient for encoder output"),
201 Self::NoMem => fmt.write_str("alloc failure"),
202 Self::InvalidState => fmt.write_str("attempt to use uninitialized encoder"),
203 Self::PsychoAcoustic => fmt.write_str("psycho acoustic problems"),
204 Self::Other(code) => fmt.write_fmt(format_args!("error code={code}")),
205 }
206 }
207}
208
209
210#[derive(Copy, Clone)]
212#[repr(u16)]
213pub enum Bitrate {
214 Kbps8 = 8,
216 Kbps16 = 16,
218 Kbps24 = 24,
220 Kbps32 = 32,
222 Kbps40 = 40,
224 Kbps48 = 48,
226 Kbps64 = 64,
228 Kbps80 = 80,
230 Kbps96 = 96,
232 Kbps112 = 112,
234 Kbps128 = 128,
236 Kbps160 = 160,
238 Kbps192 = 192,
240 Kbps224 = 224,
242 Kbps256 = 256,
244 Kbps320 = 320,
246}
247
248pub use Bitrate as Birtate;
250
251#[derive(Copy, Clone)]
252#[repr(u8)]
253pub enum VbrMode {
255 Off = ffi::vbr_mode::vbr_off as u8,
257 Mt = ffi::vbr_mode::vbr_mt as u8,
259 Rh = ffi::vbr_mode::vbr_rh as u8,
261 Abr = ffi::vbr_mode::vbr_abr as u8,
263 Mtrh = ffi::vbr_mode::vbr_mtrh as u8,
265}
266
267impl Default for VbrMode {
268 #[inline(always)]
269 fn default() -> Self {
270 Self::Mtrh
271 }
272}
273
274#[derive(Copy, Clone)]
275#[repr(u8)]
276pub enum Mode {
278 Mono = ffi::MPEG_mode::MONO as u8,
280 Stereo = ffi::MPEG_mode::STEREO as u8,
282 JointStereo = ffi::MPEG_mode::JOINT_STEREO as u8,
284 DaulChannel = ffi::MPEG_mode::DUAL_CHANNEL as u8,
288 NotSet = ffi::MPEG_mode::NOT_SET as u8,
290}
291
292#[derive(Copy, Clone)]
295#[repr(u8)]
296pub enum Quality {
297 Best = 0,
299 SecondBest = 1,
301 NearBest = 2,
303 VeryNice = 3,
305 Nice = 4,
307 Good = 5,
309 Decent = 6,
311 Ok = 7,
313 SecondWorst = 8,
315 Worst = 9,
317}
318
319#[derive(Copy, Clone)]
320pub struct Id3Tag<'a> {
326 pub title: &'a [u8],
328 pub artist: &'a [u8],
330 pub album: &'a [u8],
332 pub album_art: &'a [u8],
341 pub year: &'a [u8],
343 pub comment: &'a [u8],
345}
346
347impl Id3Tag<'_> {
348 #[inline(always)]
349 pub const fn is_any_set(&self) -> bool {
351 !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()
352 }
353}
354
355pub struct Builder {
357 inner: NonNull<ffi::lame_global_flags>,
358}
359
360impl Builder {
361 #[inline]
362 pub fn new() -> Option<Self> {
366 let ptr = unsafe {
367 ffi::lame_init()
368 };
369
370 NonNull::new(ptr).map(|inner| Self {
371 inner
372 })
373 }
374
375 #[inline(always)]
376 pub unsafe fn as_ptr(&mut self) -> *mut ffi::lame_global_flags {
380 self.ptr()
381 }
382
383 #[inline(always)]
384 fn ptr(&mut self) -> *mut ffi::lame_global_flags {
385 self.inner.as_ptr()
386 }
387
388 #[inline]
389 pub fn set_output_sample_rate(&mut self, rate: Option<NonZeroU32>) -> Result<(), BuildError> {
399 let rate = rate.map_or(0, NonZeroU32::get).try_into().unwrap_or(c_int::MAX);
400
401 let res = unsafe {
402 ffi::lame_set_out_samplerate(self.ptr(), rate)
403 };
404
405 BuildError::from_c_int(res)
406 }
407
408 #[inline]
409 pub fn with_output_sample_rate(mut self, rate: Option<NonZeroU32>) -> Result<Self, BuildError> {
419 self.set_output_sample_rate(rate)?;
420 Ok(self)
421 }
422
423 #[inline]
424 pub fn set_sample_rate(&mut self, rate: u32) -> Result<(), BuildError> {
430 let res = unsafe {
431 ffi::lame_set_in_samplerate(self.ptr(), rate.try_into().unwrap_or(c_int::MAX))
432 };
433
434 BuildError::from_c_int(res)
435 }
436
437 #[inline]
438 pub fn with_sample_rate(mut self, rate: u32) -> Result<Self, BuildError> {
444 self.set_sample_rate(rate)?;
445 Ok(self)
446 }
447
448 #[inline]
449 pub fn set_num_channels(&mut self, num: u8) -> Result<(), BuildError> {
455 let res = unsafe {
456 ffi::lame_set_num_channels(self.ptr(), num as _)
457 };
458
459 BuildError::from_c_int(res)
460 }
461
462 #[inline]
463 pub fn with_num_channels(mut self, num: u8) -> Result<Self, BuildError> {
469 self.set_num_channels(num)?;
470 Ok(self)
471 }
472
473 #[inline]
474 pub fn set_brate(&mut self, brate: Bitrate) -> Result<(), BuildError> {
480 let res = unsafe {
481 ffi::lame_set_brate(self.ptr(), brate as _)
482 };
483
484 BuildError::from_c_int(res)
485 }
486
487 #[inline]
488 pub fn with_brate(mut self, brate: Bitrate) -> Result<Self, BuildError> {
494 self.set_brate(brate)?;
495 Ok(self)
496 }
497
498 #[inline]
499 pub fn set_mode(&mut self, mode: Mode) -> Result<(), BuildError> {
505 let res = unsafe {
506 ffi::lame_set_mode(self.ptr(), mode as _)
507 };
508
509 BuildError::from_c_int(res)
510 }
511
512 #[inline]
513 pub fn with_mode(mut self, mode: Mode) -> Result<Self, BuildError> {
519 self.set_mode(mode)?;
520 Ok(self)
521 }
522
523 #[inline]
524 pub fn set_quality(&mut self, quality: Quality) -> Result<(), BuildError> {
530 let res = unsafe {
531 ffi::lame_set_quality(self.ptr(), quality as _)
532 };
533
534 BuildError::from_c_int(res)
535 }
536
537 #[inline]
538 pub fn with_quality(mut self, quality: Quality) -> Result<Self, BuildError> {
544 self.set_quality(quality)?;
545 Ok(self)
546 }
547
548 #[inline]
549 pub fn set_vbr_quality(&mut self, quality: Quality) -> Result<(), BuildError> {
553 let res = unsafe {
554 ffi::lame_set_VBR_q(self.ptr(), quality as _)
555 };
556
557 BuildError::from_c_int(res)
558 }
559
560 #[inline]
561 pub fn with_vbr_quality(mut self, quality: Quality) -> Result<Self, BuildError> {
565 self.set_vbr_quality(quality)?;
566 Ok(self)
567 }
568
569 #[inline]
570 pub fn set_to_write_vbr_tag(&mut self, value: bool) -> Result<(), BuildError> {
576 let res = unsafe {
577 ffi::lame_set_bWriteVbrTag(self.ptr(), value as _)
578 };
579
580 BuildError::from_c_int(res)
581 }
582
583 #[inline]
584 pub fn with_to_write_vbr_tag(mut self, value: bool) -> Result<Self, BuildError> {
590 self.set_to_write_vbr_tag(value)?;
591 Ok(self)
592 }
593
594 #[inline]
595 pub fn set_vbr_mode(&mut self, value: VbrMode) -> Result<(), BuildError> {
601 let res = unsafe {
602 ffi::lame_set_VBR(self.ptr(), value as _)
603 };
604
605 BuildError::from_c_int(res)
606 }
607
608 #[inline]
609 pub fn with_vbr_mode(mut self, value: VbrMode) -> Result<Self, BuildError> {
615 self.set_vbr_mode(value)?;
616 Ok(self)
617 }
618
619 #[inline]
620 pub fn set_id3_tag(&mut self, value: Id3Tag<'_>) -> Result<(), Id3TagError> {
627 if !value.is_any_set() {
628 return Ok(());
629 }
630
631 const MAX_BUFFER: usize = 250;
632 let mut buffer = [0u8; MAX_BUFFER + 1];
633
634 unsafe {
635 ffi::id3tag_init(self.ptr());
636 ffi::id3tag_add_v2(self.ptr());
637
638 if !value.album_art.is_empty() {
639 let size = value.album_art.len();
640 if size > MAX_ALBUM_ART_SIZE {
641 return Err(Id3TagError::AlbumArtOverflow);
642 }
643 let ptr = value.album_art.as_ptr();
644 ffi::id3tag_set_albumart(self.ptr(), ptr as _, size);
645 }
646
647 if !value.title.is_empty() {
648 let size = cmp::min(MAX_BUFFER, value.title.len());
649 ptr::copy_nonoverlapping(value.title.as_ptr(), buffer.as_mut_ptr(), size);
650 buffer[size] = 0;
651 ffi::id3tag_set_title(self.ptr(), buffer.as_ptr() as _);
652 }
653
654 if !value.album.is_empty() {
655 let size = cmp::min(MAX_BUFFER, value.album.len());
656 ptr::copy_nonoverlapping(value.album.as_ptr(), buffer.as_mut_ptr(), size);
657 buffer[size] = 0;
658 ffi::id3tag_set_album(self.ptr(), buffer.as_ptr() as _);
659 }
660
661 if !value.artist.is_empty() {
662 let size = cmp::min(MAX_BUFFER, value.artist.len());
663 ptr::copy_nonoverlapping(value.artist.as_ptr(), buffer.as_mut_ptr(), size);
664 buffer[size] = 0;
665 ffi::id3tag_set_artist(self.ptr(), buffer.as_ptr() as _);
666 }
667
668 if !value.year.is_empty() {
669 let size = cmp::min(MAX_BUFFER, value.year.len());
670 ptr::copy_nonoverlapping(value.year.as_ptr(), buffer.as_mut_ptr(), size);
671 buffer[size] = 0;
672 ffi::id3tag_set_year(self.ptr(), buffer.as_ptr() as _);
673 }
674
675 if !value.comment.is_empty() {
676 let size = cmp::min(MAX_BUFFER, value.comment.len());
677 ptr::copy_nonoverlapping(value.comment.as_ptr(), buffer.as_mut_ptr(), size);
678 buffer[size] = 0;
679 ffi::id3tag_set_comment(self.ptr(), buffer.as_ptr() as _);
680 }
681 }
682
683 Ok(())
684 }
685
686 #[inline]
687 pub fn with_id3_tag(mut self, value: Id3Tag<'_>) -> Result<Self, Id3TagError> {
693 self.set_id3_tag(value)?;
694 Ok(self)
695 }
696
697 #[inline]
698 pub fn build(mut self) -> Result<Encoder, BuildError> {
702 let res = unsafe {
703 ffi::lame_init_params(self.ptr())
704 };
705
706 match BuildError::from_c_int(res) {
707 Ok(()) => {
708 let inner = self.inner;
709 mem::forget(self);
710 Ok(Encoder { inner })
711 },
712 Err(error) => Err(error),
713 }
714 }
715}
716
717impl Drop for Builder {
718 #[inline]
719 fn drop(&mut self) {
720 unsafe {
721 ffi::lame_close(self.ptr())
722 };
723 }
724}
725
726pub struct Encoder {
728 inner: NonNull<ffi::lame_global_flags>,
729}
730
731impl Encoder {
732 #[inline(always)]
733 fn ptr(&self) -> *mut ffi::lame_global_flags {
734 self.inner.as_ptr()
735 }
736
737 #[inline]
738 pub fn sample_rate(&self) -> u32 {
740 unsafe {
741 ffi::lame_get_in_samplerate(self.ptr()) as u32
742 }
743 }
744
745 #[inline]
746 pub fn num_channels(&self) -> u8 {
748 unsafe {
749 ffi::lame_get_num_channels(self.ptr()) as u8
750 }
751 }
752
753 #[inline]
754 pub fn encode(&mut self, input: impl EncoderInput, output: &mut [MaybeUninit<u8>]) -> Result<usize, EncodeError> {
765 let output_len = output.len();
766 let output_buf = output.as_mut_ptr();
767
768 let result = input.encode(self, output_buf as _, output_len);
769
770 EncodeError::from_c_int(result)
771 }
772
773 #[inline(always)]
774 pub fn encode_to_vec(&mut self, input: impl EncoderInput, output: &mut Vec<u8>) -> Result<usize, EncodeError> {
780 let original_len = output.len();
781 match self.encode(input, output.spare_capacity_mut()) {
782 Ok(written) => {
783 unsafe {
784 output.set_len(original_len.saturating_add(written));
785 }
786 Ok(written)
787 },
788 Err(error) => Err(error),
789 }
790 }
791
792 #[inline]
793 pub fn flush<T: EncoderFlush>(&mut self, output: &mut [MaybeUninit<u8>]) -> Result<usize, EncodeError> {
809 let output_len = output.len();
810 let output_buf = output.as_mut_ptr();
811
812 let result = T::flush(self, output_buf as _, output_len);
813
814 EncodeError::from_c_int(result)
815 }
816
817 #[inline(always)]
818 pub fn flush_to_vec<T: EncoderFlush>(&mut self, output: &mut Vec<u8>) -> Result<usize, EncodeError> {
824 let original_len = output.len();
825 match self.flush::<T>(output.spare_capacity_mut()) {
826 Ok(written) => {
827 unsafe {
828 output.set_len(original_len.saturating_add(written));
829 }
830 Ok(written)
831 },
832 Err(error) => Err(error),
833 }
834 }
835}
836
837impl Drop for Encoder {
838 #[inline]
839 fn drop(&mut self) {
840 unsafe {
841 ffi::lame_close(self.ptr())
842 };
843 }
844}
845
846unsafe impl Send for Encoder {}
848unsafe impl Sync for Encoder {}
850
851pub fn encoder() -> Result<Encoder, BuildError> {
853 match Builder::new() {
854 Some(mut builder) => {
855 builder.set_brate(Bitrate::Kbps192)?;
856 builder.set_quality(Quality::Best)?;
857 builder.build()
858 },
859 None => Err(BuildError::NoMem)
860 }
861}