@@ -16,10 +16,12 @@ public class SimaiParser : SimaiBaseVisitor<object>, IParser
1616
1717 private Rational now = 0 ;
1818 private Rational step = new ( 1 , 4 ) ;
19+ private decimal ? absoluteTimeStep = null ; // 此项必须和step本体一起更改
1920
2021 private ParserRuleContext ? currContext ; // 供调试报错AddAlert函数使用
2122 private Note ? currNote ; // 用于在部分visitor之间传递额外的参数,如visitDuration、visitSlideBody等,都需要Note对象作为参数传入的情况
2223 private readonly List < string > extraModifiers = [ ] ;
24+ private bool absoluteTimeStepWarned = false ; // 用于确保Warning只打印一次
2325
2426 public SimaiParser ( bool bigTouch = false , bool isUtage = false , int clockCount = 4 )
2527 {
@@ -108,11 +110,22 @@ public sealed override object VisitChart(P.ChartContext context)
108110 foreach ( var notations in context . notations ( ) )
109111 {
110112 VisitNotations ( notations ) ;
113+ if ( chart . BpmList . Count == 0 ) AddDefaultBpm ( ) ;
111114 now = ( now + step ) . CanonicalForm ;
112115 }
113116 return true ;
114117 }
115118
119+ private void AddDefaultBpm ( )
120+ {
121+ // 谱面开头还没看到BPM,就看到音符(或绝对时间标记)了。这在MA2中是不允许的,MA2的BPM必须是从一开头就开始指定。
122+ // 因此,我们打印一个警告,然后帮用户补一个60。
123+ const int defaultStartBpm = 60 ;
124+ AddAlert ( Warning , string . Format ( Locale . StartNoBpm , defaultStartBpm ) ) ;
125+ Utils . Assert ( now == 0 , "现在已经不是开头了??" ) ;
126+ chart . BpmList . Add ( new BPM ( now , defaultStartBpm ) ) ;
127+ }
128+
116129 public sealed override object VisitNotations ( P . NotationsContext context )
117130 { // 形如 (120){4}1/1 算作一组notations
118131 foreach ( var child in context . children ?? [ ] )
@@ -121,33 +134,48 @@ public sealed override object VisitNotations(P.NotationsContext context)
121134 {
122135 VisitBpmTag ( bpmTag ) ;
123136 }
124- else if ( child is P . AbsulouteStepTagContext absoluteStepTag )
125- {
126- VisitAbsulouteStepTag ( absoluteStepTag ) ;
127- }
128137 else if ( child is P . MetTagContext metTag )
129138 {
130139 VisitMetTag ( metTag ) ;
131140 }
132- else if ( child is P . NoteGroupContext noteGroup )
141+ else
133142 {
134- VisitNoteGroup ( noteGroup ) ;
143+ if ( chart . BpmList . Count == 0 ) AddDefaultBpm ( ) ;
144+ if ( child is P . AbsulouteStepTagContext absoluteStepTag )
145+ {
146+ VisitAbsulouteStepTag ( absoluteStepTag ) ;
147+ }
148+ else if ( child is P . NoteGroupContext noteGroup )
149+ {
150+ VisitNoteGroup ( noteGroup ) ;
151+ }
135152 }
136153 }
137154 return true ;
138155 }
139156
140157 public sealed override object VisitAbsulouteStepTag ( P . AbsulouteStepTagContext context )
141158 {
142- AddAlert ( Error , Locale . AbsoluteStepNotImplemented , context ) ;
143- throw new ConversionException ( alerts ) ;
159+ if ( ! absoluteTimeStepWarned )
160+ {
161+ AddAlert ( Warning , string . Format ( Locale . AbsoluteStepUsed , context . GetText ( ) ) , context ) ;
162+ absoluteTimeStepWarned = true ;
163+ }
164+ absoluteTimeStep = ( decimal ) VisitNumber ( context . number ( ) ) ;
165+ var currentBpm = chart . BpmList . Last ( ) . Bpm ;
166+ step = ( Rational ) absoluteTimeStep / ( 240 / ( Rational ) currentBpm ) ;
167+ return true ;
144168 }
145169
146170 public sealed override object VisitBpmTag ( P . BpmTagContext context )
147171 {
148172 currContext = context ;
149173 var bpm = ( decimal ) VisitNumber ( context . number ( ) ) ;
150174 chart . BpmList . Add ( new BPM ( now , bpm ) ) ;
175+ if ( absoluteTimeStep != null )
176+ { // 如果当前处于绝对时间step模式下,则bpm变化也会引起小节制step的变化,更新之。
177+ step = ( Rational ) absoluteTimeStep / ( 240 / ( Rational ) bpm ) ;
178+ }
151179 return true ;
152180 }
153181
@@ -156,6 +184,7 @@ public sealed override object VisitMetTag(P.MetTagContext context)
156184 currContext = context ;
157185 var quaver = int . Parse ( context . @int ( ) . GetText ( ) ) ;
158186 step = new Rational ( 1 , quaver ) ;
187+ absoluteTimeStep = null ;
159188 return true ;
160189 }
161190
0 commit comments