nbs/lib.rs
1//! This crate provides functionality for decoding & encoding (Open)NoteBlockStudio buffers.
2//!
3//! It supports the original NBS format, aswell as version 1-4 of the unofficial new format introduced in [OpenNoteBlockStudio](https://github.com/HielkeMinecraft/OpenNoteBlockStudio).
4//! Documentation on the NBS format can be found at [NoteBlockStudio](https://www.stuffbydavid.com/mcnbs/format) and [OpenNoteBlockStudio](https://hielkeminecraft.github.io/OpenNoteBlockStudio/nbs).
5//!
6//! ## Example: Editing a NBS file
7//!
8//! ```rust
9//! use nbs::{
10//! noteblocks::{instrument, note::Note},
11//! Nbs,
12//! };
13//! use std::fs::File;
14//!
15//! fn main() {
16//! let mut file = File::open("tests/1.nbs").unwrap();
17//! let mut nbs = Nbs::decode(&mut file).unwrap();
18//! nbs.noteblocks.layers[2].name = String::from("Cows"); // Renaming the 3rd layer "Cows".
19//! nbs.noteblocks.layers[2].volume = 25; // Setting its volume to 25%.
20//! // Insert a Note in the 3rd layer at tick 0
21//! nbs.noteblocks.layers[2].notes.insert(
22//! 0,
23//! Note::new(instrument::COW_BELL, 33, Some(100), Some(100), Some(0)),
24//! );
25//! // Write the changes to `out1.nbs`.
26//! nbs.encode(&mut File::create("out1.nbs").unwrap()).unwrap();
27//! }
28//! ```
29//! ## Example: Creating a NBS file
30//! ```rust
31//! use nbs::{
32//! header::Header,
33//! noteblocks::{instrument, instrument::CustomInstruments, layer::Layer, note::Note, NoteBlocks},
34//! Nbs, NbsFormat,
35//! };
36//! use std::fs::File;
37//!
38//! fn main() {
39//! let mut file = File::create("out2.nbs").unwrap();
40//! let mut header = Header::new(NbsFormat::OpenNoteBlockStudio(4)); // Create a header.
41//! header.song_name = String::from("test"); // Change the name to `test`.
42//! let mut noteblocks = NoteBlocks::new();
43//! // Create a new Layer.
44//! noteblocks
45//! .layers
46//! .push(Layer::from_format(NbsFormat::OpenNoteBlockStudio(4)));
47//! // Insert 20 notes into the first layer
48//! for i in 0..20 {
49//! noteblocks.layers[0].notes.insert(
50//! i,
51//! Note::new(
52//! instrument::PIANO,
53//! (33 + i) as i8,
54//! Some(100),
55//! Some(100),
56//! Some(0),
57//! ),
58//! );
59//! }
60//! let custom_instruments = CustomInstruments::new(); // Create a empty list of custom instruments.
61//! let mut nbs = Nbs::from_componets(header, noteblocks, custom_instruments); // Assamble everything together.
62//! nbs.update(); // Update certian fields in the header to match the rest of the file.
63//! nbs.encode(&mut file); // save!
64//! }
65//! ```
66
67use error::NbsError;
68use header::Header;
69use io::{ReadStringExt, WriteStringExt};
70use noteblocks::{instrument::CustomInstruments, NoteBlocks};
71use std::time::Duration;
72
73pub mod error;
74pub mod header;
75pub mod io;
76pub mod noteblocks;
77
78#[derive(PartialEq, Debug, Clone, Copy)]
79pub enum NbsFormat {
80 NoteBlockStudio,
81 OpenNoteBlockStudio(i8),
82}
83impl NbsFormat {
84 pub fn is_new(&self) -> bool {
85 match self {
86 NbsFormat::NoteBlockStudio => false,
87 NbsFormat::OpenNoteBlockStudio(_) => true,
88 }
89 }
90 pub fn version(&self) -> i8 {
91 match self {
92 NbsFormat::NoteBlockStudio => 0,
93 &NbsFormat::OpenNoteBlockStudio(v) => v,
94 }
95 }
96}
97
98pub struct Nbs {
99 pub header: Header,
100 pub noteblocks: NoteBlocks,
101 pub custom_instruments: CustomInstruments,
102}
103
104impl Nbs {
105 pub fn from_componets(
106 header: Header,
107 noteblocks: NoteBlocks,
108 custom_instruments: CustomInstruments,
109 ) -> Self {
110 Nbs {
111 header,
112 noteblocks,
113 custom_instruments,
114 }
115 }
116
117 /// Decode a NBS buffer.
118 pub fn decode<R>(mut reader: &mut R) -> Result<Nbs, NbsError>
119 where
120 R: ReadStringExt,
121 {
122 let header = Header::decode(&mut reader)?;
123 let noteblocks = NoteBlocks::decode(&mut reader, &header)?;
124 let custom_instruments = CustomInstruments::decode(&mut reader, &header)?;
125 Ok(Nbs {
126 header,
127 noteblocks,
128 custom_instruments,
129 })
130 }
131
132 /// This method updates some parts of the Header to match the rest of the file
133 pub fn update(&mut self) {
134 if self.format().version() >= 3 {
135 self.header.song_length = Some(self.noteblocks.calculate_length());
136 } else if self.format().version() == 0 {
137 self.header.old_song_length = self.noteblocks.calculate_length();
138 }
139 if self.format().version() > 0 {
140 self.header.version_number = Some(self.format().version());
141 }
142 self.header.layer_count = self.noteblocks.layers.len() as i16;
143 }
144
145 /// Enocde a NBS buffer,
146 pub fn encode<W>(&self, mut writer: &mut W) -> Result<(), NbsError>
147 where
148 W: WriteStringExt,
149 {
150 self.header.encode(self.format(), &mut writer)?;
151 self.noteblocks.encode(self.format(), &mut writer)?;
152 self.custom_instruments.encode(&mut writer)?;
153 Ok(())
154 }
155
156 /// Returns the NBS format for this
157 pub fn format(&self) -> NbsFormat {
158 return self.header.format;
159 }
160
161 /// Returns the song ticks.
162 pub fn song_ticks(&self) -> i16 {
163 self.noteblocks.calculate_length()
164 }
165
166 /// Returns the song duration.
167 pub fn song_length(&self) -> Duration {
168 Duration::from_secs_f32(self.song_ticks() as f32 / (self.header.song_tempo as f32 / 100.0))
169 }
170}