Closed m4saka closed 4 years ago
Does this mean an entirely new format is up for consideration? Or would "upgrades" to KSH still be preferable?
If something entirely new is desired, I've always thought a binary format to be great. Human readable is great, but if the editor is fully-featured there's little use for it other than "debugging" of sorts.
If a human-readable form is still desired, being more explicit with chart information and separating the lanes would do wonders still.
My attempt at a binary format kept each lane of the chart in a separate array with positional + length data attached. A position could either be a fractional value, Measure + fraction of a measure, or just a double representing the same thing. I've had a terrible time using fractions in my code and fell back to floating point for both implementation and storage because of it, but fractional positions more closely reflect the current KSH format (since each line is a single 1/n of each measure.) Length being 0 for chips and the same data type as the positional data for length makes sense, or a flag telling if the object is a chip or not is something I've also done for space saving on disk.
Information like spins and fx parameters are probably best attached to the objects themselves, but I've also had a separate Events "Lane" (or multiple event lanes, one for each important kind) which changes global state instead of being a single-use parameter. The events lanes are good for camera controls, for example, so doing it for most other things wasn't a stretch. An Event Land for slam spins would allow those to play anywhere, as well, not just on a slam hit, as another example.
I've always hated working with laser segments in KSH files, but slams have been the most frustrating thing to know if I did correctly. A slam should definitely be an instantaneous self-described object separate from the linear segments. Then there's no ambiguity and no lower-bound on laser segment length (like the 1/32nd division is.)
I'd LOVE to see as many things parameterized as possible, which would mean updating the behaviour as well though. For example, an amplitude parameter for the half spin slam effect instead of it always being a constant amount, or a "shape" and associated parameters for laser segments, allowing curves without tons of linear segments (and even maybe parameterizing the "smoothness" so people who like the low-segment curve look can tune that as well?) This all might be out of scope of the format, but is worth considering and discussing, I think, for future implementations to avoid more backwards-compat hacks.
I'm a bit dazed and can't think of other important things, and this isn't even a good write up in the first place I don't think, but that's a lot of my thoughts on KSH and my experiences using it and trying to replace it for my projects.
I'd still like to finish up my own binary format and have an implementation in C/++ soon, if that's something you're interested in maybe that can be done with your help? A more "official" implementation that could be used?
I love this stuff anyway, if you have stronger ideas for other things I'd still love to help whatever direction you take it.
Just my simple input into this, but what are your thoughts on implementing something similar to BMSon? https://bmson-spec.readthedocs.io/en/master/
In regards to beatmaps breaking between versions, I think that's just something that needs to be handled by the program itself similar to versioning an API. (The program would interpret older charts in a "compatibility" mode as to not "convert" the file for hashing purposes).
In regards to internet rankings and hashing, how much slower is hashing the whole file vs just hashing the chart game data?
@nashiora, Thanks for your advice! Creating an entirely new format is intended. I bet that would be far better than using KSH-based one.
In general, binary formats are not flexible when we need to add unpredictable new features. I didn't dig into your binary format in detail, but does it work even if BT notes also had audio effects in future or the format needed to support song lyrics subtitles? We would need "magic numbers" for each feature in binary formats, but I'd say they are no better than bad namings. We need to worry about duplicate magic numbers if original extensions are implemented in third-party clients. It is clearly true that binary formats can be loaded faster, but I believe the speed would not be so different from text formats in ordinary charts.
I think your design of the format is good. Using note arrays is nice as it will greatly reduce the cost for parsing charts, and I totally agree with your opinion about laser slams. But I don't think curving lasers should be supported in KSM (because it would not be supported in a similar arcade game SDVX).
@albshin, Using JSON is a good idea. Since there are a lot of optimized libraries for parsing JSON, the speed would not be a problem.
The bmson format is well-organized, but there are some different points between KSM and BMS:
It seems that the resolution of a measure is 1/960th instead of 1/192th in the bmson. As there are some charts that have unusual resolutions (e.g. there are 1/10th chip notes in "Azul (remix)"), using higher resolutions than 1/192th or using fractional numbers would be better. For implementation, maybe we need to use fixed resolutions (I've currently planned to use 9600 in KSM), so there will be extra costs for fractions. I think 960 is large enough for resolutions (although 9600 would be better), so using the fixed resolution 960 would be a good choice if we use a bmson-like format.
In regards to beatmaps breaking between versions, I think that's just something that needs to be handled by the program itself similar to versioning an API. (The program would interpret older charts in a "compatibility" mode as to not "convert" the file for hashing purposes).
I didn't mention about hashing problems by using different editor versions, but it would be one solution for that. In order not to make a complicated "compatibility" mode necessary, we need a good format!
In regards to internet rankings and hashing, how much slower is hashing the whole file vs just hashing the chart game data?
Actually eliminating chart lines before hashing in current KSH is not that slow and feasible enough for checking one file. However, although I didn't compare the speed, I believe hashing all ksh files in songs directory considering chart-related lines would be slow and not realistic. (It can be solved by creating an SQLite table relating the original file hashes to hashes used for Internet ranking.)
It is great to specify which items are chart-related and which items are not for both lower cost and better compatibility. For example, in the case of KSH, using "#" before not chart-related lines will work (e.g. "#level=" instead of "level=", and using as is about "t=" and "beat="). With this specifier, we won't have to worry about old versions misrecognize new parameters as chart-related ones. If it is possible to separate chart-related information and header-related or audio-related information, no costs are needed and there would be no upward compatibility problems for hashing charts.
JSON can be ambiguous if it comes with extra whitespaces or unsorted keys. Removing whitespaces and sorting keys will be required before hashing. That's also a problem that JSON has.
I think a binary format is the way to go because of the almost guaranteed superior speed and size but a well thought out text format is ok as well. I don't think expanding a well designed binary format would be much harder than expanding a text based one.
We need to worry about duplicate magic numbers if original extensions are implemented in third-party clients.
I don't think compatibility with third party modifications to the format should be necessary as they clearly went outside of the spec and thus should not expect others to be compatible with them.
But I don't think curving lasers should be supported in KSM (because it would not be supported in a similar arcade game SDVX).
I think this is a bad mindset. There is no good reason to limit what is possible to do in the format just because it is something that is currently missing in the SDVX arcade game. Especially for curves as they are something that are relatively easy to add and relatively large number of charts use curves and the current way you have to make them is very tedious work for chart makers.
As for note placement resolution I mostly agree with what you say. I think storing fractions should be considered even though the cost of them might be higher than just storing an "index" in the measure, the cost might still be worth it for what we gain. We also have to consider the interaction between a fixed division and the time signature if a fixed division is to be used, maybe you already thought about this but you didn't mention it.
@m4saka Thanks for replying to my comment. I've thought about this a bit more and want to share my thoughts again.
I've created a theoretical bmson like spec in order to address some of the points you made along with an example of extending the spec and converting the first measure of Azul(remix) to pulses. It's not perfect and it's missing some of the original spec but it's a quick rough draft.
https://gist.github.com/albshin/cf535afc3f94f7d7f7c7e3d1d9ff41cf
Now your comments.
Using JSON is a good idea. Since there are a lot of optimized libraries for parsing JSON, the speed would not be a problem.
I agree. This makes it be human readable, performant, easy to extend, and easy to read. A nice tradeoff in my opinion.
There are not so many keysounds, so it would be better if there are different arrays for different lanes.
Notes are now decoupled from SoundChannels in the modified bmson format. I've grouped notes by type but that can be easily split into different lane arrays.
We need actual time signatures instead of bar lines in bmson because KSM has beat-related audio effects (e.g. Retrigger, Gate, Wobble).
In my draft, FXes are tied to a pulse as they are mapped to FXButtons and Laser points. This let's them be tied to a beat.
It seems that the resolution of a measure is 1/960th instead of 1/192th in the bmson. As there are some charts that have unusual resolutions (e.g. there are 1/10th chip notes in "Azul (remix)"), using higher resolutions than 1/192th or using fractional numbers would be better.
So I actually took a harder look into bmson. bmson isn't actually 1/960th. It just means that it supports 960 pulses in a measure (assuming a measure is in 4/4). If a song had a 6/8 time signature, since a quarter note = 240 pulses, an eighth note = 120 pulses making the measure 720 pulses. (6*120 = 720). If we wanted to map a note to the 1/10th beat of the measure, the note would be on pulse 72 ((1/10)720), 3/10 would be 216 ((3/10)720), etc.
You can increase the granularity by increasing the resolution size of a quarter note (by default it is 240).
I've currently planned to use 1/9600 in KSM
Does this make sense realistically? Can songs be written to have notes on the 1/9600th note of a measure? Maybe I'm misunderstanding here.
Actually eliminating chart lines before hashing in current KSH is not that slow and feasible enough for checking one file. However, although I didn't compare the speed, I believe hashing all ksh files in songs directory considering chart-related lines would be slow and not realistic
After thinking about online rankings and hashing, is it something the new file spec should really consider? I think it's something out of scope for a file spec and is something best left to be implemented by whoever wants to create an online ranking system. Ideally a central database for beatmaps is created and then ranked beatmaps get frozen from changes (like osu and Beat Saber) but I don't think it's relevant to the file spec.
Regarding Drewol's comments
I think a binary format is the way to go because of the almost guaranteed superior speed and size
I agree that binary would be better in terms of performance and size but it's a lot more tedious to parse vs something like JSON. Every language has efficient parsers for JSON already. In regards to size, is it really a big issue regarding beatmaps? From my experience, a large bulk of a beatmap's size comes from the FX preview songs per difficulty rather than the chart files themselves. In my opinion, that's something that should be addressed before optimizing chart file sizes.
I think this is a bad mindset. There is no good reason to limit what is possible to do in the format just because it is something that is currently missing in the SDVX arcade game.
Agreed. There's no reason for KSH to limit itself to what the arcade can do. If you want KSH to be a faithful arcade emulation, then I understand but in my opinion, KSH should try to give an unique experience that can't be found in the arcade version (or EAC). It'll also give charters a better experience if curves were supported.
As for the curve implementation I have had in mind, it'd work like this https://www.desmos.com/calculator/dmkkhu0six but you could probably also have two curve points instead of just one but I haven't figured out the math for that yet. This implementation fills a few criteria that I want such as:
x
with the variables a,b
which makes it easy to get an arbritary point on the curve using the playback time.@m4saka As far as future expandability concerns, I've implemented a lot of the properties of objects as bit-flagged optional. Even with only 8bit flags, you have a lot of expansion room since not very many new features are likely to be added (and even then, the final flag can be specified as an extension flag instead allowing for theoretically infinite flagging of note features.) For example, something similar to how I've done in the past:
Basically, all required data is in pre-determined placed and flags more-or-less add data linearly to the end of the object. This means that, assuming no breaking format changes, updates to the way things work need only give meaning to a new flag, and since flags are intended to be processed in-order there should be no breaking issues when reading old versions where the flags are off anyway. This WILL cause issues when reading newer features on older versions, as the old version won't know how much data to skip if a flag is on. A more spacious format could account for this by instead of using flags, using a single byte ID + length of payload data, so implementations can skip ID's they don't recognize.
I'm still partial to the bitflags for compactness, but having [ OBJ_ID :: OBJ_PROPERTY_COUNT :: OBJ_POSITION :: < ARRAY OF OBJ_PROPERTY > ] is a solid option for a safer format if that's desired.
As far as Laser Curves, SDVX may not support them but that doesn't mean that the concept isn't already in use. Everyone uses small linear segments for that purpose, and a single laser curve need only act as a linear one gameplay wise but provide the same look, and be MUCH easier to edit when charting. You wouldn't have to delete all your segments and try again to slightly change the shape. Not a necessary feature, but when I implemented it I loved how it looked. I can't find video or a stream of me working with it, so I can't really show it off :/ It supported a few different curve options, though, for some great freedom in how they looked.
I have a minor suggestion that wouldn't require a whole new file format.
I would love to have a "romanised title" and a "romanised artist" field in the header similarly to osu. This would help players that don't have a Japanese keyboard installed on their OS to be able to search for songs with Japanese characters. You can currently implement searching by romanised title by storing the path to the beatmap within a database (assuming the folder containing the beatmap is named correctly). However this is prone to break very easily just by changing the folder name that the beatmap is contained in.
I second the additional artist/title (and add that maybe everything text based should have the optional romanized counterpart.) for the same reasons, it wouldn't be hard and is super useful. It's not like we can force them to represent the same thing, but it's a really good step in the right direction for searchability and ease of use for non-Japanese users.
But I don't think curving lasers should be supported in KSM (because it would not be supported in a similar arcade game SDVX).
I think this is a bad mindset. There is no good reason to limit what is possible to do in the format just because it is something that is currently missing in the SDVX arcade game.
I agree. This makes it be human readable, performant, easy to extend, and easy to read. A nice tradeoff in my opinion.
As far as Laser Curves, SDVX may not support them but that doesn't mean that the concept isn't already in use. Everyone uses small linear segments for that purpose, and a single laser curve need only act as a linear one gameplay wise but provide the same look, and be MUCH easier to edit when charting.
OK, I understood. Actually I came up with the problem of direction change caused by non-linear lasers, but it would not be a problem according to @Drewol, thanks.
I've currently planned to use 1/9600 in KSM
Does this make sense realistically? Can songs be written to have notes on the 1/9600th note of a measure? Maybe I'm misunderstanding here.
I just meant the number of pulse that stands for 1 measure in 4/4 is 9600.
As far as future expandability concerns, I've implemented a lot of the properties of objects as bit-flagged optional. Even with only 8bit flags, you have a lot of expansion room since not very many new features are likely to be added (and even then, the final flag can be specified as an extension flag instead allowing for theoretically infinite flagging of note features.)
I admit future expandability can be maintained even if we choose a binary format, and it would not need any extra bytes (e.g. the same keys like "x
" "y
" "l
" for every note). But I still prefer @albshin's JSON approach. As a developer, implementing the whole spec for just using a specific information is tiresome. With a JSON-based format, since it is friendly to modern languages, you can get or edit specific parts in charts by just using your favorite language and its JSON parser.
I would like to work on a revised spec based on the first draft by @albshin.
I would love to have a "romanised title" and a "romanised artist" field in the header similarly to osu.
I second the additional artist/title (and add that maybe everything text based should have the optional romanized counterpart.)
We definitely need either. In addition, we may need "movie author" and "genre" (like BMS) field as far as I've heard from users. They're currently written in the optional "information" field.
Ok, so do we have most of the problems of ksh solved now? I just wan't to make sure before moving on.
It seems to me like the only things left is the UTF-8/ASCII problem which could be solved by just writing in the spec that it's UTF-8 and anyone using something different will not be complying with the spec so it won't oficially be supported, and the hashing thing which is a harder problem but as @albshin says it might be best to leave that up to each IR system creator.
I'm pretty happy with JSON though I would've liked to see a binary format but I realized the other day that many higher-level languages do not have very good binary readers so for applications like for example my own ksh2svg it might be very hard to read the new format if it were to be binary.
Yeah, binary being hard for some platforms is a good reason.
I forgot to share my plans about scoring. I mentioned hashing using the word "Internet ranking" (IR), but I would also like to use it for managing local scores.
The way for score management which the current KSM uses is badly implemented. Each scores is tied to a relative path of the chart. Due to this, the current KSM will never work with multiple song directories. If hashing is not costly and feasible enough to check all the files in registered directory, we can use the hash as a key of scores or play histories. I think it's meaningful.
I also agree that this requirement is not necessarily fulfilled in the chart spec, but if it can be solved, it would be better.
In this point, we should avoid floating-point values if possible (e.g. for laser x-axis position). If just opening and saving does not affect the hash is better.
But this design is not necessarily the must.
I always prefer a fixed width range for things like that if possible, yes.
OK, now I'm preparing a revised chart format spec based on the kshon spec.
I've finished a revised spec! https://gist.github.com/m4saka/a89594a17dc9422d75e01998bcfd2722/7a421bc54d3b81b4e01601e8ade715df6437ebb9
Major changes:
kshon
-> kson
kshon
" reminds of a Japanese word "shon(ben)" (=piss)...l
" (length) is 0 or not.0
=BT-A, 1
=BT-B, 2
=BT-C, 3
=BT-D0
=FX-L, 1
=FX-R0
=VOL-L (left knob), 1
=VOL-R (right knob)distance
=zoom_bottom
, shift_x
=zoom_side
, rotation_x
=zoom_top
)Looking good so far đź‘Ť
I have some suggestions:
color
field to DifficultyInfo
would be neat.
dictionary LaserSection {
LaserNote[] segments;
any wide; // sets this section to be a 2x wide sections or not
}
dictionary NoteInfo { BTNote[][]? bt; // bt notes (first key: lane index) FXNote[][]? fx; // fx notes (first key: lane index) LaserSection[][]? laser; // laser notes (first key: lane index (0: left knob, 1: right knob)) }
And also there seemed to be some redundancy for which lane the lasers and fx are in, it's both in the key and on the object. Maybe that is intentional but it looked a little weird to me.
Maybe adding a
color
field toDifficultyInfo
would be neat.
I was also thinking whether to add the color
option to DifficultyInfo
, but finally I didn't add it.
I think some skin creators want to adjust the difficulty colors in detail, so setting color code (like #FF0000) is not a good choice. But preparing every color name (like HTML color names) is not realistic, and also skin creators will not be able to fully control all these colors.
In my opinion, I think assigning colors only to known difficulty names (short_name
) by the kson clients is better (but it would be a weird logic).
I was thinking that maybe the lasers could be made into another level of arrays so you also have arrays of laser sections, something like this ...
LaserSection is a great solution! We will not have to check which lasers are connected, and also wide-range lasers can be correctly defined.
And also there seemed to be some redundancy for which lane the lasers and fx are in, it's both in the key and on the object. Maybe that is intentional but it looked a little weird to me.
Sorry, it was not intentional and it's a simple mistake. I'll fix it.
Updated the spec. https://gist.github.com/m4saka/a89594a17dc9422d75e01998bcfd2722/d1b00ae739f16f21742edcd47265e3d1eba621d9
With laser sections, just having laser points is enough, so I changed the segments
to points
.
I also changed the camera event type name "distance
" to "zoom
" referring to @nashiora's Haven (https://github.com/nashiora/Haven). With this, bigger values can clearly mean a closer look.
BTW, I chose two-dimensional arrays for representing lanes & notes, but it is not necessarily good.
For example, in NoteInfo
, BT-A and BT-B lanes cannot be skipped even in a chart that only have BT-C notes.
The same problem will likely occur in AudioEffectEventInfo
.
I have not fixed this yet, but using dictionaries may be better. If dictionaries are used, do you think their key should be index numbers (like "0", "1", "2", "3") or lane names (like "l", "r" or "a", "b", "c", "d")?
For clarification, JUST the first point in a slam contains the information about its slams sounds and effects, yes? Also, the documentation in that area still discusses l
which is no longer present.
I definitely think the 2d array is a bit much.
For clarification, JUST the first point in a slam contains the information about its slams sounds and effects, yes? Also, the documentation in that area still discusses
l
which is no longer present.
Hmmm... That would be confusing, and we should have had a length parameter in lasers.
I will switch back to the previous LaserSection
& LaserNote
specification.
But I realized that we cannot represent the last slam in a laser section with the LaserNote
spec.
Maybe we need the "end" values of laser's x axis in addition to the "beginning" values, although that will make redundancy.
Using LaserPoint
, we won't have this problem, but it'll have a problem about confusing position for specifying slam sound. Hmm...
I definitely think the 2d array is a bit much.
Do you mean dividing into lanes is also a bit much? (I ask this question because using dictionary for lanes, which I suggested, is more complex than the 2d array.)
Is a regular array of objects not an option? I'm not familiar with what we're using for description here, honestly.
But I realized that we cannot represent the last slam in a laser section with the LaserNote spec.
With a single "alpha" position, yes. As you mentioned I've always used two, the begin and end. Using the points is still fine, so long as you mark in the spec that the first point of a slam has the info for it.
I don't think finding a slam in the array is an issue, but if needed an int[]
can be added to LaserSegment
of indices into the points where slams start. I don't think that's a good or necessary idea, but may as well say it.
Is a regular array of objects not an option? I'm not familiar with what we're using for description here, honestly.
That would be still an option, but if notes are divided into lanes, it's definitely easier to look at successive notes in the same lane, and it would make enumerating lasers much easier. I admit this technique should not necessarily be applied in the file format, but you will need extra buffers in enumerating laser notes with lane-mixed note arrays.
With a single "alpha" position, yes. As you mentioned I've always used two, the begin and end. Using the points is still fine, so long as you mark in the spec that the first point of a slam has the info for it.
Since every implementation will need to store two values (the begin and end) for a laser note, always using two values is reasonable. But in this case, we need to think about illegal cases (e.g. the end value of the previous laser does not match the next beginning value).
To avoid this, only the last laser in a section can have the "end" value. With this idea, you don't have to worry about illegal cases, and the last slam can be represented.
I managed to completely misunderstand the data layout in my head, 2d array is totally fine. Don't know what I was thinking honestly.
Hello, I'm a charter who likes to make gimmicky kshoot charts.
After reading the thread I love how the new spec is being created. I have a few suggestions though:
KsonInfo.resolution
could be much lower (as low as 12).kson.notes
is an array. Shouldn't it be just NoteInfo?
?KsonInfo.bpm
field should be optional, but is a very good thing to have.
/[\s\d.,\-?]+/
would be fine.DifficultyInfo.name
should be optional too. (Leaving index
be the only required field.)LaserPoint
at a given (lane, pulse) can exist.LaserPoint
objects (before, after)
, a laser line (before.xf or before.x, after.x)
will be drawn.
dictionary LaserPoint {
unsigned long y;
int x;
int? xf; // If this exists, then a slam is created at pulse y,
// where the laser moves from x to xf.
// audio_effect, spin, key_sound, ...
}
CameraEventInfo
may contain copies of LaserSection
-likes to represent camera movements.[0, 10, ..., 90, 100, 105, 200, 215, 230, 245, 260, 275, 290, 300, 320, 340, 360, ..., (until the very last object)]
dictionary BarLine {
unsigned long y; // Pulse number for first line
unsigned long l; // 0 for single line, >0 for "pulse per line"
}
@123jimin Thanks for your feedback!
I suggest that the default encoding should be UTF-8 without BOM.
I think so, too. And I think the linefeed code should be LF (not CR+LF).
Default KsonInfo.resolution could be much lower (as low as 12).
I partially understand that, but I think it's good to have high resolution enough by default.
I even wonder why KsonInfo.resolution
is changeable. If it is set high enough by default (e.g. 240), this value will be rarely specified. I think all charts having the same resolution is good. If there's no charts with a resolution other than 240, kson clients only have to support that resolution, and their implementation would be very simple.
By the way, 12 is too low (32th notes cannot be represented with that value).
DifficultyInfo.name should be optional too. (Leaving index be the only required field.)
If difficulty terms differ depending on kson clients, it would be confusing. So I added this option, and I supposed it is specified by the editor program by default. By this, if a charter uses the KSM editor, the difficulty terminologies will be "LT/CH/EX/IN", and this terms are valid even if players use another kson client.
But allowing it not to be specified is good. DifficultyInfo.name
should be optional, but I suppose it will be usually specified by default.
I suggest to implement slams like this (using optional end values) (not considering curves for now)...
I love your suggestion. I think your idea is the best! :+1:
As you pointed out, CameraEvent
also should use the same one.
I suggest to implement bar lines like this: ...
I also think storing all the bar lines is meaningless, but I don't like your BarLine
idea.
For unification, the term "l
" should be used only for lengths in tick values.
Actually I haven't modified the bmson's bar lines yet, and the current spec of bar lines will not go well with KSM. If it can be modified, I prefer the "real" time signature specification like this. This is more similar to the current KSH format. If the full song is in 4/4 time, specifying time signatures is no longer needed.
dictionary TimeSignature {
unsigned long index; // Measure No. (not a tick value)
unsigned long n; // Numerator
unsigned long d; // Denominator
}
I also prefer this (the same as BMS spec), but it doesn't work well in some indivisible cases (but it is a rare case).
dictionary TimeSignature {
unsigned long index; // Measure No. (not a tick value)
double beat; // Numerator/Denominator (e.g. 4/4 = 1.0, 3/4 = 0.75)
}
And I agree with all your other suggestions that I didn't mention.
Changes:
KsonInfo.bpm
is renamed as KsonInfo.disp_bpm
for clarificationBarLine
is deleted, and TimeSignature
is used instead.
KsonInfo.init_time_sig
is the initial time signature (needed for specifying the time signature before the first tick)
DifficultyInfo.name
is now optional.value
" is replaced with "v
" in order to use the "f
" suffix. ("f
" stands for "finally"?)
v
" is also used in the initial version of bmson.BpmEvent.bpm
and LaserPoint.x
are renamed as "v
"duration
are renamed as "l
"
duration
is used in the bmson format, but there's no good reason to use a different term from note lengths.v
" and "vf
" values.
Although fixed laser values and camera event values are preferred in hashing, I become to think using double
for these values is more "natural" way if curves are supported.
By the way, I think the lane spin & swing can be defined as something like a "macro" for camera values.
If these "macros" affect camera values by relative changes, they work in the same way as the current spin/swing behavior. Furthermore, camera values (including the current zoom_top
/zoom_bottom
) can be tweaked by a laser slam as a trigger. I think allowing notes other than laser slams as a trigger would be interesting.
When the lane spin is defined as a macro, the lane tilt will be implemented as a camera value. Since the lane tilt follows lasers, some translation between lasers and camera values will be needed, and some scaling value is required for this translation (to support "zero"/"normal"/"bigger"/"biggest"). And if tilt value is specified by charter, the translation scale should be set to "zero" automatically. Does anybody have a good idea about this?
Using a "section" (like LaserSection
) for camera events is one solution to set the tilt scale to "zero" automatically while a charter specifying the lane tilt values manually.
@123jimin's proposed laser idea is probably the best, though part of me still dislikes the space requirement but hey that's not important.
I don't quite know if we're talking about storing the literal position of a bar line; if we are, I hate that idea and agree that they don't need to be stored at all, anywhere. @m4saka's last TimeSignature example is all that's needed for anything bar related, and bars are easily determined by N * resolution.
If that's not what was being talked about, then I have no idea :)
I really want to spins etc. as things that can be programmed and applied to more than slams. In my implementations, I tended to refer to them as Impulse events. I think it might be fair to restrict these specifically to playable objects, I've made the point of saying "anywhere might be awesome!" but then the common case of slam + spin is now tough to detect, so not a great idea.
Effecting any camera value sounds amazing, I actually implemented something similar in my first SDVX clone where a slam would always move the critical position slightly so the whole lane bobbed back and forth. (https://www.youtube.com/watch?v=tXCeHWFe1Kk is a good example, and the video uploaded right after as well for a more laser heavy section and the effect toned down a bit). Making that (basically for my effect, a slight zoom out and translation in the direction of the slam) an easy-to-apply macro would be fantastic to see, and if designed right it'd be super simple. I can work on some rough spec if needed.
On that note, since my previous example is applied to every slam, (this is probably a bad idea and I'm great at taking things too far) what if your default slam effect could be configured to be one of those macros instead? So no slam effect defaults to a pre-defined macro, and only one (or none) can be specified? Then a whole chart could have a unique feel by just adjusting the default behaviour of slams without going thru the trouble of changing every single one. Maybe a slower more flow-y song would like to add a small swing or bounce to every slam to create a consistent feeling of fluff or idunno words.
Thanks, I like the current format spec! (Except ow events are represented, but I still have no good idea.) Also, I prefer using TimeSignature
instead of BarLine
too.
Talking about macros, I made this macro language to create camera effects on my latest chart, which is full of gimmick camera movements. This works by defining macros which add relative camera movements 'on top of' existing camera effects. (Absolute camera movements invoke some problems on recursice macros...)
While I don't think that adding this to the spec is a good idea (performance and security related issues), I think that some portion of this could be helpful.
Important question: Should a laser section contain its own y value instead and each point inside becomes relative to that, or should those points always be absolute?
so should it be
or
ninjaedit: I prefer the first option personally
As for camera macros, it may be better to discuss further after the first spec draft. For future expandability, "spin", "half_spin" and "swing" can be used as implicitly-defined macros, then the macro feature itself can be settled in future. The preset spins/swings can be overwritten by a macro with the same name. This will fit @nashiora’s suggestion. I think @123jimin’s script is too much for the spec, but it would be a good reference for macro plans.
Important question: Should a laser section contain its own y value instead and each point inside becomes relative to that, or should those points always be absolute?
I like either, but we should use another term for the y value if it is a relative value.
{ "y":32, [ { "ry":0, "v":-100 }, { "ry":32, "v":0 }, { "ry":64, "v":100 } ] }
Changes:
meta
/beat
/note
/audio
/camera
because there were too many top-level objects.
meta
& audio
will do).ry
values relative to the y
value of the parent laser segment.spin
/half_spin
/swing
.
CameraMacroEvent.scale
(left="-100
", right="100
").lane_zoom
: zoom_bottomlane_shift_x
: zoom_sidelane_rotation_x
: zoom_toplane_rotation_z
: lane rotation (degree)jdgline_shift_x
: judgment line xjdgline_rotation_z
: judgment line rotation (degree)lane_rotation_z
and jdgline_rotation_z
(and possibly jdgline_shift_x
?)l
(length) value.init_bpm
and init_time_sig
are deleted.
event
and plural words are meaninglessly used (e.g. "s" is used for both dictionaries and arrays), so stopped using them.Any feedback is welcome!
Also we need further information about curves to make progress.
if the function I linked before then curves simply just need two numbers that are either 0-1
floating or some fixed range like 0-256
and if the two parameters are equal or if either of them are undefined then the laser is straight, but I'm unsure of where these should be placed since the format will use laser points and not segments. The chunithm simulator seaurchin seems to also have some form of curves implemented https://seaurchin.kb10uy.org/wiki/score/curve so maybe we could look at that for inspiration.
Using bezier (like seaurchin) is still an option but laser direction can be changed midway through a laser note. But maybe we need to predetermine the laser x value for these bezier before playing, and we would simply divide a laser note into n-ths (e.g. 10ths) and calculate these x values. Then direction change may not be a problem. (Actually seaurchin preprocesses these bezier by dividing into points at certain intervals.)
I also found cubic-bezier
in CSS3.
I believe this one is the two-point version that @Drewol pointed out before.
If this is used, the direction is not a problem if the values are restricted between 0 and 1.
http://cubic-bezier.com
I haven't figured out how to implement these control points in the current laser/camera point spec, too.
I still think leaving segment-specific data on the point which begins that segment is the best idea for this. I'm on the boat of storing the two points of the curve equation and always using that to check if it's linear. The easy, default, case is that they're both 0 anyways.
Does a linear and a curve, I assume 0-100 but these are arbitrary values anyway
PT_A {(A-B, Line) ry:0, v:-100, a:0, b:0 }, PT_B {(B-C, Curve) ry:192, v:100, a:20, b:80 }, PT_C { (END, Slam) ry:284, v:-100, vf:100, a:0, b:0 }
Yeh?
That's good. And I figured out that a cubic bezier curve can be approximated by two quadratic curves (quadratic one is the same as the nashiora's example). The quadratic curve is a reasonable choice if you don't mind the number of control points for complex curves.
https://gist.github.com/m4saka/a89594a17dc9422d75e01998bcfd2722/d539108eed32ea9fe7e93794448c02b15c28548c Added curves to the spec! Currently the value of 0-100 [%] is used for a & b values.
Here's the TODO list:
// TODO List
// - lane swing (with a camera macro with curves?)
// - meta data (which options are to be inherited from KSH?)
// - BGA layer?
Since "layer
" option of the KSH spec allows changing the animation speed and whether to spin or tilt, it is good to consider BGA layers at this time.
for BGA, are you also considering things like supporting character artwork moving around like on "boss" songs in the sdvx arcade game?
for BGA, are you also considering things like supporting character artwork moving around like on "boss" songs in the sdvx arcade game?
Not necessarily, but it’s great if it is possible. I think these "boss" BGA layer options can be designed as an extension for the current animation layer. If it is hard work to maintain the positional relationship for these layers for the client developers, it would be okay to implement them as client-dependent features.
oops
My thought on camera macros: implement them as relative "patches" to base camera angle/location, where its length is provided when the macro is invoked.
Therefore, each macro invocaton must provide {int tick, uint length, string macro}
, where some of the values can be omitted in the file format, and default length may be provided by the macro definition.
The macro definition itself is then simply given by {string name, uint defaultLength, CameraEventInfo body}
. I think that this is not the best way, but this would be the most convenient way.
Implementation example:
Some random ideas (I'm on a phone now so will skip details for now; they all have pros and cons):
ScrollSpeedEvent
for adjusting scroll speed, where the note speed is now given by bpm * scrollSpeed
.My thought on camera macros: implement them as relative "patches" to base camera angle/location, where its length is provided when the macro is invoked. Therefore, each macro invocaton must provide
{int tick, uint length, string macro}
, where some of the values can be omitted in the file format, and default length may be provided by the macro definition. The macro definition itself is then simply given by{string name, uint defaultLength, CameraEventInfo body}
. I think that this is not the best way, but this would be the most convenient way.
That's what I was thinking. (The word "macro" that I chose may not be good for it.)
I realized I forgot to write them in the KSH spec document, but the lane swing of KSH have three parameters, (1) the scale of the sine wave, (2) how many times to repeat the sine wave, and (3) whether to damp the wave and its type (linear damping or square damping).
If the lane swing is to be implemented as a macro, we need to consider these parameters. Also, apart from swings, you may want to change how many times to spin or how many degrees a half-spin is.
I even think this is too much, but this example can satisfy these requirements (variable names are random):
{int tick, MacroInfo[] macros}
{string name, uint length, int scale}
{string name, uint defaultLength, CameraEventInfo body}
As for three-time swing with linear decay, you can use {tick:0, macros:[ {"name":"swing", "length":960, "scale":100}, {"name":"swing", "length":960, "scale":-67}, {"name":"swing", "length":960, "scale":33} ]}
.
If you want to insert a three-time spin, you can just use {tick:0, macros:[ {"name":"spin1", "length":960, "scale":100}, {"name":"spin1", "length":960, "scale":100}, {"name":"spin1", "length":960, "scale":100}, {"name":"spin2", "length":960, "scale":100} ]}
, where "spin1" is a 360 deg rotation, and "spin2" is the spin reverberation.
(But of course, lane spins are not linear changes, so I don't think this example simply works well.)
I don't think this is a good design because you need to set two macros for just using one spin. But I don't know how to improve it keeping the requirements of the lane swing and multiple spins fulfilled.
Edit: Latest kson spec link: https://github.com/m4saka/ksh2kson/blob/master/kson_format.md
I know KSH format is not so great. It's basically designed referring to the jubeat_analyser format and StepMania (.sm) format. (The parameters
m=
,o=
,t=
and bar lines (--
) are from the jubeat_analyser format.)KSH format problems:
chokkakuvol=
", "m=
", "o=
" etc.)KSH Chart Format Specification: ksh_format_spec.md
Any feedback would be appreciated!