Create a new utils file with common useful functions.
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
Split the codecs in two new enums OriginalCodec and NewCodec.
This commit is contained in:
parent
6951dc7f11
commit
3bdcc7faed
|
@ -1,3 +1,4 @@
|
|||
pub mod reader;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
pub mod writer;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::types::{BlockType, Checksum, Codec, Version, Voc, SIGNATURE1, SIGNATURE2};
|
||||
use crate::types::{BlockType, Checksum, Version, Voc, SIGNATURE1, SIGNATURE2};
|
||||
use crate::utils::{calculate_checksum, match_codec_id_u16, match_codec_id_u8};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::streaming::{tag, take};
|
||||
use nom::combinator::map_res;
|
||||
|
@ -26,15 +27,11 @@ fn parse_sound_data(input: &[u8]) -> IResult<&[u8], BlockType> {
|
|||
),
|
||||
)(input)?;
|
||||
let sample_rate: u32 = 1000000_u32 / (256_u32 - frequency_divisor as u32);
|
||||
let codec = match codec_id {
|
||||
0 => Codec::Pcm8BitUnsigned,
|
||||
1 => Codec::Adpcm4to8,
|
||||
2 => Codec::Adpcm3to8,
|
||||
3 => Codec::Adpcm2to8,
|
||||
4 => Codec::Pcm16BitSigned,
|
||||
6 => Codec::Alaw,
|
||||
7 => Codec::Ulaw,
|
||||
_ => return Err(Err::Error(Error::new(input, ErrorKind::Alt))),
|
||||
let codec = match match_codec_id_u8(codec_id) {
|
||||
Ok(original_codec) => original_codec,
|
||||
Err(error) => {
|
||||
panic!("Failed to match codec ID: {}", error);
|
||||
}
|
||||
};
|
||||
let (input, data) = context("Failed to parse data", take(block_size - header_size))(input)?;
|
||||
Ok((
|
||||
|
@ -114,15 +111,11 @@ fn parse_extra_information(input: &[u8]) -> IResult<&[u8], BlockType> {
|
|||
),
|
||||
)(input)?;
|
||||
let (input, channels) = context("Failed to parse channels", le_u8)(input)?;
|
||||
let codec = match codec_id {
|
||||
0 => Codec::Pcm8BitUnsigned,
|
||||
1 => Codec::Adpcm4to8,
|
||||
2 => Codec::Adpcm3to8,
|
||||
3 => Codec::Adpcm2to8,
|
||||
4 => Codec::Pcm16BitSigned,
|
||||
6 => Codec::Alaw,
|
||||
7 => Codec::Ulaw,
|
||||
_ => return Err(Err::Error(Error::new(input, ErrorKind::Alt))),
|
||||
let codec = match match_codec_id_u8(codec_id) {
|
||||
Ok(original_codec) => original_codec,
|
||||
Err(error) => {
|
||||
panic!("Failed to match codec ID: {}", error);
|
||||
}
|
||||
};
|
||||
let sample_rate: u32 =
|
||||
256000000_u32 / ((channels as u32 + 1) * (65536 - frequency_divisor as u32));
|
||||
|
@ -160,16 +153,11 @@ fn parse_sound_data_new(input: &[u8]) -> IResult<&[u8], BlockType> {
|
|||
)(input)?;
|
||||
let (input, reserved) = context("Failed to parse reserved", le_u32)(input)?;
|
||||
let (input, data) = context("Failed to parse data", take(block_size - header_size))(input)?;
|
||||
let codec = match codec_id {
|
||||
0 => Codec::Pcm8BitUnsigned,
|
||||
1 => Codec::Adpcm4to8,
|
||||
2 => Codec::Adpcm3to8,
|
||||
3 => Codec::Adpcm2to8,
|
||||
4 => Codec::Pcm16BitSigned,
|
||||
6 => Codec::Alaw,
|
||||
7 => Codec::Ulaw,
|
||||
0x0200 => Codec::Adpcm4to16,
|
||||
_ => return Err(Err::Error(Error::new(input, ErrorKind::Alt))),
|
||||
let codec = match match_codec_id_u16(codec_id) {
|
||||
Ok(original_codec) => original_codec,
|
||||
Err(error) => {
|
||||
panic!("Failed to match codec ID: {}", error);
|
||||
}
|
||||
};
|
||||
Ok((
|
||||
input,
|
||||
|
@ -235,8 +223,11 @@ pub fn parse_voc(input: &[u8]) -> IResult<&[u8], Voc> {
|
|||
let (input, checksum) = context("Failed to parse checksum", le_u16)(input)?;
|
||||
|
||||
// Calculate checksum
|
||||
let checksum2 = (!i16::from_le_bytes([version_minor, version_major]) + 0x1234) as u16;
|
||||
let valid = checksum == checksum2;
|
||||
let correct_checksum = calculate_checksum(Version {
|
||||
major: version_major,
|
||||
minor: version_minor,
|
||||
});
|
||||
let valid = checksum == correct_checksum;
|
||||
|
||||
let mut blocks = Vec::new();
|
||||
let mut remaining_input = input;
|
||||
|
|
34
src/types.rs
34
src/types.rs
|
@ -42,13 +42,28 @@ pub struct Checksum {
|
|||
pub valid: bool,
|
||||
}
|
||||
|
||||
/// Represents the codec formats.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The `Adpcm4to16` codec is only allowed in a `SoundDataNew`.
|
||||
/// Represents the codec formats used in `SoundData` and `ExtraInformation`.
|
||||
#[derive(Eq, Debug, PartialEq)]
|
||||
pub enum Codec {
|
||||
pub enum OriginalCodec {
|
||||
/// PCM 8-bit unsigned format.
|
||||
Pcm8BitUnsigned,
|
||||
/// ADPCM 4-bit to 8-bit format.
|
||||
Adpcm4to8,
|
||||
/// ADPCM 3-bit to 8-bit format.
|
||||
Adpcm3to8,
|
||||
/// ADPCM 2-bit to 8-bit format.
|
||||
Adpcm2to8,
|
||||
/// PCM 16-bit signed format.
|
||||
Pcm16BitSigned,
|
||||
/// A-law format.
|
||||
Alaw,
|
||||
/// u-law format.
|
||||
Ulaw,
|
||||
}
|
||||
|
||||
/// Represents the codec formats used in `SoundDataNew`.
|
||||
#[derive(Eq, Debug, PartialEq)]
|
||||
pub enum NewCodec {
|
||||
/// PCM 8-bit unsigned format.
|
||||
Pcm8BitUnsigned,
|
||||
/// ADPCM 4-bit to 8-bit format.
|
||||
|
@ -66,6 +81,7 @@ pub enum Codec {
|
|||
/// ADPCM 4-bit to 16-bit format.
|
||||
Adpcm4to16,
|
||||
}
|
||||
|
||||
/// Represents the different types of blocks in a VOC file.
|
||||
#[derive(Eq, Debug, PartialEq)]
|
||||
pub enum BlockType {
|
||||
|
@ -76,7 +92,7 @@ pub enum BlockType {
|
|||
/// The sample rate of the sound data.
|
||||
sample_rate: u32,
|
||||
/// The codec used to encode the sound data.
|
||||
codec: Codec,
|
||||
codec: OriginalCodec,
|
||||
/// The sound data.
|
||||
data: Vec<u8>,
|
||||
},
|
||||
|
@ -114,7 +130,7 @@ pub enum BlockType {
|
|||
/// The sample rate of the sound data.
|
||||
sample_rate: u32,
|
||||
/// The codec used to encode the sound data.
|
||||
codec: Codec,
|
||||
codec: OriginalCodec,
|
||||
/// The number of channels in the sound data.
|
||||
channels: u8,
|
||||
},
|
||||
|
@ -127,7 +143,7 @@ pub enum BlockType {
|
|||
/// The number of channels in the sound data.
|
||||
channels: u8,
|
||||
/// The codec used to encode the sound data.
|
||||
codec: Codec,
|
||||
codec: NewCodec,
|
||||
/// Reserved for future use.
|
||||
reserved: u32,
|
||||
/// The sound data.
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
use crate::types::{NewCodec, OriginalCodec, Version};
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
/// Calculates the checksum for a given `Version`.
|
||||
///
|
||||
/// The checksum is calculated by taking the bitwise NOT of a little-endian i16
|
||||
/// value constructed from the `minor` and `major` fields of the `Version`
|
||||
/// struct, adding 0x1234 to it, and then casting it to a u16.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `version` - The `Version` struct for which to calculate the checksum.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The calculated checksum as a u16 value.
|
||||
pub fn calculate_checksum(version: Version) -> u16 {
|
||||
(!i16::from_le_bytes([version.minor, version.major]) + 0x1234) as u16
|
||||
}
|
||||
|
||||
/// Matches a u8 `codec_id` to its corresponding `OriginalCodec`.
|
||||
///
|
||||
/// This function takes a `codec_id` value as a u8 and returns a `Result`
|
||||
/// containing the corresponding `OriginalCodec`. If the `codec_id` is invalid
|
||||
/// and does not correspond to any known `OriginalCodec`, an `Error` is returned.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `codec_id` - The u8 `codec_id` value to match to an `OriginalCodec`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` containing the corresponding `OriginalCodec` if the `codec_id`
|
||||
/// is valid, or an `Error` if it is invalid.
|
||||
pub fn match_codec_id_u8(codec_id: u8) -> Result<OriginalCodec, Error> {
|
||||
match codec_id {
|
||||
0 => Ok(OriginalCodec::Pcm8BitUnsigned),
|
||||
1 => Ok(OriginalCodec::Adpcm4to8),
|
||||
2 => Ok(OriginalCodec::Adpcm3to8),
|
||||
3 => Ok(OriginalCodec::Adpcm2to8),
|
||||
4 => Ok(OriginalCodec::Pcm16BitSigned),
|
||||
6 => Ok(OriginalCodec::Alaw),
|
||||
7 => Ok(OriginalCodec::Ulaw),
|
||||
_ => Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("Invalid codec id `{}` for OriginalCodec", codec_id),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Matches a u16 `codec_id` to its corresponding `NewCodec`.
|
||||
///
|
||||
/// This function takes a `codec_id` value as a u16 and returns a `Result`
|
||||
/// containing the corresponding `NewCodec`. If the `codec_id` is invalid
|
||||
/// and does not correspond to any known `NewCodec`, an `Error` is returned.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `codec_id` - The u16 `codec_id` value to match to an `NewCodec`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` containing the corresponding `NewCodec` if the `codec_id`
|
||||
/// is valid, or an `Error` if it is invalid.
|
||||
pub fn match_codec_id_u16(codec_id: u16) -> Result<NewCodec, Error> {
|
||||
match codec_id {
|
||||
0 => Ok(NewCodec::Pcm8BitUnsigned),
|
||||
1 => Ok(NewCodec::Adpcm4to8),
|
||||
2 => Ok(NewCodec::Adpcm3to8),
|
||||
3 => Ok(NewCodec::Adpcm2to8),
|
||||
4 => Ok(NewCodec::Pcm16BitSigned),
|
||||
6 => Ok(NewCodec::Alaw),
|
||||
7 => Ok(NewCodec::Ulaw),
|
||||
0x0200 => Ok(NewCodec::Adpcm4to16),
|
||||
_ => Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("Invalid codec id `{}` for NewCodec", codec_id),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the corresponding codec ID for a given `OriginalCodec`.
|
||||
///
|
||||
/// This function takes a reference to an `OriginalCodec` and returns the corresponding codec ID
|
||||
/// as an `u8`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `codec` - A reference to the `OriginalCodec` for which the corresponding codec ID is needed.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The corresponding codec ID for the given `OriginalCodec` as an `u8`.
|
||||
pub fn match_original_codec(codec: &OriginalCodec) -> u8 {
|
||||
match codec {
|
||||
OriginalCodec::Pcm8BitUnsigned => 0,
|
||||
OriginalCodec::Adpcm4to8 => 1,
|
||||
OriginalCodec::Adpcm3to8 => 2,
|
||||
OriginalCodec::Adpcm2to8 => 3,
|
||||
OriginalCodec::Pcm16BitSigned => 4,
|
||||
OriginalCodec::Alaw => 6,
|
||||
OriginalCodec::Ulaw => 7,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the corresponding codec ID for a given `NewCodec`.
|
||||
///
|
||||
/// This function takes a reference to an `NewCodec` and returns the corresponding codec ID
|
||||
/// as an `u16`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `codec` - A reference to the `NewCodec` for which the corresponding codec ID is needed.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The corresponding codec ID for the given `NewCodec` as an `u16`.
|
||||
pub fn match_new_codec(codec: &NewCodec) -> u16 {
|
||||
match codec {
|
||||
NewCodec::Pcm8BitUnsigned => 0,
|
||||
NewCodec::Adpcm4to8 => 1,
|
||||
NewCodec::Adpcm3to8 => 2,
|
||||
NewCodec::Adpcm2to8 => 3,
|
||||
NewCodec::Pcm16BitSigned => 4,
|
||||
NewCodec::Alaw => 6,
|
||||
NewCodec::Ulaw => 7,
|
||||
NewCodec::Adpcm4to16 => 0x0200,
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use crate::types::{BlockType, Codec, Voc, SIGNATURE1, SIGNATURE2};
|
||||
use crate::types::{BlockType, Voc, SIGNATURE1, SIGNATURE2};
|
||||
use crate::utils::{match_new_codec, match_original_codec};
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
fn get_size(size: usize) -> [u8; 3] {
|
||||
|
@ -26,16 +27,7 @@ fn write_sound_data(block: &BlockType) -> Result<Vec<u8>, Error> {
|
|||
bytes.extend_from_slice(&get_size(data.len() + header_size));
|
||||
let frequency_divisor: u8 = (256_u32 - 1000000_u32 / sample_rate) as u8;
|
||||
bytes.extend_from_slice(&[frequency_divisor]);
|
||||
let codec_id: u8 = match codec {
|
||||
Codec::Pcm8BitUnsigned => 0,
|
||||
Codec::Adpcm4to8 => 1,
|
||||
Codec::Adpcm3to8 => 2,
|
||||
Codec::Adpcm2to8 => 3,
|
||||
Codec::Pcm16BitSigned => 4,
|
||||
Codec::Alaw => 6,
|
||||
Codec::Ulaw => 7,
|
||||
_ => return Err(Error::new(ErrorKind::Other, "Invalid codec")),
|
||||
};
|
||||
let codec_id = match_original_codec(codec);
|
||||
bytes.extend_from_slice(&[codec_id]);
|
||||
bytes.extend_from_slice(data);
|
||||
Ok(bytes)
|
||||
|
@ -136,16 +128,7 @@ fn write_extra_information(block: &BlockType) -> Result<Vec<u8>, Error> {
|
|||
let frequency_divisor =
|
||||
(65536 - (256000000_u32 / ((*channels as u32 + 1) * sample_rate))) as u16;
|
||||
bytes.extend_from_slice(&frequency_divisor.to_le_bytes());
|
||||
let codec_id: u8 = match codec {
|
||||
Codec::Pcm8BitUnsigned => 0,
|
||||
Codec::Adpcm4to8 => 1,
|
||||
Codec::Adpcm3to8 => 2,
|
||||
Codec::Adpcm2to8 => 3,
|
||||
Codec::Pcm16BitSigned => 4,
|
||||
Codec::Alaw => 6,
|
||||
Codec::Ulaw => 7,
|
||||
_ => return Err(Error::new(ErrorKind::Other, "Invalid codec")),
|
||||
};
|
||||
let codec_id = match_original_codec(codec);
|
||||
bytes.extend_from_slice(&[codec_id]);
|
||||
bytes.extend_from_slice(&[*channels]);
|
||||
Ok(bytes)
|
||||
|
@ -173,16 +156,7 @@ fn write_sound_data_new(block: &BlockType) -> Result<Vec<u8>, Error> {
|
|||
bytes.extend_from_slice(&sample_rate.to_le_bytes());
|
||||
bytes.extend_from_slice(&[*bits]);
|
||||
bytes.extend_from_slice(&[*channels]);
|
||||
let codec_id: u16 = match codec {
|
||||
Codec::Pcm8BitUnsigned => 0,
|
||||
Codec::Adpcm4to8 => 1,
|
||||
Codec::Adpcm3to8 => 2,
|
||||
Codec::Adpcm2to8 => 3,
|
||||
Codec::Pcm16BitSigned => 4,
|
||||
Codec::Alaw => 6,
|
||||
Codec::Ulaw => 7,
|
||||
Codec::Adpcm4to16 => 0x0200,
|
||||
};
|
||||
let codec_id = match_new_codec(codec);
|
||||
bytes.extend_from_slice(&codec_id.to_le_bytes());
|
||||
bytes.extend_from_slice(&reserved.to_le_bytes());
|
||||
bytes.extend_from_slice(data);
|
||||
|
@ -225,13 +199,12 @@ pub fn write_voc(voc: &Voc) -> Vec<u8> {
|
|||
// Version
|
||||
bytes.extend_from_slice(&[voc.version.minor, voc.version.major]);
|
||||
// Checksum
|
||||
let checksum = (!i16::from_le_bytes([voc.version.minor, voc.version.major]) + 0x1234) as u16;
|
||||
bytes.extend_from_slice(&checksum.to_le_bytes());
|
||||
bytes.extend_from_slice(&voc.checksum.value.to_le_bytes());
|
||||
for block in &voc.blocks {
|
||||
match write_block(block) {
|
||||
Ok(block_bytes) => {
|
||||
bytes.extend_from_slice(&block_bytes);
|
||||
},
|
||||
}
|
||||
Err(error) => {
|
||||
eprintln!("Error writing block: {}", error);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use vocnom::reader::parse_voc;
|
||||
use vocnom::types::{BlockType, Codec};
|
||||
use vocnom::types::{BlockType, NewCodec};
|
||||
|
||||
const VOC_CONTENTS: &[u8] = include_bytes!("../assets/ADOOR2.VOC");
|
||||
|
||||
|
@ -60,7 +60,7 @@ fn block_0_sound_data_new_test() {
|
|||
assert_eq!(sample_rate, &11025);
|
||||
assert_eq!(bits, &8);
|
||||
assert_eq!(channels, &1);
|
||||
assert_eq!(codec, &Codec::Pcm8BitUnsigned);
|
||||
assert_eq!(codec, &NewCodec::Pcm8BitUnsigned);
|
||||
assert_eq!(reserved, &0);
|
||||
assert_eq!(data.len(), 21394);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use vocnom::reader::parse_voc;
|
||||
use vocnom::types::{BlockType, Codec};
|
||||
use vocnom::types::{BlockType, OriginalCodec};
|
||||
|
||||
const VOC_CONTENTS: &[u8] = include_bytes!("../assets/C24FF78A.VOC");
|
||||
|
||||
|
@ -55,7 +55,7 @@ fn block_0_sound_data_test() {
|
|||
} = &voc.blocks[1]
|
||||
{
|
||||
assert_eq!(sample_rate, &11111);
|
||||
assert_eq!(codec, &Codec::Pcm8BitUnsigned);
|
||||
assert_eq!(codec, &OriginalCodec::Pcm8BitUnsigned);
|
||||
assert_eq!(data.len(), 485);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use vocnom::reader::parse_voc;
|
||||
use vocnom::types::{BlockType, Codec};
|
||||
use vocnom::types::{BlockType, OriginalCodec};
|
||||
|
||||
const VOC_CONTENTS: &[u8] = include_bytes!("../assets/CONGA.VOC");
|
||||
|
||||
|
@ -71,7 +71,7 @@ fn block_1_extra_information_test() {
|
|||
{
|
||||
assert_eq!(sample_rate, &11158);
|
||||
assert_eq!(channels, &1);
|
||||
assert_eq!(codec, &Codec::Pcm8BitUnsigned);
|
||||
assert_eq!(codec, &OriginalCodec::Pcm8BitUnsigned);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -92,7 +92,7 @@ fn block_2_sound_data_test() {
|
|||
} = &voc.blocks[2]
|
||||
{
|
||||
assert_eq!(sample_rate, &22222);
|
||||
assert_eq!(codec, &Codec::Pcm8BitUnsigned);
|
||||
assert_eq!(codec, &OriginalCodec::Pcm8BitUnsigned);
|
||||
assert_eq!(data.len(), 164912);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use vocnom::reader::parse_voc;
|
||||
use vocnom::types::{BlockType, Codec};
|
||||
use vocnom::types::{BlockType, OriginalCodec};
|
||||
|
||||
const VOC_CONTENTS: &[u8] = include_bytes!("../assets/EDEN.MUS");
|
||||
|
||||
|
@ -70,7 +70,7 @@ fn block_1_sound_data_test() {
|
|||
} = &voc.blocks[1]
|
||||
{
|
||||
assert_eq!(sample_rate, &11111);
|
||||
assert_eq!(codec, &Codec::Pcm8BitUnsigned);
|
||||
assert_eq!(codec, &OriginalCodec::Pcm8BitUnsigned);
|
||||
assert_eq!(data.len(), 809282);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use vocnom::reader::parse_voc;
|
||||
use vocnom::types::{BlockType, Codec};
|
||||
use vocnom::types::{BlockType, OriginalCodec};
|
||||
|
||||
const VOC_CONTENTS: &[u8] = include_bytes!("../assets/GUARDIAN.VOC");
|
||||
|
||||
|
@ -56,7 +56,7 @@ fn blocks_0_to_3_sound_data_test() {
|
|||
} = &voc.blocks[idx]
|
||||
{
|
||||
assert_eq!(sample_rate, &16129);
|
||||
assert_eq!(codec, &Codec::Pcm8BitUnsigned);
|
||||
assert_eq!(codec, &OriginalCodec::Pcm8BitUnsigned);
|
||||
assert_eq!(data.len(), 202498);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue