parent
06d23501ca
commit
514c01dc3f
@ -54,11 +54,14 @@ Contains information that applies to the whole set of charts
|
|||||||
- string, optional
|
- string, optional
|
||||||
- Relative path to the jacket / album cover / album art to be shown in music select for example. usually a square image.
|
- Relative path to the jacket / album cover / album art to be shown in music select for example. usually a square image.
|
||||||
- **BPM**
|
- **BPM**
|
||||||
- number, required
|
- number or string, required
|
||||||
- Song tempo in Beats per Minute.
|
- Song tempo in Beats per Minute.
|
||||||
|
- Striclty positive
|
||||||
|
- Strings allowed for easier decimal representation preservation
|
||||||
- **offset**
|
- **offset**
|
||||||
- number, required
|
- 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
|
- 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**
|
- **preview**
|
||||||
- object or string, optional
|
- object or string, optional
|
||||||
- Contains either a {ref}`preview object <preview>` or a path to a bms-style preview file
|
- Contains either a {ref}`preview object <preview>` or a path to a bms-style preview file
|
||||||
@ -76,11 +79,15 @@ Contains information that applies to the whole set of charts
|
|||||||
Describes the part of the music file that's meant to be played on loop when previewing this song at the music select screen
|
Describes the part of the music file that's meant to be played on loop when previewing this song at the music select screen
|
||||||
|
|
||||||
- **start**
|
- **start**
|
||||||
- number, required
|
- number or string, required
|
||||||
- In seconds, start of the loop
|
- In seconds, start of the loop
|
||||||
|
- Positive
|
||||||
|
- Strings allowed for easier decimal representation preservation
|
||||||
- **duration**
|
- **duration**
|
||||||
- number, required
|
- number or string, required
|
||||||
- In seconds, duration of the loop
|
- In seconds, duration of the loop
|
||||||
|
- Strictly positive
|
||||||
|
- Strings allowed for easier decimal representation preservation
|
||||||
|
|
||||||
(data)=
|
(data)=
|
||||||
## Data
|
## Data
|
||||||
@ -165,7 +172,7 @@ A classic note.
|
|||||||
|
|
||||||
Ticks are fractions of the beat.
|
Ticks are fractions of the beat.
|
||||||
The resolution defines how many ticks are in a beat for a given chart.
|
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 of a beat
|
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*).
|
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 :
|
- as an array :
|
||||||
|
@ -103,19 +103,13 @@ Since this is mostly an implicit rule that's not strictly followed by official c
|
|||||||
|
|
||||||
## Decimal values
|
## 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 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 :
|
||||||
|
|
||||||
Be careful that the serialized values in the resulting file must still be *number litterals*, not strings.
|
- **Valid**
|
||||||
|
|
||||||
- **Good**
|
|
||||||
```json
|
```json
|
||||||
{ "BPM" : 195.3 }
|
{ "BPM" : 195.3 }
|
||||||
```
|
```
|
||||||
- **Bad**
|
- **Valid since 1.0.0**
|
||||||
```json
|
```json
|
||||||
{ "BPM": "195.3" }
|
{ "BPM": "195.3" }
|
||||||
```
|
```
|
||||||
|
|
||||||
This is not that easy. Python's standard module `json`, for instance, allows *deserializing* (reading) numbers in a json file as `decimal.Decimal` instances, but has no easy way of *serializing* (writing) `decimal.Decimal` instances as a json number litteral that respects the original representation.
|
|
||||||
|
|
||||||
A possible solution is to use the [simplejson](https://pypi.org/project/simplejson/) module as a near drop-in replacement for `json` and use the `use_decimal` options
|
|
45
schema.json
45
schema.json
@ -30,12 +30,11 @@
|
|||||||
},
|
},
|
||||||
"BPM": {
|
"BPM": {
|
||||||
"description": "Song tempo in Beats per Minute",
|
"description": "Song tempo in Beats per Minute",
|
||||||
"type": "number",
|
"$ref": "#/$defs/strictlyPositiveDecimal"
|
||||||
"exclusiveMinimum": 0
|
|
||||||
},
|
},
|
||||||
"offset": {
|
"offset": {
|
||||||
"description": "In seconds, opposite of the time position of the first beat in the music file",
|
"description": "In seconds, opposite of the time position of the first beat in the music file",
|
||||||
"type": "number"
|
"$ref": "#/$defs/decimal"
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"description": "Describes the part of the music file that's to be played on loop when previewing this song",
|
"description": "Describes the part of the music file that's to be played on loop when previewing this song",
|
||||||
@ -45,13 +44,11 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"start": {
|
"start": {
|
||||||
"description": "In seconds, Time at which preview should start",
|
"description": "In seconds, Time at which preview should start",
|
||||||
"type": "number",
|
"$ref": "#/$defs/positiveDecimal"
|
||||||
"minimum": 0
|
|
||||||
},
|
},
|
||||||
"duration": {
|
"duration": {
|
||||||
"description": "In seconds, for how long should the preview be played past the starting point",
|
"description": "In seconds, for how long should the preview be played past the starting point",
|
||||||
"type": "number",
|
"$ref": "#/$defs/strictlyPositiveDecimal"
|
||||||
"minimum": 0
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["start", "duration"]
|
"required": ["start", "duration"]
|
||||||
@ -180,6 +177,40 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"decimal": {
|
||||||
|
"description": "a decimal number as either a number literal or a string",
|
||||||
|
"oneOf": [{
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^-?\\d+(\\.\\d+)?$"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"positiveDecimal": {
|
||||||
|
"description": "a decimal number as either a number literal or a string",
|
||||||
|
"oneOf": [{
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^\\d+(\\.\\d+)?$"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"strictlyPositiveDecimal": {
|
||||||
|
"description": "a strictly positive decimal number as either a number literal or a string",
|
||||||
|
"oneOf": [{
|
||||||
|
"type": "number",
|
||||||
|
"exclusiveMinimum": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(0\\.\\d*[1-9]\\d*|\\d*[1-9]\\d*(\\.\\d+)?)$"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,7 @@
|
|||||||
"offset": 0,
|
"offset": 0,
|
||||||
"preview": {
|
"preview": {
|
||||||
"start": 0,
|
"start": 0,
|
||||||
"duration": 0
|
"duration": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"data": {}
|
"data": {}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": -1,
|
||||||
|
"offset": 0
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": "-1",
|
||||||
|
"offset": 0
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": 1,
|
||||||
|
"offset": 0,
|
||||||
|
"preview": {
|
||||||
|
"start": "0",
|
||||||
|
"duration": "0.000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": 1,
|
||||||
|
"offset": 0,
|
||||||
|
"preview": {
|
||||||
|
"start": "0",
|
||||||
|
"duration": "-10.000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": 1,
|
||||||
|
"offset": 0,
|
||||||
|
"preview": {
|
||||||
|
"start": "0",
|
||||||
|
"duration": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": "0.000",
|
||||||
|
"offset": 0
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
10
tests/data/06 - decimals as string/fail/zero BPM number.json
Normal file
10
tests/data/06 - decimals as string/fail/zero BPM number.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": 0,
|
||||||
|
"offset": 0
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
10
tests/data/06 - decimals as string/fail/zero BPM string.json
Normal file
10
tests/data/06 - decimals as string/fail/zero BPM string.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": "0",
|
||||||
|
"offset": 0
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": "0.1",
|
||||||
|
"offset": 0
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": "123.456",
|
||||||
|
"offset": 0
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": 123.456,
|
||||||
|
"offset": "-0.24"
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": 123.456,
|
||||||
|
"offset": "12.34"
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
10
tests/data/06 - decimals as string/pass/integer BPM.json
Normal file
10
tests/data/06 - decimals as string/pass/integer BPM.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": 125,
|
||||||
|
"offset": 0
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": "123",
|
||||||
|
"offset": 0
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
10
tests/data/06 - decimals as string/pass/number BPM.json
Normal file
10
tests/data/06 - decimals as string/pass/number BPM.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": 123.456,
|
||||||
|
"offset": 0
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
14
tests/data/06 - decimals as string/pass/preview object.json
Normal file
14
tests/data/06 - decimals as string/pass/preview object.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"metadata": {
|
||||||
|
"title": "",
|
||||||
|
"artist": "",
|
||||||
|
"BPM": 1,
|
||||||
|
"offset": 0,
|
||||||
|
"preview": {
|
||||||
|
"start": "8346.9346",
|
||||||
|
"duration": "35.936"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
}
|
@ -7,7 +7,7 @@ from jschon.jsonschema import Scope
|
|||||||
from jschon import create_catalog, JSONSchema, JSON
|
from jschon import create_catalog, JSONSchema, JSON
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
_catalog = create_catalog('2020-12')
|
_catalog = create_catalog("2020-12")
|
||||||
SCHEMA = JSONSchema.loadf("schema.json", catalog=_catalog)
|
SCHEMA = JSONSchema.loadf("schema.json", catalog=_catalog)
|
||||||
|
|
||||||
def prettify_path(json_path: str) -> str:
|
def prettify_path(json_path: str) -> str:
|
||||||
|
Loading…
Reference in New Issue
Block a user