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::ptr::{self, NonNull};
72use core::{cmp, fmt};
73
74mod input;
75pub use input::*;
76
77pub const MAX_ALBUM_ART_SIZE: usize = 128 * 1024;
79
80pub const fn max_required_buffer_size(sample_number: usize) -> usize {
85 let mut sample_extra_size = sample_number / 4;
87 if (sample_number % 4) > 0 {
88 sample_extra_size = sample_extra_size.wrapping_add(1);
89 }
90 sample_number.wrapping_add(sample_extra_size).wrapping_add(7200)
91}
92
93#[derive(Debug, Copy, Clone, Eq, PartialEq)]
94pub enum Id3TagError {
96 AlbumArtOverflow,
98}
99
100#[derive(Debug, Copy, Clone, Eq, PartialEq)]
101pub enum BuildError {
103 Generic,
105 NoMem,
107 BadBRate,
109 BadSampleFreq,
111 InternalError,
113 Other(libc::c_int),
115}
116
117impl BuildError {
118 #[inline(always)]
119 fn from_c_int(code: libc::c_int) -> Result<(), Self> {
120 if code >= 0 {
121 return Ok(())
122 }
123
124 match code {
125 -1 => Err(Self::Generic),
126 -10 => Err(Self::NoMem),
127 -11 => Err(Self::BadBRate),
128 -12 => Err(Self::BadSampleFreq),
129 -13 => Err(Self::InternalError),
130 _ => Err(Self::Other(code)),
131 }
132 }
133}
134
135#[cfg(feature = "std")]
136impl std::error::Error for BuildError {
137}
138
139impl fmt::Display for BuildError {
140 #[inline]
141 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
142 match self {
143 Self::Generic => fmt.write_str("error"),
144 Self::NoMem => fmt.write_str("alloc failure"),
145 Self::BadBRate => fmt.write_str("bad bitrate"),
146 Self::BadSampleFreq => fmt.write_str("bad sample frequency"),
147 Self::InternalError => fmt.write_str("internal error"),
148 Self::Other(code) => fmt.write_fmt(format_args!("error code={code}")),
149 }
150 }
151}
152
153#[derive(Debug, Copy, Clone, Eq, PartialEq)]
154pub enum EncodeError {
156 BufferTooSmall,
161 NoMem,
163 InvalidState,
167 PsychoAcoustic,
169 Other(libc::c_int),
171}
172
173impl EncodeError {
174 #[inline(always)]
175 fn from_c_int(code: libc::c_int) -> Result<usize, Self> {
176 if code >= 0 {
177 return Ok(code as usize)
178 }
179
180 match code {
181 -1 => Err(Self::BufferTooSmall),
182 -2 => Err(Self::NoMem),
183 -3 => Err(Self::InvalidState),
184 -4 => Err(Self::PsychoAcoustic),
185 _ => Err(Self::Other(code)),
186 }
187 }
188}
189
190#[cfg(feature = "std")]
191impl std::error::Error for EncodeError {
192}
193
194impl fmt::Display for EncodeError {
195 #[inline]
196 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
197 match self {
198 Self::BufferTooSmall => fmt.write_str("output buffer is insufficient for encoder output"),
199 Self::NoMem => fmt.write_str("alloc failure"),
200 Self::InvalidState => fmt.write_str("attempt to use uninitialized encoder"),
201 Self::PsychoAcoustic => fmt.write_str("psycho acoustic problems"),
202 Self::Other(code) => fmt.write_fmt(format_args!("error code={code}")),
203 }
204 }
205}
206
207
208#[derive(Copy, Clone)]
210#[repr(u16)]
211pub enum Bitrate {
212 Kbps8 = 8,
214 Kbps16 = 16,
216 Kbps24 = 24,
218 Kbps32 = 32,
220 Kbps40 = 40,
222 Kbps48 = 48,
224 Kbps64 = 64,
226 Kbps80 = 80,
228 Kbps96 = 96,
230 Kbps112 = 112,
232 Kbps128 = 128,
234 Kbps160 = 160,
236 Kbps192 = 192,
238 Kbps224 = 224,
240 Kbps256 = 256,
242 Kbps320 = 320,
244}
245
246pub use Bitrate as Birtate;
248
249#[derive(Copy, Clone)]
250#[repr(u8)]
251pub enum VbrMode {
253 Off = ffi::vbr_mode::vbr_off as u8,
255 Mt = ffi::vbr_mode::vbr_mt as u8,
257 Rh = ffi::vbr_mode::vbr_rh as u8,
259 Abr = ffi::vbr_mode::vbr_abr as u8,
261 Mtrh = ffi::vbr_mode::vbr_mtrh as u8,
263}
264
265impl Default for VbrMode {
266 #[inline(always)]
267 fn default() -> Self {
268 Self::Mtrh
269 }
270}
271
272#[derive(Copy, Clone)]
273#[repr(u8)]
274pub enum Mode {
276 Mono = ffi::MPEG_mode::MONO as u8,
278 Stereo = ffi::MPEG_mode::STEREO as u8,
280 JointStereo = ffi::MPEG_mode::JOINT_STEREO as u8,
282 DaulChannel = ffi::MPEG_mode::DUAL_CHANNEL as u8,
286 NotSet = ffi::MPEG_mode::NOT_SET as u8,
288}
289
290#[derive(Copy, Clone)]
293#[repr(u8)]
294pub enum Quality {
295 Best = 0,
297 SecondBest = 1,
299 NearBest = 2,
301 VeryNice = 3,
303 Nice = 4,
305 Good = 5,
307 Decent = 6,
309 Ok = 7,
311 SecondWorst = 8,
313 Worst = 9,
315}
316
317#[derive(Copy, Clone)]
318pub struct Id3Tag<'a> {
324 pub title: &'a [u8],
326 pub artist: &'a [u8],
328 pub album: &'a [u8],
330 pub album_art: &'a [u8],
339 pub year: &'a [u8],
341 pub comment: &'a [u8],
343}
344
345impl Id3Tag<'_> {
346 #[inline(always)]
347 pub const fn is_any_set(&self) -> bool {
349 !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()
350 }
351}
352
353pub struct Builder {
355 inner: NonNull<ffi::lame_global_flags>,
356}
357
358impl Builder {
359 #[inline]
360 pub fn new() -> Option<Self> {
364 let ptr = unsafe {
365 ffi::lame_init()
366 };
367
368 NonNull::new(ptr).map(|inner| Self {
369 inner
370 })
371 }
372
373 #[inline(always)]
374 pub unsafe fn as_ptr(&mut self) -> *mut ffi::lame_global_flags {
378 self.ptr()
379 }
380
381 #[inline(always)]
382 fn ptr(&mut self) -> *mut ffi::lame_global_flags {
383 self.inner.as_ptr()
384 }
385
386 #[inline]
387 pub fn set_sample_rate(&mut self, rate: u32) -> Result<(), BuildError> {
393 let res = unsafe {
394 ffi::lame_set_in_samplerate(self.ptr(), rate.try_into().unwrap_or(libc::c_int::MAX))
395 };
396
397 BuildError::from_c_int(res)
398 }
399
400 #[inline]
401 pub fn with_sample_rate(mut self, rate: u32) -> Result<Self, BuildError> {
407 self.set_sample_rate(rate)?;
408 Ok(self)
409 }
410
411 #[inline]
412 pub fn set_num_channels(&mut self, num: u8) -> Result<(), BuildError> {
418 let res = unsafe {
419 ffi::lame_set_num_channels(self.ptr(), num as _)
420 };
421
422 BuildError::from_c_int(res)
423 }
424
425 #[inline]
426 pub fn with_num_channels(mut self, num: u8) -> Result<Self, BuildError> {
432 self.set_num_channels(num)?;
433 Ok(self)
434 }
435
436 #[inline]
437 pub fn set_brate(&mut self, brate: Bitrate) -> Result<(), BuildError> {
443 let res = unsafe {
444 ffi::lame_set_brate(self.ptr(), brate as _)
445 };
446
447 BuildError::from_c_int(res)
448 }
449
450 #[inline]
451 pub fn with_brate(mut self, brate: Bitrate) -> Result<Self, BuildError> {
457 self.set_brate(brate)?;
458 Ok(self)
459 }
460
461 #[inline]
462 pub fn set_mode(&mut self, mode: Mode) -> Result<(), BuildError> {
468 let res = unsafe {
469 ffi::lame_set_mode(self.ptr(), mode as _)
470 };
471
472 BuildError::from_c_int(res)
473 }
474
475 #[inline]
476 pub fn with_mode(mut self, mode: Mode) -> Result<Self, BuildError> {
482 self.set_mode(mode)?;
483 Ok(self)
484 }
485
486 #[inline]
487 pub fn set_quality(&mut self, quality: Quality) -> Result<(), BuildError> {
493 let res = unsafe {
494 ffi::lame_set_quality(self.ptr(), quality as _)
495 };
496
497 BuildError::from_c_int(res)
498 }
499
500 #[inline]
501 pub fn with_quality(mut self, quality: Quality) -> Result<Self, BuildError> {
507 self.set_quality(quality)?;
508 Ok(self)
509 }
510
511 #[inline]
512 pub fn set_vbr_quality(&mut self, quality: Quality) -> Result<(), BuildError> {
516 let res = unsafe {
517 ffi::lame_set_VBR_q(self.ptr(), quality as _)
518 };
519
520 BuildError::from_c_int(res)
521 }
522
523 #[inline]
524 pub fn with_vbr_quality(mut self, quality: Quality) -> Result<Self, BuildError> {
528 self.set_vbr_quality(quality)?;
529 Ok(self)
530 }
531
532 #[inline]
533 pub fn set_to_write_vbr_tag(&mut self, value: bool) -> Result<(), BuildError> {
539 let res = unsafe {
540 ffi::lame_set_bWriteVbrTag(self.ptr(), value as _)
541 };
542
543 BuildError::from_c_int(res)
544 }
545
546 #[inline]
547 pub fn with_to_write_vbr_tag(mut self, value: bool) -> Result<Self, BuildError> {
553 self.set_to_write_vbr_tag(value)?;
554 Ok(self)
555 }
556
557 #[inline]
558 pub fn set_vbr_mode(&mut self, value: VbrMode) -> Result<(), BuildError> {
564 let res = unsafe {
565 ffi::lame_set_VBR(self.ptr(), value as _)
566 };
567
568 BuildError::from_c_int(res)
569 }
570
571 #[inline]
572 pub fn with_vbr_mode(mut self, value: VbrMode) -> Result<Self, BuildError> {
578 self.set_vbr_mode(value)?;
579 Ok(self)
580 }
581
582 #[inline]
583 pub fn set_id3_tag(&mut self, value: Id3Tag<'_>) -> Result<(), Id3TagError> {
590 if !value.is_any_set() {
591 return Ok(());
592 }
593
594 const MAX_BUFFER: usize = 250;
595 let mut buffer = [0u8; MAX_BUFFER + 1];
596
597 unsafe {
598 ffi::id3tag_init(self.ptr());
599 ffi::id3tag_add_v2(self.ptr());
600
601 if !value.album_art.is_empty() {
602 let size = value.album_art.len();
603 if size > MAX_ALBUM_ART_SIZE {
604 return Err(Id3TagError::AlbumArtOverflow);
605 }
606 let ptr = value.album_art.as_ptr();
607 ffi::id3tag_set_albumart(self.ptr(), ptr as _, size);
608 }
609
610 if !value.title.is_empty() {
611 let size = cmp::min(MAX_BUFFER, value.title.len());
612 ptr::copy_nonoverlapping(value.title.as_ptr(), buffer.as_mut_ptr(), size);
613 buffer[size] = 0;
614 ffi::id3tag_set_title(self.ptr(), buffer.as_ptr() as _);
615 }
616
617 if !value.album.is_empty() {
618 let size = cmp::min(MAX_BUFFER, value.album.len());
619 ptr::copy_nonoverlapping(value.album.as_ptr(), buffer.as_mut_ptr(), size);
620 buffer[size] = 0;
621 ffi::id3tag_set_album(self.ptr(), buffer.as_ptr() as _);
622 }
623
624 if !value.artist.is_empty() {
625 let size = cmp::min(MAX_BUFFER, value.artist.len());
626 ptr::copy_nonoverlapping(value.artist.as_ptr(), buffer.as_mut_ptr(), size);
627 buffer[size] = 0;
628 ffi::id3tag_set_artist(self.ptr(), buffer.as_ptr() as _);
629 }
630
631 if !value.year.is_empty() {
632 let size = cmp::min(MAX_BUFFER, value.year.len());
633 ptr::copy_nonoverlapping(value.year.as_ptr(), buffer.as_mut_ptr(), size);
634 buffer[size] = 0;
635 ffi::id3tag_set_year(self.ptr(), buffer.as_ptr() as _);
636 }
637
638 if !value.comment.is_empty() {
639 let size = cmp::min(MAX_BUFFER, value.comment.len());
640 ptr::copy_nonoverlapping(value.comment.as_ptr(), buffer.as_mut_ptr(), size);
641 buffer[size] = 0;
642 ffi::id3tag_set_comment(self.ptr(), buffer.as_ptr() as _);
643 }
644 }
645
646 Ok(())
647 }
648
649 #[inline]
650 pub fn with_id3_tag(mut self, value: Id3Tag<'_>) -> Result<Self, Id3TagError> {
656 self.set_id3_tag(value)?;
657 Ok(self)
658 }
659
660 #[inline]
661 pub fn build(mut self) -> Result<Encoder, BuildError> {
665 let res = unsafe {
666 ffi::lame_init_params(self.ptr())
667 };
668
669 match BuildError::from_c_int(res) {
670 Ok(()) => {
671 let inner = self.inner;
672 mem::forget(self);
673 Ok(Encoder { inner })
674 },
675 Err(error) => Err(error),
676 }
677 }
678}
679
680impl Drop for Builder {
681 #[inline]
682 fn drop(&mut self) {
683 unsafe {
684 ffi::lame_close(self.ptr())
685 };
686 }
687}
688
689pub struct Encoder {
691 inner: NonNull<ffi::lame_global_flags>,
692}
693
694impl Encoder {
695 #[inline(always)]
696 fn ptr(&self) -> *mut ffi::lame_global_flags {
697 self.inner.as_ptr()
698 }
699
700 #[inline]
701 pub fn sample_rate(&self) -> u32 {
703 unsafe {
704 ffi::lame_get_in_samplerate(self.ptr()) as u32
705 }
706 }
707
708 #[inline]
709 pub fn num_channels(&self) -> u8 {
711 unsafe {
712 ffi::lame_get_num_channels(self.ptr()) as u8
713 }
714 }
715
716 #[inline]
717 pub fn encode(&mut self, input: impl EncoderInput, output: &mut [MaybeUninit<u8>]) -> Result<usize, EncodeError> {
728 let output_len = output.len();
729 let output_buf = output.as_mut_ptr();
730
731 let result = input.encode(self, output_buf as _, output_len);
732
733 EncodeError::from_c_int(result)
734 }
735
736 #[inline(always)]
737 pub fn encode_to_vec(&mut self, input: impl EncoderInput, output: &mut Vec<u8>) -> Result<usize, EncodeError> {
743 let original_len = output.len();
744 match self.encode(input, output.spare_capacity_mut()) {
745 Ok(written) => {
746 unsafe {
747 output.set_len(original_len.saturating_add(written));
748 }
749 Ok(written)
750 },
751 Err(error) => Err(error),
752 }
753 }
754
755 #[inline]
756 pub fn flush<T: EncoderFlush>(&mut self, output: &mut [MaybeUninit<u8>]) -> Result<usize, EncodeError> {
772 let output_len = output.len();
773 let output_buf = output.as_mut_ptr();
774
775 let result = T::flush(self, output_buf as _, output_len);
776
777 EncodeError::from_c_int(result)
778 }
779
780 #[inline(always)]
781 pub fn flush_to_vec<T: EncoderFlush>(&mut self, output: &mut Vec<u8>) -> Result<usize, EncodeError> {
787 let original_len = output.len();
788 match self.flush::<T>(output.spare_capacity_mut()) {
789 Ok(written) => {
790 unsafe {
791 output.set_len(original_len.saturating_add(written));
792 }
793 Ok(written)
794 },
795 Err(error) => Err(error),
796 }
797 }
798}
799
800impl Drop for Encoder {
801 #[inline]
802 fn drop(&mut self) {
803 unsafe {
804 ffi::lame_close(self.ptr())
805 };
806 }
807}
808
809unsafe impl Send for Encoder {}
811unsafe impl Sync for Encoder {}
813
814pub fn encoder() -> Result<Encoder, BuildError> {
816 match Builder::new() {
817 Some(mut builder) => {
818 builder.set_brate(Bitrate::Kbps192)?;
819 builder.set_quality(Quality::Best)?;
820 builder.build()
821 },
822 None => Err(BuildError::NoMem)
823 }
824}