Skip to content

Commit

Permalink
added equalizer preset parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
Raleigh Littles authored and Raleigh Littles committed Nov 17, 2024
1 parent e7409c6 commit 89d498c
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 16 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The table below shows which iTunesDB files are supported.
|----------------------|------------------------------------------------------------------|
| Artwork DB | :negative_squared_cross_mark: Not yet supported |
| DeviceInfo | Partial - Can extract iPod name only |
| Equalizer Presets | :negative_squared_cross_mark: Not yet supported |
| Equalizer Presets | :heavy_check_mark: Extracts both the iTunes and actual DSP values |
| On The Go Playlist | :negative_squared_cross_mark: Not yet supported |
| Photo Database | :heavy_check_mark: Can extract all associated metadata of images |
| PhotoFolderAlbums | Partial - only can detect the # of photo albums |
Expand Down Expand Up @@ -75,6 +75,7 @@ The 7 possible "type" options are:
| "pfalbums" | Photo Folder Albums |
| "preferences" | Preferences file |
| "deviceinfo" | DeviceInfo file |
| "equalizer" | Equalizer Presets file |


```bash
Expand Down
2 changes: 1 addition & 1 deletion parser/src/constants/deviceinfo_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
*/

pub const DEVICEINFO_FILE_SIZE: usize = 1536; // 0x600
pub const DEVICEINFO_STRING_LENGTH: usize = 510;
pub const DEVICEINFO_MAX_STRING_LENGTH: usize = 510;
39 changes: 39 additions & 0 deletions parser/src/constants/equalizer_constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* File: equalizer_constants.rs
*
* http://www.ipodlinux.org/ITunesDB/#Equalizer_Presets_file
*/

/// Container constants
pub const EQUALIZER_PRESET_CONTAINER_OBJECT_KEY: &str = "mqed";
pub const EQUALIZER_NUMBER_OF_PRESETS_OFFSET: usize = 16;
pub const EQUALIZER_NUMBER_OF_PRESETS_LEN: usize = 4;

pub const EQUALIZER_PRESET_CHILDSIZE_OFFSET : usize = 20;
pub const EQUALIZER_PRESET_CHILDSIZE_LEN : usize = 4;
pub const EQUALIZER_PRESET_CHILDSIZE_VALUE : usize = 588;


pub const EQUALIZER_PRESET_PRESET_OBJECT_KEY: &str = "pqed";
pub const EQUALIZER_PRESET_PRESET_NAME_LENGTH_OFFSET : usize = 4;
pub const EQUALIZER_PRESET_FIELD_MAX_LENGTH : usize = 510;

pub const EQUALIZER_PRESET_NAME_OFFSET : usize = 6; // 4 + 2

pub const EQUALIZER_PREAMP_OFFSET : usize = 516; // 4 + 2 + 510
pub const EQUALIZER_PREAMP_LEN : usize = 4;

pub const EQUALIZER_NUM_OF_ITUNES_BANDS_OFFSET : usize = EQUALIZER_PREAMP_OFFSET + EQUALIZER_PREAMP_LEN;
pub const EQUALIZER_NUM_OF_ITUNES_BANDS_EXPECTED_VALUE : usize = 10;

pub const EQUALIZER_ITUNES_BAND_VALUES_OFFSET : usize = EQUALIZER_NUM_OF_ITUNES_BANDS_OFFSET + 4;
pub const EQUALIZER_ITUNES_BAND_VALUES_LEN : usize = 40;

pub const MAX_EQUALIZER_BAND_VALUE : i32 = 1200;
pub const MIN_EQUALIZER_BAND_VALUE : i32 = -1200;

pub const EQUALIZER_NUM_OF_DSP_BANDS_OFFSET : usize = EQUALIZER_ITUNES_BAND_VALUES_OFFSET + EQUALIZER_ITUNES_BAND_VALUES_LEN;
pub const EQUALIZER_NUM_OF_DSP_BANDS_EXPECTED_VALUE : usize = 5;

pub const EQUALIZER_DSP_BAND_VALUES_OFFSET : usize = EQUALIZER_NUM_OF_DSP_BANDS_OFFSET + 4;
pub const EQUALIZER_DSP_BAND_VALUES_LEN : usize = 20;
7 changes: 6 additions & 1 deletion parser/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod constants {
pub mod playcounts_constants;
pub mod preferences_constants;
pub mod deviceinfo_constants;
pub mod equalizer_constants;
}

mod helpers {
Expand All @@ -23,6 +24,7 @@ mod parsers {
pub mod playcounts_parser;
pub mod preferences_parser;
pub mod deviceinfo_parser;
pub mod equalizer_parser;
}

mod itunesdb;
Expand Down Expand Up @@ -64,7 +66,7 @@ fn main() {

let itunesdb_file_type: String = std::env::args()
.nth(2)
.expect("Missing second parameter: iTunes DB file type. Supported types are 'photo', 'itunes', 'itprefs', 'playcounts', 'pfalbumbs', and 'preferences'");
.expect("Missing second parameter: iTunes DB file type");

let desired_report_csv_filename = itunesdb_filename.to_string() + ".csv";

Expand All @@ -90,6 +92,9 @@ fn main() {
} else if itunesdb_file_type == "deviceinfo" {
parsers::deviceinfo_parser::parse_device_info_file(itunesdb_file_as_bytes);
}
else if itunesdb_file_type == "equalizer" {
parsers::equalizer_parser::parse_equalizer_file(itunesdb_file_as_bytes);
}
else {
println!(
"'{}' is not a supported iTunesDB file type!",
Expand Down
32 changes: 19 additions & 13 deletions parser/src/parsers/deviceinfo_parser.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@

use crate::constants::deviceinfo_constants;
use crate::helpers::helpers;

pub fn parse_device_info_file(deviceinfo_file_as_bytes: Vec<u8>) {

if deviceinfo_file_as_bytes.len() != deviceinfo_constants::DEVICEINFO_FILE_SIZE {
panic!("Invalid DeviceInfo file size! Expected: {} | Got: {}", deviceinfo_constants::DEVICEINFO_FILE_SIZE, deviceinfo_file_as_bytes.len());
panic!(
"Invalid DeviceInfo file size! Expected: {} | Got: {}",
deviceinfo_constants::DEVICEINFO_FILE_SIZE,
deviceinfo_file_as_bytes.len()
);
}

let ipod_name_length_raw = &deviceinfo_file_as_bytes[0..2];

let ipod_name_length = helpers::build_le_u16_from_bytes(ipod_name_length_raw) as usize;

// The strings are formatted using UTF-16 so this byte value must be a multiple of 2
if ipod_name_length % 2 != 0 {
panic!("Invalid iPod Name Length! Expected multiple of 2 | Got: {}", ipod_name_length);
if ipod_name_length % 2 != 0
|| ipod_name_length > deviceinfo_constants::DEVICEINFO_MAX_STRING_LENGTH
{
panic!("Invalid iPod Name length value of '{}'", ipod_name_length);
}

println!("iPod Name Length: {}", ipod_name_length);

// factor of 2 to account for UTF-16 encoding (2 bytes per character),
// and the +2 to account for the length bytes
let ipod_name_raw_bytes = &deviceinfo_file_as_bytes[2 .. (ipod_name_length * 2 + 2)];

println!("iPod Name: {:?}", String::from_utf16(&helpers::return_utf16_from_utf8(ipod_name_raw_bytes)).unwrap());


}
// and the +2 to account for the length bytes.
// no need to use helper method here because there's no index variable
let ipod_name_raw_bytes = &deviceinfo_file_as_bytes[2..(ipod_name_length * 2 + 2)];

println!(
"iPod Name: {:?}",
String::from_utf16(&helpers::return_utf16_from_utf8(ipod_name_raw_bytes)).unwrap()
);
}
157 changes: 157 additions & 0 deletions parser/src/parsers/equalizer_parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use crate::constants::equalizer_constants;
use crate::constants::itunesdb_constants;

use crate::helpers::helpers;

pub fn parse_equalizer_file(equalizer_file_as_bytes: Vec<u8>) {
let mut idx: usize = 0;

while idx < (equalizer_file_as_bytes.len() - itunesdb_constants::DEFAULT_SUBSTRUCTURE_SIZE) {
let equalizer_type_heading: &[u8] =
&equalizer_file_as_bytes[idx..idx + itunesdb_constants::DEFAULT_SUBSTRUCTURE_SIZE];

if equalizer_type_heading
== equalizer_constants::EQUALIZER_PRESET_CONTAINER_OBJECT_KEY.as_bytes()
{
let num_presets = helpers::get_slice_as_le_u32(
idx,
&equalizer_file_as_bytes,
equalizer_constants::EQUALIZER_NUMBER_OF_PRESETS_OFFSET,
equalizer_constants::EQUALIZER_NUMBER_OF_PRESETS_LEN,
);

println!("Equalizer file has {} presets", num_presets);

let preset_child_size = helpers::get_slice_as_le_u32(
idx,
&equalizer_file_as_bytes,
equalizer_constants::EQUALIZER_PRESET_CHILDSIZE_OFFSET,
equalizer_constants::EQUALIZER_PRESET_CHILDSIZE_LEN,
);

if preset_child_size != equalizer_constants::EQUALIZER_PRESET_CHILDSIZE_VALUE as u32 {
panic!("Invalid preset child size value of '{}'", preset_child_size);
}

println!("==========");
} else if equalizer_type_heading
== equalizer_constants::EQUALIZER_PRESET_PRESET_OBJECT_KEY.as_bytes()
{
let preset_name_length = helpers::build_le_u16_from_bytes(
&equalizer_file_as_bytes[idx
+ equalizer_constants::EQUALIZER_PRESET_PRESET_NAME_LENGTH_OFFSET
..idx + equalizer_constants::EQUALIZER_PRESET_PRESET_NAME_LENGTH_OFFSET + 2],
) as usize;

if preset_name_length > equalizer_constants::EQUALIZER_PRESET_FIELD_MAX_LENGTH {
panic!(
"Invalid preset name length value of '{}'",
preset_name_length
);
}

println!("Preset Name Length: {}", preset_name_length);

// Factor of 2 to account for UTF-16 encoding (2 bytes per character)
let preset_name_raw_bytes = helpers::get_slice_from_offset_with_len(
idx,
&equalizer_file_as_bytes,
equalizer_constants::EQUALIZER_PRESET_NAME_OFFSET,
preset_name_length * 2,
);

println!(
"Preset Name: {:?}",
String::from_utf16(&helpers::return_utf16_from_utf8(&preset_name_raw_bytes))
.unwrap()
);

let preamp = helpers::get_slice_as_le_u32(
idx,
&equalizer_file_as_bytes,
equalizer_constants::EQUALIZER_PREAMP_OFFSET,
equalizer_constants::EQUALIZER_PREAMP_LEN,
);

println!("Preamp value: {}", preamp);

let num_itunes_bands = helpers::get_slice_as_le_u32(
idx,
&equalizer_file_as_bytes,
equalizer_constants::EQUALIZER_NUM_OF_ITUNES_BANDS_OFFSET,
4,
);

if num_itunes_bands
!= equalizer_constants::EQUALIZER_NUM_OF_ITUNES_BANDS_EXPECTED_VALUE as u32
{
panic!(
"Invalid number of equalizer bands value of '{}'",
num_itunes_bands
);
}

let equalizer_itunes_band_values_bytes = helpers::get_slice_from_offset_with_len(
idx,
&equalizer_file_as_bytes,
equalizer_constants::EQUALIZER_ITUNES_BAND_VALUES_OFFSET,
equalizer_constants::EQUALIZER_ITUNES_BAND_VALUES_LEN,
);

for i in (0..equalizer_itunes_band_values_bytes.len()).step_by(4) {
let band_value =
helpers::build_le_u32_from_bytes(&equalizer_itunes_band_values_bytes[i..i + 4])
as i32;

if band_value > equalizer_constants::MAX_EQUALIZER_BAND_VALUE
|| band_value < equalizer_constants::MIN_EQUALIZER_BAND_VALUE
{
panic!("Invalid equalizer band value of '{}'", band_value);
}

println!("[iTunes] Band Value: {}", band_value);
}

let num_dsp_bands = helpers::get_slice_as_le_u32(
idx,
&equalizer_file_as_bytes,
equalizer_constants::EQUALIZER_NUM_OF_DSP_BANDS_OFFSET,
4,
);

if num_dsp_bands
!= equalizer_constants::EQUALIZER_NUM_OF_DSP_BANDS_EXPECTED_VALUE as u32
{
panic!(
"Invalid number of equalizer bands value of '{}'",
num_dsp_bands
);
}

let equalizer_dsp_band_values_bytes = helpers::get_slice_from_offset_with_len(
idx,
&equalizer_file_as_bytes,
equalizer_constants::EQUALIZER_DSP_BAND_VALUES_OFFSET,
equalizer_constants::EQUALIZER_DSP_BAND_VALUES_LEN,
);

for i in (0..equalizer_dsp_band_values_bytes.len()).step_by(4) {
let band_value =
helpers::build_le_u32_from_bytes(&equalizer_itunes_band_values_bytes[i..i + 4])
as i32;

if band_value > equalizer_constants::MAX_EQUALIZER_BAND_VALUE
|| band_value < equalizer_constants::MIN_EQUALIZER_BAND_VALUE
{
panic!("Invalid equalizer band value of '{}'", band_value);
}

println!("[DSP] Band Value: {}", band_value);
}

println!("-----------");
}

idx += itunesdb_constants::DEFAULT_SUBSTRUCTURE_SIZE
}
}
Binary file added sample-files/2005-10-06_iTunesEQPresets
Binary file not shown.

0 comments on commit 89d498c

Please sign in to comment.