dipzza / ultrastar-song2txt

Tools that automate parts of making a song in the ultrastar txt format
GNU Affero General Public License v3.0
1 stars 0 forks source link

Design decisions for data structures for song data #47

Closed dipzza closed 1 year ago

dipzza commented 2 years ago

Related to https://github.com/dipzza/ultrastar-song2txt/issues/37 we need a data structure to save the song information (player turn, notes, when does a phrase ends, ...), this is needed for pitch estimation tasks related to https://github.com/dipzza/ultrastar-song2txt/issues/7.

What do we need the structure for?

Which Python structure/s should we use?

How should we define the structure/s?

Should we implement some related behavior?

dipzza commented 2 years ago

Which Python structure should it use then?

A class for each type of information (notes, player turn change, phrase ends) allows for easy input validation and text generation.

A more complete representation with just one clase for a "phrase" which contained all this information was considered, but it would be more difficult to work with for our use-case, as it needs more calculations to modify values, and seems more appropriate as a higher layer of abstraction for other applications like karaoke videogames.

The class for player turn change should be inmutable, as we don't have the need to modify it, and this provides more robust objects and the possibility to cache them.

The classes for notes and phrase ends should be mutable, as the project requires modifying each one of them quite frequently and, for each song, all objects are going to be different, so it's not possible to reuse them. According to domain driven design they could be considered entities, as a concrete note or phrase end inside a song project is different just for it's relative position to others, giving it is own identity, and it conserves this identity even when it's pitch or other attributes are adjusted.

How should we define the structure?

With the same reasoning made on #46 we should use dataclasses, which allows specification whether we want mutable or inmutable objects.

Should we implement some related behavior?

To validate input data we can use "__post_init" for inmutable objects, same as in #46. For mutable objects we should ensure that both initialization and modification are validated, usually for this Python properties are used to ensure every access to the attribute uses defined functions. However, they are not compatible with dataclasses by default and require some boilerplate to work around it. For a simpler sintax we can just override the "setattr__" method which is called every time any attribute is set to a different value, and make all checks there.

For generating the equivalent string representation for the UltraStar txt format implementing the special method "str" is appropriate, as we already have the automatically generated 'repr" method for a more direct string representation.