4track 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +80 -0
  3. package/dist/assets/btn_fwd.svg +30 -0
  4. package/dist/assets/btn_normal.png +0 -0
  5. package/dist/assets/btn_pause.svg +30 -0
  6. package/dist/assets/btn_play.svg +25 -0
  7. package/dist/assets/btn_pressed.png +0 -0
  8. package/dist/assets/btn_rec.svg +25 -0
  9. package/dist/assets/btn_rew.svg +30 -0
  10. package/dist/assets/btn_stop.svg +25 -0
  11. package/dist/assets/casette_hiss.mp3 +0 -0
  12. package/dist/assets/casette_hiss_compressed.mp3 +0 -0
  13. package/dist/assets/cassette.jpg +0 -0
  14. package/dist/assets/counter_bg.png +0 -0
  15. package/dist/assets/fx/counter.wav +0 -0
  16. package/dist/assets/fx/ffwd.wav +0 -0
  17. package/dist/assets/fx/pause.wav +0 -0
  18. package/dist/assets/fx/play.wav +0 -0
  19. package/dist/assets/fx/record.wav +0 -0
  20. package/dist/assets/fx/stop.wav +0 -0
  21. package/dist/assets/fx/track.wav +0 -0
  22. package/dist/assets/logo.svg +51 -0
  23. package/dist/assets/noise_50.jpg +0 -0
  24. package/dist/assets/openstudio.svg +38 -0
  25. package/dist/assets/recorder-worklet.d.ts +8 -0
  26. package/dist/assets/recorder-worklet.js +30 -0
  27. package/dist/assets/rotator.png +0 -0
  28. package/dist/assets/slider-indicator.svg +139 -0
  29. package/dist/assets/slider.png +0 -0
  30. package/dist/assets/slideselect-indicator.svg +64 -0
  31. package/dist/assets/slideselect-thumb.png +0 -0
  32. package/dist/assets/svg-icons.d.ts +6 -0
  33. package/dist/assets/svg-icons.js +8 -0
  34. package/dist/assets.d.ts +34 -0
  35. package/dist/audio/constants.d.ts +4 -0
  36. package/dist/audio/constants.js +27 -0
  37. package/dist/audio/engine.svelte.d.ts +90 -0
  38. package/dist/audio/engine.svelte.js +604 -0
  39. package/dist/audio/input-fx.d.ts +8 -0
  40. package/dist/audio/input-fx.js +44 -0
  41. package/dist/audio/metering.d.ts +3 -0
  42. package/dist/audio/metering.js +20 -0
  43. package/dist/audio/pcm.d.ts +2 -0
  44. package/dist/audio/pcm.js +43 -0
  45. package/dist/audio/project-io.d.ts +6 -0
  46. package/dist/audio/project-io.js +85 -0
  47. package/dist/audio/recording.d.ts +2 -0
  48. package/dist/audio/recording.js +80 -0
  49. package/dist/audio/track.svelte.d.ts +13 -0
  50. package/dist/audio/track.svelte.js +17 -0
  51. package/dist/components/Cassette.svelte +179 -0
  52. package/dist/components/Cassette.svelte.d.ts +9 -0
  53. package/dist/components/FourTrack.svelte +443 -0
  54. package/dist/components/FourTrack.svelte.d.ts +16 -0
  55. package/dist/components/Mixer.svelte +105 -0
  56. package/dist/components/Mixer.svelte.d.ts +7 -0
  57. package/dist/components/TransportButtons.svelte +299 -0
  58. package/dist/components/TransportButtons.svelte.d.ts +10 -0
  59. package/dist/components/els/DigitRoller.svelte +82 -0
  60. package/dist/components/els/DigitRoller.svelte.d.ts +5 -0
  61. package/dist/components/els/Knob.svelte +267 -0
  62. package/dist/components/els/Knob.svelte.d.ts +12 -0
  63. package/dist/components/els/Light.svelte +104 -0
  64. package/dist/components/els/Light.svelte.d.ts +8 -0
  65. package/dist/components/els/Lights.svelte +101 -0
  66. package/dist/components/els/Lights.svelte.d.ts +11 -0
  67. package/dist/components/els/SlideSelect.svelte +159 -0
  68. package/dist/components/els/SlideSelect.svelte.d.ts +15 -0
  69. package/dist/components/els/Slider.svelte +139 -0
  70. package/dist/components/els/Slider.svelte.d.ts +21 -0
  71. package/dist/components/els/Timestamp.svelte +92 -0
  72. package/dist/components/els/Timestamp.svelte.d.ts +5 -0
  73. package/dist/fx/soundfx.d.ts +14 -0
  74. package/dist/fx/soundfx.js +65 -0
  75. package/dist/index.d.ts +4 -0
  76. package/dist/index.js +3 -0
  77. package/dist/types.d.ts +40 -0
  78. package/dist/types.js +1 -0
  79. package/package.json +48 -0
@@ -0,0 +1,104 @@
1
+ <script lang="ts">
2
+ let {
3
+ color = "green",
4
+ active = false,
5
+ pulsing = false,
6
+ }: {
7
+ color?: "red" | "green"
8
+ active?: boolean
9
+ pulsing?: false | "slow" | "fast"
10
+ } = $props()
11
+ </script>
12
+
13
+ <div class="light-bevel">
14
+ <div
15
+ class="light {color}"
16
+ class:active
17
+ class:pulsing-slow={pulsing === "slow" || pulsing === true}
18
+ class:pulsing-fast={pulsing === "fast"}
19
+ >
20
+ &nbsp;
21
+ </div>
22
+ </div>
23
+
24
+ <style>
25
+ .light-bevel {
26
+ background: linear-gradient(to bottom right, #101010, #a2a1a1);
27
+ height: 1.5cqw;
28
+ width: 1.5cqw;
29
+ border-radius: 50%;
30
+ container-type: size;
31
+ }
32
+
33
+ .light {
34
+ border-radius: 89cqw;
35
+ width: 66cqw;
36
+ height: 66cqh;
37
+ margin-top: 22cqh;
38
+ margin-left: 20cqw;
39
+ background: #645855;
40
+ box-shadow: inset 6cqw -6cqh 22cqw 17cqw rgba(62, 2, 2, 0.7);
41
+
42
+ /* &.red {
43
+ background: #8f3333;
44
+ box-shadow: inset 6cqw -6cqh 22cqw 17cqw rgba(62, 2, 2, 0.7);
45
+ } */
46
+ &.red.active {
47
+ background: #ff0000;
48
+ box-shadow:
49
+ inset 0 0 33cqw rgba(32, 1, 1, 0.9),
50
+ 0 0 89cqw 89cqw rgba(209, 24, 24, 0.1);
51
+ }
52
+ &.red.pulsing-slow {
53
+ animation: pulse-red 2s ease-in-out infinite;
54
+ }
55
+ &.red.pulsing-fast {
56
+ animation: pulse-red 0.5s ease-in-out infinite;
57
+ }
58
+
59
+ &.green {
60
+ background: #628615;
61
+ box-shadow: inset 6cqw -6cqh 22cqw 17cqw rgba(32, 40, 0, 0.8);
62
+ }
63
+ &.green.active {
64
+ background: #bbff00;
65
+ box-shadow:
66
+ inset 0 0 33cqw rgba(45, 53, 2, 0.9),
67
+ 0 0 89cqw 89cqw rgba(176, 240, 2, 0.1);
68
+ }
69
+ &.green.pulsing-slow {
70
+ animation: pulse-green 2s ease-in-out infinite;
71
+ }
72
+ &.green.pulsing-fast {
73
+ animation: pulse-green 0.5s ease-in-out infinite;
74
+ }
75
+ }
76
+
77
+ @keyframes pulse-red {
78
+ 0%,
79
+ 100% {
80
+ background: #645855;
81
+ box-shadow: inset 6cqw -6cqh 22cqw 17cqw rgba(62, 2, 2, 0.7);
82
+ }
83
+ 50% {
84
+ background: #ff0000;
85
+ box-shadow:
86
+ inset 0 0 33cqw rgba(32, 1, 1, 0.9),
87
+ 0 0 89cqw 89cqw rgba(209, 24, 24, 0.1);
88
+ }
89
+ }
90
+
91
+ @keyframes pulse-green {
92
+ 0%,
93
+ 100% {
94
+ background: #645855;
95
+ box-shadow: inset 6cqw -6cqh 22cqw 17cqw rgba(32, 40, 0, 0.8);
96
+ }
97
+ 50% {
98
+ background: #bbff00;
99
+ box-shadow:
100
+ inset 0 0 33cqw rgba(45, 53, 2, 0.9),
101
+ 0 0 89cqw 89cqw rgba(176, 240, 2, 0.1);
102
+ }
103
+ }
104
+ </style>
@@ -0,0 +1,8 @@
1
+ type $$ComponentProps = {
2
+ color?: "red" | "green";
3
+ active?: boolean;
4
+ pulsing?: false | "slow" | "fast";
5
+ };
6
+ declare const Light: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type Light = ReturnType<typeof Light>;
8
+ export default Light;
@@ -0,0 +1,101 @@
1
+ <script>
2
+ let { level } = $props()
3
+ </script>
4
+
5
+ <div class="indicators">
6
+ <div class="labels">
7
+ <div class="left">+6</div>
8
+ <div class="right">0</div>
9
+ </div>
10
+ <div class="lights">
11
+ <div class="inner">
12
+ <div class="light-bevel">
13
+ <div class="light high active" class:active={level > 3}>&nbsp;</div>
14
+ </div>
15
+ <div class="light-bevel">
16
+ <div class="light low active" class:active={level > 1}>&nbsp;</div>
17
+ </div>
18
+ <!-- <div class="light low" class:active={level > 0.5}>&nbsp;</div> -->
19
+ </div>
20
+ </div>
21
+ </div>
22
+
23
+ <style>
24
+ .indicators {
25
+ container-type: size;
26
+ width: 100%;
27
+ height: 100%;
28
+ }
29
+ .labels {
30
+ color: white;
31
+ display: flex;
32
+ font-size: 20cqw;
33
+ opacity: 0.8;
34
+ .left {
35
+ flex: 1;
36
+ }
37
+ .right {
38
+ flex: 1;
39
+ text-align: right;
40
+ transform: translate(-4cqw, 20cqh);
41
+ }
42
+ }
43
+ .lights {
44
+ height: 130cqw;
45
+ width: 50cqw;
46
+ background: #46474a;
47
+ box-shadow:
48
+ inset 4cqw -2cqw 12cqw rgba(0, 0, 0, 0.6),
49
+ inset 4cqw -4cqw 4cqw rgba(255, 255, 255, 0.5);
50
+ position: relative;
51
+ border-radius: 40cqw;
52
+ transform: rotate(-60deg) translate(10cqw, 10cqh);
53
+ }
54
+ .inner {
55
+ position: relative;
56
+ top: 3.5cqw;
57
+ left: 4cqw;
58
+ height: 120cqw;
59
+ width: 44cqw;
60
+ background: #46474a;
61
+ border-radius: 40cqw;
62
+ }
63
+ .light-bevel {
64
+ background: linear-gradient(to bottom, #101010, #6b6b6b);
65
+ top: 2.5cqh;
66
+ left: 4cqw;
67
+ height: 36cqw;
68
+ width: 36cqw;
69
+ margin-bottom: 15cqh;
70
+ border-radius: 50%;
71
+ position: relative;
72
+ }
73
+ .light {
74
+ position: absolute;
75
+ width: 31cqw;
76
+ height: 31cqw;
77
+ top: 3cqw;
78
+ left: 3cqw;
79
+ border-radius: 50%;
80
+ &.low {
81
+ background: #628615;
82
+ box-shadow: inset 2cqw -2cqw 8cqw 6cqw rgba(32, 40, 0, 0.8);
83
+ }
84
+ &.low.active {
85
+ background: #bbff00;
86
+ box-shadow:
87
+ inset 0 0 12cqw rgba(45, 53, 2, 0.9),
88
+ 0 0 32cqw 32cqw rgba(176, 240, 2, 0.1);
89
+ }
90
+ &.high {
91
+ background: #8f3333;
92
+ box-shadow: inset 2cqw -2cqw 8cqw 6cqw rgba(62, 2, 2, 0.7);
93
+ }
94
+ &.high.active {
95
+ background: #ff0000;
96
+ box-shadow:
97
+ inset 0 0 12cqw rgba(32, 1, 1, 0.9),
98
+ 0 0 32cqw 32cqw rgba(209, 24, 24, 0.1);
99
+ }
100
+ }
101
+ </style>
@@ -0,0 +1,11 @@
1
+ export default Lights;
2
+ type Lights = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const Lights: import("svelte").Component<{
7
+ level: any;
8
+ }, {}, "">;
9
+ type $$ComponentProps = {
10
+ level: any;
11
+ };
@@ -0,0 +1,159 @@
1
+ <!-- svelte-ignore a11y_role_has_required_aria_props -->
2
+ <!-- svelte-ignore a11y_interactive_supports_focus -->
3
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
4
+ <script>
5
+ import { playFx } from "../../fx/soundfx"
6
+ import slideSelectIndicatorImg from '../../assets/slideselect-indicator.svg?url'
7
+ import slideSelectThumbImg from '../../assets/slideselect-thumb.png'
8
+ let dragging = $state(false)
9
+ let trackEl = $state()
10
+ let selected_i = $state(4)
11
+ let startY = $state(0)
12
+ let startIndex = $state(0)
13
+ let initialized = false // to disable sound fx on loading of the component
14
+ let selections = [
15
+ { lbl: "TRK 1", val: 0 },
16
+ { lbl: "2", val: 1 },
17
+ { lbl: "3", val: 2 },
18
+ { lbl: "4", val: 3 },
19
+ { lbl: "SAFE", val: -1 },
20
+ ]
21
+ let btnHeight = 0.55
22
+
23
+ let { value = $bindable(), padding = 2, disabled = false } = $props() // padding: % inset top/bottom
24
+
25
+ const steps = selections.length - 1
26
+ const adjusted_scrollarea = 1 - btnHeight - (2 * padding) / 100
27
+ let xpos_percentage = $derived(
28
+ padding + Math.round((selected_i / steps) * adjusted_scrollarea * 100),
29
+ )
30
+
31
+ const start = (event) => {
32
+ if (disabled) return
33
+ dragging = true
34
+ startY = event.clientY
35
+ startIndex = selected_i
36
+ event.target.setPointerCapture(event.pointerId)
37
+ }
38
+
39
+ const move = (event) => {
40
+ if (!dragging) return
41
+ const rect = trackEl.getBoundingClientRect()
42
+ const steps = selections.length - 1
43
+ const adjusted_scrollarea = 1 - btnHeight - (2 * padding) / 100
44
+ const scrollHeight = rect.height * adjusted_scrollarea
45
+ const stepHeight = scrollHeight / steps
46
+ const deltaSteps = Math.round((event.clientY - startY) / stepHeight)
47
+
48
+ selected_i = Math.max(0, Math.min(startIndex + deltaSteps, steps))
49
+
50
+ // This is the bindable value
51
+ value = selections[selected_i]?.val
52
+ }
53
+
54
+ const stop = (event) => {
55
+ dragging = false
56
+ event.target.releasePointerCapture?.(event.pointerId)
57
+ }
58
+
59
+ $effect(() => {
60
+ const trigger = value // We are referencing value so that Svelte triggers the effect smartly
61
+
62
+ if (!initialized) {
63
+ initialized = true
64
+ return
65
+ }
66
+
67
+ playFx("track")
68
+ })
69
+ </script>
70
+
71
+ <div class="slider-holder" style:--bg-slideselect-indicator="url({slideSelectIndicatorImg})" style:--bg-slideselect-thumb="url({slideSelectThumbImg})">
72
+ <div class="slideselect-indicator"></div>
73
+ <div
74
+ bind:this={trackEl}
75
+ class="track"
76
+ onpointermove={move}
77
+ onpointerup={stop}
78
+ onpointerleave={stop}
79
+ role="slider"
80
+ >
81
+ <div
82
+ class="thumb"
83
+ class:dragging
84
+ class:disabled
85
+ onpointerdown={start}
86
+ style="top: {xpos_percentage}%; height: {btnHeight * 100}%"
87
+ ></div>
88
+ <!-- <div
89
+ class="slot"
90
+ style="height: {(1 - btnHeight) * 100}%; top: {(btnHeight / 2) * 100}%"
91
+ ></div> -->
92
+ </div>
93
+ </div>
94
+
95
+ <style>
96
+ .slider-holder {
97
+ height: 100%;
98
+ width: 100%;
99
+ display: flex;
100
+ container-type: size;
101
+ padding-top: 0cqh;
102
+ }
103
+ .slideselect-indicator {
104
+ position: relative;
105
+ background: var(--bg-slideselect-indicator);
106
+ background-repeat: no-repeat;
107
+ background-size: contain;
108
+ width: 100%;
109
+ height: 43cqh;
110
+ background-position: top right;
111
+ opacity: 0.7;
112
+ top: 10%;
113
+ }
114
+ .track {
115
+ width: 32cqw;
116
+ height: 100%;
117
+ position: relative;
118
+ border-radius: 4cqw;
119
+ background: rgb(100, 100, 100);
120
+ box-shadow:
121
+ inset 8cqw 1cqh 12cqw rgba(31, 31, 31, 0.75),
122
+ inset 2cqw 0.2cqh 2cqw rgba(31, 31, 31, 0.45),
123
+ inset -2cqw -0.2cqh 2cqw rgba(255, 252, 252, 0.35);
124
+
125
+ .slot {
126
+ display: block;
127
+ content: " ";
128
+ width: 50%;
129
+ background-color: rgb(28, 28, 29);
130
+ position: absolute;
131
+ margin: 0 auto;
132
+ left: 50%;
133
+ top: 50%;
134
+ transform: translate(-50%);
135
+ }
136
+ }
137
+
138
+ .thumb {
139
+ width: 75%;
140
+ margin: 0 12%;
141
+ background: var(--bg-slideselect-thumb);
142
+ background-size: 100% 100%;
143
+ position: absolute;
144
+ border-radius: 1cqh;
145
+ top: 0%;
146
+ left: 0;
147
+ cursor: grab;
148
+ z-index: 1;
149
+ box-shadow:
150
+ 10cqw 0.5cqh 10cqw rgba(0, 0, 0, 0.4),
151
+ inset 1.5cqw 0.5cqh 1cqw rgba(255, 252, 252, 0.35);
152
+ }
153
+ .thumb.dragging {
154
+ cursor: grabbing;
155
+ }
156
+ .thumb.disabled {
157
+ cursor: not-allowed;
158
+ }
159
+ </style>
@@ -0,0 +1,15 @@
1
+ export default SlideSelect;
2
+ type SlideSelect = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const SlideSelect: import("svelte").Component<{
7
+ value?: any;
8
+ padding?: number;
9
+ disabled?: boolean;
10
+ }, {}, "value">;
11
+ type $$ComponentProps = {
12
+ value?: any;
13
+ padding?: number;
14
+ disabled?: boolean;
15
+ };
@@ -0,0 +1,139 @@
1
+ <!-- svelte-ignore a11y_role_has_required_aria_props -->
2
+ <!-- svelte-ignore a11y_interactive_supports_focus -->
3
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
4
+ <script>
5
+ import sliderIndicatorImg from '../../assets/slider-indicator.svg?url'
6
+ import sliderImg from '../../assets/slider.png'
7
+
8
+ let { value = $bindable(0), min = 0, max = 1, onchange, btnHeight = 0.35, padding = 1 } = $props()
9
+ let dragging = $state(false)
10
+ let trackEl = $state()
11
+ let startY = $state(0)
12
+ let startNorm = $state(0)
13
+
14
+ function normalizeValue(val) {
15
+ return (val - min) / (max - min)
16
+ }
17
+ function denormalizeValue(norm) {
18
+ return min + norm * (max - min)
19
+ }
20
+
21
+ // Inverted: top = max, bottom = min (like a real fader)
22
+ let topPercent = $derived(
23
+ padding + ((1 - normalizeValue(value)) * (1 - btnHeight - (2 * padding) / 100) * 100),
24
+ )
25
+
26
+ const start = (event) => {
27
+ dragging = true
28
+ startY = event.clientY
29
+ startNorm = normalizeValue(value)
30
+ trackEl.setPointerCapture(event.pointerId)
31
+ }
32
+
33
+ const move = (event) => {
34
+ if (!dragging) return
35
+ const rect = trackEl.getBoundingClientRect()
36
+ const deltaY = event.clientY - startY
37
+ const scrollHeight = rect.height * (1 - btnHeight)
38
+ // Inverted: dragging down decreases value
39
+ const deltaNorm = -(deltaY / scrollHeight)
40
+ const newNorm = Math.max(0, Math.min(startNorm + deltaNorm, 1))
41
+ value = denormalizeValue(newNorm)
42
+ onchange?.(value)
43
+ }
44
+
45
+ const stop = (event) => {
46
+ dragging = false
47
+ trackEl.releasePointerCapture?.(event.pointerId)
48
+ }
49
+ </script>
50
+
51
+ <div class="slider-holder" style:--bg-slider-indicator="url({sliderIndicatorImg})" style:--bg-slider="url({sliderImg})">
52
+ <div
53
+ class="slider-indicator"
54
+ style="height: {(1 - btnHeight) * 100}%; top: {(btnHeight / 2) * 100}%"
55
+ ></div>
56
+ <div
57
+ bind:this={trackEl}
58
+ class="track"
59
+ onpointermove={move}
60
+ onpointerup={stop}
61
+ onpointerleave={stop}
62
+ role="slider"
63
+ >
64
+ <div
65
+ class="thumb"
66
+ class:dragging
67
+ onpointerdown={start}
68
+ style="top: {topPercent}%; height: {btnHeight * 100}%"
69
+ ></div>
70
+ <div
71
+ class="slot"
72
+ style="height: {(1 - btnHeight) * 100}%; top: {(btnHeight / 2) * 100}%"
73
+ ></div>
74
+ </div>
75
+ </div>
76
+
77
+ <style>
78
+ .slider-holder {
79
+ height: 100%;
80
+ width: 5cqw;
81
+ display: flex;
82
+ container-type: size;
83
+ padding-top: 2cqh;
84
+ }
85
+
86
+ .slider-indicator {
87
+ position: relative;
88
+ background: var(--bg-slider-indicator);
89
+ background-repeat: no-repeat;
90
+ background-size: contain;
91
+ width: 55cqw;
92
+ height: 100%;
93
+ background-position: center;
94
+ opacity: 0.7;
95
+ }
96
+
97
+ .track {
98
+ width: 45cqw;
99
+ height: 100%;
100
+ position: relative;
101
+ border-radius: 8cqw;
102
+ box-shadow:
103
+ inset 9cqw 2cqh 15cqw rgba(31, 31, 31, 0.75),
104
+ inset 1.5cqw 0.2cqh 1.5cqw rgba(31, 31, 31, 0.45),
105
+ inset -1.5cqw -0.2cqh 1.5cqw rgba(255, 252, 252, 0.35);
106
+
107
+ .slot {
108
+ display: block;
109
+ content: " ";
110
+ width: 50%;
111
+ background-color: rgb(28, 28, 29);
112
+ border-radius: 3cqw;
113
+ position: absolute;
114
+ margin: 0 auto;
115
+ left: 50%;
116
+ top: 50%;
117
+ transform: translate(-50%);
118
+ }
119
+ }
120
+
121
+ .thumb {
122
+ width: 80%;
123
+ margin: 0 10%;
124
+ background: var(--bg-slider);
125
+ background-size: 100% 100%;
126
+ position: absolute;
127
+ top: 0%;
128
+ left: 0;
129
+ cursor: grab;
130
+ z-index: 1;
131
+ border-radius: 10cqw;
132
+ box-shadow:
133
+ 15cqw 1cqh 8cqw rgba(0, 0, 0, 0.4),
134
+ inset 1.5cqw 0.5cqh 1cqw rgba(255, 252, 252, 0.35);
135
+ }
136
+ .thumb.dragging {
137
+ cursor: grabbing;
138
+ }
139
+ </style>
@@ -0,0 +1,21 @@
1
+ export default Slider;
2
+ type Slider = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const Slider: import("svelte").Component<{
7
+ value?: number;
8
+ min?: number;
9
+ max?: number;
10
+ onchange: any;
11
+ btnHeight?: number;
12
+ padding?: number;
13
+ }, {}, "value">;
14
+ type $$ComponentProps = {
15
+ value?: number;
16
+ min?: number;
17
+ max?: number;
18
+ onchange: any;
19
+ btnHeight?: number;
20
+ padding?: number;
21
+ };
@@ -0,0 +1,92 @@
1
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
2
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
3
+ <!-- svelte-ignore a11y_missing_attribute -->
4
+ <script lang="ts">
5
+ let { timestamp } = $props()
6
+ import { playFx } from "../../fx/soundfx"
7
+ import DigitRoller from "./DigitRoller.svelte"
8
+ import counterBgImg from '../../assets/counter_bg.png'
9
+
10
+ let correction = $state(0)
11
+ function count_to_str(nr: number) {
12
+ var cor_nr = Math.floor(nr - correction)
13
+ if (cor_nr < 0) cor_nr += 1000
14
+
15
+ if (cor_nr > 99) {
16
+ return cor_nr.toString()
17
+ } else if (cor_nr > 9) {
18
+ return "0" + cor_nr.toString()
19
+ } else {
20
+ return "00" + cor_nr.toString()
21
+ }
22
+ }
23
+
24
+ function get_digit(nr, i) {
25
+ return count_to_str(nr).charAt(i)
26
+ }
27
+
28
+ function reset() {
29
+ playFx("counter")
30
+ correction = timestamp
31
+ }
32
+ </script>
33
+
34
+ <!-- <div>{count_to_str(timestamp)}</div> -->
35
+
36
+ <div class="wrapper" style:--bg-counter="url({counterBgImg})">
37
+ <div class="counter">
38
+ <a onmousedown={() => reset()}>&nbsp;</a>
39
+ <div class="number-ticker">
40
+ <DigitRoller digit={get_digit(timestamp, 0)} />
41
+ <DigitRoller digit={get_digit(timestamp, 1)} />
42
+ <DigitRoller digit={get_digit(timestamp, 2)} />
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <style>
48
+ .number-ticker {
49
+ display: flex;
50
+ height: 17cqw;
51
+ overflow: hidden;
52
+ }
53
+
54
+ .wrapper {
55
+ container-type: size;
56
+ height: 8cqh;
57
+ aspect-ratio: 180 / 80;
58
+ }
59
+ .counter {
60
+ padding: 15.2cqw 17cqh 13.8cqw;
61
+ color: rgb(216, 216, 216);
62
+ background-image: var(--bg-counter);
63
+ background-size: 100% 100%;
64
+ background-repeat: no-repeat;
65
+ width: 100cqw;
66
+ height: 100cqh;
67
+ position: relative;
68
+ user-select: none;
69
+
70
+ box-sizing: border-box;
71
+ }
72
+ a {
73
+ width: 11cqw;
74
+ height: 25cqh;
75
+ background-color: rgb(34, 34, 34);
76
+ border-radius: 50%;
77
+ position: absolute;
78
+ right: 16.7cqw;
79
+ top: 50%;
80
+ transform: translateY(-15cqh);
81
+ cursor: pointer;
82
+ box-shadow:
83
+ 6cqw 15cqh 5.5cqw rgba(0, 0, 0, 0.8),
84
+ inset 1cqw 2.5cqh 1.7cqw rgba(255, 255, 255, 0.4);
85
+
86
+ &:active {
87
+ box-shadow:
88
+ 5.5cqw 13.75cqh 6cqw rgba(0, 0, 0, 0.8),
89
+ inset 1cqw 2.5cqh 1.7cqw rgba(255, 255, 255, 0.4);
90
+ }
91
+ }
92
+ </style>
@@ -0,0 +1,5 @@
1
+ declare const Timestamp: import("svelte").Component<{
2
+ timestamp: any;
3
+ }, {}, "">;
4
+ type Timestamp = ReturnType<typeof Timestamp>;
5
+ export default Timestamp;
@@ -0,0 +1,14 @@
1
+ declare const soundPaths: {
2
+ stop: string;
3
+ ffwd: string;
4
+ pause: string;
5
+ play: string;
6
+ track: string;
7
+ counter: string;
8
+ record: string;
9
+ };
10
+ type SoundKey = keyof typeof soundPaths;
11
+ export declare function playFx(key: SoundKey, volume?: number): Promise<void>;
12
+ export declare function playLoop(key: SoundKey, volume?: number): Promise<void>;
13
+ export declare function stopLoop(key: SoundKey): void;
14
+ export {};