Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ jobs:
with:
runs-on: ${{ matrix.os }}
min-rustc-version: "1.65.0"
valgrind_flags: "--suppressions=valgrind.supp"
valgrind: true
valgrind_flags: "--suppressions=valgrind.supp --gen-suppressions=all --leak-check=full"

cross-compilation-test:
needs: test
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,30 @@ unsafe {
}
//At this point your mp3_out_buffer should have full MP3 data, ready to be written on file system or whatever

if mp3_encoder.lame_tag_size() > 0 {
let id3v2_tag_boundary = mp3_encoder.id3v2_tag_size();
assert_eq!(id3v2_tag_boundary, 158);
let mut lame_tag = [core::mem::MaybeUninit::uninit(); 1024];
assert!(lame_tag.len() >= mp3_encoder.lame_tag_size(), "Increase buffer size");
let lame_tag_size = mp3_encoder.lame_tag_encode(&mut lame_tag).expect("to write lame tag");
assert_eq!(mp3_encoder.lame_tag_size(), lame_tag_size.get());

//If you need VBR tag then you need to write mp3 file in following order
//- id3v2 tag
//- VBR tag
//- actual mp3 content
let chunks_to_write = [
&mp3_out_buffer[..id3v2_tag_boundary],
unsafe {
core::slice::from_raw_parts(lame_tag.as_ptr() as *const u8, lame_tag_size.get())
},
&mp3_out_buffer[id3v2_tag_boundary..],
];
} else {
let chunks_to_write = [
&mp3_out_buffer[..]
];
}
```

## License
Expand Down
116 changes: 98 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,22 @@
//! comment: b"Just my comment",
//!};
//!
//!//Create codec using builder
//!let mut mp3_encoder = Builder::new().expect("Create LAME builder");
//!mp3_encoder.set_num_channels(2).expect("set channels");
//!mp3_encoder.set_sample_rate(44_100).expect("set sample rate");
//!mp3_encoder.set_brate(mp3lame_encoder::Bitrate::Kbps192).expect("set brate");
//!mp3_encoder.set_quality(mp3lame_encoder::Quality::Best).expect("set quality");
//!mp3_encoder.set_id3_tag(id3tag);
//!let mut mp3_encoder = mp3_encoder.build().expect("To initialize LAME encoder");
//!
//!//Methods prefixed with `with_*` return Self for convenience
//!let mut mp3_encoder = Builder::new().expect("Create LAME builder")
//! .with_num_channels(2).expect("set channels")
//! .with_sample_rate(44_100).expect("set sample rate")
//! .with_brate(mp3lame_encoder::Bitrate::Kbps192).expect("set brate")
//! .with_quality(mp3lame_encoder::Quality::Best).expect("set quality")
//! .with_vbr_mode(mp3lame_encoder::VbrMode::Mtrh).expect("set VBR")
//! .with_vbr_quality(mp3lame_encoder::Quality::Best).expect("set VBR quality")
//! .with_to_write_vbr_tag(true).expect("set to write VBR tag")
//! .with_id3_tag(id3tag).expect("set tags")
//! .build().expect("To initialize LAME encoder");
//!
//!//use actual PCM data
//!let input = DualPcm {
//! left: &[0u16, 0],
//! right: &[0u16, 0],
//! left: &[0u16, 1024],
//! right: &[0u16, 1024],
//!};
//!
//!let mut mp3_out_buffer = Vec::new();
Expand All @@ -51,6 +45,30 @@
//!}
//!//At this point your mp3_out_buffer should have full MP3 data, ready to be written on file system or whatever
//!
//!if mp3_encoder.lame_tag_size() > 0 {
//! let id3v2_tag_boundary = mp3_encoder.id3v2_tag_size();
//! assert_eq!(id3v2_tag_boundary, 158);
//! let mut lame_tag = [core::mem::MaybeUninit::uninit(); 1024];
//! assert!(lame_tag.len() >= mp3_encoder.lame_tag_size(), "Increase buffer size");
//! let lame_tag_size = mp3_encoder.lame_tag_encode(&mut lame_tag).expect("to write lame tag");
//! assert_eq!(mp3_encoder.lame_tag_size(), lame_tag_size.get());
//!
//! //If you need VBR tag then you need to write mp3 file in following order
//! //- id3v2 tag
//! //- VBR tag
//! //- actual mp3 content
//! let chunks_to_write = [
//! &mp3_out_buffer[..id3v2_tag_boundary],
//! unsafe {
//! core::slice::from_raw_parts(lame_tag.as_ptr() as *const u8, lame_tag_size.get())
//! },
//! &mp3_out_buffer[id3v2_tag_boundary..],
//! ];
//!} else {
//! let chunks_to_write = [
//! &mp3_out_buffer[..]
//! ];
//!}
//!```

#![no_std]
Expand All @@ -68,7 +86,7 @@ pub use mp3lame_sys as ffi;

use alloc::vec::Vec;
use core::mem::{self, MaybeUninit};
use core::num::NonZeroU32;
use core::num::{NonZeroU32, NonZeroUsize};
use core::ptr::{self, NonNull};
use core::{cmp, fmt};
use core::ffi::c_int;
Expand Down Expand Up @@ -157,7 +175,7 @@ impl fmt::Display for BuildError {
pub enum EncodeError {
///Indicates output buffer is insufficient.
///
///Consider using [max_required_buffer_size](max_required_buffer_size) to determine required
///Consider using [max_required_buffer_size] to determine required
///space to alloc.
BufferTooSmall,
///Failed to allocate memory
Expand Down Expand Up @@ -619,7 +637,7 @@ impl Builder {
#[inline]
///Sets id3tag tag.
///
///If [FlushGap](FlushGap) is used, then `v1` will not be added.
///If [FlushGap] is used, then `v1` will not be added.
///But `v2` is always added at the beginning.
///
///Returns whether it is supported or not.
Expand Down Expand Up @@ -686,7 +704,7 @@ impl Builder {
#[inline]
///Sets id3tag tag using the builder pattern.
///
///If [FlushGap](FlushGap) is used, then `v1` will not be added.
///If [FlushGap] is used, then `v1` will not be added.
///
///Returns an error if it is not supported.
pub fn with_id3_tag(mut self, value: Id3Tag<'_>) -> Result<Self, Id3TagError> {
Expand Down Expand Up @@ -750,12 +768,74 @@ impl Encoder {
}
}

#[inline]
///Returns indication whether encoder is configured to write LAME tag
pub fn is_lame_tag_written(&self) -> bool {
unsafe {
ffi::lame_get_bWriteVbrTag(self.ptr()) != 0
}
}

#[inline]
///Returns size of ths [Id3Tag] written, if any
pub fn id3v2_tag_size(&self) -> usize {
unsafe {
ffi::lame_get_id3v2_tag(self.ptr(), ptr::null_mut(), 0)
}
}

#[inline]
///Retrieves size of the lame tag
pub fn lame_tag_size(&self) -> usize {
unsafe {
ffi::lame_get_lametag_frame(self.ptr(), ptr::null_mut(), 0)
}
}

#[inline]
///Attempts to write lame tag into `output` returning number of written bytes in case of success
///
///Note that if you write [Id3Tag] you must not write this tag at the start.
///Instead you must insert Lame Tag after [Id3Tag] position.
///
///To determine where [Id3Tag] ends in output stream, you can use [Encoder::id3v2_tag_size]
///which returns full size of [Id3Tag] metadata written (therefore indicating where actual mp3
///content is starting)
pub fn lame_tag_encode(&self, output: &mut [MaybeUninit<u8>]) -> Option<NonZeroUsize> {
//lame_get_lametag_frame() returns full required size in case `output` is not sufficient,
//so first manually check buffer size
if output.len() < self.lame_tag_size() {
None
} else {
NonZeroUsize::new(unsafe {
ffi::lame_get_lametag_frame(self.ptr(), output.as_mut_ptr() as _, output.len())
})
}
}

#[inline]
///Attempts to write lame tag into `output` returning number of written bytes in case of success
///
///Refer to [Encoder::lame_tag_encode] for details
pub fn lame_tag_encode_to_vec(&self, output: &mut Vec<u8>) -> Option<NonZeroUsize> {
let original_len = output.len();
match self.lame_tag_encode(output.spare_capacity_mut()) {
Some(written) => {
unsafe {
output.set_len(original_len.saturating_add(written.get()));
}
Some(written)
},
None => None
}
}

#[inline]
///Attempts to encode PCM data, writing whatever available onto `output` buffer
///
///### Arguments:
///
/// - `input` - Data input. Can be [MonoPcm](MonoPcm), [DualPcm](DualPcm) or [InterleavedPcm](InterleavedPcm)
/// - `input` - Data input. Can be [MonoPcm], [DualPcm] or [InterleavedPcm]
/// - `output` - Output buffer to write into.
///
///### Result:
Expand Down Expand Up @@ -795,8 +875,8 @@ impl Encoder {
///
///### Type:
///
///- [FlushNoGap](FlushNoGap) - performs flush, using ancillary data to fill gaps;
///- [FlushGap](FlushGap) - performs flush, padding with 0;
///- [FlushNoGap] - performs flush, using ancillary data to fill gaps;
///- [FlushGap] - performs flush, padding with 0;
///
///### Arguments:
///
Expand Down
Loading