4track 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audio/engine.svelte.d.ts +2 -1
- package/dist/audio/engine.svelte.js +4 -2
- package/dist/audio/project-io.d.ts +3 -2
- package/dist/audio/project-io.js +15 -2
- package/dist/components/FourTrack.svelte +9 -2
- package/dist/components/FourTrack.svelte.d.ts +3 -2
- package/dist/components/els/DigitRoller.svelte +8 -1
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +8 -0
- package/package.json +1 -1
- package/dist/components/els/DigitRollerFirst.svelte +0 -82
- package/dist/components/els/DigitRollerFirst.svelte.d.ts +0 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AudioEngineConfig, MicStatus, PlayState } from "../types.js";
|
|
1
|
+
import type { AudioEngineConfig, MicStatus, PlayState, ProjectMeta } from "../types.js";
|
|
2
2
|
import { Track } from "./track.svelte.js";
|
|
3
3
|
export declare class AudioEngine {
|
|
4
4
|
playState: PlayState;
|
|
@@ -8,6 +8,7 @@ export declare class AudioEngine {
|
|
|
8
8
|
latencyInfo: string;
|
|
9
9
|
trimValue: number;
|
|
10
10
|
recordingVolume: number;
|
|
11
|
+
meta: ProjectMeta;
|
|
11
12
|
tracks: Track[];
|
|
12
13
|
/** Rounds a time value to 2 decimal places for position display. */
|
|
13
14
|
private roundPosition;
|
|
@@ -16,6 +16,7 @@ export class AudioEngine {
|
|
|
16
16
|
latencyInfo = $state("");
|
|
17
17
|
trimValue = $state(-1);
|
|
18
18
|
recordingVolume = $state(0.75);
|
|
19
|
+
meta = $state({ artist: "", title: "", comment: "" });
|
|
19
20
|
tracks;
|
|
20
21
|
/** Rounds a time value to 2 decimal places for position display. */
|
|
21
22
|
roundPosition(seconds) {
|
|
@@ -577,12 +578,13 @@ export class AudioEngine {
|
|
|
577
578
|
// ─── Save / Load ────────────────────────────────────────────────────
|
|
578
579
|
/** Serializes all tracks and settings into a compressed .4trk binary blob. */
|
|
579
580
|
exportProject() {
|
|
580
|
-
return _exportProject(this.tracks, this.config, this.masterVolume);
|
|
581
|
+
return _exportProject(this.tracks, this.config, this.masterVolume, this.meta);
|
|
581
582
|
}
|
|
582
583
|
/** Loads a .4trk file, restoring all track buffers, mixer settings, and master volume. */
|
|
583
584
|
async importProject(file) {
|
|
584
|
-
const { masterVolume } = await _importProject(file, this.tracks, () => this.ensureContext());
|
|
585
|
+
const { masterVolume, meta } = await _importProject(file, this.tracks, () => this.ensureContext());
|
|
585
586
|
this.setMasterVolume(masterVolume);
|
|
587
|
+
this.meta = meta;
|
|
586
588
|
this.rewind();
|
|
587
589
|
}
|
|
588
590
|
// ─── Cleanup ────────────────────────────────────────────────────────
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Track } from './track.svelte.js';
|
|
2
|
-
import type { AudioEngineConfig } from '../types.js';
|
|
3
|
-
export declare function exportProject(tracks: Track[], config: AudioEngineConfig, masterVolume: number): Promise<Blob>;
|
|
2
|
+
import type { AudioEngineConfig, ProjectMeta } from '../types.js';
|
|
3
|
+
export declare function exportProject(tracks: Track[], config: AudioEngineConfig, masterVolume: number, meta?: ProjectMeta): Promise<Blob>;
|
|
4
4
|
export declare function importProject(file: File | Blob, tracks: Track[], ensureContext: () => AudioContext): Promise<{
|
|
5
5
|
masterVolume: number;
|
|
6
|
+
meta: ProjectMeta;
|
|
6
7
|
}>;
|
package/dist/audio/project-io.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Uses integer quantization from ./pcm.ts for compact storage.
|
|
4
4
|
import { Track } from './track.svelte.js';
|
|
5
5
|
import { quantizePCM, dequantizePCM } from './pcm.js';
|
|
6
|
-
export async function exportProject(tracks, config, masterVolume) {
|
|
6
|
+
export async function exportProject(tracks, config, masterVolume, meta = { artist: '', title: '', comment: '' }) {
|
|
7
7
|
const trackMeta = [];
|
|
8
8
|
const pcmParts = [];
|
|
9
9
|
for (const track of tracks) {
|
|
@@ -37,6 +37,12 @@ export async function exportProject(tracks, config, masterVolume) {
|
|
|
37
37
|
masterVolume,
|
|
38
38
|
tracks: trackMeta,
|
|
39
39
|
};
|
|
40
|
+
if (meta.artist)
|
|
41
|
+
metadata.artist = meta.artist.slice(0, 64);
|
|
42
|
+
if (meta.title)
|
|
43
|
+
metadata.title = meta.title.slice(0, 64);
|
|
44
|
+
if (meta.comment)
|
|
45
|
+
metadata.comment = meta.comment.slice(0, 256);
|
|
40
46
|
const encoder = new TextEncoder();
|
|
41
47
|
const metaBytes = encoder.encode(JSON.stringify(metadata));
|
|
42
48
|
const metaLength = new Uint32Array([metaBytes.length]);
|
|
@@ -84,5 +90,12 @@ export async function importProject(file, tracks, ensureContext) {
|
|
|
84
90
|
if (track.panNode)
|
|
85
91
|
track.panNode.pan.value = track.pan;
|
|
86
92
|
}
|
|
87
|
-
return {
|
|
93
|
+
return {
|
|
94
|
+
masterVolume: metadata.masterVolume ?? 1.0,
|
|
95
|
+
meta: {
|
|
96
|
+
artist: metadata.artist ?? '',
|
|
97
|
+
title: metadata.title ?? '',
|
|
98
|
+
comment: metadata.comment ?? '',
|
|
99
|
+
},
|
|
100
|
+
};
|
|
88
101
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { AudioEngine } from "../audio/engine.svelte.js"
|
|
3
|
-
import type { HiddenTrackConfig, LoadStatus } from "../types.js"
|
|
3
|
+
import type { HiddenTrackConfig, LoadStatus, ProjectMeta } from "../types.js"
|
|
4
4
|
import casetteHissUrl from "../assets/casette_hiss_compressed.mp3"
|
|
5
5
|
import noiseImg from "../assets/noise_50.jpg"
|
|
6
6
|
import logoImg from "../assets/logo.svg?url"
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
initialProject,
|
|
24
24
|
status = $bindable<LoadStatus>("idle"),
|
|
25
25
|
loadProgress = $bindable(0),
|
|
26
|
+
meta = $bindable<ProjectMeta>({ artist: "", title: "", comment: "" }),
|
|
26
27
|
}: {
|
|
27
28
|
hiddenTracks?: HiddenTrackConfig[]
|
|
28
29
|
onready?: (detail: { engine: AudioEngine }) => void
|
|
@@ -31,6 +32,7 @@
|
|
|
31
32
|
initialProject?: string | File
|
|
32
33
|
status?: LoadStatus
|
|
33
34
|
loadProgress?: number
|
|
35
|
+
meta?: ProjectMeta
|
|
34
36
|
} = $props()
|
|
35
37
|
|
|
36
38
|
let engine: AudioEngine | null = $state(null)
|
|
@@ -39,6 +41,7 @@
|
|
|
39
41
|
let recordEngaged = $state(false)
|
|
40
42
|
let resetTransport: (() => void) | undefined = $state()
|
|
41
43
|
|
|
44
|
+
|
|
42
45
|
async function fetchWithProgress(url: string): Promise<File> {
|
|
43
46
|
const response = await fetch(url)
|
|
44
47
|
const contentLength = response.headers.get("Content-Length")
|
|
@@ -76,7 +79,10 @@
|
|
|
76
79
|
engine = new AudioEngine({ hiddenTracks })
|
|
77
80
|
engine.initAudioContext()
|
|
78
81
|
|
|
79
|
-
save = () =>
|
|
82
|
+
save = () => {
|
|
83
|
+
engine!.meta = { ...meta }
|
|
84
|
+
return engine!.exportProject()
|
|
85
|
+
}
|
|
80
86
|
load = async (source: File | string) => {
|
|
81
87
|
status = "loading"
|
|
82
88
|
loadProgress = 0
|
|
@@ -84,6 +90,7 @@
|
|
|
84
90
|
const file =
|
|
85
91
|
typeof source === "string" ? await fetchWithProgress(source) : source
|
|
86
92
|
await engine!.importProject(file)
|
|
93
|
+
meta = { ...engine!.meta }
|
|
87
94
|
resetTransport?.()
|
|
88
95
|
status = "ready"
|
|
89
96
|
} catch (e) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AudioEngine } from "../audio/engine.svelte.js";
|
|
2
|
-
import type { HiddenTrackConfig, LoadStatus } from "../types.js";
|
|
2
|
+
import type { HiddenTrackConfig, LoadStatus, ProjectMeta } from "../types.js";
|
|
3
3
|
type $$ComponentProps = {
|
|
4
4
|
hiddenTracks?: HiddenTrackConfig[];
|
|
5
5
|
onready?: (detail: {
|
|
@@ -10,7 +10,8 @@ type $$ComponentProps = {
|
|
|
10
10
|
initialProject?: string | File;
|
|
11
11
|
status?: LoadStatus;
|
|
12
12
|
loadProgress?: number;
|
|
13
|
+
meta?: ProjectMeta;
|
|
13
14
|
};
|
|
14
|
-
declare const FourTrack: import("svelte").Component<$$ComponentProps, {}, "save" | "load" | "status" | "loadProgress">;
|
|
15
|
+
declare const FourTrack: import("svelte").Component<$$ComponentProps, {}, "meta" | "save" | "load" | "status" | "loadProgress">;
|
|
15
16
|
type FourTrack = ReturnType<typeof FourTrack>;
|
|
16
17
|
export default FourTrack;
|
|
@@ -22,7 +22,14 @@
|
|
|
22
22
|
const digit = Math.floor(value)
|
|
23
23
|
const fraction = value - digit
|
|
24
24
|
|
|
25
|
-
const eased =
|
|
25
|
+
const eased =
|
|
26
|
+
divisor == 1
|
|
27
|
+
? fraction
|
|
28
|
+
: sigmoidEase(
|
|
29
|
+
fraction,
|
|
30
|
+
divisor == 10 ? 12 : 50,
|
|
31
|
+
divisor == 10 ? 0.98 : 0.9995,
|
|
32
|
+
)
|
|
26
33
|
roller.style.transform = pos(digit + eased)
|
|
27
34
|
})
|
|
28
35
|
</script>
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { AudioEngine } from './audio/engine.svelte.js';
|
|
2
2
|
export { Track } from './audio/track.svelte.js';
|
|
3
3
|
export { default as FourTrack } from './components/FourTrack.svelte';
|
|
4
|
-
export type { AudioEngineConfig, HiddenTrackConfig, TrimFxConfig, ProjectMetadata, TrackMeta, PlayState, LoadStatus } from './types.js';
|
|
4
|
+
export type { AudioEngineConfig, HiddenTrackConfig, TrimFxConfig, ProjectMetadata, ProjectMeta, TrackMeta, PlayState, LoadStatus } from './types.js';
|
package/dist/types.d.ts
CHANGED
|
@@ -34,6 +34,14 @@ export interface ProjectMetadata {
|
|
|
34
34
|
bitDepth: number;
|
|
35
35
|
masterVolume: number;
|
|
36
36
|
tracks: TrackMeta[];
|
|
37
|
+
artist?: string;
|
|
38
|
+
title?: string;
|
|
39
|
+
comment?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface ProjectMeta {
|
|
42
|
+
artist: string;
|
|
43
|
+
title: string;
|
|
44
|
+
comment: string;
|
|
37
45
|
}
|
|
38
46
|
export type PlayState = 'stopped' | 'playing' | 'paused' | 'recording';
|
|
39
47
|
export type MicStatus = 'unsupported' | 'prompt' | 'denied' | 'no-device' | 'inactive' | 'active' | 'error';
|
package/package.json
CHANGED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
let { digit = 0 } = $props()
|
|
3
|
-
|
|
4
|
-
let roller: HTMLDivElement
|
|
5
|
-
let prev = -1
|
|
6
|
-
let wrapping = false
|
|
7
|
-
const step = 100 / 12
|
|
8
|
-
const pos = (d: number) => `translateY(${-step * (1 + d)}%)`
|
|
9
|
-
|
|
10
|
-
function jumpTo(transform: string) {
|
|
11
|
-
roller.style.transition = "none"
|
|
12
|
-
roller.style.transform = transform
|
|
13
|
-
roller.offsetHeight
|
|
14
|
-
roller.style.transition = ""
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
$effect(() => {
|
|
18
|
-
const d = +digit
|
|
19
|
-
if (!roller) return
|
|
20
|
-
|
|
21
|
-
if (prev === -1) {
|
|
22
|
-
jumpTo(pos(d))
|
|
23
|
-
} else if (prev === 9 && d === 0) {
|
|
24
|
-
wrapping = true
|
|
25
|
-
roller.style.transform = `translateY(${-step * 11}%)`
|
|
26
|
-
} else if (prev === 0 && d === 9) {
|
|
27
|
-
wrapping = true
|
|
28
|
-
roller.style.transform = `translateY(0%)`
|
|
29
|
-
} else {
|
|
30
|
-
wrapping = false
|
|
31
|
-
roller.style.transform = pos(d)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
prev = d
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
function onTransitionEnd() {
|
|
38
|
-
if (!wrapping) return
|
|
39
|
-
wrapping = false
|
|
40
|
-
jumpTo(pos(+digit))
|
|
41
|
-
}
|
|
42
|
-
</script>
|
|
43
|
-
|
|
44
|
-
<div class="digits">
|
|
45
|
-
<div class="roller" bind:this={roller} ontransitionend={onTransitionEnd}>
|
|
46
|
-
{#each [9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0] as n}
|
|
47
|
-
<div class="digit"><span>{n}</span></div>
|
|
48
|
-
{/each}
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
|
|
52
|
-
<style>
|
|
53
|
-
.digits {
|
|
54
|
-
overflow: hidden;
|
|
55
|
-
height: 20cqw;
|
|
56
|
-
width: 17cqw;
|
|
57
|
-
text-align: center;
|
|
58
|
-
color: #cfcdd3;
|
|
59
|
-
background: linear-gradient(to bottom, #474748, #000000, #545454);
|
|
60
|
-
border-right: 2px solid rgb(33, 33, 33);
|
|
61
|
-
font-family: sans-serif;
|
|
62
|
-
&:nth-child(1) {
|
|
63
|
-
transform: translateY(-1.5cqw);
|
|
64
|
-
}
|
|
65
|
-
&:nth-child(2) {
|
|
66
|
-
transform: translateY(-0.5cqw);
|
|
67
|
-
}
|
|
68
|
-
&:nth-child(3) {
|
|
69
|
-
transform: translateY(-2cqw);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
.roller {
|
|
73
|
-
transition: 0.4s ease transform;
|
|
74
|
-
}
|
|
75
|
-
.digit {
|
|
76
|
-
font-size: 35cqh;
|
|
77
|
-
/* letter-spacing: 7cqw; */
|
|
78
|
-
}
|
|
79
|
-
.span {
|
|
80
|
-
padding-left: 1cqw;
|
|
81
|
-
}
|
|
82
|
-
</style>
|