winamp-eqf 0.0.0-next-c2c0675

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 ADDED
@@ -0,0 +1,90 @@
1
+ # Winamp Equalizer Preset Parser
2
+
3
+ Winamp allows you to save your equalizer settings to `.eqf` file. This package allows you to parse these files.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install --save winamp-eqf
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { parser, creator, EqfData, CreateEqfData } from 'winamp-eqf';
15
+
16
+ // ... Get your .eqf or .q1 file as an ArrayBuffer
17
+ const eqf: EqfData = parser(eqfArrayBuffer);
18
+
19
+ const eqfArrayBuffer: ArrayBuffer = creator(eqf);
20
+ ```
21
+
22
+ This package is an ES module and requires Node.js 14+ or a modern bundler that supports ES modules.
23
+
24
+ ## API
25
+
26
+ This package is written in TypeScript and provides full type definitions.
27
+
28
+ ### `parser(ArrayBuffer): EqfData`
29
+
30
+ #### Return value
31
+
32
+ ```JavaScript
33
+ {
34
+ "presets": [
35
+ {
36
+ "name": "Entry1",
37
+ "preamp": 33, // 1-64
38
+ "hz60": 64, // 1-64
39
+ "hz170": 64, // ...
40
+ "hz310": 64,
41
+ "hz600": 64,
42
+ "hz1000": 64,
43
+ "hz3000": 64,
44
+ "hz6000": 64,
45
+ "hz12000": 64,
46
+ "hz14000": 64,
47
+ "hz16000": 64,
48
+ },
49
+ // Some files, such as winamp.q1, may contain multiple preset objects.
50
+ ],
51
+ "type": "Winamp EQ library file v1.1",
52
+ }
53
+ ```
54
+
55
+ ### `creator(eqfObject: CreateEqfData): ArrayBuffer`
56
+
57
+ #### Return Value: `ArrayBuffer`
58
+
59
+ `eqfObject` is an object with the same shape as that returned by `parser()`.
60
+
61
+
62
+
63
+ ## Source Material
64
+
65
+ Starting with this spec found here: <http://www.perlmonks.org/bare/?node_id=584875>:
66
+
67
+ > I've taken a look at some EQF files that I made for the purpose. The format is apparently very simple:
68
+ > The file is 299 bytes long.
69
+ >
70
+ > It starts with a text header, which in my case, is 37 bytes long. It is, in double-quotish notation — note the control-Z character:
71
+ >
72
+ > Winamp EQ library file v1.1\cZ!--Entry1
73
+ >
74
+ > Next is a block of null bytes ("\0") up till the next, final part.
75
+ > The real data is stored in the last 11 bytes of the file: the last byte is for the general volume, the 10 bytes before that are for each of the 10 EQ controls, in ascending order: the first of these 10 for the deepest bass, the last one (right in front of the volume byte) is for the highest treble.
76
+ > The values are 0x20 in neutral position, and are reversed in value: 0x00 is maximum, 0x3F is minimum. So there are 31 positions below, and 31 32 levels above neutral.
77
+
78
+ Additionally, I got some info from [Darren Owen](https://twitter.com/The_DoctorO) via Twitter:
79
+
80
+ <https://twitter.com/The_DoctorO/status/856223002530373632>
81
+
82
+ > Not that i'm aware off as sadly documentation of things was never great. Looking at the link vs files in a hex editor it seems mostly right.
83
+
84
+ > The current 1.1 format should be fine as I don't believe the format has changed for a very long time :)
85
+
86
+ And then via direct message:
87
+
88
+ > Will do it here as I can type a bit more, but the only obvious thing wrong with the link is the signature assumption as it's not guaranteed to be 'entry1' As you can have multiple eq blocks in a file.
89
+
90
+ > If you've looked at winamp.q1 you should see multiple presets in that file which follow one after each other so the file signature (winamp.q1 or a specific *.eqf file) is "Winamp EQ library file v1.1\x1A!--" (pulled that out from the disassembler) it's then a 257 byte buffer (256 + null character to terminate correctly) then the 10 byte block relating to the eq sliders (need to double-check the range base) followed by the 1 byte for the preamp slider then if there's more presets in the file, they follow on immediately after with the name block looking at the preamp slider, -12dB = 0x3F, 0dB = 0x1F, 12dB = 0 (so a 0-63 range) that seems to be the same for the other sliders (and matches 1:1 with the sdk details) and I think that's it :) in the winamp.q1 file, the 'default' entry is either a flat preset or what's been saved after customisation (in-case you're wanting to mirror the native behaviour via the preset -> save -> default action)
@@ -0,0 +1,4 @@
1
+ export declare const PRESET_VALUES: readonly ["hz60", "hz170", "hz310", "hz600", "hz1000", "hz3000", "hz6000", "hz12000", "hz14000", "hz16000", "preamp"];
2
+ export declare const HEADER = "Winamp EQ library file v1.1";
3
+ export type PresetValueKey = (typeof PRESET_VALUES)[number];
4
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,uHAYhB,CAAC;AAEX,eAAO,MAAM,MAAM,gCAAgC,CAAC;AAEpD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ export const PRESET_VALUES = [
2
+ "hz60",
3
+ "hz170",
4
+ "hz310",
5
+ "hz600",
6
+ "hz1000",
7
+ "hz3000",
8
+ "hz6000",
9
+ "hz12000",
10
+ "hz14000",
11
+ "hz16000",
12
+ "preamp",
13
+ ];
14
+ export const HEADER = "Winamp EQ library file v1.1";
@@ -0,0 +1,3 @@
1
+ import { CreateEqfData } from "./types.js";
2
+ export declare function creator(data: CreateEqfData): ArrayBuffer;
3
+ //# sourceMappingURL=creator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"creator.d.ts","sourceRoot":"","sources":["../src/creator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAI3C,wBAAgB,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,WAAW,CAqCxD"}
@@ -0,0 +1,34 @@
1
+ import { HEADER, PRESET_VALUES } from "./constants.js";
2
+ const PRESET_LENGTH = 257;
3
+ export function creator(data) {
4
+ const buffer = [];
5
+ // Add header
6
+ for (let i = 0; i < HEADER.length; i++) {
7
+ buffer.push(HEADER.charCodeAt(i));
8
+ }
9
+ // Add control character and ending
10
+ buffer.push(26); // <ctrl-z>
11
+ const ending = "!--";
12
+ for (let i = 0; i < ending.length; i++) {
13
+ buffer.push(ending.charCodeAt(i));
14
+ }
15
+ if (!data.presets) {
16
+ throw new Error("Eqf data is missing presets");
17
+ }
18
+ data.presets.forEach((preset) => {
19
+ // Add preset name
20
+ let k = 0;
21
+ for (; k < preset.name.length; k++) {
22
+ buffer.push(preset.name.charCodeAt(k));
23
+ }
24
+ // Pad name to fixed length
25
+ for (; k < PRESET_LENGTH; k++) {
26
+ buffer.push(0);
27
+ }
28
+ // Add preset values
29
+ PRESET_VALUES.forEach((valueName) => {
30
+ buffer.push(64 - preset[valueName]); // Adjust for inverse values
31
+ });
32
+ });
33
+ return new Uint8Array(buffer).buffer;
34
+ }
@@ -0,0 +1,5 @@
1
+ export { parser } from "./parser.js";
2
+ export { creator } from "./creator.js";
3
+ export * from "./types.js";
4
+ export * from "./constants.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC"}
package/built/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { parser } from "./parser.js";
2
+ export { creator } from "./creator.js";
3
+ export * from "./types.js";
4
+ export * from "./constants.js";
@@ -0,0 +1,3 @@
1
+ import { EqfData } from "./types.js";
2
+ export declare function parser(arrayBuffer: ArrayBuffer): EqfData;
3
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAa,MAAM,YAAY,CAAC;AAEhD,wBAAgB,MAAM,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CA+CxD"}
@@ -0,0 +1,36 @@
1
+ import { HEADER, PRESET_VALUES } from "./constants.js";
2
+ export function parser(arrayBuffer) {
3
+ const data = {
4
+ type: "",
5
+ presets: [],
6
+ };
7
+ let i = 0;
8
+ const arr = new Int8Array(arrayBuffer);
9
+ // Parse header
10
+ data.type = String.fromCharCode.apply(null, Array.from(arr.slice(i, HEADER.length)));
11
+ if (data.type !== HEADER) {
12
+ throw new Error("Invalid .eqf file.");
13
+ }
14
+ i += HEADER.length;
15
+ // Skip "<ctrl-z>!--"
16
+ i += 4;
17
+ // Get the presets
18
+ while (i < arr.length) {
19
+ const preset = {};
20
+ // Get the name
21
+ const nameStart = i;
22
+ const nameEnd = nameStart + 257; // Str is fixed length
23
+ // Str is null terminated
24
+ while (arr[i] !== 0 && i <= nameEnd) {
25
+ i++;
26
+ }
27
+ preset.name = String.fromCharCode.apply(null, Array.from(arr.slice(nameStart, i)));
28
+ i = nameEnd; // Skip over any unused bytes
29
+ // Get the levels
30
+ PRESET_VALUES.forEach((valueName) => {
31
+ preset[valueName] = 64 - arr[i++]; // Adjust for inverse values
32
+ });
33
+ data.presets.push(preset);
34
+ }
35
+ return data;
36
+ }
@@ -0,0 +1,27 @@
1
+ import { PresetValueKey } from "./constants.js";
2
+ export interface EqfPreset {
3
+ name: string;
4
+ hz60: number;
5
+ hz170: number;
6
+ hz310: number;
7
+ hz600: number;
8
+ hz1000: number;
9
+ hz3000: number;
10
+ hz6000: number;
11
+ hz12000: number;
12
+ hz14000: number;
13
+ hz16000: number;
14
+ preamp: number;
15
+ }
16
+ export interface EqfData {
17
+ type: string;
18
+ presets: EqfPreset[];
19
+ }
20
+ export interface CreateEqfData {
21
+ presets: Array<{
22
+ [K in PresetValueKey]: number;
23
+ } & {
24
+ name: string;
25
+ }>;
26
+ }
27
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,KAAK,CACZ;SACG,CAAC,IAAI,cAAc,GAAG,MAAM;KAC9B,GAAG;QACF,IAAI,EAAE,MAAM,CAAC;KACd,CACF,CAAC;CACH"}
package/built/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "winamp-eqf",
3
+ "version": "0.0.0-next-c2c0675",
4
+ "description": "Parse and create Winamp .EQF files which describe equalizer settings",
5
+ "type": "module",
6
+ "main": "built/index.js",
7
+ "types": "built/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./built/index.js",
11
+ "types": "./built/index.d.ts"
12
+ }
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/captbaritone/webamp.git",
17
+ "directory": "packages/winamp-eqf"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/captbaritone/webamp/issues"
21
+ },
22
+ "homepage": "https://github.com/captbaritone/webamp/tree/master/packages/winamp-eqf",
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "prepublishOnly": "npm run build",
26
+ "test": "jest",
27
+ "type-check": "tsc --noEmit"
28
+ },
29
+ "files": [
30
+ "built/**/*",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "keywords": [
35
+ "winamp",
36
+ "equalizer",
37
+ "parse",
38
+ "create"
39
+ ],
40
+ "author": "Jordan Eldredge",
41
+ "license": "ISC",
42
+ "engines": {
43
+ "node": ">=14.0.0"
44
+ },
45
+ "devDependencies": {
46
+ "@swc/jest": "^0.2.24",
47
+ "@types/jest": "^30.0.0",
48
+ "@types/node": "^24.0.10",
49
+ "buffer-to-arraybuffer": "0.0.4",
50
+ "typescript": "^5.3.3"
51
+ },
52
+ "jest": {
53
+ "modulePathIgnorePatterns": [
54
+ "built"
55
+ ],
56
+ "testEnvironment": "jsdom",
57
+ "extensionsToTreatAsEsm": [
58
+ ".ts"
59
+ ],
60
+ "moduleNameMapper": {
61
+ "^(\\.{1,2}/.*)\\.js$": "$1"
62
+ },
63
+ "transform": {
64
+ "^.+\\.(t|j)sx?$": [
65
+ "@swc/jest"
66
+ ]
67
+ }
68
+ }
69
+ }