Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Source/AutoRecorder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ private static void BeginRun(Level level) {
// The selected mode is run-wide; rhythm exceptions are attached to individual clips.
reconstructBgm = MicroblocksQolUtilsModule.Settings.BgmMode == BgmRecordingMode.SfxOnlyWithPostMix;
recordingRoomName = null;
recordingRoomBgmFollowsVideo = MicroblocksQolUtilsModule.Settings.RecordingBgmFollowsVideo;
}

private static void StartRunRecording(Level level) {
Expand Down Expand Up @@ -968,7 +969,8 @@ private static void ObserveRoom(Level level) {
DeathReplayPrefix.Add(deathClip);

recordingRoomName = room;
recordingRoomBgmFollowsVideo = RhythmMapDetector.IsRhythmSensitive(level.Session.MapData, room);
recordingRoomBgmFollowsVideo = MicroblocksQolUtilsModule.Settings.RecordingBgmFollowsVideo
|| RhythmMapDetector.IsRhythmSensitive(level.Session.MapData, room);
if (branchActive) {
branchStartSeconds = fullTime;
branchMusicStart = MusicPosition.Read();
Expand Down
7 changes: 7 additions & 0 deletions Source/QolSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,13 @@ public AutoRecordingMode AutomaticRecording {
[DefaultValue(false)]
public bool RecordingKeepFailedAttempts { get; set; }

// Rhythm-sensitive rooms already read the real source time so music keeps its
// on-screen timing. Outside those rooms the BGM is reconstructed across cuts and
// therefore keeps playing while the video jumps; this opt-in asks for the strict
// behaviour (jump with the video) in every room.
[DefaultValue(false)]
public bool RecordingBgmFollowsVideo { get; set; }

[DefaultValue(60)]
[SettingRange(30, 120)]
public int RecordingFrameRate { get; set; } = 60;
Expand Down
8 changes: 8 additions & 0 deletions Source/QolSettingsOverlay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1680,6 +1680,10 @@ SettingRow Golden(SettingRow row) {
row.IsVisible = () => settings.AutomaticRecording == AutoRecordingMode.Golden;
return row;
}
SettingRow BgmRebuildOnly(SettingRow row) {
row.IsVisible = () => settings.BgmMode == BgmRecordingMode.SfxOnlyWithPostMix;
return row;
}
Group(RecorderSettingsSection.Automatic,
EnumRow("自动录制", () => settings.AutomaticRecording, value => settings.AutomaticRecording = value),
Golden(EnumRow("成功时录到", () => settings.GoldenRecordingEnd, value => settings.GoldenRecordingEnd = value)),
Expand All @@ -1696,6 +1700,10 @@ SettingRow Golden(SettingRow row) {
Toggle("切面自动存档(保持视频连续)", () => settings.RecordingAutoSaveOnTransition,
value => settings.RecordingAutoSaveOnTransition = value),
EnumRow("BGM 拼接", () => settings.BgmMode, value => settings.BgmMode = value),
// Only the reconstruction mode has a music cursor that can either follow the
// source time or keep playing across cuts; game-mix BGM already follows the video.
BgmRebuildOnly(Toggle("BGM 跟随视频剪辑", () => settings.RecordingBgmFollowsVideo,
value => settings.RecordingBgmFollowsVideo = value)),
Toggle("剪辑冻结帧", () => settings.RecordingRemoveFreezeFrames, value => settings.RecordingRemoveFreezeFrames = value, highlightWhenOn: true),
Range("录制帧率", () => settings.RecordingFrameRate, value => settings.RecordingFrameRate = value,
30, 120, 30, value => $"{value} FPS"),
Expand Down
2 changes: 1 addition & 1 deletion Tests/Recorder/Stubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public class QolSettings {
public AutoRecordingMode AutomaticRecording;
public bool AutoRecorderEnabled => AutomaticRecording != AutoRecordingMode.Off;
public bool DeathReplayEnabled, RecordingRemoveFreezeFrames, RecordingEditingEnabled = true,
RecordingKeepPausedFrames, RecordingKeepFailedAttempts;
RecordingKeepPausedFrames, RecordingKeepFailedAttempts, RecordingBgmFollowsVideo;
public int DeathReplayBufferSeconds=30, RecordingRetentionCount, AutoRecordingRetentionCount, DeathReplayRetentionCount;
public string RecordingDirectory="";
public GoldenRecordingEnd GoldenRecordingEnd;
Expand Down
19 changes: 19 additions & 0 deletions Tests/Recording.Policy/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,25 @@ static void SetField(object owner, string name, object value) => owner.GetType()
Call("BeginRun", level);
Check(!State<bool>("reconstructBgm"), "explicit game-mix mode was ignored");

// Forcing "BGM follows the video" makes every room read source time, so clips split by
// room/music cuts jump with the picture instead of letting the reconstructed BGM run on.
// The default (false) must keep the automatic per-room policy asserted above.
settings.BgmMode = BgmRecordingMode.SfxOnlyWithPostMix;
settings.RecordingBgmFollowsVideo = true;
session.Level = normal.Name;
Call("BeginRun", level);
Check(State<bool>("recordingRoomBgmFollowsVideo"), "forced BGM policy was not applied before the first room snapshot");
foreach (var room in new[] { normal, sensitive, normal, triggered }) {
session.Level = room.Name;
Call("ObserveRoom", level);
Check(State<bool>("recordingRoomBgmFollowsVideo"), $"forced BGM policy ignored in room {room.Name}");
}
settings.RecordingBgmFollowsVideo = false;
session.Level = normal.Name;
Call("BeginRun", level);
Call("ObserveRoom", level);
Check(!State<bool>("recordingRoomBgmFollowsVideo"), "clearing the forced BGM policy did not restore the per-room policy");

// Exercise actual clip creation for BOTH sinks with a deterministic stopped clock.
// No native handle, files, hooks, Steam process or game settings are touched.
object Recording(string path) {
Expand Down
Loading