Skip to content

Commit ebd7534

Browse files
committed
add LoopPlayTiming
1 parent 22a2f5b commit ebd7534

File tree

8 files changed

+124
-7
lines changed

8 files changed

+124
-7
lines changed

OngekiFumenEditor/App.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@
131131
<setting name="RecoveryCurrentTimeAfterExecuteAction" serializeAs="String">
132132
<value>False</value>
133133
</setting>
134+
<setting name="LoopPlayTiming" serializeAs="String">
135+
<value>False</value>
136+
</setting>
134137
</OngekiFumenEditor.Properties.EditorGlobalSetting>
135138
<OngekiFumenEditor.Properties.AudioPlayerToolViewerSetting>
136139
<setting name="ResampleSize" serializeAs="String">

OngekiFumenEditor/Kernel/Audio/DefaultCommonImpl/Sound/DefaultFumenSoundPlayer.cs

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,19 @@ namespace OngekiFumenEditor.Kernel.Audio.DefaultCommonImpl.Sound
2323
[Export(typeof(IFumenSoundPlayer))]
2424
public partial class DefaultFumenSoundPlayer : PropertyChangedBase, IFumenSoundPlayer, IDisposable
2525
{
26+
private record MeterAction(TimeSpan Time, TimeSpan BeatInterval, bool isSkip);
27+
2628
private IntervalTree<TimeSpan, DurationSoundEvent> durationEvents = new();
2729
private HashSet<DurationSoundEvent> currentPlayingDurationEvents = new();
2830
private object locker = new object();
2931

3032
private LinkedList<SoundEvent> events = new();
3133
private LinkedListNode<SoundEvent> itor;
3234

35+
private LinkedList<MeterAction> meterActions = new();
36+
private LinkedListNode<MeterAction> meterActionsItor;
37+
private int currentMeterHitCount = 0;
38+
3339
private AbortableThread thread;
3440

3541
private IAudioPlayer player;
@@ -296,11 +302,32 @@ void AddDurationSound(SoundControl sound, TGrid tGrid, TGrid endTGrid, int loopI
296302
foreach (var durationEvent in durationList)
297303
durationEvents.Add(durationEvent.Time, durationEvent.EndTime, durationEvent);
298304
itor = null;
305+
306+
meterActions.Clear();
307+
if (EditorGlobalSetting.Default.LoopPlayTiming)
308+
{
309+
var oneTGrid = new TGrid(1, 0);
310+
311+
var timeSignatureList = fumen.MeterChanges.GetCachedAllTimeSignatureUniformPositionList(fumen.BpmList);
312+
foreach (var timeSignature in timeSignatureList)
313+
{
314+
var beatCount = timeSignature.meter.BunShi;
315+
var isSkip = beatCount == 0;
316+
var beatInterval = (isSkip ? default :
317+
TimeSpan.FromMilliseconds(MathUtils.CalculateBPMLength(TGrid.Zero, oneTGrid, timeSignature.bpm.BPM))
318+
/ beatCount);
319+
320+
var action = new MeterAction(timeSignature.audioTime, beatInterval, isSkip);
321+
meterActions.AddLast(action);
322+
}
323+
}
324+
meterActionsItor = default;
325+
currentMeterHitCount = 0;
299326
}
300327

301328
private void UpdateInternal(CancellationToken token)
302329
{
303-
if (itor is null || player is null || token.IsCancellationRequested)
330+
if ((itor is null && meterActionsItor is null) || player is null || token.IsCancellationRequested)
304331
return;
305332
if (!IsPlaying)
306333
{
@@ -311,7 +338,7 @@ private void UpdateInternal(CancellationToken token)
311338

312339
var currentTime = player.CurrentTime;
313340

314-
//播放普通音乐
341+
//播放物件音效
315342
while (itor is not null)
316343
{
317344
var nextBeatTime = itor.Value.Time.TotalMilliseconds;
@@ -326,6 +353,47 @@ private void UpdateInternal(CancellationToken token)
326353
break;
327354
}
328355

356+
//播放节拍器
357+
while (meterActionsItor is not null)
358+
{
359+
var nextActionItor = meterActionsItor.Next;
360+
361+
//检查当前是否有效
362+
if (meterActionsItor.Value.isSkip)
363+
{
364+
meterActionsItor = nextActionItor;
365+
currentMeterHitCount = 0;
366+
continue;
367+
}
368+
369+
var nextBeatTime = meterActionsItor.Value.Time +
370+
meterActionsItor.Value.BeatInterval * currentMeterHitCount;
371+
372+
//检查是否超过下一个
373+
if (nextActionItor != null)
374+
{
375+
if (nextBeatTime > nextActionItor.Value.Time)
376+
{
377+
meterActionsItor = nextActionItor;
378+
currentMeterHitCount = 0;
379+
continue;
380+
}
381+
}
382+
383+
//没超过就检查了
384+
var ct = currentTime.TotalMilliseconds - nextBeatTime.TotalMilliseconds;
385+
if (ct >= 0)
386+
{
387+
Log.LogDebug($"currentMeterHitCount:{currentMeterHitCount}, nextBeatTime:{nextBeatTime}, diff:{ct:F2}ms, meterActionsItor:{meterActionsItor.Value}");
388+
if (editor.IsDesignMode && cacheSounds.TryGetValue(SoundControl.ClickSE, out var soundPlayer))
389+
soundPlayer.PlayOnce();
390+
currentMeterHitCount++;
391+
}
392+
else
393+
break;
394+
}
395+
396+
//检查循环音效
329397
lock (locker)
330398
{
331399
var queryDurationEvents = durationEvents.Query(currentTime);
@@ -340,7 +408,6 @@ private void UpdateInternal(CancellationToken token)
340408
soundPlayer.PlayLoop(durationEvent.LoopId, initPlayTime);
341409

342410
currentPlayingDurationEvents.Add(durationEvent);
343-
344411
}
345412
}
346413
}
@@ -402,6 +469,16 @@ public void Seek(TimeSpan msec, bool pause)
402469
{
403470
Pause();
404471
itor = events.Find(events.FirstOrDefault(x => msec < x.Time));
472+
meterActionsItor = meterActions.Find(meterActions.LastOrDefault(x => msec >= x.Time));
473+
if (meterActionsItor is null)
474+
currentMeterHitCount = 0;
475+
else
476+
{
477+
if (meterActionsItor.Value.isSkip)
478+
currentMeterHitCount = 0;
479+
else
480+
currentMeterHitCount = (int)((msec - meterActionsItor.Value.Time) / meterActionsItor.Value.BeatInterval);
481+
}
405482

406483
if (!pause)
407484
PlayInternal();
@@ -443,6 +520,9 @@ public void Play()
443520
if (player is null)
444521
return;
445522
itor = itor ?? events.First;
523+
meterActionsItor = meterActionsItor ?? meterActions.First;
524+
currentMeterHitCount = 0;
525+
446526
PlayInternal();
447527
}
448528

OngekiFumenEditor/Modules/FumenVisualEditor/Models/EditorSetting.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,18 @@ public bool AdjustPastedObjects
232232
}
233233
}
234234

235+
private bool loopPlayTiming = Properties.EditorGlobalSetting.Default.LoopPlayTiming;
236+
public bool LoopPlayTiming
237+
{
238+
get => loopPlayTiming;
239+
set
240+
{
241+
loopPlayTiming = Properties.EditorGlobalSetting.Default.LoopPlayTiming = value;
242+
RequestSave();
243+
NotifyOfPropertyChange(() => loopPlayTiming);
244+
}
245+
}
246+
235247
public enum TimeFormat
236248
{
237249
TGrid,
@@ -308,6 +320,9 @@ private void Default_PropertyChanged(object sender, System.ComponentModel.Proper
308320
case nameof(Properties.EditorGlobalSetting.AdjustPastedObjects):
309321
adjustPastedObjects = Properties.EditorGlobalSetting.Default.AdjustPastedObjects;
310322
break;
323+
case nameof(Properties.EditorGlobalSetting.LoopPlayTiming):
324+
loopPlayTiming = Properties.EditorGlobalSetting.Default.LoopPlayTiming;
325+
break;
311326
default:
312327
Log.LogWarn($"unknown Properties.EditorGlobalSetting property changed : {e.PropertyName}");
313328
break;

OngekiFumenEditor/Modules/FumenVisualEditorSettings/ViewModels/OgkiFumenListBrowserViewModel.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using OngekiFumenEditor.Modules.FumenVisualEditor.Kernel;
55
using OngekiFumenEditor.Modules.FumenVisualEditor.Models;
66
using OngekiFumenEditor.Modules.FumenVisualEditor.ViewModels;
7+
using OngekiFumenEditor.Properties;
78
using OngekiFumenEditor.Utils;
89
using System.ComponentModel;
910
using System.ComponentModel.Composition;
@@ -78,9 +79,7 @@ private void OnActivateEditorChanged(FumenVisualEditorViewModel @new, FumenVisua
7879
private void OnEditorPropertyChanged(object sender, PropertyChangedEventArgs e)
7980
{
8081
if (e.PropertyName == nameof(FumenVisualEditorViewModel.Setting))
81-
{
8282
Setting = Editor?.Setting;
83-
}
8483
}
8584
}
8685
}

OngekiFumenEditor/Modules/FumenVisualEditorSettings/Views/FumenVisualEditorSettingsView.xaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@
146146
SelectedValue="{Binding Setting.DisplayTimeFormat, Converter={StaticResource EnumToStringConverter}}">
147147
</ComboBox>
148148
</Grid>
149+
<CheckBox
150+
Margin="0,10,0,2"
151+
Content="打开节拍器(仅编辑模式)"
152+
IsChecked="{Binding Setting.LoopPlayTiming}">
153+
</CheckBox>
149154
</StackPanel>
150155
</ScrollViewer>
151156
</UserControl>

OngekiFumenEditor/Properties/AudioSetting.Designer.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

OngekiFumenEditor/Properties/EditorGlobalSetting.Designer.cs

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

OngekiFumenEditor/Properties/EditorGlobalSetting.settings

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,8 @@
7777
<Setting Name="RecoveryCurrentTimeAfterExecuteAction" Type="System.Boolean" Scope="User">
7878
<Value Profile="(Default)">False</Value>
7979
</Setting>
80+
<Setting Name="LoopPlayTiming" Type="System.Boolean" Scope="User">
81+
<Value Profile="(Default)">False</Value>
82+
</Setting>
8083
</Settings>
8184
</SettingsFile>

0 commit comments

Comments
 (0)