1#![no_std]
45#![warn(missing_docs)]
46#![allow(clippy::style)]
47#![allow(clippy::missing_safety_doc)]
48#![cfg_attr(rustfmt, rustfmt_skip)]
49
50#[cfg(feature = "std")]
51extern crate std;
52
53extern crate alloc;
54
55pub use mp3lame_sys as ffi;
56
57use alloc::vec::Vec;
58use core::mem::{self, MaybeUninit};
59use core::ptr::{self, NonNull};
60use core::{cmp, fmt};
61
62mod input;
63pub use input::*;
64
65pub const MAX_ALBUM_ART_SIZE: usize = 128 * 1024;
67
68pub const fn max_required_buffer_size(sample_number: usize) -> usize {
73 let mut sample_extra_size = sample_number / 4;
75 if (sample_number % 4) > 0 {
76 sample_extra_size = sample_extra_size.wrapping_add(1);
77 }
78 sample_number.wrapping_add(sample_extra_size).wrapping_add(7200)
79}
80
81#[derive(Debug, Copy, Clone, Eq, PartialEq)]
82pub enum Id3TagError {
84 AlbumArtOverflow,
86}
87
88#[derive(Debug, Copy, Clone, Eq, PartialEq)]
89pub enum BuildError {
91 Generic,
93 NoMem,
95 BadBRate,
97 BadSampleFreq,
99 InternalError,
101 Other(libc::c_int),
103}
104
105impl BuildError {
106 #[inline(always)]
107 fn from_c_int(code: libc::c_int) -> Result<(), Self> {
108 if code >= 0 {
109 return Ok(())
110 }
111
112 match code {
113 -1 => Err(Self::Generic),
114 -10 => Err(Self::NoMem),
115 -11 => Err(Self::BadBRate),
116 -12 => Err(Self::BadSampleFreq),
117 -13 => Err(Self::InternalError),
118 _ => Err(Self::Other(code)),
119 }
120 }
121}
122
123#[cfg(feature = "std")]
124impl std::error::Error for BuildError {
125}
126
127impl fmt::Display for BuildError {
128 #[inline]
129 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
130 match self {
131 Self::Generic => fmt.write_str("error"),
132 Self::NoMem => fmt.write_str("alloc failure"),
133 Self::BadBRate => fmt.write_str("bad bitrate"),
134 Self::BadSampleFreq => fmt.write_str("bad sample frequency"),
135 Self::InternalError => fmt.write_str("internal error"),
136 Self::Other(code) => fmt.write_fmt(format_args!("error code={code}")),
137 }
138 }
139}
140
141#[derive(Debug, Copy, Clone, Eq, PartialEq)]
142pub enum EncodeError {
144 BufferTooSmall,
149 NoMem,
151 InvalidState,
155 PsychoAcoustic,
157 Other(libc::c_int),
159}
160
161impl EncodeError {
162 #[inline(always)]
163 fn from_c_int(code: libc::c_int) -> Result<usize, Self> {
164 if code >= 0 {
165 return Ok(code as usize)
166 }
167
168 match code {
169 -1 => Err(Self::BufferTooSmall),
170 -2 => Err(Self::NoMem),
171 -3 => Err(Self::InvalidState),
172 -4 => Err(Self::PsychoAcoustic),
173 _ => Err(Self::Other(code)),
174 }
175 }
176}
177
178#[cfg(feature = "std")]
179impl std::error::Error for EncodeError {
180}
181
182impl fmt::Display for EncodeError {
183 #[inline]
184 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
185 match self {
186 Self::BufferTooSmall => fmt.write_str("output buffer is insufficient for encoder output"),
187 Self::NoMem => fmt.write_str("alloc failure"),
188 Self::InvalidState => fmt.write_str("attempt to use uninitialized encoder"),
189 Self::PsychoAcoustic => fmt.write_str("psycho acoustic problems"),
190 Self::Other(code) => fmt.write_fmt(format_args!("error code={code}")),
191 }
192 }
193}
194
195
196#[derive(Copy, Clone)]
198#[repr(u16)]
199pub enum Bitrate {
200 Kbps8 = 8,
202 Kbps16 = 16,
204 Kbps24 = 24,
206 Kbps32 = 32,
208 Kbps40 = 40,
210 Kbps48 = 48,
212 Kbps64 = 64,
214 Kbps80 = 80,
216 Kbps96 = 96,
218 Kbps112 = 112,
220 Kbps128 = 128,
222 Kbps160 = 160,
224 Kbps192 = 192,
226 Kbps224 = 224,
228 Kbps256 = 256,
230 Kbps320 = 320,
232}
233
234pub use Bitrate as Birtate;
236
237#[derive(Copy, Clone)]
238#[repr(u8)]
239pub enum VbrMode {
241 Off = ffi::vbr_mode::vbr_off as u8,
243 Mt = ffi::vbr_mode::vbr_mt as u8,
245 Rh = ffi::vbr_mode::vbr_rh as u8,
247 Abr = ffi::vbr_mode::vbr_abr as u8,
249 Mtrh = ffi::vbr_mode::vbr_mtrh as u8,
251}
252
253impl Default for VbrMode {
254 #[inline(always)]
255 fn default() -> Self {
256 Self::Mtrh
257 }
258}
259
260#[derive(Copy, Clone)]
261#[repr(u8)]
262pub enum Mode {
264 Mono = ffi::MPEG_mode::MONO as u8,
266 Stereo = ffi::MPEG_mode::STEREO as u8,
268 JointStereo = ffi::MPEG_mode::JOINT_STEREO as u8,
270 DaulChannel = ffi::MPEG_mode::DUAL_CHANNEL as u8,
274 NotSet = ffi::MPEG_mode::NOT_SET as u8,
276}
277
278#[derive(Copy, Clone)]
281#[repr(u8)]
282pub enum Quality {
283 Best = 0,
285 SecondBest = 1,
287 NearBest = 2,
289 VeryNice = 3,
291 Nice = 4,
293 Good = 5,
295 Decent = 6,
297 Ok = 7,
299 SecondWorst = 8,
301 Worst = 9,
303}
304
305pub struct Id3Tag<'a> {
311 pub title: &'a [u8],
313 pub artist: &'a [u8],
315 pub album: &'a [u8],
317 pub album_art: &'a [u8],
326 pub year: &'a [u8],
328 pub comment: &'a [u8],
330}
331
332impl Id3Tag<'_> {
333 #[inline(always)]
334 pub const fn is_any_set(&self) -> bool {
336 !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()
337 }
338}
339
340pub struct Builder {
342 inner: NonNull<ffi::lame_global_flags>,
343}
344
345impl Builder {
346 #[inline]
347 pub fn new() -> Option<Self> {
351 let ptr = unsafe {
352 ffi::lame_init()
353 };
354
355 NonNull::new(ptr).map(|inner| Self {
356 inner
357 })
358 }
359
360 #[inline(always)]
361 pub unsafe fn as_ptr(&mut self) -> *mut ffi::lame_global_flags {
365 self.ptr()
366 }
367
368 #[inline(always)]
369 fn ptr(&mut self) -> *mut ffi::lame_global_flags {
370 self.inner.as_ptr()
371 }
372
373 #[inline]
374 pub fn set_sample_rate(&mut self, rate: u32) -> Result<(), BuildError> {
380 let res = unsafe {
381 ffi::lame_set_in_samplerate(self.ptr(), rate.try_into().unwrap_or(libc::c_int::MAX))
382 };
383
384 BuildError::from_c_int(res)
385 }
386
387 #[inline]
388 pub fn set_num_channels(&mut self, num: u8) -> Result<(), BuildError> {
394 let res = unsafe {
395 ffi::lame_set_num_channels(self.ptr(), num as _)
396 };
397
398 BuildError::from_c_int(res)
399 }
400
401 #[inline]
402 pub fn set_brate(&mut self, brate: Bitrate) -> Result<(), BuildError> {
408 let res = unsafe {
409 ffi::lame_set_brate(self.ptr(), brate as _)
410 };
411
412 BuildError::from_c_int(res)
413 }
414
415 #[inline]
416 pub fn set_mode(&mut self, mode: Mode) -> Result<(), BuildError> {
422 let res = unsafe {
423 ffi::lame_set_mode(self.ptr(), mode as _)
424 };
425
426 BuildError::from_c_int(res)
427 }
428
429 #[inline]
430 pub fn set_quality(&mut self, quality: Quality) -> Result<(), BuildError> {
436 let res = unsafe {
437 ffi::lame_set_quality(self.ptr(), quality as _)
438 };
439
440 BuildError::from_c_int(res)
441 }
442
443 #[inline]
444 pub fn set_vbr_quality(&mut self, quality: Quality) -> Result<(), BuildError> {
448 let res = unsafe {
449 ffi::lame_set_VBR_q(self.ptr(), quality as _)
450 };
451
452 BuildError::from_c_int(res)
453 }
454
455
456 #[inline]
457 pub fn set_to_write_vbr_tag(&mut self, value: bool) -> Result<(), BuildError> {
463 let res = unsafe {
464 ffi::lame_set_bWriteVbrTag(self.ptr(), value as _)
465 };
466
467 BuildError::from_c_int(res)
468 }
469
470 #[inline]
471 pub fn set_vbr_mode(&mut self, value: VbrMode) -> Result<(), BuildError> {
477 let res = unsafe {
478 ffi::lame_set_VBR(self.ptr(), value as _)
479 };
480
481 BuildError::from_c_int(res)
482 }
483
484 #[inline]
485 pub fn set_id3_tag(&mut self, value: Id3Tag<'_>) -> Result<(), Id3TagError> {
492 if !value.is_any_set() {
493 return Ok(());
494 }
495
496 const MAX_BUFFER: usize = 250;
497 let mut buffer = [0u8; MAX_BUFFER + 1];
498
499 unsafe {
500 ffi::id3tag_init(self.ptr());
501 ffi::id3tag_add_v2(self.ptr());
502
503 if !value.album_art.is_empty() {
504 let size = value.album_art.len();
505 if size > MAX_ALBUM_ART_SIZE {
506 return Err(Id3TagError::AlbumArtOverflow);
507 }
508 let ptr = value.album_art.as_ptr();
509 ffi::id3tag_set_albumart(self.ptr(), ptr as _, size);
510 }
511
512 if !value.title.is_empty() {
513 let size = cmp::min(MAX_BUFFER, value.title.len());
514 ptr::copy_nonoverlapping(value.title.as_ptr(), buffer.as_mut_ptr(), size);
515 buffer[size] = 0;
516 ffi::id3tag_set_title(self.ptr(), buffer.as_ptr() as _);
517 }
518
519 if !value.album.is_empty() {
520 let size = cmp::min(MAX_BUFFER, value.album.len());
521 ptr::copy_nonoverlapping(value.album.as_ptr(), buffer.as_mut_ptr(), size);
522 buffer[size] = 0;
523 ffi::id3tag_set_album(self.ptr(), buffer.as_ptr() as _);
524 }
525
526 if !value.artist.is_empty() {
527 let size = cmp::min(MAX_BUFFER, value.artist.len());
528 ptr::copy_nonoverlapping(value.artist.as_ptr(), buffer.as_mut_ptr(), size);
529 buffer[size] = 0;
530 ffi::id3tag_set_artist(self.ptr(), buffer.as_ptr() as _);
531 }
532
533 if !value.year.is_empty() {
534 let size = cmp::min(MAX_BUFFER, value.year.len());
535 ptr::copy_nonoverlapping(value.year.as_ptr(), buffer.as_mut_ptr(), size);
536 buffer[size] = 0;
537 ffi::id3tag_set_year(self.ptr(), buffer.as_ptr() as _);
538 }
539
540 if !value.comment.is_empty() {
541 let size = cmp::min(MAX_BUFFER, value.comment.len());
542 ptr::copy_nonoverlapping(value.comment.as_ptr(), buffer.as_mut_ptr(), size);
543 buffer[size] = 0;
544 ffi::id3tag_set_comment(self.ptr(), buffer.as_ptr() as _);
545 }
546 }
547
548 Ok(())
549 }
550
551 #[inline]
552 pub fn build(mut self) -> Result<Encoder, BuildError> {
556 let res = unsafe {
557 ffi::lame_init_params(self.ptr())
558 };
559
560 match BuildError::from_c_int(res) {
561 Ok(()) => {
562 let inner = self.inner;
563 mem::forget(self);
564 Ok(Encoder { inner })
565 },
566 Err(error) => Err(error),
567 }
568 }
569}
570
571impl Drop for Builder {
572 #[inline]
573 fn drop(&mut self) {
574 unsafe {
575 ffi::lame_close(self.ptr())
576 };
577 }
578}
579
580pub struct Encoder {
582 inner: NonNull<ffi::lame_global_flags>,
583}
584
585impl Encoder {
586 #[inline(always)]
587 fn ptr(&self) -> *mut ffi::lame_global_flags {
588 self.inner.as_ptr()
589 }
590
591 #[inline]
592 pub fn sample_rate(&self) -> u32 {
594 unsafe {
595 ffi::lame_get_in_samplerate(self.ptr()) as u32
596 }
597 }
598
599 #[inline]
600 pub fn num_channels(&self) -> u8 {
602 unsafe {
603 ffi::lame_get_num_channels(self.ptr()) as u8
604 }
605 }
606
607 #[inline]
608 pub fn encode(&mut self, input: impl EncoderInput, output: &mut [MaybeUninit<u8>]) -> Result<usize, EncodeError> {
619 let output_len = output.len();
620 let output_buf = output.as_mut_ptr();
621
622 let result = input.encode(self, output_buf as _, output_len);
623
624 EncodeError::from_c_int(result)
625 }
626
627 #[inline(always)]
628 pub fn encode_to_vec(&mut self, input: impl EncoderInput, output: &mut Vec<u8>) -> Result<usize, EncodeError> {
634 let original_len = output.len();
635 match self.encode(input, output.spare_capacity_mut()) {
636 Ok(written) => {
637 unsafe {
638 output.set_len(original_len.saturating_add(written));
639 }
640 Ok(written)
641 },
642 Err(error) => Err(error),
643 }
644 }
645
646 #[inline]
647 pub fn flush<T: EncoderFlush>(&mut self, output: &mut [MaybeUninit<u8>]) -> Result<usize, EncodeError> {
663 let output_len = output.len();
664 let output_buf = output.as_mut_ptr();
665
666 let result = T::flush(self, output_buf as _, output_len);
667
668 EncodeError::from_c_int(result)
669 }
670
671 #[inline(always)]
672 pub fn flush_to_vec<T: EncoderFlush>(&mut self, output: &mut Vec<u8>) -> Result<usize, EncodeError> {
678 let original_len = output.len();
679 match self.flush::<T>(output.spare_capacity_mut()) {
680 Ok(written) => {
681 unsafe {
682 output.set_len(original_len.saturating_add(written));
683 }
684 Ok(written)
685 },
686 Err(error) => Err(error),
687 }
688 }
689}
690
691impl Drop for Encoder {
692 #[inline]
693 fn drop(&mut self) {
694 unsafe {
695 ffi::lame_close(self.ptr())
696 };
697 }
698}
699
700unsafe impl Send for Encoder {}
702unsafe impl Sync for Encoder {}
704
705pub fn encoder() -> Result<Encoder, BuildError> {
707 match Builder::new() {
708 Some(mut builder) => {
709 builder.set_brate(Bitrate::Kbps192)?;
710 builder.set_quality(Quality::Best)?;
711 builder.build()
712 },
713 None => Err(BuildError::NoMem)
714 }
715}