Compare commits

..

118 commits
main ... audio

Author SHA1 Message Date
themancalledjakob
3b6a57024d audioLayer panel injection
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 15b2b9543be7a9ed5c8889e04df715f91d479d45
2024-04-14 17:36:04 +02:00
themancalledjakob
8353bb837c initialize audioPlayer on reload
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 15b2b9543be7a9ed5c8889e04df715f91d479d45
2024-04-14 11:33:15 +02:00
themancalledjakob
8075cf2b16 save and load audioLayer and audioPlayer
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre c60e63f5be4466e21b6bf06f6580f6100687306b
2024-04-13 18:03:44 +02:00
themancalledjakob
ef308f61b2 update theatre
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre c60e63f5be4466e21b6bf06f6580f6100687306b
2024-04-13 10:27:45 +02:00
themancalledjakob
32778138ba fix playing multiple recordings with audio and more
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 78a67ee6650d846fe5cc4770770b1511328033e6
2024-04-12 21:33:37 +02:00
themancalledjakob
8750151b7d update theatre
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 78a67ee6650d846fe5cc4770770b1511328033e6
2024-04-12 19:17:38 +02:00
themancalledjakob
18300baf46 add recorded to timeline
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-12 16:32:47 +02:00
themancalledjakob
42c47aa003 update to new folder structure
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-12 12:18:13 +02:00
themancalledjakob
d42a1365a4 perfectionize toCssClass and add sanitizeTheatreKey
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-12 12:17:49 +02:00
themancalledjakob
d050789d77 improved watching
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-11 18:07:00 +02:00
themancalledjakob
d35d949526 add audio layer and audio player
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-11 18:06:45 +02:00
e625b0d14c update ignore rules
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-09 14:53:17 +02:00
themancalledjakob
09897d7178 inbetween stuff
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-07 14:30:22 +02:00
themancalledjakob
c832982d46 record microphone to audiofile and add to timeline
dirty dirty inbetween

dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-02 19:42:42 +02:00
themancalledjakob
5ff6ee3905 change defaults
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-02 13:52:38 +02:00
themancalledjakob
157703886d fix disable/enable letterDelay input update
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-02 13:51:20 +02:00
themancalledjakob
14e79208f3 color fixing measures when alpha is missing, cleanup
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-01 19:04:11 +02:00
themancalledjakob
996ad358e7 color fixing measures when alpha is missing
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-01 17:18:28 +02:00
themancalledjakob
d7ac9e834d fix loader waits too long when project loads superfast
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-01 15:44:26 +02:00
themancalledjakob
0d732943b3 loader waits longer
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-01 15:30:03 +02:00
asuk
7931dffca1 ui css changes
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
2024-04-01 14:01:32 +02:00
asuk
5c9f41b7a7 ui css changes
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
2024-04-01 13:25:55 +02:00
themancalledjakob
6c67d3aaa8 more css classes in side panel
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-01 11:47:41 +02:00
themancalledjakob
77ffed0225 more css classes in side panel
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-04-01 11:29:10 +02:00
asuk
30c662f5a4 beautify AudioOptions
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
2024-04-01 10:56:31 +02:00
themancalledjakob
87e296a1d5 pink hint in colorpicker
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-31 10:13:56 +02:00
themancalledjakob
f421a344b1 add color picker for audio min max
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-31 10:11:51 +02:00
themancalledjakob
086e93e786 fix letterdelay not recorded for nested properties
fixes #3

dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-29 13:31:20 +01:00
themancalledjakob
9bd2f08c8f fix (partially) linux compilation
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-29 09:19:37 +01:00
themancalledjakob
8a92646038 audio colors in config
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-24 11:08:00 +01:00
themancalledjakob
64253eaf1f do we need these?
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-24 11:07:51 +01:00
themancalledjakob
4ef2ee4670 alert when microphone has no permission
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-22 18:16:04 +01:00
themancalledjakob
ede5b5d44c buttons order
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-22 18:15:26 +01:00
themancalledjakob
94081b9345 add stoprecording button in notice
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-22 18:14:51 +01:00
themancalledjakob
6e60818136 fix selection after deleting selected layer
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-22 17:49:29 +01:00
themancalledjakob
6ec94b4beb linear keyframes
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-22 16:38:59 +01:00
themancalledjakob
a4fda03f7e fix lightclean and watch
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-22 16:38:44 +01:00
themancalledjakob
83184737ea fix bug
recording did not work, when there were layers with mapped but not hot values

dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-22 16:36:45 +01:00
themancalledjakob
962344e8cd get input source on hover
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-22 16:35:44 +01:00
themancalledjakob
1fdce3ee96 move addFont button to main.js
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-22 16:34:55 +01:00
themancalledjakob
a9a40a8de1 cleanup
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-22 16:32:09 +01:00
themancalledjakob
f2ed11c206 comment unnecessary conditional
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-19 15:11:52 +01:00
themancalledjakob
a1570e7fc3 ignore letterDelay from savedMapping, if prop is sequenced
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-19 15:11:39 +01:00
themancalledjakob
28fb7e2198 more instructions
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler fa1d70e6a5fae16633eed2ce65fb8b23f2edbbb8
-----> watch out ofxProfiler has uncommitted changes!
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-16 16:17:53 +01:00
themancalledjakob
b80f73558b cleanup
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler fa1d70e6a5fae16633eed2ce65fb8b23f2edbbb8
-----> watch out ofxProfiler has uncommitted changes!
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-16 16:15:49 +01:00
themancalledjakob
efaad8d143 cleanup
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler fa1d70e6a5fae16633eed2ce65fb8b23f2edbbb8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-16 16:14:22 +01:00
themancalledjakob
7d174dd189 cleanup
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler fa1d70e6a5fae16633eed2ce65fb8b23f2edbbb8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-16 16:12:09 +01:00
themancalledjakob
0b29009db9 cleanup
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler fa1d70e6a5fae16633eed2ce65fb8b23f2edbbb8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-16 16:03:23 +01:00
themancalledjakob
365ac34de6 cleanup
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler fa1d70e6a5fae16633eed2ce65fb8b23f2edbbb8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-16 15:54:32 +01:00
themancalledjakob
6d3b064da7 cleanup
dependencies hashes:
openFrameworks
ofxMsdfgen
-----> watch out ofxMsdfgen has uncommitted changes!
ofxGPUFont
-----> watch out ofxGPUFont has uncommitted changes!
ofxVariableLab
-----> watch out ofxVariableLab has uncommitted changes!
ofxProfiler
-----> watch out ofxProfiler has uncommitted changes!
theatre
2024-03-16 15:44:16 +01:00
themancalledjakob
8a17aa7a39 cleanup
dependencies hashes:
openFrameworks
ofxMsdfgen
-----> watch out ofxMsdfgen has uncommitted changes!
ofxGPUFont
-----> watch out ofxGPUFont has uncommitted changes!
ofxVariableLab
-----> watch out ofxVariableLab has uncommitted changes!
ofxProfiler
-----> watch out ofxProfiler has uncommitted changes!
theatre
2024-03-16 15:42:48 +01:00
themancalledjakob
c8e90a1317 cleanup
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler fa1d70e6a5fae16633eed2ce65fb8b23f2edbbb8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-16 15:35:56 +01:00
themancalledjakob
16e1839616 cleanup
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler fa1d70e6a5fae16633eed2ce65fb8b23f2edbbb8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-16 15:35:06 +01:00
themancalledjakob
13eca27957 cleanup 2024-03-16 15:32:14 +01:00
themancalledjakob
9b336aefed cleanup 2024-03-16 15:29:38 +01:00
themancalledjakob
596b4fd413 cleanup 2024-03-16 15:27:28 +01:00
themancalledjakob
826af338bf cleanup
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler fa1d70e6a5fae16633eed2ce65fb8b23f2edbbb8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-16 15:26:05 +01:00
themancalledjakob
574f86a7b9 cleanup
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler fa1d70e6a5fae16633eed2ce65fb8b23f2edbbb8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-16 15:15:55 +01:00
themancalledjakob
017b487a50 cleanup
dependencies hashes:
openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501
ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3
ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239
ofxVariableLab 8df98846248a93aa068989a3ebd0d2f0f16e5e69
ofxProfiler fa1d70e6a5fae16633eed2ce65fb8b23f2edbbb8
theatre 86d3e07f6f2c75fd6e08fca8c97e3617c9e23b18
2024-03-16 15:12:30 +01:00
themancalledjakob
4282909d88 cleanup 2024-03-16 15:08:13 +01:00
themancalledjakob
424053f080 remove audio mapping after recording 2024-03-16 14:48:35 +01:00
themancalledjakob
e5ce29a4e9 remove canvasCombos from all layers 2024-03-16 14:46:05 +01:00
themancalledjakob
7c01738d6d add little text to readme 2024-03-16 13:36:04 +01:00
themancalledjakob
7e35b00d5b add .eslintrc.js 2024-03-16 13:23:00 +01:00
themancalledjakob
997247cde3 recording 2024-03-16 13:22:08 +01:00
themancalledjakob
c5bdff23ee automatic reload on change 2024-03-16 09:57:27 +01:00
themancalledjakob
bd87a53872 remove debug color 2024-03-07 12:42:47 +01:00
themancalledjakob
7823711f1a shadowCss in external file 2024-03-07 12:41:11 +01:00
themancalledjakob
f829eea911 remember play state 2024-03-07 11:14:24 +01:00
themancalledjakob
be11f015b9 sync letter delays after recording 2024-03-07 11:13:31 +01:00
themancalledjakob
05c6f3f5f9 remove unnecessary logs 2024-03-07 11:11:37 +01:00
themancalledjakob
e10d8dbe1d spaces in filenames are OK 2024-03-05 18:46:50 +01:00
themancalledjakob
f18bafc543 so much faster keyframe adding 2024-03-05 17:12:18 +01:00
themancalledjakob
d95ec3d965 enable recording 2024-02-29 16:34:05 +01:00
themancalledjakob
63ddc4b317 the great reshuffle and new theatre 2024-02-29 16:32:49 +01:00
48c7db3f6e artboard color backwards compatibility 2024-01-09 12:30:55 +01:00
e4ca41c2f0 various little audio fixes 2023-10-24 23:57:49 +02:00
dc6e668d0c mapping, check against undefined 2023-10-24 09:02:43 +02:00
2d55ef11b0 reset fontvariationaxes on font change 2023-10-24 07:42:29 +02:00
ab3786ef23 recordbutton can be disabled 2023-10-24 07:41:44 +02:00
f19843bd46 full range audio fft on click 2023-10-24 07:38:06 +02:00
2d43063537 default range on hover 2023-10-24 07:36:50 +02:00
223121655e fix mute button css 2023-10-23 15:16:46 +02:00
49ce43d324 remember letterDelay 2023-10-22 18:16:50 +02:00
e926d60d62 fix artboard update 2023-10-22 18:16:25 +02:00
5f1f5a10a1 artboard audio, save audio with project json
not stable yet
2023-10-20 13:29:15 +02:00
922834161f typo 2023-10-13 17:28:01 +02:00
494f562b3b prevent dummy breaking things 2023-10-13 17:28:01 +02:00
asuk
21a5888022 css changes 2023-10-13 17:01:11 +02:00
0ca73bca05 mute, letterDelay and other fixes 2023-10-13 16:24:34 +02:00
748af243fa individual audiofiles <-> prop mapping 2023-10-13 11:29:18 +02:00
16124c755d intermediate audiofile test
hardcoded filepath watch out, also it automatically starts playing and the fft visualisation shows only one file
2023-10-12 21:32:38 +02:00
e6e705f86f add letterDelay audio, fix audio update when playing, misc 2023-10-12 18:10:07 +02:00
5860343f70 remove logs 2023-10-10 17:01:28 +02:00
b46d6bb5d4 almost multilayer recording 2023-10-10 17:00:34 +02:00
6aba91b6ca hide ui and remove audio effects/monitoring 2023-10-10 16:45:16 +02:00
64af8d49d1 recording notice, default mapping range 2023-10-10 15:13:53 +02:00
62f03862d6 better click handling 2023-10-10 13:28:40 +02:00
523eb8c7f8 Merge branch 'audio' of git.pointer.click:variablelab/variabletime into audio 2023-10-10 12:41:09 +02:00
asuk
9cd830f7de css changes 2023-10-10 12:40:12 +02:00
04041870da ignore outboundfrequencies and pitchcombine 2023-10-10 12:34:52 +02:00
870a99d725 fix drawing multiple canvasses 2023-10-10 07:26:12 +02:00
35ed6009e2 draw audio options 2023-10-08 21:26:22 +02:00
bb76bd09c6 fix remove color mapping & options 2023-10-08 17:28:15 +02:00
667447b9a2 add color with separate rgba 2023-10-08 15:57:29 +02:00
6ed4bd1227 separate createAudioOptions 2023-10-08 07:19:27 +02:00
fad1bbb875 nested props 2023-10-07 18:01:00 +02:00
7eb3ab7615 allow more props in audio/record 2023-10-07 18:00:51 +02:00
1ef64e4eba use liveUpdater in audio 2023-10-07 13:29:25 +02:00
f8984174e0 live preview of multiple props at the same time 2023-10-07 13:18:39 +02:00
de5550287c recording and setKeyframes entanglement 2023-10-07 12:21:36 +02:00
7f6ab572c7 record and set keyframes, introduce RecordingBuffer, avoid toDataUrl() for fft visualisation 2023-10-02 10:05:20 +02:00
64b3d26384 add css-order spacing 2023-10-01 15:21:33 +02:00
4cb5742afb simple generic recording, hook up audio 2023-09-28 16:41:11 +02:00
50126f7cd4 hopefully no cache (chrome cli flags are notoriously unstable) 2023-09-28 16:39:51 +02:00
ad7a7f7936 remove unused files 2023-09-28 16:38:58 +02:00
e2d56245d6 add record button svg 2023-09-28 16:28:57 +02:00
8c89d5abc2 add live audio 2023-09-27 13:10:28 +02:00
72 changed files with 6204 additions and 598 deletions

26
.eslintrc.js Normal file
View file

@ -0,0 +1,26 @@
module.exports = {
"env": {
"browser": true,
"es2021": true
},
"extends": "eslint:recommended",
"overrides": [
{
"env": {
"node": true
},
"files": [
".eslintrc.{js,cjs}"
],
"parserOptions": {
"sourceType": "script"
}
}
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
}
}

21
.gitignore vendored
View file

@ -2,8 +2,22 @@
bin/*
!bin/data/
!bin/data/*
!bin/web/
!bin/web/*
# ugly recursion https://stackoverflow.com/questions/3667986/git-ignore-exception-not-working-as-desired
!bin/em/
bin/em/*
!bin/em/*/
bin/em/*/*
!bin/em/*/web/
bin/em/*/web/*
!bin/em/*/web/js/
!bin/em/*/web/css/
!bin/em/*/web/assets/
bin/em/*/web/js/*
bin/em/*/web/css/*
bin/em/*/web/assets/*
!bin/em/*/web/js/*.*
!bin/em/*/web/css/*.*
!bin/em/*/web/assets/*.*
obj/
bin/data/ofxMsdfgen
bin/data/ofxGPUFont
@ -39,3 +53,6 @@ bin/web/fonts/*
# DIST
# these are related to distributing variable time
upload*
# GENERATED
.browserpid

View file

@ -26,6 +26,7 @@ Install all dependencies, then
### Optional:
- [ofxProfiler](https://git.pointer.click/ofxAddons/ofxProfiler)
This allows us to profile the web export in the same way as a desktop export
### Included dependencies:
- [ffmpeg.wasm](https://github.com/ffmpegwasm/ffmpeg.wasm)

View file

@ -290,10 +290,13 @@
<!--<div class="move">move</div>-->
<!--</div>-->
<button id="midi_open">midi</button>
<button id="hide_ui">hide ui</button>
<button id="exporter_open">export</button>
<button id="upload_audio">add audio</button>
<button id="save_project" onclick="window.tp.downloadProject()">save project</button>
<button id="open_project" onclick="window.tp.uploadProject(true)">open project</button>
<button id="start_new_project" onclick="window.tp.startNewProject()">start new project</button>
<button id="add_font">add font</button>
<!--<button id="debug_profiling" onclick="window.toggleProfiling()">debug start profiling</button>-->
</div>
@ -357,6 +360,13 @@
<div class="content">
<div class="what"><p></p></div>
<div class="details"><p></p></div>
<div class="button"><p>OK</p></div>
</div>
</div>
<div id="notice_recording">
<div class="content">
<div class="what"><p>recording</p></div>
<div class="details"><p>please wait</p></div>
</div>
</div>
<!-- MIDI BEGIN -->
@ -523,10 +533,50 @@
</div>
<!-- ABOUT END -->
<div id="timeline"><div id="timeline_head"></div><div>
<!-- AUDIO BEGIN -->
<div class="audioWrapper">
<header>
<h1>Voice-change-O-matic</h1>
</header>
<canvas class="visualizer" width="640" height="100"></canvas>
<form class="controls">
<div>
<label for="voice">Voice setting</label>
<select id="voice" name="voice">
<option value="distortion">Distortion</option>
<option value="convolver">Reverb</option>
<option value="biquad">Bass Boost</option>
<option value="delay">Echo Delay</option>
<option value="off" selected>Off</option>
</select>
</div>
<div>
<label for="visual">Visualizer setting</label>
<select id="visual" name="visual">
<option value="sinewave">Sinewave</option>
<option value="frequencybars" selected>Frequency bars</option>
<option value="off">Off</option>
</select>
</div>
<div>
<a class="mute">Mute</a>
</div>
</form>
</div>
<!-- AUDIO END -->
<!-- EXPORT BEGIN -->
<script id="ffmpeg.min.js" type="application/javascript" src="/web/ffmpeg_modules/ffmpeg.min.js"></script>
<!-- EXPORT END -->
<script> const process = {
env: {
NODE_ENV: 'production'
}
};
</script>
<!--<script id="core-and-studio.min.js" type="application/javascript" src="/web/theatre_modules/core-and-studio.js"></script>-->
<script src="/web/js/vanilla-picker.js"> </script>
<script type="module" src="/web/js/main.js"> </script>
</body>
</html>

View file

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View file

Before

Width:  |  Height:  |  Size: 481 B

After

Width:  |  Height:  |  Size: 481 B

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 646 B

After

Width:  |  Height:  |  Size: 646 B

View file

Before

Width:  |  Height:  |  Size: 610 B

After

Width:  |  Height:  |  Size: 610 B

View file

Before

Width:  |  Height:  |  Size: 615 B

After

Width:  |  Height:  |  Size: 615 B

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 475 B

After

Width:  |  Height:  |  Size: 475 B

View file

Before

Width:  |  Height:  |  Size: 854 B

After

Width:  |  Height:  |  Size: 854 B

View file

Before

Width:  |  Height:  |  Size: 633 B

After

Width:  |  Height:  |  Size: 633 B

View file

Before

Width:  |  Height:  |  Size: 490 B

After

Width:  |  Height:  |  Size: 490 B

View file

Before

Width:  |  Height:  |  Size: 501 B

After

Width:  |  Height:  |  Size: 501 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 83.2 83.2" style="enable-background:new 0 0 83.2 83.2;" xml:space="preserve">
<style type="text/css">
.st0{fill:#ED2024;}
</style>
<g>
<circle class="st0" cx="41.6" cy="41.6" r="40.9"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 475 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
<path d="M15.6,11c0-0.1,0-0.1,0-0.2V0.3L5.8,2.7v9.2c-0.7-0.3-1.5-0.5-2.5-0.3c-1.8,0.3-3.1,1.4-2.9,2.6c0.2,1.2,1.8,1.9,3.5,1.6
c1.5-0.2,2.7-1.1,2.9-2.1h0.1V3.6l7.6-1.9v8c-0.6-0.3-1.5-0.4-2.4-0.3C10.3,9.7,9,10.9,9.2,12c0.2,1.2,1.8,1.9,3.5,1.6
C14.5,13.4,15.8,12.2,15.6,11z"/>
</svg>

After

Width:  |  Height:  |  Size: 628 B

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -207,7 +207,7 @@ body.debug div:not(.centerLine) {
#notice .content {
width: fit-content;
height: fit-content;
padding: 2em;
padding: 10px 10px 20px 10px;
color: black;
background-color: white;
display: flex;
@ -217,11 +217,122 @@ body.debug div:not(.centerLine) {
}
#notice .content .what p {
font-size: 1.2em;
color: black;
}
#notice .content .details p {
color: black;
text-align: center;
max-width: 95%;
margin-bottom: 20px;
}
#notice .content .button {
display: none;
margin: 0px;
background-color: rgba(222, 222, 222, 0.97);
border: none;
cursor: pointer;
padding: 7px 15px;
border-radius: 10px;
font-size: 1.2em;
text-transform: uppercase;
font-variation-settings: 'wght' 800;
transition: all 0.125s;
}
#notice .content .button:hover {
background-color: rgba(0, 0, 0, 0.97);
color: rgba(222, 222, 222, 1.0);
}
#notice .content .button.visible {
display: flex;
}
#notice .content .details{
display: flex;
align-items: center;
justify-content: center;
}
#notice .content .button p {
user-select: none;
margin: 0px;
}
#notice_recording {
position: fixed;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.1);
z-index: 2000;
display: none;
justify-content: center;
align-items: center;
font-family: "Tonka";
font-variation-settings: 'wght' 500;
font-size: 0.8em;
pointer-events: none;
}
#notice_recording .what p, #notice_recording .details button{
text-align: center;
width: fit-content;
}
#notice_recording .what p{
animation: blink 1s linear infinite !important;
}
#notice_recording .details button{
border: none;
background-color: white;
font-size: 1.1em;
padding: 5px 15px 4px 15px !important;
margin: 7px 10px;
display: flex;
flex-grow: 1;
justify-content: center;
border-radius: 10px;
border: none;
padding: 7px;
text-transform: uppercase;
width: calc(100% - 20px);
box-sizing: border-box;
cursor: pointer;
font-variation-settings: 'wght' 750, 'wdth' 100;
font-family: 'Tonka';
}
@keyframes blink{
0%{
opacity: 1;
}
50%{
opacity: 0;
}
100%{
opacity: 1;
}
}
#notice_recording .details button:hover{
background: black;
color: white;
}
#notice_recording .content{
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
#notice_recording.visible {
display: flex;
}
#notice_recording button {
pointer-events: all;
}
#notice_recording.impenetrable {
pointer-events: all;
background-color: rgba(0,0,0,0.5);
}
.exporterChild * {
@ -458,6 +569,8 @@ body.debug div:not(.centerLine) {
margin: 0;
}
#exporter_render_info {
display: none;
color: rgb(234, 35, 51);
@ -926,3 +1039,21 @@ h4{
/* ABOUT END */
.audioWrapper {
position: absolute;
left: 0px;
bottom: 0px;
z-index: 42000;
background-color: rgba(255,125,125,0.5);
opacity: 0;
pointer-events: none;
}
.audioWrapper canvas.visualizer {
border-top: 1px solid black;
border-bottom: 1px solid black;
margin-bottom: -3px;
box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.7), 0 3px 4px rgba(0, 0, 0, 0.7);
}
.invisible {
display: none;
}

View file

@ -0,0 +1,750 @@
.alignButtons,
.textAlignButtons {
flex-direction: row;
padding: 10px 0px !important;
justify-content: center;
}
.word {
margin-right: 10px;
}
.letter {
transition: 0.5s font-variation-settings;
}
@font-face {
font-family: 'vtVF';
src: url("/web/fonts/vtVF.ttf") format("TrueType");
}
.vtTitle {
font-family: 'vtVF';
font-size: 3em;
font-variation-settings: "wght" 0, "wdth" 0, "opsz" 0;
height: 50px;
width: 100%;
position: inherit;
display: flex;
justify-content: center;
align-items: center;
}
#panel,
.panel {
display: flex;
flex-direction: column;
}
.panel>div {
order: 1;
}
.panel>.panelWrapperMom {
order: 2;
}
.panel>.bottomButtonsContainer {
order: 3;
display: flex;
flex-direction: column;
}
.panel>.bottomButtonsContainer #start_new_project {
order: 1;
margin: 7px 10px 0px 10px;
}
.panel>.bottomButtonsContainer #open_project {
order: 2;
margin: 7px 10px 0px 10px;
}
.panel>.bottomButtonsContainer #save_project {
order: 3;
margin: 7px 10px 0px 10px;
}
.panel>.bottomButtonsContainer #add_font {
order: 4;
margin: 7px 10px 0px 10px;
}
.panel>.bottomButtonsContainer #upload_audio {
order: 5;
margin: 7px 10px 0px 10px;
}
.panel>.bottomButtonsContainer #exporter_open {
order: 6;
margin: 7px 10px 0px 10px;
}
.panel>.bottomButtonsContainer #hide_ui {
order: 7;
margin: 7px 10px;
}
.panelWrapper {}
.panelControlsWrapper {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
grid-row-start: 2;
grid-column-start: 1;
grid-column-end: 7;
display: flex;
flex-wrap: wrap;
}
.propWrapper{
/* margin-top: 5px;*/
margin-bottom: 3px;
}
.xWrapper,
.yWrapper,
.fontFamilyWrapper,
.fontFamilyWrapper,
.rotationWrapper,
.letterDelayWrapper,
.transformOriginWrapper,
.widthWrapper,
.fontSizeWrapper,
.letterSpacingWrapper,
.lineHeighWrapper,
.textWrapper,
.colorWrapper,
.mirror_xWrapper,
.mirror_yWrapper,
.mirror_xyWrapper,
.mirror_x_distanceWrapper,
.mirror_y_distanceWrapper,
.alignButtons,
.textAlignButtons {
border: none;
padding: 0px;
display: flex;
width: 100%;
}
.colorTitleWrapper{
flex-grow: 0;
/* background: blue; */
width: fit-content;
margin-right: 10px;
}
.colorTitleWrapper + div div div:first-of-type{
border: none !important;
margin-right: 5px;
}
.colorTitleWrapper + div{
flex-grow: 1 !important;
}
.textWrappingButton {
margin-left: 30px;
}
.textWrappingButton.active {
background: white;
}
.alignButtonsVertical {
padding-bottom: 10px;
}
.alignButtons {
display: flex;
width: 50%;
padding-top: 10px;
padding-bottom: 10px;
}
.xWrapper {}
.yWrapper {}
.mirror_xWrapper {}
.mirror_xWrapper input,
.mirror_yWrapper input,
.mirror_xyWrapper input {
margin: 0px 10px;
}
.mirror_xyWrapper input[type=checkbox] + label::after, .mirror_xWrapper input[type=checkbox] + label::after, .mirror_yWrapper input[type=checkbox] + label::after {
content: ' OFF';
}
.mirror_xyWrapper input[type=checkbox]:checked + label, .mirror_xWrapper input[type=checkbox]:checked + label, .mirror_yWrapper input[type=checkbox]:checked + label {
color: #1cba94;
}
.mirror_xyWrapper input[type=checkbox]:checked + label::after, .mirror_xWrapper input[type=checkbox]:checked + label::after, .mirror_yWrapper input[type=checkbox]:checked + label::after {
content: ' ON';
}
.mirror_xyWrapper input[type=checkbox], .mirror_xWrapper input[type=checkbox], .mirror_yWrapper input[type=checkbox] {
position: absolute;
width: 27px;
opacity: 0;
cursor: pointer;
}
.audio_min_max {
padding: 5px;
box-sizing: border-box;
display: grid;
/* flex-direction: column; */
grid-template-columns: 1fr 1fr;
row-gap: 7px;
}
.sourceLabel_Cont{
font-variation-settings: 'wght' 500;
}
.source_Dom_Cont {
padding: 5px;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
border-top: 1px dashed #91919177;
/* padding-top: 10px; */
margin-top: 10px;
margin: 10px 0px;
border-bottom: 1px dashed #91919177;
}
.source_Dom_Cont select {
background-color: white;
box-sizing: border-box;
border: 1px solid transparent;
color: rgb(0, 0, 0);
padding: 1px 6px;
font-style: inherit;
font-variant: inherit;
font-weight: inherit;
font-stretch: inherit;
font-size: inherit;
line-height: inherit;
font-family: inherit;
font-optical-sizing: inherit;
font-kerning: inherit;
font-feature-settings: inherit;
outline: none;
text-align: left;
/* width: 100%; */
border-radius: 0px;
font-variation-settings: "wght" 700;
height: 26px;
margin-right: 10px;
width: -webkit-fill-available;
margin-left: 10px;
}
.audio_min_Cont,
.audio_max_Cont,
.letterDelayCont {
grid-column-start: 1;
grid-column-end: 3;
display: flex;
justify-content: space-between;
}
.letterDelayCont{
}
.letterDelayCont input {
margin-left: 10px;
flex-shrink: 1 !important;
width: 100%;
}
.audio_min_max input, .letterDelayCont input {
background: rgb(255, 255, 255);
border: none;
color: rgb(0, 0, 0);
padding: 3px 6px 1px;
font-style: inherit;
font-variant: inherit;
font-weight: inherit;
font-stretch: inherit;
font-size: inherit;
line-height: inherit;
font-family: inherit;
font-optical-sizing: inherit;
font-kerning: inherit;
font-feature-settings: inherit;
outline: none;
/* cursor: ew-resize; */
/* width: 100%; */
height: calc(100% - 4px);
border-radius: 0px;
margin-bottom: 0px;
text-align: center;
font-variation-settings: "wght" 700;
}
.audio_min_max label, .letterDelayCont label{
display: flex;
align-items: center;
}
.audioOptions {
margin-top: 5px;
margin-bottom: 5px;
padding: 0px 7px 7px 7px;
background: rgba(163, 163, 163, 0.4) !important;
}
.fontVariationAxesContWrapper .audioOptions{
margin-left: -10px;
}
.audioOptions label {
color: black;
font-family: 'Tonka';
font-variation-settings: 'wght' 500;
}
.mirror_xWrapper label,
.mirror_yWrapper label,
.mirror_xyWrapper label {
color: #ea2333;
}
input[type=checkbox] {}
input[type=checkbox]:checked+label {
color: #1cba94;
padding-left: 4px;
}
.muteSvg, .soundonSvg{
display: flex;
align-items: center;
justify-content: center;
}
.consentContainer{
display: flex;
align-items: center;
justify-content: center;
}
.checkmark, .unchecked {
height: 15px;
width: 15px;
background: rgb(163, 163, 163);
border-radius: 50%;
padding: 5px;
cursor: pointer;
margin: 5px;
}
.consentContainer input ~ .muteSvg {
display: block;
}
.consentContainer input:checked ~ .soundonSvg {
display: block;
}
.consentContainer input ~ .soundonSvg {
display: none;
}
.consentContainer input:checked ~ .muteSvg {
display: none;
}
.consentContainer input {
position: absolute;
opacity: 0 !important;
cursor: pointer;
height: 0;
width: 0;
-webkit-tap-highlight-color: transparent;
}
label svg{
width: 20px;
height: 20px;
}
.audioOptions input[type="radio"] {
appearance: none;
background-color: #fff;
margin: 0;
font: inherit;
color: black;
width: 1.15em;
height: 1.15em;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.audioOptions input[type="radio"]::before {
content: "";
width: 0.65em;
height: 0.65em;
border-radius: 50%;
opacity: 0;
transition: 120ms transform ease-in-out;
box-shadow: inset 1em 1em black;
}
.audioOptions input[type="radio"]:checked::before {
opacity: 1;
}
.sync_inputDom_Cont {
display: flex;
width: fit-content;
justify-content: center;
align-items: center;
column-gap: 5px;
margin: 5px 0px 5px 0px;
}
.sync_Dom {
padding: 5px;
display: flex;
flex-direction: column;
row-gap: 7px;
border-top: 1px dashed #91919177;
padding-top: 10px;
margin-top: 10px;
margin: 10px 0px 0px 0px;
}
.sync_Dom p{
text-align: center;
}
.sync_titleDom_Cont {
display: flex;
justify-content: space-between;
}
.mirror_yWrapper {}
.mirror_xyWrapper {}
.mirror_x_distanceWrapper {}
.mirror_y_distanceWrapper {}
.fontFamilyWrapper {}
.rotationWrapper {
border-top: 1px dashed #91919177;
padding-top: 10px;
}
.transformOriginWrapper {}
.widthWrapper {}
.heightWrapper {
display: none;
}
.fontSizeWrapper {}
.letterSpacingWrapper {}
.letterDelayWrapper {}
.lineHeighWrapper {}
.textWrapper {}
.colorWrapper {
border-top: 1px dashed #91919177;
/* margin-bottom: 10px;*/
padding-top: 10px;
}
.letterDelaysContWrapper{
border-top: 1px dashed #91919177;
padding-top: 10px;
margin-top: 10px;
}
.mirror_xyInputWrapper, .mirror_yInputWrapper, .mirror_xInputWrapper{
margin-left: 0px !important;
}
span.icon {
font-family: 'VariableIcons';
font-size: 3em;
margin: 2px 5px;
line-height: 0.2em;
box-sizing: border-box;
}
.main_panel_button,
.vte_button {
background-color: white;
font-size: 1.1em;
padding: 5px 5px 4px 5px;
margin: 7px 10px;
display: flex;
flex-grow: 1;
justify-content: center;
border-radius: 10px;
border: none;
padding: 7px;
text-transform: uppercase;
width: calc(100% - 20px);
box-sizing: border-box;
cursor: pointer;
font-variation-settings: 'wght' 750, 'wdth' 100;
}
.main_panel_button:hover,
.vte_button:hover {
color: white;
background-color: black;
}
.removeButtonContainer {
display: flex;
padding-right: 10px;
padding-bottom: 10px;
}
.alignButtons img,
.textAlignButtons img {
height: 19px;
cursor: pointer;
margin: 5px 5px 2px 5px;
}
li.layerMover div {}
li.layerMover div.selected {
background: rgba(255, 255, 255, 1);
}
li.layerMover div.selected svg circle {
fill: #ea2333;
}
.letterDelaysContWrapper {
display: flex;
flex-direction: column;
width: 100%;
padding-bottom: 10px;
}
.letterDelaysContWrapper>*,
.fontVariationAxesContWrapper>* {
margin-left: calc(var(--left-pad) * (var(--depth) - 1));
}
.fontVariationAxesContWrapper {
display: flex;
flex-direction: column;
width: 100%;
padding-bottom: 10px;
margin-bottom: 10px;
border-bottom: 1px dashed #91919177;
border-top: 1px dashed #91919177;
margin-top: 10px;
}
.moveLayerButton {}
.removeLayerButton img,
.duplicateLayerButton img,
.addLayerButton img,
.moveLayerButton img {
height: 10px;
cursor: pointer;
}
.propTitleStuff {
color: red;
}
.addLayerButton {
width: calc(100% - 40px);
display: flex;
align-items: center;
justify-content: center;
/* padding-left: 15px; */
align-self: end;
margin-top: 5px;
}
.audioButton {
width: 17px;
margin: 2px 2px 2px 5px;
flex-shrink: 0;
display: flex;
background: rgb(163, 163, 163);
border-radius: 50%;
height: 17px;
align-self: center;
display: flex;
align-items: center;
justify-content: center;
padding: 2px;
box-sizing: border-box;
cursor: pointer
}
.audioButton:hover, .checkmark:hover, .unchecked:hover {
background: #1cba94;
}
.audioButton img {
max-width: 70%;
}
.audioButton.active {
background: #1cba94;
}
.record_Dom_Cont{
display: flex;
align-items: center;
justify-content: space-around;
padding: 5px 0px 0px 0px;
}
.audio_recordButton,
.recordButton {
max-width: 50%;
margin: 2px;
flex-shrink: 0;
display: flex;
height: 17px;
align-self: center;
display: flex;
align-items: center;
justify-content: center;
padding: 2px;
box-sizing: border-box;
/* flex-grow: 1;*/
margin-top: 5px;
}
.audio_recordButton,
.recordButton, .recordButton label{
cursor: pointer;
}
.recordButton .buttonCont{
background: rgb(163, 163, 163);
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
padding: 0;
border-radius: 50%;
}
.audio_recordButton {
justify-content: flex-start;
}
.recordButton:hover .buttonCont{
background: #1cba94;
}
.recordButton img {
max-width: 70%;
/* margin-left: 2px;*/
width: 12px;
height: 12px;
}
.recordButton label {
margin-left: 8px;
white-space: nowrap;
}
.recordButton.active {
background: #1cba94;
}
.propInputWrapper{
flex-grow: 1 !important;
flex-shrink: 1 !important;
width: 100%;
margin-left: 10px;
}
.recording {
overflow: hidden;
background: rgb(255, 255, 255);
border: none;
color: rgb(0, 0, 0);
padding: 3px 6px 1px;
font-style: inherit;
font-variant: inherit;
font-weight: inherit;
font-stretch: inherit;
font-size: inherit;
line-height: inherit;
font-family: inherit;
font-optical-sizing: inherit;
font-kerning: inherit;
font-feature-settings: inherit;
outline: none;
cursor: ew-resize;
width: 100%;
height: calc(100% - 4px);
border-radius: 0px;
margin-bottom: 0px;
text-align: center;
font-variation-settings: "wght" 700;
}
.panelMomWrapper {
overflow-x: hidden;
}
/* ASYA: color picker can be changed like this */
.picker_wrapper.popup .picker_arrow::before {
/* background: pink;*/
}
.color_preview{
border: none !important;
}
input:disabled {
background: darkgrey;
color: lightgrey;
}
/* ASYA: most the following code does not work*/
.onOffIndicatorWrapper input[type=checkbox] + label::after {
content: ' OFF';
}
.onOffIndicatorWrapper input[type=checkbox]:checked + label{
color: #1cba94;
}
.onOffIndicatorWrapper input[type=checkbox]:checked + label::after{
content: ' ON';
}
.onOffIndicatorWrapper {
width: auto;
}

View file

@ -11,7 +11,7 @@ const Artboard = function(tp, domElement = false, autoInit = true) {
let x = 0;
let y = 0;
let props = {
backgroundColor: tp.core.types.rgba({
color: tp.core.types.rgba({
r: 74,
g: 94,
b: 181,
@ -100,25 +100,29 @@ const Artboard = function(tp, domElement = false, autoInit = true) {
};
const props2cppProps = (_props) => {
let cppProps = JSON.parse(JSON.stringify(_props));
let bgIsArray = Array.isArray(cppProps.backgroundColor);
if (bgIsArray && cppProps.backgroundColor.length === 4) {
let bgIsArray = Array.isArray(cppProps.color);
if (bgIsArray && cppProps.color.length === 4) {
cppProps.backgroundColor = cppProps.color;
delete cppProps.color;
// nothing to do
} else if (!bgIsArray && cppProps.backgroundColor.hasOwnProperty('r')) {
} else if (!bgIsArray && cppProps.color.hasOwnProperty('r')) {
cppProps.backgroundColor = [
cppProps.backgroundColor.r,
cppProps.backgroundColor.g,
cppProps.backgroundColor.b,
cppProps.backgroundColor.a
cppProps.color.r,
cppProps.color.g,
cppProps.color.b,
cppProps.color.a
];
} else if (!bgIsArray && cppProps.backgroundColor.default.hasOwnProperty('r')) {
delete cppProps.color;
} else if (!bgIsArray && cppProps.color.default.hasOwnProperty('r')) {
cppProps.backgroundColor = [
cppProps.backgroundColor.default.r,
cppProps.backgroundColor.default.g,
cppProps.backgroundColor.default.b,
cppProps.backgroundColor.default.a
cppProps.color.default.r,
cppProps.color.default.g,
cppProps.color.default.b,
cppProps.color.default.a
];
delete cppProps.color;
} else {
console.error('js::layer::props2cppProps', 'color could not be translated');
console.error('js::artboard::props2cppProps', 'color could not be translated');
}
return cppProps;
};
@ -146,6 +150,16 @@ const Artboard = function(tp, domElement = false, autoInit = true) {
panelPropTitle.innerHTML = friendlyName;
}
});
// should we have an audio object, let's inject the buttons, etc
if (typeof audio === 'object' && audio.hasOwnProperty('injectPanel')) {
const success = audio.injectPanel(this);
if (success) {
const e = new CustomEvent('injectedAll', {});
tp.getPanel().dispatchEvent(e);
}
} else {
console.log('Artboard::findInjectPanel', `cannot inject audio panel for ${this.id()} for some reason.`);
}
doItAgain = false;
}
}
@ -163,6 +177,35 @@ const Artboard = function(tp, domElement = false, autoInit = true) {
this.hide = () => {
// nothing to do
};
const getHierarchyPanelButton = () => {
if (hierarchyPanelButton === null) {
hierarchyPanelButton = tp.shadowRoot.querySelector(`.layerMover${this.id()}`);
}
return hierarchyPanelButton;
};
this.isSelected = () => {
const panel = getHierarchyPanelButton();
if (panel === null) {
return false;
} else {
const notSelected = panel.querySelector('.not-selected');
return !notSelected;
}
};
this.select = () => {
const panel = getHierarchyPanelButton();
const selectables = panel.querySelector('.not-selected');
if (selectables !== null && typeof selectables.dispatchEvent === 'function') {
var clickEvent = new MouseEvent("click", {
"view": window,
"bubbles": true,
"cancelable": false
});
selectables.dispatchEvent(clickEvent);
} else {
window.debugElement = panel;
}
};
// action
if (typeof domElement !== 'object') {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,396 @@
import {
sanitizeTheatreKey,
arraysEqual,
clone,
} from './utils.js';
const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) {
// private
let props = { };
if (typeof values === 'string') {
const audioID = sanitizeTheatreKey(values);
values = {};
values[audioID] = false;
}
if (typeof values === 'object') {
props[Object.keys(values)[0]] = tp.core.types.boolean(false);
}
const onValuesChangeCallbacks = [];
let hierarchyPanelButton = null;
let panelFinderTimeout = false;
// private functions
//
const onValuesChange = (values) => {
const n_callbacks = onValuesChangeCallbacks.length;
const successes = [];
if (n_callbacks > 0) {
for (let i = 0; i < n_callbacks; i++) {
if (typeof onValuesChangeCallbacks[i] === 'function') {
if(onValuesChangeCallbacks[i](values)) {
successes.unshift(i);
}
} else {
console.log('AudioLayer::onValuesChange', 'holy shit, the callback is not a function');
}
}
successes.forEach((i) => {
onValuesChangeCallbacks.splice(i, 1);
});
}
};
const getHierarchyPanelButton = () => {
if (hierarchyPanelButton === null) {
hierarchyPanelButton = tp.shadowRoot.querySelector(`.layerMover${this.id()}`);
}
return hierarchyPanelButton;
};
const getPanelPropDiamond = (panelPropTitle) => {
return panelPropTitle.previousSibling;
};
const getPanelPropInput = (panelPropTitle) => {
return panelPropTitle
.parentElement
.nextSibling
.querySelector('input');
};
const findInjectPanel = () => {
let doItAgain = true;
console.log('AudioLayer::findInjectPanel');
const panel = tp.getPanel();
let panelSuccesses = 0;
const audioIDs = Object.keys(this.theatreObject.value);
{ // PANEL
for (let i = 0; i < audioIDs.length; i++) {
const audioID = audioIDs[i];
let panelPropTitle = tp.getPanelPropTitle(audioID);
const panelPropContainer = tp.getPanelPropContainer(panelPropTitle);
if (panelPropTitle === null || panelPropContainer == null) {
continue;
}
// Phase 1) prevent unwanted interaction
// remove theatrejs listeners
// such as contextmenu
// NOTE: this does not work with stop(Immediate)Propagation here
const tmp = panelPropTitle.cloneNode(true);
panelPropTitle.parentNode.replaceChild(tmp, panelPropTitle);
panelPropTitle = tmp;
// remove diamond
const diamond = getPanelPropDiamond(panelPropTitle);
if (diamond !== null) {
diamond.remove();
}
// unchangeable boolean
const boolean = getPanelPropInput(panelPropTitle);
boolean.disabled = true;
boolean.parentElement.classList.add('onOffIndicatorWrapper');
boolean.classList.add('onOffIndicator');
boolean.style.width = 'auto';
const container = document.createElement('div');
container.innerHTML = `
<div class="main_panel_button button delete${audioID}">
delete
</div>
`;
for (let i = 0; i < container.children.length; i++) {
panelPropContainer.append(container.children[i]);
};
panelPropContainer.querySelector(`.button.delete${audioID}`)
.addEventListener('click', () => {
this.removeFile(audioID);
});
panelSuccesses++;
};
}
if (panelSuccesses === audioIDs.length) {
doItAgain = false;
}
if (doItAgain) {
clearTimeout(panelFinderTimeout);
panelFinderTimeout = setTimeout(() => {
this.findInjectPanel();
}, 30);
}
}
const findInjectSequencePanel = () => {
let doItAgain = true;
let sequencePanelSuccess = 0;
const sequencePanel = tp.getSequencePanel();
const sequencePanelLeft = tp.getSequencePanelLeft();
if (sequencePanelLeft !== null) { // SEQUENCE PANEL LEFT
const propTitles = sequencePanelLeft.querySelectorAll('[data-testid="SequencePanel-PropTitle"]');
const audioLayerTitle = (() => {
const titles = sequencePanelLeft.querySelectorAll('[data-testid="SequencePanel-Title"]');
for (let i = 0; i < titles.length; i++) {
if (titles[i].innerHTML === this.id()) {
return titles[i];
}
}
return null;
})();
if (audioLayerTitle !== null) {
const audioSection = audioLayerTitle.closest('li');
if (audioSection !== null) {
const audioPropTitles = audioSection.querySelectorAll('[data-testid="SequencePanel-PropTitle"]');
window.audioPropTitles = audioPropTitles;
audioPropTitles.forEach((audioPropTitle) => {
const diamond = (() => {
const potential = audioPropTitle.nextSibling;
if (potential !== null
&& potential.nodeName.toLowerCase() === 'div'
&& potential.querySelectorAll(':scope > [data-pi-key]').length === 3) {
return potential;
}
return null;
})();
if (diamond !== null) {
diamond.style.display = 'none';
}
if (audioPropTitle.innerHTML.length > config.audio.maxFilenameLength) {
const file = audioPropTitle.innerHTML;
const m = (config.audio.maxFilenameLength / 2) - 2;
audioPropTitle.innerHTML = file.substr(0,m) + '..' + file.substr(file.length - m, m);
}
});
sequencePanelSuccess++;
}
}
}
if (sequencePanelLeft !== null) { // SEQUENCE PANEL RIGHT
const audioSectionAnchor = sequencePanelLeft
.nextSibling
.querySelector(`[data-pi-key*="${this.id()}"]`);
if (audioSectionAnchor !== null) {
const audioSection = audioSectionAnchor.closest('li');
if (audioSection !== null) {
audioSection.addEventListener('contextmenu', (e) => {
e.stopPropagation();
e.stopImmediatePropagation();
});
audioSection.querySelectorAll('*').forEach((element) => {
element.addEventListener('contextmenu', (e) => {
e.stopPropagation();
e.stopImmediatePropagation();
});
});
sequencePanelSuccess++;
}
}
}
if (sequencePanelSuccess === 2) {
doItAgain = false;
}
if (doItAgain) {
clearTimeout(panelFinderTimeout);
panelFinderTimeout = setTimeout(() => {
this.findInjectSequencePanel();
}, 30);
}
};
const setTime = (audioID, start, stop) => {
audioID = sanitizeTheatreKey(audioID);
return new Promise((resolve) => {
const keyframes = [{
path: [audioID],
keyframes: [{
position: -1,
value: false,
type: 'hold',
},
{
position: start,
value: true,
type: 'hold',
},
{
position: stop,
value: false,
type: 'hold',
},
],
}];
if (tp.getKeyframes(this, [audioID]).length > 0) {
tp.studio.transaction(({
__experimental_deleteKeyframes
}) => {
__experimental_deleteKeyframes(this.theatreObject.props[audioID]);
});
}
tp.addKeyframes(this, keyframes).then(() => {
resolve();
});
});
};
const getValue = (audioID) => {
const values = this.theatreObject.value;
if (typeof values[audioID] !== 'undefined') {
return values[audioID];
} else {
return null;
}
};
const init = () => {
return new Promise((resolve) => {
this.theatreObject = tp.addObject(audioLayerID, this.props, onValuesChange);
tp.studio.transaction(({
set
}) => {
const mergedValues = {
...this.theatreObject.value,
...values
};
set(this.theatreObject.props, values ? mergedValues : this.theatreObject.value);
});
resolve();
});
};
const remove = () => {
tp.removeObject(audioLayerID);
};
// public
this.props = props;
this.findInjectPanel = findInjectPanel;
this.findInjectSequencePanel = findInjectSequencePanel;
this.addFile = (audioID) => {
return new Promise((resolve) => {
audioID = sanitizeTheatreKey(audioID);
const values = this.theatreObject.value;
values[audioID] = false;
props[audioID] = tp.core.types.boolean(values[audioID]);
this.props = props;
const setDummy = () => {
tp.changeObject(audioLayerID, {
dummy: true
});
};
onValuesChangeCallbacks.push((changedValues) => {
if (changedValues.dummy) {
// NOTE: any idea how to avoid this ugly nesting?
onValuesChangeCallbacks.push((againChangedValues) => {
const expectedValueKeys = Object.keys(values);
const givenValueKeys = Object.keys(againChangedValues);
if (arraysEqual(expectedValueKeys, givenValueKeys)) {
resolve();
return true;
} else {
return false;
}
});
tp.changeObject(audioLayerID, this.props);
return true;
} else {
return false;
}
});
setDummy();
});
};
this.removeFile = (audioID) => {
return new Promise((resolve) => {
audioID = sanitizeTheatreKey(audioID);
const values = clone(this.theatreObject.value);
delete values[audioID];
delete props[audioID];
this.props = props;
const setDummy = () => {
tp.changeObject(audioLayerID, {
dummy: true
});
};
onValuesChangeCallbacks.push((changedValues) => {
if (changedValues.dummy) {
// NOTE: any idea how to avoid this ugly nesting?
onValuesChangeCallbacks.push((againChangedValues) => {
const expectedValueKeys = Object.keys(values);
const givenValueKeys = Object.keys(againChangedValues);
if (arraysEqual(expectedValueKeys, givenValueKeys)) {
resolve();
return true;
} else {
return false;
}
});
tp.changeObject(audioLayerID, this.props);
return true;
} else {
return false;
}
});
setDummy();
if (!audio.audioPlayer.remove(audioID)) {
console.log('AudioLayer::remove', `could not remove ${audioID} from audioPlayer`);
}
});
};
this.setTime = setTime;
this.getValue = getValue;
this.isSelected = () => {
const panel = getHierarchyPanelButton();
if (panel === null) {
return false;
} else {
const notSelected = panel.querySelector('.not-selected');
return !notSelected;
}
};
this.select = () => {
const panel = getHierarchyPanelButton();
const selectables = panel.querySelector('.not-selected');
if (selectables !== null && typeof selectables.dispatchEvent === 'function') {
var clickEvent = new MouseEvent("click", {
"view": window,
"bubbles": true,
"cancelable": false
});
selectables.dispatchEvent(clickEvent);
} else {
window.debugElement = panel;
}
};
this.init = init;
this.id = () => {
return audioLayerID;
};
// well, I know this is a shitty name. My apologies. However, ..
this.prepareForDepartureFromThisBeautifulExperience = remove;
// action
if (autoInit) {
this.init();
}
};
export {
AudioLayer,
}

View file

@ -0,0 +1,160 @@
import {
clone,
sanitizeTheatreKey,
} from './utils.js';
const AudioPlayer = function() {
const audioElements = [];
let updateInterval = false;
let updateInterval_ms = 10;
const getAudioID = (filename) => {
let audioID = sanitizeTheatreKey(filename);
const duplicate = audioElements.findIndex((e) => e.audioID === audioID) !== -1;
if (duplicate) {
let index = 0;
let unique = false;
while (!unique) { // uuuh, while loops..
const newAudioID = `${audioID}_${index}`;
if (audioElements.findIndex((e) => e.audioID === newAudioID) === -1) {
audioID = newAudioID;
unique = true;
} else {
index++;
}
}
return audioID
}
return audioID;
};
this.addMany = (manyAudioElements) => {
manyAudioElements.forEach((e) => {
this.add(e.audioID, e.layerID, e.propTitle, e.file, e.startTime);
});
};
this.add = (audioID, layer, propTitle, file, startTime) => {
const layerID = typeof layer === 'string' ? layer : layer.id();
propTitle = Array.isArray(propTitle) ? propTitle.join('.') : propTitle;
if (!audioID) {
audioID = getAudioID(file);
}
const audioDomElement = new Audio(moduleFS.objectUrl(`${config.fs.idbfsAudioDir}/${file}`));
audioDomElement.loop = true;
audioDomElement.load();
//const audioDomElement = document.createElement('audio');
//audioDomElement.classList.add('invisible');
//audioDomElement.classList.add('audio_file');
//audioDomElement.classList.add('audioPlayer');
//audioDomElement.src = moduleFS.objectUrl(`${config.fs.idbfsAudioDir}/${file}`);
audioDomElement.loop = true;
audioElements.push({
audioID,
layerID,
propTitle,
audioDomElement,
file,
startTime
});
};
this.remove = (audioID) => {
const index = audioElements.findIndex((e) => { return e.audioID === audioID; });
if (index < 0) {
return false;
} else {
audioElements.splice(index, 1);
return true;
}
};
this.update = () => {
audioElements.forEach((audioElement, i) => {
if (tp.isPlaying() && !record.isRecording()) {
const shouldBePlaying = getAudioLayer().getValue([audioElement.audioID]);
if (shouldBePlaying !== null) {
if (shouldBePlaying && audioElement.audioDomElement.paused) {
// sequence position is always greater than startTime
// this is true, as it is written
const diff = tp.sheet.sequence.position - audioElement.startTime;
audioElement.audioDomElement.currentTime = diff;
audioElement.audioDomElement.play();
} else if (!shouldBePlaying && !audioElement.audioDomElement.paused) {
audioElement.audioDomElement.pause();
audioElement.audioDomElement.currentTime = 0;
}
} else {
console.log('AudioPlayer::update',`${audioElement.audioID} does not exist in audioLayer.`);
}
} else if (!audioElement.audioDomElement.paused) {
audioElement.audioDomElement.pause();
audioElement.audioDomElement.currentTime = 0;
}
});
};
this.audioElements = audioElements;
this.init = () => {
clearInterval(updateInterval);
updateInterval = setInterval(() => {
this.update();
}, updateInterval_ms);
};
let hot = false;
let startTime = false;
this.listener = (event) => {
if (event.detail === record.possibleStates.RECORDING) {
hot = clone(record.getHot());
startTime = tp.sheet.sequence.position;
const layerIDs = Object.keys(hot);
layerIDs.forEach((layerID) => {
const propTitles = Object.keys(hot[layerID]);
propTitles.forEach((propTitle) => {
const m = audio.getMapping()[layerID][propTitle];
if (m.addToTimeline) {
if (m.source === 'microphone') {
const waitForMicrophoneListener = (event) => {
if (event.detail.fileIsRead) {
const filename = event.detail.filename;
const audioID = getAudioID(filename);
this.add(audioID, layerID, propTitle, filename, startTime);
addAudioLayerTrack(audioID, startTime, tp.sheet.sequence.position);
window.removeEventListener(
'microphoneRecorder',
waitForMicrophoneListener);
}
};
window.addEventListener(
'microphoneRecorder',
waitForMicrophoneListener);
}
}
});
});
}
if (event.detail === record.possibleStates.NOT_RECORDING) {
const layerIDs = Object.keys(hot);
layerIDs.forEach((layerID) => {
const propTitles = Object.keys(hot[layerID]);
propTitles.forEach((propTitle) => {
const m = audio.getSavedMapping()[layerID][propTitle];
if (m.addToTimeline) {
if (m.source === 'microphone') {
// handled above, as we need to wait for the microphone recording to be written and read
// and we rather start waiting, when it has definitely not happened yet
} else {
const filename = m.source;
const audioID = getAudioID(filename);
this.add(audioID, layerID, propTitle, filename, startTime);
addAudioLayerTrack(audioID, startTime, tp.sheet.sequence.position);
}
}
});
});
window.removeEventListener('record', this.listener);
}
};
};
export {
AudioPlayer
};

View file

@ -6,7 +6,7 @@ const config = {
maximumPixelDensity: 3.0,
incrementPixelDensity: 0.01,
friendlyNames: {
'backgroundColor': 'Background<br>Color',
'color': 'Background<br>Color',
'x': 'Position X',
'y': 'Position Y',
'width': 'Artboard Width',
@ -45,6 +45,7 @@ const config = {
'color',
'letterDelays',
],
orderSpacing: 2,
friendlyNames: {
'fontFamily': 'Font Family',
'textAlignButtonsHorizontal': '',
@ -63,9 +64,9 @@ const config = {
'rotation': 'Rotation',
'transformOrigin': 'Rotation Origin',
'mirror_x': 'Mirror X',
'mirror_x_distance': 'Mirror X Distance',
'mirror_x_distance': 'X Distance',
'mirror_y': 'Mirror Y',
'mirror_y_distance': 'Mirror Y Distance',
'mirror_y_distance': 'Y Distance',
'mirror_xy': 'Mirrox XY',
'color': 'Color',
'letterDelays': 'Letter Delays',
@ -81,6 +82,68 @@ const config = {
zoomBaseFactor: 0.001,
zoomDynamicMax: 42,
},
audio: {
defaultRange: { // check audio.getDefaultRange for dynamic defaults
'textAlignment': [0, 1],
'fontSize_px': [42, 100],
'letterSpacing': [0, 1],
'lineHeight': [0, 1],
'rotation': [0, 180],
'mirror_x_distance': [0, 200],
'mirror_y_distance': [0, 70],
'color': [{'r': 0, 'g': 0, 'b': 0, 'a': 1}, {'r': 1, 'g': 1, 'b': 1, 'a': 1}],
'letterDelays': [0, 1000],
},
ignoreProps: {
artboard: ['x', 'y', 'zoom', 'pixelDensity', 'width', 'height'],
layer: ['transformOrigin', 'fontFamily', 'text', 'mirror_x', 'mirror_y', 'mirror_xy', 'height'],
},
maxFilenameLength: 24,
defaultSmoothing: 0.5,
analyser: {
fftSize: 256 * 8,
minDecibels: -90,
maxDecibels: -10,
smoothingTimeConstant: 0.85,
},
fftBandsAnalysed: 256 * 8,
fftBandsUsed: 256 / 2,
fftHeight: 256 / 4,
ignoreOutboundFrequencies: true,
pitchCombineFrequencies: false,
rolloverResetLoop: true,
colors: {
background_fill: 'hsla(166, 74%, 20%, 1.0)',
select_fill: 'hsla(166, 52%, 42%, 1.0)',
select_stroke: 'hsla(166, 74%, 92%, 1.0)',
select_stroke_width: 1,
highlight_stroke: 'hsla(64, 100%, 64%, 1.0)',
highlight_stroke_width: 1,
highlight_2_stroke: 'hsla(166, 74%, 64%, 1.0)',
frequency_fill: 'hsla(166, 74%, 84%, 1.0)',
options_r_background: 'hsla(360, 100%, 50%, 0.2)',
options_g_background: 'hsla(120, 100%, 50%, 0.2)',
options_b_background: 'hsla(240, 100%, 50%, 0.2)',
options_a_background: 'hsla(0, 0%, 100%, 0.2)',
options_background: 'hsla(0, 0%, 64%, 0.2)',
// overlay is the div used for mouse interactions
// it spans over the whole canvas.
// it can be used to adjust the color underneath.
overlay: 'rgba(28, 186, 148, 0.0)',
// gradient needs backgroundImage not backgroundColor in audio.js
//overlay: 'linear-gradient(red, yellow)',
overlay_stroke: 'none',
overlay_blendmode: 'color',
},
},
record: {
active: true,
ignoreProps: {
artboard: ['x', 'y', 'zoom', 'pixelDensity', 'width', 'height'],
layer: ['transformOrigin', 'fontFamily', 'text', 'mirror_x', 'mirror_y', 'mirror_xy'],
},
recordMapped: true,
},
midi: {
touchTimeThreshold_s: 0.5,
smoothingMix: 0.1,
@ -88,6 +151,7 @@ const config = {
fs: {
idbfsDir: '/idbfs',
idbfsFontDir: '/idbfs/fonts',
idbfsAudioDir: '/idbfs/audio',
idbfsTmpDir: '/idbfs/tmp',
},
timeline: {

View file

@ -200,7 +200,12 @@ const Exporter = function() {
};
const updateArtboardOptions = () => {
options.artboard = {...options.artboard, ...Module.getArtboardProps()};
const artboardValues = clone(Module.getArtboardProps());
if (typeof artboardValues.backgroundColor === 'object') {
artboardValues.color = artboardValues.backgroundColor;
delete artboardValues.backgroundColor;
}
options.artboard = {...options.artboard, ...artboardValues};
//options.artboard.width = getArtboard().theatreObject.value.width;
//options.artboard.height = getArtboard().theatreObject.value.height;
options.artboard.pixelDensity = getArtboard().theatreObject.value.pixelDensity;
@ -218,6 +223,10 @@ const Exporter = function() {
const setArtboardPropsToRenderDimensions = () => {
const artboardValues = clone(options.artboard);//{...options.artboard, ...renderDimensions};
if (typeof artboardValues.backgroundColor === 'object') {
artboardValues.color = artboardValues.backgroundColor;
delete artboardValues.backgroundColor;
}
const densityRatio = renderDimensions.width / options.artboard.width;
//artboardValues.pixelDensity *= densityRatio;
artboardValues.pixelDensity = densityRatio;
@ -227,6 +236,10 @@ const Exporter = function() {
|| currentArtboardValues.height !== artboardCppProps.height
|| currentArtboardValues.pixelDensity !== artboardCppProps.pixelDensity) {
window.isRenderDirty = true;
if (typeof artboardCppProps.color === 'object') {
artboardCppProps.backgroundColor = artboardCppProps.color;
delete artboardCppProps.color;
}
Module.setArtboardProps(artboardCppProps);
}
Module.setTimeScale(renderDimensions.timeScale);

View file

@ -55,6 +55,7 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
text: config.layer.defaultTexts[Math.floor(Math.random()*config.layer.defaultTexts.length)],
};
let updateValuesViaTheatre = true;
let lastValues = {};
let fontsHashMap = {};
let sequenceEventBuffer = [];
@ -99,14 +100,14 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
);
let axes = fontsAndAxes[selectedFontIndex].axes;
if (axes.length > 0) {
let variationAxes = {};
let variationAxes = {};
for (let a = 0; a < axes.length; a++) {
const sanity_minMax = axes[a].minValue < axes[a].maxValue;
const sanity_minDefault = axes[a].minValue <= axes[a].defaultValue;
const sanity_maxDefault = axes[a].maxValue >= axes[a].defaultValue;
if (sanity_minMax && sanity_minDefault && sanity_maxDefault) {
variationAxes[axes[a].name] = tp.core.types.number(axes[a].defaultValue, {
range: [axes[a].minValue, axes[a].maxValue],
range: [axes[a].minValue, axes[a].maxValue],
});
} else {
console.log('js::layer::selectFont', 'this axis is insane, abort', axes[a]);
@ -127,26 +128,21 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
return new Promise((resolve) => {
// NOTE: stupid hack, seems that theatrejs tries to be too smart
// detecting if reconfiguring the object is necessary.
// file bug report and test in future versions.
// file bug report and test in future versions?
//
// this overcomplicates some of our code though.. urgh..
// btw, we need a dummy property
// this does not work
//tp.changeObject(this.id(), {});
//tp.changeObject(this.id(), {dummy:true});
//updateTheatrePropsTimeout = setTimeout(() => {
tp.changeObject(this.id(), {dummy: true});
setTimeout(() => {
tp.changeObject(this.id(), props);
setTimeout(() => {
//updateTheatrePropsTimeout = false;
//this.afterUpdateTheatrePropsCallback();
this.findInjectPanel();
resolve();
}, 100);
}, 100);
//}, 100);
});
};
const getDefaultFont = (selectableFonts) => {
@ -204,6 +200,8 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
const onValuesChange = (values) => {
if (values.dummy === true)
return;
if (!updateValuesViaTheatre)
return;
window.isRenderDirty = true;
if (Object.keys(values).length > 1) {
values.text = values.text.trim();
@ -275,28 +273,19 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
const showBoundingBoxDiv = (boundingBox = false) => {
const boundingBoxDivId = `boundingBox-${this.id()}`;
//const layerBoxDivId = `layerBox-${this.id()}`;
if (document.querySelector(`#${boundingBoxDivId}`) === null) {
if (boundingBox === false) {
boundingBox = Module.getBoundingBox(this.id());
}
const artboard = window.getArtboard();
const boundingBoxDiv = document.createElement('div');
//const layerBoxDiv = document.createElement('div');
boundingBoxDiv.id = boundingBoxDivId;
boundingBoxDiv.style.position = 'fixed';
boundingBoxDiv.style.background = 'transparent';
boundingBoxDiv.style.border = '1px dashed dimgrey';
boundingBoxDiv.style.boxSizing = 'border-box';
//layerBoxDiv.id = layerBoxDivId;
//layerBoxDiv.style.position = 'fixed';
//layerBoxDiv.style.background = 'transparent';
//layerBoxDiv.style.border = '1px solid green';
//layerBoxDiv.style.boxSizing = 'border-box';
document.getElementById('body').append(boundingBoxDiv);
//document.getElementById('body').append(layerBoxDiv);
clearInterval(boundingBoxInterval);
boundingBoxInterval = setInterval(() => {
updateBoundingBoxDiv();
@ -311,7 +300,6 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
if (bb !== null) {
bb.remove();
}
//document.getElementById(`layerBox-${this.id()}`).remove();
clearInterval(boundingBoxInterval);
boundingBoxInterval = false;
};
@ -370,7 +358,7 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
set(this.theatreObject.props, values);
});
setTimeout(() => {
promises.push(this.findInjectPanelPromise);
promises.push(this.findInjectPanelPromise());
Promise.allSettled(promises).then(() => {
resolve();
if (config.autoSave && window.isInitialized) {
@ -425,7 +413,6 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
mom.append(upButton);
mom.append(downButton);
mom.append(removeButton);
//window.layerOrder.add(this.id());
panel.addEventListener('mouseover', showBoundingBoxDiv);
panel.addEventListener('mouseout', hideBoundingBoxDiv);
@ -460,11 +447,14 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
const panelProp= tp.getPanelPropTitle(propName);
const panelPropMom = tp.getPanelPropContainer(panelProp);
const newValue = active ? getArtboard().theatreObject.value.width : 0;
const textWrappingButton = tp.getPanel().querySelector('.textWrappingButton');
if (panelPropMom !== null && textWrappingButton !== null) {
if (active) {
panelPropMom.style.display = 'flex';
tp.getPanel().querySelector('.textWrappingButton').classList.add('active');
textWrappingButton.classList.add('active');
} else {
tp.getPanel().querySelector('.textWrappingButton').classList.remove('active');
textWrappingButton.classList.remove('active');
panelPropMom.style.display = 'none';
}
@ -475,6 +465,7 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
set(this.theatreObject.props[propName], newValue);
});
}
}
}
};
@ -489,6 +480,9 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
};
this.findInjectPanel = (resolve = false) => {
if (tp.studio.selection.length === 0 || (tp.studio.selection.length > 0 && tp.studio.selection[0].address.objectKey !== this.id())) {
if (typeof resolve === 'function') {
resolve();
}
return;
} else {
const panel = tp.getPanel();
@ -509,17 +503,33 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
const panelPropContainer = tp.getPanelPropContainer(panelPropTitle);
if (panelPropContainer !== null) {
panelPropContainers[propKey] = panelPropContainer;
const order_index = panelOrder.indexOf(propKey);
const order_index = panelOrder.indexOf(propKey) * config.layer.orderSpacing;
panelPropContainer.style.order = order_index;
if (propKey === 'fontVariationAxes'
|| propKey === 'letterDelays') {
panelPropContainer
.classList
.add(`${propKey}ContWrapper`);
panelPropContainer
.classList
.add(`propContWrapper`);
} else {
panelPropContainer
.classList
.add(`${propKey}Wrapper`);
panelPropContainer
.classList
.add(`propWrapper`);
const inputElement = panelPropContainer
.querySelector('input');
if (inputElement !== null) {
inputElement
.classList
.add(`${propKey}Input`);
inputElement
.classList
.add(`propInput`);
}
}
} else {
console.log('Layer::findInjectPanel',
@ -545,11 +555,29 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
const subFriendlyName = config.layer.friendlyNames[subUnfriendlyName];
if (e.innerHTML === subUnfriendlyName) {
e.innerHTML = subFriendlyName;
e.classList.add('propTitle');
e.classList.add(`${unfriendlyName}_${subUnfriendlyName}Title`);
}
});
});
} else {
panelPropTitles[unfriendlyName].innerHTML = friendlyName;
panelPropTitles[unfriendlyName].classList.add('propTitle');
panelPropTitles[unfriendlyName].classList.add(`${unfriendlyName}Title`);
let panelPropContainer = panelPropContainers[unfriendlyName];
for (let i = 0; i < panelPropContainer.children.length; i++) {
const child = panelPropContainer.children[i];
if (child.querySelector('input') !== null) {
child.classList.add(`${unfriendlyName}InputWrapper`);
child.classList.add('propInputWrapper');
}
if (child.contains(panelPropTitles[unfriendlyName])) {
child.classList.add(`${unfriendlyName}TitleWrapper`);
child.classList.add('propTitleWrapper');
}
};
}
}
});
@ -582,7 +610,7 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
alignCenterButton.innerHTML = `<img src="/web/assets/align-center-horizontal.svg" alt="alignCenter" />`;
alignRightButton.innerHTML = `<img src="/web/assets/align-right.svg" alt="alignRight" />`;
panelControlsWrapper.append(alignButtons);
const order_index = panelOrder.indexOf('alignButtonsHorizontal');
const order_index = panelOrder.indexOf('alignButtonsHorizontal') * config.layer.orderSpacing;
alignButtons.style.order = order_index;
alignButtons.classList.add('alignButtons');
alignButtons.classList.add('alignButtonsHorizontal');
@ -637,7 +665,7 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
alignCenterButton.innerHTML = `<img src="/web/assets/align-center-vertical.svg" alt="alignCenter" />`;
alignBottomButton.innerHTML = `<img src="/web/assets/align-bottom.svg" alt="alignBottom" />`;
panelControlsWrapper.append(alignButtons);
const order_index = panelOrder.indexOf('alignButtonsVertical');
const order_index = panelOrder.indexOf('alignButtonsVertical') * config.layer.orderSpacing;
alignButtons.style.order = order_index;
alignButtons.classList.add('alignButtons');
alignButtons.classList.add('alignButtonsVertical');
@ -684,8 +712,6 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
const alignButtonsHorizontal = panel.querySelector('.alignButtonsHorizontal');
const alignButtonsVertical = panel.querySelector('.alignButtonsVertical');
//panelControlsWrapper.add(alignButtonsHorizontal);
//panelControlsWrapper.add(alignButtonsVertical);
// first get previous textAlign buttons,
// if they are already there
@ -705,7 +731,7 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
textAlignCenterButton.innerHTML = `<img src="/web/assets/align-text-center.svg" alt="textAlignCenter" />`;
textAlignRightButton.innerHTML = `<img src="/web/assets/align-text-right.svg" alt="textAlignRight" />`;
textWrappingButton.innerHTML = `<img src="/web/assets/text-wrapping.svg" alt="textWrapping" />`;
const order_index = panelOrder.indexOf('textAlignButtonsHorizontal');
const order_index = panelOrder.indexOf('textAlignButtonsHorizontal') * config.layer.orderSpacing;
textAlignButtons.style.order = order_index;
panelControlsWrapper.append(textAlignButtons);
textAlignButtons.classList.add('textAlignButtons');
@ -743,24 +769,6 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
}
togglePanelProp('width', this.theatreObject.value.width > 0, false);
let bottomButtonsContainer = panel.querySelector('.bottomButtonsContainer');
if (bottomButtonsContainer === null) {
bottomButtonsContainer = document.createElement('div');
bottomButtonsContainer.classList.add("bottomButtonsContainer");
panel.append(bottomButtonsContainer);
}
if (bottomButtonsContainer.querySelector('.vte_button') === null) {
const addFontButton = document.createElement('div');
addFontButton.classList.add('vte_button');
addFontButton.style.cursor = 'pointer';
addFontButton.innerHTML = "add Font";
addFontButton.addEventListener('click', (clickEvent) => {
addUserFont();
});
bottomButtonsContainer.append(addFontButton);
}
doItAgain = false;
clearTimeout(panelFinderTimeout);
panelFinderTimeout = false;
@ -792,8 +800,27 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
panel.addEventListener("mouseover", showBoundingBoxDivIfSelected);
panel.addEventListener("mouseleave", hideBoundingBoxDiv);
// should we have an audio object, let's inject the buttons, etc
if (typeof audio === 'object' && typeof audio.injectPanel === 'function') {
const success = audio.injectPanel(this);
if (success) {
const e = new CustomEvent('injectedAll', {});
tp.getPanel().dispatchEvent(e);
}
} else {
console.log('Layer::findInjectPanel', `cannot inject audio panel for ${this.id()} for some reason.`);
}
// should we have a record object, let's inject the buttons, etc
if (config.record.active) {
if (typeof record === 'object' && record.hasOwnProperty('injectPanel')) {
record.injectPanel(this);
} else {
console.log('Layer::findInjectPanel', `cannot inject record panel for ${this.id()} for some reason.`);
}
}
injectedPanel = true;
const detail = {titles: Object.keys(panelPropTitles), containers: Object.keys(panelPropContainers)};
const detail = {layerID: this.id(), titles: Object.keys(panelPropTitles), containers: Object.keys(panelPropContainers)};
const e = new CustomEvent('injected', {detail});
tp.getPanel().dispatchEvent(e);
if (typeof resolve === 'function') {
@ -820,43 +847,6 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
}
return letterDelays.hasOwnProperty(prop[0]);
};
const addUserFont = () => {
return new Promise((resolve, reject) => {
uploadFile('font')
.then((fontFile) => {
moduleFS
.save(fontFile)
.then(() => {
getFontsAndAxes()
.then((newFontsAndAxes) => {
// let's select the new uploaded font
if (newFontsAndAxes.length > 0) {
const path = newFontsAndAxes[0].fontPath;
this.selectFont(path);
// we need to update theatreprops
// a bit awkwardly twice
//
// first like this
this.updateTheatreProps()
.then(() => {
// and again with a transaction
const hash = props.fontFamily.default;
tp.studio.transaction(({
set
}) => {
set(this.theatreObject.props.fontFamily, hash);
});
this.findInjectPanel();
resolve();
});
} else {
reject();
}
});
});
});
});
};
this.handleSequenceEvent = (detail, updateTheatre = true) => {
return new Promise((resolve) => {
tp.friendlySequenceNames();
@ -1030,6 +1020,9 @@ const Layer = function(tp, layerID, fontsAndAxes, autoInit = true) {
return cppProps;
};
this.onValuesChange = onValuesChange;
this.updateValuesViaTheatre = (doIt) => {
updateValuesViaTheatre = doIt;
};
this.prepareForDepartureFromThisBeautifulExperience = () => {
this.hide();
hideBoundingBoxDiv();

View file

@ -26,6 +26,17 @@ import {
ModuleFS
} from './moduleFS.js';
import {
Audio
} from './audio.js';
import {
Record
} from './record.js';
import {
AudioLayer
} from './audioLayer.js';
//import {
//MidiController
//} from './midiController.js';
@ -44,27 +55,35 @@ import {
Config
} from './config.js';
window.uploadFile = uploadFile;
window.downloadFile = downloadFile;
window.isInitialized = false;
window.hashFromString = hashFromString;
const config = new Config();
window.config = config;
const tp = new TheatrePlay();
window.tp = tp;
const layers = [];
const layersById = {};
const layerOrder = new LayerOrder();
window.layerOrder = layerOrder;
const fontsAndAxes = [];
let artboard;
const exporter = new Exporter();
const interactor = new Interactor();
const moduleFS = new ModuleFS();
window.moduleFS = moduleFS;
const record = new Record(tp);
const audio = new Audio(tp, record);
const audioLayers = [];
// globally reachables
// classes
window.config = config;
window.tp = tp;
window.layerOrder = layerOrder;
window.moduleFS = moduleFS;
window.record = record;
window.audio = audio;
// utilities
window.uploadFile = uploadFile;
window.downloadFile = downloadFile;
window.isInitialized = false;
window.panelFinderTimeout = false;
const sequenceEventBuffer = {};
//const midiController = new MidiController();
@ -86,7 +105,7 @@ const getAbout = () => {
const buttonExp = textParent.querySelector(".expandText");
if (buttonExp === null) {
console.error("Could not find .expandText within .textParent");
console.error("Main::getAbout","Could not find .expandText within .textParent");
return;
}
@ -109,10 +128,7 @@ window.showAbout = () => {
if (getAbout() === null) {
return;
}
getAbout().classList.remove("hidden");
}
window.hideAbout = () => {
if (getAbout() !== null) {
@ -139,6 +155,16 @@ const findInjectPanel = () => {
bottomButtonsContainer.classList.add("bottomButtonsContainer");
panel.append(bottomButtonsContainer);
}
const hideuiButton = document.querySelector('#hide_ui');
if (hideuiButton !== null) {
bottomButtonsContainer.append(hideuiButton);
hideuiButton.classList.add("main_panel_button");
}
const audiofileButton = document.querySelector('#upload_audio');
if (audiofileButton !== null) {
bottomButtonsContainer.append(audiofileButton);
audiofileButton.classList.add("main_panel_button");
}
const exportButton = document.querySelector('#exporter_open');
if (exportButton !== null) {
bottomButtonsContainer.append(exportButton);
@ -164,6 +190,11 @@ const findInjectPanel = () => {
bottomButtonsContainer.append(startNewButton);
startNewButton.classList.add("main_panel_button");
}
const addFontButton = document.querySelector('#add_font');
if (addFontButton !== null) {
bottomButtonsContainer.append(addFontButton);
addFontButton.classList.add("main_panel_button");
}
} else {
setTimeout(() => {
findInjectPanel();
@ -223,7 +254,7 @@ window.onload = () => {
}
tp.studio.onSelectionChange((newSelection) => {
if (newSelection.length > 0) {
[getArtboard(), getLayers()].flat().forEach((e) => {
[getArtboard(), getLayers(), getAudioLayers()].flat().forEach((e) => {
if (e.id() === newSelection[0].address.objectKey) {
if (e.id().indexOf('layer-') === 0) {
e.findInjectPanel();
@ -233,12 +264,15 @@ window.onload = () => {
}, 60);
} else if (e.id() === 'artboard') {
e.findInjectPanel();
} else if (e.id().indexOf('audio') === 0) {
e.findInjectPanel();
}
}
});
}
findInjectPanel();
tp.friendlySequenceNames();
console.log('Main::selectionChange',newSelection.length > 0 ? newSelection[0].address.objectKey : 'aah nothing');
});
});
// ABOUT BEGIN
@ -299,8 +333,31 @@ const resize = () => {
Module.windowResized(Math.round(width * ratio), Math.round(height * ratio));
};
const setLoadingDoneHook = () => {
let addedListener = false;
const loadingInjectInterval = setInterval(() => {
if (!addedListener) {
const panel = tp.getPanel();
if (panel !== null) {
if (audio.injectedPanelAtLeastOnce) {
window.setLoadingDone();
} else {
addedListener = true;
const injectedAll = () => {
window.setLoadingDone();
tp.getPanel().removeEventListener('injectedAll', injectedAll);
};
tp.getPanel().addEventListener('injectedAll', injectedAll);
}
clearInterval(loadingInjectInterval);
}
}
}, 20);
};
const postModuleInitialized = () => {
window.setLoadingTask('setting up animation', 80);
setLoadingDoneHook();
moduleFS.init()
.then(() => {
artboard = new Artboard(tp, content);
@ -309,13 +366,13 @@ const postModuleInitialized = () => {
tp.connectModuleCallbacks();
exporter.init();
getFontsAndAxes();
window.setLoadingTask('loading project', 85);
tp.loadProject().then(() => {
interactor.init();
resize();
adjustPanel();
window.setLoadingTask('setting up animation', 100);
window.setLoadingTask('setting up whatever else is necessary', 100);
window.isInitialized = true;
window.setLoadingDone();
window.autoSaveInterval = setInterval(() => {
if (config.autoSave && window.isInitialized) {
tp.saveProject();
@ -394,10 +451,32 @@ const listAvailableFontsAndAxes = () => {
window.listAvailableFontsAndAxes = listAvailableFontsAndAxes;
window.getFontsAndAxes = getFontsAndAxes;
window.getArtboard = () => {
return artboard;
};
window.getLayers = () => {
return layers;
};
window.getAudioLayers = () => {
return audioLayers;
};
// for now we changed the design to have only one audio layer
window.getAudioLayer = () => {
return audioLayers.length > 0 ? audioLayers[0] : false;
};
window.getLayer = (layerID = tp.studio.selection[0].address.objectKey) => {
if (layerID === 'artboard') {
return artboard;
} else {
//return layers.find((layer) => layer.id() === layerID);
return layersById[layerID];
}
};
window.moveLayerUp = (layerID) => {
layerOrder.moveUp(layerID);
};
@ -406,10 +485,6 @@ window.moveLayerDown = (layerID) => {
layerOrder.moveDown(layerID);
};
window.getArtboard = () => {
return artboard;
};
const addLayer = (autoInit = true) => {
const layerID = Module.addNewLayer();
const layer = new Layer(tp, layerID, fontsAndAxes, autoInit);
@ -444,24 +519,21 @@ const duplicateLayer = (originalLayer) => {
const givenKeys = e.detail.titles;
let allKeysFound = true;
for (let i = 0; i < originalKeys.length; i++) {
//const originalValue = originalValues[originalKeys[i]];
if (givenKeys.indexOf(originalKeys[i]) < 0) {
//delete originalValues[originalKeys[i]];
allKeysFound = false;
}
};
}
if (allKeysFound) {
tp.getPanel().removeEventListener("injected", addKeyframes);
}
tp.addKeyframes(newLayer, originalKeyframes).then(() => {
if (allKeysFound) {
resolve();
};
}
});
};
tp.getPanel().addEventListener("injected", addKeyframes);
newLayer.select();
//tp.shadowRoot.querySelector(`.layerMover${newLayer.id()} div`).click();
});
});
};
@ -477,9 +549,33 @@ const deleteLayer = (id, saveProject = true) => {
index = i;
}
}
// delete from audio
if (typeof audio.getMapping() === 'object' && typeof audio.getMapping()[id] === 'object') {
delete audio.getMapping()[id];
}
if (typeof audio.getSavedMapping() === 'object' && typeof audio.getSavedMapping()[id] === 'object') {
delete audio.getSavedMapping()[id];
}
if (typeof audio.canvasCombos === 'object') {
Object.keys(audio.canvasCombos).forEach((propTitle) => {
if (audio.canvasCombos[propTitle][2] === id) {
delete audio.canvasCombos[propTitle];
}
});
}
const wasSelected = layers[index].isSelected();
layers[index].prepareForDepartureFromThisBeautifulExperience();
layers.splice(index, 1);
delete layersById[id];
if (wasSelected) {
if (layers.length > index + 1) {
layers[index].select();
} else if (layers.length > 0) {
layers[layers.length - 1].select();
} else {
artboard.select();
}
}
if (saveProject) {
setTimeout(() => {
tp.saveProject();
@ -487,6 +583,77 @@ const deleteLayer = (id, saveProject = true) => {
}
};
const addAudioLayer = (filename = false) => {
return new Promise((resolve) => {
const audioLayerID = (() => {
if (audioLayers.length === 0) {
return 'audio';
}
let index = 0;
for (let i = 0; i < audioLayers.length; i++) {
if (audioLayers.findIndex((audioLayer) => audioLayer.id() === `audio-${index}`) < 0) {
break;
}
index++;
}
return `audio-${index}`;
})();
const audioLayer = new AudioLayer(tp, audioLayerID, filename, false);
layersById[audioLayerID] = audioLayer;
audioLayers.push(audioLayer);
audioLayer.init().then(() => {
resolve(audioLayer);
});
});
};
const addExistingAudioLayer = (audioLayerID, values = false) => {
return new Promise((resolve) => {
const audioLayer = new AudioLayer(tp, audioLayerID, values, false);
audioLayers.push(audioLayer);
layersById[audioLayerID] = audioLayer;
audioLayer.init().then(() => {
resolve(audioLayer);
});
});
};
const deleteAudioLayer = (audioLayerID) => {
tp.removeObject(audioLayerID);
let index = 0;
for (let i = 0; i < audioLayers.length; i++) {
if (audioLayers[i].id() === audioLayerID) {
index = i;
}
}
audioLayers.splice(index, 1);
//delete audioLayersById[id];
};
const addAudioLayerTrack = (filename, start = 0, end = 1) => {
const addTrack = () => {
return new Promise((resolve) => {
audioLayers[0].addFile(filename).then(() => {
audioLayers[0].setTime(filename, start, end).then(() => {
resolve();
});
});
});
};
return new Promise((resolve) => {
if (audioLayers.length < 1) {
addAudioLayer().then((audioLayer) => {
addTrack().then(() => {
resolve();
});
});;
} else {
addTrack().then(() => {
resolve();
});
}
});
};
// TODO: better function names
// because, come on. it may be funny for a second
// but tolerance for fun is low when you're grumpy
@ -511,10 +678,102 @@ window.duplicateLayer = (layer) => {
window.addLayer = addLayer;
window.addExistingLayer = addExistingLayer;
window.deleteLayer = deleteLayer;
window.addAudioLayer = addAudioLayer;
window.addExistingAudioLayer = addExistingAudioLayer;
window.deleteAudioLayer = deleteAudioLayer;
window.addAudioLayerTrack = addAudioLayerTrack;
window.renderFrames = exporter.renderFrames;
const layer_panel = document.querySelector('#layer_panel');
const initPanels = () => {
//makeDraggable(layer_panel);
const ui = (show) => {
if (show && tp.studio.ui.isHidden) {
tp.studio.ui.restore();
} else if (!show && !tp.studio.ui.isHidden) {
tp.studio.ui.hide();
}
};
const handleUiKeypress = (e) => {
if (e.key.toLowerCase() === 'q') {
document.removeEventListener('keypress', handleUiKeypress);
ui(true);
}
};
const initPanels = () => {
let hideuiButton = document.querySelector('#hide_ui');
if (hideuiButton === null) {
hideuiButton = tp.getPanel().querySelector('#hide_ui');
}
if (hideuiButton !== null) {
hideuiButton.addEventListener('click', () => {
ui(false);
document.addEventListener('keypress', handleUiKeypress);
});
}
let audiofileButton = document.querySelector('#upload_audio');
if (audiofileButton === null) {
audiofileButton = tp.getPanel().querySelector('#upload_audio');
}
if (audiofileButton !== null) {
audiofileButton.addEventListener('click', () => {
uploadFile('audio')
.then((file) => {
moduleFS
.save(file)
.then(() => {
audio.readAudioFiles();
});
});
});
}
let addFontButton = document.querySelector('#add_font');
if (addFontButton === null) {
addFontButton = tp.getPanel().querySelector('#add_font');
}
if (addFontButton !== null) {
addFontButton.addEventListener('click', () => {
addUserFont();
});
}
};
const addUserFont = () => {
uploadFile('font')
.then((fontFile) => {
moduleFS
.save(fontFile)
.then(() => {
getFontsAndAxes()
.then((newFontsAndAxes) => {
// let's select the new uploaded font
// if we have a layer selected
const layer = getLayer();
if (layer.id().indexOf('layer-') === 0 && newFontsAndAxes.length > 0) {
const path = newFontsAndAxes[0].fontPath;
layer.selectFont(path);
// we need to update theatreprops
// a bit awkwardly twice
//
// first like this
layer.updateTheatreProps()
.then(() => {
// and again with a transaction
const hash = layer.props.fontFamily.default;
tp.studio.transaction(({
set
}) => {
set(layer.theatreObject.props.fontFamily, hash);
});
layer.findInjectPanel();
resolve();
});
} else {
reject();
}
});
});
});
};
window.addUserFont = addUserFont;

View file

@ -13,6 +13,9 @@ const ModuleFS = function() {
if (!FS.analyzePath(config.fs.idbfsFontDir).exists) {
FS.mkdir(config.fs.idbfsFontDir);
}
if (!FS.analyzePath(config.fs.idbfsAudioDir).exists) {
FS.mkdir(config.fs.idbfsAudioDir);
}
if (!FS.analyzePath(config.fs.idbfsTmpDir).exists) {
FS.mkdir(config.fs.idbfsTmpDir);
}
@ -35,12 +38,28 @@ const ModuleFS = function() {
});
};
this.readdir = (path) => {
return new Promise((resolve) => {
this.syncfs(MODE_READ_FROM_PERSISTENT)
.then(() => {
const analyzed = FS.analyzePath(path);
if (analyzed.exists && FS.isDir(analyzed.object.mode)) {
const content = FS.readdir(path)
.filter((e) => ['.','..'].indexOf(e) < 0);
resolve(content);
} else {
console.log('ModuleFS::readdir', `${path} is not a directory or does not exist`);
resolve([]);
}
});
});
};
// check utils::uploadFile() for details of file
this.save = (file) => {
return new Promise((resolve) => {
if (file.type.indexOf('font') >= 0 || file.hasOwnProperty('isFont') && file.isFont === true) {
var uint8View = new Uint8Array(file.arrayBuffer);
console.log('trying to save the font file, file, uint8View', file, uint8View);
if (!FS.analyzePath(`${config.fs.idbfsFontDir}/${file.name}`).exists) {
FS.createDataFile(config.fs.idbfsFontDir, file.name, uint8View, true, true);
}
@ -51,7 +70,6 @@ const ModuleFS = function() {
} else if (file.type.indexOf('zip') >= 0 || file.hasOwnProperty('isZip') && file.isZip === true) {
var uint8View = new Uint8Array(file.arrayBuffer);
var filePath = `${config.fs.idbfsTmpDir}/${file.name}`;
console.log(filePath);
if (!FS.analyzePath(filePath).exists) {
FS.createDataFile(config.fs.idbfsTmpDir, file.name, uint8View, true, true);
}
@ -59,6 +77,18 @@ const ModuleFS = function() {
.then(() => {
resolve(filePath);
});
} else if (file.type.indexOf('audio') === 0) {
var uint8View = new Uint8Array(file.arrayBuffer);
if (!FS.analyzePath(`${config.fs.idbfsAudioDir}/${file.name}`).exists) {
FS.createDataFile(config.fs.idbfsAudioDir, file.name, uint8View, true, true);
this.syncfs(MODE_WRITE_TO_PERSISTENT)
.then(() => {
resolve(true);
});
} else {
alert(`It seems as if an audiofile with the name "${file.name}" already exists. Please rename your file and upload again, thanks <3`);
resolve(false);
}
} else {
resolve(false);
}
@ -81,6 +111,33 @@ const ModuleFS = function() {
resolve(false);
}
};
const objectUrls = {};
this.objectUrl = (filePath) => {
if (typeof objectUrls[filePath] === 'undefined') {
const arr = FS.readFile(filePath);
let type = 'audio/wav';
const filesplit = filePath.split('.');
const extension = filesplit[filesplit.length - 1];
// is there a way to get mime type without guessing by extension?
if (extension === 'wav') {
type = 'audio/wav';
} else if (extension === 'mp3') {
type = 'audio/mpeg';
} else if (extension === 'ogg') {
type = 'audio/ogg';
} else if (extension === 'webm') {
type = 'audio/webm';
}
objectUrls[filePath] = URL.createObjectURL(
new Blob([arr], {
type
})
);
}
return objectUrls[filePath];
};
};
export {

View file

@ -0,0 +1,653 @@
import {
clone,
sequencialPromises,
toCssClass,
flattenObject,
deFlattenObject,
getNestedProperty,
} from './utils.js';
const LiveBuffer = function() {
// private
//
// constants
const NO_TIME = -1;
// variables
/// @brief valueBuffer stores all values.
/// it is an object with layerIDs/artboard as key
const valueBuffer = {};
// functions
const register = (id) => {
if (!valueBuffer.hasOwnProperty(id)) {
valueBuffer[id] = new Map();
}
};
const deregister = (id) => {
if (valueBuffer.hasOwnProperty(id)) {
delete valueBuffer[id];
}
};
/// @brief addValues
// values are expected to be
// {
// x: 42.0,
// fontSize_px: 24,
// whatever: "something",
// }
const addValues = (id, values, time_s, start_time_s) => {
const subValueBuffer = valueBuffer[id];
// delete between start_time_s and time_s
if (start_time_s !== NO_TIME) {
subValueBuffer.forEach((value, value_time_s) => {
if (value_time_s > start_time_s && value_time_s <= time_s) {
const keys = Object.keys(values);
for (let k = 0; k < keys.length; k++) {
delete subValueBuffer.get(value_time_s)[keys[k]];
if (Object.keys(subValueBuffer.get(value_time_s)).length === 0) {
subValueBuffer.delete(value_time_s);
break;
}
}
}
});
}
if (subValueBuffer.has(time_s)) {
subValueBuffer.set(time_s, {...subValueBuffer.get(time_s), ...values});
} else {
subValueBuffer.set(time_s, clone(values));
}
};
// get values and merge with previous
// 0.42s: { x: 4 }
// 0.64s: { y: 7 }
// 0.82s: { y: 12, z: 24 }
//
// return for 0.82s = { x: 4, y: 12, x: 24 }
const getValues = (id, time_s) => {
if (valueBuffer[id].size === 0) {
return {};
} else {
valueBuffer[id] = new Map([...valueBuffer[id].entries()].sort());
let mergedValues = {};
let didMergeValues = {};
valueBuffer[id].forEach((value, value_time_s) => {
if (value_time_s <= time_s) {
mergedValues = {...mergedValues, ...value};
} else {
if (Object.keys(didMergeValues).length === 0) {
didMergeValues = clone(mergedValues);
}
Object.keys(value).forEach((key) => {
if(!didMergeValues.hasOwnProperty(key)) {
mergedValues[key] = value[key];
}
});
}
});
return mergedValues;
}
};
// public
this.NO_TIME = NO_TIME;
this.addValues = addValues;
this.getValues = getValues;
this.register = register;
this.deregister = deregister;
};
const LiveUpdater = function(tp, buffy) {
const toUpdate = [];
const update = () => {
const time_s = tp.sheet.sequence.position;
}
this.add = (layer) => {
if (toUpdate.indexOf(layer) < 0) {
toUpdate.push(layer);
}
};
this.remove = (layer) => {
const index = toUpdate.indexOf(layer);
if (index >= 0) {
toUpdate.push(layer);
}
};
this.immediateUpdate = (layer, values) => {
const cv = clone(values);
const ctv = clone(layer.theatreObject.value);
if (cv.hasOwnProperty('color.r')) {
cv['color'] = {
r: cv['color.r'],
g: cv['color.g'],
b: cv['color.b'],
a: cv['color.a'],
};
delete cv['color.r'];
delete cv['color.g'];
delete cv['color.b'];
delete cv['color.a'];
}
flattenObject(cv, ['color']);
flattenObject(ctv, ['color']);
const v = {...ctv, ...cv};
deFlattenObject(v, ['color']);
const p = layer.values2cppProps(v);
if (p !== false) {
const id = layer.id();
if (id !== 'artboard') {
Module.setProps(p, layer.id());
} else {
Module.setArtboardProps(p);
}
}
};
};
const Record = function(tp) {
const NOT_RECORDING = 0;
const STARTING_RECORDING = 1;
const RECORDING = 2;
const STOPPING_RECORDING = 3;
this.possibleStates = {
NOT_RECORDING,
STARTING_RECORDING,
RECORDING,
STOPPING_RECORDING,
};
this.friendlyState = (state) => {
switch(state) {
case NOT_RECORDING: return 'NOT_RECORDING';
case STARTING_RECORDING: return 'STARTING_RECORDING';
case RECORDING: return 'RECORDING';
case STOPPING_RECORDING: return 'STOPPING_RECORDING';
}
};
const setIsRecording = (status) => {
isRecording = status;
window.dispatchEvent(new CustomEvent("record", {detail: status}));
console.log('setIsRecording', this.friendlyState(status));
};
const hot = {};
let isRecording = NOT_RECORDING;
const buffy = new LiveBuffer();
const liveUpdater = new LiveUpdater(tp, buffy);
let isInitialized = false;
let remember = {};
const init = () => {
if (!isInitialized) {
tp.core.onChange(tp.sheet.sequence.pointer.playing, (playing) => {
if (isRecording === RECORDING && !playing) {
// when you hit space to stop recording,
// we want to keep it paused
remember.isPlaying = false;
stopRecording();
}
});
isInitialized = true;
}
};
const isHot = (layerID, propTitle) => {
return typeof hot[layerID] === 'object'
&& typeof hot[layerID][propTitle] === 'object';
//return hot.hasOwnProperty(layerID)
//&& hot[layerID].hasOwnProperty(propTitle);
};
const addHot = (layerID, propTitle) => {
if (!isHot(layerID, propTitle)) {
if (!hot.hasOwnProperty(layerID)) {
hot[layerID] = {};
}
hot[layerID][propTitle] = {
recording: [],
};
}
buffy.register(layerID);
// handle UI only if layer is selected
if (getLayer(layerID).isSelected()) {
let cPropTitle = clone(propTitle);
// if colors are separate, there is still just one button
if (cPropTitle.indexOf('color.') === 0) {
cPropTitle = 'color';
}
const button = tp
.getPanelPropTitle(cPropTitle)
.parentNode.parentNode
.querySelector('.recordButton');
if (button !== null) {
button.classList.add('active');
}
}
};
const removeHot = (layerID, propTitle) => {
if (isHot(layerID, propTitle)) {
delete hot[layerID][propTitle];
}
// what if it is the last prop in the layer
if (hot.hasOwnProperty(layerID)) {
if (Object.keys(hot[layerID]).length === 0) {
delete hot[layerID];
buffy.deregister(layerID);
}
}
// handle UI only if layer is selected
if (getLayer(layerID).isSelected()) {
let cPropTitle = clone(propTitle);
// if colors are separate, there is still just one button
if (cPropTitle.indexOf('color.') === 0) {
cPropTitle = 'color';
}
const propTitleDom = tp.getPanelPropTitle(cPropTitle);
if (propTitleDom === null) {
// whoops, probably currently transitioning,
// so it will not be necessary to remove the class
// when it will be recreated on demand,
// it won't have the class anyways.
return;
}
const button = tp
.getPanelPropTitle(cPropTitle)
.parentNode.parentNode
.querySelector('.recordButton');
if (button !== null) {
button.classList.remove('active');
}
}
};
const addRecordButton = (layer, propTitle) => {
const panel = tp.getPanel();
const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle !== null) {
const container = panelPropTitle.parentNode.parentNode;
if (container === null) {
console.log("Record::addRecordButton",
`impossible! cannot find panelPropContainer for ${propTitle}`);
} else if (container.querySelector('.recordButton') !== null) {
// this is super verbose, let's not log by default
//console.log("Record::addRecordButton",
//`already added an record button for ${propTitle}`);
} else {
const button = document.createElement('div');
button.classList.add('recordButton');
button.classList.add(toCssClass(`recordButton${propTitle}`));
button.innerHTML = `<img src="/web/assets/record.svg" alt="record" />`;
container.append(button);
button.addEventListener('click', () => {
if(isRecording === RECORDING) {
stopRecording();
} else {
if (config.record.recordMapped) {
// make all mapped props hot and
Object.keys(audio.getMapping())
.forEach((layerID) => {
//if (getLayer(layerID).isSelected()) { // NOTE: multilayer recording
Object.keys(audio.getMapping()[layerID])
.forEach((propTitle) => {
addHot(layerID, propTitle);
});
//}
});
} else {
// only make this propTitle hot and
// register its layer for recording
addHot(layer.id(), propTitle);
}
startRecording();
}
});
//console.log("Record::addRecordButton",
//`added a record button for ${propTitle}`);
}
} else {
console.log("Record::addRecordButton",
`cannot find panelPropTitle for ${propTitle}`);
}
};
const toggleRecording = (propPaths = false) => {
if(isRecording === RECORDING) {
stopRecording();
} else {
// set microphone recording to false by default
if (!propPaths) {
// make all mapped props hot and
Object.keys(audio.getMapping())
.forEach((layerID) => {
//if (getLayer(layerID).isSelected()) { // NOTE: multilayer recording
Object.keys(audio.getMapping()[layerID])
.forEach((propTitle) => {
addHot(layerID, propTitle);
});
//}
});
} else if (Array.isArray(propPaths)) {
// only make these propTitles hot and
// register their layer for recording
propPaths.forEach((p) => {
if (typeof p === 'string') {
p = p.split('.');
}
if (Array.isArray(p)) {
const layerID = p[0];
const propTitle = p.slice(1).join('.');
addHot(layerID, propTitle);
}
});
}
startRecording();
}
};
const cloneInput = (layer, propTitle) => {
return;
const panel = tp.getPanel();
const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle !== null) {
const container = panelPropTitle.parentNode.parentNode;
if (container === null) {
console.log("Record::cloneInput",
`impossible! cannot find panelPropContainer for ${propTitle}`);
} else if (container.querySelector('input.recording') !== null) {
console.log("Record::cloneInput",
`already cloned input for ${propTitle}`);
} else {
const input = container.querySelector('input');
if (input === null) {
console.log("Record::cloneInput",
`uuh.. seems there is no input to clone for ${propTitle}`);
} else {
const input_clone = input.cloneNode();
input_clone.classList.value = '';
input_clone.classList.add('recording');
input.parentNode.after(input_clone);
input.setAttribute('data-previousDisplay', input.style.display);
input.style.display = 'none';
return input_clone;
}
}
}
return null;
};
const uncloneInput = (layer, propTitle) => {
return;
const panel = tp.getPanel();
const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle !== null) {
const container = panelPropTitle.parentNode.parentNode;
if (container === null) {
console.log("Record::uncloneInput",
`impossible! cannot find panelPropContainer for ${propTitle}`);
} else if (container.querySelector('input.recording') === null) {
console.log("Record::uncloneInput",
`already uncloned input for ${propTitle}`);
} else {
const input = container.querySelector('input:not(.recording)');
const input_clone = container.querySelector('input.recording');
if (input === null ) {
console.log("Record::uncloneInput",
`uuh.. seems there is no input for ${propTitle}`);
} else if (input_clone === null ) {
console.log("Record::uncloneInput",
`uuh.. seems there is no input_clone for ${propTitle}`);
} else {
input_clone.remove();
input.removeAttribute('data-previousDisplay');
const previousInputDisplay = input.getAttribute('data-previousDisplay');
input.style.display = previousInputDisplay;
}
}
}
};
const injectPanel = (layer) => {
//const flatValues = clone(layer.theatreObject.value);
//flattenObject(flatValues, ['color']);
//const props = Object.keys(flatValues);
//const layerType = layer.id().split('-')[0];
//props.forEach((propTitle) => {
//if (config.record.ignoreProps[layerType].indexOf(propTitle) < 0) {
//addRecordButton(layer, propTitle);
//}
//});
};
let lastPositions = {};
const addValue = (layerID,
propTitle,
value,
position = tp.sheet.sequence.position,
lastPosition = buffy.NO_TIME) => {
// NOTE: multilayer recording
if (!hot.hasOwnProperty(layerID) || !hot[layerID].hasOwnProperty(propTitle)) {
return;
}
hot[layerID][propTitle].recording.push({
position,
value,
type: 'linear',
});
const recording = {
[propTitle]: value,
};
if (!lastPositions.hasOwnProperty(layerID)) {
lastPositions[layerID] = {};
}
if (lastPosition === buffy.NO_TIME) {
if (!lastPositions[layerID].hasOwnProperty(propTitle)) {
lastPositions[layerID][propTitle] = position;
}
} else {
lastPositions[layerID][propTitle] = lastPosition;
}
buffy.addValues(layerID, recording, position, lastPositions[layerID][propTitle]);
};
const getValue = (layerID, position) => {
const merged = clone(buffy.getValues(layerID, position));
deFlattenObject(merged);
return merged;
};
const liveUpdate = (layer, position = tp.sheet.sequence.position) => {
const merged = getValue(layer.id(), position);
liveUpdater.immediateUpdate(layer, merged);
};
/// @brief = copies letterDelay value from audio mapping to the recorded value.
///
/// @param layer - a layer object
/// @param propPaths - two dimensional array of strings
///
/// @return
const syncLetterDelays = (layer, propPaths) => {
propPaths.forEach((path) => {
const oldLetterDelay = getNestedProperty(layer.theatreObject.value.letterDelays, path, true);
//const isOriginalSequenced = tp.isSequenced(path, layer);
const isLetterDelaySequenced = tp.isSequenced(["letterDelays", ...path], layer);
// we use audio.getSavedMapping(), because mapping may be removed directly after recording
const mapping = getNestedProperty(audio.getSavedMapping(), [layer.id(),...[path.join('.')]], true);
const newLetterDelay = (() => {
if (typeof mapping['letterDelay'] === 'undefined') {
return false;
}
return mapping['letterDelay'];
})();
if (newLetterDelay !== oldLetterDelay &&
!isLetterDelaySequenced &&
newLetterDelay !== false) {
const prop = getNestedProperty(layer.theatreObject.props.letterDelays, path);
tp.studio.transaction(({
set
}) => {
set(prop, newLetterDelay);
});
}
});
};
const startRecording = () => {
setIsRecording(STARTING_RECORDING);
console.log('Record::startRecording');
document.querySelector('#notice_recording')
.classList.add('visible');
document.querySelector('#notice_recording')
.classList.remove('impenetrable');
document.querySelector('#notice_recording .what p').innerHTML = 'recording';
document.querySelector('#notice_recording .details p').innerHTML = '<button onclick="record.stopRecording()">stop recording</button>';
if (!isInitialized) {
init();
}
lastPositions = {};
remember.isPlaying = tp.core.val(tp.sheet.sequence.pointer.playing);
tp.sheet.sequence.pause();
// if there are no sequenced values,
// we very very likely want to start recording from start
if (tp.getSequencePanelLeft() === null) {
tp.sheet.sequence.position = 0;
}
const layerKeys = Object.keys(hot);
layerKeys.forEach((layerID) => {
const layer = getLayer(layerID);
layer.updateValuesViaTheatre(false);
const propTitles = Object.keys(hot[layerID]);
propTitles.forEach((propTitle) => {
// NOTE: layerID is not actually used atm
// and should be the layer anyways
const input_clone = cloneInput(layerID, propTitle);
let lastPosition = buffy.NO_TIME;
if (input_clone !== null) {
//input_clone.addEventListener('change', () => {
//const position = tp.sheet.sequence.position;
//const value = parseFloat(input_clone.value);
//addValue(layerID, propTitle, value, position, lastPosition);
//const merged = getValue(layerID, position);
//liveUpdater.immediateUpdate(layer, merged);
//lastPosition = position;
//});
} else {
console.log('Record::startRecording', `whoops input_clone for ${propTitle} is null`);
}
});
//tp.sheet.sequence.position = 0;
tp.sheet.sequence.play();
});
setIsRecording(RECORDING);
};
const stopRecording = () => {
document.querySelector('#notice_recording')
.classList.add('visible');
document.querySelector('#notice_recording')
.classList.add('imprenetrable');
document.querySelector('#notice_recording .what p').innerHTML = 'digesting recording';
document.querySelector('#notice_recording .details p').innerHTML = 'please wait';
setIsRecording(STOPPING_RECORDING);
return new Promise((resolve) => {
const layerKeys = Object.keys(hot);
const promises = [];
layerKeys.forEach((layerID) => {
const layer = getLayer(layerID);
const propTitles = Object.keys(hot[layerID]);
const keyframes = [];
propTitles.forEach((propTitle) => {
audio.removeAudio(getLayer(layerID),propTitle);
// NOTE: layerID is not actually used atm
// and should be the layer anyways
uncloneInput(layerID, propTitle);
// special treatment if we have sperate RGBA for color
if (propTitle.indexOf('color.') === 0) {
if (propTitle === 'color.r') {
const recording = [];
hot[layerID]['color.r'].recording.forEach((rr, ri) => {
if (ri < hot[layerID]['color.r'].recording.length &&
ri < hot[layerID]['color.g'].recording.length &&
ri < hot[layerID]['color.b'].recording.length &&
ri < hot[layerID]['color.a'].recording.length) {
const r = clone(rr);
r.value = {
r: hot[layerID]['color.r'].recording[ri].value,
g: hot[layerID]['color.g'].recording[ri].value,
b: hot[layerID]['color.b'].recording[ri].value,
a: hot[layerID]['color.a'].recording[ri].value,
};
recording.push(r);
}
});
keyframes.push({
path: ['color'],
keyframes: recording,
});
}
} else {
keyframes.push({
path: propTitle.split('.'),
keyframes: hot[layerID][propTitle].recording,
});
}
});
promises.push(() => {
return new Promise((subResolve) => {
tp.addKeyframes(layer, keyframes).then(() => {
syncLetterDelays(layer, keyframes.map(k => k.path));
layer.updateValuesViaTheatre(true);
subResolve();
});
})
});
});
sequencialPromises(promises, () => {
Object.keys(hot).forEach((layerID) => {
Object.keys(hot[layerID]).forEach((propTitle) => {
removeHot(layerID, propTitle);
});
buffy.deregister(layerID);
});
document.querySelector('#notice_recording')
.classList.remove('visible');
console.log('Record::stopRecording', 'stopped recording');
setIsRecording(NOT_RECORDING);
if (remember.isPlaying) {
tp.sheet.sequence.play();
} else {
tp.sheet.sequence.pause();
}
resolve();
});
});
};
// public
this.addValue = addValue;
this.getValue = getValue;
this.liveUpdate = liveUpdate;
this.liveUpdater = liveUpdater;
this.addRecordButton = addRecordButton;
this.isHot = isHot;
this.addHot = addHot;
this.removeHot = removeHot;
this.getHot = () => {
return hot;
};
this.isRecording = () => {
return isRecording != NOT_RECORDING;
};
this.injectPanel = injectPanel;
this.startRecording = startRecording;
this.stopRecording = stopRecording;
this.toggleRecording = toggleRecording;
};
export {
Record
}

View file

@ -7,12 +7,10 @@ import {
clone,
getParents,
arraysEqual,
sequencialPromises,
getNestedProperty,
} from './utils.js';
//import {
//config
//} from './config.js';
const TheatrePlay = function(autoInit = false) {
//private
@ -25,10 +23,12 @@ const TheatrePlay = function(autoInit = false) {
const theatreObjects = {};
let theatrePanel = null;
let sequencePanelLeft = null;
const getSequencePanel = () => {
sequencePanelLeft = tp.shadowRoot.querySelector('[data-testid="SequencePanel-Object"]');
return sequencePanelLeft;
};
const getSequencePanelLeft = () => {
// if (sequencePanelLeft === null) {
sequencePanelLeft = tp.shadowRoot.querySelector('[data-testid="SequencePanel-Left"]');
// }
return sequencePanelLeft;
};
const getPanel = () => {
@ -96,7 +96,7 @@ const TheatrePlay = function(autoInit = false) {
if (typeof value === 'undefined') {
return false;
}
};
}
return this.sheet.sequence.__experimental_getKeyframes(prop);
};
// wtf, this function was being written in one go
@ -134,88 +134,126 @@ const TheatrePlay = function(autoInit = false) {
return keyframes;
};
const getSequenceButton = (path) => {
let t = getPanelPropTitle(path.join('.'));
let t = getPanelPropTitle(Array.isArray(path) ? path.join('.') : path);
if (t === null) {
return null;
}
return t.parentElement.querySelector('[title="Sequence this prop"]');
};
// no idea how to delete keyframes
// so we can only add
const isSequenced = (path, layer = getLayer()) => {
if (!Array.isArray(path)) {
path = path.split('.');
}
const prop = getNestedProperty(layer.theatreObject.props, path);
return studio.__experimental.__experimental_isPropSequenced(prop);
};
const setSequenced = (path, sequenced = true, layer = getLayer()) => {
if (!Array.isArray(path)) {
path = path.split('.');
}
return new Promise((resolve) => {
if(isSequenced(path, layer) === sequenced) {
resolve(true);
} else {
const prop = getNestedProperty(layer.theatreObject.props, path);
if (sequenced) {
tp.studio.__experimental.__experimental_setPropAsSequenced(prop);
} else {
tp.studio.__experimental.__experimental_setPropAsStatic(prop);
}
let count = 0;
const interval = setInterval(() => {
if (isSequenced(path, layer) === sequenced) {
clearInterval(interval);
resolve(true);
} else if (count >= 10) {
clearInterval(interval);
resolve(false);
}
count++;
}, 10);
}
});
};
/*
* layer = getLayer()
* keyframes = [
* {
* path: ['fontVariationAxes','Weight'],
* keyframes: [
* {
* "id": "yHFP-HCINm", // not sure if used when sent back
* "position": 0,
* "connectedRight": true, // optional
* "handles": [ 0.5, 1, 0.5, 0 ], // optional
* "type": "bezier", // optional
* "value": 200
* }, ...
* ]
* }, ...
* ]
*/
const addKeyframes = (layer, keyframes) => {
return new Promise((resolve) => {
const promises = [];
keyframes.forEach((k) => {
promises.push(() => {
return new Promise((subResolve) => {
let prop = layer.theatreObject.props;
for (let i = 0; i < k.path.length; i++) {
prop = prop[k.path[i]];
}
setSequenced(k.path, true, layer).then(() => {
tp.studio.transaction(({
__experimental_addKeyframes
}) => {
__experimental_addKeyframes(prop, k.keyframes);
});
subResolve();
});
});
});
});
sequencialPromises(promises, resolve);
});
};
// NOTE: instead of setting proptitle static and then sequenced again,
// we could simply delete all keyframes. but hey, pfff...
const setKeyframes = (layer, keyframes) => {
return new Promise((resolve) => {
if (!Array.isArray(keyframes)) {
resolve(false);
return false;
}
const existingKeyframes = getKeyframes(layer);
const promises = [];
const ms = config.tp.addKeyframesTimeout_s * 1000;
let waitify = false;
keyframes.forEach((k) => {
let prop = layer.theatreObject.props;
for (let i = 0; i < k.path.length; i++) {
prop = prop[k.path[i]];
}
const position = tp.sheet.sequence.position;
// NOTE: can we sequence values without pretend clicking?
const sequenceButton = getSequenceButton(k.path);
if (sequenceButton !== null) {
promises.push(new Promise((subResolve) => {
setTimeout(() => {
sequenceButton.click();
subResolve();
}, ms * promises.length);
}));
} else {
//console.error(k.path, 'did not find sequence button');
// is (probably) already sequenced
}
let propHasKeyframesAt = -1;
if (existingKeyframes !== null &&
existingKeyframes !== false &&
typeof existingKeyframes !== 'undefined' &&
Array.isArray(existingKeyframes)) {
existingKeyframes.forEach((existingK, existingKI) => {
if (arraysEqual(k.path, existingK.path)) {
propHasKeyframesAt = existingKI;
}
});
}
k.keyframes.forEach((keyframe) => {
let alreadyThere = false;
if (propHasKeyframesAt >= 0) {
existingKeyframes[propHasKeyframesAt].keyframes.forEach((kf) => {
if (keyframe.position === kf.position &&
keyframe.value === kf.value) {
alreadyThere = true;
}
});
}
if (!alreadyThere) {
promises.push(new Promise((subResolve) => {
setTimeout(() => {
tp.sheet.sequence.position = keyframe.position;
this.studio.transaction(({
set
}) => {
set(prop, keyframe.value);
subResolve();
const propTitle = k.path.join('.');
if (isSequenced(propTitle, layer)) {
waitify = true;
promises.push(() => {
return new Promise((subResolve) => {
setSequenced(propTitle, false, layer)
.then(() => {
subResolve();
});
}, ms * promises.length);
}));
}
});
promises.push(new Promise((subResolve) => {
setTimeout(() => {
tp.sheet.sequence.position = position;
subResolve();
}, ms * promises.length);
}));
});
});
}
});
Promise.all(promises).then(() => {
resolve();
sequencialPromises(promises, () => {
const timeout_ms = waitify ? 1000 : 0;
setTimeout(() => {
addKeyframes(layer, keyframes)
.then(resolve);
}, timeout_ms);
});
});
};
const friendlySequenceNames = () => {
const sequencePanelLeft = tp.getSequencePanelLeft();
let doItAgain = true;
@ -246,12 +284,19 @@ const TheatrePlay = function(autoInit = false) {
friendlySequenceNames();
}, 1000);
}
// dirty, should be not in here:
if (getAudioLayers().length > 0) {
getAudioLayer().findInjectSequencePanel();
}
};
//public
this.setSequenced = setSequenced;
this.friendlySequenceNames = friendlySequenceNames;
this.getKeyframes = getKeyframes;
this.addKeyframes = addKeyframes;
this.setKeyframes = setKeyframes;
this.theatreObjects = theatreObjects;
this.addObject = (name, props, onValuesChange) => {
const obj = this.sheet.object(name, props);
@ -283,6 +328,9 @@ const TheatrePlay = function(autoInit = false) {
// remove object from objects list
delete theatreObjects[name];
};
this.isSequenced = isSequenced;
this.getSequenceButton = getSequenceButton;
this.getSequencePanel = getSequencePanel;
this.getSequencePanelLeft = getSequencePanelLeft;
this.getPanel = getPanel;
this.getPanelPropTitle = getPanelPropTitle;
@ -308,269 +356,12 @@ const TheatrePlay = function(autoInit = false) {
};
this.addShadowCss = () => {
if (this.shadowRoot.querySelector(`#tpShadowCss`) === null) {
let style = document.createElement("style");
this.shadowRoot.insertBefore(Picker.StyleElement.cloneNode(true), this.shadowRoot.querySelector('#pointer-root'));
let style = document.createElement("link");
style.setAttribute('id', 'tpShadowCss');
// ASYA: here you can adjust css
style.textContent = `
.alignButtons, .textAlignButtons {
flex-direction: row;
padding: 10px 0px !important;
justify-content: center;
}
.word{
margin-right: 10px;
}
.letter{
transition: 0.5s font-variation-settings;
}
@font-face {
font-family: 'vtVF';
src: url("/web/fonts/vtVF.ttf") format("TrueType");
}
.vtTitle{
font-family: 'vtVF';
font-size: 3em;
font-variation-settings: "wght" 0, "wdth" 0, "opsz" 0;
height: 50px;
width: 100%;
position: inherit;
display: flex;
justify-content: center;
align-items: center;
}
#panel,
.panel {
display: flex;
flex-direction: column;
}
.panel > div {
order: 1;
}
.panel > .panelWrapperMom {
order: 2;
}
.panel > .bottomButtonsContainer {
order: 3;
}
.panelWrapper{
}
.panelControlsWrapper{
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
grid-row-start: 2;
grid-column-start: 1;
grid-column-end: 7;
display: flex;
flex-wrap: wrap;
}
.xWrapper, .yWrapper, .fontFamilyWrapper, .fontFamilyWrapper, .rotationWrapper, .letterDelayWrapper, .transformOriginWrapper, .widthWrapper, .fontSizeWrapper, .letterSpacingWrapper, .lineHeighWrapper, .textWrapper, .colorWrapper, .mirror_xWrapper, .mirror_yWrapper, .mirror_xyWrapper, .mirror_x_distanceWrapper, .mirror_y_distanceWrapper, .alignButtons, .textAlignButtons{
border: none;
padding: 0px;
display: flex;
width: 100%;
}
.textWrappingButton {
margin-left: 30px;
}
.textWrappingButton.active {
background: white;
}
.alignButtonsVertical{
padding-bottom: 10px;
}
.alignButtons{
display: flex;
width: 50%;
padding-top: 10px;
padding-bottom: 10px;
}
.xWrapper{
}
.yWrapper{
}
.mirror_xWrapper{
}
.mirror_xWrapper input, .mirror_yWrapper input, .mirror_xyWrapper input{
margin: 0px 10px;
}
label {
color: #ea2333;
margin-left:10px;
}
input[type=checkbox] {
position: absolute;
width: 27px;
opacity: 0;
}
input[type=checkbox]:checked + label {
color: #1cba94;
}
input[type=checkbox] + label::after{
content: ' OFF';
}
input[type=checkbox]:checked + label::after{
content: ' ON';
}
.mirror_yWrapper{
}
.mirror_xyWrapper{
}
.mirror_x_distanceWrapper{
}
.mirror_y_distanceWrapper{
}
.fontFamilyWrapper{
}
.rotationWrapper{
border-top: 1px dashed #91919177;
padding-top: 10px;
}
.transformOriginWrapper{
}
.widthWrapper{
}
.heightWrapper{
display: none;
}
.fontSizeWrapper{
}
.letterSpacingWrapper{
}
.letterDelayWrapper{
}
.lineHeighWrapper{
}
.textWrapper{
}
.colorWrapper{
border-bottom: 1px dashed #91919177;
border-top: 1px dashed #91919177;
margin-bottom: 10px;
padding-bottom: 10px;
padding-top: 10px;
}
span.icon{
font-family: 'VariableIcons';
font-size: 3em;
margin: 2px 5px;
line-height: 0.2em;
box-sizing: border-box;
}
.main_panel_button, .vte_button{
background-color: white;
font-size: 1.15em;
padding: 5px 5px 4px 5px;
margin: 10px;
display: flex;
flex-grow: 1;
justify-content: center;
border-radius: 10px;
border: none;
padding: 10px;
text-transform: uppercase;
width: calc(100% - 20px);
box-sizing: border-box;
cursor: pointer;
font-variation-settings: 'wght' 750, 'wdth' 100;
}
.main_panel_button:hover, .vte_button:hover{
color: white;
background-color:black;
}
.removeButtonContainer{
display: flex;
padding-right: 10px;
padding-bottom: 10px;
}
.alignButtons img,
.textAlignButtons img{
height: 19px;
cursor: pointer;
margin: 5px 5px 2px 5px;
}
li.layerMover div {
}
li.layerMover div.selected {
background: rgba(255, 255, 255, 1);
}
li.layerMover div.selected svg circle{
fill: #ea2333;
}
.letterDelaysContWrapper{
display: flex;
flex-direction: column;
width: 100%;
padding-bottom: 10px;
}
.letterDelaysContWrapper > *, .fontVariationAxesContWrapper > *{
margin-left: calc(var(--left-pad) * (var(--depth) - 1));
}
.fontVariationAxesContWrapper{
display: flex;
flex-direction: column;
width: 100%;
padding-bottom: 10px;
margin-bottom: 10px;
border-bottom: 1px dashed #91919177;
border-top: 1px dashed #91919177;
margin-top: 10px;
}
.moveLayerButton {
}
.removeLayerButton img,
.duplicateLayerButton img,
.addLayerButton img,
.moveLayerButton img {
height: 10px;
cursor: pointer;
}
.propTitleStuff {
color: red;
}
.addLayerButton{
width: calc(100% - 40px);
display: flex;
align-items: center;
justify-content: center;
/* padding-left: 15px; */
align-self: end;
margin-top: 5px;
}
`;
this.shadowRoot.appendChild(style);
style.setAttribute('rel', 'stylesheet');
style.setAttribute('href', '/web/css/theatre.css');
this.shadowRoot.insertBefore(style, this.shadowRoot.querySelector('#pointer-root'));
}
};
const waitingForShadowRoot = (callback) => {
@ -779,13 +570,9 @@ const TheatrePlay = function(autoInit = false) {
console.log('TheatrePlay::uploadProject', 'could not verify project');
}
});
//Module.importProjectAsZip(vt_project.arrayBuffer, vt_project.fileSize, "debug_tmp");
} else if (verifyVariableTimeProject(vt_project)) {
this.possiblyAskForFontsPromise(vt_project).then((vt_project_u) => {
vt_project = vt_project_u;
//if (reeeload) {
//localStorage.clear();
//}
this.saveProject(vt_project.projectId, vt_project, true);
if (reeeload) {
this.reloadToProject(vt_project.projectId);
@ -808,8 +595,32 @@ const TheatrePlay = function(autoInit = false) {
layerOrder: window.layerOrder.get(),
fontsHashMap,
projectId,
audioSavedMapping: audio.getSavedMapping(),
audioMapping: audio.getMapping(),
};
const theatre = tp.studio.createContentOfSaveFile(currentProjectId);
// yeaaaah we don't want to fuck with theatre's saveFile stuff
// let's do this ourselves
const audioLayers = getAudioLayers();
if (audioLayers.length > 0) {
vt_params['audioLayers'] = {};
audioLayers.forEach((audioLayer) => {
vt_params['audioLayers'][audioLayer.id()] = getKeyframes(audioLayer);
});
const audioPlayerElements = audio
.audioPlayer
.audioElements
.map((e) => {
return {
audioID: e.audioID,
layerID: e.layerID,
propTitle: e.propTitle,
file: e.file,
startTime: e.startTime
};
});
vt_params['audioPlayerElements'] = audioPlayerElements;
}
return this.theatre2variableTime(theatre, vt_params);
};
this.saveProject = (projectId = project.address.projectId, vt_project = false, silent = true) => {
@ -834,12 +645,10 @@ const TheatrePlay = function(autoInit = false) {
if (saveBeforeDownloading) {
this.saveProject(projectId, vt_project, true);
}
//downloadFile(JSON.stringify(vt_project), `${config.projects.savePrefix}${projectId}.json`, 'application/json');
Module.downloadProject(projectId, JSON.stringify(vt_project));
} else {
const p = this.getProject(projectId);
if (p !== false) {
//downloadFile(p, `${config.projects.savePrefix}${projectId}.json`, 'application/json');
Module.downloadProject(projectId, JSON.stringify(p));
} else {
console.error('TheatrePlay::downloadProject', `cannot download project with id ${projectId}, because it is neither current project or saved in localStorage`);
@ -874,6 +683,7 @@ const TheatrePlay = function(autoInit = false) {
project = projectJson === false ? core.getProject(projectId) : core.getProject(projectId, {
state: projectJson
});
//console.log({project, projectJson});
window.setLoadingTask('setting up animation', 10);
project.ready.then(() => {
this.sheet = project.sheet('mainSheet');
@ -916,26 +726,78 @@ const TheatrePlay = function(autoInit = false) {
artboardValues['zoom'] = artboardValues['scale'];
delete artboardValues['scale'];
}
if (artboardValues.hasOwnProperty('backgroundColor')) {
artboardValues['color'] = artboardValues['backgroundColor'];
delete artboardValues['backgroundColor'];
}
// for backward compatibility merge with current values
// for backward compatibility merge with current values
const defaultArtboardValues = window.getArtboard().theatreObject.value;
const artboardProps = {...defaultArtboardValues, ...artboardValues};
window.setLoadingTask('setting up artboard', 90);
studio.transaction(({
set
}) => {
set(window.getArtboard().theatreObject.props, artboardProps);
});
if (project.hasOwnProperty('audioMapping')) {
audio.setMapping(project.audioMapping);
}
if (project.hasOwnProperty('audioSavedMapping')) {
audio.setSavedMapping(project.audioSavedMapping);
}
// load layers
const layerPromises = [];
Object.keys(objects)
.filter((e) => e.indexOf('layer-') === 0)
.forEach((layerId) => {
window.setLoadingTask(`setting up the shapes of ${layerId} to come`, 90);
window.project_fontsHashMap = project.fontsHashMap;
layerPromises.push(window.addExistingLayer(layerId, objects[layerId]));
//layerPromises.push(window.addExistingLayer(layerId, objects[layerId]));
layerPromises.push(() => {
return new Promise((r) => {
window.addExistingLayer(layerId, objects[layerId]).then(() => {
r();
});
});
});
});
if (typeof project['audioPlayerElements'] === 'object') {
audio.audioPlayer.addMany(project['audioPlayerElements']);
audio.audioPlayer.init();
}
if (typeof project['audioLayers'] === 'object') {
const audioLayerIDs = Object.keys(project['audioLayers']);
audioLayerIDs.forEach((audioLayerID) => {
layerPromises.push(() => {
window.setLoadingTask(`setting up the sounds of ${audioLayerID} to come`, 90);
return new Promise((r) => {
window.addExistingAudioLayer(audioLayerID).then((audioLayer) => {
const promises = [];
project['audioLayers'][audioLayerID].forEach((keyframeCombo) => {
const filename = keyframeCombo.path.join('');
promises.push(() => {
return new Promise((rr) => {
audioLayer.addFile(filename)
.then(() => {
rr(); // resolve
});
Promise.all(layerPromises).then(() => {
});
});
});
sequencialPromises(promises, () => {
tp.addKeyframes(audioLayer, project['audioLayers'][audioLayerID]);
r(); // resolve
});
});
});
});
});
}
sequencialPromises(layerPromises, () => {
window.layerOrder.set(project.layerOrder);
this.duration = this.core.val(this.sheet.sequence.pointer.length);
if (project.layerOrder.length > 0) {
@ -961,6 +823,9 @@ const TheatrePlay = function(autoInit = false) {
});
});
} else {
const layers = getLayers();
if (layers.length > 0) {
}
this.duration = this.core.val(this.sheet.sequence.pointer.length);
resolve();
this.isProjectLoaded = true;
@ -1007,9 +872,6 @@ const TheatrePlay = function(autoInit = false) {
}
this.connectModuleCallbacks = () => {
console.log('TheatrePlay::connectModuleCallbacks');
//onChange(sequence.pointer.length, (len) => {
//console.log('Length of the sequence changed to:', len)
//})
if (config.timeline.rolloverReset) {
core.onChange(this.sheet.sequence.pointer.position, (position) => {
@ -1023,6 +885,9 @@ const TheatrePlay = function(autoInit = false) {
Module.setPlaying(playing);
});
};
this.isPlaying = () => {
return this.core.val(this.sheet.sequence.pointer.playing);
};
this.studio = studio;
this.core = core;

View file

@ -34,6 +34,19 @@ const getUuid = () => {
return uuid.getUuid();
}
const getTimestamp = () => {
const now = new Date();
const dd = String(now.getDate()).padStart(2, '0');
const mm = String(now.getMonth() + 1).padStart(2, '0'); //January is 0!
const yyyy = now.getFullYear();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
const timestamp = `${yyyy}.${mm}.${dd}.${hours}:${minutes}:${seconds}`;
return timestamp;
}
const makeEven = (n) => {
const nr = Math.round(n);
return nr - nr % 2;
@ -115,14 +128,12 @@ function uploadFile(expectedType = 'application/json') {
if (files.length == 0) return;
const file = files[0];
console.log('file', file);
let reader = new FileReader();
if (expectedType === 'application/zip' || file.type === 'application/zip') {
if (expectedType === 'application/zip' || file.type === 'application/zip' || file.type.indexOf('audio') === 0) {
reader.onload = (e) => {
const f = e.target.result;
console.log(e, file.name, file.size, file.type, f);
resolve({
name: file.name,
size: file.size,
@ -136,7 +147,6 @@ function uploadFile(expectedType = 'application/json') {
reader.readAsArrayBuffer(file);
} else if (expectedType === 'application/json') {
reader.onload = (e) => {
console.log(e);
const f = e.target.result;
// This is a regular expression to identify carriage
@ -156,12 +166,9 @@ function uploadFile(expectedType = 'application/json') {
reader.readAsText(file);
} else if (expectedType.indexOf('font') >= 0) {
console.log('expect font');
reader.onload = (e) => {
console.log(e);
const f = e.target.result;
if (file.type.indexOf('font') >= 0) {
console.log('is font');
//var uint8View = new Uint8Array(f);
//console.log('trying to save the font file, file, uint8View', file, uint8View);
//FS.createDataFile(config.fs.idbfsFontDir, file.name, uint8View, true, true);
@ -182,7 +189,6 @@ function uploadFile(expectedType = 'application/json') {
type: file.type,
arrayBuffer: f
};
console.log({outputFile});
resolve(outputFile);
} else {
reject('not a font');
@ -347,7 +353,7 @@ function verifyVariableTimeProject(vt_project) {
function clone(a) {
return JSON.parse(JSON.stringify(a));
};
}
function getParents(elem, until = null) {
const parents = [];
@ -366,6 +372,12 @@ function getParents(elem, until = null) {
return parents;
}
function removeAllEventListeners(elem) {
const tmp = elem.cloneNode(true);
elem.parentNode.replaceChild(tmp, elem);
elem = tmp;
}
function arraysEqual(a, b, sortingMatters = false) {
if (a === b) return true;
if (a == null || b == null) return false;
@ -381,11 +393,37 @@ function arraysEqual(a, b, sortingMatters = false) {
return true;
}
// NOTE: will break if input is not number or rgba
const mapValue = (value, low1, high1, low2, high2, clamp=false) => {
const mapped = low2 + (high2 - low2) * (value - low1) / (high1 - low1);
return clamp ? Math.min(high2 > low2 ? high2 : low2, Math.max(low2 < high2 ? low2 : high2, mapped)) : mapped;
if (typeof low2 === 'number') {
const mapped = low2 + (high2 - low2) * (value - low1) / (high1 - low1);
return clamp ? Math.min(high2 > low2 ? high2 : low2, Math.max(low2 < high2 ? low2 : high2, mapped)) : mapped;
} else if (typeof low2 === 'object' && typeof low2.r === 'number') {
const mapped = {
r: mapValue(value, low1, high1, low2.r, high2.r, clamp),
g: mapValue(value, low1, high1, low2.g, high2.g, clamp),
b: mapValue(value, low1, high1, low2.b, high2.b, clamp),
a: mapValue(value, low1, high1, low2.a, high2.a, clamp),
};
return mapped;
}
return false;
}
// NOTE: will break if input is not number or rgba
const smoothValue = (oldValue, newValue, smoothing) => {
if (typeof oldValue === 'number') {
return oldValue * smoothing + (1.0 - smoothing) * newValue;
} else {
return {
r: smoothValue(oldValue.r, newValue.r, smoothing),
g: smoothValue(oldValue.g, newValue.g, smoothing),
b: smoothValue(oldValue.b, newValue.b, smoothing),
a: smoothValue(oldValue.a, newValue.a, smoothing),
};
}
};
const isMobile = () => {
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
return true;
@ -395,10 +433,184 @@ const isMobile = () => {
return false;
};
// NOTE:
// promises must be delivered inside a function like:
//
// const promises = [];
//
// promises.push(() => { return new Promise((resolve) => { console.log('lalala ONE'); resolve() }); });
// promises.push(() => { return new Promise((resolve) => { console.log('lalala TWO'); resolve() }); });
// promises.push(() => { return new Promise((resolve) => { console.log('lalala THREE'); resolve() }); });
//
// sequencialPromises(promises, () => { console.log('i am done'); });
const sequencialPromises = async (iterable, callback = false) => {
for (const x of iterable) {
await x();
}
if (typeof callback === 'function') {
callback();
}
};
// NOTE: this is perfect
const toCssClass = (text, prefix = '') => {
const cssClass = prefix + 'vt_' + text
.replaceAll('.', '-d-')
.replaceAll(' ', '_')
.replaceAll(':', '-c-')
.replaceAll('#', '-h-')
.replace(/[^a-zA-Z0-9_-]/g, "")
;
return cssClass;
};
const sanitizeTheatreKey = (key, removeExtension = true) => {
let theatreKey = key;
if (removeExtension) {
theatreKey = theatreKey.split('.');
if (theatreKey.length > 1) {
theatreKey.pop();
}
theatreKey = theatreKey.join('');
}
if (theatreKey.substr(0, 1).match(/^([0-9])$/i)) {
theatreKey = `t_${theatreKey}`;
}
return theatreKey.replace(/[^a-zA-Z0-9_]/g, "");
};
const renameProperty = (o, old_key, new_key) => {
Object.defineProperty(o, new_key,
Object.getOwnPropertyDescriptor(o, old_key));
delete o[old_key];
};
const flattenObject = (o, ignoreKeys = [], pathSymbol = '.') => {
if (typeof o !== 'object') {
return o;
}
let doItAgain = false;
Object.keys(o).forEach((k) => {
if (typeof o[k] === 'object' &&
ignoreKeys.indexOf(k) < 0) {
doItAgain = true;
Object.keys(o[k]).forEach((sk) => {
const nk = `${k}${pathSymbol}${sk}`;
o[nk] = o[k][sk];
});
delete o[k];
}
});
if (doItAgain) {
flattenObject(o, ignoreKeys, pathSymbol);
}
};
const deFlattenObject = (o, ignoreKeys = [], pathSymbol = '.') => {
Object.keys(o).forEach((k) => {
if (ignoreKeys.indexOf(k) < 0) {
const ks = k.split(pathSymbol);
if (ks.length > 1) {
let sos = o[k];
for (let i = ks.length - 1; i > 0; i--) {
sos = {[ks[i]]: sos};
}
if (typeof o[ks[0]] === 'object') {
o[ks[0]] = {...o[ks[0]], ...sos};
} else {
o[ks[0]] = sos;
}
delete o[k];
}
}
});
};
const getNestedProperty = (o, a, verify = false) => {
if (verify && (!Array.isArray(a) || typeof o !== "object" || !o.hasOwnProperty(a[0]))) {
return false;
}
let b = clone(a);
if (b.length > 1) {
let c = o[b.shift()];
return getNestedProperty(c, b, verify);
}
return o[b[0]];
};
const findNestedObjectKey = (o, k, exact = true, history = [], result = []) => {
const oks = Object.keys(o);
for (let i = 0; i < oks.length; i++) {
if (k === oks[i] || (!exact && oks[i].indexOf(k) >= 0)) {
history.push(oks[i]);
result.push({found: o[oks[i]], history});
}
if (typeof o[oks[i]] === 'object') {
const ok = Object.keys(o[oks[i]]);
const ok_history = clone(history);
ok_history.push(oks[i]);
findNestedObjectKey(o[oks[i]], k, exact, ok_history, result);
}
}
if (history.length === 0) {
return result;
}
};
// NOTE: all input values are range 0-1
const rgbaToHexa = (rgba) => {
return '#' + (
('0' + Math.round(rgba.r * 255).toString(16)).slice(-2) +
('0' + Math.round(rgba.g * 255).toString(16)).slice(-2) +
('0' + Math.round(rgba.b * 255).toString(16)).slice(-2) +
('0' + Math.round(rgba.a * 255).toString(16)).slice(-2)
).toUpperCase();
};
// NOTE: all output values are range 0-1
const hexaToRgba = (hex_a) => {
const o = hex_a[0] === '#' ? 1 : 0;
// if it is hex, we need to add alpha
const hexa = hex_a.padEnd(8 + o, 'F');
return {
r: parseInt(hexa.slice(o + 0, o + 2), 16) / 255,
g: parseInt(hexa.slice(o + 2, o + 4), 16) / 255,
b: parseInt(hexa.slice(o + 4, o + 6), 16) / 255,
a: parseInt(hexa.slice(o + 6, o + 8), 16) / 255
}
};
const getFileExtensionFromMimeType = (mimeType) => {
if (mimeType.toLowerCase().indexOf('audio/webm') >= 0) {
return "webm";
}
if (mimeType.toLowerCase().indexOf('audio/mpeg') >= 0) {
return "mp3";
}
if (mimeType.toLowerCase().indexOf('audio/ogg') >= 0) {
return "ogg";
}
if (mimeType.toLowerCase().indexOf('audio/wav') >= 0) {
return "wav";
}
return false;
};
/////////////////////////////////////
// you can test these functions in
// the browser like so:
/*
import("./web/js/utils.js").then((module) => {
const toast = module.toCssClass('lol.lol');
console.log(toast);
});
*/
//
/////////////////////////////////////
export {
getUuid,
getTimestamp,
htmlToElement,
downloadFile,
uploadFile,
@ -412,7 +624,20 @@ export {
mixObject,
clone,
getParents,
removeAllEventListeners,
arraysEqual,
mapValue,
smoothValue,
isMobile,
sequencialPromises,
toCssClass,
sanitizeTheatreKey,
renameProperty,
flattenObject,
deFlattenObject,
getNestedProperty,
findNestedObjectKey,
rgbaToHexa,
hexaToRgba,
getFileExtensionFromMimeType,
}

File diff suppressed because it is too large Load diff

View file

@ -50429,14 +50429,16 @@ ${content}</tr>
max = Math.max(n3, max);
}
keyframes.forEach((cur, i3) => {
const curVal = valueInProp(cur.value, propConfig);
const cv = typeof cur.value === "boolean" ? cur.value ? 1 : 0 : cur.value;
const curVal = valueInProp(cv, propConfig);
check(curVal);
if (!cur.connectedRight)
return;
const next = keyframes[i3 + 1];
if (!next)
return;
const diff = (typeof next.value === "number" ? next.value : 1) - curVal;
const nv = typeof next.value === "boolean" ? next.value ? 1 : 0 : next.value;
const diff = (typeof nv === "number" ? nv : 1) - curVal;
check(curVal + cur.handles[3] * diff);
check(curVal + next.handles[1] * diff);
});
@ -50492,7 +50494,7 @@ ${content}</tr>
};
}, []);
const extremumSpace = (0, import_react185.useMemo)(() => {
const extremums = propConfig.type === "number" ? calculateScalarExtremums(trackData.keyframes, propConfig) : calculateNonScalarExtremums(trackData.keyframes);
const extremums = propConfig.type === "number" || propConfig.type === "boolean" ? calculateScalarExtremums(trackData.keyframes, propConfig) : calculateNonScalarExtremums(trackData.keyframes);
const fromValueSpace = (val3) => (val3 - extremums[0]) / (extremums[1] - extremums[0]);
const toValueSpace = (ex) => extremums[0] + deltaToValueSpace(ex);
const deltaToValueSpace = (ex) => ex * (extremums[1] - extremums[0]);
@ -50517,7 +50519,7 @@ ${content}</tr>
layoutP,
sheetObject,
trackId,
isScalar: propConfig.type === "number",
isScalar: propConfig.type === "number" || propConfig.type === "boolean",
key: kf.id,
extremumSpace: cachedExtremumSpace.current,
color: color2
@ -61808,8 +61810,51 @@ Note that it **is okay** to import '@theatre/core' multiple times. But those imp
}
}
stateEditors2.coreByProject.historic.sheetsById.staticOverrides.byObject.setValueOfCompoundProp(p3);
const toVT = {
sequenced: false,
panelID: p3.objectKey,
prop: p3.pathToProp,
origin: "stateEditors.ts"
};
const event = new CustomEvent("sequenceEvent", {
bubbles: false,
detail: toVT
});
window.dispatchEvent(event);
}
sequence2.setCompoundPropAsStatic = setCompoundPropAsStatic;
function setCompoundPropAsSequenced(p3) {
const tracks = _ensureTracksOfObject(p3);
for (const encodedPropPath of Object.keys(tracks.trackIdByPropPath)) {
const propPath = JSON.parse(encodedPropPath);
const isSubOfTargetPath = p3.pathToProp.every((key, i3) => propPath[i3] === key);
if (isSubOfTargetPath) {
const possibleTrackId = tracks.trackIdByPropPath[encodedPropPath];
if (typeof possibleTrackId === "string")
return;
const trackId = generateSequenceTrackId();
const track = {
type: "BasicKeyframedTrack",
__debugName: `${p3.objectKey}:${encodedPropPath}`,
keyframes: []
};
tracks.trackData[trackId] = track;
tracks.trackIdByPropPath[encodedPropPath] = trackId;
}
}
const toVT = {
sequenced: false,
panelID: p3.objectKey,
prop: p3.pathToProp,
origin: "stateEditors.ts"
};
const event = new CustomEvent("sequenceEvent", {
bubbles: false,
detail: toVT
});
window.dispatchEvent(event);
}
sequence2.setCompoundPropAsSequenced = setCompoundPropAsSequenced;
function _getTrack(p3) {
return _ensureTracksOfObject(p3).trackData[p3.trackId];
}
@ -61933,6 +61978,51 @@ Note that it **is okay** to import '@theatre/core' multiple times. But those imp
}
}
sequence2.setHandlesForKeyframe = setHandlesForKeyframe;
function addKeyframes(p3, override = true) {
const track = _getTrack(p3);
if (!track)
return;
if (p3.keyframes.length < 1)
throw new Error("holy shit, we are trying to add non-existing keyframes");
if (!override)
throw new Error("whoopsie, not overriding is not implemented");
const oldKeyframes = track.keyframes;
track.keyframes = [];
oldKeyframes.forEach((kf) => {
if (p3.keyframes[0].position > kf.position) {
track.keyframes.push(kf);
}
});
p3.keyframes.forEach((pkf) => {
track.keyframes.push({
id: generateKeyframeId(),
position: pkf.position,
connectedRight: true,
handles: pkf.handles || [0.5, 1, 0.5, 0],
type: pkf.type || "bezier",
value: pkf.value
});
});
oldKeyframes.forEach((kf) => {
if (p3.keyframes[p3.keyframes.length - 1].position < kf.position) {
track.keyframes.push(kf);
}
});
}
sequence2.addKeyframes = addKeyframes;
function keepKeyframes(p3) {
const track = _getTrack(p3);
if (!track)
return;
const keyframes = track.keyframes;
track.keyframes = [];
keyframes.forEach((kf) => {
if (p3.keyframeIds.indexOf(kf.id) >= 0) {
track.keyframes.push(kf);
}
});
}
sequence2.keepKeyframes = keepKeyframes;
function deleteKeyframes(p3) {
const track = _getTrack(p3);
if (!track)
@ -62525,7 +62615,12 @@ Note that it **is okay** to import '@theatre/core' multiple times. But those imp
};
// ../../theatre/studio/src/TheatreStudio.ts
init_get();
init_src();
init_utils2();
init_pointerDeep();
init_forEachDeep();
init_getDeep();
init_instanceTypes();
init_selectors();
init_getStudio();
@ -62560,6 +62655,60 @@ Note that it **is okay** to import '@theatre/core' multiple times. But those imp
},
__experimental_createContentOfSaveFileTyped(projectId) {
return getStudio().createContentOfSaveFile(projectId);
},
__experimental_setPropAsSequenced(prop) {
const { path, root: root3 } = getPointerParts(prop);
if (!isSheetObject(root3)) {
throw new Error("Argument prop must be a pointer to a SheetObject property");
}
const propAddress = __spreadProps(__spreadValues({}, root3.address), { pathToProp: path });
const propConfig = getPropConfigByPath(root3.template.staticConfig, path);
if (typeof propConfig !== "undefined")
getStudio().transaction(({ stateEditors: stateEditors2 }) => {
stateEditors2.coreByProject.historic.sheetsById.sequence.setPrimitivePropAsSequenced(propAddress, propConfig);
});
},
__experimental_setPropAsStatic(prop) {
const { path, root: root3 } = getPointerParts(prop);
if (!isSheetObject(root3)) {
throw new Error("Argument prop must be a pointer to a SheetObject property");
}
const propAddress = __spreadProps(__spreadValues({}, root3.address), { pathToProp: path });
const propConfig = getPropConfigByPath(root3.template.staticConfig, path);
if (typeof propConfig !== "undefined") {
for (const { path: subPath, conf } of iteratePropType(propConfig, [])) {
if (isPropConfigComposite(conf))
continue;
getStudio().transaction(({ stateEditors: stateEditors2 }) => {
const pointerToSub = pointerDeep(prop, subPath);
stateEditors2.coreByProject.historic.sheetsById.sequence.setPrimitivePropAsStatic(__spreadProps(__spreadValues({}, propAddress), {
value: root3.getValueByPointer(pointerToSub)
}));
});
}
}
},
__experimental_isPropSequenced(prop) {
const { path, root: root3 } = getPointerParts(prop);
if (!isSheetObject(root3)) {
throw new Error("Argument prop must be a pointer to a SheetObject property");
}
const propAddress = __spreadProps(__spreadValues({}, root3.address), { pathToProp: path });
const propConfig = getPropConfigByPath(root3.template.staticConfig, path);
const validTracks = root3.template.getArrayOfValidSequenceTracks().getValue();
tf:
for (let t4 = 0; t4 < validTracks.length; t4++) {
const otherPath = validTracks[t4].pathToProp;
if (otherPath.length === path.length) {
for (let p3 = 0; p3 < path.length; p3++) {
if (path[p3] !== otherPath[p3]) {
continue tf;
}
}
return true;
}
}
return false;
}
});
}
@ -62584,11 +62733,130 @@ Note that it **is okay** to import '@theatre/core' multiple times. But those imp
}
stateEditors2.coreByProject.historic.sheetsById.forgetSheet(sheet.address);
};
const __experimental_sequenceProp = (prop) => {
const { path, root: root3 } = getPointerParts(prop);
if (!isSheetObject(root3)) {
throw new Error("Argument prop must be a pointer to a SheetObject property");
}
const propAddress = __spreadProps(__spreadValues({}, root3.address), { pathToProp: path });
const propConfig = getPropConfigByPath(root3.template.staticConfig, path);
if (propConfig === void 0) {
throw new Error("propConfig is undefined. so, yeah.");
}
stateEditors2.coreByProject.historic.sheetsById.sequence.setPrimitivePropAsSequenced(propAddress, propConfig);
};
const __experimental_staticProp = (prop) => {
const { path, root: root3 } = getPointerParts(prop);
if (!isSheetObject(root3)) {
throw new Error("Argument prop must be a pointer to a SheetObject property");
}
const propAddress = __spreadProps(__spreadValues({}, root3.address), { pathToProp: path });
const propConfig = getPropConfigByPath(root3.template.staticConfig, path);
if (propConfig === void 0) {
throw new Error("propConfig is undefined. so, yeah.");
}
for (const { path: subPath, conf } of iteratePropType(propConfig, [])) {
if (isPropConfigComposite(conf))
continue;
const pointerToSub = pointerDeep(prop, subPath);
stateEditors2.coreByProject.historic.sheetsById.sequence.setPrimitivePropAsStatic(__spreadProps(__spreadValues({}, propAddress), {
value: root3.getValueByPointer(pointerToSub)
}));
}
};
const __experimental_deleteKeyframes = (prop, from = 0, to = 0) => {
const { root: root3, path } = getPointerParts(prop);
if (isSheetObject(root3)) {
const sequenceTracksTree = root3.template.getMapOfValidSequenceTracks_forStudio().getValue();
const defaultValue = getDeep(root3.template.getDefaultValues().getValue(), path);
const propConfig = getPropConfigByPath(root3.template.staticConfig, path);
const unsetStaticOrKeyframeProp = (value, path2) => {
const propAddress = __spreadProps(__spreadValues({}, root3.address), { pathToProp: path2 });
const trackId = get_default(sequenceTracksTree, path2);
const sequence = root3.sheet.getSequence();
const trackP = val(sequence._project.pointers.historic.sheetsById[sequence._sheet.address.sheetId].sequence.tracksByObject[root3.address.objectKey]);
if (!trackP) {
throw new Error("whatever, man");
}
const { trackData, trackIdByPropPath } = trackP;
if (typeof trackId === "string" && typeof trackData !== "undefined") {
const track = trackData[trackId];
if (!track) {
throw new Error("whatever, man");
}
const keyframeIds = [];
if (to > from) {
track.keyframes.forEach((kf) => {
if (kf.position >= from && kf.position <= to) {
} else {
keyframeIds.push(kf.id);
}
});
}
const objectKey = propAddress.objectKey;
stateEditors2.coreByProject.historic.sheetsById.sequence.keepKeyframes(__spreadProps(__spreadValues({}, root3.address), {
objectKey,
trackId,
keyframeIds
}));
} else if (propConfig !== void 0) {
stateEditors2.coreByProject.historic.sheetsById.staticOverrides.byObject.unsetValueOfPrimitiveProp(propAddress);
}
};
if (propConfig.type === "compound") {
forEachPropDeep(defaultValue, (v6, pathToProp) => {
unsetStaticOrKeyframeProp(v6, pathToProp);
}, getPointerParts(prop).path);
} else {
unsetStaticOrKeyframeProp(defaultValue, path);
}
} else {
throw new Error("Only setting props of SheetObject-s is supported in a transaction so far");
}
};
const __experimental_addKeyframes = (prop, keyframes) => {
const { root: root3, path } = getPointerParts(prop);
if (isSheetObject(root3)) {
let sequenceTracksTree = root3.template.getMapOfValidSequenceTracks_forStudio().getValue();
const defaultValue = getDeep(root3.template.getDefaultValues().getValue(), path);
const propConfig = getPropConfigByPath(root3.template.staticConfig, path);
const addStaticOrKeyframeProp = (value, path2) => {
const propAddress = __spreadProps(__spreadValues({}, root3.address), { pathToProp: path2 });
let trackId = get_default(sequenceTracksTree, path2);
if (typeof trackId !== "string" && propConfig !== void 0) {
throw Error("can only add keyframes to sequenced prop");
}
if (typeof trackId === "string") {
const objectKey = propAddress.objectKey;
stateEditors2.coreByProject.historic.sheetsById.sequence.addKeyframes(__spreadProps(__spreadValues({}, root3.address), {
objectKey,
trackId,
keyframes
}));
} else if (propConfig !== void 0) {
throw Error("hmmm");
}
};
if (propConfig.type === "compound") {
forEachPropDeep(defaultValue, (v6, pathToProp) => {
addStaticOrKeyframeProp(v6, pathToProp);
}, getPointerParts(prop).path);
} else {
addStaticOrKeyframeProp(defaultValue, path);
}
} else {
throw new Error("Only setting props of SheetObject-s is supported in a transaction so far");
}
};
return fn2({
set: set3,
unset: unset2,
__experimental_forgetObject,
__experimental_forgetSheet
__experimental_forgetSheet,
__experimental_sequenceProp,
__experimental_staticProp,
__experimental_deleteKeyframes,
__experimental_addKeyframes
});
});
}

View file

@ -1,50 +0,0 @@
const Record = function(tp) {
const hot = {};
const addRecordButton = (layer, propTitle, isActive) => {
const panel = tp.getPanel();
const panelPropTitle = tp.getPanelPropTitle(propTitle);
if (panelPropTitle !== null) {
const container = tp.getPanelPropContainer(panelPropTitle);
if (container === null) {
console.log("Record::addRecordButton",
`impossible! cannot find panelPropContainer for ${propTitle}`);
} else if (container.querySelector('.recordButton') !== null) {
console.log("Record::addRecordButton",
`already added an record button for ${propTitle}`);
} else {
const button = document.createElement('div');
button.classList.add('recordButton');
button.classList.add(`recordButton${propTitle}`);
button.innerHTML = `<img src="/web/assets/record.svg" alt="record" />`;
container.append(button);
button.addEventListener('click', () => {
if (!hot.hasOwnProperty(layer.id())) {
hot[layer.id()] = {};
}
if (!hot[layer.id()].hasOwnProperty(propTitle)) {
hot[layer.id()][propTitle] = {};
button.classList.add('active');
} else {
delete hot[layer.id()][propTitle];
if (Object.keys(hot[layer.id()]).length === 0) {
delete hot[layer.id()];
}
button.classList.remove('active');
}
});
}
} else {
console.log("Record::addRecordButton",
`cannot find panelPropTitle for ${propTitle}`);
}
};
// public
this.addRecordButton = addRecordButton;
};
export {
Record
}

View file

@ -6,7 +6,18 @@ fly ()
echo $!
}
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
PREVIOUS_DIR=$(pwd)
#. ~/.bash_aliases
cd $DIR
#echo $(fly /usr/bin/chromium --incognito --screen=1 $@)
#/usr/bin/chromium --incognito --screen=1 $@ &
/usr/bin/chromium --screen=1 --enable-logging --password-store=basic --v=1 $@ &
echo $!
fly flatpak run com.github.Eloston.UngoogledChromium --screen=1 --disk-cache-dir=/dev/null --disk-cache-size=1 --enable-logging --password-store=basic --v=1 $@ &
#flatchrome Tmp --screen=1 --disk-cache-dir=/dev/null --disk-cache-size=1 --enable-logging --password-store=basic --v=1 $@ &
browser_pid=$!
echo $browser_pid > .browserpid
cd $PREVIOUS_DIR

View file

@ -5,7 +5,7 @@ PREVIOUS_DIR=$(pwd)
cd $DIR
make clean && rm -rf obj && rm -rf ../../../addons/obj
emmake make clean && make clean && rm -rf obj && rm -rf ../../../addons/obj
rm -rf ../../../libs/openFrameworksCompiled/lib/linux64/obj
rm -rf ../../../libs/openFrameworksCompiled/lib/linux64/libopenFrameworks.a
rm -rf ../../../libs/openFrameworksCompiled/lib/emscripten/obj

View file

@ -1,3 +1,3 @@
#!/bin/bash
grep bin/web/js --exclude="*.min.*" --exclude="node_modules" --exclude="*.swp" --exclude="*.bundle.js" --exclude="script.js" --exclude="ffmpeg_modules" -nr -e $@
grep bin/em/variabletime/web/js --exclude="*.min.*" --exclude="node_modules" --exclude="*.swp" --exclude="*.bundle.js" --exclude="script.js" --exclude="ffmpeg_modules" -nr -e "$@"

View file

@ -7,7 +7,7 @@ cd $DIR
project=$(basename $DIR)
rm -rf bin/$project*
rm -rf bin/em/$project/index.*
rm -rf bin/data/ofxMsdfgen
rm -rf bin/data/ofxGPUFont
cp -r ../../../addons/ofxMsdfgen/data/ofxMsdfgen ./bin/data/

14
rebuild.sh Executable file
View file

@ -0,0 +1,14 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
PREVIOUS_DIR=$(pwd)
cd $DIR
./lightclean.sh
emmake make -j12
./reloadbrowser.sh
cd $PREVIOUS_DIR

17
reloadbrowser.sh Executable file
View file

@ -0,0 +1,17 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
PREVIOUS_DIR=$(pwd)
cd $DIR
# get active window in a variable
lol=$(xdotool getactivewindow)
# reload the browser
xdotool search --onlyvisible --sync --pid $(cat ./.browserpid) --name chromium windowactivate --sync key CTRL+SHIFT+R
# reactivate old window
xdotool windowactivate $lol
cd $PREVIOUS_DIR

View file

@ -10,6 +10,8 @@ import ssl
# openssl req -new -x509 -keyout ssl/key.pem -out ssl/server.pem -days 365 -nodes
browser_cmd = "/usr/bin/chromium --screen=1 --disk-cache-dir=/dev/null --disk-cache-size=1 --enable-logging --password-store=basic --v=1"
class CORSRequestHandler(SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header("Cross-Origin-Opener-Policy", "same-origin")
@ -34,40 +36,41 @@ def serve(root, port, run_browser):
host = '0.0.0.0'
server_address = (host, port)
# test(CORSRequestHandler, HTTPServer, port=port)
httpd = HTTPServer(server_address, CORSRequestHandler)
certfile = "ssl/localhost+4.pem"
keyfile = "ssl/localhost+4-key.pem"
if not os.path.exists(certfile):
print("using ssl/server.pm")
certfile = "ssl/server.pem"
if not os.path.exists(keyfile):
print("using ssl/key.pm")
keyfile = "ssl/key.pem"
print(f"using {certfile} and {keyfile} for ssl")
# NOTE: read README.md for more instructions if you want a CA
if os.path.exists(certfile) and os.path.exists(keyfile):
httpd.socket = ssl.wrap_socket(httpd.socket,
server_side=True,
certfile=certfile,
keyfile=keyfile,
ssl_version=ssl.PROTOCOL_TLS)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile, keyfile)
httpd.socket = context.wrap_socket(httpd.socket,
server_side=True)
protocol = 'https'
if run_browser:
# Open the served page in the user's default browser.
print("Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this).")
subprocess.call([f"../browser.sh", f"{protocol}://{open_host}:{port}/variabletime.html"])
subprocess.call(["pwd"])
subprocess.call([f"../../../browser.sh", f"{protocol}://{open_host}:{port}/index.html"])
print(f"serving on port {port}")
httpd.serve_forever()
# The main function
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--port", help="port to listen on", default=8060, type=int)
parser.add_argument(
"-r", "--root", help="path to serve as root (relative to `platform/web/`)", default="./bin", type=Path
"-r", "--root", help="path to serve as root (relative to `platform/web/`)", default="./bin/em/variabletime", type=Path
)
browser_parser = parser.add_mutually_exclusive_group(required=False)
browser_parser.add_argument(

View file

@ -137,6 +137,7 @@ shared_ptr <VariableEditor::ofApp> app;
dir.listDir();
ofDirectory userFonts("/idbfs/fonts");
ofDirectory userAudio("/idbfs/audio"); // possibly rename to media?
nlohmann::json json;
json["files"] = nlohmann::json::array();
@ -157,6 +158,15 @@ shared_ptr <VariableEditor::ofApp> app;
if(!file.moveTo(userFonts)){
std::cout << "Module::importProjectAsZip" << " - cannot move font " << file.getFileName();
}
}else if(ofToLower(file.getExtension()) == "mp3"
|| ofToLower(file.getExtension()) == "m4a"
|| ofToLower(file.getExtension()) == "wav"
|| ofToLower(file.getExtension()) == "webm"
|| ofToLower(file.getExtension()) == "aac"
|| ofToLower(file.getExtension()) == "ogg"){
if(!file.moveTo(userAudio)){
std::cout << "Module::importProjectAsZip" << " - cannot move audiofile " << file.getFileName();
}
}else{
file.remove();
}

View file

@ -31,7 +31,9 @@ namespace VariableEditor {
//--------------------------------------------------------------
void ofApp::setup(){
OFX_PROFILER_FUNCTION();
EM_ASM({window.setLoadingTask('setting up rendering', 0)});
#ifdef TARGET_EMSCRIPTEN
EM_ASM({window.setLoadingTask('setting up rendering', 0)});
#endif
{
ofFile sf("appSettings.json");
@ -58,7 +60,9 @@ void ofApp::setup(){
10000000, // farDist
glm::vec2(0, 0) // lensOffset
);
EM_ASM({window.setLoadingTask('setting up rendering', 10)});
#ifdef TARGET_EMSCRIPTEN
EM_ASM({window.setLoadingTask('setting up rendering', 10)});
#endif
ofDisableArbTex();
fboSettings.width = ofGetWidth() * AA;
@ -79,25 +83,26 @@ void ofApp::setup(){
//fbo.allocate(ofGetWidth() * AA, ofGetHeight() * AA, GL_RGB);
EM_ASM({window.setLoadingTask('setting up rendering', 30)});
#ifdef TARGET_EMSCRIPTEN
EM_ASM({window.setLoadingTask('setting up rendering', 30)});
#endif
layerComposition.setup();
EM_ASM({window.setLoadingTask('setting up rendering', 90)});
#ifdef TARGET_EMSCRIPTEN
EM_ASM({window.setLoadingTask('setting up rendering', 90)});
#endif
layerComposition.setVFlip(true);
#ifndef TARGET_OPENGLES
#ifndef TARGET_EMSCRIPTEN
{
std::string fontPath = "data/fonts/Version-2-var.ttf";
ofxVariableLab::LayerType type = ofxVariableLab::LayerType::GPUFONT;
std::string fontPath = "data/fonts/Version-2.ttf";
ofxVariableLab::Layer::Props props;
props.fontPath = fontPath;
props.text = "yo, whatever you want, and especially pancakes";
props.y = 120;
props.x = 95;
layerComposition.addLayer(
{fontPath, type},
props,
{{"Weight", 100.0}, {"Weight", 700.0}}
);
if(ofFile(fontPath).exists()){
layerComposition.addLayer(props, "layer-0");
}
}
#endif
@ -130,7 +135,9 @@ void ofApp::setup(){
<< " | Minor(" << ofToString(glMajor) << ")"
<< endl;
artboard.setup();
EM_ASM({window.setLoadingTask('setting up rendering', 100)});
#ifdef TARGET_EMSCRIPTEN
EM_ASM({window.setLoadingTask('setting up rendering', 100)});
#endif
}
//--------------------------------------------------------------

View file

@ -5,6 +5,7 @@
#include "conversion.h"
#include "Artboard.h"
#include "ofEasyCam.h"
#include "ofFileUtils.h"
#include "ofMain.h"
#include "ofQuaternion.h"
#include "ofTrueTypeFont.h"
@ -34,8 +35,8 @@ struct AppSettings {
212 / 255.0,
212 / 255.0,
1}; // check data/appSettings.json
string tmpExportDir = "data/export";
string tmpImportDir = "data/import";
std::string tmpExportDir = "data/export";
std::string tmpImportDir = "data/import";
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AppSettings,
backgroundColor,
@ -43,23 +44,29 @@ struct AppSettings {
};
class ZipProjectSaver : public ofThread {
class ZipProjectSaver :
public ofThread {
public:
void setup(string projectName,
string projectJsonString){
this->projectName = projectName;
this->projectJsonString = projectJsonString;
this->userFontsPath = "/idbfs/fonts";
this->userAudioPath = "/idbfs/audio";
this->timestamp = ofGetTimestampString();
this->filename = projectName + "_project_" + timestamp + ".zip";
}
void update(){
if(freshDownload.load()){
emscripten_browser_file::download(filename.c_str(),
"application/zip",
buffer,
buffer_size);
#ifdef TARGET_EMSCRIPTEN
emscripten_browser_file::download(filename.c_str(),
"application/zip",
buffer,
buffer_size);
#else
ofBufferToFile(filename.c_str(), ofBuffer(buffer, buffer_size));
#endif
freshDownload.store(false);
}
}
@ -93,6 +100,28 @@ class ZipProjectSaver : public ofThread {
ofBuffer buffy = file.readToBuffer();
zip.addBuffer(fontFilename, buffy.getData(), buffy.size());
}
ofDirectory userAudio(userAudioPath);
userAudio.sort();
userAudio.allowExt("mp3");
userAudio.allowExt("ogg");
userAudio.allowExt("wav");
userAudio.allowExt("webm");
userAudio.allowExt("m4a");
userAudio.listDir();
for(int i = 0; i < userAudio.size(); i++){
std::string audioFilename = userAudio.getName(i);
std::string audioFilepath = userAudioPath + "/" + audioFilename;
ofFile file = userAudio.getFile(i);
ofStringReplace(audioFilepath, "/idbfs/", "idbfs/");
if(of::filesystem::exists(audioFilepath)){
//cout << "huuurrayy " << audioFilepath << " exists" << endl;
}else{
cout << "ofApp::downloadProject() trying to load " << audioFilepath << " but it does not exist." << endl;
}
file.open(audioFilepath);
ofBuffer buffy = file.readToBuffer();
zip.addBuffer(audioFilename, buffy.getData(), buffy.size());
}
buffer = NULL;
buffer_size = 0;
zip.getOutputBuffer(&buffer, buffer_size);
@ -104,11 +133,12 @@ class ZipProjectSaver : public ofThread {
void exit(){
free(buffer);
}
string projectName;
string projectJsonString;
string userFontsPath;
string timestamp;
string filename;
std::string projectName;
std::string projectJsonString;
std::string userFontsPath;
std::string userAudioPath;
std::string timestamp;
std::string filename;
char * buffer;
size_t buffer_size;
std::atomic <bool> freshDownload{false};
@ -130,7 +160,8 @@ class ZipSaver : public ofThread {
void update(){
if(freshDownload.load()){
EM_ASM({
#ifdef TARGET_EMSCRIPTEN
EM_ASM({
document.getElementById('export_progress_task').innerHTML = 'rendering';
let innerHTML = "|";
for(let i = 0; i < 100; i++){
@ -142,10 +173,13 @@ class ZipSaver : public ofThread {
let progress_task = document.getElementById("export_progress_task");
progress_task.innerHTML = "idle";
});
emscripten_browser_file::download(filename.c_str(),
"application/zip",
buffer,
buffer_size);
emscripten_browser_file::download(filename.c_str(),
"application/zip",
buffer,
buffer_size);
#else
ofBufferToFile(filename.c_str(), ofBuffer(buffer, buffer_size));
#endif
freshDownload.store(false);
}else if(isThreadRunning()){
setProgress(percent.load());
@ -153,7 +187,8 @@ class ZipSaver : public ofThread {
}
void setProgress(int percent){
EM_ASM_INT({
#ifdef TARGET_EMSCRIPTEN
EM_ASM_INT({
let percent = $0;
document.getElementById('export_progress_task').innerHTML = 'rendering';
let innerHTML = "|";
@ -171,6 +206,9 @@ class ZipSaver : public ofThread {
let progress_task = document.getElementById("export_progress_task");
progress_task.innerHTML = "creating zip file";
}, percent);
#else
std::cout << "progress: " << percent << "/100" << std::endl;
#endif
}
void threadedFunction(){
@ -188,9 +226,9 @@ class ZipSaver : public ofThread {
ofImage image;
image.setUseTexture(false);
image.load(filepath);
ofBuffer buffer;
ofSaveImage(image.getPixels(), buffer, OF_IMAGE_FORMAT_PNG);
zip.addBuffer(f + ".png", buffer.getData(), buffer.size());
ofBuffer b;
ofSaveImage(image.getPixels(), b, OF_IMAGE_FORMAT_PNG);
zip.addBuffer(f + ".png", b.getData(), b.size());
percent.store((float(i) / float(total)) * 100.0f);
i++;
}
@ -232,7 +270,8 @@ class ZipUnpacker : public ofThread {
}
void setProgress(int percent){
EM_ASM_INT({
#ifdef TARGET_EMSCRIPTEN
EM_ASM_INT({
let percent = $0;
document.getElementById('export_progress_task').innerHTML = 'uploading and unpacking';
let innerHTML = "|";
@ -250,6 +289,9 @@ class ZipUnpacker : public ofThread {
let progress_task = document.getElementById("import_progress_task");
progress_task.innerHTML = "creating zip file";
}, percent);
#else
std::cout << "progress: " << percent << "/100" << std::endl;
#endif
}
void threadedFunction(){

14
watch.sh Executable file
View file

@ -0,0 +1,14 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
PREVIOUS_DIR=$(pwd)
cd $DIR
while true;
do
echo "$(git ls-files src && git ls-files bin/data && echo "assets/template.html")" | entr -d ./rebuild.sh
sleep 1
done
cd $PREVIOUS_DIR

14
watchJs.sh Executable file
View file

@ -0,0 +1,14 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
PREVIOUS_DIR=$(pwd)
cd $DIR
while true;
do
echo "$(git ls-files bin/em/variabletime/web && find "$(pwd)/bin/em/variabletime/web/theatre_modules" -iname "*.js")" | entr -d ./reloadbrowser.sh
sleep 1
done
cd $PREVIOUS_DIR