I need help with fixing this issue that I am facing. The app is supposed to queue audio files, so when audio is playing from a checkpoint, then the next checkpoint is reached and triggered, the expected behavior is that it will not immediately initiate any change. This is not what is happening though, currently the closed captions are being triggered to the next checkpoint’s closed captions. This causes different closed captions to be playing from the audio that is playing. Here is part of the "TourSystem" class in the file "System.swift":
 func playAudioForCheckpoint(_ checkpoint: Checkpoint) {

    let audioSet = checkpoint.audio

    print("now playing \(audioSet.foreground)")

    var result: PlayFileStatus?
    //TODO see if we can make this queued audio logic work. it works incoonsistently. see life track checkpoints 1-3 for example of inconsistent queing behaviour
    if alwaysQueueAudio {
      result = AudioSystem.shared.queueFileInForeground(fileName: audioSet.foreground)
    } else if let queueAudio: Bool = checkpoint.queueAudio,
      queueAudio == true
    {
      result = AudioSystem.shared.queueFileInForeground(fileName: audioSet.foreground)
    } else {
      AudioSystem.shared.foregroundAudioNode.stop()
      AudioSystem.shared.foregroundAudioNode.reset()
      result = AudioSystem.shared.playFileInForeground(
        fileName: audioSet.foreground, captionFile: audioSet.closedCaption)

    }

    if result != nil {
      //            handlePlayResult(result: result!)
      //            foregroundAudioLabel.text = "Fore Audio: Error"
      state.currentForegroundAudio = "Error"
      print("failed to play foregrouned audio due to error \(result)")
    } else {
      //            foregroundAudioLabel.text = "Fore Audio: \(audioSet.foreground)"
      //            tour.state.currentForegroundAudio = audioSet.foreground
      state.currentForegroundAudio = audioSet.foreground
      print("playing foreground audio file \(audioSet.foreground)")
    }

    let currentBackgroundAudioFile: AudioFileID = AudioSystem.shared.backgroundAudioFile
    if let backgroundAudioFile: AudioFileID = audioSet.background,
      backgroundAudioFile != currentBackgroundAudioFile
    {
      AudioSystem.shared.backgroundAudioNode.stop()
      result = AudioSystem.shared.playFileInBackground(fileName: backgroundAudioFile)
      if result != nil {
        //                handlePlayResult(result: result!)
        //                backgroundAudioLabel.text = "Back Audio: Error"
        //                tour.state.currentBackgroundAudio = "error"
        state.currentBackgroundAudio = ""
        print(
          "Could not playing background audio file \(backgroundAudioFile) due to error \(result)")
      } else {
        //                backgroundAudioLabel.text = "Back Audio: \(backgroundAudioFile)"
        //                tour.state.currentBackgroundAudio = backgroundAudioFile
        state.currentBackgroundAudio = backgroundAudioFile
        print("playing background audio file \(backgroundAudioFile)")
      }
    } else {
      print(
        "not setting new background audio because checkpoint does not specify any background audio")
    }

  }

  func initializeSRTParser() -> SRTParser? {
    let audioFileName = AudioSystem.shared.foregroundAudioFile

    let srtFileName = (audioFileName as NSString).deletingPathExtension
    print("Attempting to load SRT file: \(audioFileName).srt")
    print("Attempting to load SRT file: \(srtFileName).srt")

    let srtURL =
      Bundle.main.url(forResource: srtFileName, withExtension: "srt")
      ?? Bundle(for: type(of: self)).url(forResource: srtFileName, withExtension: "srt")

    if let url = srtURL {
      print("SRT file found at: \(url.path)")
      return SRTParser(fileURL: url)
    } else {
      print("SRT file not found for: \(srtFileName).srt")
      return nil
    }
  }
  private var captionTimerID: UUID?
  private var captionStartTime: Date?
  var isCaptionRunning: Bool = false
  var currentCaptionFile: String?

  func startClosedCaptions(time: Double, file: String) {
    print("DEBUG: startClosedCaptions called with file: \(file)")

    // Check if captions are already running for this file
    if isCaptionRunning && currentCaptionFile == file {
      print("DEBUG: Captions already running for \(file)")
      return
    }

    stopClosedCaptions()
    clearClosedCaptions()

    currentCaptionFile = file
    isCaptionRunning = true
    let srtFileName = (file as NSString).deletingPathExtension

    currentSRTParser = initializeSRTParser()
    guard let parser = currentSRTParser else {
      print("DEBUG: Parser not initialized.")
      return
    }

    guard AudioSystem.shared.foregroundAudioNode.isStarted else {
      print("Audio not started, cannot start captions.")
      return
    }

    let maxDuration = AudioSystem.shared.foregroundAudioDuration
    let timerID = UUID()
    captionTimerID = timerID
    print("DEBUG: Starting caption timer with ID: \(timerID)")

    captionStartTime = Date().addingTimeInterval(-time)

    captionTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] timer in
      guard let self = self, self.isCaptionRunning, let startTime = self.captionStartTime else {
        timer.invalidate()
        return
      }

      let currentTime = Date().timeIntervalSince(startTime)
      if AudioSystem.shared.foregroundAudioNode.isStarted && currentTime < maxDuration {
        if let caption = parser.getCaption(for: currentTime) {
          DispatchQueue.main.async {
            self.gameViewController?.closedCaptionsLabel.text = caption
          }
        }
      } else {
        print(
          "Stopping captions. Audio playing: \(AudioSystem.shared.foregroundAudioNode.isStarted)")
        self.stopClosedCaptions()
      }
    }
  }

  func stopClosedCaptions() {
    print("DEBUG: stopClosedCaptions called")
    if let timerID = captionTimerID {
      print("DEBUG: Stopping caption timer with ID: \(timerID)")
    }
    isCaptionRunning = false
    currentCaptionFile = nil
    captionTimer?.invalidate()
    captionTimer = nil
    captionStartTime = nil
    captionTimerID = nil
    currentSRTParser = nil
    DispatchQueue.main.async { [weak self] in
      self?.gameViewController?.closedCaptionsLabel.text = ""
    }
  }

  func clearClosedCaptions() {
    DispatchQueue.main.async { [weak self] in
      self?.gameViewController?.closedCaptionsLabel.text = ""
    }
  }

  init() {
    state = TourState(
      currentCheckpointIndex: 0, completedCheckpoints: [], currentPath: AmbroseTour.startPath,
      completedPaths: [], currentForegroundAudio: "none", currentBackgroundAudio: "none",
      currentClosedCaptionFile: "none", timeArrivedAtLastCheckpoint: Date())
    //        let currentCheckpoint = state.currentPath.checkpoints[state.currentCheckpointIndex]
    //        AudioSystem.shared.playFileInForeground(fileName: currentCheckpoint.audio.foreground)
    //        if let backgroundAudio:AudioFileID = currentCheckpoint.audio.background {
    //            AudioSystem.shared.playFileInBackground(fileName: backgroundAudio)
    //        }

  }

  func transit(_ transition: Transition) {
    switch transition {
    case .nextCheckpoint:
      nextCheckpoint()
    case .startPath(let path):
      state.currentPath = path
      state.currentCheckpointIndex = 0
      state.timeArrivedAtLastCheckpoint = Date()
      print("we've started a new path: \(path.name)")
    case .endTour:
      print("we're ending the tour")
    case .contingency:
      print("were handling a contingency (?)")
    }
  }

  //    var strictMode:Bool
  //
  //    func loadCheckpoint(_ checkpoint:Checkpoint) {
  //
  //    }

  private func playTimeoutAudio(_ audioFile: AudioFileID) {
    print("playing timeout audio file \(audioFile)")
    AudioSystem.shared.foregroundAudioNode.stop()
    AudioSystem.shared.foregroundAudioNode.reset()

    var result = AudioSystem.shared.queueFileInForeground(fileName: audioFile)  //TODORELEASE make sure queueing is right beahviour for timeout audio
    if result != nil {
      //            handlePlayResult(result: result!)
      //            foregroundAudioLabel.text = "Fore Audio: Error"
      switch result {
      case .noSuchFile(let filename):
        state.currentForegroundAudio = "Error: File does not exist. Try setting it."
      default: state.currentForegroundAudio = "Error"
      }

      print("failed to play foregrouned audio due to error \(result)")
    } else {
      //            foregroundAudioLabel.text = "Fore Audio: \(audioSet.foreground)"
      //            tour.state.currentForegroundAudio = audioSet.foreground
      state.currentForegroundAudio = audioFile
      print("playing foreground audio file \(audioFile)")
    }
  }

  private var timeoutContingencyTimer: Timer?

  private func setupTimeoutContingency(timeout: TimeInterval, audioFile: AudioFileID) {
    print(
      "settingup timeout contingency for checkpoint to activate in \(timeout) and play \(audioFile)"
    )

    let timeoutTimerSetupDelay: TimeInterval = 1
    //        let foreAud = AudioSystem.shared.foregroundAudioNode
    //        foreAud.buffer

    Timer.scheduledTimer(withTimeInterval: timeoutTimerSetupDelay, repeats: false) { _ in
      let remainingForegroundAudioDuration =
        AudioSystem.shared.foregroundAudioDuration
        - AudioSystem.shared.foregroundAudioNode.getCurrentTime()
      print(
        "setting up timeout contingency. specified timeout time is \(timeout) and the current foreground audio has duration \(AudioSystem.shared.foregroundAudioDuration)  and current playtime \(AudioSystem.shared.foregroundAudioNode.getCurrentTime()) and so still has \(remainingForegroundAudioDuration)s left, so we'll play the timeout contingency audio file \"\(audioFile)\" in \(timeout + remainingForegroundAudioDuration - timeoutTimerSetupDelay)s"
      )
      // Check if we need to reset the time, and do it only once
      //TODORELEASE NATE Check prior with beacons, might be unnecessary to do
      // Reinitialize the parser each time
      TourSystem.srtParser = self.initializeSRTParser()

      guard let parser = TourSystem.srtParser else {
        print("Parser not initialized.")
        return
      }

      let isIntro003 = AudioSystem.shared.foregroundAudioFile == "003 Intro.mp3"
      var captionStartTime: TimeInterval = 4
      var hasStartedCaptions = false

      self.timeoutContingencyTimer = Timer.scheduledTimer(
        withTimeInterval: 0.1,  // Check every 0.1 seconds
        repeats: true,
        block: { [weak self] timer in
          guard let self = self else {
            timer.invalidate()
            return
          }

          let currentTime = AudioSystem.shared.foregroundAudioNode.getCurrentTime()

          // if isIntro003 {
          //   if !hasStartedCaptions && currentTime > 0 {
          //     captionStartTime = currentTime
          //     hasStartedCaptions = true
          //     print("Starting captions for 003 Intro.mp3 at time: \(captionStartTime)")
          //   }
          //   var maxDuration = AudioSystem.durationOfAudio(audioFileName: "003 Intro.mp3")
          //   var isPlaying = AudioSystem.shared.foregroundAudioNode.isStarted
          //   if isPlaying && hasStartedCaptions && currentTime < maxDuration! {
          //     let adjustedTime = currentTime - captionStartTime
          //     if let caption = parser.getCaption(for: adjustedTime) {
          //       print("Caption found for adjusted time \(adjustedTime): \(caption)")
          //       DispatchQueue.main.async {
          //         self.gameViewController?.closedCaptionsLabel.text = caption
          //       }
          //     }
          //   }
          // }

          // Check if it's time to play the contingency audio
          if currentTime >= timeout + remainingForegroundAudioDuration - timeoutTimerSetupDelay {
            self.playTimeoutAudio(audioFile)
            timer.invalidate()
          }
        })

      self.timeoutContingencyTimer = Timer.scheduledTimer(
        withTimeInterval: timeout + remainingForegroundAudioDuration - timeoutTimerSetupDelay,
        repeats: false,
        block: { timer in
          self.playTimeoutAudio(audioFile)
          timer.invalidate()
        })
    }

  }
  private func setupOfftrackContingency(localization: Localization, audioFile: AudioFileID) {
    //        print("dummy placeholder nonfunctional offtrack contingency handler setup function")
    //        print("setting up offtrack contingency dummy code")

    //        VALocation.shared.react(to: .didExitRegion(3)) { change in
    //            print("we've gone off track, according to \(change), so we're printing this")
    //        }
    //        VALocation.shared.event(.didExitRegion(3))
    Localizer.shared.on(localization: localization) {
      AudioSystem.shared.queueFileInForeground(fileName: audioFile)  //TODO handle result of this call, handle possible errors //TODORELEASE make sure queing behaviour is appropriate
      Localizer.shared.stopReacting(to: localization.id)  //TODO determine if it makes sense to remove this reaction or simply to pause if for a while and then reenable it for later?
    }
  }

  //TODO NATE look at this and decide if it needs to be extended or shortend.
  private let delayForOfftrackContigencies: TimeInterval = 10  //TODOrelease make sure this is an appropriate value
  private func handleContingencies(_ checkpoint: Checkpoint) {
    if let (timeout, timeoutAudio): (TimeInterval, AudioFileID) = Util.getTimeoutContingency(
      checkpoint: checkpoint)
    {
      setupTimeoutContingency(timeout: timeout, audioFile: timeoutAudio)
    }

    Timer.scheduledTimer(withTimeInterval: delayForOfftrackContigencies, repeats: false) { _ in
      if let offtrackConts: [(Localization, AudioFileID)] = Util.getOfftrackContingency(
        checkpoint: checkpoint)
      {
        for (localization, offTrackAudio) in offtrackConts {
          self.setupOfftrackContingency(
            localization: localization, audioFile: offTrackAudio)
        }
      }
    }
  }

  func setTriggerForNextCheckpoint() {
    print(
      "DEBUG: setTriggerForNextCheckpoint called for checkpoint index: \(state.currentCheckpointIndex)"
    )
    guard state.currentPath.checkpoints.count - 1 > state.currentCheckpointIndex else {
      print(
        "already at last checkpoint in the path, so setting up trigger for end of path conditions")
      //this can be special path specific handling code, hard coded, because we only have 4 possibilities
      //            print("error: not implemented")

      //TODO handle end of path special cases here
      //Case:  end of start path
      let currentCheckpoint = state.currentPath.checkpoints[state.currentCheckpointIndex]
      let currentPath = state.currentPath
      switch currentPath.name {
      case "Start Path":
        let deathTrackStartLocalization = AmbroseLocations.southSideAlleyway
        let lifeTrackStartLocalization = AmbroseLocations.lobbyStairsBottom

        print(
          "we've reached the last checkpoint of the start path; setting up special triggers for life and death paths"
        )
        var dstart = false
        Localizer.shared.on(localization: deathTrackStartLocalization) {
          guard !dstart else { return }
          dstart = true
          print(
            "we've reached the trigger location for the start of the death path, setting up death path logic"
          )
          Localizer.shared.stopReacting(to: deathTrackStartLocalization.id)
          Localizer.shared.stopReacting(to: lifeTrackStartLocalization.id)
          self.state.currentPath = AmbroseTour.deathPath
          self.state.currentCheckpointIndex = -1
          self.nextCheckpoint()
          //TODO update completedCheckpoints list
        }
        var lstart = false
        Localizer.shared.on(localization: lifeTrackStartLocalization) {
          guard !lstart else { return }
          lstart = true
          print(
            "we've reached the trigger location for the start of the life path, setting up life path logic"
          )
          Localizer.shared.stopReacting(to: deathTrackStartLocalization.id)
          Localizer.shared.stopReacting(to: lifeTrackStartLocalization.id)
          self.state.currentPath = AmbroseTour.lifePath
          self.state.currentCheckpointIndex = -1
          self.nextCheckpoint()
          //TODO update completedCheckpoints list
        }

      case "Death Path":
        print(
          "we've reached the last checkpoint of the death path; setting up special triggers for next paths"
        )
        //TODO setup handling of offtrack contingencies for end of death track, which aren't handled normally, because this is the last node of the path
        let endPathStartLocalization = AmbroseLocations.levelOneCarPark
        var estart = false
        Localizer.shared.on(localization: endPathStartLocalization) {
          guard !estart else { return }
          estart = true
          print(
            "we've reached the trigger location for start of the end path(from death path); setting up end path logic "
          )
          Localizer.shared.stopReacting(to: endPathStartLocalization.id)
          self.state.currentPath = AmbroseTour.endPath
          self.state.currentCheckpointIndex = -1
          self.nextCheckpoint()
        }

      case "Life Path":
        print(
          "we've reached the last checkpoint of the life path; setting up special triggers for next paths"
        )
        let endPathStartLocalization = AmbroseLocations.levelOneCarPark

        var estart = false
        Localizer.shared.on(localization: endPathStartLocalization) {
          guard !estart else { return }
          estart = true
          print(
            "we've reached teh trigger location for start of the end path(from life path); setting up end path logic "
          )
          Localizer.shared.stopReacting(to: endPathStartLocalization.id)
          self.state.currentPath = AmbroseTour.endPath
          self.state.currentCheckpointIndex = -1
          self.nextCheckpoint()

        }
      case "End Path":
        print(
          "we've reached the last checkpoint of the end path; setting up special triggers for next paths"
        )
        endOfTourLogic?()
      default:
        print(
          "unhandled case for setting up end of path triggers for path with name \(currentPath.name) "
        )
      }

      return
    }
    let nextCheckpointIndex = state.currentCheckpointIndex + 1
    let nextCheckpoint = state.currentPath.checkpoints[nextCheckpointIndex]

    guard let triggerLocalization: Localization = nextCheckpoint.trigger else {
      print(
        "error: next checkpoint after \(state.currentPath.checkpoints[state.currentCheckpointIndex].name) does not have a trigger beacon defined, cannot set up trigger without trigger beacon"
      )
      return
    }
    var processedNextCheckpoint = false
    //get localization for localizationID
    //        if let localization:Localization = AmbroseLocations.localization[triggerBeaconID] {
    Localizer.shared.on(localization: triggerLocalization) {
      print(
        "triggered (this basic print statement) reaction to beacon with name \(triggerLocalization.name) and with id \(triggerLocalization.id)"
      )
      guard !processedNextCheckpoint else { return }
      processedNextCheckpoint = true
      self.nextCheckpoint()
      Localizer.shared.stopReacting(to: triggerLocalization.id)  //TODO consider having previous triggers deleted by nextCheckpoint method

    }
  }

  func nextCheckpoint() {
    print("DEBUG: nextCheckpoint called for checkpoint index: \(state.currentCheckpointIndex)")
    stopClosedCaptions()
    clearClosedCaptions()
    //TODOlogic determine if it makes sense to invalidate the timeout contingency timer here or if its better to do it elsewhere
    timeoutContingencyTimer?.invalidate()
    timeoutContingencyTimer = nil
    //TODO invalidate offtrack contingency handler/"spacer", once that is implemented

    let indexOfCurrentCheckpoint: Int = state.currentCheckpointIndex
    //TODOlogic determine if it makes sense to add this here or better elsewhere
    if indexOfCurrentCheckpoint >= 0 {
      state.completedCheckpoints.append(
        state.currentPath.checkpoints[indexOfCurrentCheckpoint])
    }

    guard indexOfCurrentCheckpoint >= -1 else {
      print(
        "cannot proceed to next checkpoint, current checkpoing has invalid index \(indexOfCurrentCheckpoint), unable to determine next valid index"
      )
      return
    }

    guard indexOfCurrentCheckpoint < state.currentPath.checkpoints.count - 1 else {
      print(
        "cannot proceed to next checkpoint in path, checkpoint \"\(state.currentPath.checkpoints[state.currentCheckpointIndex].name)\" is the last checkpoint in the path"
      )
      return
    }

    let nextCheckpointIndex = indexOfCurrentCheckpoint + 1
    let nextCheckpoint = state.currentPath.checkpoints[nextCheckpointIndex]
    AudioSystem.shared.playOrQueueFileInForeground(
      fileName: nextCheckpoint.audio.foreground,
      captionFile: nextCheckpoint.audio.closedCaption
    ) { [weak self] in
      if let closedCaptionFile = nextCheckpoint.audio.closedCaption {
        self?.gameViewController?.initializeClosedCaptions(for: closedCaptionFile)
      }
    }
    playAudioForCheckpoint(nextCheckpoint)
    stopClosedCaptions()
    clearClosedCaptions()
    //        TODO Nate check this for validity
    if let closedCaptionFile = nextCheckpoint.audio.closedCaption {
      print("DEBUG: Initializing closed captions for file: \(closedCaptionFile)")
      gameViewController?.initializeClosedCaptions(for: closedCaptionFile)

      // startClosedCaptions(time: 0.0, file: closedCaptionFile)
    }

    //IMPORTANT handleContingencies must be called AFTER playAUdioForCheckpoint because it relies on calculations of the currently playing foreground audio's length as determined by the current state of the foreground audio player
    handleContingencies(nextCheckpoint)

    state.currentCheckpointIndex = nextCheckpointIndex

    state.timeArrivedAtLastCheckpoint = Date()

    print(
      "moved to next checkpoint \(state.currentPath.checkpoints[state.currentCheckpointIndex].name) in path \(state.currentPath.name) "
    )

    //TODO otherwise, setup handling of when the criteria for activation of the next checkpoint occurs
    //TODO clear previous triggers ie clear the localizer of all previously defined reactions

    //        print("how many previous rangingin events before  clearing: \(Localizer.shared.recentDetections.count)")
    Localizer.shared.clearEventsOutsideWindow(before: Date())
    //        print("how many previous rangingin events after clearing: \(Localizer.shared.recentDetections.count)")
    Localizer.shared.clearReactionMap()  //TODOrelease make sure this is safe
    setTriggerForNextCheckpoint()
    //TODO unsetup contingencies from the now passed checkpoint, ie those contingencies from the checkpoint we've left should cease.
    //WIP process new nodes contingencies, if any, setting up related subsystems, like timing subsystem or location subsystem, to notify the runner when associated contingency criteria have been met.  setup callbacks to handle contingencies when they occur.

    //TODO If the new current checkpoint is the last checkpoint in the path, instead setup handling of path end contingencies.

  }
}
Here is the "AudioSystem" class in the "Audio.swift" file: 
import AVFoundation
import AudioKit
import Foundation

enum PlayFileStatus {
  case success
  case noSuchFile(String)
  case error(String)
}

class AudioSystem {
  static var shared: AudioSystem = AudioSystem()

  private(set) var currentAudioFile: String?
  private(set) var currentCaptionFile: String?

  private init() {
    setupAudioGraph()
  }

  //MARK: audiokit setup

  let engine = AudioEngine()
  let backgroundAudioNode: AudioPlayer = AudioPlayer()
  let foregroundAudioNode = AudioPlayer()

  private var _backgroundAudioFile: String = ""
  private var _foregroundAudioFile: String = ""
  public var backgroundAudioFile: String { _backgroundAudioFile }
  public var foregroundAudioFile: String { _foregroundAudioFile }

  public var backgroundVolume: Float {
    get { backgroundAudioNode.volume }
    set {
      backgroundAudioNode.volume = newValue
      UserDefaults.standard.set(newValue, forKey: Preferences.backgroundVolumeKey)
    }
  }

  private func setupAudioGraph() {
    //        let foregroundAudioNode = AudioPlayer()
    //        backgroundAudioNode = AudioPlayer()
    let mainMixerNode = Mixer([foregroundAudioNode, backgroundAudioNode])

    mainMixerNode.volume = 2

    engine.output = mainMixerNode
    foregroundAudioNode.play()
    backgroundAudioNode.play()
    //        foregroundAudioNode.load(file: <#T##AVAudioFile#>)
  }

  func start() {
    do {
      try engine.start()
    } catch let err {
      Log(err)
    }
  }

  func stop() {
    engine.stop()
  }

  func clearBuffer() {
    //        let oldAlwaysQueueAudioFlagValue = alwaysQueueAudio

    //        alwaysQueueAudio = false
    self.playFileInForeground(fileName: "empty.mp3", captionFile: "empty.srt")

    //        alwaysQueueAudio = oldAlwaysQueueAudioFlagValue
  }

  //MARK: public api functions

  //    var alwaysQueueAudio = true

  var foregroundAudioDuration: TimeInterval = 0

  func pauseForegroundAudio() {
    foregroundAudioNode.pause()
  }
  func pauseBackgroundAudio() {
    backgroundAudioNode.pause()
  }
  func playFileInForeground(fileName: String, captionFile: String?) -> PlayFileStatus? {

    //        guard !alwaysQueueAudio else { return queueFileInForeground(fileName: fileName)}
    TourSystem.shared.stopClosedCaptions()
    let docDir = try! FileManager.default.url(
      for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    let audioFileURL = docDir.appendingPathComponent(fileName, conformingTo: .audio)
    do {
      let audioFile = try AVAudioFile(forReading: audioFileURL)  //todorelease don't force unwrap
      let audioFileLength = AVAudioFrameCount(audioFile.length)
      //        print("SRT File: \(audioFileURL.lastPathComponent)")
      let sampleRate = audioFile.fileFormat.sampleRate
      let audioFileDuration: TimeInterval = TimeInterval(Double(audioFileLength) / sampleRate)
      print(
        "played audio file has duration \(audioFileDuration); setting this to the duration of the foregraound audio"
      )
      foregroundAudioDuration = audioFileDuration
      foregroundAudioNode.stop()
      foregroundAudioNode.reset()
      //            try foregroundAudioNode.load(file: audioFile)
      foregroundAudioNode.playerNode.scheduleFile(
        audioFile, at: AVAudioTime.now(), completionHandler: nil)
      foregroundAudioNode.start()
      // TourSystem.shared.startClosedCaptions(time: 0.0, file: fileName)

      _foregroundAudioFile = fileName
      currentAudioFile = fileName
      currentCaptionFile = captionFile

      foregroundAudioNode.start()
      return nil
      return nil
    } catch {
      print("failed to load audio file, it doesn' exist maybe, here's the error: \(error)")
      _foregroundAudioFile = ""
      return .noSuchFile(fileName)

    }
  }

  static func durationOfAudio(audioFileName: String) -> TimeInterval? {
    let docDir = try! FileManager.default.url(
      for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    let audioFileURL = docDir.appendingPathComponent(audioFileName, conformingTo: .audio)
    do {
      let audioFile = try AVAudioFile(forReading: audioFileURL)  //todorelease don't force unwrap
      //        TODO NATE - fix durations so they access the duration property instead of the length/samplerate calc
      //        let fileDuration: TimeInterval = audioFile.duration
      //        print("audio file duration: \(fileDuration)")
      let audioFileLength = AVAudioFrameCount(audioFile.length)
      let sampleRate = audioFile.fileFormat.sampleRate
      let audioFileDuration: TimeInterval = TimeInterval(Double(audioFileLength) / sampleRate)
      return audioFileDuration
    } catch {
      print("could not get audio file duration because of error \(error)")
      return nil
    }
  }

  //  func onCurrentAudioFinished() {
  //    let gameViewController: GameViewController = GameViewController()
  //    let currentCheckpoint = TourSystem.shared.state.currentPath.checkpoints[
  //      TourSystem.shared.state.currentCheckpointIndex]
  //    TourSystem.shared.playAudioForCheckpoint(currentCheckpoint)
  //      if let closedCaptionFile = currentCheckpoint.closedCaptionFile {
  //      print("DEBUG: Initializing closed captions for file: \(closedCaptionFile)")
  //      gameViewController.initializeClosedCaptions(for: closedCaptionFile)
  //    }
  //  }

  func queueFileInForeground(fileName: String) -> PlayFileStatus? {
    let docDir = try! FileManager.default.url(
      for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    let audioFileURL = docDir.appendingPathComponent(fileName, conformingTo: .audio)
    do {
      let audioFile = try AVAudioFile(forReading: audioFileURL)  //todorelease don't force unwrap
      let audioFileLength = AVAudioFrameCount(audioFile.length)
      let sampleRate = audioFile.fileFormat.sampleRate
      let audioFileDuration: TimeInterval = TimeInterval(Double(audioFileLength) / sampleRate)
      print(
        "queued audio file has duration \(audioFileDuration); adding this to the duration of the foregraound audio, which was \(foregroundAudioDuration)"
      )

      print("new foregroundAudio Duration is \(foregroundAudioDuration)")
      //            foregroundAudioNode.stop();foregroundAudioNode.reset()
      let currentTrackDuration = foregroundAudioDuration  //foregroundAudioNode.duration
      let currentTrackPlaybackTime = foregroundAudioNode.getCurrentTime()
      //            try foregroundAudioNode.load(file: audioFile)
      foregroundAudioNode.playerNode.scheduleFile(
        audioFile,
        at: AVAudioTime.now().offset(
          seconds: currentTrackDuration - currentTrackPlaybackTime + 0.1), completionHandler: nil)

      _foregroundAudioFile = fileName
      foregroundAudioDuration += audioFileDuration

      //            if !foregroundAudioNode.isStarted {
      //                foregroundAudioNode.start()
      //                foregroundAudioNode.play()
      //            }
      foregroundAudioNode.play()

      return nil
    } catch {
      print("failed to load audio file, it doesn't exist maybe, here's the error: \(error)")
      _foregroundAudioFile = ""
      return .noSuchFile(fileName)

    }
  }
  func playFileInBackground(fileName: String) -> PlayFileStatus? {
    let docDir = try! FileManager.default.url(
      for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    let audioFileURL = docDir.appendingPathComponent(fileName, conformingTo: .audio)
    do {
      let audioFile = try AVAudioFile(forReading: audioFileURL)  //todorelease don't force unwrap
      //            try backgroundAudioNode.load(file: audioFile)
      backgroundAudioNode.stop()
      backgroundAudioNode.reset()
      backgroundAudioNode.playerNode.scheduleFile(
        audioFile, at: AVAudioTime.now(), completionHandler: nil)
      backgroundAudioNode.start()
      _backgroundAudioFile = fileName
      return nil
    } catch {
      print("failed to load audio file, it doesn't exist maybe, here's the error: \(error)")
      _backgroundAudioFile = ""
      return .noSuchFile(fileName)
    }
  }

  func playOrQueueFileInForeground(
    fileName: String, captionFile: String?, completion: (() -> Void)? = nil
  ) -> PlayFileStatus? {
    let docDir = try! FileManager.default.url(
      for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    let audioFileURL = docDir.appendingPathComponent(fileName, conformingTo: .audio)
    do {
      let audioFile = try AVAudioFile(forReading: audioFileURL)
      let audioFileLength = AVAudioFrameCount(audioFile.length)
      let sampleRate = audioFile.fileFormat.sampleRate
      let audioFileDuration: TimeInterval = TimeInterval(Double(audioFileLength) / sampleRate)

      let currentTrackDuration = foregroundAudioDuration
      let currentTrackPlaybackTime = foregroundAudioNode.getCurrentTime()

      if currentTrackPlaybackTime < currentTrackDuration {
        // Queue the file
        foregroundAudioNode.playerNode.scheduleFile(
          audioFile,
          at: AVAudioTime.now().offset(
            seconds: currentTrackDuration - currentTrackPlaybackTime + 0.1),
          completionHandler: {
            DispatchQueue.main.async {
              completion?()
            }
          })

        foregroundAudioDuration += audioFileDuration
      } else {
        // Play the file immediately
        foregroundAudioNode.stop()
        foregroundAudioNode.reset()
        foregroundAudioNode.playerNode.scheduleFile(
          audioFile, at: nil,
          completionHandler: {
            DispatchQueue.main.async {
              completion?()
            }
          })
        foregroundAudioNode.start()
        foregroundAudioDuration = audioFileDuration
      }

      _foregroundAudioFile = fileName
      currentAudioFile = fileName
      currentCaptionFile = captionFile

      foregroundAudioNode.play()
      return nil
    } catch {
      print("Failed to load audio file, it doesn't exist maybe, here's the error: \(error)")
      _foregroundAudioFile = ""
      return .noSuchFile(fileName)
    }
  }
}