2022-09-06 00:59:54 -04:00
package funkin . play . song ;
2022-09-13 01:09:30 -04:00
import flixel . util . typeLimit . OneOfTwo ;
import funkin . play . song . ScriptedSong ;
2022-09-07 19:07:08 -04:00
import funkin . util . assets . DataAssets ;
2022-09-13 01:09:30 -04:00
import haxe . DynamicAccess ;
import haxe . Json ;
2022-09-07 19:07:08 -04:00
import openfl . utils . Assets ;
import thx . semver . Version ;
2022-09-06 00:59:54 -04:00
/ * *
* Contains utilities for l o a d i n g a n d p a r s i n g s t a g e d a t a .
* /
class SongDataParser
{
2023-01-22 19:55:30 -05:00
/ * *
* A list containing all the songs available to the game .
* /
static final songCache : Map < String , Song > = new Map < String , Song > ( ) ;
2023-02-21 20:58:15 -05:00
static final DEFAULT_SONG_ID : String = ' U N K N O W N ' ;
static final SONG_DATA_PATH : String = ' s o n g s / ' ;
static final SONG_DATA_SUFFIX : String = ' - m e t a d a t a . j s o n ' ;
2023-01-22 19:55:30 -05:00
/ * *
* Parses and preloads the game ' s s o n g m e t a d a t a a n d s c r i p t s w h e n t h e g a m e s t a r t s .
*
* If you want to force song metadata to be reloaded , you can just call this function again .
* /
public static function loadSongCache ( ) : Void
{
clearSongCache ( ) ;
2023-02-21 20:58:15 -05:00
trace ( ' L o a d i n g s o n g c a c h e . . . ' ) ;
2023-01-22 19:55:30 -05:00
//
// SCRIPTED SONGS
//
var scriptedSongClassNames: Array < String > = ScriptedSong . listScriptClasses ( ) ;
trace ( ' I n s t a n t i a t i n g ${ scriptedSongClassNames . length } s c r i p t e d s o n g s . . . ' ) ;
for ( songCls in scriptedSongClassNames )
{
var song: Song = ScriptedSong . init ( songCls , DEFAULT_SONG_ID ) ;
if ( song != null )
{
trace ( ' L o a d e d s c r i p t e d s o n g : ${ song . songId } ' ) ;
songCache . set ( song . songId , song ) ;
}
e lse
{
trace ( ' F a i l e d t o i n s t a n t i a t e s c r i p t e d s o n g c l a s s : ${ songCls } ' ) ;
}
}
//
// UNSCRIPTED SONGS
//
2023-02-21 20:58:15 -05:00
var songIdList: Array < String > = DataAssets . listDataFilesInPath ( SONG_DATA_PATH , SONG_DATA_SUFFIX ) . map ( function ( songDataPath : String ) : String {
2023-01-22 19:55:30 -05:00
return songDataPath . split ( ' / ' ) [ 0 ] ;
} ) ;
2023-02-21 20:58:15 -05:00
var unscriptedSongIds: Array < String > = songIdList . filter ( function ( songId : String ) : Bool {
2023-01-22 19:55:30 -05:00
return ! songCache . exists ( songId ) ;
} ) ;
trace ( ' I n s t a n t i a t i n g ${ unscriptedSongIds . length } n o n - s c r i p t e d s o n g s . . . ' ) ;
for ( songId in unscriptedSongIds )
{
try
{
2023-02-21 20:58:15 -05:00
var song: Song = new Song ( songId ) ;
2023-01-22 19:55:30 -05:00
if ( song != null )
{
trace ( ' L o a d e d s o n g d a t a : ${ song . songId } ' ) ;
songCache . set ( song . songId , song ) ;
}
}
c atch ( e )
{
trace ( ' A n e r r o r o c c u r r e d w h i l e l o a d i n g s o n g d a t a : ${ songId } ' ) ;
trace ( e ) ;
// Assume error was already logged.
continue ;
}
}
trace ( ' S u c c e s s f u l l y l o a d e d ${ Lambda . count ( songCache ) } s t a g e s . ' ) ;
}
/ * *
* Retrieves a particular song from the cache .
2023-02-21 20:58:15 -05:00
* @ param songId The ID of the song to retrieve .
* @ return The song , or null if i t w a s n o t f o u n d .
2023-01-22 19:55:30 -05:00
* /
public static function fetchSong ( songId : String ) : Null < Song >
{
if ( songCache . exists ( songId ) )
{
var song: Song = songCache . get ( songId ) ;
trace ( ' S u c c e s s f u l l y f e t c h s o n g : ${ songId } ' ) ;
return song ;
}
e lse
{
trace ( ' F a i l e d t o f e t c h s o n g , n o t f o u n d i n c a c h e : ${ songId } ' ) ;
return null ;
}
}
static function clearSongCache ( ) : Void
{
if ( songCache != null )
{
songCache . clear ( ) ;
}
}
public static function listSongIds ( ) : Array < String >
{
return songCache . keys ( ) . array ( ) ;
}
public static function parseSongMetadata ( songId : String ) : Array < SongMetadata >
{
var result: Array < SongMetadata > = [ ] ;
var rawJson: String = loadSongMetadataFile ( songId ) ;
var jsonData: Dynamic = null ;
try
{
jsonData = Json . parse ( rawJson ) ;
}
c atch ( e ) { }
var songMetadata: SongMetadata = SongMigrator . migrateSongMetadata ( jsonData , songId ) ;
songMetadata = SongValidator . validateSongMetadata ( songMetadata , songId ) ;
if ( songMetadata == null )
{
return result ;
}
result . push ( songMetadata ) ;
var variations = songMetadata . playData . songVariations ;
for ( variation in variations )
{
var variationRawJson: String = loadSongMetadataFile ( songId , variation ) ;
var variationSongMetadata: SongMetadata = SongMigrator . migrateSongMetadata ( variationRawJson , ' ${ songId } _ ${ variation } ' ) ;
variationSongMetadata = SongValidator . validateSongMetadata ( variationSongMetadata , ' ${ songId } _ ${ variation } ' ) ;
if ( variationSongMetadata != null )
{
variationSongMetadata . variation = variation ;
result . push ( variationSongMetadata ) ;
}
}
return result ;
}
static function loadSongMetadataFile ( songPath : String , variation : String = ' ' ) : String
{
var songMetadataFilePath: String = ( variation != ' ' ) ? Paths . json ( ' $ SONG_DATA_PATH $ songPath / $ songPath - m e t a d a t a - $ variation ' ) : Paths . json ( ' $ SONG_DATA_PATH $ songPath / $ songPath - m e t a d a t a ' ) ;
var rawJson: String = Assets . getText ( songMetadataFilePath ) . trim ( ) ;
while ( ! rawJson . endsWith ( " } " ) )
{
rawJson = rawJson . substr ( 0 , rawJson . length - 1 ) ;
}
return rawJson ;
}
public static function parseSongChartData ( songId : String , variation : String = " " ) : SongChartData
{
var rawJson: String = loadSongChartDataFile ( songId , variation ) ;
var jsonData: Dynamic = null ;
try
{
jsonData = Json . parse ( rawJson ) ;
}
c atch ( e ) { }
var songChartData: SongChartData = SongMigrator . migrateSongChartData ( jsonData , songId ) ;
songChartData = SongValidator . validateSongChartData ( songChartData , songId ) ;
if ( songChartData == null )
{
trace ( ' F a i l e d t o v a l i d a t e s o n g c h a r t d a t a : ${ songId } ' ) ;
return null ;
}
return songChartData ;
}
static function loadSongChartDataFile ( songPath : String , variation : String = ' ' ) : String
{
var songChartDataFilePath: String = ( variation != ' ' ) ? Paths . json ( ' $ SONG_DATA_PATH $ songPath / $ songPath - c h a r t - $ variation ' ) : Paths . json ( ' $ SONG_DATA_PATH $ songPath / $ songPath - c h a r t ' ) ;
var rawJson: String = Assets . getText ( songChartDataFilePath ) . trim ( ) ;
while ( ! rawJson . endsWith ( " } " ) )
{
rawJson = rawJson . substr ( 0 , rawJson . length - 1 ) ;
}
return rawJson ;
}
2022-09-06 00:59:54 -04:00
}
2022-09-07 19:07:08 -04:00
2022-10-07 00:37:21 -04:00
typedef RawSongMetadata =
2022-09-07 19:07:08 -04:00
{
2023-01-22 19:55:30 -05:00
/ * *
* A semantic versioning string for t h e s o n g d a t a f o r m a t .
*
* /
var version: Version ;
var songName: String ;
var artist: String ;
var timeFormat: SongTimeFormat ;
var divisions: Int ;
var timeChanges: Array < SongTimeChange > ;
var loop: Bool ;
var playData: SongPlayData ;
var generatedBy: String ;
/ * *
* Defaults to ` d efault ` or ` ' ' ` . Populated later .
* /
var variation: String ;
2022-09-07 19:07:08 -04:00
} ;
2022-10-07 00:37:21 -04:00
@ : forward
abstract SongMetadata ( RawSongMetadata )
{
2023-01-22 19:55:30 -05:00
public function n e w ( songName : String , artist : String , variation : String = ' d e f a u l t ' )
{
2023-01-22 22:25:45 -05:00
this =
{
version : SongMigrator . CHART_VERSION ,
songName : songName ,
artist : artist ,
timeFormat : ' m s ' ,
divisions : 96 ,
timeChanges : [ new SongTimeChange ( - 1 , 0 , 100 , 4 , 4 , [ 4 , 4 , 4 , 4 ] ) ] ,
loop : false ,
playData :
{
songVariations : [ ] ,
difficulties : [ ' n o r m a l ' ] ,
playableChars :
{
bf : new SongPlayableChar ( ' g f ' , ' d a d ' ) ,
} ,
stage : ' m a i n S t a g e ' ,
noteSkin : ' N o r m a l '
} ,
generatedBy : SongValidator . DEFAULT_GENERATEDBY ,
// Variation ID.
variation : variation
} ;
2023-01-22 19:55:30 -05:00
}
public function clone ( ? newVariation : String = null ) : SongMetadata
{
var result = new SongMetadata ( this . songName , this . artist , newVariation == null ? this . variation : newVariation ) ;
result . version = this . version ;
result . timeFormat = this . timeFormat ;
result . divisions = this . divisions ;
result . timeChanges = this . timeChanges ;
result . loop = this . loop ;
result . playData = this . playData ;
result . generatedBy = this . generatedBy ;
return result ;
}
2022-10-07 00:37:21 -04:00
}
2022-09-13 01:09:30 -04:00
typedef SongPlayData =
{
2023-01-22 19:55:30 -05:00
var songVariations: Array < String > ;
var difficulties: Array < String > ;
2022-09-13 01:09:30 -04:00
2023-01-22 19:55:30 -05:00
/ * *
* Keys are the player characters and the values give info on what opponent / GF / inst to use .
* /
var playableChars: DynamicAccess < SongPlayableChar > ;
2022-09-13 01:09:30 -04:00
2023-01-22 19:55:30 -05:00
var stage: String ;
var noteSkin: String ;
2022-09-13 01:09:30 -04:00
}
typedef RawSongPlayableChar =
{
2023-01-22 19:55:30 -05:00
var g: String ;
var o: String ;
var i: String ;
2022-09-13 01:09:30 -04:00
}
2022-09-16 15:37:00 -04:00
typedef RawSongNoteData =
{
2023-01-22 19:55:30 -05:00
/ * *
* The timestamp of the note . The timestamp is in the format of the song ' s t i m e f o r m a t .
* /
var t: Float ;
/ * *
* Data for t h e n o t e . R e p r e s e n t s t h e i n d e x o n t h e s t r u m l i n e .
* 0 = left , 1 = down , 2 = up , 3 = right
* ` floor ( direction / strumlineSize ) ` specifies which strumline the note is on .
* 0 = player , 1 = opponent , etc .
* /
var d: Int ;
/ * *
* Length of the note , if a p p l i c a b l e .
* Defaults to 0 for s i n g l e n o t e s .
* /
var l: Float ;
/ * *
* The kind of the note .
* This can allow the note to include information used for c u s t o m b e h a v i o r .
* Defaults to blank or ` " n o r m a l " ` .
* /
var k: String ;
2022-09-16 15:37:00 -04:00
}
abstract SongNoteData ( RawSongNoteData )
{
2023-02-21 20:58:15 -05:00
public function n e w ( time : Float , data : Int , length : Float = 0 , kind : String = ' ' )
2023-01-22 19:55:30 -05:00
{
2023-01-22 22:25:45 -05:00
this =
{
t : time ,
d : data ,
l : length ,
k : kind
} ;
2023-01-22 19:55:30 -05:00
}
public var time( get , set ) : Float ;
public function get_time ( ) : Float
{
return this . t ;
}
public function set_time ( value : Float ) : Float
{
return this . t = value ;
}
public var stepTime( get , never ) : Float ;
public function get_stepTime ( ) : Float
{
// TODO: Account for changes in BPM.
return this . t / Conductor . stepCrochet ;
}
/ * *
* The raw data for t h e n o t e .
* /
public var data( get , set ) : Int ;
public function get_data ( ) : Int
{
return this . d ;
}
public function set_data ( value : Int ) : Int
{
return this . d = value ;
}
/ * *
* The direction of the note , if a p p l i c a b l e .
* Strips the strumline index from the data .
*
* 0 = left , 1 = down , 2 = up , 3 = right
* /
public inline function getDirection ( strumlineSize : Int = 4 ) : Int
{
return this . d % strumlineSize ;
}
public function getDirectionName ( strumlineSize : Int = 4 ) : String
{
switch ( this . d % strumlineSize )
{
c ase 0 :
return ' L e f t ' ;
c ase 1 :
return ' D o w n ' ;
c ase 2 :
return ' U p ' ;
c ase 3 :
return ' R i g h t ' ;
d efault :
return ' U n k n o w n ' ;
}
}
/ * *
* The strumline index of the note , if a p p l i c a b l e .
* Strips the direction from the data .
*
* 0 = player , 1 = opponent , etc .
* /
public inline function getStrumlineIndex ( strumlineSize : Int = 4 ) : Int
{
return Math . floor ( this . d / strumlineSize ) ;
}
public inline function getMustHitNote ( strumlineSize : Int = 4 ) : Bool
{
return getStrumlineIndex ( strumlineSize ) == 0 ;
}
public var length( get , set ) : Float ;
public function get_length ( ) : Float
{
return this . l ;
}
public function set_length ( value : Float ) : Float
{
return this . l = value ;
}
public var kind( get , set ) : String ;
public function get_kind ( ) : String
{
2023-01-22 22:25:45 -05:00
if ( this . k == null || this . k == ' ' ) return ' n o r m a l ' ;
2023-01-22 19:55:30 -05:00
return this . k ;
}
public function set_kind ( value : String ) : String
{
2023-01-22 22:25:45 -05:00
if ( value == ' n o r m a l ' || value == ' ' ) value = null ;
2023-01-22 19:55:30 -05:00
return this . k = value ;
}
@ : op ( A == B )
public function op_equals ( other : SongNoteData ) : Bool
{
2023-01-22 22:25:45 -05:00
if ( this . k == ' ' ) if ( other . kind != ' ' && other . kind != ' n o r m a l ' ) return false ;
2023-01-22 19:55:30 -05:00
return this . t == other . time && this . d == other . data && this . l == other . length ;
}
@ : op ( A != B )
public function op_notEquals ( other : SongNoteData ) : Bool
{
return this . t != other . time || this . d != other . data || this . l != other . length || this . k != other . kind ;
}
@ : op ( A > B )
public function op_greaterThan ( other : SongNoteData ) : Bool
{
return this . t > other . time ;
}
@ : op ( A < B )
public function op_lessThan ( other : SongNoteData ) : Bool
{
return this . t < other . time ;
}
@ : op ( A >= B )
public function op_greaterThanOrEquals ( other : SongNoteData ) : Bool
{
return this . t >= other . time ;
}
@ : op ( A <= B )
public function op_lessThanOrEquals ( other : SongNoteData ) : Bool
{
return this . t <= other . time ;
}
2022-09-16 15:37:00 -04:00
}
typedef RawSongEventData =
{
2023-01-22 19:55:30 -05:00
/ * *
* The timestamp of the event . The timestamp is in the format of the song ' s t i m e f o r m a t .
* /
var t: Float ;
/ * *
* The kind of the event .
* Examples include " F o c u s C a m e r a " and " P l a y A n i m a t i o n "
* Custom events can be added by scripts with the ` ScriptedSongEvent ` c lass .
* /
var e: String ;
/ * *
* The data for t h e e v e n t .
* This can allow the event to include information used for c u s t o m b e h a v i o r .
* Data type depends on the event kind . It can be anything that ' s J S O N s e r i a l i z a b l e .
* /
var v: DynamicAccess < Dynamic > ;
/ * *
* Whether this event has been activated .
* This is only used internally by the game . It should not be serialized .
* /
@ : optional var a: Bool ;
2022-09-16 15:37:00 -04:00
}
abstract SongEventData ( RawSongEventData )
{
2023-01-22 19:55:30 -05:00
public function n e w ( time : Float , event : String , value : Dynamic = null )
{
2023-01-22 22:25:45 -05:00
this =
{
t : time ,
e : event ,
v : value ,
a : false
} ;
2023-01-22 19:55:30 -05:00
}
public var time( get , set ) : Float ;
public function get_time ( ) : Float
{
return this . t ;
}
public function set_time ( value : Float ) : Float
{
return this . t = value ;
}
public var stepTime( get , never ) : Float ;
public function get_stepTime ( ) : Float
{
// TODO: Account for changes in BPM.
return this . t / Conductor . stepCrochet ;
}
public var event( get , set ) : String ;
public function get_event ( ) : String
{
return this . e ;
}
public function set_event ( value : String ) : String
{
return this . e = value ;
}
public var value( get , set ) : Dynamic ;
public function get_value ( ) : Dynamic
{
return this . v ;
}
public function set_value ( value : Dynamic ) : Dynamic
{
return this . v = value ;
}
public var activated( get , set ) : Bool ;
public function get_activated ( ) : Bool
{
return this . a ;
}
public function set_activated ( value : Bool ) : Bool
{
return this . a = value ;
}
public inline function getDynamic ( key : String ) : Null < Dynamic >
{
return this . v . get ( key ) ;
}
public inline function getBool ( key : String ) : Null < Bool >
{
return cast this . v . get ( key ) ;
}
public inline function getInt ( key : String ) : Null < Int >
{
return cast this . v . get ( key ) ;
}
public inline function getFloat ( key : String ) : Null < Float >
{
return cast this . v . get ( key ) ;
}
public inline function getString ( key : String ) : String
{
return cast this . v . get ( key ) ;
}
public inline function getArray ( key : String ) : Array < Dynamic >
{
return cast this . v . get ( key ) ;
}
public inline function getBoolArray ( key : String ) : Array < Bool >
{
return cast this . v . get ( key ) ;
}
@ : op ( A == B )
public function op_equals ( other : SongEventData ) : Bool
{
return this . t == other . time && this . e == other . event && this . v == other . value ;
}
@ : op ( A != B )
public function op_notEquals ( other : SongEventData ) : Bool
{
return this . t != other . time || this . e != other . event || this . v != other . value ;
}
@ : op ( A > B )
public function op_greaterThan ( other : SongEventData ) : Bool
{
return this . t > other . time ;
}
@ : op ( A < B )
public function op_lessThan ( other : SongEventData ) : Bool
{
return this . t < other . time ;
}
@ : op ( A >= B )
public function op_greaterThanOrEquals ( other : SongEventData ) : Bool
{
return this . t >= other . time ;
}
@ : op ( A <= B )
public function op_lessThanOrEquals ( other : SongEventData ) : Bool
{
return this . t <= other . time ;
}
2022-09-16 15:37:00 -04:00
}
2022-09-13 01:09:30 -04:00
abstract SongPlayableChar ( RawSongPlayableChar )
{
2023-01-22 19:55:30 -05:00
public function n e w ( girlfriend : String , opponent : String , inst : String = " " )
{
2023-01-22 22:25:45 -05:00
this =
{
g : girlfriend ,
o : opponent ,
i : inst
} ;
2023-01-22 19:55:30 -05:00
}
public var girlfriend( get , set ) : String ;
public function get_girlfriend ( ) : String
{
return this . g ;
}
public function set_girlfriend ( value : String ) : String
{
return this . g = value ;
}
public var opponent( get , set ) : String ;
public function get_opponent ( ) : String
{
return this . o ;
}
public function set_opponent ( value : String ) : String
{
return this . o = value ;
}
public var inst( get , set ) : String ;
public function get_inst ( ) : String
{
return this . i ;
}
public function set_inst ( value : String ) : String
{
return this . i = value ;
}
2022-09-13 01:09:30 -04:00
}
2022-09-22 06:34:03 -04:00
typedef RawSongChartData =
2022-09-07 19:07:08 -04:00
{
2023-01-22 19:55:30 -05:00
var version: Version ;
2022-09-16 15:37:00 -04:00
2023-01-22 19:55:30 -05:00
var scrollSpeed: DynamicAccess < Float > ;
var events: Array < SongEventData > ;
var notes: DynamicAccess < Array < SongNoteData > > ;
var generatedBy: String ;
2022-09-07 19:07:08 -04:00
} ;
2022-09-22 06:34:03 -04:00
@ : forward
abstract SongChartData ( RawSongChartData )
{
2023-01-22 19:55:30 -05:00
public function n e w ( scrollSpeed : Float , events : Array < SongEventData > , notes : Array < SongNoteData > )
{
2023-01-22 22:25:45 -05:00
this =
{
version : SongMigrator . CHART_VERSION ,
events : events ,
notes :
{
normal : notes
} ,
scrollSpeed :
{
normal : scrollSpeed
} ,
generatedBy : SongValidator . DEFAULT_GENERATEDBY
}
2023-01-22 19:55:30 -05:00
}
public function getScrollSpeed ( diff : String = ' d e f a u l t ' ) : Float
{
var result: Float = this . scrollSpeed . get ( diff ) ;
2023-01-22 22:25:45 -05:00
if ( result == 0.0 && diff != ' d e f a u l t ' ) return getScrollSpeed ( ' d e f a u l t ' ) ;
2023-01-22 19:55:30 -05:00
return ( result == 0.0 ) ? 1.0 : result ;
}
2022-09-22 06:34:03 -04:00
}
2022-09-13 01:09:30 -04:00
typedef RawSongTimeChange =
{
2023-01-22 19:55:30 -05:00
/ * *
* Timestamp in specified ` timeFormat ` .
* /
var t: Float ;
/ * *
* Time in beats ( int ) . The game will calculate further beat values based on this one ,
* so it can do it in a s i m p l e l i n e a r f a s h i o n .
* /
var b: Int ;
/ * *
* Quarter notes per minute ( float ) . Cannot be empty in the first element of the list ,
* but otherwise it ' s o p t i o n a l , a n d d e f a u l t s t o t h e v a l u e o f t h e p r e v i o u s e l e m e n t .
* /
var bpm: Float ;
/ * *
* Time signature numerator ( int ) . Optional , defaults to 4.
* /
var n: Int ;
/ * *
* Time signature denominator ( int ) . Optional , defaults to 4. Should only ever be a power of two .
* /
var d: Int ;
/ * *
* Beat tuplets ( Array < int > or i n t ) . This defines how many steps each beat is divided into .
* It can either be an array of length ` n ` ( see a b o v e ) or a single integer number .
* Optional , defaults to ` [ 4 ] ` .
* /
var bt: OneOfTwo < Int , Array < Int > > ;
2022-09-13 01:09:30 -04:00
}
/ * *
* Add aliases to the minimalized property names of the typedef ,
* to improve readability .
* /
abstract SongTimeChange ( RawSongTimeChange )
{
2023-01-22 19:55:30 -05:00
public function n e w ( timeStamp : Float , beatTime : Int , bpm : Float , timeSignatureNum : Int = 4 , timeSignatureDen : Int = 4 , beatTuplets : Array < Int > )
{
2023-01-22 22:25:45 -05:00
this =
{
t : timeStamp ,
b : beatTime ,
bpm : bpm ,
n : timeSignatureNum ,
d : timeSignatureDen ,
bt : beatTuplets ,
}
2023-01-22 19:55:30 -05:00
}
public var timeStamp( get , set ) : Float ;
public function get_timeStamp ( ) : Float
{
return this . t ;
}
public function set_timeStamp ( value : Float ) : Float
{
return this . t = value ;
}
public var beatTime( get , set ) : Int ;
public function get_beatTime ( ) : Int
{
return this . b ;
}
public function set_beatTime ( value : Int ) : Int
{
return this . b = value ;
}
public var bpm( get , set ) : Float ;
public function get_bpm ( ) : Float
{
return this . bpm ;
}
public function set_bpm ( value : Float ) : Float
{
return this . bpm = value ;
}
public var timeSignatureNum( get , set ) : Int ;
public function get_timeSignatureNum ( ) : Int
{
return this . n ;
}
public function set_timeSignatureNum ( value : Int ) : Int
{
return this . n = value ;
}
public var timeSignatureDen( get , set ) : Int ;
public function get_timeSignatureDen ( ) : Int
{
return this . d ;
}
public function set_timeSignatureDen ( value : Int ) : Int
{
return this . d = value ;
}
public var beatTuplets( get , set ) : Array < Int > ;
public function get_beatTuplets ( ) : Array < Int >
{
if ( Std . isOfType ( this . bt , Int ) )
{
return [ this . bt ] ;
}
e lse
{
return this . bt ;
}
}
public function set_beatTuplets ( value : Array < Int > ) : Array < Int >
{
return this . bt = value ;
}
2022-09-13 01:09:30 -04:00
}
2022-09-07 19:07:08 -04:00
enum a b s t r a c t SongTimeFormat ( S t r i n g ) f r o m S t r i n g t o S t r i n g
{
2023-01-22 19:55:30 -05:00
var TICKS = " t i c k s " ;
var FLOAT = " f l o a t " ;
var MILLISECONDS = " m s " ;
2022-09-07 19:07:08 -04:00
}