Add docs for timing objects, Fixes #25
This commit is contained in:
parent
5c1540fcdc
commit
3df99079d4
@ -1,3 +1,3 @@
|
||||
Sphinx==3.3.1
|
||||
sphinx-rtd-theme==1.0.0
|
||||
myst-parser==0.15.2
|
||||
Sphinx~=3.3
|
||||
sphinx-rtd-theme~=1.0
|
||||
myst-parser~=0.15
|
@ -30,7 +30,6 @@ release = '1.0.0'
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autosectionlabel',
|
||||
'sphinx_rtd_theme',
|
||||
'myst_parser'
|
||||
]
|
||||
@ -68,4 +67,8 @@ html_css_files = [
|
||||
html_theme_options = {
|
||||
'logo_only': False,
|
||||
'collapse_navigation': False,
|
||||
}
|
||||
}
|
||||
|
||||
# Turn on Auto-generated header anchors so myst *actually* checks "doc.md#header" links for validity
|
||||
# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#auto-generated-header-anchors
|
||||
myst_heading_anchors = 2
|
@ -11,5 +11,5 @@ memon
|
||||
|
||||
.. toctree::
|
||||
schema
|
||||
validation
|
||||
other-things
|
||||
changelog
|
||||
|
@ -1,6 +1,8 @@
|
||||
# Validation
|
||||
# Other things to look out for
|
||||
|
||||
This page explains a few things to look out for when reading/writing memon files.
|
||||
[JSON Schema](https://json-schema.org/) is great but by itself, a schema can't describe *everything* that makes a memon file "valid". The json schema file given in the github repository is meant to serve as a *reference* rather than a direct implementation, so some bits are left to be handled by the people adding memon compatibility into their software.
|
||||
|
||||
So, here are a few things to look out for or to keep in mind when reading or writing memon files :
|
||||
|
||||
## Notes
|
||||
|
||||
@ -25,9 +27,6 @@ All the numbers are within the intervals defined in the schema, however this wou
|
||||
|
||||
Notice the tail starting outside the screen.
|
||||
|
||||
Parsers should **reject** such notes
|
||||
|
||||
|
||||
### Uniqueness
|
||||
|
||||
The schema itself does not prevent the following cases from happening in the array of notes that make up the chart :
|
||||
@ -48,9 +47,6 @@ The schema itself does not prevent the following cases from happening in the arr
|
||||
]
|
||||
```
|
||||
|
||||
Parsers should only keep one note for each `(n, t)` couple
|
||||
|
||||
|
||||
### Hitbox Overlap
|
||||
|
||||
*(This describes **hitbox** overlap, for marker animation overlap see {ref}`marker-animation-overlap`)*
|
||||
@ -91,8 +87,6 @@ Here, two long notes overlap on the same square
|
||||
|
||||
To clarify, a long note *lasts* for the amount of ticks specified by its `L` key, this means there **cannot** be another note on the same square from `T` to `T+L`, inclusive.
|
||||
|
||||
Parsers should **reject** charts with these kinds of overlapping notes.
|
||||
|
||||
(marker-animation-overlap)=
|
||||
### Marker Animation Overlap
|
||||
|
||||
@ -103,13 +97,62 @@ Since this is mostly an implicit rule that's not strictly followed by official c
|
||||
|
||||
## Decimal values
|
||||
|
||||
If possible, non-integer values like `BPM` or `offset` should be manipulated using a [Decimal Data Type](https://en.wikipedia.org/wiki/Decimal_data_type) to preserve their original decimal representation. I think no one likes to see the BPM they defined as a clean `195.3` in the editor be stored as a messy `195.3000030517578125` in the file. If doing this is too hard with your language / library of choice, keep in mind that `1.0.0` allows strings instead :
|
||||
If possible, non-integer values like `bpm` or `offset` should be manipulated using a [Decimal Data Type](https://en.wikipedia.org/wiki/Decimal_data_type) to preserve their original decimal representation. I think no one likes to see the BPM they defined as a clean `195.3` in the editor be stored as a messy `195.3000030517578125` in the file. If doing this is too hard with your language / library of choice, keep in mind that `1.0.0` allows strings instead :
|
||||
|
||||
- **Valid**
|
||||
```json
|
||||
{ "BPM" : 195.3 }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "bpm" : 195.3 }
|
||||
```
|
||||
|
||||
- **Valid since 1.0.0**
|
||||
```json
|
||||
{ "BPM": "195.3" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "bpm": "195.3" }
|
||||
```
|
||||
|
||||
## Multiple timing objects
|
||||
|
||||
memon version 1.0.0 introduced [timing objects](schema.md#timing) in two different places in the file, either at the root or in a chart.
|
||||
|
||||
All the keys in a timing object are optional, this is to allow for a chart to only redefine what is different from the default timing object.
|
||||
|
||||
In other words the timing object at the root acts as a fallback.
|
||||
|
||||
In general, when deciding what timing information applies to a given chart, timing objects should be searched in that order *for every key* :
|
||||
|
||||
- Chart-specific timing object
|
||||
- Root timing object
|
||||
- Default values defined by the schema
|
||||
|
||||
For instance in the following file :
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"timing": {
|
||||
"offset": 0.84
|
||||
},
|
||||
"data": {
|
||||
"BSC": {
|
||||
"timing": {
|
||||
"bpms": [{"beat": 0, "bpm": 200}]
|
||||
},
|
||||
"notes": []
|
||||
},
|
||||
"ADV": {
|
||||
"timing": {
|
||||
"offset": 0.31,
|
||||
"bpms": [{"beat": 0, "bpm": 100}]
|
||||
},
|
||||
"notes": []
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Neither `BSC` nor `ADV` define `resolution` in their timing info so the implicit default is used instead for both charts.
|
||||
|
||||
`BSC` defines no chart-specific `offset` so it uses the value `0.84` from the timing object at the root of the file instead.
|
||||
|
||||
`ADV` defines its own `offset` so it gets used instead of all the others
|
@ -8,6 +8,7 @@ This page gives a top-down view of how a memon file is structured
|
||||
{
|
||||
"version": "x.y.z",
|
||||
"metadata": {},
|
||||
"timing": {},
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
@ -19,14 +20,17 @@ It's a json object with the following keys :
|
||||
- string, required
|
||||
- Indicates the schema version this memon file uses, follows [semver](https://semver.org/). If a memon file does not have this key it's probably following the pre-semver format
|
||||
- **metadata**
|
||||
- object, required
|
||||
- Contains the {ref}`metadata object <metadata>`
|
||||
- object, optional
|
||||
- Contains the [metadata object](#metadata)
|
||||
- **timing**
|
||||
- object, optional
|
||||
- Contains the default / fallback [timing object](#timing)
|
||||
- See [](other-things.md#multiple-timing-objects) for more details on the behavior this should have
|
||||
- **data**
|
||||
- object, required
|
||||
- Contains the {ref}`data object <data>`
|
||||
- Contains the [data object](#data)
|
||||
|
||||
|
||||
(metadata)=
|
||||
## Metadata
|
||||
|
||||
```json
|
||||
@ -35,38 +39,27 @@ It's a json object with the following keys :
|
||||
"artist": "",
|
||||
"audio": "",
|
||||
"jacket": "",
|
||||
"BPM": 120.0,
|
||||
"offset": 0.0,
|
||||
"preview": {},
|
||||
}
|
||||
```
|
||||
Contains information that applies to the whole set of charts
|
||||
|
||||
- **title**
|
||||
- string, required
|
||||
- string, optional
|
||||
- Song title
|
||||
- **artist**
|
||||
- string, required
|
||||
- string, optional
|
||||
- **audio**
|
||||
- string, optional
|
||||
- Path to the music file, *relative* to the memon file.
|
||||
- **jacket**
|
||||
- string, optional
|
||||
- Relative path to the jacket / album cover / album art to be shown in music select for example. usually a square image.
|
||||
- **BPM**
|
||||
- number or string, required
|
||||
- Song tempo in Beats per Minute.
|
||||
- Striclty positive
|
||||
- Strings allowed for easier decimal representation preservation
|
||||
- **offset**
|
||||
- number or string, required
|
||||
- In seconds, opposite of the time position of the first beat in the music file. For instance, if the first beat occurs at 0.15 seconds in the audio file, `offset̀` should be -0.15
|
||||
- Strings allowed for easier decimal representation preservation
|
||||
- **preview**
|
||||
- object or string, optional
|
||||
- Contains either a {ref}`preview object <preview>` or a path to a bms-style preview file
|
||||
- Contains either a [preview object](#preview) or a path to a bms-style preview file
|
||||
|
||||
|
||||
(preview)=
|
||||
## Preview
|
||||
|
||||
```json
|
||||
@ -89,7 +82,54 @@ Describes the part of the music file that's meant to be played on loop when prev
|
||||
- Strictly positive
|
||||
- Strings allowed for easier decimal representation preservation
|
||||
|
||||
(data)=
|
||||
|
||||
## Timing
|
||||
|
||||
```json
|
||||
{
|
||||
"offset": 0.0,
|
||||
"resolution": 1,
|
||||
"bpms": []
|
||||
}
|
||||
```
|
||||
|
||||
Describes the relationship between seconds in the audio file and symbolic time (time measured in beats)
|
||||
|
||||
- **offset**
|
||||
- number or string, optional
|
||||
- In seconds, time at which the first beat occurs in the music file.
|
||||
|
||||
For instance, if the first beat occurs at 0.15 seconds in the audio file, the offset should be the number literal `0.15`, or the string `"0.15"` if the tools used can't keep a clean decimal representation when using json number literals.
|
||||
- **resolution**
|
||||
- integer, optional
|
||||
- Greater than 0, always an integer
|
||||
- Number of ticks in a beat for the bpm events defined in this timing object, if some bpm events define a beat using a single integer, this is the implicit fraction denominator to use to convert the integer number of ticks to a fractional number of beats.
|
||||
- **bpms**
|
||||
- array, optional
|
||||
- Array of [BPM events](#bpm)
|
||||
|
||||
|
||||
## BPM
|
||||
|
||||
```json
|
||||
{
|
||||
"beat": 0,
|
||||
"bpm": 120
|
||||
}
|
||||
```
|
||||
|
||||
Defines a change in tempo measured in beats per minutes happening at a specific symbolic time (measured in beats)
|
||||
|
||||
- **beat**
|
||||
- [symbolic time](#symbolic-time), required
|
||||
- Beat at which the tempo changes
|
||||
- **bpm**
|
||||
- number or string
|
||||
- Song tempo at the given beat, in Beats per Minute.
|
||||
- Striclty positive
|
||||
- Strings allowed for easier decimal representation preservation
|
||||
|
||||
|
||||
## Data
|
||||
|
||||
```json
|
||||
@ -101,7 +141,7 @@ Describes the part of the music file that's meant to be played on loop when prev
|
||||
}
|
||||
```
|
||||
|
||||
The data object maps difficulty names to {ref}`chart objects <chart>`.
|
||||
The data object maps difficulty names to [chart objects](#chart).
|
||||
|
||||
Keys in this object are not fixed, they can be any string.
|
||||
|
||||
@ -111,29 +151,34 @@ When sorting, difficulties may be presented in that order :
|
||||
|
||||
BSC ➔ ADV ➔ EXT ➔ (everything else in alphabetical order)
|
||||
|
||||
(chart)=
|
||||
|
||||
## Chart
|
||||
|
||||
```json
|
||||
{
|
||||
"level": 10.3,
|
||||
"resolution": 240,
|
||||
"notes" : []
|
||||
"timing": {},
|
||||
"notes": []
|
||||
}
|
||||
```
|
||||
|
||||
- **level**
|
||||
- number, required
|
||||
- Chart level, can be an integer or a decimal value
|
||||
- number or string, optional
|
||||
- Chart level, can be an integer or a decimal value, or even a string holding a decimal number
|
||||
- **resolution**
|
||||
- number, required
|
||||
- integer, optional
|
||||
- Greater than 0, always an integer
|
||||
- Number of ticks in a beat, denominator of all beat fractions. Usually 240
|
||||
- Number of ticks in a beat for all the notes in the chart, see [](#symbolic-time)
|
||||
- **timing**
|
||||
- [Timing object](#timing), optional
|
||||
- Chart-specific timing information
|
||||
- See [](other-things.md#multiple-timing-objects) for more details on the behavior this should have
|
||||
- **notes**
|
||||
- array, required
|
||||
- Array of {ref}`tap notes <tap-note>` and {ref}`long notes <long-note>` that make up the chart
|
||||
- Array of [tap notes](#tap-note) and [long notes](#long-note) that make up the chart
|
||||
|
||||
|
||||
(tap-note)=
|
||||
## Tap Note
|
||||
|
||||
```json
|
||||
@ -145,15 +190,6 @@ When sorting, difficulties may be presented in that order :
|
||||
|
||||
A classic note.
|
||||
|
||||
**t** can also be defined this way :
|
||||
|
||||
```json
|
||||
{
|
||||
"n": 0,
|
||||
"t": [1, 0, 1]
|
||||
}
|
||||
```
|
||||
|
||||
- **n**
|
||||
- number, required
|
||||
- Integer between 0 and 15 inclusive
|
||||
@ -165,28 +201,10 @@ A classic note.
|
||||
12 13 14 15
|
||||
```
|
||||
- **t**
|
||||
- required
|
||||
- as a number :
|
||||
- Integer greater or equal to 0
|
||||
- Note time in ticks.
|
||||
- [Symbolic Time](#symbolic-time), required
|
||||
- Beat at which the note occurs
|
||||
|
||||
Ticks are fractions of the beat.
|
||||
The resolution defines how many ticks are in a beat for a given chart.
|
||||
In other words if the resolution is 420, a tick lasts for 1/420th oLike for the BPM, sf a beat
|
||||
|
||||
For more info about measuring time in ticks, see [bmson's docs](https://bmson-spec.readthedocs.io/en/master/doc/index.html#terminologies) (their docs refers to ticks as *pulses*).
|
||||
- as an array :
|
||||
- The array MUST have length 3
|
||||
- `t[0]` and `t[1]` are integers greater or equal to 0
|
||||
- `t[2]` is an integer greater than 0
|
||||
- Reprensents the note time in beats as a fraction, the value can be retrieved by the following computation :
|
||||
```
|
||||
t[0] + (t[1] / t[2])
|
||||
```
|
||||
|
||||
For instance `[1, 2, 3]` means the note happens 2/3 of a beat after beat 1
|
||||
|
||||
(long-note)=
|
||||
## Long Note
|
||||
|
||||
```json
|
||||
@ -200,28 +218,13 @@ A classic note.
|
||||
|
||||
A classic long note, with a tail
|
||||
|
||||
**`l`** can also take the same 3-int tuple form as **`t`**, except it has to represent a non-zero duration :
|
||||
|
||||
```json
|
||||
{
|
||||
"n": 0,
|
||||
"t": 3600,
|
||||
"l": [0, 1, 10],
|
||||
"p": 5
|
||||
}
|
||||
```
|
||||
|
||||
**n** and **t** are the same as in a {ref}`tap note <tap-note>`
|
||||
|
||||
- **n** : same as in a [tap note](#tap-note)
|
||||
- **t** : same as in a [tap note](#tap-note)
|
||||
- **l**
|
||||
- required
|
||||
- as a number :
|
||||
- Integer greater than 0
|
||||
- Long note duration ("l" as in length ?!), in ticks
|
||||
- as an array:
|
||||
- same constraints as the array-variant of **t**
|
||||
- `t[0]` and `t[1]` cannot both be zero at the same time
|
||||
- Long note duration as a beat fraction
|
||||
- [Symbolic Time](#symbolic-time), required
|
||||
- If a number, strictly positive
|
||||
- If an array, `l[0]` and `l[1]` cannot both be zero at the same time
|
||||
- Long note duration in ticks or beats
|
||||
- **p**
|
||||
- number, required
|
||||
- Integer between 0 and 11 inclusive
|
||||
@ -249,4 +252,52 @@ A classic long note, with a tail
|
||||
|
|
||||
|
|
||||
△
|
||||
```
|
||||
```
|
||||
|
||||
|
||||
## Symbolic Time
|
||||
|
||||
Either an integer :
|
||||
|
||||
```json
|
||||
0
|
||||
```
|
||||
|
||||
or an array :
|
||||
|
||||
```json
|
||||
[0, 2, 3]
|
||||
```
|
||||
|
||||
Represents a time point (or duration) measured in *beats*
|
||||
|
||||
|
||||
### As a number
|
||||
|
||||
- integer greater or equal to 0
|
||||
- Time measured in *ticks* :
|
||||
|
||||
Ticks are fractions of the beat.
|
||||
Using ticks implies a *resolution* is defined somewhere else in the file.
|
||||
The resolution defines how many ticks are in a beat.
|
||||
In other words if the resolution is 240, a tick lasts for 1/240th of a beat.
|
||||
|
||||
For more info about measuring time in ticks, see [bmson's docs](https://bmson-spec.readthedocs.io/en/master/doc/index.html#terminologies) (their docs refers to ticks as *pulses*).
|
||||
|
||||
|
||||
### As an array
|
||||
|
||||
- The array **MUST** have length 3
|
||||
- The first and second elements are integers greater or equal to 0
|
||||
- The third element is an integer greater than 0
|
||||
- The array reprensents a time in beats as a [mixed number](https://en.wikipedia.org/wiki/Fraction#Mixed_numbers)
|
||||
|
||||
If `a` is the array in question and we use the bracket notation for array access, the value represented by the array is the following :
|
||||
|
||||
```
|
||||
a[0] + (a[1] / a[2])
|
||||
```
|
||||
|
||||
For instance `[1, 2, 3]` represents 1 + 2/3, `[0, 1, 20]` represents 0 + 1/20.
|
||||
|
||||
As it currently is, the schema allows for improper fractions (0 + 5/1) and non-reduced fractions (0 + 2/4)
|
Loading…
Reference in New Issue
Block a user