createWaveformFromAhapEvents function
Implementation
Waveform createWaveformFromAhapEvents(List<AhapEvent> input) {
List<int> timings = [];
List<int> amplitudes = [];
bool repeat = false;
// split input events into parameters and events
input.sort((a, b) => a.time.compareTo(b.time));
List<AhapEvent> ahapEvents = input.where((element) => element.type == AhapEventType.hapticEvent).toList();
List<AhapEvent> parameters = input.where((element) => element.type == AhapEventType.hapticParameter).toList();
// find where all cuts in events are
List<int> borders = [0];
for (var event in input) {
borders.add(event.time.toMs());
borders.add((event.time + event.duration).toMs());
}
borders = borders.toSet().toList()..sort();
for (int i = 0; i < borders.length - 1; i++) {
int start = borders[i];
int end = borders[i + 1];
List<AhapEvent> filteredEvents = ahapEvents
.where((element) => element.time.toMs() < end && (element.time + element.duration).toMs() > start)
.toList();
timings.add(end - start);
if (filteredEvents.isEmpty) {
amplitudes.add(0);
} else {
filteredEvents.sort((a, b) => a.intensity.compareTo(b.intensity));
amplitudes.add(min(255, filteredEvents.last.intensity * 255).round());
}
}
// After creating all seperate events, we now need to apply the paremeters. We need to make cuts in the waveform where the parameters change
// How to apply parameters according to the AHAP standard:
// For haptic intensity and audio volume, the final property value is equal to the original event parameter value multiplied by the dynamic parameter value.
// For all other parameters, the final property value is equal to the dynamic parameter value added to the original event parameter value.
// In both cases, the resulting value is limited to the range with minimum and maximum values corresponding to the specified event parameter.
// https://developer.apple.com/documentation/corehaptics/chhapticdynamicparameter
// We will only support haptic intensity for now, as we can only pass amplitude to the waveform
var time = 0;
for (int i = 0; i < timings.length - 1; i++) {
var timing = timings[i];
var amplitude = amplitudes[i];
time += timing;
// find matching parameters for this timing
AhapEvent? parameter = parameters.where((element) => element.time.toMs() <= time).lastOrNull;
// apply parameter to amplitude
if (parameter != null) {
amplitudes[i] = max(0, min(255, (amplitude * parameter.intensity).round()));
}
}
return Waveform(
timings: timings,
amplitudes: amplitudes,
repeat: repeat,
);
}