triseratops/tag/
serato32.rs

1// Copyright (c) 2023 0000001 1001100`
2//
3// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy
4// of the MPL was not distributed with this file, You can obtain one at
5// http://mozilla.org/MPL/2.0/.
6//
7// SPDX-License-Identifier: MPL-2.0
8
9//! These functions convert between a custom 4-byte format (that we'll call `serato32` for brevity)
10//! and 3-byte plaintext (both `u32`). Serato's custom format inserts a single null bit after every 7
11//! payload bits, starting from the rightmost bit.
12//!
13//! This format is used to encode the 3-byte RGB color values (track color, cue colors) and the cue
14//! positions and the `Serato Markers_` tag.
15//!
16//! # Binary Format Details
17//!
18//! ```text
19//! serato32     |     Byte1     |     Byte2     |     Byte3     |     Byte4     |
20//!              | Nibb1 | Nibb2 | Nibb3 | Nibb4 | Nibb5 | Nibb6 | Nibb7 | Nibb8 |
21//! Bits         |A A A A B B B B C C C C D D D D E E E E F F F F G G G G H H H H|
22//! Ignored Bits |^ ^ ^ ^ ^       ^               ^               ^              |
23//! Plaintext    |||||||||||     Byte1       |      Byte2      |      Byte3      |
24//!              |||||||||||  Nibb1  | Nibb2 |  Nibb3  | Nibb4 |  Nibb5  | Nibb6 |
25//! ```
26//!
27//! More information can be found in the [format
28//! documentation](https://github.com/Holzhaus/serato-tags/blob/master/docs/serato_markers_.md#custom-serato32-binary-format).
29//!
30//! ## Example
31//!
32//! |                  | Hex           | Binary
33//! | ---------------- | ------------- | ----------------------------------
34//! | 3-byte plaintext | `   00 00 cc` | `     000 0000000 0000001 1001100`
35//! | `serato32` value | `00 00 01 4c` | `00000000000000000000000101001100`
36//! |                  |
37//! | 3-byte plaintext | `   cc 88 00` | `     110 0110010 0010000 0000000`
38//! | `serato32` value | `06 32 10 00` | `00000110001100100001000000000000`
39
40use super::color::Color;
41use crate::error::Error;
42use crate::util::Res;
43use nom::number::complete::u8;
44use std::io;
45
46/// Decodes value from Serato's 32-bit custom format to 24-bit plaintext.
47///
48/// # Example
49/// ```rust
50/// use triseratops::tag::serato32::{decode, encode};
51///
52/// assert_eq!(decode(0x00, 0x00, 0x01, 0x4C), (0x00, 0x00, 0xCC));
53///
54/// let (a, b, c, d) = encode(0x00, 0x00, 0xCC);
55/// assert_eq!(decode(a, b, c, d), (0x00, 0x00, 0xCC));
56/// ```
57#[must_use]
58pub const fn decode(enc1: u8, enc2: u8, enc3: u8, enc4: u8) -> (u8, u8, u8) {
59    let dec3: u8 = (enc4 & 0x7F) | ((enc3 & 0x01) << 7);
60    let dec2: u8 = ((enc3 & 0x7F) >> 1) | ((enc2 & 0x03) << 6);
61    let dec1: u8 = ((enc2 & 0x7F) >> 2) | ((enc1 & 0x07) << 5);
62    (dec1, dec2, dec3)
63}
64
65/// Encodes 3-byte value to to Serato's 32-bit custom format.
66///
67/// # Example
68/// ```rust
69/// use triseratops::tag::serato32::{decode, encode};
70///
71/// assert_eq!(encode(0x00, 0x00, 0xCC), (0x00, 0x00, 0x01, 0x4C));
72///
73/// let (x, y, z) = decode(0x00, 0x00, 0x01, 0x4C);
74/// assert_eq!(encode(x, y, z), (0x00, 0x00, 0x01, 0x4C));
75/// ```
76#[must_use]
77pub const fn encode(dec1: u8, dec2: u8, dec3: u8) -> (u8, u8, u8, u8) {
78    let enc4: u8 = dec3 & 0x7F;
79    let enc3: u8 = ((dec3 >> 7) | (dec2 << 1)) & 0x7F;
80    let enc2: u8 = ((dec2 >> 6) | (dec1 << 2)) & 0x7F;
81    let enc1: u8 = dec1 >> 5;
82    (enc1, enc2, enc3, enc4)
83}
84
85/// Returns a 3-byte tuple decoded from the first 4 input bytes.
86///
87/// # Example
88/// ```
89/// use triseratops::tag::serato32::take;
90/// use nom::Err;
91/// use nom::error::{Error, ErrorKind};
92///
93/// assert_eq!(take(&[0x00, 0x00, 0x01, 0x4C]), Ok((&[][..], (0x00, 0x00, 0xCC))));
94/// assert_eq!(take(&[0x00, 0x00, 0x01, 0x4C, 0x7F]), Ok((&[0x07F][..], (0x00, 0x00, 0xCC))));
95/// assert!(take(&[0x00, 0x00, 0x01]).is_err());
96/// ```
97pub fn take(input: &[u8]) -> Res<&[u8], (u8, u8, u8)> {
98    let (input, bytes) = nom::bytes::complete::take(4usize)(input)?;
99    let (bytes, byte1) = u8(bytes)?;
100    let (bytes, byte2) = u8(bytes)?;
101    let (bytes, byte3) = u8(bytes)?;
102    let (_, byte4) = nom::combinator::all_consuming(u8)(bytes)?;
103    let value = decode(byte1, byte2, byte3, byte4);
104    Ok((input, value))
105}
106
107pub fn write(writer: &mut impl io::Write, data: (u8, u8, u8)) -> Result<usize, Error> {
108    let (in1, in2, in3) = data;
109    let (byte1, byte2, byte3, byte4) = encode(in1, in2, in3);
110    Ok(writer.write(&[byte1, byte2, byte3, byte4])?)
111}
112
113/// Returns a `Color` decoded from the first 4 input bytes.
114///
115/// # Example
116/// ```
117/// use triseratops::tag::color::Color;
118/// use triseratops::tag::serato32::take_color;
119/// use nom::Err;
120/// use nom::error::{Error, ErrorKind};
121///
122/// assert_eq!(take_color(&[0x00, 0x00, 0x01, 0x4C]), Ok((&[][..], Color { red: 0x00, green: 0x00, blue: 0xCC})));
123/// assert_eq!(take_color(&[0x00, 0x00, 0x01, 0x4C, 0x7F]), Ok((&[0x07F][..], Color { red: 0x00, green: 0x00, blue: 0xCC})));
124/// assert!(take_color(&[0x00, 0x00, 0x01]).is_err());
125/// ```
126pub fn take_color(input: &[u8]) -> Res<&[u8], Color> {
127    let (input, (red, green, blue)) = take(input)?;
128    let color = Color { red, green, blue };
129    Ok((input, color))
130}
131
132pub fn write_color(writer: &mut impl io::Write, color: Color) -> Result<usize, Error> {
133    write(writer, (color.red, color.green, color.blue))
134}
135
136/// Returns a `u32` decoded from the first 4 input bytes.
137///
138/// The first 8 bits are always 0.
139///
140/// # Example
141/// ```
142/// use triseratops::tag::serato32::take_u32;
143/// use nom::Err;
144/// use nom::error::{Error, ErrorKind};
145///
146/// assert_eq!(take_u32(&[0x00, 0x00, 0x01, 0x4C]), Ok((&[][..], 0x0000CC)));
147/// assert_eq!(take_u32(&[0x00, 0x00, 0x01, 0x4C, 0x7F]), Ok((&[0x07F][..], 0x0000CC)));
148/// assert!(take_u32(&[0x00, 0x00, 0x01]).is_err());
149/// ```
150pub fn take_u32(input: &[u8]) -> Res<&[u8], u32> {
151    let (input, (a, b, c)) = take(input)?;
152    let value = (a as u32) << 16 | (b as u32) << 8 | c as u32;
153    Ok((input, value))
154}
155
156pub fn write_u32(writer: &mut impl io::Write, value: u32) -> Result<usize, Error> {
157    let byte1 = ((value >> 16) & 0xFF) as u8;
158    let byte2 = ((value >> 8) & 0xFF) as u8;
159    let byte3 = (value & 0xFF) as u8;
160    write(writer, (byte1, byte2, byte3))
161}