summaryrefslogtreecommitdiff
path: root/src/flac.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/flac.rs')
-rw-r--r--src/flac.rs150
1 files changed, 126 insertions, 24 deletions
diff --git a/src/flac.rs b/src/flac.rs
index 9b1b7f3..a4c204e 100644
--- a/src/flac.rs
+++ b/src/flac.rs
@@ -3,7 +3,7 @@ use flac_bound::{FlacEncoder, WriteWrapper};
use i24::i24;
use md5::{Digest, Md5, Md5Core, digest::core_api::CoreWrapper};
use metaflac::{Block, Tag};
-use std::fs::File;
+use std::{ffi::OsStr, fs::File, path::Path};
use symphonia::core::{
audio::{Audio, GenericAudioBufferRef},
codecs::audio::AudioDecoder,
@@ -12,6 +12,9 @@ use symphonia::core::{
meta::MetadataOptions,
};
+#[allow(dead_code)]
+pub const CURRENT_VENDOR: &str = "reference libFLAC 1.5.0 20250211";
+
struct StreamConfig {
channels: u32,
bits_per_sample: Bps,
@@ -201,8 +204,14 @@ fn encode_cycle_32(
Ok(())
}
-pub fn encode_file(file: &std::path::Path) -> Result<()> {
- let src = std::fs::File::open(file)?;
+fn init_decoder(
+ filename: impl AsRef<Path>,
+) -> Result<(
+ Box<dyn FormatReader>,
+ Box<dyn AudioDecoder + 'static>,
+ StreamConfig,
+)> {
+ let src = std::fs::File::open(filename)?;
let mss = MediaSourceStream::new(Box::new(src), Default::default());
let mut hint = Hint::new();
hint.with_extension("flac");
@@ -231,9 +240,44 @@ pub fn encode_file(file: &std::path::Path) -> Result<()> {
sample_rate: params.sample_rate.unwrap(),
};
- let tempname = format!("{}.tmp", file.display());
+ Ok((format, decoder, config))
+}
+
+fn write_tags(
+ file: impl AsRef<Path>,
+ tempname: impl AsRef<Path>,
+ hasher: impl Digest,
+) -> Result<()> {
+ let tags = Tag::read_from_path(file)?;
+ let mut output = Tag::read_from_path(&tempname)?;
+ let mut streaminfo = tags.get_streaminfo().unwrap().clone();
+
+ streaminfo.md5 = hasher.finalize()[..].to_vec();
+ output.set_streaminfo(streaminfo);
+
+ for block in tags.blocks() {
+ match block {
+ Block::VorbisComment(comment) => {
+ for (key, val) in comment.comments.clone() {
+ output.set_vorbis(key, val);
+ }
+ }
+ Block::StreamInfo(_) => {}
+ _ => output.push_block(block.clone()),
+ }
+ }
+
+ output.write_to_path(&tempname)?;
+ Ok(())
+}
+
+pub fn encode_file(filename: impl AsRef<OsStr>) -> Result<()> {
+ let file = Path::new(&filename);
+ let tempname = &format!("{}.tmp", file.to_str().unwrap());
- let mut outf = File::create(&tempname)?;
+ let (format, decoder, config) = init_decoder(file)?;
+
+ let mut outf = File::create(tempname)?;
let mut outw = WriteWrapper(&mut outf);
let enc = FlacEncoder::new()
.unwrap()
@@ -253,28 +297,86 @@ pub fn encode_file(file: &std::path::Path) -> Result<()> {
Bps::_32 => encode_cycle_32(format, decoder, enc, &mut hasher),
}?;
- let tags = Tag::read_from_path(file)?;
- let mut output = Tag::read_from_path(&tempname)?;
- let mut streaminfo = tags.get_streaminfo().unwrap().clone();
+ write_tags(file, tempname, hasher)?;
- streaminfo.md5 = hasher.finalize()[..].to_vec();
- output.set_streaminfo(streaminfo);
+ std::fs::rename(tempname, file)?;
- for block in tags.blocks() {
- match block {
- Block::VorbisComment(comment) => {
- for (key, val) in comment.comments.clone() {
- output.set_vorbis(key, val);
- }
- }
- Block::StreamInfo(_) => {}
- _ => output.push_block(block.clone()),
- }
- }
+ Ok(())
+}
- output.write_to_path(&tempname)?;
+pub fn get_vendor(file: &Path) -> String {
+ let tag = Tag::read_from_path(file).unwrap();
+ tag.vorbis_comments().unwrap().vendor_string.clone()
+}
- std::fs::rename(&tempname, file)?;
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use metaflac::Tag;
- Ok(())
+ #[test]
+ fn bit16() {
+ let name = "16bit.flac";
+ let tempname = "16bit.flac.temp";
+ std::fs::copy(name, tempname).unwrap();
+ encode_file(std::path::Path::new(name)).unwrap();
+ let target_md5 = Tag::read_from_path(tempname)
+ .unwrap()
+ .get_streaminfo()
+ .unwrap()
+ .md5
+ .clone();
+ let encoded_md5 = Tag::read_from_path(name)
+ .unwrap()
+ .get_streaminfo()
+ .unwrap()
+ .md5
+ .clone();
+ std::fs::remove_file(tempname).unwrap();
+ assert_eq!(target_md5, encoded_md5);
+ }
+
+ #[test]
+ fn bit24() {
+ let name = "24bit.flac";
+ let tempname = "24bit.flac.temp";
+ std::fs::copy(name, tempname).unwrap();
+ encode_file(std::path::Path::new(name)).unwrap();
+ let target_md5 = Tag::read_from_path(tempname)
+ .unwrap()
+ .get_streaminfo()
+ .unwrap()
+ .md5
+ .clone();
+ let encoded_md5 = Tag::read_from_path(name)
+ .unwrap()
+ .get_streaminfo()
+ .unwrap()
+ .md5
+ .clone();
+ std::fs::remove_file(tempname).unwrap();
+ assert_eq!(target_md5, encoded_md5);
+ }
+
+ #[test]
+ fn bit32() {
+ let name = "32bit.flac";
+ let tempname = "32bit.flac.temp";
+ std::fs::copy(name, tempname).unwrap();
+ encode_file(std::path::Path::new(name)).unwrap();
+ let target_md5 = Tag::read_from_path(tempname)
+ .unwrap()
+ .get_streaminfo()
+ .unwrap()
+ .md5
+ .clone();
+ let encoded_md5 = Tag::read_from_path(name)
+ .unwrap()
+ .get_streaminfo()
+ .unwrap()
+ .md5
+ .clone();
+ std::fs::remove_file(tempname).unwrap();
+ assert_eq!(target_md5, encoded_md5);
+ }
}