@modplayjs/fmt-med 0.0.0-stage → 0.2.1
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 +39 -2
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/instr.d.ts +69 -0
- package/dist/instr.d.ts.map +1 -0
- package/dist/instr.js +453 -0
- package/dist/instr.js.map +1 -0
- package/dist/med2.d.ts +12 -0
- package/dist/med2.d.ts.map +1 -0
- package/dist/med2.js +240 -0
- package/dist/med2.js.map +1 -0
- package/dist/med3.d.ts +8 -0
- package/dist/med3.d.ts.map +1 -0
- package/dist/med3.js +464 -0
- package/dist/med3.js.map +1 -0
- package/dist/med4.d.ts +5 -0
- package/dist/med4.d.ts.map +1 -0
- package/dist/med4.js +813 -0
- package/dist/med4.js.map +1 -0
- package/dist/mmd1.d.ts +11 -0
- package/dist/mmd1.d.ts.map +1 -0
- package/dist/mmd1.js +496 -0
- package/dist/mmd1.js.map +1 -0
- package/dist/mmd3.d.ts +10 -0
- package/dist/mmd3.d.ts.map +1 -0
- package/dist/mmd3.js +440 -0
- package/dist/mmd3.js.map +1 -0
- package/dist/mmdCommon.d.ts +98 -0
- package/dist/mmdCommon.d.ts.map +1 -0
- package/dist/mmdCommon.js +395 -0
- package/dist/mmdCommon.js.map +1 -0
- package/dist/readevent.d.ts +6 -0
- package/dist/readevent.d.ts.map +1 -0
- package/dist/readevent.js +168 -0
- package/dist/readevent.js.map +1 -0
- package/package.json +47 -4
package/README.md
CHANGED
|
@@ -1,3 +1,40 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @modplayjs/fmt-med
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**MED / OctaMED (.med) format plugin** for `@modplayjs/core`, ported 1:1
|
|
4
|
+
from libxmp's MED loader family:
|
|
5
|
+
|
|
6
|
+
- MMD0/MMD1 (`mmd1_load.c`) — MED/OctaMED tracker modes
|
|
7
|
+
- MMD2/MMD3 (`mmd3_load.c`) — OctaMED Soundstudio
|
|
8
|
+
- MMDCommon (`mmd_common.c`) — shared instrument/sample decoding incl.
|
|
9
|
+
the MED synth/hybrid instruments (MED V1–V5, IFFOCT/IFF5OCT wavelists)
|
|
10
|
+
- MED2/MED3/MED4 (`.med` 5/6/8-channel song variants, `med2/3/4_load.c`)
|
|
11
|
+
|
|
12
|
+
Playback includes the MED synth extras (arpeggios, wave/PSS commands,
|
|
13
|
+
hold/decay) through the shared extras engine.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install @modplayjs/fmt-med
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { CorePlayer } from '@modplayjs/core';
|
|
25
|
+
import { plugin as medPlugin } from '@modplayjs/fmt-med';
|
|
26
|
+
|
|
27
|
+
const core = new CorePlayer();
|
|
28
|
+
core.registries.registerFormat(medPlugin);
|
|
29
|
+
core.loadModule(bytes); // .med / .mmd0-.mmd3 / .med2-.med4
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Also exports
|
|
33
|
+
|
|
34
|
+
Plugins: `plugin` (MMD0/MMD1), `mmd3Plugin`, `med2Plugin`, `med3Plugin`,
|
|
35
|
+
`med4Plugin` — register each format you want to accept. Direct loaders:
|
|
36
|
+
`mmd1Test/Load`, `mmd3Test/Load`, `med2Test/Load`, `med3Test/Load`,
|
|
37
|
+
`med4Test/Load`, `mmdReadTitle`, plus `readEventMed` for the shared MED
|
|
38
|
+
event reader.
|
|
39
|
+
|
|
40
|
+
Part of the [modplayjs monorepo](../../README.md). BSD-3-Clause.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { FormatPlugin } from '@modplayjs/core';
|
|
2
|
+
export { mmdConvertTempo, mmdXlatFx, mmdSetBpm, mmdTrackerVersion, MED_TIME_FACTOR, FLAG_INSTRSATT, type MedExpData, type MedInstrExt, type MedSample, } from './mmdCommon.js';
|
|
3
|
+
export { mmd1Test, mmd1Load, mmdReadTitle } from './mmd1.js';
|
|
4
|
+
export { mmd3Test, mmd3Load } from './mmd3.js';
|
|
5
|
+
export { med2Test, med2Load } from './med2.js';
|
|
6
|
+
export { med3Test, med3Load } from './med3.js';
|
|
7
|
+
export { med4Test, med4Load } from './med4.js';
|
|
8
|
+
export { readEventMed } from './readevent.js';
|
|
9
|
+
/** MMD0/MMD1/MMDC (MED 2.10 / OctaMED). */
|
|
10
|
+
export declare const plugin: FormatPlugin;
|
|
11
|
+
/** MMD2/MMD3 (OctaMED / OctaMED Soundstudio). */
|
|
12
|
+
export declare const mmd3Plugin: FormatPlugin;
|
|
13
|
+
/** MED 1.12 MED2. */
|
|
14
|
+
export declare const med2Plugin: FormatPlugin;
|
|
15
|
+
/** MED 2.00 MED3. */
|
|
16
|
+
export declare const med3Plugin: FormatPlugin;
|
|
17
|
+
/** MED 2.10 MED4. */
|
|
18
|
+
export declare const med4Plugin: FormatPlugin;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAQ,YAAY,EAAuB,MAAM,iBAAiB,CAAC;AAQ/E,OAAO,EACL,eAAe,EACf,SAAS,EACT,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,SAAS,GACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAoB9C,2CAA2C;AAC3C,eAAO,MAAM,MAAM,cAAwC,CAAC;AAC5D,iDAAiD;AACjD,eAAO,MAAM,UAAU,cAAyC,CAAC;AACjE,qBAAqB;AACrB,eAAO,MAAM,UAAU,cAAyC,CAAC;AACjE,qBAAqB;AACrB,eAAO,MAAM,UAAU,cAAyC,CAAC;AACjE,qBAAqB;AACrB,eAAO,MAAM,UAAU,cAAyC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BSD-3-Clause
|
|
2
|
+
// Copyright (c) 2026 Bitti09 — modplayjs contributors
|
|
3
|
+
// Project-original code.
|
|
4
|
+
// @modplayjs/fmt-med — MED/OctaMED loaders (MMD0/1/C, MMD2/3, MED2/3/4)
|
|
5
|
+
// + event reader. Port of reference/libxmp src/loaders/mmd_common.c,
|
|
6
|
+
// mmd1_load.c, mmd3_load.c, med2_load.c, med3_load.c, med4_load.c.
|
|
7
|
+
import { mmd1Test, mmd1Load } from './mmd1.js';
|
|
8
|
+
import { mmd3Test, mmd3Load } from './mmd3.js';
|
|
9
|
+
import { med2Test, med2Load } from './med2.js';
|
|
10
|
+
import { med3Test, med3Load } from './med3.js';
|
|
11
|
+
import { med4Test, med4Load } from './med4.js';
|
|
12
|
+
import { readEventMed } from './readevent.js';
|
|
13
|
+
export { mmdConvertTempo, mmdXlatFx, mmdSetBpm, mmdTrackerVersion, MED_TIME_FACTOR, FLAG_INSTRSATT, } from './mmdCommon.js';
|
|
14
|
+
export { mmd1Test, mmd1Load, mmdReadTitle } from './mmd1.js';
|
|
15
|
+
export { mmd3Test, mmd3Load } from './mmd3.js';
|
|
16
|
+
export { med2Test, med2Load } from './med2.js';
|
|
17
|
+
export { med3Test, med3Load } from './med3.js';
|
|
18
|
+
export { med4Test, med4Load } from './med4.js';
|
|
19
|
+
export { readEventMed } from './readevent.js';
|
|
20
|
+
function makePlugin(name, test, load) {
|
|
21
|
+
return {
|
|
22
|
+
name,
|
|
23
|
+
test,
|
|
24
|
+
load,
|
|
25
|
+
readEvent(core, chn, row) {
|
|
26
|
+
const mod = core.module;
|
|
27
|
+
const e = core.readEventScratch(chn) ?? core.readEventAt(mod.xxo[core.ctx.p.ord] ?? 0, chn, row);
|
|
28
|
+
readEventMed(core, e, chn);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** MMD0/MMD1/MMDC (MED 2.10 / OctaMED). */
|
|
33
|
+
export const plugin = makePlugin('med', mmd1Test, mmd1Load);
|
|
34
|
+
/** MMD2/MMD3 (OctaMED / OctaMED Soundstudio). */
|
|
35
|
+
export const mmd3Plugin = makePlugin('mmd3', mmd3Test, mmd3Load);
|
|
36
|
+
/** MED 1.12 MED2. */
|
|
37
|
+
export const med2Plugin = makePlugin('med2', med2Test, med2Load);
|
|
38
|
+
/** MED 2.00 MED3. */
|
|
39
|
+
export const med3Plugin = makePlugin('med3', med3Test, med3Load);
|
|
40
|
+
/** MED 2.10 MED4. */
|
|
41
|
+
export const med4Plugin = makePlugin('med4', med4Test, med4Load);
|
|
42
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,sDAAsD;AACtD,yBAAyB;AACzB,wEAAwE;AACxE,qEAAqE;AACrE,mEAAmE;AAGnE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EACL,eAAe,EACf,SAAS,EACT,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,cAAc,GAIf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,SAAS,UAAU,CACjB,IAAY,EACZ,IAAoC,EACpC,IAAqD;IAErD,OAAO;QACL,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,SAAS,CAAC,IAAU,EAAE,GAAW,EAAE,GAAW;YAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,MAAoB,CAAC;YACtC,MAAM,CAAC,GACL,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YACzF,YAAY,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;QAC7B,CAAC;KACF,CAAC;AACJ,CAAC;AAED,2CAA2C;AAC3C,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAC5D,iDAAiD;AACjD,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACjE,qBAAqB;AACrB,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACjE,qBAAqB;AACrB,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACjE,qBAAqB;AACrB,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC"}
|
package/dist/instr.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Instrument, RawSample } from '@modplayjs/core';
|
|
2
|
+
import { type MedExpData, type MedInstrExt, type MedSample } from './mmdCommon.js';
|
|
3
|
+
/** InstrHdr view (med.h:159-166). */
|
|
4
|
+
export interface InstrHdr {
|
|
5
|
+
length: number;
|
|
6
|
+
type: number;
|
|
7
|
+
}
|
|
8
|
+
/** Everything the instrument loaders need from the calling loader. */
|
|
9
|
+
export interface InstrumentLoadState {
|
|
10
|
+
bytes: Uint8Array;
|
|
11
|
+
start: number;
|
|
12
|
+
/** Current byte cursor (advances as samples are consumed). */
|
|
13
|
+
pos: number;
|
|
14
|
+
ver: number;
|
|
15
|
+
samples: RawSample[];
|
|
16
|
+
instruments: Instrument[];
|
|
17
|
+
expdata: MedExpData;
|
|
18
|
+
expSmp: MedInstrExt[];
|
|
19
|
+
song: MedSample[];
|
|
20
|
+
}
|
|
21
|
+
/** SynthInstr (med.h:175-190) + the absolute file positions of the tables. */
|
|
22
|
+
export interface SynthInstr {
|
|
23
|
+
defaultdecay: number;
|
|
24
|
+
rep: number;
|
|
25
|
+
replen: number;
|
|
26
|
+
voltbllen: number;
|
|
27
|
+
wftbllen: number;
|
|
28
|
+
volspeed: number;
|
|
29
|
+
wfspeed: number;
|
|
30
|
+
wforms: number;
|
|
31
|
+
wf: number[];
|
|
32
|
+
voltblPos?: number;
|
|
33
|
+
wftblPos?: number;
|
|
34
|
+
}
|
|
35
|
+
export declare function emptySynth(): SynthInstr;
|
|
36
|
+
/** Big-endian cursor (shared with mmd1's Reader; declared here so the
|
|
37
|
+
* instrument loader can seek the raw bytes). */
|
|
38
|
+
export declare class Reader {
|
|
39
|
+
readonly bytes: Uint8Array;
|
|
40
|
+
pos: number;
|
|
41
|
+
constructor(bytes: Uint8Array, start: number);
|
|
42
|
+
get length(): number;
|
|
43
|
+
u8(): number;
|
|
44
|
+
s8(): number;
|
|
45
|
+
u16(): number;
|
|
46
|
+
s16(): number;
|
|
47
|
+
u32(): number;
|
|
48
|
+
raw(n: number): Uint8Array;
|
|
49
|
+
seek(p: number): void;
|
|
50
|
+
skip(n: number): void;
|
|
51
|
+
eof(): boolean;
|
|
52
|
+
}
|
|
53
|
+
/** mmd_load_instrument_common (mmd_common.c:413-482). */
|
|
54
|
+
export declare function instrumentInfo(st: InstrumentLoadState, instr: InstrHdr | null, e: MedInstrExt, sample: MedSample): {
|
|
55
|
+
length: number;
|
|
56
|
+
rep: number;
|
|
57
|
+
replen: number;
|
|
58
|
+
sampletrans: number;
|
|
59
|
+
synthtrans: number;
|
|
60
|
+
flg: number;
|
|
61
|
+
enable: number;
|
|
62
|
+
};
|
|
63
|
+
/** libxmp_load_sample equivalent over the byte buffer. */
|
|
64
|
+
export declare function loadSampleInto(st: InstrumentLoadState & Partial<{
|
|
65
|
+
r: Reader;
|
|
66
|
+
}>, smpIdx: number, bigend: number): void;
|
|
67
|
+
/** mmd_load_instrument (mmd_common.c:912-990). Returns the next smp_idx. */
|
|
68
|
+
export declare function mmdLoadInstrument(st: InstrumentLoadState, i: number, smpIdx: number): number;
|
|
69
|
+
//# sourceMappingURL=instr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instr.d.ts","sourceRoot":"","sources":["../src/instr.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,UAAU,EAEV,SAAS,EACV,MAAM,iBAAiB,CAAC;AAMzB,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAExB,qCAAqC;AACrC,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,sEAAsE;AACtE,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB;AAED,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,IAAI,UAAU,CAKvC;AAED;iDACiD;AACjD,qBAAa,MAAM;IAEL,QAAQ,CAAC,KAAK,EAAE,UAAU;IADtC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAqB,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAuB;IAC5E,IAAI,MAAM,IAAI,MAAM,CAA8B;IAClD,EAAE,IAAI,MAAM,CAAwC;IACpD,EAAE,IAAI,MAAM,CAAoC;IAChD,GAAG,IAAI,MAAM,CAIZ;IACD,GAAG,IAAI,MAAM,CAAqC;IAClD,GAAG,IAAI,MAAM,CAMZ;IACD,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAIzB;IACD,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAkB;IACvC,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAmB;IACxC,GAAG,IAAI,OAAO,CAA0C;CACzD;AASD,yDAAyD;AACzD,wBAAgB,cAAc,CAC5B,EAAE,EAAE,mBAAmB,EACvB,KAAK,EAAE,QAAQ,GAAG,IAAI,EACtB,CAAC,EAAE,WAAW,EACd,MAAM,EAAE,SAAS,GAChB;IACD,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAC5C,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;CACtE,CAiDA;AA+BD,0DAA0D;AAC1D,wBAAgB,cAAc,CAC5B,EAAE,EAAE,mBAAmB,GAAG,OAAO,CAAC;IAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAC/E,IAAI,CAcN;AA6UD,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,mBAAmB,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GACjD,MAAM,CAgCR"}
|
package/dist/instr.js
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BSD-3-Clause
|
|
2
|
+
// Copyright (c) 2026 Bitti09 — modplayjs contributors
|
|
3
|
+
// Ported from: libxmp src/loaders/mmd_common.c (mmd_load_instrument and the
|
|
4
|
+
// per-type loaders, :400-990). Shared by the MMD0/1 and MMD2/3 loaders.
|
|
5
|
+
import { C4_NTSC_RATE, ParseError, SampleFlags, } from '@modplayjs/core';
|
|
6
|
+
import { MMD_NUM_OCT, S_16, STEREO, } from './mmdCommon.js';
|
|
7
|
+
export function emptySynth() {
|
|
8
|
+
return {
|
|
9
|
+
defaultdecay: 0, rep: 0, replen: 0, voltbllen: 0, wftbllen: 0,
|
|
10
|
+
volspeed: 0, wfspeed: 0, wforms: 0, wf: [],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** Big-endian cursor (shared with mmd1's Reader; declared here so the
|
|
14
|
+
* instrument loader can seek the raw bytes). */
|
|
15
|
+
export class Reader {
|
|
16
|
+
bytes;
|
|
17
|
+
pos;
|
|
18
|
+
constructor(bytes, start) {
|
|
19
|
+
this.bytes = bytes;
|
|
20
|
+
this.pos = start;
|
|
21
|
+
}
|
|
22
|
+
get length() { return this.bytes.length; }
|
|
23
|
+
u8() { return this.bytes[this.pos++] ?? 0; }
|
|
24
|
+
s8() { return (this.u8() << 24) >> 24; }
|
|
25
|
+
u16() {
|
|
26
|
+
const v = (this.bytes[this.pos] << 8) | this.bytes[this.pos + 1];
|
|
27
|
+
this.pos += 2;
|
|
28
|
+
return v;
|
|
29
|
+
}
|
|
30
|
+
s16() { return (this.u16() << 16) >> 16; }
|
|
31
|
+
u32() {
|
|
32
|
+
const v = ((this.bytes[this.pos] << 24) | (this.bytes[this.pos + 1] << 16) |
|
|
33
|
+
(this.bytes[this.pos + 2] << 8) | this.bytes[this.pos + 3]) >>> 0;
|
|
34
|
+
this.pos += 4;
|
|
35
|
+
return v;
|
|
36
|
+
}
|
|
37
|
+
raw(n) {
|
|
38
|
+
const s = this.bytes.subarray(this.pos, this.pos + n);
|
|
39
|
+
this.pos += n;
|
|
40
|
+
return s;
|
|
41
|
+
}
|
|
42
|
+
seek(p) { this.pos = p; }
|
|
43
|
+
skip(n) { this.pos += n; }
|
|
44
|
+
eof() { return this.pos >= this.bytes.length; }
|
|
45
|
+
}
|
|
46
|
+
function makeSub() {
|
|
47
|
+
return {
|
|
48
|
+
vol: 0, gvl: 0x40, pan: -1, xpo: 0, fin: 0, vwf: 0, vde: 0,
|
|
49
|
+
vra: 0, vsw: 0, sid: 0, rvv: 0, nna: 0, dct: 0, dca: 0, ifc: 0, ifr: 0,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** mmd_load_instrument_common (mmd_common.c:413-482). */
|
|
53
|
+
export function instrumentInfo(st, instr, e, sample) {
|
|
54
|
+
const ver = st.ver;
|
|
55
|
+
let flg = 0;
|
|
56
|
+
let enable = 1;
|
|
57
|
+
if (ver >= 2 && st.expdata.s_ext_entrsz >= 8) { // MMD2+ instrument flags
|
|
58
|
+
if (e.instr_flags & 0x01 /* SSFLG_LOOP */)
|
|
59
|
+
flg |= SampleFlags.LOOP;
|
|
60
|
+
if (e.instr_flags & 0x08 /* SSFLG_PINGPONG */)
|
|
61
|
+
flg |= SampleFlags.BIDIR;
|
|
62
|
+
if (e.instr_flags & 0x04 /* SSFLG_DISABLED */)
|
|
63
|
+
enable = 0;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
if (sample.replen > 1)
|
|
67
|
+
flg |= SampleFlags.LOOP;
|
|
68
|
+
}
|
|
69
|
+
let sampletrans = 36 + sample.strans;
|
|
70
|
+
const synthtrans = 12 + sample.strans;
|
|
71
|
+
let length = 0;
|
|
72
|
+
let rep = 0;
|
|
73
|
+
let replen = 0;
|
|
74
|
+
if (instr) {
|
|
75
|
+
const sampleType = instr.type & ~(S_16 | 0x18 /* MD16 */ | STEREO);
|
|
76
|
+
if ((ver >= 3 && sampleType === 0) || sampleType === 7) {
|
|
77
|
+
// Mix mode transposes samples down two octaves. ExtSamples (7)
|
|
78
|
+
// are transposed regardless.
|
|
79
|
+
sampletrans -= 24;
|
|
80
|
+
}
|
|
81
|
+
length = instr.length;
|
|
82
|
+
if (ver >= 3 && st.expdata.s_ext_entrsz >= 18) {
|
|
83
|
+
rep = e.long_repeat;
|
|
84
|
+
replen = e.long_replen;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
rep = sample.rep << 1;
|
|
88
|
+
replen = sample.replen << 1;
|
|
89
|
+
}
|
|
90
|
+
if (instr.type & S_16) {
|
|
91
|
+
flg |= SampleFlags.BITS16;
|
|
92
|
+
// Length is (bytes / channels) but the loop is measured in frames.
|
|
93
|
+
length >>= 1;
|
|
94
|
+
}
|
|
95
|
+
if (instr.type & STEREO) {
|
|
96
|
+
flg |= SampleFlags.STEREO;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return { length, rep, replen, sampletrans, synthtrans, flg, enable };
|
|
100
|
+
}
|
|
101
|
+
/** mmd_set_default_pitch_note (mmd_common.c:492-504). */
|
|
102
|
+
function mmdSetDefaultPitchNote(st, i, e, ver) {
|
|
103
|
+
if (ver >= 3) {
|
|
104
|
+
let note = 53; // MMD3_DEFAULT_NOTE
|
|
105
|
+
if (e.default_pitch)
|
|
106
|
+
note = e.default_pitch - 1;
|
|
107
|
+
if (note >= 0 && note < 121) {
|
|
108
|
+
st.instruments[i].mapXpo[12] = note;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** mmd_alloc_tables (mmd_common.c:506-526). */
|
|
113
|
+
function mmdAllocTables(st, i, synth) {
|
|
114
|
+
const ie = st.instruments[i].extras;
|
|
115
|
+
if (!ie)
|
|
116
|
+
return;
|
|
117
|
+
ie.volTable = st.bytes.slice(st.start + (synth.voltblPos ?? 0), st.start + (synth.voltblPos ?? 0) + synth.voltbllen);
|
|
118
|
+
ie.wavTable = st.bytes.slice(st.start + (synth.wftblPos ?? 0), st.start + (synth.wftblPos ?? 0) + synth.wftbllen);
|
|
119
|
+
}
|
|
120
|
+
/** libxmp_load_sample equivalent over the byte buffer. */
|
|
121
|
+
export function loadSampleInto(st, smpIdx, bigend) {
|
|
122
|
+
const s = st.samples[smpIdx];
|
|
123
|
+
const is16 = (s.flags & SampleFlags.BITS16) !== 0;
|
|
124
|
+
const stereo = (s.flags & SampleFlags.STEREO) !== 0;
|
|
125
|
+
const bytelen = s.length * (is16 ? 2 : 1) * (stereo ? 2 : 1);
|
|
126
|
+
// The C loader reads through the SAME hio handle the struct header came
|
|
127
|
+
// from. Keep the reader cursor (r) authoritative when present so the
|
|
128
|
+
// instrument header reads and the sample data share one position.
|
|
129
|
+
const cur = 'r' in st && st.r ? st.r : st;
|
|
130
|
+
const take = Math.min(bytelen, Math.max(0, st.bytes.length - cur.pos));
|
|
131
|
+
s.data = st.bytes.subarray(cur.pos, cur.pos + take);
|
|
132
|
+
if (bigend)
|
|
133
|
+
s.flags |= 1 << 11; // DecodeFlag.BIGEND
|
|
134
|
+
cur.pos += bytelen; // hio_read advances by requested count
|
|
135
|
+
if (cur !== st)
|
|
136
|
+
st.pos = cur.pos;
|
|
137
|
+
}
|
|
138
|
+
/** Read a SynthInstr body (mmd_common.c:548-558 / 663-673). */
|
|
139
|
+
function readSynthInstr(r, pos) {
|
|
140
|
+
r.seek(pos);
|
|
141
|
+
const synth = emptySynth();
|
|
142
|
+
synth.defaultdecay = r.u8();
|
|
143
|
+
r.skip(3);
|
|
144
|
+
synth.rep = r.u16();
|
|
145
|
+
synth.replen = r.u16();
|
|
146
|
+
synth.voltbllen = r.u16();
|
|
147
|
+
synth.wftbllen = r.u16();
|
|
148
|
+
synth.volspeed = r.u8();
|
|
149
|
+
synth.wfspeed = r.u8();
|
|
150
|
+
synth.wforms = r.u16();
|
|
151
|
+
synth.voltblPos = r.pos;
|
|
152
|
+
r.skip(synth.voltbllen);
|
|
153
|
+
synth.wftblPos = r.pos;
|
|
154
|
+
r.skip(synth.wftbllen);
|
|
155
|
+
for (let j = 0; j < synth.wforms; j++)
|
|
156
|
+
synth.wf.push(r.u32());
|
|
157
|
+
return synth;
|
|
158
|
+
}
|
|
159
|
+
function synthExtras(synth) {
|
|
160
|
+
return {
|
|
161
|
+
vts: synth.volspeed, wts: synth.wfspeed,
|
|
162
|
+
vtlen: synth.voltbllen, wtlen: synth.wftbllen,
|
|
163
|
+
hold: 0, decay: 0, default_pitch: 0, finetune: 0,
|
|
164
|
+
suppress_midi_off: 0, long_repeat: 0, long_replen: 0,
|
|
165
|
+
volTable: null, wavTable: null,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function holdDecayExtras(e) {
|
|
169
|
+
return {
|
|
170
|
+
vts: 0, wts: 0, vtlen: 0, wtlen: 0,
|
|
171
|
+
hold: e.hold, decay: e.decay, default_pitch: e.default_pitch,
|
|
172
|
+
finetune: e.finetune, suppress_midi_off: e.suppress_midi_off,
|
|
173
|
+
long_repeat: 0, long_replen: 0,
|
|
174
|
+
volTable: null, wavTable: null,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/** mmd_load_hybrid_instrument (mmd_common.c:528-646). */
|
|
178
|
+
function loadHybridInstrument(st, i, smpIdx0, e, sample) {
|
|
179
|
+
const r = st.r;
|
|
180
|
+
let smpIdx = smpIdx0;
|
|
181
|
+
const pos = r.pos;
|
|
182
|
+
const synth = readSynthInstr(r, pos);
|
|
183
|
+
if (synth.voltbllen > 128 || synth.wftbllen > 128 ||
|
|
184
|
+
synth.wforms < 1 || synth.wforms > 64) {
|
|
185
|
+
throw new ParseError(`MED: invalid synth tables at instrument ${i}`);
|
|
186
|
+
}
|
|
187
|
+
// Sample header at wf[0]; hybrids don't carry IFFOCT/ext samples.
|
|
188
|
+
r.seek(pos - 6 + synth.wf[0]);
|
|
189
|
+
const length = r.u32();
|
|
190
|
+
const type = r.s16();
|
|
191
|
+
if (type !== 0) {
|
|
192
|
+
throw new ParseError(`MED: unsupported sample type ${type} for hybrid`);
|
|
193
|
+
}
|
|
194
|
+
const xxi = st.instruments[i];
|
|
195
|
+
xxi.nsm = synth.wforms;
|
|
196
|
+
xxi.sub = [];
|
|
197
|
+
for (let j = 0; j < synth.wforms; j++)
|
|
198
|
+
xxi.sub.push(makeSub());
|
|
199
|
+
const ie = synthExtras(synth);
|
|
200
|
+
xxi.extras = ie;
|
|
201
|
+
const info = instrumentInfo(st, { length, type }, e, sample);
|
|
202
|
+
mmdSetDefaultPitchNote(st, i, e, st.ver);
|
|
203
|
+
const fin = (((e.finetune << 4) & 0xff) << 24) >> 24;
|
|
204
|
+
// sub 0 = the sample.
|
|
205
|
+
{
|
|
206
|
+
const sub = xxi.sub[0];
|
|
207
|
+
sub.vol = info.enable ? sample.svol : 0;
|
|
208
|
+
sub.xpo = info.sampletrans;
|
|
209
|
+
sub.sid = smpIdx;
|
|
210
|
+
sub.fin = fin;
|
|
211
|
+
st.samples[smpIdx] = {
|
|
212
|
+
name: '',
|
|
213
|
+
data: new Uint8Array(0),
|
|
214
|
+
length: info.length,
|
|
215
|
+
loopStart: info.rep,
|
|
216
|
+
loopEnd: info.rep + info.replen,
|
|
217
|
+
sustainStart: 0,
|
|
218
|
+
sustainEnd: 0,
|
|
219
|
+
finetune: fin,
|
|
220
|
+
volume: sub.vol,
|
|
221
|
+
flags: info.flg,
|
|
222
|
+
c5spd: C4_NTSC_RATE,
|
|
223
|
+
};
|
|
224
|
+
loadSampleInto(st, smpIdx, 0);
|
|
225
|
+
smpIdx++;
|
|
226
|
+
}
|
|
227
|
+
for (let j = 1; j < synth.wforms; j++) {
|
|
228
|
+
const sub = xxi.sub[j];
|
|
229
|
+
sub.vol = info.enable ? 64 : 0;
|
|
230
|
+
sub.xpo = info.synthtrans;
|
|
231
|
+
sub.sid = smpIdx;
|
|
232
|
+
sub.fin = fin;
|
|
233
|
+
r.seek(pos - 6 + synth.wf[j]);
|
|
234
|
+
const len = r.u16() * 2;
|
|
235
|
+
st.samples[smpIdx] = {
|
|
236
|
+
name: '',
|
|
237
|
+
data: new Uint8Array(0),
|
|
238
|
+
length: len,
|
|
239
|
+
loopStart: 0,
|
|
240
|
+
loopEnd: len,
|
|
241
|
+
sustainStart: 0,
|
|
242
|
+
sustainEnd: 0,
|
|
243
|
+
finetune: fin,
|
|
244
|
+
volume: sub.vol,
|
|
245
|
+
flags: SampleFlags.LOOP,
|
|
246
|
+
c5spd: C4_NTSC_RATE,
|
|
247
|
+
};
|
|
248
|
+
loadSampleInto(st, smpIdx, 0);
|
|
249
|
+
smpIdx++;
|
|
250
|
+
}
|
|
251
|
+
mmdAllocTables(st, i, synth);
|
|
252
|
+
return smpIdx;
|
|
253
|
+
}
|
|
254
|
+
/** mmd_load_synth_instrument (mmd_common.c:648-739). Returns smpIdx
|
|
255
|
+
* unchanged when wforms == 0xffff (empty synth). */
|
|
256
|
+
function loadSynthInstrument(st, i, smpIdx, e, sample) {
|
|
257
|
+
const r = st.r;
|
|
258
|
+
const pos = r.pos;
|
|
259
|
+
const info = instrumentInfo(st, null, e, sample);
|
|
260
|
+
mmdSetDefaultPitchNote(st, i, e, st.ver);
|
|
261
|
+
const synth = readSynthInstr(r, pos);
|
|
262
|
+
if (synth.wforms === 0xffff) {
|
|
263
|
+
st.instruments[i].nsm = 0;
|
|
264
|
+
return { smpIdx, wforms: 0 };
|
|
265
|
+
}
|
|
266
|
+
if (synth.voltbllen > 128 || synth.wftbllen > 128 || synth.wforms > 64) {
|
|
267
|
+
throw new ParseError(`MED: invalid synth tables at instrument ${i}`);
|
|
268
|
+
}
|
|
269
|
+
const xxi = st.instruments[i];
|
|
270
|
+
xxi.nsm = synth.wforms;
|
|
271
|
+
xxi.sub = [];
|
|
272
|
+
for (let j = 0; j < synth.wforms; j++)
|
|
273
|
+
xxi.sub.push(makeSub());
|
|
274
|
+
const ie = synthExtras(synth);
|
|
275
|
+
xxi.extras = ie;
|
|
276
|
+
const fin = (((e.finetune << 4) & 0xff) << 24) >> 24;
|
|
277
|
+
for (let j = 0; j < synth.wforms; j++) {
|
|
278
|
+
const sub = xxi.sub[j];
|
|
279
|
+
sub.vol = info.enable ? 64 : 0;
|
|
280
|
+
sub.xpo = info.synthtrans;
|
|
281
|
+
sub.sid = smpIdx;
|
|
282
|
+
sub.fin = fin;
|
|
283
|
+
r.seek(pos - 6 + synth.wf[j]);
|
|
284
|
+
const len = r.u16() * 2;
|
|
285
|
+
st.samples[smpIdx] = {
|
|
286
|
+
name: '',
|
|
287
|
+
data: new Uint8Array(0),
|
|
288
|
+
length: len,
|
|
289
|
+
loopStart: 0,
|
|
290
|
+
loopEnd: len,
|
|
291
|
+
sustainStart: 0,
|
|
292
|
+
sustainEnd: 0,
|
|
293
|
+
finetune: fin,
|
|
294
|
+
volume: sub.vol,
|
|
295
|
+
flags: SampleFlags.LOOP,
|
|
296
|
+
c5spd: C4_NTSC_RATE,
|
|
297
|
+
};
|
|
298
|
+
loadSampleInto(st, smpIdx, 0);
|
|
299
|
+
smpIdx++;
|
|
300
|
+
}
|
|
301
|
+
mmdAllocTables(st, i, synth);
|
|
302
|
+
return { smpIdx, wforms: synth.wforms };
|
|
303
|
+
}
|
|
304
|
+
/** iffoct_insmap / iffoct_xpomap (mmd_common.c:814-830). */
|
|
305
|
+
const IFFOCT_INSMAP = [
|
|
306
|
+
[1, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
307
|
+
[2, 2, 2, 2, 2, 2, 1, 1, 0],
|
|
308
|
+
[3, 3, 3, 2, 2, 2, 1, 1, 0],
|
|
309
|
+
[4, 4, 4, 3, 2, 2, 1, 1, 0],
|
|
310
|
+
[5, 5, 5, 5, 4, 3, 2, 1, 0],
|
|
311
|
+
[6, 6, 6, 6, 5, 4, 3, 2, 1],
|
|
312
|
+
];
|
|
313
|
+
const IFFOCT_XPOMAP = [
|
|
314
|
+
[12, 12, 12, 0, 0, 0, 0, 0, 0],
|
|
315
|
+
[12, 12, 12, 12, 12, 12, 0, 0, -12],
|
|
316
|
+
[12, 12, 12, 0, 0, 0, -12, -12, -24],
|
|
317
|
+
[24, 24, 24, 12, 0, 0, -12, -24, -36],
|
|
318
|
+
[12, 12, 12, 12, 0, -12, -24, -36, -48],
|
|
319
|
+
[12, 12, 12, 12, 0, -12, -24, -36, -48],
|
|
320
|
+
];
|
|
321
|
+
/** mmd_load_iffoct_instrument (mmd_common.c:832-907). */
|
|
322
|
+
function loadIffoctInstrument(st, i, smpIdx0, instr, numOct, e, sample) {
|
|
323
|
+
let smpIdx = smpIdx0;
|
|
324
|
+
if (numOct < 2 || numOct > 7) {
|
|
325
|
+
throw new ParseError(`MED: invalid octave count at instrument ${i}`);
|
|
326
|
+
}
|
|
327
|
+
if ((instr.length | 0) < 0) {
|
|
328
|
+
throw new ParseError(`MED: absurd IFFOCT instrument ${i}`);
|
|
329
|
+
}
|
|
330
|
+
const xxi = st.instruments[i];
|
|
331
|
+
xxi.nsm = numOct;
|
|
332
|
+
xxi.sub = [];
|
|
333
|
+
for (let j = 0; j < numOct; j++)
|
|
334
|
+
xxi.sub.push(makeSub());
|
|
335
|
+
xxi.extras = holdDecayExtras(e);
|
|
336
|
+
// Base octave size.
|
|
337
|
+
let size = Math.trunc(instr.length / ((1 << numOct) - 1));
|
|
338
|
+
const info = instrumentInfo(st, instr, e, sample);
|
|
339
|
+
const fin = (((e.finetune << 4) & 0xff) << 24) >> 24;
|
|
340
|
+
for (let j = 0; j < numOct; j++) {
|
|
341
|
+
const sub = xxi.sub[j];
|
|
342
|
+
sub.vol = info.enable ? sample.svol : 0;
|
|
343
|
+
sub.xpo = info.sampletrans - 12;
|
|
344
|
+
sub.sid = smpIdx;
|
|
345
|
+
sub.fin = fin;
|
|
346
|
+
st.samples[smpIdx] = {
|
|
347
|
+
name: '',
|
|
348
|
+
data: new Uint8Array(0),
|
|
349
|
+
length: size,
|
|
350
|
+
loopStart: info.rep,
|
|
351
|
+
loopEnd: info.rep + info.replen,
|
|
352
|
+
sustainStart: 0,
|
|
353
|
+
sustainEnd: 0,
|
|
354
|
+
finetune: fin,
|
|
355
|
+
volume: sub.vol,
|
|
356
|
+
flags: info.flg,
|
|
357
|
+
c5spd: C4_NTSC_RATE,
|
|
358
|
+
};
|
|
359
|
+
loadSampleInto(st, smpIdx, 1); // SAMPLE_FLAG_BIGEND
|
|
360
|
+
smpIdx++;
|
|
361
|
+
size <<= 1;
|
|
362
|
+
info.rep <<= 1;
|
|
363
|
+
info.replen <<= 1;
|
|
364
|
+
}
|
|
365
|
+
// Instrument mapping.
|
|
366
|
+
for (let j = 0; j < 9; j++) {
|
|
367
|
+
for (let k = 0; k < 12; k++) {
|
|
368
|
+
xxi.map[12 * j + k] = IFFOCT_INSMAP[numOct - 2][j];
|
|
369
|
+
xxi.mapXpo[12 * j + k] = IFFOCT_XPOMAP[numOct - 2][j];
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return smpIdx;
|
|
373
|
+
}
|
|
374
|
+
/** mmd_load_sampled_instrument (mmd_common.c:741-812). */
|
|
375
|
+
function loadSampledInstrument(st, i, smpIdx, instr, e, sample) {
|
|
376
|
+
const xxi = st.instruments[i];
|
|
377
|
+
xxi.nsm = 1;
|
|
378
|
+
xxi.sub = [makeSub()];
|
|
379
|
+
xxi.extras = holdDecayExtras(e);
|
|
380
|
+
const info = instrumentInfo(st, instr, e, sample);
|
|
381
|
+
mmdSetDefaultPitchNote(st, i, e, st.ver);
|
|
382
|
+
const sub = xxi.sub[0];
|
|
383
|
+
sub.vol = info.enable ? sample.svol : 0;
|
|
384
|
+
sub.xpo = info.sampletrans;
|
|
385
|
+
sub.sid = smpIdx;
|
|
386
|
+
sub.fin = (((e.finetune << 4) & 0xff) << 24) >> 24;
|
|
387
|
+
st.samples[smpIdx] = {
|
|
388
|
+
name: '',
|
|
389
|
+
data: new Uint8Array(0),
|
|
390
|
+
length: info.length,
|
|
391
|
+
loopStart: info.rep,
|
|
392
|
+
loopEnd: info.rep + info.replen,
|
|
393
|
+
sustainStart: 0,
|
|
394
|
+
sustainEnd: 0,
|
|
395
|
+
finetune: sub.fin,
|
|
396
|
+
volume: sub.vol,
|
|
397
|
+
flags: info.flg,
|
|
398
|
+
c5spd: C4_NTSC_RATE,
|
|
399
|
+
};
|
|
400
|
+
// Restrict sampled instruments to a 3-octave range except for MMD3.
|
|
401
|
+
// ExtSamples have two extra octaves.
|
|
402
|
+
if (st.ver < 3) {
|
|
403
|
+
const octaves = (instr.type & 7) === 7 ? 5 : 3;
|
|
404
|
+
for (let j = 0; j < 9; j++) {
|
|
405
|
+
for (let k = 0; k < 12; k++) {
|
|
406
|
+
let xpo = 0;
|
|
407
|
+
if (j < 1)
|
|
408
|
+
xpo = 12 * (1 - j);
|
|
409
|
+
else if (j > octaves)
|
|
410
|
+
xpo = -12 * (j - octaves);
|
|
411
|
+
xxi.mapXpo[12 * j + k] = xpo;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
loadSampleInto(st, smpIdx, 1); // SAMPLE_FLAG_BIGEND
|
|
416
|
+
return smpIdx + 1;
|
|
417
|
+
}
|
|
418
|
+
/** mmd_load_instrument (mmd_common.c:912-990). Returns the next smp_idx. */
|
|
419
|
+
export function mmdLoadInstrument(st, i, smpIdx) {
|
|
420
|
+
const r = new Reader(st.bytes, st.pos);
|
|
421
|
+
const cst = { ...st, r };
|
|
422
|
+
const length = r.u32();
|
|
423
|
+
const type = r.s16();
|
|
424
|
+
const sampleType = type & ~(S_16 | 0x18 | STEREO);
|
|
425
|
+
const e = st.expSmp[i] ?? {
|
|
426
|
+
hold: 0, decay: 0, suppress_midi_off: 0, finetune: 0,
|
|
427
|
+
default_pitch: 0, instr_flags: 0, long_repeat: 0, long_replen: 0,
|
|
428
|
+
};
|
|
429
|
+
const sample = st.song[i] ?? {
|
|
430
|
+
rep: 0, replen: 0, midich: 0, midipreset: 0, svol: 0, strans: 0,
|
|
431
|
+
};
|
|
432
|
+
let out;
|
|
433
|
+
if (type === -2) { // Hybrid
|
|
434
|
+
out = loadHybridInstrument(cst, i, smpIdx, e, sample);
|
|
435
|
+
}
|
|
436
|
+
else if (type === -1) { // Synthetic
|
|
437
|
+
const res = loadSynthInstrument(cst, i, smpIdx, e, sample);
|
|
438
|
+
out = res.smpIdx;
|
|
439
|
+
}
|
|
440
|
+
else if (type >= 1 && type <= 6) { // IFFOCT
|
|
441
|
+
const oct = MMD_NUM_OCT[type - 1];
|
|
442
|
+
out = loadIffoctInstrument(cst, i, smpIdx, { length, type }, oct, e, sample);
|
|
443
|
+
}
|
|
444
|
+
else if (sampleType === 0 || sampleType === 7) { // Sample
|
|
445
|
+
out = loadSampledInstrument(cst, i, smpIdx, { length, type }, e, sample);
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
throw new ParseError(`MED: invalid instrument type ${type}`);
|
|
449
|
+
}
|
|
450
|
+
st.pos = r.pos;
|
|
451
|
+
return out;
|
|
452
|
+
}
|
|
453
|
+
//# sourceMappingURL=instr.js.map
|