Skip to content

Commit

Permalink
Implement Audio and improve documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
valentinegb committed Aug 4, 2024
1 parent 95f2cba commit 86196cb
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 43 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ Documentation can be found on [docs.rs](https://docs.rs/beat_saber_map).

- [ ] Create structures for map files
- [x] [`Info`](https://docs.rs/beat_saber_map/latest/beat_saber_map/info/struct.Info.html)
- [ ] `Audio`
- [x] [`Audio`](https://docs.rs/beat_saber_map/latest/beat_saber_map/audio/struct.Audio.html)
- [ ] `Beatmap`
- [ ] `Lightshow`
- [ ] Create utility methods for map file structures and [`BeatSaberMap`](https://docs.rs/beat_saber_map/latest/beat_saber_map/struct.BeatSaberMap.html)
- [x] [`BeatSaberMap::from_dir()`](https://docs.rs/beat_saber_map/latest/beat_saber_map/struct.BeatSaberMap.html#method.from_dir)
- [x] [`Info::from_file()`](https://docs.rs/beat_saber_map/latest/beat_saber_map/info/struct.Info.html#method.from_file)
- [x] [`Audio::from_file()`](https://docs.rs/beat_saber_map/latest/beat_saber_map/audio/struct.Audio.html#method.from_file)
- [ ] More...
21 changes: 21 additions & 0 deletions sample/BPMInfo.dat
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"version": "4.0.0",
"songChecksum": "",
"songSampleCount": 1149214,
"songFrequency": 44100,
"bpmData": [
{
"si": 0,
"ei": 1149214,
"sb": 0,
"eb": 26
}
],
"lufsData": [
{
"si": 0,
"ei": 1149214,
"l": 0
}
]
}
129 changes: 129 additions & 0 deletions src/audio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//! Module related to `BPMInfo.dat` map file.
use std::{fs, path::Path};

use serde::{Deserialize, Serialize};

use crate::Error;

/// Information regarding how audio file should be processed.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/audio.html) for language-agnostic documentation.
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(default)]
pub struct Audio {
/// Should be "4.0.0", that's the currently supported schema version.
pub version: String,
/// Used for verifying internal relationships and leaderboard integrity.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format.html#checksums) for language-agnostic documentation.
pub song_checksum: String,
/// Measures duration of audio file in samples.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/audio.html#sample-count) for language-agnostic documentation.
pub song_sample_count: u32,
/// Caches quality level of audio file.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/audio.html#song-frequency) for language-agnostic documentation.
pub song_frequency: u32,
/// See [`BpmData`].
pub bpm_data: Vec<BpmData>,
/// See [`LufsData`].
pub lufs_data: Vec<LufsData>,
}

impl Default for Audio {
fn default() -> Self {
Self {
version: "4.0.0".to_string(),
song_checksum: Default::default(),
song_sample_count: Default::default(),
song_frequency: Default::default(),
bpm_data: Default::default(),
lufs_data: Default::default(),
}
}
}

impl Audio {
/// Instatiates an [`Audio`] from an audio file, typically named `BPMInfo.dat`.
pub fn from_file(path: impl AsRef<Path>) -> Result<Self, Error> {
Ok(serde_json::from_str(&fs::read_to_string(path)?)?)
}
}

/// Alters BPM of specified region.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/audio.html#bpm-regions) for language-agnostic documentation.
#[derive(Debug, PartialEq, Eq, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct BpmData {
/// Start sample index.
pub si: usize,
/// End sample index.
pub ei: usize,
/// Start beat.
pub sb: usize,
/// End beat.
pub eb: usize,
}

/// Applies normalization to loudness of audio file within specified region.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/audio.html#lufs-data) for language-agnostic documentation.
#[derive(Debug, PartialEq, Eq, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct LufsData {
/// Start sample index.
pub si: usize,
/// End sample index.
pub ei: usize,
/// Loudness.
pub l: usize,
}

#[cfg(test)]
mod tests {
use std::fs;

use super::*;

fn sample() -> String {
fs::read_to_string("sample/BPMInfo.dat").unwrap()
}

fn manual_recreation() -> Audio {
Audio {
version: "4.0.0".to_string(),
song_checksum: "".to_string(),
song_sample_count: 1149214,
song_frequency: 44100,
bpm_data: vec![BpmData {
si: 0,
ei: 1149214,
sb: 0,
eb: 26,
}],
lufs_data: vec![LufsData {
si: 0,
ei: 1149214,
l: 0,
}],
}
}

#[test]
fn serializes_correctly() {
let serialized = serde_json::to_string_pretty(&manual_recreation()).unwrap();

assert_eq!(serialized, sample());
}

#[test]
fn deserializes_correctly() {
let deserialized: Audio = serde_json::from_str(&sample()).unwrap();

assert_eq!(deserialized, manual_recreation());
}
}
46 changes: 5 additions & 41 deletions src/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,19 @@ pub struct Audio {
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#audio-filename-s) for language-agnostic documentation.
pub song_filename: PathBuf,
/// Value (in seconds) which caches length of audio file.
/// Caches length of audio file (in seconds).
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#song-duration) for language-agnostic documentation.
pub song_duration: f64,
/// Audio metadata file associated with map.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#related-files) for language-agnostic documentation.
pub audio_data_filename: PathBuf,
/// Value which dictates how grid will align with audio file.
/// Dictates how grid will align with audio file.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#bpm) for language-agnostic documentation.
pub bpm: f64,
/// Value which controls overall loudness of audio file.
/// Controls overall loudness of audio file.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#lufs-data-integrated) for language-agnostic documentation.
pub lufs: f64,
Expand Down Expand Up @@ -146,42 +146,26 @@ pub struct ColorScheme {
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#color-schemes) for language-agnostic documentation.
pub use_override: bool,
/// Player-facing name of color scheme.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#color-schemes) for language-agnostic documentation.
pub color_scheme_name: String,
/// Color of left saber.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#color-schemes) for language-agnostic documentation.
#[serde(with = "super::hex")]
pub saber_a_color: u32,
/// Color of right saber.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#color-schemes) for language-agnostic documentation.
#[serde(with = "super::hex")]
pub saber_b_color: u32,
/// Color of wall obstacles.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#color-schemes) for language-agnostic documentation.
#[serde(with = "super::hex")]
pub obstacles_color: u32,
/// One of two environment colors.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#color-schemes) for language-agnostic documentation.
#[serde(with = "super::hex")]
pub environment_color_0: u32,
/// One of two environment colors.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#color-schemes) for language-agnostic documentation.
#[serde(with = "super::hex")]
pub environment_color_1: u32,
/// Boosted variant of one of two environment colors.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#color-schemes) for language-agnostic documentation.
#[serde(with = "super::hex")]
pub environment_color_0_boost: u32,
/// Boosted variant of one of two environment colors.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#color-schemes) for language-agnostic documentation.
#[serde(with = "super::hex")]
pub environment_color_1_boost: u32,
}
Expand Down Expand Up @@ -225,55 +209,39 @@ pub struct DifficultyBeatmap {
pub lightshow_data_filename: PathBuf,
}

/// Value which groups beatmaps into unique categories and applies specialized behaviors to those affected beatmaps.
/// Groups beatmaps into unique categories and applies specialized behaviors to those affected beatmaps.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#characteristic) for language-agnostic documentation.
#[derive(Debug, PartialEq, Eq, Default, Deserialize, Serialize)]
pub enum Characteristic {
/// No special behavior.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#characteristic) for language-agnostic documentation.
#[default]
Standard,
/// No special behavior.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#characteristic) for language-agnostic documentation.
NoArrows,
/// Disables Left (Red) saber.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#characteristic) for language-agnostic documentation.
OneSaber,
/// Uses rotation behaviors.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#characteristic) for language-agnostic documentation.
#[serde(rename = "360Degree")]
ThreeSixtyDegree,
/// Uses rotation behaviors.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#characteristic) for language-agnostic documentation.
#[serde(rename = "90Degree")]
NinetyDegree,
/// No special behavior.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#characteristic) for language-agnostic documentation.
Legacy,
}

/// Cosmetic label to indicate overall difficulty of beatmap, relative to its characteristic.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#difficulty) for language-agnostic documentation.
#[allow(missing_docs)]
#[derive(Debug, PartialEq, Eq, Default, Deserialize, Serialize)]
pub enum Difficulty {
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#difficulty) for language-agnostic documentation.
Easy,
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#difficulty) for language-agnostic documentation.
#[default]
Normal,
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#difficulty) for language-agnostic documentation.
Hard,
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#difficulty) for language-agnostic documentation.
Expert,
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#difficulty) for language-agnostic documentation.
ExpertPlus,
}

Expand All @@ -284,12 +252,8 @@ pub enum Difficulty {
#[serde(default)]
pub struct BeatmapAuthors {
/// Map designer(s) of beatmap.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#beatmap-authors) for language-agnostic documentation.
pub mappers: Vec<String>,
/// Light designer(s) of beatmap.
///
/// Refer to the [BSMG Wiki](https://bsmg.wiki/mapping/map-format/info.html#beatmap-authors) for language-agnostic documentation.
pub lighters: Vec<String>,
}

Expand Down
8 changes: 7 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
#![warn(missing_docs)]

pub mod audio;
mod hex;
pub mod info;

use std::{io, path::Path};

use thiserror::Error;

pub use self::info::Info;
pub use self::{audio::Audio, info::Info};

/// Any error that may occur from a function originating in this library.
#[derive(Error, Debug)]
Expand All @@ -41,13 +42,18 @@ pub struct BeatSaberMap {
///
/// See [`Info`].
pub info: Info,
/// `BPMInfo.dat` file.
///
/// See [`Audio`].
pub audio: Audio,
}

impl BeatSaberMap {
/// Deserializes the files in a map folder.
pub fn from_dir(dir: impl AsRef<Path>) -> Result<Self, Error> {
Ok(BeatSaberMap {
info: Info::from_file(dir.as_ref().join("Info.dat"))?,
audio: Audio::from_file(dir.as_ref().join("BPMInfo.dat"))?,
})
}
}

0 comments on commit 86196cb

Please sign in to comment.