CHHapticAdvancedPatternPlayer seek method not working

Hello,

We are trying to do some advanced stuff with CoreHaptics where we need to navigate in the haptic pattern (start, pause, resume, rewind or fast forward). But it seems like the seek(toOffset:) method of CHHapticAdvancedPatternPlayer doesn't work on iOS 14 and iOS 15.

Here's a sample code :

import CoreHaptics
import Foundation
import SwiftUI

let events = [
    CHHapticEvent(
        eventType: .hapticContinuous,
        parameters: [
            CHHapticEventParameter(parameterID: .hapticSharpness, value: 1),
            CHHapticEventParameter(parameterID: .hapticIntensity, value: 0.8)
        ],
        relativeTime: 0,
        duration: 3
    )
]

let parameters = [
    CHHapticDynamicParameter(parameterID: .hapticIntensityControl, value: 1, relativeTime: 0),
    CHHapticDynamicParameter(parameterID: .hapticIntensityControl, value: 0.5, relativeTime: 0.5),
    CHHapticDynamicParameter(parameterID: .hapticIntensityControl, value: 1, relativeTime: 1),
    CHHapticDynamicParameter(parameterID: .hapticIntensityControl, value: 0.5, relativeTime: 1.5),
    CHHapticDynamicParameter(parameterID: .hapticIntensityControl, value: 1, relativeTime: 2),
    CHHapticDynamicParameter(parameterID: .hapticIntensityControl, value: 0.5, relativeTime: 2.5)
]

let pattern = try! CHHapticPattern(events: events, parameters: parameters)

struct ContentView: View {
    private var player: CHHapticAdvancedPatternPlayer?

    var body: some View {
        VStack(spacing: 20) {
            Button("Start") {
                try? player?.start(atTime: CHHapticTimeImmediate)
            }
            Button("Pause") {
                try? player?.pause(atTime: CHHapticTimeImmediate)
            }
            Button("Resume") {
                try? player?.resume(atTime: CHHapticTimeImmediate)
            }
            Button("Stop") {
                try? player?.stop(atTime: CHHapticTimeImmediate)
            }
            Button("Seek to offset 1 sec") {
                do {
                    try player?.seek(toOffset: 1)
                    print("No error")
                } catch {
                    print(error)
                }
            }
        }
    }

    init() {
        guard CHHapticEngine.capabilitiesForHardware().supportsHaptics,
              let engine = try? CHHapticEngine()
        else { return }
        engine.resetHandler = { try? engine.start() }
        try? engine.start()
        self.player = try? engine.makeAdvancedPlayer(with: pattern)
        print(pattern.duration)
    }
}

Is there anything wrong with the code ? Can someone provide a working example ?

Thanks !

Answered by in 696751022

I had not noticed at first that you are playing a single long continuous event, and then attempting to seek on it. This is a known issue described here in the Core Haptics Release Notes:

Events — such as audioContinuous, hapticContinuous, and audioCustom — can’t be resumed during the event; no output occurs for that event, only for subsequent events. This applies to playback at a specific time offset, seeking, and resuming.(29274583)

The best workaround is to convert your pattern into a series of three shorter continuous events - or even into smaller events if possible. That way the pattern will continue to play at the next event it hits, regardless of where you are in the timeline.

Hope this helps you get this working.

-DS

The commands generated by your buttons will interact with each other. Can you give me a sequence of actions which does not do what you expect? Like, "Press Start button, wait 2 seconds, press Seek button", etc. Thanks!

The haptic pattern is basically 1 intense vibration and 1 less intense repeated 3 times. What I expected when calling player?.seek(toOffset: 1) is that it would skip the first sequence and only play it twice instead of 3 times. We tried to "Press start button" and "Press seek button" right after but it stopped playing. We also tried to just "Press seek button" to see if would play the pattern with the offset but it didn't work either.

@SoundOfMind, did you have time to look into it ?

Will see if I can investigate this week.

I am unable to reproduce this in our test system. It is legal to seek a player before you start it - it will start at the new offset. It is also (obviously) legal to start it and then seek to an earlier or later point. If you just start it playing and press no other buttons, does it play all the way through?

Accepted Answer

I had not noticed at first that you are playing a single long continuous event, and then attempting to seek on it. This is a known issue described here in the Core Haptics Release Notes:

Events — such as audioContinuous, hapticContinuous, and audioCustom — can’t be resumed during the event; no output occurs for that event, only for subsequent events. This applies to playback at a specific time offset, seeking, and resuming.(29274583)

The best workaround is to convert your pattern into a series of three shorter continuous events - or even into smaller events if possible. That way the pattern will continue to play at the next event it hits, regardless of where you are in the timeline.

Hope this helps you get this working.

-DS

CHHapticAdvancedPatternPlayer seek method not working
 
 
Q