roxify 1.6.5 → 1.6.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 +550 -212
- package/dist/cli.js +54 -23
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/pack.d.ts +1 -0
- package/dist/pack.js +52 -3
- package/dist/utils/audio.d.ts +23 -0
- package/dist/utils/audio.js +98 -0
- package/dist/utils/decoder.js +114 -21
- package/dist/utils/ecc.d.ts +75 -0
- package/dist/utils/ecc.js +446 -0
- package/dist/utils/encoder.js +155 -49
- package/dist/utils/native.js +43 -26
- package/dist/utils/robust-audio.d.ts +54 -0
- package/dist/utils/robust-audio.js +400 -0
- package/dist/utils/robust-image.d.ts +54 -0
- package/dist/utils/robust-image.js +515 -0
- package/dist/utils/types.d.ts +26 -0
- package/dist/utils/zstd.d.ts +2 -2
- package/dist/utils/zstd.js +41 -18
- package/package.json +7 -6
- package/roxify_native-x86_64-unknown-linux-gnu.node +0 -0
- package/roxify_native.node +0 -0
- package/dist/_esmodule_test.d.ts +0 -1
- package/dist/_esmodule_test.js +0 -1
- package/dist/hybrid-compression.d.ts +0 -25
- package/dist/hybrid-compression.js +0 -90
- package/dist/minpng.d.ts +0 -20
- package/dist/minpng.js +0 -285
- package/dist/rox.exe +0 -0
- package/dist/roxify_native-x86_64-pc-windows-gnu.node +0 -0
- package/dist/roxify_native-x86_64-unknown-linux-gnu.node +0 -0
- package/dist/roxify_native.exe +0 -0
- package/dist/roxify_native.node +0 -0
- package/roxify_native-x86_64-pc-windows-msvc.node +0 -0
package/dist/utils/encoder.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { createCipheriv, pbkdf2Sync, randomBytes } from 'crypto';
|
|
2
2
|
import * as zlib from 'zlib';
|
|
3
3
|
import { unpackBuffer } from '../pack.js';
|
|
4
|
+
import { bytesToWav } from './audio.js';
|
|
4
5
|
import { COMPRESSION_MARKERS, ENC_AES, ENC_NONE, ENC_XOR, MAGIC, MARKER_END, MARKER_START, PIXEL_MAGIC, PIXEL_MAGIC_BLOCK, PNG_HEADER, } from './constants.js';
|
|
5
6
|
import { crc32 } from './crc.js';
|
|
6
7
|
import { colorsToBytes } from './helpers.js';
|
|
7
8
|
import { native } from './native.js';
|
|
9
|
+
import { encodeRobustAudio } from './robust-audio.js';
|
|
10
|
+
import { encodeRobustImage } from './robust-image.js';
|
|
8
11
|
import { parallelZstdCompress } from './zstd.js';
|
|
9
12
|
/**
|
|
10
13
|
* Encode a buffer or array of buffers into a PNG image (ROX format).
|
|
@@ -56,6 +59,105 @@ export async function encodeBinaryToPng(input, opts = {}) {
|
|
|
56
59
|
};
|
|
57
60
|
}
|
|
58
61
|
}
|
|
62
|
+
const compressionLevel = opts.compressionLevel ?? 19;
|
|
63
|
+
// ─── Lossy-resilient encoding fast path ────────────────────────────────────
|
|
64
|
+
// When lossyResilient is true, use QR-code-style block encoding with
|
|
65
|
+
// Reed-Solomon FEC. This produces output that survives lossy compression.
|
|
66
|
+
if (opts.lossyResilient) {
|
|
67
|
+
const inputBuf = Array.isArray(input) ? Buffer.concat(input) : input;
|
|
68
|
+
if (opts.onProgress)
|
|
69
|
+
opts.onProgress({ phase: 'compress_start', total: inputBuf.length });
|
|
70
|
+
if (opts.container === 'sound') {
|
|
71
|
+
// Robust audio encoding (multi-tone FSK + RS ECC)
|
|
72
|
+
const result = encodeRobustAudio(inputBuf, {
|
|
73
|
+
eccLevel: opts.eccLevel ?? 'medium',
|
|
74
|
+
});
|
|
75
|
+
if (opts.onProgress)
|
|
76
|
+
opts.onProgress({ phase: 'done' });
|
|
77
|
+
progressBar?.stop();
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Robust image encoding (QR-code-like blocks + RS ECC)
|
|
82
|
+
const result = encodeRobustImage(inputBuf, {
|
|
83
|
+
blockSize: opts.robustBlockSize ?? 4,
|
|
84
|
+
eccLevel: opts.eccLevel ?? 'medium',
|
|
85
|
+
});
|
|
86
|
+
if (opts.onProgress)
|
|
87
|
+
opts.onProgress({ phase: 'done' });
|
|
88
|
+
progressBar?.stop();
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// --- Native encoder fast path: let Rust handle compression/encryption/PNG ---
|
|
93
|
+
// This must be checked BEFORE TS compression to avoid double-compression.
|
|
94
|
+
if (typeof native.nativeEncodePngWithNameAndFilelist === 'function' &&
|
|
95
|
+
opts.includeFileList &&
|
|
96
|
+
opts.fileList) {
|
|
97
|
+
const fileName = opts.name || undefined;
|
|
98
|
+
const inputBuf = Array.isArray(input) ? Buffer.concat(input) : input;
|
|
99
|
+
let sizeMap = null;
|
|
100
|
+
try {
|
|
101
|
+
const unpack = unpackBuffer(inputBuf);
|
|
102
|
+
if (unpack) {
|
|
103
|
+
sizeMap = {};
|
|
104
|
+
for (const ef of unpack.files)
|
|
105
|
+
sizeMap[ef.path] = ef.buf.length;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (e) { }
|
|
109
|
+
const normalized = opts.fileList.map((f) => {
|
|
110
|
+
if (typeof f === 'string')
|
|
111
|
+
return { name: f, size: sizeMap && sizeMap[f] ? sizeMap[f] : 0 };
|
|
112
|
+
if (f && typeof f === 'object') {
|
|
113
|
+
if (f.name)
|
|
114
|
+
return { name: f.name, size: f.size ?? 0 };
|
|
115
|
+
if (f.path)
|
|
116
|
+
return { name: f.path, size: f.size ?? 0 };
|
|
117
|
+
}
|
|
118
|
+
return { name: String(f), size: 0 };
|
|
119
|
+
});
|
|
120
|
+
const fileListJson = JSON.stringify(normalized);
|
|
121
|
+
if (opts.onProgress)
|
|
122
|
+
opts.onProgress({ phase: 'compress_start', total: inputBuf.length });
|
|
123
|
+
// ── WAV container (--sound) via native Rust encoder ──
|
|
124
|
+
if (opts.container === 'sound') {
|
|
125
|
+
if (typeof native.nativeEncodeWavWithEncryptionNameAndFilelist === 'function' &&
|
|
126
|
+
opts.passphrase && opts.encrypt && opts.encrypt !== 'auto') {
|
|
127
|
+
const result = native.nativeEncodeWavWithEncryptionNameAndFilelist(inputBuf, compressionLevel, opts.passphrase, opts.encrypt, fileName, fileListJson);
|
|
128
|
+
if (opts.onProgress)
|
|
129
|
+
opts.onProgress({ phase: 'done' });
|
|
130
|
+
progressBar?.stop();
|
|
131
|
+
return Buffer.from(result);
|
|
132
|
+
}
|
|
133
|
+
else if (typeof native.nativeEncodeWavWithNameAndFilelist === 'function') {
|
|
134
|
+
const result = native.nativeEncodeWavWithNameAndFilelist(inputBuf, compressionLevel, fileName, fileListJson);
|
|
135
|
+
if (opts.onProgress)
|
|
136
|
+
opts.onProgress({ phase: 'done' });
|
|
137
|
+
progressBar?.stop();
|
|
138
|
+
return Buffer.from(result);
|
|
139
|
+
}
|
|
140
|
+
// fallthrough to TS WAV path below if native WAV not available
|
|
141
|
+
}
|
|
142
|
+
// ── PNG container (default) via native Rust encoder ──
|
|
143
|
+
if (opts.container !== 'sound') {
|
|
144
|
+
if (opts.passphrase && opts.encrypt && opts.encrypt !== 'auto') {
|
|
145
|
+
const result = native.nativeEncodePngWithEncryptionNameAndFilelist(inputBuf, compressionLevel, opts.passphrase, opts.encrypt, fileName, fileListJson);
|
|
146
|
+
if (opts.onProgress)
|
|
147
|
+
opts.onProgress({ phase: 'done' });
|
|
148
|
+
progressBar?.stop();
|
|
149
|
+
return Buffer.from(result);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
const result = native.nativeEncodePngWithNameAndFilelist(inputBuf, compressionLevel, fileName, fileListJson);
|
|
153
|
+
if (opts.onProgress)
|
|
154
|
+
opts.onProgress({ phase: 'done' });
|
|
155
|
+
progressBar?.stop();
|
|
156
|
+
return Buffer.from(result);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// --- TypeScript compression/encryption pipeline ---
|
|
59
161
|
let payloadInput;
|
|
60
162
|
let totalLen = 0;
|
|
61
163
|
if (Array.isArray(input)) {
|
|
@@ -68,7 +170,6 @@ export async function encodeBinaryToPng(input, opts = {}) {
|
|
|
68
170
|
}
|
|
69
171
|
if (opts.onProgress)
|
|
70
172
|
opts.onProgress({ phase: 'compress_start', total: totalLen });
|
|
71
|
-
const compressionLevel = opts.compressionLevel ?? 19;
|
|
72
173
|
let payload = await parallelZstdCompress(payloadInput, compressionLevel, (loaded, total) => {
|
|
73
174
|
if (opts.onProgress) {
|
|
74
175
|
opts.onProgress({
|
|
@@ -77,7 +178,7 @@ export async function encodeBinaryToPng(input, opts = {}) {
|
|
|
77
178
|
total,
|
|
78
179
|
});
|
|
79
180
|
}
|
|
80
|
-
});
|
|
181
|
+
}, opts.dict);
|
|
81
182
|
if (opts.onProgress)
|
|
82
183
|
opts.onProgress({ phase: 'compress_done', loaded: payload.length });
|
|
83
184
|
if (Array.isArray(input)) {
|
|
@@ -148,48 +249,6 @@ export async function encodeBinaryToPng(input, opts = {}) {
|
|
|
148
249
|
const payloadTotalLen = payload.reduce((a, b) => a + b.length, 0);
|
|
149
250
|
if (opts.onProgress)
|
|
150
251
|
opts.onProgress({ phase: 'meta_prep_done', loaded: payloadTotalLen });
|
|
151
|
-
if (typeof native.nativeEncodePngWithNameAndFilelist === 'function' &&
|
|
152
|
-
opts.includeFileList &&
|
|
153
|
-
opts.fileList) {
|
|
154
|
-
const fileName = opts.name || undefined;
|
|
155
|
-
let sizeMap = null;
|
|
156
|
-
if (!Array.isArray(input)) {
|
|
157
|
-
try {
|
|
158
|
-
const unpack = unpackBuffer(input);
|
|
159
|
-
if (unpack) {
|
|
160
|
-
sizeMap = {};
|
|
161
|
-
for (const ef of unpack.files)
|
|
162
|
-
sizeMap[ef.path] = ef.buf.length;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
catch (e) { }
|
|
166
|
-
}
|
|
167
|
-
const normalized = opts.fileList.map((f) => {
|
|
168
|
-
if (typeof f === 'string')
|
|
169
|
-
return { name: f, size: sizeMap && sizeMap[f] ? sizeMap[f] : 0 };
|
|
170
|
-
if (f && typeof f === 'object') {
|
|
171
|
-
if (f.name)
|
|
172
|
-
return { name: f.name, size: f.size ?? 0 };
|
|
173
|
-
if (f.path)
|
|
174
|
-
return { name: f.path, size: f.size ?? 0 };
|
|
175
|
-
}
|
|
176
|
-
return { name: String(f), size: 0 };
|
|
177
|
-
});
|
|
178
|
-
const fileListJson = JSON.stringify(normalized);
|
|
179
|
-
const flatPayload = Buffer.concat(payload);
|
|
180
|
-
if (opts.passphrase && opts.encrypt && opts.encrypt !== 'auto') {
|
|
181
|
-
const result = native.nativeEncodePngWithEncryptionNameAndFilelist(flatPayload, compressionLevel, opts.passphrase, opts.encrypt, fileName, fileListJson);
|
|
182
|
-
if (opts.onProgress)
|
|
183
|
-
opts.onProgress({ phase: 'done' });
|
|
184
|
-
return Buffer.from(result);
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
const result = native.nativeEncodePngWithNameAndFilelist(flatPayload, compressionLevel, fileName, fileListJson);
|
|
188
|
-
if (opts.onProgress)
|
|
189
|
-
opts.onProgress({ phase: 'done' });
|
|
190
|
-
return Buffer.from(result);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
252
|
const metaParts = [];
|
|
194
253
|
const includeName = opts.includeName === undefined ? true : !!opts.includeName;
|
|
195
254
|
if (includeName && opts.name) {
|
|
@@ -233,10 +292,57 @@ export async function encodeBinaryToPng(input, opts = {}) {
|
|
|
233
292
|
if (opts.output === 'rox') {
|
|
234
293
|
return Buffer.concat([MAGIC, ...meta]);
|
|
235
294
|
}
|
|
295
|
+
// ─── WAV container (TS fallback path) ──────────────────────────────────────
|
|
296
|
+
if (opts.container === 'sound') {
|
|
297
|
+
const nameBuf = opts.name ? Buffer.from(opts.name, 'utf8') : Buffer.alloc(0);
|
|
298
|
+
const nameLen = nameBuf.length;
|
|
299
|
+
const payloadLenBuf = Buffer.alloc(4);
|
|
300
|
+
payloadLenBuf.writeUInt32BE(payloadTotalLen, 0);
|
|
301
|
+
const version = 1;
|
|
302
|
+
let wavPayload = [
|
|
303
|
+
PIXEL_MAGIC,
|
|
304
|
+
Buffer.from([version]),
|
|
305
|
+
Buffer.from([nameLen]),
|
|
306
|
+
nameBuf,
|
|
307
|
+
payloadLenBuf,
|
|
308
|
+
...payload,
|
|
309
|
+
];
|
|
310
|
+
if (opts.includeFileList && opts.fileList) {
|
|
311
|
+
let sizeMapW = null;
|
|
312
|
+
if (!Array.isArray(input)) {
|
|
313
|
+
try {
|
|
314
|
+
const unpack = unpackBuffer(input);
|
|
315
|
+
if (unpack) {
|
|
316
|
+
sizeMapW = {};
|
|
317
|
+
for (const ef of unpack.files)
|
|
318
|
+
sizeMapW[ef.path] = ef.buf.length;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
catch (e) { }
|
|
322
|
+
}
|
|
323
|
+
const normalizedW = opts.fileList.map((f) => {
|
|
324
|
+
if (typeof f === 'string')
|
|
325
|
+
return { name: f, size: sizeMapW && sizeMapW[f] ? sizeMapW[f] : 0 };
|
|
326
|
+
if (f && typeof f === 'object') {
|
|
327
|
+
if (f.name)
|
|
328
|
+
return { name: f.name, size: f.size ?? 0 };
|
|
329
|
+
if (f.path)
|
|
330
|
+
return { name: f.path, size: f.size ?? 0 };
|
|
331
|
+
}
|
|
332
|
+
return { name: String(f), size: 0 };
|
|
333
|
+
});
|
|
334
|
+
const jsonBufW = Buffer.from(JSON.stringify(normalizedW), 'utf8');
|
|
335
|
+
const lenBufW = Buffer.alloc(4);
|
|
336
|
+
lenBufW.writeUInt32BE(jsonBufW.length, 0);
|
|
337
|
+
wavPayload = [...wavPayload, Buffer.from('rXFL', 'utf8'), lenBufW, jsonBufW];
|
|
338
|
+
}
|
|
339
|
+
const wavData = bytesToWav(Buffer.concat(wavPayload));
|
|
340
|
+
payload.length = 0;
|
|
341
|
+
progressBar?.stop();
|
|
342
|
+
return wavData;
|
|
343
|
+
}
|
|
236
344
|
{
|
|
237
|
-
const nameBuf = opts.name
|
|
238
|
-
? Buffer.from(opts.name, 'utf8')
|
|
239
|
-
: Buffer.alloc(0);
|
|
345
|
+
const nameBuf = opts.name ? Buffer.from(opts.name, 'utf8') : Buffer.alloc(0);
|
|
240
346
|
const nameLen = nameBuf.length;
|
|
241
347
|
const payloadLenBuf = Buffer.alloc(4);
|
|
242
348
|
payloadLenBuf.writeUInt32BE(payloadTotalLen, 0);
|
|
@@ -282,8 +388,8 @@ export async function encodeBinaryToPng(input, opts = {}) {
|
|
|
282
388
|
const dataWithoutMarkers = [pixelMagic, ...metaPixel];
|
|
283
389
|
const dataWithoutMarkersLen = dataWithoutMarkers.reduce((a, b) => a + b.length, 0);
|
|
284
390
|
const padding = (3 - (dataWithoutMarkersLen % 3)) % 3;
|
|
285
|
-
const paddedData = padding > 0
|
|
286
|
-
|
|
391
|
+
const paddedData = padding > 0 ?
|
|
392
|
+
[...dataWithoutMarkers, Buffer.alloc(padding)]
|
|
287
393
|
: dataWithoutMarkers;
|
|
288
394
|
const markerStartBytes = colorsToBytes(MARKER_START);
|
|
289
395
|
const compressionMarkerBytes = colorsToBytes(COMPRESSION_MARKERS.zstd);
|
package/dist/utils/native.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'fs';
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
3
|
import { arch, platform } from 'os';
|
|
4
|
-
import {
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
4
|
+
import { join, resolve } from 'path';
|
|
6
5
|
function getNativeModule() {
|
|
7
6
|
let moduleDir;
|
|
8
7
|
let nativeRequire;
|
|
@@ -11,18 +10,18 @@ function getNativeModule() {
|
|
|
11
10
|
nativeRequire = require;
|
|
12
11
|
}
|
|
13
12
|
else {
|
|
14
|
-
|
|
15
|
-
moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
moduleDir = process.cwd();
|
|
16
14
|
try {
|
|
17
15
|
nativeRequire = require;
|
|
18
16
|
}
|
|
19
17
|
catch {
|
|
20
|
-
nativeRequire = createRequire(
|
|
18
|
+
nativeRequire = createRequire(process.cwd() + '/package.json');
|
|
21
19
|
}
|
|
22
20
|
}
|
|
23
21
|
function getNativePath() {
|
|
24
22
|
const platformMap = {
|
|
25
23
|
linux: 'x86_64-unknown-linux-gnu',
|
|
24
|
+
darwin: arch() === 'arm64' ? 'aarch64-apple-darwin' : 'x86_64-apple-darwin',
|
|
26
25
|
win32: 'x86_64-pc-windows-gnu',
|
|
27
26
|
};
|
|
28
27
|
const platformAltMap = {
|
|
@@ -30,6 +29,7 @@ function getNativeModule() {
|
|
|
30
29
|
};
|
|
31
30
|
const extMap = {
|
|
32
31
|
linux: 'so',
|
|
32
|
+
darwin: 'dylib',
|
|
33
33
|
win32: 'node',
|
|
34
34
|
};
|
|
35
35
|
const currentPlatform = platform();
|
|
@@ -39,18 +39,12 @@ function getNativeModule() {
|
|
|
39
39
|
if (!target || !ext) {
|
|
40
40
|
throw new Error(`Unsupported platform: ${currentPlatform}`);
|
|
41
41
|
}
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
};
|
|
48
|
-
// Try multiple locations relative to the compiled JS file (dist),
|
|
49
|
-
// package root and common 'dist' directories used by packaging tools.
|
|
50
|
-
for (const t of targets) {
|
|
51
|
-
pushIf(resolve(moduleDir, `../roxify_native-${t}.node`), resolve(moduleDir, `../libroxify_native-${t}.node`), resolve(moduleDir, `../../roxify_native-${t}.node`), resolve(moduleDir, `../../libroxify_native-${t}.node`), resolve(moduleDir, `../dist/roxify_native-${t}.node`), resolve(moduleDir, `../../dist/roxify_native-${t}.node`));
|
|
52
|
-
}
|
|
53
|
-
pushIf(resolve(moduleDir, '../roxify_native.node'), resolve(moduleDir, '../libroxify_native.node'), resolve(moduleDir, '../../roxify_native.node'), resolve(moduleDir, '../../libroxify_native.node'), resolve(moduleDir, '../dist/roxify_native.node'), resolve(moduleDir, '../../dist/roxify_native.node'));
|
|
42
|
+
const prebuiltPath = join(moduleDir, '../../roxify_native.node');
|
|
43
|
+
const prebuiltLibPath = join(moduleDir, '../../libroxify_native.node');
|
|
44
|
+
const bundlePath = join(moduleDir, '../roxify_native.node');
|
|
45
|
+
const bundleLibPath = join(moduleDir, '../libroxify_native.node');
|
|
46
|
+
const bundlePathWithTarget = join(moduleDir, `../roxify_native-${target}.node`);
|
|
47
|
+
const bundleLibPathWithTarget = join(moduleDir, `../libroxify_native-${target}.node`);
|
|
54
48
|
let root = moduleDir && moduleDir !== '.' ? moduleDir : process.cwd();
|
|
55
49
|
while (root.length > 1 &&
|
|
56
50
|
!existsSync(resolve(root, 'package.json')) &&
|
|
@@ -60,13 +54,38 @@ function getNativeModule() {
|
|
|
60
54
|
break;
|
|
61
55
|
root = parent;
|
|
62
56
|
}
|
|
57
|
+
const bundleNode = resolve(moduleDir, '../roxify_native.node');
|
|
58
|
+
const bundleLibNode = resolve(moduleDir, '../libroxify_native.node');
|
|
59
|
+
const bundleNodeWithTarget = resolve(moduleDir, `../roxify_native-${target}.node`);
|
|
60
|
+
const bundleLibNodeWithTarget = resolve(moduleDir, `../libroxify_native-${target}.node`);
|
|
61
|
+
const repoNode = resolve(root, 'roxify_native.node');
|
|
62
|
+
const repoLibNode = resolve(root, 'libroxify_native.node');
|
|
63
|
+
const repoNodeWithTarget = resolve(root, `roxify_native-${target}.node`);
|
|
64
|
+
const repoLibNodeWithTarget = resolve(root, `libroxify_native-${target}.node`);
|
|
65
|
+
const targetNode = resolve(root, 'target/release/roxify_native.node');
|
|
66
|
+
const targetSo = resolve(root, 'target/release/roxify_native.so');
|
|
67
|
+
const targetLibSo = resolve(root, 'target/release/libroxify_native.so');
|
|
68
|
+
const nodeModulesNode = resolve(root, 'node_modules/roxify/roxify_native.node');
|
|
69
|
+
const nodeModulesNodeWithTarget = resolve(root, `node_modules/roxify/roxify_native-${target}.node`);
|
|
70
|
+
const prebuiltNode = resolve(moduleDir, '../../roxify_native.node');
|
|
71
|
+
const prebuiltLibNode = resolve(moduleDir, '../../libroxify_native.node');
|
|
72
|
+
const prebuiltNodeWithTarget = resolve(moduleDir, `../../roxify_native-${target}.node`);
|
|
73
|
+
const prebuiltLibNodeWithTarget = resolve(moduleDir, `../../libroxify_native-${target}.node`);
|
|
74
|
+
// Support multiple possible OS triples (e.g. windows-gnu and windows-msvc)
|
|
75
|
+
const targets = targetAlt ? [target, targetAlt] : [target];
|
|
76
|
+
const candidates = [];
|
|
63
77
|
for (const t of targets) {
|
|
64
|
-
|
|
78
|
+
const bundleNodeWithT = resolve(moduleDir, `../roxify_native-${t}.node`);
|
|
79
|
+
const bundleLibNodeWithT = resolve(moduleDir, `../libroxify_native-${t}.node`);
|
|
80
|
+
const repoNodeWithT = resolve(root, `roxify_native-${t}.node`);
|
|
81
|
+
const repoLibNodeWithT = resolve(root, `libroxify_native-${t}.node`);
|
|
82
|
+
const nodeModulesNodeWithT = resolve(root, `node_modules/roxify/roxify_native-${t}.node`);
|
|
83
|
+
const prebuiltNodeWithT = resolve(moduleDir, `../../roxify_native-${t}.node`);
|
|
84
|
+
const prebuiltLibNodeWithT = resolve(moduleDir, `../../libroxify_native-${t}.node`);
|
|
85
|
+
candidates.push(bundleLibNodeWithT, bundleNodeWithT, repoLibNodeWithT, repoNodeWithT, nodeModulesNodeWithT, prebuiltLibNodeWithT, prebuiltNodeWithT);
|
|
65
86
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const uniqueCandidates = [...new Set(candidates)];
|
|
69
|
-
for (const c of uniqueCandidates) {
|
|
87
|
+
candidates.push(bundleLibNode, bundleNode, repoLibNode, repoNode, targetNode, targetLibSo, targetSo, nodeModulesNode, prebuiltLibNode, prebuiltNode);
|
|
88
|
+
for (const c of candidates) {
|
|
70
89
|
try {
|
|
71
90
|
if (!existsSync(c))
|
|
72
91
|
continue;
|
|
@@ -84,11 +103,9 @@ function getNativeModule() {
|
|
|
84
103
|
}
|
|
85
104
|
return c;
|
|
86
105
|
}
|
|
87
|
-
catch
|
|
88
|
-
// ignore errors while checking candidates
|
|
89
|
-
}
|
|
106
|
+
catch { }
|
|
90
107
|
}
|
|
91
|
-
throw new Error(`Native module not found for ${currentPlatform}-${arch()}. Checked: ${
|
|
108
|
+
throw new Error(`Native module not found for ${currentPlatform}-${arch()}. Checked: ${candidates.join(' ')}`);
|
|
92
109
|
}
|
|
93
110
|
return nativeRequire(getNativePath());
|
|
94
111
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lossy-Resilient Audio Encoding (WAV container).
|
|
3
|
+
*
|
|
4
|
+
* Encodes binary data as multi-frequency tones (OFDM-like) that survive
|
|
5
|
+
* lossy audio compression (MP3, AAC, OGG Vorbis). The output sounds like
|
|
6
|
+
* a series of harmonious chords — not white noise.
|
|
7
|
+
*
|
|
8
|
+
* Architecture:
|
|
9
|
+
* 1. Data is protected with Reed-Solomon ECC (configurable level).
|
|
10
|
+
* 2. Each byte is transmitted as 8 simultaneous frequency channels
|
|
11
|
+
* (one bit per channel: tone present = 1, absent = 0).
|
|
12
|
+
* 3. Raised-cosine windowing prevents spectral splatter.
|
|
13
|
+
* 4. A chirp sync preamble enables frame alignment after lossy re-encoding.
|
|
14
|
+
*
|
|
15
|
+
* Frequency plan:
|
|
16
|
+
* 8 carriers at 600, 900, 1200, 1500, 1800, 2100, 2400, 2700 Hz.
|
|
17
|
+
* All within the 300–3400 Hz band preserved by most lossy codecs.
|
|
18
|
+
*
|
|
19
|
+
* Throughput: ~17 bytes/sec raw (with default symbol timing).
|
|
20
|
+
*/
|
|
21
|
+
import { EccLevel } from './ecc.js';
|
|
22
|
+
export interface RobustAudioEncodeOptions {
|
|
23
|
+
/** Error correction level. Default: 'medium'. */
|
|
24
|
+
eccLevel?: EccLevel;
|
|
25
|
+
}
|
|
26
|
+
export interface RobustAudioDecodeResult {
|
|
27
|
+
data: Buffer;
|
|
28
|
+
correctedErrors: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Encode binary data into a lossy-resilient WAV file.
|
|
32
|
+
*
|
|
33
|
+
* The output uses multi-frequency tones (not white noise) and includes
|
|
34
|
+
* Reed-Solomon error correction for recovery after MP3/AAC/OGG compression.
|
|
35
|
+
*
|
|
36
|
+
* @param data - Raw data to encode.
|
|
37
|
+
* @param opts - Encoding options.
|
|
38
|
+
* @returns WAV file as a Buffer.
|
|
39
|
+
*/
|
|
40
|
+
export declare function encodeRobustAudio(data: Buffer, opts?: RobustAudioEncodeOptions): Buffer;
|
|
41
|
+
/**
|
|
42
|
+
* Decode binary data from a lossy-resilient WAV file.
|
|
43
|
+
*
|
|
44
|
+
* Handles WAV files that have been re-encoded through lossy codecs.
|
|
45
|
+
*
|
|
46
|
+
* @param wav - WAV file buffer (16-bit PCM preferred, 8-bit also accepted).
|
|
47
|
+
* @returns Decoded data and error correction stats.
|
|
48
|
+
*/
|
|
49
|
+
export declare function decodeRobustAudio(wav: Buffer): RobustAudioDecodeResult;
|
|
50
|
+
/**
|
|
51
|
+
* Check if a buffer looks like a robust-audio-encoded WAV.
|
|
52
|
+
* Detects the sync preamble signature.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isRobustAudioWav(buf: Buffer): boolean;
|