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 +90 -0
- package/built/constants.d.ts +4 -0
- package/built/constants.d.ts.map +1 -0
- package/built/constants.js +14 -0
- package/built/creator.d.ts +3 -0
- package/built/creator.d.ts.map +1 -0
- package/built/creator.js +34 -0
- package/built/index.d.ts +5 -0
- package/built/index.d.ts.map +1 -0
- package/built/index.js +4 -0
- package/built/parser.d.ts +3 -0
- package/built/parser.d.ts.map +1 -0
- package/built/parser.js +36 -0
- package/built/types.d.ts +27 -0
- package/built/types.d.ts.map +1 -0
- package/built/types.js +1 -0
- package/package.json +69 -0
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 @@
|
|
|
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"}
|
package/built/creator.js
ADDED
|
@@ -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
|
+
}
|
package/built/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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"}
|
package/built/parser.js
ADDED
|
@@ -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
|
+
}
|
package/built/types.d.ts
ADDED
|
@@ -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
|
+
}
|