carta1 1.5.0 → 1.6.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 +13 -6
- package/bin/cli.js +17 -180
- package/bin/options.js +41 -0
- package/codec/boundary/inspect.js +96 -0
- package/codec/boundary/pcm.js +86 -0
- package/codec/boundary/stream.js +48 -54
- package/dist/carta1-worker.min.js +1 -1
- package/dist/carta1-worker.min.js.map +1 -1
- package/dist/carta1.min.js +1 -1
- package/dist/carta1.min.js.map +1 -1
- package/dist/carta1.wme +0 -0
- package/docs/native.md +107 -0
- package/docs/porffor.md +23 -0
- package/package.json +13 -5
- package/bin/build-wme.js +0 -69
package/dist/carta1.wme
CHANGED
|
Binary file
|
package/docs/native.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Native Carta1 CLI
|
|
2
|
+
|
|
3
|
+
Carta1's native CLI compiles the JavaScript codec to machine code with Porffor.
|
|
4
|
+
The executable uses the OS C runtime and does not require Node.js. JavaScript
|
|
5
|
+
packages and browser bundles continue to ship separately.
|
|
6
|
+
|
|
7
|
+
## Download and run
|
|
8
|
+
|
|
9
|
+
GitHub Releases include `carta1-VERSION-OS-ARCH.tar.gz` and a matching `.sha256`
|
|
10
|
+
file. Targets are `linux-x64`, `linux-arm64`, `darwin-x64`, and `darwin-arm64`.
|
|
11
|
+
Linux builds target Ubuntu 24.04/glibc; macOS ARM64 builds target macOS 14 and
|
|
12
|
+
Intel builds target macOS 15. Native Windows and musl builds are not included.
|
|
13
|
+
|
|
14
|
+
Extract the archive and run its `carta1` executable:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
./carta1 --encode input.wav output.aea
|
|
18
|
+
./carta1 --decode input.aea output.wav
|
|
19
|
+
./carta1 --json input.aea structure.json
|
|
20
|
+
./carta1 --help
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The native CLI accepts the Node CLI's encode/decode/JSON operations and its
|
|
24
|
+
quiet, force, UTF-8 title, allocation bias, and fixed block mode options.
|
|
25
|
+
WAV input is mono/stereo signed little-endian 16/24/32-bit integer PCM.
|
|
26
|
+
Decoding writes PCM16 WAV. Input is normally 44.1 kHz; other rates trigger a
|
|
27
|
+
warning and are not resampled. JSON inspection escapes non-ASCII characters
|
|
28
|
+
as Unicode escapes, preserving the same parsed data as Node.
|
|
29
|
+
|
|
30
|
+
The native adapter processes complete files in memory and prints a completion
|
|
31
|
+
line instead of an interactive progress bar. The Node CLI remains available
|
|
32
|
+
for stream-based input processing. Existing output files require `--force`.
|
|
33
|
+
The archive contains the executable, build metadata, this guide, Carta1's ISC
|
|
34
|
+
license, and the Porffor runtime's MIT license.
|
|
35
|
+
|
|
36
|
+
## Build and verify
|
|
37
|
+
|
|
38
|
+
From the repository checkout, with Node.js and a system C compiler installed:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
npm ci
|
|
42
|
+
node build/install-porffor.js /tmp/carta1-toolchain
|
|
43
|
+
export PORFFOR=/tmp/carta1-toolchain/porf
|
|
44
|
+
npm run build:native
|
|
45
|
+
npm run probe:porffor
|
|
46
|
+
npm run test:native
|
|
47
|
+
npm run package:native
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The installer pins Porffor alpha 19 (`0c135ce`) and verifies the downloaded
|
|
51
|
+
archive against a checked-in SHA-256. It does not change shell configuration.
|
|
52
|
+
`PORFFOR` can instead select an already installed copy of that exact version.
|
|
53
|
+
`CC` may select a C compiler executable without arguments.
|
|
54
|
+
|
|
55
|
+
`build:native` writes `dist/native/carta1` and `build-info.json`. Its optional
|
|
56
|
+
argument selects another binary output path. Compilation uses a temporary
|
|
57
|
+
source tree and hardcodes the package version, so the executable needs no
|
|
58
|
+
adjacent JavaScript files or `package.json`. C compilation always uses
|
|
59
|
+
`-fno-fast-math -ffp-contract=off`: default floating-point contraction changed
|
|
60
|
+
decoded sample bits in the initial experiment. Generated native files are
|
|
61
|
+
excluded from the npm package.
|
|
62
|
+
|
|
63
|
+
`probe:porffor` checks exact encoded bytes and float32 reconstruction bits for
|
|
64
|
+
288 channel frames, public package startup with the native host, and native
|
|
65
|
+
CLI help. `test:native` runs 58 WAV/AEA cases against the actual Node CLI,
|
|
66
|
+
including independent stereo state, 16/24/32-bit input, partial/empty files,
|
|
67
|
+
block modes and bias, UTF-8 titles and paths, JSON data, overwrite behavior,
|
|
68
|
+
invalid input, and a 20-second stereo case that exercises garbage collection.
|
|
69
|
+
Encoded AEA and decoded WAV files must match byte for byte.
|
|
70
|
+
|
|
71
|
+
To include external corpus files and retain hash evidence:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
CARTA1_NATIVE_CORPUS=/absolute/castanets.wav:/absolute/music.wav \
|
|
75
|
+
CARTA1_KEEP_NATIVE_EVIDENCE=1 npm run test:native
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Use the root harness with a native CLI adapter and `--jobs 1` for final ODG
|
|
79
|
+
comparisons. Native release jobs also run the frame and file parity gates on
|
|
80
|
+
each target machine before uploading archives.
|
|
81
|
+
|
|
82
|
+
## Host implementation
|
|
83
|
+
|
|
84
|
+
`native/host.js` supplies argument decoding, bounded binary reads, exclusive
|
|
85
|
+
output creation, the UTF-8 operations needed by AEA titles, and the byte Blob
|
|
86
|
+
operations used by the existing PCM facade. Its small C bridge relies on the
|
|
87
|
+
pinned Porffor runtime's ArrayBuffer layout. The native CLI imports
|
|
88
|
+
`codec/index.js` and reuses the existing container, serialization, timeline,
|
|
89
|
+
and PCM conversions. WAV sample conversion and JSON inspection are shared
|
|
90
|
+
with the Node CLI; the codec algorithm is unchanged.
|
|
91
|
+
|
|
92
|
+
Complete-file helpers use synchronous frame operations on the same stateful
|
|
93
|
+
stream classes used by async callers. This avoids a Porffor alpha 19
|
|
94
|
+
async-generator queue crash on longer audio without disabling garbage
|
|
95
|
+
collection or duplicating priming, tail flushing, or source-length trimming.
|
|
96
|
+
Their public Promise-returning API is unchanged. The host's UTF-8 and Blob
|
|
97
|
+
classes implement the surface needed by Carta1, rather than a general browser
|
|
98
|
+
or Node compatibility layer.
|
|
99
|
+
|
|
100
|
+
## GitHub release gate
|
|
101
|
+
|
|
102
|
+
The reusable Native binaries workflow builds and tests all four targets on
|
|
103
|
+
native GitHub runners. It runs in CI and is required by the tag-triggered
|
|
104
|
+
publishing workflow. A failed native parity check prevents publishing.
|
|
105
|
+
Successful jobs package versioned archives, build metadata, licenses, and
|
|
106
|
+
SHA-256 checksums; the publishing job attaches them alongside browser/WME
|
|
107
|
+
assets. The existing version-bump workflow still controls versioning and tags.
|
package/docs/porffor.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Porffor investigation
|
|
2
|
+
|
|
3
|
+
Carta1 can compile to machine code with Porffor alpha 19. The initial
|
|
4
|
+
unadapted CLI failed at Node filesystem imports, and the public package failed
|
|
5
|
+
at startup without TextEncoder. A native host now supplies the required
|
|
6
|
+
operations; see [native builds, verification and release assets](native.md).
|
|
7
|
+
|
|
8
|
+
The original frame-only test on macOS ARM64 used Carta1 1.5.0, Node 24.11.1,
|
|
9
|
+
Apple Clang 16.0.0, and Porffor `alpha 19 (0c135ce 2026-10-21)`
|
|
10
|
+
([compiler source](https://github.com/CanadaHonk/porffor/tree/0c135ce2f36fdd3467d5a58daa92283810705a51)).
|
|
11
|
+
All 288 channel frames encoded identically under default C flags, but 8,708
|
|
12
|
+
decoded float32 samples differed, with maximum absolute error about `1.19e-7`.
|
|
13
|
+
Disabling C floating-point contraction restored exact sample-bit parity.
|
|
14
|
+
The matched frame output SHA-256 was
|
|
15
|
+
`413f72d3f2e1c7735f831e7c637b49d7757b3f76b6ff8df16fb8ee36a572a57d`.
|
|
16
|
+
|
|
17
|
+
The repeatable `npm run probe:porffor` command now includes native host
|
|
18
|
+
initialization and CLI help, and succeeds when those checks and strict frame
|
|
19
|
+
parity pass. It keeps its binaries, complete output records, build logs and
|
|
20
|
+
`report.json` in the printed temporary directory. It does not download a
|
|
21
|
+
compiler or produce release assets. `npm run test:native` supplies the actual
|
|
22
|
+
WAV/AEA release gate, including long-file coverage for the compiler runtime's
|
|
23
|
+
async-generator limitation.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carta1",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "An ATRAC1 audio codec implementation in JavaScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "codec/index.js",
|
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"bin/",
|
|
12
12
|
"codec/",
|
|
13
|
-
"dist
|
|
13
|
+
"dist/*.js",
|
|
14
|
+
"dist/*.map",
|
|
15
|
+
"dist/*.wme",
|
|
16
|
+
"dist/wme/",
|
|
17
|
+
"docs/",
|
|
14
18
|
"LICENSE",
|
|
15
19
|
"README.md"
|
|
16
20
|
],
|
|
@@ -18,10 +22,14 @@
|
|
|
18
22
|
"start": "node bin/cli.js",
|
|
19
23
|
"test": "vitest run",
|
|
20
24
|
"test:watch": "vitest",
|
|
25
|
+
"probe:porffor": "node build/porffor.js",
|
|
26
|
+
"build:native": "node build/native.js",
|
|
27
|
+
"test:native": "node --test tests/native-cli.integration.mjs",
|
|
28
|
+
"package:native": "node build/package-native.js",
|
|
21
29
|
"prepare": "husky",
|
|
22
|
-
"lint": "eslint bin/ codec/ tests/",
|
|
30
|
+
"lint": "eslint bin/ build/ codec/ native/ tests/",
|
|
23
31
|
"format": "prettier --write .",
|
|
24
|
-
"build": "webpack --config webpack.config.js && node
|
|
32
|
+
"build": "webpack --config webpack.config.js && node build/wme.js",
|
|
25
33
|
"check": "npm run lint && npm test && npm run build && npm run test:wme",
|
|
26
34
|
"prepublishOnly": "npm run check",
|
|
27
35
|
"test:wme": "node --test tests/wme-build.integration.mjs"
|
|
@@ -70,7 +78,7 @@
|
|
|
70
78
|
"webpack-cli": "^6.0.1"
|
|
71
79
|
},
|
|
72
80
|
"lint-staged": {
|
|
73
|
-
"(bin|codec|tests)/**/*.js": [
|
|
81
|
+
"(bin|build|codec|tests)/**/*.js": [
|
|
74
82
|
"prettier --write",
|
|
75
83
|
"eslint --fix"
|
|
76
84
|
]
|
package/bin/build-wme.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/** Package the bundled API v1 module and existing worker as a WME SAR archive.
|
|
2
|
-
* Wire format: asivery/webminidisc src/services/audio/apiv1/external-archive.ts
|
|
3
|
-
* (9637bed5), with big-endian lengths and metadata.json as the first entry.
|
|
4
|
-
*/
|
|
5
|
-
import { readFile, writeFile } from 'node:fs/promises'
|
|
6
|
-
import { fileURLToPath } from 'node:url'
|
|
7
|
-
|
|
8
|
-
// The host imports blobs; relative source-map URLs cannot resolve there.
|
|
9
|
-
function bundledScript(contents) {
|
|
10
|
-
return Buffer.from(
|
|
11
|
-
Buffer.from(contents)
|
|
12
|
-
.toString('utf8')
|
|
13
|
-
.replace(/^\/\/# sourceMappingURL=.*$/gm, '')
|
|
14
|
-
)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function createWmeArchive(metadata, index, worker) {
|
|
18
|
-
const parts = [Buffer.from('SARFile')]
|
|
19
|
-
const subMagic = Buffer.from('Web MiniDisc Dynamic Encoder')
|
|
20
|
-
const length = Buffer.alloc(2)
|
|
21
|
-
length.writeUInt16BE(subMagic.length)
|
|
22
|
-
parts.push(length, subMagic)
|
|
23
|
-
for (const [name, contents] of [
|
|
24
|
-
['metadata.json', Buffer.from(JSON.stringify(metadata))],
|
|
25
|
-
['index.js', bundledScript(index)],
|
|
26
|
-
['worker.js', bundledScript(worker)],
|
|
27
|
-
]) {
|
|
28
|
-
const filename = Buffer.from(name)
|
|
29
|
-
const nameLength = Buffer.alloc(2)
|
|
30
|
-
nameLength.writeUInt16BE(filename.length)
|
|
31
|
-
const size = Buffer.alloc(4)
|
|
32
|
-
size.writeUInt32BE(contents.length)
|
|
33
|
-
parts.push(nameLength, filename, size, contents)
|
|
34
|
-
}
|
|
35
|
-
const archive = Buffer.concat(parts)
|
|
36
|
-
if (archive.length > 50 * 1024 * 1024) {
|
|
37
|
-
throw new RangeError('WME archive exceeds the host 50 MiB limit')
|
|
38
|
-
}
|
|
39
|
-
return archive
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function main() {
|
|
43
|
-
const root = new URL('../', import.meta.url)
|
|
44
|
-
const pkg = JSON.parse(await readFile(new URL('package.json', root), 'utf8'))
|
|
45
|
-
const [index, worker] = await Promise.all([
|
|
46
|
-
readFile(new URL('dist/wme/index.js', root)),
|
|
47
|
-
readFile(new URL(`dist/${pkg.name}-worker.min.js`, root)),
|
|
48
|
-
])
|
|
49
|
-
const archive = createWmeArchive(
|
|
50
|
-
{
|
|
51
|
-
encoderId: pkg.name,
|
|
52
|
-
userFriendlyName: 'Carta1 (ATRAC1, experimental)',
|
|
53
|
-
description:
|
|
54
|
-
'ATRAC1 codec for standard MD SP stereo/mono. Experimental: requires host support for software ATRAC1; current Web MiniDisc API v1 is unsupported.',
|
|
55
|
-
version: pkg.version,
|
|
56
|
-
encoderDependencies: [],
|
|
57
|
-
apiVersion: '1',
|
|
58
|
-
},
|
|
59
|
-
index,
|
|
60
|
-
worker
|
|
61
|
-
)
|
|
62
|
-
const output = new URL(`dist/${pkg.name}.wme`, root)
|
|
63
|
-
await writeFile(output, archive)
|
|
64
|
-
console.log(`Built ${fileURLToPath(output)} (${archive.length} bytes)`)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
68
|
-
await main()
|
|
69
|
-
}
|