Replace metadata.BPM and metadata.offset with default/fallback + per-chart timing info objects that support bpm changes, Fixes #7
Flip the sign of the offset, Fixes #1 Allow data.<chart>.level to be a decimal number Relax the requirements for some properties in charts and the root object Rename some internal definitions in the schema Sort test cases by file name
This commit is contained in:
parent
514c01dc3f
commit
5c1540fcdc
130
schema.json
130
schema.json
@ -11,6 +11,7 @@
|
||||
"metadata": {
|
||||
"description": "Contains information that applies to the whole set of charts",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"title": {
|
||||
"description": "The title of the song",
|
||||
@ -28,14 +29,6 @@
|
||||
"description": "Path to the album cover, relative to the memon file",
|
||||
"type": "string"
|
||||
},
|
||||
"BPM": {
|
||||
"description": "Song tempo in Beats per Minute",
|
||||
"$ref": "#/$defs/strictlyPositiveDecimal"
|
||||
},
|
||||
"offset": {
|
||||
"description": "In seconds, opposite of the time position of the first beat in the music file",
|
||||
"$ref": "#/$defs/decimal"
|
||||
},
|
||||
"preview": {
|
||||
"description": "Describes the part of the music file that's to be played on loop when previewing this song",
|
||||
"oneOf": [{
|
||||
@ -59,24 +52,31 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["title", "artist", "BPM", "offset"]
|
||||
}
|
||||
},
|
||||
"timing": {
|
||||
"description": "default timing, applies by default to every chart in the file",
|
||||
"$ref": "#/$defs/timingObject"
|
||||
},
|
||||
"data": {
|
||||
"description": "Charts with difficulty names used as keys",
|
||||
"description": "Mapping that associates difficulty names to charts",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"description": "A chart",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"level": {
|
||||
"description": "Level rating of the chart, typically goes from 0 to 10 in jubeat",
|
||||
"type": "integer"
|
||||
"description": "Level rating of the chart, typically goes from 1 to 10.9 in jubeat",
|
||||
"$ref": "#/$defs/positiveDecimal"
|
||||
},
|
||||
"resolution": {
|
||||
"description": "Tempo resolution, number of \"ticks\" in a beat",
|
||||
"description": "Number of ticks in a beat for the notes",
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0
|
||||
"minimum": 1
|
||||
},
|
||||
"timing": {
|
||||
"description": "Chart-specific timing to be used instead of the default timing info",
|
||||
"$ref": "#/$defs/timingObject"
|
||||
},
|
||||
"notes": {
|
||||
"description": "The array of notes",
|
||||
@ -92,28 +92,12 @@
|
||||
"maximum": 15
|
||||
},
|
||||
"t": {
|
||||
"description": "Note time",
|
||||
"oneOf": [{
|
||||
"description": "Time measured in \"ticks\" as specified with the resolution",
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/timeFraction"
|
||||
}
|
||||
]
|
||||
"description": "Note time, either in ticks or as a standalone fraction",
|
||||
"$ref": "#/$defs/timeInBeats"
|
||||
},
|
||||
"l": {
|
||||
"description": "Long note duration",
|
||||
"oneOf": [{
|
||||
"description": "Long Note Lenght in ticks",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/nonZeroTimeFraction"
|
||||
}
|
||||
]
|
||||
"description": "Long note duration, either in ticks or as a standalone fraction",
|
||||
"$ref": "#/$defs/nonZeroTimeInBeats"
|
||||
},
|
||||
"p": {
|
||||
"description": "Tail starting position, relative to note position, counting from 0 to 11 clockwise and expanding out starting one square above the note",
|
||||
@ -131,14 +115,38 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["level", "resolution", "notes"]
|
||||
"required": ["notes"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["version", "metadata", "data"],
|
||||
"required": ["version", "data"],
|
||||
"$defs": {
|
||||
"timeFraction": {
|
||||
"description": "Time represented as a fraction",
|
||||
"timeInBeats": {
|
||||
"description": "Time measured as a fraction of beats, either in ticks or as a standalone fraction",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/positiveTimeFraction"
|
||||
}
|
||||
]
|
||||
},
|
||||
"nonZeroTimeInBeats": {
|
||||
"description": "Strictly positive time measured as a fraction of beats, either in ticks or as a standalone fraction",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/strictlyPositiveTimeFraction"
|
||||
}
|
||||
]
|
||||
},
|
||||
"positiveTimeFraction": {
|
||||
"description": "Time in beats represented as a fraction",
|
||||
"type": "array",
|
||||
"minItems": 3,
|
||||
"maxItems": 3,
|
||||
@ -159,10 +167,10 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"nonZeroTimeFraction": {
|
||||
"description": "Non-zero time represented as a fraction",
|
||||
"strictlyPositiveTimeFraction": {
|
||||
"description": "Non-zero time in beats represented as a fraction",
|
||||
"allOf": [{
|
||||
"$ref": "#/$defs/timeFraction"
|
||||
"$ref": "#/$defs/positiveTimeFraction"
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
@ -190,7 +198,7 @@
|
||||
]
|
||||
},
|
||||
"positiveDecimal": {
|
||||
"description": "a decimal number as either a number literal or a string",
|
||||
"description": "a positive decimal number as either a number literal or a string",
|
||||
"oneOf": [{
|
||||
"type": "number"
|
||||
},
|
||||
@ -204,13 +212,47 @@
|
||||
"description": "a strictly positive decimal number as either a number literal or a string",
|
||||
"oneOf": [{
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0
|
||||
"minimum": 1
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^(0\\.\\d*[1-9]\\d*|\\d*[1-9]\\d*(\\.\\d+)?)$"
|
||||
}
|
||||
]
|
||||
},
|
||||
"timingObject": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"offset": {
|
||||
"description": "In seconds, time at which the first beat occurs in the music file",
|
||||
"$ref": "#/$defs/decimal"
|
||||
},
|
||||
"resolution": {
|
||||
"description": "Number of ticks in a beat for the bpm events",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"bpms": {
|
||||
"description": "Array of BPM events",
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"description": "A single BPM event",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"beat": {
|
||||
"description": "Time at which the bpm changes",
|
||||
"$ref": "#/$defs/timeInBeats"
|
||||
},
|
||||
"bpm": {
|
||||
"description": "Tempo measured in Beats Per Minute",
|
||||
"$ref": "#/$defs/strictlyPositiveDecimal"
|
||||
}
|
||||
},
|
||||
"required": ["beat", "bpm"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -32,10 +32,10 @@ class ExamplesFolder(pytest.Collector):
|
||||
class ExampleSubfolder(pytest.Collector):
|
||||
def collect(self):
|
||||
path = Path(self.fspath)
|
||||
for file in (path / "pass").glob("*.json"):
|
||||
for file in sorted((path / "pass").glob("*.json")):
|
||||
yield ValidExample.from_parent(self, name=file.stem, path=file)
|
||||
|
||||
for file in (path / "fail").glob("*.json"):
|
||||
for file in sorted((path / "fail").glob("*.json")):
|
||||
yield InvalidExample.from_parent(self, name=file.stem, path=file)
|
||||
|
||||
|
||||
|
@ -1,10 +1,4 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -1,10 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"song title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0,
|
||||
"preview": []
|
||||
},
|
||||
"data": {}
|
||||
|
@ -1,10 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"song title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0,
|
||||
"preview": {
|
||||
"position": 0,
|
||||
"duration": 0
|
||||
|
@ -1,10 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"song title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0,
|
||||
"preview": null
|
||||
},
|
||||
"data": {}
|
||||
|
@ -1,10 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"song title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0,
|
||||
"preview": 3
|
||||
},
|
||||
"data": {}
|
||||
|
@ -1,10 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0,
|
||||
"preview": {
|
||||
"start": 0,
|
||||
"duration": 1
|
||||
|
@ -1,10 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0,
|
||||
"preview": "blablabla"
|
||||
},
|
||||
"data": {}
|
||||
|
@ -1,11 +1,5 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0
|
||||
},
|
||||
"data": {
|
||||
"BSC": {
|
||||
"level": 0,
|
||||
|
@ -1,11 +1,5 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0
|
||||
},
|
||||
"data": {
|
||||
"BSC": {
|
||||
"level": 0,
|
||||
|
@ -1,11 +1,5 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0
|
||||
},
|
||||
"data": {
|
||||
"BSC": {
|
||||
"level": 0,
|
||||
|
@ -1,11 +1,5 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0
|
||||
},
|
||||
"data": {
|
||||
"BSC": {
|
||||
"level": 0,
|
||||
|
@ -1,11 +1,5 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0
|
||||
},
|
||||
"data": {
|
||||
"BSC": {
|
||||
"level": 0,
|
||||
|
@ -1,11 +1,5 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0
|
||||
},
|
||||
"data": {
|
||||
"BSC": {
|
||||
"level": 0,
|
||||
|
@ -1,11 +1,5 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0
|
||||
},
|
||||
"data": {
|
||||
"BSC": {
|
||||
"level": 0,
|
||||
|
@ -1,11 +1,5 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0
|
||||
},
|
||||
"data": {
|
||||
"BSC": {
|
||||
"level": 0,
|
||||
|
@ -1,10 +1,12 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": -1,
|
||||
"offset": 0
|
||||
"timing": {
|
||||
"bpms": [
|
||||
{
|
||||
"beat": 0,
|
||||
"bpm": -1
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"timing": {
|
||||
"bpms": [
|
||||
{
|
||||
"beat": 0,
|
||||
"bpm": "-1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -1,10 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0,
|
||||
"preview": {
|
||||
"start": "0",
|
||||
"duration": "0.000"
|
||||
|
@ -1,10 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0,
|
||||
"preview": {
|
||||
"start": "0",
|
||||
"duration": "-10.000"
|
||||
|
@ -1,10 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0,
|
||||
"preview": {
|
||||
"start": "0",
|
||||
"duration": "0"
|
@ -1,10 +1,12 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": "0.000",
|
||||
"offset": 0
|
||||
"timing": {
|
||||
"bpms": [
|
||||
{
|
||||
"beat": "0.000",
|
||||
"bpm": -1
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 0,
|
||||
"offset": 0
|
||||
"timing": {
|
||||
"bpms": [
|
||||
{
|
||||
"beat": 0,
|
||||
"bpm": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": "0",
|
||||
"offset": 0
|
||||
"timing": {
|
||||
"bpms": [
|
||||
{
|
||||
"beat": 0,
|
||||
"bpm": "0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": "0.1",
|
||||
"offset": 0
|
||||
"timing": {
|
||||
"bpms": [
|
||||
{
|
||||
"beat": 0,
|
||||
"bpm": "0.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": "123.456",
|
||||
"offset": 0
|
||||
"timing": {
|
||||
"bpms": [
|
||||
{
|
||||
"beat": 0,
|
||||
"bpm": "123.456"
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 123.456,
|
||||
"offset": "-0.24"
|
||||
"timing": {
|
||||
"offset": "-0.24",
|
||||
"bpms": [
|
||||
{
|
||||
"beat": 0,
|
||||
"bpm": 123.456
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 123.456,
|
||||
"offset": "12.34"
|
||||
"timing": {
|
||||
"offset": "12.34",
|
||||
"bpms": [
|
||||
{
|
||||
"beat": 0,
|
||||
"bpm": 123.456
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 125,
|
||||
"offset": 0
|
||||
"timing": {
|
||||
"bpms": [
|
||||
{
|
||||
"beat": 0,
|
||||
"bpm": 125
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": "123",
|
||||
"offset": 0
|
||||
"timing": {
|
||||
"bpms": [
|
||||
{
|
||||
"beat": 0,
|
||||
"bpm": "123"
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 123.456,
|
||||
"offset": 0
|
||||
"timing": {
|
||||
"bpms": [
|
||||
{
|
||||
"beat": 0,
|
||||
"bpm": 123.456
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {}
|
||||
}
|
@ -1,10 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": 1,
|
||||
"offset": 0,
|
||||
"preview": {
|
||||
"start": "8346.9346",
|
||||
"duration": "35.936"
|
||||
|
@ -1,9 +1,7 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"metadata": {
|
||||
"title": "",
|
||||
"artist": "",
|
||||
"BPM": "-1",
|
||||
"BPM": 1,
|
||||
"offset": 0
|
||||
},
|
||||
"data": {}
|
9
tests/data/07 - bpm changes/pass/basic.json
Normal file
9
tests/data/07 - bpm changes/pass/basic.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"timing": {
|
||||
"offset": 0,
|
||||
"resolution": 1,
|
||||
"bpms": [{"beat": 0, "bpm": 1}]
|
||||
},
|
||||
"data": {}
|
||||
}
|
29
tests/data/07 - bpm changes/pass/per-chart partial.json
Normal file
29
tests/data/07 - bpm changes/pass/per-chart partial.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"timing": {
|
||||
"offset": 0,
|
||||
"resolution": 1,
|
||||
"bpms": [{"beat": 0, "bpm": 1}]
|
||||
},
|
||||
"data": {
|
||||
"BSC": {
|
||||
"timing": {
|
||||
"bpms": [{"beat": 0, "bpm": 1}]
|
||||
},
|
||||
"notes": []
|
||||
},
|
||||
"ADV": {
|
||||
"timing": {
|
||||
"bpms": [{"beat": 0, "bpm": 2}]
|
||||
},
|
||||
"notes": []
|
||||
},
|
||||
"EXT": {
|
||||
"timing": {
|
||||
"resolution": 2,
|
||||
"bpms": [{"beat": 0, "bpm": 3}]
|
||||
},
|
||||
"notes": []
|
||||
}
|
||||
}
|
||||
}
|
34
tests/data/07 - bpm changes/pass/per-chart.json
Normal file
34
tests/data/07 - bpm changes/pass/per-chart.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"timing": {
|
||||
"offset": 0,
|
||||
"resolution": 1,
|
||||
"bpms": [{"beat": 0, "bpm": 1}]
|
||||
},
|
||||
"data": {
|
||||
"BSC": {
|
||||
"timing": {
|
||||
"offset": 0,
|
||||
"resolution": 1,
|
||||
"bpms": [{"beat": 0, "bpm": 1}]
|
||||
},
|
||||
"notes": []
|
||||
},
|
||||
"ADV": {
|
||||
"timing": {
|
||||
"offset": 0,
|
||||
"resolution": 1,
|
||||
"bpms": [{"beat": 0, "bpm": 1}]
|
||||
},
|
||||
"notes": []
|
||||
},
|
||||
"EXT": {
|
||||
"timing": {
|
||||
"offset": 0,
|
||||
"resolution": 1,
|
||||
"bpms": [{"beat": 0, "bpm": 1}]
|
||||
},
|
||||
"notes": []
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user