@m4l-jweb/wrapper 0.9.9 → 1.0.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.
- package/README.md +41 -0
- package/package.json +1 -1
- package/src/core.ts +26 -4
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @m4l-jweb/wrapper
|
|
2
|
+
|
|
3
|
+
The Max-side glue: the ES5 `[js]` script that owns a device's lifecycle, its LiveAPI work, transport polling, clip I/O and file writes. You do not usually import this - `@m4l-jweb/build` compiles it into every device it produces.
|
|
4
|
+
|
|
5
|
+
Part of **[m4l-jweb](https://github.com/alienmind/m4l-jweb)** - build Ableton Live devices (`.amxd`) from a TypeScript repo: React UI, LiveAPI glue, CI builds, no Max editor.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @m4l-jweb/wrapper
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
// Only a device repo extending the wrapper needs this: wrapper/device.ts is
|
|
17
|
+
// compiled together with the packaged sources into one ES5 script.
|
|
18
|
+
//
|
|
19
|
+
// Everything here must be ES5 - Max's [js] is not a modern JavaScript engine, and the
|
|
20
|
+
// build proves it with acorn before packaging.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Notes
|
|
24
|
+
|
|
25
|
+
- `[js]` runs even inside a frozen device, and it is the only place LiveAPI exists - which is why the lifecycle lives here rather than in the browser.
|
|
26
|
+
- This package exists mainly so the build can find the wrapper sources (`@m4l-jweb/wrapper/sources`). It has no browser-facing API of its own.
|
|
27
|
+
- **`[node.script]` is never used.** It proved unstable in the field - silent non-start, then a full Live crash.
|
|
28
|
+
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
Ableton Live 12 with Max 9. Devices are built on `[jweb~]`, the browser view with signal outlets; older hosts are unverified.
|
|
32
|
+
|
|
33
|
+
## Links
|
|
34
|
+
|
|
35
|
+
- [Repository and full README](https://github.com/alienmind/m4l-jweb)
|
|
36
|
+
- [Architecture](https://github.com/alienmind/m4l-jweb/blob/main/doc/ARCHITECTURE.md)
|
|
37
|
+
- [What Max actually does: the measured facts](https://github.com/alienmind/m4l-jweb/blob/main/doc/MAX-FACTS.md)
|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
|
|
41
|
+
MIT
|
package/package.json
CHANGED
package/src/core.ts
CHANGED
|
@@ -690,6 +690,28 @@ function partPath(destPath: string): string {
|
|
|
690
690
|
return destPath + ".part";
|
|
691
691
|
}
|
|
692
692
|
|
|
693
|
+
/**
|
|
694
|
+
* Where a SAVE lands before it has earned its destination - and deliberately NOT
|
|
695
|
+
* `<dest>.part`.
|
|
696
|
+
*
|
|
697
|
+
* `[js]` cannot delete a file, so a spent `.part` is truncated to zero bytes and left.
|
|
698
|
+
* For a download that is harmless: the destination is derived from the URL, so the same
|
|
699
|
+
* sample re-fetched reuses (and overwrites) the same scratch file. A SAVE has no such
|
|
700
|
+
* guarantee - an audio export names its file after the moment it was rendered, so every
|
|
701
|
+
* bounce would strand another 0-byte `superdough-export-<n>.wav.part` next to the real
|
|
702
|
+
* one, forever.
|
|
703
|
+
*
|
|
704
|
+
* One fixed scratch name per device folder instead: reused, overwritten, and never more
|
|
705
|
+
* than a single stray zero-byte file however many exports are made. Safe because saves
|
|
706
|
+
* are strictly one at a time (`activeSave`), and separate from the fetch scratch so a
|
|
707
|
+
* download in flight cannot collide with a save.
|
|
708
|
+
*/
|
|
709
|
+
function savePartPath(destPath: string): string {
|
|
710
|
+
var cut = destPath.lastIndexOf("/");
|
|
711
|
+
var dir = cut < 0 ? "" : destPath.slice(0, cut + 1);
|
|
712
|
+
return dir + "m4l-jweb-save.part";
|
|
713
|
+
}
|
|
714
|
+
|
|
693
715
|
interface ActiveFetch {
|
|
694
716
|
requestId: string;
|
|
695
717
|
url: string;
|
|
@@ -786,7 +808,7 @@ function save_begin(requestId: string, destPath: string, byteCount: number): voi
|
|
|
786
808
|
if (activeSave && activeSave.file && activeSave.file.isopen) activeSave.file.close();
|
|
787
809
|
|
|
788
810
|
var resolved = resolveFetchPath(destPath);
|
|
789
|
-
var target =
|
|
811
|
+
var target = savePartPath(resolved);
|
|
790
812
|
var f: File | null = null;
|
|
791
813
|
try {
|
|
792
814
|
f = new File(target, "write");
|
|
@@ -820,7 +842,7 @@ function save_end(requestId: string): void {
|
|
|
820
842
|
var save = activeSave;
|
|
821
843
|
if (save.file && save.file.isopen) save.file.close();
|
|
822
844
|
|
|
823
|
-
var onDisk = fileSize(
|
|
845
|
+
var onDisk = fileSize(savePartPath(save.destPath));
|
|
824
846
|
if (onDisk !== save.expect) {
|
|
825
847
|
outlet(0, "save_error", requestId, "size mismatch: wrote " + onDisk + " bytes, expected " + save.expect);
|
|
826
848
|
activeSave = null;
|
|
@@ -830,7 +852,7 @@ function save_end(requestId: string): void {
|
|
|
830
852
|
// destPath must be a FLAT filename in the device folder - maxurl (libcurl) and Max's
|
|
831
853
|
// [js] File resolve a subdirectory differently, so `sub/x.wav` writes the .part where
|
|
832
854
|
// File agrees but maxurl cannot reach it, and the place returns -1. Keep saves flat.
|
|
833
|
-
var placeUrl = encodeURI("file:///" +
|
|
855
|
+
var placeUrl = encodeURI("file:///" + savePartPath(save.destPath));
|
|
834
856
|
post("m4l-jweb: save place " + placeUrl + " -> " + save.destPath + "\n");
|
|
835
857
|
var reqDict = new Dict();
|
|
836
858
|
reqDict.set("url", placeUrl);
|
|
@@ -851,7 +873,7 @@ function finishSavePlace(): void {
|
|
|
851
873
|
activeSave = null;
|
|
852
874
|
return;
|
|
853
875
|
}
|
|
854
|
-
truncate(
|
|
876
|
+
truncate(savePartPath(save.destPath)); // [js] cannot delete; zero it, and reuse it
|
|
855
877
|
post("m4l-jweb: saved " + placed + " bytes to " + save.destPath + "\n");
|
|
856
878
|
outlet(0, "save_done", save.requestId, placed);
|
|
857
879
|
activeSave = null;
|