@clockworkdog/cogs-client 3.0.0-alpha.0 → 3.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export { default as CogsAudioPlayer } from './AudioPlayer';
9
9
  export { default as CogsVideoPlayer } from './VideoPlayer';
10
10
  export * from './types/AudioState';
11
11
  export { assetUrl, preloadUrl } from './utils/urls';
12
+ export { getStateAtTime } from './utils/getStateAtTime';
12
13
  export * from './types/CogsPluginManifest';
13
14
  export * as ManifestTypes from './types/ManifestTypes';
14
15
  export * from './DataStore';
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export { default as CogsAudioPlayer } from './AudioPlayer';
5
5
  export { default as CogsVideoPlayer } from './VideoPlayer';
6
6
  export * from './types/AudioState';
7
7
  export { assetUrl, preloadUrl } from './utils/urls';
8
+ export { getStateAtTime } from './utils/getStateAtTime';
8
9
  export * from './types/CogsPluginManifest';
9
10
  export * as ManifestTypes from './types/ManifestTypes';
10
11
  export * from './DataStore';
@@ -0,0 +1,28 @@
1
+ import { MediaClipState, TemporalProperties } from '../types/MediaSchema';
2
+ export declare function getStateAtTime<State extends MediaClipState>(state: State, time: number): State['keyframes'][0][1]['set'] | undefined;
3
+ /**
4
+ * Goes through all keyframes to lerp between many different properties
5
+ * Note: This has no specific logic regarding types of properties
6
+ * Do not use this for setting time / rate.
7
+ * They behave differently in the schema. ( @see getTemporalPropertiesAtTime )
8
+ *
9
+ * @param keyframes keyframes from a given MediaClipState
10
+ * @param time the time (on or between keyframes) to calculate
11
+ * @returns a grouped object of all properties
12
+ */
13
+ export declare function getPropertiesAtTime<P extends Record<string, unknown>>(keyframes: [number, {
14
+ set?: Partial<P>;
15
+ lerp?: Partial<P>;
16
+ }][], time: number): P;
17
+ /**
18
+ * Keep track of time whilst going through the keyframes at respective rate.
19
+ * Temporal properties cannot be interpolated in the media schema.
20
+ *
21
+ * @param keyframes temporal keyframes from an AudioClipState or VideoClipState
22
+ * @param time the time (on or between keyframes) to calculate
23
+ * @returns the temporal properties of the media at the given time
24
+ */
25
+ export declare function getTemporalPropertiesAtTime<P extends TemporalProperties>(keyframes: [number, {
26
+ set?: Partial<P>;
27
+ lerp?: Partial<P>;
28
+ }][], time: number): TemporalProperties | undefined;
@@ -0,0 +1,141 @@
1
+ import { defaultAudioOptions, defaultImageOptions, defaultVideoOptions } from '../types/MediaSchema';
2
+ export function getStateAtTime(state, time) {
3
+ switch (state.type) {
4
+ case 'image': {
5
+ const firstTimestamp = state.keyframes[0][0];
6
+ if (firstTimestamp > time) {
7
+ return undefined;
8
+ }
9
+ const nonNullKeyframes = state.keyframes.filter((k) => k[1] !== null);
10
+ const properties = getPropertiesAtTime(nonNullKeyframes, time);
11
+ return { ...defaultImageOptions, ...properties };
12
+ }
13
+ case 'audio': {
14
+ const nonNullKeyframes = state.keyframes.filter((k) => k[1] !== null);
15
+ const temporalProperties = getTemporalPropertiesAtTime(nonNullKeyframes, time);
16
+ if (!temporalProperties) {
17
+ return undefined;
18
+ }
19
+ const properties = getPropertiesAtTime(nonNullKeyframes, time);
20
+ return { ...defaultAudioOptions, ...properties, ...temporalProperties };
21
+ }
22
+ case 'video': {
23
+ const nonNullKeyframes = state.keyframes.filter((k) => k[1] !== null);
24
+ const temporalProperties = getTemporalPropertiesAtTime(nonNullKeyframes, time);
25
+ if (!temporalProperties) {
26
+ return undefined;
27
+ }
28
+ const properties = getPropertiesAtTime(nonNullKeyframes, time);
29
+ return { ...defaultVideoOptions, ...properties, ...temporalProperties };
30
+ }
31
+ }
32
+ }
33
+ /**
34
+ * Goes through all keyframes to lerp between many different properties
35
+ * Note: This has no specific logic regarding types of properties
36
+ * Do not use this for setting time / rate.
37
+ * They behave differently in the schema. ( @see getTemporalPropertiesAtTime )
38
+ *
39
+ * @param keyframes keyframes from a given MediaClipState
40
+ * @param time the time (on or between keyframes) to calculate
41
+ * @returns a grouped object of all properties
42
+ */
43
+ export function getPropertiesAtTime(keyframes, time) {
44
+ const propertyKeyframes = {};
45
+ for (const [timestamp, properties] of keyframes) {
46
+ if (timestamp <= time) {
47
+ // If lerp and set are both present, we assume we lerp up until the timestamp,
48
+ // then set to a new value
49
+ Object.entries(properties.lerp ?? {}).forEach(([property, value]) => {
50
+ propertyKeyframes[property] ?? (propertyKeyframes[property] = {});
51
+ propertyKeyframes[property].before = [timestamp, value];
52
+ });
53
+ Object.entries(properties.set ?? {}).forEach(([property, value]) => {
54
+ propertyKeyframes[property] ?? (propertyKeyframes[property] = {});
55
+ propertyKeyframes[property].before = [timestamp, value];
56
+ });
57
+ }
58
+ else {
59
+ // We're trying to find the closest timestamp afterwards for lerping
60
+ // So only set if not yet set
61
+ Object.entries(properties.lerp ?? {}).forEach(([property, value]) => {
62
+ propertyKeyframes[property] ?? (propertyKeyframes[property] = {});
63
+ if (propertyKeyframes[property].after === undefined) {
64
+ propertyKeyframes[property].after = [timestamp, value];
65
+ }
66
+ });
67
+ }
68
+ }
69
+ const properties = {};
70
+ Object.entries(propertyKeyframes).forEach(([property, { before, after }]) => {
71
+ // There is no lerping, and it has been set before
72
+ // It's a constant!
73
+ if (after === undefined && before) {
74
+ properties[property] = before[1];
75
+ return;
76
+ }
77
+ // Multiple timestamps on the same, we return after
78
+ if (before && after && before[0] === after[0]) {
79
+ properties[property] = after[1];
80
+ return;
81
+ }
82
+ // Property has been set, and there's a lerp timestamp afterwards
83
+ // We've got numbers, so lets try to linearly inerpolate
84
+ if (before && typeof before[1] === 'number' && after && typeof after[1] === 'number') {
85
+ properties[property] = (before[1] + ((time - before[0]) * (after[1] - before[1])) / (after[0] - before[0]));
86
+ return;
87
+ }
88
+ });
89
+ return properties;
90
+ }
91
+ /**
92
+ * Keep track of time whilst going through the keyframes at respective rate.
93
+ * Temporal properties cannot be interpolated in the media schema.
94
+ *
95
+ * @param keyframes temporal keyframes from an AudioClipState or VideoClipState
96
+ * @param time the time (on or between keyframes) to calculate
97
+ * @returns the temporal properties of the media at the given time
98
+ */
99
+ export function getTemporalPropertiesAtTime(keyframes, time) {
100
+ // Not defined if the media starts in the future
101
+ const firstKeyframe = keyframes[0];
102
+ if (!firstKeyframe || firstKeyframe[0] > time) {
103
+ return undefined;
104
+ }
105
+ let timeAtLastKeyframe = 0;
106
+ let { t: mediaTimeAtLastKeyframe, rate: mediaRateAtLastKeyframe } = defaultAudioOptions;
107
+ for (const [timestamp, properties] of keyframes) {
108
+ // Only calculate up to the keyframe on or before
109
+ if (timestamp > time)
110
+ break;
111
+ const { set } = properties;
112
+ if (!set)
113
+ continue;
114
+ const { t, rate } = set;
115
+ // time is set - no calculations needed
116
+ if (t !== undefined) {
117
+ timeAtLastKeyframe = timestamp;
118
+ mediaTimeAtLastKeyframe = t;
119
+ if (rate !== undefined) {
120
+ mediaRateAtLastKeyframe = rate;
121
+ }
122
+ continue;
123
+ }
124
+ // rate is set on it's own, calculate how much time has passed
125
+ if (rate !== undefined) {
126
+ const duration = timestamp - timeAtLastKeyframe;
127
+ const mediaDuration = duration * mediaRateAtLastKeyframe;
128
+ timeAtLastKeyframe = timestamp;
129
+ mediaTimeAtLastKeyframe += mediaDuration;
130
+ mediaRateAtLastKeyframe = rate;
131
+ }
132
+ }
133
+ // Calculate time after last keyframe
134
+ const finalDuration = time - timeAtLastKeyframe;
135
+ const finalMediaDuration = finalDuration * mediaRateAtLastKeyframe;
136
+ const finalMediaTime = mediaTimeAtLastKeyframe + finalMediaDuration;
137
+ return {
138
+ rate: mediaRateAtLastKeyframe,
139
+ t: finalMediaTime,
140
+ };
141
+ }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Connect to COGS to build a custom Media Master",
4
4
  "author": "Clockwork Dog <info@clockwork.dog>",
5
5
  "homepage": "https://github.com/clockwork-dog/cogs-sdk/tree/main/packages/javascript",
6
- "version": "3.0.0-alpha.0",
6
+ "version": "3.0.0-alpha.1",
7
7
  "keywords": [],
8
8
  "license": "MIT",
9
9
  "repository": {
@@ -34,7 +34,7 @@
34
34
  "prerelease": "yarn npm publish --access public --tag=next"
35
35
  },
36
36
  "dependencies": {
37
- "@clockworkdog/timesync": "^3.0.0-alpha.0",
37
+ "@clockworkdog/timesync": "^3.0.0-alpha.1",
38
38
  "howler": "clockwork-dog/howler.js#fix-looping-clips",
39
39
  "reconnecting-websocket": "^4.4.0",
40
40
  "zod": "^4.1.13"