Skip to content
Merged
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
14 changes: 14 additions & 0 deletions Packages/ShowRecorderKit/Sources/Recording/TakeSequencer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ public final class TakeSequencer {
public private(set) var takeStartedAt: Date?
/// Whether the recorder was disarmed for sitting idle, so coming back should Arm it again.
public private(set) var disarmedForIdle = false
/// A re-Arm for a changed setting is waiting for a Take to finish saving (#932).
private var settingsArmWaiting = false
/// What to do about audio interruptions, and the banner they leave.
public private(set) var interruptions = InterruptionPolicy()
@ObservationIgnored private var toldInputStopped = false
Expand Down Expand Up @@ -339,6 +341,18 @@ public final class TakeSequencer {
/// The Pre-roll length or the sample rate choice changed: re-Arm so the input uses it. Only between Takes; a
/// running Take keeps what it started with (Show-Safe Promise).
public func settingsChanged() {
// A Take being saved is still the Take: re-Arm once it is on disk, or the Arm would fail with a false "Couldn't
// start" after a clean Take (#932). Several changes during one save make one Arm, as each Arm restarts the input.
if settingsArmWaiting { return }
if recorder.isFinishing {
settingsArmWaiting = true
Task { [self] in
await recorder.waitWhileFinishing()
settingsArmWaiting = false
settingsChanged()
}
return
}
if recorder.isArmed, !recorder.isRecording { arm() }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,32 @@ struct AsyncStopOffMainTests {
#expect(sequencer.armError == nil)
}

@Test("Pre-roll or sample rate changes while the Take is being saved show no error, and the input re-Arms once after the save (#932)")
func settingsChangedWhileFinishing() async throws {
let gate = TestGate()
nonisolated(unsafe) var gated: GatedStem?
let recorder = makeRecorder(in: root, asyncStop: true, gate: gate, stem: { gated = $0 })
let source = FakeInputDevices([("xr18", 1)])
let sequencer = TakeSequencer(recorder: recorder, devices: source, capsAt48k: { false })
sequencer.arm()
let audio = try #require(source.opened.last)
let armsBefore = source.opened.count
let stop = try await startBlockedStop(recorder, audio: audio, gated: { gated })

sequencer.settingsChanged()
sequencer.settingsChanged()
sequencer.settingsChanged()
#expect(sequencer.armError == nil, "no false \"Couldn't start\" after a clean Take: \(sequencer.armError ?? "")")
gate.open()
try await stop.value

try await waitUntil { source.opened.count > armsBefore }
try await Task.sleep(for: .milliseconds(200))
#expect(sequencer.armError == nil)
#expect(source.opened.count == armsBefore + 1, "three changes, one Arm")
#expect(recorder.isArmed)
}

@Test("A gain change buffered during the save doesn't write the Show.json of a Drive Stop gave up on")
func gainChangeSkipsAbandonedDrive() async throws {
let drive = root.appending(path: "Drive")
Expand Down
Loading