From e35e2e9827d2e71d6fa1a27c7b26a60660209ec8 Mon Sep 17 00:00:00 2001 From: TheAwiteb Date: Mon, 27 Feb 2023 18:37:36 +0300 Subject: [PATCH 1/6] Add `create_wav_header` utility to create the header of wav file --- src/utils.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index 9187810..ea617d3 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,4 @@ -use crate::{Error, Result}; +use crate::{Error, Result, SAMPLE_RATE}; use std::fs; /// Check the file size. The maximum file size is 4 GB. @@ -20,3 +20,27 @@ pub(crate) fn check_wav_file_size(file: &fs::File) -> Result<()> { }) .unwrap_or(Ok(())) } + +/// Create the wave file header, the header is 44 bytes. +/// ### Note +/// - The `file_size` is the size of the file that will be encoded. +/// - The bits per sample is 16. +/// - The sample rate is 202860 Hz (202.86 kHz). +/// - The number of channels is 1. +pub(crate) fn create_wav_header(file_size: u32) -> [u8; 44] { + let mut header = [0; 44]; + header[0..4].copy_from_slice(b"RIFF"); + header[4..8].copy_from_slice(&(file_size + 36).to_le_bytes()); + header[8..12].copy_from_slice(b"WAVE"); + header[12..16].copy_from_slice(b"fmt "); + header[16..20].copy_from_slice(&16u32.to_le_bytes()); + header[20..22].copy_from_slice(&1u16.to_le_bytes()); + header[22..24].copy_from_slice(&1u16.to_le_bytes()); + header[24..28].copy_from_slice(&SAMPLE_RATE.to_le_bytes()); + header[28..32].copy_from_slice(&(SAMPLE_RATE * 2).to_le_bytes()); + header[32..34].copy_from_slice(&2u16.to_le_bytes()); + header[34..36].copy_from_slice(&16u16.to_le_bytes()); + header[36..40].copy_from_slice(b"data"); + header[40..44].copy_from_slice(&file_size.to_le_bytes()); + header +} From d75053b79e002103d6a1229ab6befa74d05fa5a0 Mon Sep 17 00:00:00 2001 From: TheAwiteb Date: Mon, 27 Feb 2023 18:38:31 +0300 Subject: [PATCH 2/6] Create the header when encoding --- src/lib.rs | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4117aba..486fd9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ -use file_utils::read::Read; use std::{ fs, - io::{self, BufReader, BufWriter, Seek}, + io::{self, BufReader, BufWriter, Seek, Write}, path, }; @@ -19,31 +18,23 @@ pub const SAMPLE_RATE: u32 = 202860; /// # Example /// ```rust|no_run /// use data2sound::encode; -/// use std::fs; -/// let file = fs::File::open("test.txt").unwrap(); -/// encode(file, "test.wav").unwrap(); +/// encode("test.txt", "test.wav").unwrap(); /// ``` -pub fn encode(file: fs::File, wav_output: impl AsRef) -> Result<()> { +pub fn encode(file: impl AsRef, wav_output: impl AsRef) -> Result<()> { + let file = fs::File::open(file)?; utils::check_file_size(&file)?; - let spec = hound::WavSpec { - channels: 1, - sample_rate: SAMPLE_RATE, - bits_per_sample: 16, - sample_format: hound::SampleFormat::Int, - }; let str_path = wav_output.as_ref().display().to_string(); let wav_output = if !str_path.ends_with(".wav") { format!("{}.wav", wav_output.as_ref().display()) } else { str_path }; - let mut writer = hound::WavWriter::create(wav_output, spec)?; - - let mut file = BufReader::new(file); - - while let Ok(byte) = file.read_i16() { - writer.write_sample(byte)?; - } + let mut reader = BufReader::new(&file); + let mut writer = BufWriter::new(fs::File::create(wav_output)?); + // Write the wav header to the wav file + writer.write_all(&utils::create_wav_header(file.metadata()?.len() as u32))?; + // Copy the file to the wav file + io::copy(&mut reader, &mut writer)?; Ok(()) } @@ -78,7 +69,7 @@ mod tests { #[test] fn test_encode_decode() { fs::write("test.txt", "Some context").unwrap(); - encode(fs::File::open("test.txt").unwrap(), "test.wav").unwrap(); + encode("test.txt", "test.wav").unwrap(); decode("test.wav", "test2.txt").unwrap(); let file = fs::File::open("test.txt").unwrap(); let file2 = fs::File::open("test2.txt").unwrap(); From 94f8e7cb2aa6f57257f3c3371a73100d57556f33 Mon Sep 17 00:00:00 2001 From: TheAwiteb Date: Mon, 27 Feb 2023 18:40:05 +0300 Subject: [PATCH 3/6] Remove `hound` error --- src/errors.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index d45c696..e837220 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -6,8 +6,6 @@ pub type Result = std::result::Result; /// Error enum, used to return errors from the library. #[derive(Debug)] pub enum Error { - /// Error from the hound crate - Hound(hound::Error), /// IO error, such as file not found Io(std::io::Error), /// Larg file size error (maxnimum file size is 4 GB) @@ -15,13 +13,6 @@ pub enum Error { /// Invalid wav file error InvalidWavFile(String), } - -impl From for Error { - fn from(err: hound::Error) -> Self { - Error::Hound(err) - } -} - impl From for Error { fn from(err: std::io::Error) -> Self { Error::Io(err) @@ -31,7 +22,6 @@ impl From for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Error::Hound(err) => write!(f, "Hound error: {}", err), Error::Io(err) => write!(f, "IO error: {}", err), Error::LargeFileSize => write!(f, "File size is too large, maximum file size is 4 GB"), Error::InvalidWavFile(msg) => write!(f, "Invalid wav file, {}", msg), From 1bd4d02ef76cfdba04b7b21eaa94b68cad663a91 Mon Sep 17 00:00:00 2001 From: TheAwiteb Date: Mon, 27 Feb 2023 18:40:31 +0300 Subject: [PATCH 4/6] Update `encode` usage --- src/main.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index fec1438..61fe891 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -use std::{env, fs}; - const HELP_MESSAGE: &str = r#"Usage: data2sound Commands: encode, e Encode a file to a wav file @@ -22,7 +20,7 @@ fn help() { } fn try_main() -> data2sound::Result<()> { - let args: Vec = env::args().collect(); + let args: Vec = std::env::args().collect(); if args.iter().any(|arg| arg == "--version" || arg == "-v") { version() } else if args.iter().any(|arg| arg == "--help" || arg == "-h") || args.len() < 4 { @@ -35,7 +33,7 @@ fn try_main() -> data2sound::Result<()> { let input = args.next().unwrap(); let output = args.next().unwrap(); match command.as_str() { - "encode" | "e" => data2sound::encode(fs::File::open(input)?, output)?, + "encode" | "e" => data2sound::encode(input, output)?, "decode" | "d" => data2sound::decode(input, output)?, _ => eprintln!( "Unknown command '{}' Run 'data2sound --help' for more information", From e1263bda2cea8727c253efd096440a798e61108c Mon Sep 17 00:00:00 2001 From: TheAwiteb Date: Mon, 27 Feb 2023 18:41:03 +0300 Subject: [PATCH 5/6] Remove the dependencies --- Cargo.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 16ef586..f9b7b5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "data2sound" -description = "A library to convert data to sound, and vice versa" +description = "A library to convert data to sound, and vice versa (dependency-free)" version = "0.1.6" rust-version = "1.56.1" edition = "2021" @@ -15,8 +15,6 @@ categories = ["multimedia::audio"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -file-utils = "= 0.1.5" -hound = "= 3.5.0" [profile.release] @@ -25,4 +23,4 @@ lto = true # Link-time optimization. debug = false # Disable debug info. codegen-units = 1 # Use a single codegen unit. panic = "abort" # Use abort instead of unwind. -strip = true # Strip symbols. \ No newline at end of file +strip = true # Strip symbols. From 5ef14a1af34c28c10e0015a1c2a52c7bbb832b6d Mon Sep 17 00:00:00 2001 From: TheAwiteb Date: Mon, 27 Feb 2023 18:42:18 +0300 Subject: [PATCH 6/6] Update README.md Update benchmarks, and add `dependency-free` --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1e01e51..b870538 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Data to sound A simple crate to convert data to sound, and sound to data. The sound file format is wave (.wav). -You can use it as a library or as a command line tool. +You can use it as a library or as a command line tool. (dependency-free) ## Minimum supported Rust version -The minimum supported Rust version is 1.56.1. +The minimum supported Rust version is 1.59.0. ## Note The sound frequency is 202860Hz (202.86kHz), and the sound is mono. The sound is encoded in 16 bits. @@ -51,13 +51,13 @@ The following benchmarks were made on a 4.600GHz 12th Gen Intel i7-12700H CPU wi ### Encoding | File size | Audio file size | Audio length | Speed | Link | |-----------|-----------------|------|-------| ---- | -| 2687.94MB | 2687.94MB | 01:28:13 | 6.27s | [Soundcloud-link](https://soundcloud.com/awiteb/pop-os-2204-amd64-intel-23iso) | -| 35.3MB | 35.3MB | 00:01::27 | 113.47ms | [Soundcloud-link](https://soundcloud.com/awiteb/rust-1671zip) | +| 2687.94MB | 2687.94MB | 01:28:13 | 2.67s | [Soundcloud-link](https://soundcloud.com/awiteb/pop-os-2204-amd64-intel-23iso) | +| 35.3MB | 35.3MB | 00:01::27 | 51.34ms | [Soundcloud-link](https://soundcloud.com/awiteb/rust-1671zip) | ## Decoding | File size | Audio file size | Audio length | Speed | Link | |-----------|-----------------|------|-------| ---- | -| 2687.94MB | 2687.94MB | 01:28:13 | 5.14s | [Soundcloud-link](https://soundcloud.com/awiteb/pop-os-2204-amd64-intel-23iso) | -| 35.3MB | 35.3MB | 00:01::27 | 117.58ms | [Soundcloud-link](https://soundcloud.com/awiteb/rust-1671zip) | +| 2687.94MB | 2687.94MB | 01:28:13 | 2.79s | [Soundcloud-link](https://soundcloud.com/awiteb/pop-os-2204-amd64-intel-23iso) | +| 35.3MB | 35.3MB | 00:01::27 | 68.21ms | [Soundcloud-link](https://soundcloud.com/awiteb/rust-1671zip) | ## Disclaimer This tool was designed for educational purposes as it explains how to save data in an audio file. It is not recommended to exploit this thing to use cloud audio storage services to store your data, as your account may be banned.