4track 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  A browser-based 4-track audio recorder built with the Web Audio API and SvelteKit. Designed for low-latency recording with overdub support, latency compensation, and sample-accurate multi-track playback — all running entirely client-side with no server required.
4
4
 
5
+ ![4-Track Recorder](img/4track.png)
6
+
7
+ ## Give it a try
8
+
9
+ You can try out the 4 track recorder on [4track.cc](https://www.4track.cc)
10
+
5
11
  ## Installation
6
12
 
7
13
  ```bash
@@ -12,7 +18,7 @@ npm install 4-track-recorder
12
18
 
13
19
  ```svelte
14
20
  <script>
15
- import { FourTrack } from '4-track-recorder'
21
+ import { FourTrack } from "4-track-recorder"
16
22
  </script>
17
23
 
18
24
  <FourTrack />
@@ -28,7 +34,7 @@ Use `bind:save` and `bind:load` to get functions you can call from your own UI:
28
34
 
29
35
  ```svelte
30
36
  <script>
31
- import { FourTrack } from '4-track-recorder'
37
+ import { FourTrack } from "4-track-recorder"
32
38
 
33
39
  let save
34
40
  let load
@@ -37,9 +43,9 @@ Use `bind:save` and `bind:load` to get functions you can call from your own UI:
37
43
  const blob = save()
38
44
  // Download as file
39
45
  const url = URL.createObjectURL(blob)
40
- const a = document.createElement('a')
46
+ const a = document.createElement("a")
41
47
  a.href = url
42
- a.download = 'my-song.4trk'
48
+ a.download = "my-song.4trk"
43
49
  a.click()
44
50
  URL.revokeObjectURL(url)
45
51
  }
@@ -51,7 +57,7 @@ Use `bind:save` and `bind:load` to get functions you can call from your own UI:
51
57
 
52
58
  // Or load directly from a URL
53
59
  function loadFromUrl() {
54
- load('https://example.com/songs/demo.4trk')
60
+ load("https://example.com/songs/demo.4trk")
55
61
  }
56
62
  </script>
57
63
 
@@ -71,10 +77,10 @@ Pass a URL or `File` to `initialProject` to auto-load a project when the compone
71
77
 
72
78
  ## Props
73
79
 
74
- | Prop | Type | Description |
75
- |------|------|-------------|
76
- | `hiddenTracks` | `HiddenTrackConfig[]` | Background audio tracks (e.g. cassette hiss) |
77
- | `onready` | `(detail: { engine: AudioEngine }) => void` | Callback when the engine is initialized |
78
- | `save` | `() => Blob` (bindable) | Bind to get a function that exports the project as a `.4trk` blob |
79
- | `load` | `(source: File \| string) => Promise<void>` (bindable) | Bind to get a function that imports a `.4trk` file or URL |
80
- | `initialProject` | `string \| File` | URL or File to auto-load on mount |
80
+ | Prop | Type | Description |
81
+ | ---------------- | ------------------------------------------------------ | ----------------------------------------------------------------- |
82
+ | `hiddenTracks` | `HiddenTrackConfig[]` | Background audio tracks (e.g. cassette hiss) |
83
+ | `onready` | `(detail: { engine: AudioEngine }) => void` | Callback when the engine is initialized |
84
+ | `save` | `() => Blob` (bindable) | Bind to get a function that exports the project as a `.4trk` blob |
85
+ | `load` | `(source: File \| string) => Promise<void>` (bindable) | Bind to get a function that imports a `.4trk` file or URL |
86
+ | `initialProject` | `string \| File` | URL or File to auto-load on mount |
@@ -3,8 +3,8 @@
3
3
  <!-- svelte-ignore a11y_no_static_element_interactions -->
4
4
  <script>
5
5
  import { playFx } from "../../fx/soundfx"
6
- import slideSelectIndicatorImg from '../../assets/slideselect-indicator.svg?url'
7
- import slideSelectThumbImg from '../../assets/slideselect-thumb.png'
6
+ import slideSelectIndicatorImg from "../../assets/slideselect-indicator.svg?url"
7
+ import slideSelectThumbImg from "../../assets/slideselect-thumb.png"
8
8
  let dragging = $state(false)
9
9
  let trackEl = $state()
10
10
  let selected_i = $state(4)
@@ -68,7 +68,11 @@
68
68
  })
69
69
  </script>
70
70
 
71
- <div class="slider-holder" style:--bg-slideselect-indicator="url({slideSelectIndicatorImg})" style:--bg-slideselect-thumb="url({slideSelectThumbImg})">
71
+ <div
72
+ class="slider-holder"
73
+ style:--bg-slideselect-indicator="url({slideSelectIndicatorImg})"
74
+ style:--bg-slideselect-thumb="url({slideSelectThumbImg})"
75
+ >
72
76
  <div class="slideselect-indicator"></div>
73
77
  <div
74
78
  bind:this={trackEl}
@@ -117,6 +121,7 @@
117
121
  position: relative;
118
122
  border-radius: 4cqw;
119
123
  background: rgb(100, 100, 100);
124
+ touch-action: none;
120
125
  box-shadow:
121
126
  inset 8cqw 1cqh 12cqw rgba(31, 31, 31, 0.75),
122
127
  inset 2cqw 0.2cqh 2cqw rgba(31, 31, 31, 0.45),
@@ -142,6 +147,7 @@
142
147
  background-size: 100% 100%;
143
148
  position: absolute;
144
149
  border-radius: 1cqh;
150
+ touch-action: none;
145
151
  top: 0%;
146
152
  left: 0;
147
153
  cursor: grab;
@@ -2,10 +2,17 @@
2
2
  <!-- svelte-ignore a11y_interactive_supports_focus -->
3
3
  <!-- svelte-ignore a11y_no_static_element_interactions -->
4
4
  <script>
5
- import sliderIndicatorImg from '../../assets/slider-indicator.svg?url'
6
- import sliderImg from '../../assets/slider.png'
5
+ import sliderIndicatorImg from "../../assets/slider-indicator.svg?url"
6
+ import sliderImg from "../../assets/slider.png"
7
7
 
8
- let { value = $bindable(0), min = 0, max = 1, onchange, btnHeight = 0.35, padding = 1 } = $props()
8
+ let {
9
+ value = $bindable(0),
10
+ min = 0,
11
+ max = 1,
12
+ onchange,
13
+ btnHeight = 0.35,
14
+ padding = 1,
15
+ } = $props()
9
16
  let dragging = $state(false)
10
17
  let trackEl = $state()
11
18
  let startY = $state(0)
@@ -20,7 +27,8 @@
20
27
 
21
28
  // Inverted: top = max, bottom = min (like a real fader)
22
29
  let topPercent = $derived(
23
- padding + ((1 - normalizeValue(value)) * (1 - btnHeight - (2 * padding) / 100) * 100),
30
+ padding +
31
+ (1 - normalizeValue(value)) * (1 - btnHeight - (2 * padding) / 100) * 100,
24
32
  )
25
33
 
26
34
  const start = (event) => {
@@ -48,7 +56,11 @@
48
56
  }
49
57
  </script>
50
58
 
51
- <div class="slider-holder" style:--bg-slider-indicator="url({sliderIndicatorImg})" style:--bg-slider="url({sliderImg})">
59
+ <div
60
+ class="slider-holder"
61
+ style:--bg-slider-indicator="url({sliderIndicatorImg})"
62
+ style:--bg-slider="url({sliderImg})"
63
+ >
52
64
  <div
53
65
  class="slider-indicator"
54
66
  style="height: {(1 - btnHeight) * 100}%; top: {(btnHeight / 2) * 100}%"
@@ -99,6 +111,7 @@
99
111
  height: 100%;
100
112
  position: relative;
101
113
  border-radius: 8cqw;
114
+ touch-action: none;
102
115
  box-shadow:
103
116
  inset 9cqw 2cqh 15cqw rgba(31, 31, 31, 0.75),
104
117
  inset 1.5cqw 0.2cqh 1.5cqw rgba(31, 31, 31, 0.45),
@@ -129,6 +142,7 @@
129
142
  cursor: grab;
130
143
  z-index: 1;
131
144
  border-radius: 10cqw;
145
+ touch-action: none
132
146
  box-shadow:
133
147
  15cqw 1cqh 8cqw rgba(0, 0, 0, 0.4),
134
148
  inset 1.5cqw 0.5cqh 1cqw rgba(255, 252, 252, 0.35);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "4track",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "A 4-track cassette recorder component for Svelte 5",
5
5
  "license": "GPL-3.0-only",
6
6
  "author": "Andre Boekhorst",