picopilot 0.2.1 → 0.3.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/dist/commands/audio.d.ts +23 -2
- package/dist/commands/audio.d.ts.map +1 -1
- package/dist/commands/audio.js +228 -23
- package/dist/commands/audio.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +1 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/music.d.ts +2 -1
- package/dist/commands/music.d.ts.map +1 -1
- package/dist/commands/music.js +209 -2
- package/dist/commands/music.js.map +1 -1
- package/dist/commands/sfx.d.ts.map +1 -1
- package/dist/commands/sfx.js +99 -11
- package/dist/commands/sfx.js.map +1 -1
- package/dist/engine/audio/analyze.d.ts +164 -0
- package/dist/engine/audio/analyze.d.ts.map +1 -0
- package/dist/engine/audio/analyze.js +512 -0
- package/dist/engine/audio/analyze.js.map +1 -0
- package/dist/engine/audio/index.d.ts +4 -2
- package/dist/engine/audio/index.d.ts.map +1 -1
- package/dist/engine/audio/index.js +4 -2
- package/dist/engine/audio/index.js.map +1 -1
- package/dist/engine/audio/score.d.ts +137 -0
- package/dist/engine/audio/score.d.ts.map +1 -0
- package/dist/engine/audio/score.js +420 -0
- package/dist/engine/audio/score.js.map +1 -0
- package/dist/engine/audio/section.d.ts +7 -0
- package/dist/engine/audio/section.d.ts.map +1 -1
- package/dist/engine/audio/section.js +17 -0
- package/dist/engine/audio/section.js.map +1 -1
- package/dist/engine/audio/sfx.d.ts +114 -12
- package/dist/engine/audio/sfx.d.ts.map +1 -1
- package/dist/engine/audio/sfx.js +236 -33
- package/dist/engine/audio/sfx.js.map +1 -1
- package/dist/engine/pico8/harness.d.ts +11 -5
- package/dist/engine/pico8/harness.d.ts.map +1 -1
- package/dist/engine/pico8/harness.js +24 -10
- package/dist/engine/pico8/harness.js.map +1 -1
- package/dist/engine/scaffold/scaffold.d.ts.map +1 -1
- package/dist/engine/scaffold/scaffold.js +5 -1
- package/dist/engine/scaffold/scaffold.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/audio.test.ts +170 -1
- package/src/commands/audio.ts +284 -27
- package/src/commands/doctor.test.ts +1 -0
- package/src/commands/doctor.ts +1 -0
- package/src/commands/music.test.ts +119 -0
- package/src/commands/music.ts +251 -1
- package/src/commands/sfx.test.ts +55 -0
- package/src/commands/sfx.ts +118 -11
- package/src/engine/audio/analyze.test.ts +239 -0
- package/src/engine/audio/analyze.ts +664 -0
- package/src/engine/audio/index.ts +46 -1
- package/src/engine/audio/score.test.ts +402 -0
- package/src/engine/audio/score.ts +630 -0
- package/src/engine/audio/section.ts +19 -0
- package/src/engine/audio/sfx.test.ts +219 -4
- package/src/engine/audio/sfx.ts +326 -40
- package/src/engine/pico8/harness.test.ts +19 -0
- package/src/engine/pico8/harness.ts +29 -11
- package/src/engine/scaffold/scaffold.ts +5 -1
- package/src/skills/picopilot-audio/SKILL.md +74 -184
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `engine/audio`: the score to `__sfx__` + `__music__` planner (ADR-0020).
|
|
3
|
+
*
|
|
4
|
+
* A SCORE is one or more SONGS (sections of `__music__`, e.g. a title song and
|
|
5
|
+
* an in-game song), each up to 4 channels of picopilot-MML of ANY length. The
|
|
6
|
+
* planner does the bookkeeping an author used to do by hand (the conquest
|
|
7
|
+
* dogfood needed a 250-line script for it):
|
|
8
|
+
*
|
|
9
|
+
* 1. parse every channel into its tracker-row stream ({@link mmlToRows});
|
|
10
|
+
* 2. cut all channels of a song at the SAME row boundaries into patterns of
|
|
11
|
+
* `patternRows` rows (default 32), so channels stay aligned;
|
|
12
|
+
* 3. a channel that is silent for a whole pattern becomes `null` (channel OFF),
|
|
13
|
+
* not a silent SFX; a pattern silent on EVERY channel keeps one silent SFX on
|
|
14
|
+
* channel 0 so it still lasts its length;
|
|
15
|
+
* 4. de-duplicate identical SFX rows across the whole score;
|
|
16
|
+
* 5. allocate slots: free (note-less) slots only, from 63 downwards, unless the
|
|
17
|
+
* caller names an explicit slot RANGE, which the score then OWNS (it may
|
|
18
|
+
* overwrite any slot in it and clears the slots in it it does not use);
|
|
19
|
+
* 6. lay songs out back to back in `__music__`, with loop / stop flags.
|
|
20
|
+
*
|
|
21
|
+
* Pure: cart-free inputs in, a plan out. The `music from-score` command applies
|
|
22
|
+
* the plan through the cart model (only owned `__sfx__` slots + `__music__`
|
|
23
|
+
* change).
|
|
24
|
+
*/
|
|
25
|
+
/** One score channel: MML text, or `{mml, filters}` where `filters` are `!` directives. */
|
|
26
|
+
export type ScoreChannel = string | {
|
|
27
|
+
mml: string;
|
|
28
|
+
filters?: string;
|
|
29
|
+
} | null;
|
|
30
|
+
/** One song (a contiguous section of `__music__`). */
|
|
31
|
+
export interface ScoreSong {
|
|
32
|
+
/** A label for the report (default `song<N>`). */
|
|
33
|
+
name?: string;
|
|
34
|
+
/** SFX speed (ticks/row) for every channel. Default: the channels' shared `s<N>`, else 16. */
|
|
35
|
+
speed?: number;
|
|
36
|
+
/** Rows per pattern, 1..32 (default 32). Under 32, each SFX carries the LEN header. */
|
|
37
|
+
patternRows?: number;
|
|
38
|
+
/** Loop the song (default true). false = the last pattern stops the music. */
|
|
39
|
+
loop?: boolean;
|
|
40
|
+
/** The pattern (0-based, within the song) the loop returns to (default 0). */
|
|
41
|
+
loopFrom?: number;
|
|
42
|
+
/** Up to 4 channels (ch0..ch3); `null` or a missing entry = no channel. */
|
|
43
|
+
channels: ScoreChannel[];
|
|
44
|
+
}
|
|
45
|
+
/** A whole score: the songs, laid out in this order. */
|
|
46
|
+
export interface Score {
|
|
47
|
+
songs: ScoreSong[];
|
|
48
|
+
}
|
|
49
|
+
/** Machine-readable reason a score could not be planned. */
|
|
50
|
+
export type ScoreErrorCode = 'audio-score-parse-error' | 'audio-score-empty' | 'audio-score-bad-channel-count' | 'audio-score-bad-speed' | 'audio-score-speed-mismatch' | 'audio-score-bad-pattern-rows' | 'audio-score-bad-loop-from' | 'audio-score-loop-marker' | 'audio-score-too-many-patterns' | 'audio-score-no-free-slots' | 'audio-score-bad-slot-range';
|
|
51
|
+
/** A structured score refusal (mirrors {@link AudioMmlError}). */
|
|
52
|
+
export declare class ScoreError extends Error {
|
|
53
|
+
readonly code: ScoreErrorCode;
|
|
54
|
+
constructor(code: ScoreErrorCode, message: string);
|
|
55
|
+
}
|
|
56
|
+
/** What the planner needs to know about the cart it plans for. */
|
|
57
|
+
export interface ScoreCartState {
|
|
58
|
+
/** The current `__sfx__` rows, indexed by slot (missing = empty). */
|
|
59
|
+
sfxRows: readonly (string | undefined)[];
|
|
60
|
+
/** The SFX slots the cart's CURRENT `__music__` references (the song being replaced). */
|
|
61
|
+
musicRefs: ReadonlySet<number>;
|
|
62
|
+
}
|
|
63
|
+
/** Planner options. */
|
|
64
|
+
export interface ScorePlanOptions {
|
|
65
|
+
/** An explicit inclusive slot range the score OWNS (overwrite + clear unused). */
|
|
66
|
+
slots?: {
|
|
67
|
+
from: number;
|
|
68
|
+
to: number;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** One song as laid out in `__music__`. */
|
|
72
|
+
export interface PlannedSong {
|
|
73
|
+
name: string;
|
|
74
|
+
/** The first pattern index: play it with `music(startPattern)`. */
|
|
75
|
+
startPattern: number;
|
|
76
|
+
/** The last pattern index of the song. */
|
|
77
|
+
endPattern: number;
|
|
78
|
+
patterns: number;
|
|
79
|
+
speed: number;
|
|
80
|
+
patternRows: number;
|
|
81
|
+
loop: boolean;
|
|
82
|
+
/** The absolute pattern index the loop returns to (when `loop`). */
|
|
83
|
+
loopStartPattern: number | undefined;
|
|
84
|
+
/** Play length of one pass through the song, in seconds. */
|
|
85
|
+
seconds: number;
|
|
86
|
+
}
|
|
87
|
+
/** One SFX slot the plan writes. */
|
|
88
|
+
export interface PlannedSlot {
|
|
89
|
+
slot: number;
|
|
90
|
+
row: string;
|
|
91
|
+
/** Where the SFX is used, e.g. `title p0 ch1` (pattern index is within the song). */
|
|
92
|
+
uses: string[];
|
|
93
|
+
/** Whether the slot held DIFFERENT, non-empty content before (an overwrite). */
|
|
94
|
+
overwrote: boolean;
|
|
95
|
+
}
|
|
96
|
+
/** The plan: what to write into the cart, and the report. */
|
|
97
|
+
export interface ScorePlan {
|
|
98
|
+
songs: PlannedSong[];
|
|
99
|
+
slots: PlannedSlot[];
|
|
100
|
+
/** Owned slots the score does not use, cleared to empty (only with a range). */
|
|
101
|
+
cleared: number[];
|
|
102
|
+
/** The `__music__` section body. */
|
|
103
|
+
musicSection: string;
|
|
104
|
+
/** Total patterns written. */
|
|
105
|
+
patterns: number;
|
|
106
|
+
/** Advisory warnings. */
|
|
107
|
+
warnings: string[];
|
|
108
|
+
}
|
|
109
|
+
/** An all-zero `__sfx__` row: the canonical empty slot. */
|
|
110
|
+
export declare const EMPTY_SFX_ROW: string;
|
|
111
|
+
/** Whether an `__sfx__` row has no notes (its 32 note slots are all zero). */
|
|
112
|
+
export declare function isFreeSfxRow(row: string | undefined): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Parses + validates a score from JSON-ish input. Accepts `{songs:[...]}` or a
|
|
115
|
+
* single song object (`{channels:[...]}`) as shorthand.
|
|
116
|
+
*
|
|
117
|
+
* @throws {ScoreError} `audio-score-parse-error` for a shape it cannot read.
|
|
118
|
+
*/
|
|
119
|
+
export declare function readScore(raw: unknown): Score;
|
|
120
|
+
/**
|
|
121
|
+
* Plans a score against a cart's current audio (ADR-0020). Pure.
|
|
122
|
+
*
|
|
123
|
+
* @throws {ScoreError} a malformed score, a speed/loop conflict, more than 64
|
|
124
|
+
* patterns, or not enough slots.
|
|
125
|
+
* @throws {AudioMmlError} a channel's MML is invalid (message names song+channel).
|
|
126
|
+
*/
|
|
127
|
+
export declare function planScore(score: Score, cart: ScoreCartState, options?: ScorePlanOptions): ScorePlan;
|
|
128
|
+
/** Formats slot numbers compactly: `16-20,25`. */
|
|
129
|
+
export declare function formatSlots(slots: readonly number[]): string;
|
|
130
|
+
/**
|
|
131
|
+
* Parses a slot list like `5`, `16-63` or `3,5,16-20` into sorted unique slots.
|
|
132
|
+
* Returns `undefined` when the text is not a valid list within 0..63.
|
|
133
|
+
*/
|
|
134
|
+
export declare function parseSlotList(text: string): number[] | undefined;
|
|
135
|
+
/** The SFX slots a `__music__` body references (on channels that are not off). */
|
|
136
|
+
export declare function musicSfxRefs(body: string | undefined): Set<number>;
|
|
137
|
+
//# sourceMappingURL=score.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.d.ts","sourceRoot":"","sources":["../../../src/engine/audio/score.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `engine/audio`: the score to `__sfx__` + `__music__` planner (ADR-0020).
|
|
3
|
+
*
|
|
4
|
+
* A SCORE is one or more SONGS (sections of `__music__`, e.g. a title song and
|
|
5
|
+
* an in-game song), each up to 4 channels of picopilot-MML of ANY length. The
|
|
6
|
+
* planner does the bookkeeping an author used to do by hand (the conquest
|
|
7
|
+
* dogfood needed a 250-line script for it):
|
|
8
|
+
*
|
|
9
|
+
* 1. parse every channel into its tracker-row stream ({@link mmlToRows});
|
|
10
|
+
* 2. cut all channels of a song at the SAME row boundaries into patterns of
|
|
11
|
+
* `patternRows` rows (default 32), so channels stay aligned;
|
|
12
|
+
* 3. a channel that is silent for a whole pattern becomes `null` (channel OFF),
|
|
13
|
+
* not a silent SFX; a pattern silent on EVERY channel keeps one silent SFX on
|
|
14
|
+
* channel 0 so it still lasts its length;
|
|
15
|
+
* 4. de-duplicate identical SFX rows across the whole score;
|
|
16
|
+
* 5. allocate slots: free (note-less) slots only, from 63 downwards, unless the
|
|
17
|
+
* caller names an explicit slot RANGE, which the score then OWNS (it may
|
|
18
|
+
* overwrite any slot in it and clears the slots in it it does not use);
|
|
19
|
+
* 6. lay songs out back to back in `__music__`, with loop / stop flags.
|
|
20
|
+
*
|
|
21
|
+
* Pure: cart-free inputs in, a plan out. The `music from-score` command applies
|
|
22
|
+
* the plan through the cart model (only owned `__sfx__` slots + `__music__`
|
|
23
|
+
* change).
|
|
24
|
+
*/
|
|
25
|
+
import { CHANNEL_OFF_BIT, patternsToMusic, MUSIC_PATTERN_MAX, } from './music.js';
|
|
26
|
+
import { AudioMmlError, DEFAULT_SPEED, emitSfxRow, mmlToRows, SFX_MAX_ROWS, SFX_ROW_LENGTH, } from './sfx.js';
|
|
27
|
+
/** The number of SFX slots (0..63). */
|
|
28
|
+
const SLOT_COUNT = 64;
|
|
29
|
+
/** PICO-8 SFX ticks per second (speed 1 = 1/120 s per row). */
|
|
30
|
+
const TICKS_PER_SECOND = 120;
|
|
31
|
+
/** A structured score refusal (mirrors {@link AudioMmlError}). */
|
|
32
|
+
export class ScoreError extends Error {
|
|
33
|
+
code;
|
|
34
|
+
constructor(code, message) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = 'ScoreError';
|
|
37
|
+
this.code = code;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** An all-zero `__sfx__` row: the canonical empty slot. */
|
|
41
|
+
export const EMPTY_SFX_ROW = '0'.repeat(SFX_ROW_LENGTH);
|
|
42
|
+
/** Whether an `__sfx__` row has no notes (its 32 note slots are all zero). */
|
|
43
|
+
export function isFreeSfxRow(row) {
|
|
44
|
+
return row === undefined || /^0*$/.test(row.slice(8));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parses + validates a score from JSON-ish input. Accepts `{songs:[...]}` or a
|
|
48
|
+
* single song object (`{channels:[...]}`) as shorthand.
|
|
49
|
+
*
|
|
50
|
+
* @throws {ScoreError} `audio-score-parse-error` for a shape it cannot read.
|
|
51
|
+
*/
|
|
52
|
+
export function readScore(raw) {
|
|
53
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
54
|
+
throw new ScoreError('audio-score-parse-error', 'a score is a JSON object: {"songs":[{"name":"title","speed":16,"channels":["<mml>","<mml>",null,null]}]} (or one song object with "channels").');
|
|
55
|
+
}
|
|
56
|
+
const obj = raw;
|
|
57
|
+
const songs = Array.isArray(obj.songs)
|
|
58
|
+
? obj.songs
|
|
59
|
+
: Array.isArray(obj.channels)
|
|
60
|
+
? [obj]
|
|
61
|
+
: undefined;
|
|
62
|
+
if (songs === undefined) {
|
|
63
|
+
throw new ScoreError('audio-score-parse-error', 'a score needs "songs" (an array of songs) or, for one song, "channels".');
|
|
64
|
+
}
|
|
65
|
+
if (songs.length === 0) {
|
|
66
|
+
throw new ScoreError('audio-score-empty', 'the score has no songs.');
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
songs: songs.map((s, i) => {
|
|
70
|
+
if (s === null ||
|
|
71
|
+
typeof s !== 'object' ||
|
|
72
|
+
!Array.isArray(s.channels)) {
|
|
73
|
+
throw new ScoreError('audio-score-parse-error', `song ${i} must be an object with a "channels" array (up to 4 MML strings, {"mml","filters"} objects, or null).`);
|
|
74
|
+
}
|
|
75
|
+
const song = s;
|
|
76
|
+
if (song.loop !== undefined && typeof song.loop !== 'boolean') {
|
|
77
|
+
throw new ScoreError('audio-score-parse-error', `song ${i} "loop" must be true or false, got ${JSON.stringify(song.loop)}.`);
|
|
78
|
+
}
|
|
79
|
+
for (const [c, ch] of song.channels.entries()) {
|
|
80
|
+
const ok = ch === null ||
|
|
81
|
+
typeof ch === 'string' ||
|
|
82
|
+
(typeof ch === 'object' &&
|
|
83
|
+
typeof ch.mml === 'string' &&
|
|
84
|
+
(ch.filters === undefined ||
|
|
85
|
+
typeof ch.filters === 'string'));
|
|
86
|
+
const filters = ch !== null && typeof ch === 'object'
|
|
87
|
+
? ch.filters
|
|
88
|
+
: undefined;
|
|
89
|
+
if (ok &&
|
|
90
|
+
filters !== undefined &&
|
|
91
|
+
!/^\s*(![a-z]+\d?\s*)*$/i.test(filters)) {
|
|
92
|
+
throw new ScoreError('audio-score-parse-error', `song ${i} channel ${c} "filters" may only hold ! filter directives (e.g. "!dampen !reverb"), got "${filters}". Put notes and modal settings in "mml".`);
|
|
93
|
+
}
|
|
94
|
+
if (!ok) {
|
|
95
|
+
throw new ScoreError('audio-score-parse-error', `song ${i} channel ${c} must be an MML string, {"mml": "...", "filters": "!dampen"}, or null.`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return song;
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** Whether a run of rows is silent (every row at volume 0). */
|
|
103
|
+
function isSilent(notes) {
|
|
104
|
+
return notes.every((n) => n.volume === 0);
|
|
105
|
+
}
|
|
106
|
+
/** A silent row (the padding past a channel's end). */
|
|
107
|
+
const SILENT = { pitch: 0, waveform: 0, volume: 0, effect: 0 };
|
|
108
|
+
/**
|
|
109
|
+
* Plans a score against a cart's current audio (ADR-0020). Pure.
|
|
110
|
+
*
|
|
111
|
+
* @throws {ScoreError} a malformed score, a speed/loop conflict, more than 64
|
|
112
|
+
* patterns, or not enough slots.
|
|
113
|
+
* @throws {AudioMmlError} a channel's MML is invalid (message names song+channel).
|
|
114
|
+
*/
|
|
115
|
+
export function planScore(score, cart, options = {}) {
|
|
116
|
+
const warnings = [];
|
|
117
|
+
const allPatterns = [];
|
|
118
|
+
const songs = [];
|
|
119
|
+
/** Unique SFX rows, in first-use order, with their uses. */
|
|
120
|
+
const unique = new Map();
|
|
121
|
+
/** Per pattern+channel, the unique row it plays (resolved to a slot later). */
|
|
122
|
+
const channelRows = [];
|
|
123
|
+
let usesCustomInstrument = false;
|
|
124
|
+
for (const [si, song] of score.songs.entries()) {
|
|
125
|
+
const name = song.name ?? `song${si}`;
|
|
126
|
+
if (song.channels.length === 0 || song.channels.length > 4) {
|
|
127
|
+
throw new ScoreError('audio-score-bad-channel-count', `song '${name}' has ${song.channels.length} channels; PICO-8 music has 1 to 4 (ch0..ch3).`);
|
|
128
|
+
}
|
|
129
|
+
const patternRows = song.patternRows ?? SFX_MAX_ROWS;
|
|
130
|
+
if (!Number.isInteger(patternRows) ||
|
|
131
|
+
patternRows < 1 ||
|
|
132
|
+
patternRows > SFX_MAX_ROWS) {
|
|
133
|
+
throw new ScoreError('audio-score-bad-pattern-rows', `song '${name}' patternRows ${patternRows} is not a whole number in 1..${SFX_MAX_ROWS} (one SFX holds 32 rows).`);
|
|
134
|
+
}
|
|
135
|
+
// Parse every channel (no 32-row cap: the planner cuts them).
|
|
136
|
+
const parsed = [];
|
|
137
|
+
const speeds = new Set();
|
|
138
|
+
for (const [c, ch] of song.channels.entries()) {
|
|
139
|
+
if (ch === null || ch === undefined) {
|
|
140
|
+
parsed.push(null);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const text = typeof ch === 'string' ? ch : `${ch.filters ?? ''} ${ch.mml}`;
|
|
144
|
+
let rows;
|
|
145
|
+
try {
|
|
146
|
+
rows = mmlToRows(text, { maxRows: Number.POSITIVE_INFINITY });
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
if (e instanceof AudioMmlError) {
|
|
150
|
+
throw new AudioMmlError(e.code, `song '${name}' channel ${c}: ${e.message}`);
|
|
151
|
+
}
|
|
152
|
+
throw e;
|
|
153
|
+
}
|
|
154
|
+
if (rows.loopStartRow !== undefined || rows.loopEndRow !== undefined) {
|
|
155
|
+
throw new ScoreError('audio-score-loop-marker', `song '${name}' channel ${c} uses a '{'/'}' loop marker. In a score, looping is per SONG ("loop", "loopFrom"), not per SFX; remove the markers.`);
|
|
156
|
+
}
|
|
157
|
+
if (rows.speedSet)
|
|
158
|
+
speeds.add(rows.speed);
|
|
159
|
+
if (rows.notes.some((n) => n.waveform >= 8 && n.volume > 0)) {
|
|
160
|
+
usesCustomInstrument = true;
|
|
161
|
+
}
|
|
162
|
+
parsed.push({ notes: rows.notes, filters: rows.filters });
|
|
163
|
+
}
|
|
164
|
+
let speed;
|
|
165
|
+
if (song.speed !== undefined) {
|
|
166
|
+
if (!Number.isInteger(song.speed) || song.speed < 1 || song.speed > 255) {
|
|
167
|
+
throw new ScoreError('audio-score-bad-speed', `song '${name}' speed ${song.speed} is not a whole number in 1..255 (ticks per row).`);
|
|
168
|
+
}
|
|
169
|
+
speed = song.speed;
|
|
170
|
+
const other = [...speeds].find((s) => s !== speed);
|
|
171
|
+
if (other !== undefined) {
|
|
172
|
+
throw new ScoreError('audio-score-speed-mismatch', `song '${name}' has speed ${speed} but a channel sets s${other}. Every channel of a song shares ONE speed (patterns must stay aligned); drop the s<N> from the channel MML.`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else if (speeds.size > 1) {
|
|
176
|
+
throw new ScoreError('audio-score-speed-mismatch', `song '${name}' channels set different speeds (${[...speeds].map((s) => `s${s}`).join(', ')}). Every channel of a song shares ONE speed; set "speed" on the song instead.`);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
speed = speeds.size === 1 ? [...speeds][0] : DEFAULT_SPEED;
|
|
180
|
+
}
|
|
181
|
+
const totalRows = Math.max(0, ...parsed.map((p) => p?.notes.length ?? 0));
|
|
182
|
+
if (totalRows === 0) {
|
|
183
|
+
throw new ScoreError('audio-score-empty', `song '${name}' has no rows on any channel.`);
|
|
184
|
+
}
|
|
185
|
+
const count = Math.ceil(totalRows / patternRows);
|
|
186
|
+
for (const [c, p] of parsed.entries()) {
|
|
187
|
+
if (p !== null && p.notes.length < totalRows) {
|
|
188
|
+
warnings.push(`song '${name}' channel ${c} is ${p.notes.length} rows, shorter than the song (${totalRows}); it is padded with silence.`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (totalRows % patternRows !== 0) {
|
|
192
|
+
warnings.push(`song '${name}' is ${totalRows} rows, not a multiple of ${patternRows}; the last pattern is padded with ${count * patternRows - totalRows} silent rows.`);
|
|
193
|
+
}
|
|
194
|
+
const loop = song.loop ?? true;
|
|
195
|
+
const loopFrom = song.loopFrom ?? 0;
|
|
196
|
+
if (!Number.isInteger(loopFrom) || loopFrom < 0 || loopFrom >= count) {
|
|
197
|
+
throw new ScoreError('audio-score-bad-loop-from', `song '${name}' loopFrom ${loopFrom} is not a pattern of the song (0..${count - 1}).`);
|
|
198
|
+
}
|
|
199
|
+
const start = allPatterns.length;
|
|
200
|
+
const lenHeader = patternRows < SFX_MAX_ROWS ? patternRows : 0;
|
|
201
|
+
for (let p = 0; p < count; p++) {
|
|
202
|
+
const refs = [];
|
|
203
|
+
for (let c = 0; c < 4; c++) {
|
|
204
|
+
const ch = parsed[c];
|
|
205
|
+
if (ch === null || ch === undefined) {
|
|
206
|
+
refs.push(null);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const chunk = ch.notes.slice(p * patternRows, (p + 1) * patternRows);
|
|
210
|
+
if (isSilent(chunk)) {
|
|
211
|
+
refs.push(null);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
while (chunk.length < patternRows)
|
|
215
|
+
chunk.push(SILENT);
|
|
216
|
+
const row = emitSfxRow({
|
|
217
|
+
filters: ch.filters,
|
|
218
|
+
speed,
|
|
219
|
+
loopStart: lenHeader,
|
|
220
|
+
loopEnd: 0,
|
|
221
|
+
notes: chunk,
|
|
222
|
+
});
|
|
223
|
+
refs.push(row);
|
|
224
|
+
const uses = unique.get(row) ?? [];
|
|
225
|
+
uses.push(`${name} p${p} ch${c}`);
|
|
226
|
+
unique.set(row, uses);
|
|
227
|
+
}
|
|
228
|
+
if (!refs.some((r) => r !== null)) {
|
|
229
|
+
// A pattern silent on every channel still has to LAST its length:
|
|
230
|
+
// keep one silent SFX on channel 0 (PICO-8 times a pattern by its
|
|
231
|
+
// left-most playing channel).
|
|
232
|
+
const rest = emitSfxRow({
|
|
233
|
+
speed,
|
|
234
|
+
filters: 0,
|
|
235
|
+
loopStart: lenHeader,
|
|
236
|
+
loopEnd: 0,
|
|
237
|
+
notes: [],
|
|
238
|
+
});
|
|
239
|
+
refs[0] = rest;
|
|
240
|
+
const uses = unique.get(rest) ?? [];
|
|
241
|
+
uses.push(`${name} p${p} ch0 (rest)`);
|
|
242
|
+
unique.set(rest, uses);
|
|
243
|
+
}
|
|
244
|
+
channelRows.push(refs);
|
|
245
|
+
allPatterns.push({
|
|
246
|
+
channels: [null, null, null, null],
|
|
247
|
+
...(loop && p === loopFrom ? { loopStart: true } : {}),
|
|
248
|
+
...(loop && p === count - 1 ? { loopBack: true } : {}),
|
|
249
|
+
...(!loop && p === count - 1 ? { stop: true } : {}),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
songs.push({
|
|
253
|
+
name,
|
|
254
|
+
startPattern: start,
|
|
255
|
+
endPattern: start + count - 1,
|
|
256
|
+
patterns: count,
|
|
257
|
+
speed,
|
|
258
|
+
patternRows,
|
|
259
|
+
loop,
|
|
260
|
+
loopStartPattern: loop ? start + loopFrom : undefined,
|
|
261
|
+
seconds: round2((count * patternRows * speed) / TICKS_PER_SECOND),
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
if (allPatterns.length > MUSIC_PATTERN_MAX) {
|
|
265
|
+
throw new ScoreError('audio-score-too-many-patterns', `the score needs ${allPatterns.length} patterns; __music__ holds ${MUSIC_PATTERN_MAX}. Shorten a song, or reuse material (identical patterns still take a pattern each).`);
|
|
266
|
+
}
|
|
267
|
+
// --- slot allocation --------------------------------------------------------
|
|
268
|
+
const range = options.slots;
|
|
269
|
+
if (range !== undefined) {
|
|
270
|
+
if (!Number.isInteger(range.from) ||
|
|
271
|
+
!Number.isInteger(range.to) ||
|
|
272
|
+
range.from < 0 ||
|
|
273
|
+
range.to > SLOT_COUNT - 1 ||
|
|
274
|
+
range.from > range.to) {
|
|
275
|
+
throw new ScoreError('audio-score-bad-slot-range', `slot range ${range.from}-${range.to} is not within 0-63 (from <= to).`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Stable allocation: an SFX whose exact row already sits in a usable slot
|
|
279
|
+
// keeps that slot, so re-running an unchanged score is a byte-identical
|
|
280
|
+
// no-op. With a range, "usable" is the range; without one, it is ANY slot
|
|
281
|
+
// (reusing an identical row overwrites nothing), so default-mode re-runs do
|
|
282
|
+
// not leak copies of the previous song into fresh slots.
|
|
283
|
+
const usable = (s) => range !== undefined
|
|
284
|
+
? s >= range.from && s <= range.to
|
|
285
|
+
: !(usesCustomInstrument && s < 8); // @8-@f read SFX 0..7
|
|
286
|
+
const slotOf = new Map();
|
|
287
|
+
const taken = new Set();
|
|
288
|
+
for (const row of unique.keys()) {
|
|
289
|
+
for (let s = SLOT_COUNT - 1; s >= 0; s--) {
|
|
290
|
+
if (!taken.has(s) && usable(s) && cart.sfxRows[s] === row) {
|
|
291
|
+
slotOf.set(row, s);
|
|
292
|
+
taken.add(s);
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const candidates = [];
|
|
298
|
+
if (range !== undefined) {
|
|
299
|
+
for (let s = range.from; s <= range.to; s++)
|
|
300
|
+
candidates.push(s);
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
// Free slots only, from the top down: game SFX are usually authored from
|
|
304
|
+
// slot 0 up, so music fills from 63 down and the two meet in the middle.
|
|
305
|
+
for (let s = SLOT_COUNT - 1; s >= 0; s--) {
|
|
306
|
+
if (usable(s) && isFreeSfxRow(cart.sfxRows[s]))
|
|
307
|
+
candidates.push(s);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const open = candidates.filter((c) => !taken.has(c));
|
|
311
|
+
const needed = unique.size - slotOf.size;
|
|
312
|
+
if (needed > open.length) {
|
|
313
|
+
throw new ScoreError('audio-score-no-free-slots', range !== undefined
|
|
314
|
+
? `the score needs ${unique.size} distinct SFX but the slot range ${range.from}-${range.to} has ${candidates.length}. Widen the range or shorten/simplify the score.`
|
|
315
|
+
: `the score needs ${needed} more SFX slots but the cart has ${open.length} free. Pass --slots <from-to> to give the music a range it owns (it overwrites that range), clear old slots with \`sfx clear\`, or shorten the score.`);
|
|
316
|
+
}
|
|
317
|
+
if (range !== undefined && usesCustomInstrument && range.from < 8) {
|
|
318
|
+
warnings.push(`the score uses custom instruments (@8-@f = SFX 0-7) and the slot range ${range.from}-${range.to} overlaps slots 0-7: music written there would replace those instruments.`);
|
|
319
|
+
}
|
|
320
|
+
for (const row of unique.keys()) {
|
|
321
|
+
if (slotOf.has(row))
|
|
322
|
+
continue;
|
|
323
|
+
const s = open.shift();
|
|
324
|
+
slotOf.set(row, s);
|
|
325
|
+
taken.add(s);
|
|
326
|
+
}
|
|
327
|
+
const slots = [...unique.entries()]
|
|
328
|
+
.map(([row, uses]) => {
|
|
329
|
+
const slot = slotOf.get(row);
|
|
330
|
+
const before = cart.sfxRows[slot];
|
|
331
|
+
return {
|
|
332
|
+
slot,
|
|
333
|
+
row,
|
|
334
|
+
uses,
|
|
335
|
+
overwrote: !isFreeSfxRow(before) && before !== row,
|
|
336
|
+
};
|
|
337
|
+
})
|
|
338
|
+
.sort((a, b) => a.slot - b.slot);
|
|
339
|
+
const cleared = range === undefined
|
|
340
|
+
? []
|
|
341
|
+
: candidates.filter((s) => !taken.has(s) && !isFreeSfxRow(cart.sfxRows[s]));
|
|
342
|
+
for (const [p, refs] of channelRows.entries()) {
|
|
343
|
+
allPatterns[p].channels = refs.map((r) => r === null ? null : slotOf.get(r));
|
|
344
|
+
}
|
|
345
|
+
// Slots the previous __music__ used that nothing references any more, and
|
|
346
|
+
// that we neither rewrote nor cleared: leftovers of the replaced song.
|
|
347
|
+
const nowUsed = new Set(slots.map((s) => s.slot));
|
|
348
|
+
const orphans = [...cart.musicRefs]
|
|
349
|
+
.filter((s) => !nowUsed.has(s) &&
|
|
350
|
+
!cleared.includes(s) &&
|
|
351
|
+
!isFreeSfxRow(cart.sfxRows[s]))
|
|
352
|
+
.sort((a, b) => a - b);
|
|
353
|
+
if (orphans.length > 0) {
|
|
354
|
+
warnings.push(`SFX ${formatSlots(orphans)} were used by the previous __music__ and are no longer referenced; they still hold the old song's notes. Clear them with \`sfx clear ${formatSlots(orphans)}\`, or re-run with --slots <from-to> so the score owns (and clears) its range.`);
|
|
355
|
+
}
|
|
356
|
+
const music = patternsToMusic(allPatterns);
|
|
357
|
+
warnings.push(...music.warnings.map((w) => w.message));
|
|
358
|
+
return {
|
|
359
|
+
songs,
|
|
360
|
+
slots,
|
|
361
|
+
cleared,
|
|
362
|
+
musicSection: music.section,
|
|
363
|
+
patterns: allPatterns.length,
|
|
364
|
+
warnings,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
/** Rounds to 2 decimals. */
|
|
368
|
+
function round2(n) {
|
|
369
|
+
return Math.round(n * 100) / 100;
|
|
370
|
+
}
|
|
371
|
+
/** Formats slot numbers compactly: `16-20,25`. */
|
|
372
|
+
export function formatSlots(slots) {
|
|
373
|
+
const sorted = [...slots].sort((a, b) => a - b);
|
|
374
|
+
const parts = [];
|
|
375
|
+
for (let i = 0; i < sorted.length;) {
|
|
376
|
+
let j = i;
|
|
377
|
+
while (j + 1 < sorted.length && sorted[j + 1] === sorted[j] + 1)
|
|
378
|
+
j++;
|
|
379
|
+
parts.push(i === j ? `${sorted[i]}` : `${sorted[i]}-${sorted[j]}`);
|
|
380
|
+
i = j + 1;
|
|
381
|
+
}
|
|
382
|
+
return parts.join(',');
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Parses a slot list like `5`, `16-63` or `3,5,16-20` into sorted unique slots.
|
|
386
|
+
* Returns `undefined` when the text is not a valid list within 0..63.
|
|
387
|
+
*/
|
|
388
|
+
export function parseSlotList(text) {
|
|
389
|
+
const out = new Set();
|
|
390
|
+
for (const part of text.split(',')) {
|
|
391
|
+
const m = /^\s*(\d+)\s*(?:-\s*(\d+)\s*)?$/.exec(part);
|
|
392
|
+
if (m === null)
|
|
393
|
+
return undefined;
|
|
394
|
+
const from = Number(m[1]);
|
|
395
|
+
const to = m[2] === undefined ? from : Number(m[2]);
|
|
396
|
+
if (from > to || to > SLOT_COUNT - 1)
|
|
397
|
+
return undefined;
|
|
398
|
+
for (let s = from; s <= to; s++)
|
|
399
|
+
out.add(s);
|
|
400
|
+
}
|
|
401
|
+
return [...out].sort((a, b) => a - b);
|
|
402
|
+
}
|
|
403
|
+
/** The SFX slots a `__music__` body references (on channels that are not off). */
|
|
404
|
+
export function musicSfxRefs(body) {
|
|
405
|
+
const refs = new Set();
|
|
406
|
+
if (body === undefined)
|
|
407
|
+
return refs;
|
|
408
|
+
for (const line of body.split(/\r?\n/)) {
|
|
409
|
+
const m = /^[0-9a-f]{2} ([0-9a-f]{8})$/i.exec(line.trim());
|
|
410
|
+
if (m === null)
|
|
411
|
+
continue;
|
|
412
|
+
for (let c = 0; c < 4; c++) {
|
|
413
|
+
const b = Number.parseInt(m[1].slice(c * 2, c * 2 + 2), 16);
|
|
414
|
+
if ((b & CHANNEL_OFF_BIT) === 0)
|
|
415
|
+
refs.add(b & 0x3f);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return refs;
|
|
419
|
+
}
|
|
420
|
+
//# sourceMappingURL=score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.js","sourceRoot":"","sources":["../../../src/engine/audio/score.ts"],"names":[],"mappings":"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"}
|
|
@@ -31,4 +31,11 @@ export declare function mergeSfxRow(cart: Cart, slot: number, row: string): void
|
|
|
31
31
|
* `__music__`/dominant newline so the round-trip stays stable.
|
|
32
32
|
*/
|
|
33
33
|
export declare function setMusicSection(cart: Cart, body: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Reads a cart's `__sfx__` section into one entry per slot (0..63); a slot past
|
|
36
|
+
* the end of the (trimmed) section is `undefined` (empty).
|
|
37
|
+
*/
|
|
38
|
+
export declare function readSfxRows(cart: Cart): (string | undefined)[];
|
|
39
|
+
/** Clears SFX slot `slot` back to empty (the all-zero row), via {@link mergeSfxRow}. */
|
|
40
|
+
export declare function clearSfxSlot(cart: Cart, slot: number): void;
|
|
34
41
|
//# sourceMappingURL=section.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section.d.ts","sourceRoot":"","sources":["../../../src/engine/audio/section.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAC;AAG3C;;GAEG;AACH,eAAO,MAAM,cAAc,KAAK,CAAC;AAKjC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAgCvE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAO9D"}
|
|
1
|
+
{"version":3,"file":"section.d.ts","sourceRoot":"","sources":["../../../src/engine/audio/section.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAC;AAG3C;;GAEG;AACH,eAAO,MAAM,cAAc,KAAK,CAAC;AAKjC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAgCvE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAO9D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAQ9D;AAED,wFAAwF;AACxF,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE3D"}
|
|
@@ -64,4 +64,21 @@ export function setMusicSection(cart, body) {
|
|
|
64
64
|
const normalised = crlf ? body.replace(/\n/g, '\r\n') : body;
|
|
65
65
|
cart.setSection('music', normalised);
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Reads a cart's `__sfx__` section into one entry per slot (0..63); a slot past
|
|
69
|
+
* the end of the (trimmed) section is `undefined` (empty).
|
|
70
|
+
*/
|
|
71
|
+
export function readSfxRows(cart) {
|
|
72
|
+
const body = cart.getSection('sfx');
|
|
73
|
+
const rows = body === undefined
|
|
74
|
+
? []
|
|
75
|
+
: body.split(/\r?\n/).filter((line) => line.length > 0);
|
|
76
|
+
while (rows.length < SFX_SLOT_COUNT)
|
|
77
|
+
rows.push(undefined);
|
|
78
|
+
return rows.slice(0, SFX_SLOT_COUNT);
|
|
79
|
+
}
|
|
80
|
+
/** Clears SFX slot `slot` back to empty (the all-zero row), via {@link mergeSfxRow}. */
|
|
81
|
+
export function clearSfxSlot(cart, slot) {
|
|
82
|
+
mergeSfxRow(cart, slot, EMPTY_ROW);
|
|
83
|
+
}
|
|
67
84
|
//# sourceMappingURL=section.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section.js","sourceRoot":"","sources":["../../../src/engine/audio/section.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,cAAc,EAAC,MAAM,UAAU,CAAC;AAExC;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAEjC,yEAAyE;AACzE,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;AAE7C;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CAAC,IAAU,EAAE,IAAY,EAAE,GAAW;IAChE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,cAAc,EAAE,CAAC;QACnE,MAAM,IAAI,UAAU,CACnB,YAAY,IAAI,oBAAoB,cAAc,GAAG,CAAC,EAAE,CACxD,CAAC;IACH,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;QACnC,MAAM,IAAI,UAAU,CACnB,mBAAmB,cAAc,mBAAmB,GAAG,CAAC,MAAM,EAAE,CAChE,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAExE,0EAA0E;IAC1E,MAAM,QAAQ,GACb,IAAI,KAAK,SAAS;QACjB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE1D,4EAA4E;IAC5E,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC9B,OAAO,IAAI,CAAC,MAAM,IAAI,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACjD,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;IAEjB,4EAA4E;IAC5E,2EAA2E;IAC3E,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,SAAS;QAAE,IAAI,CAAC,GAAG,EAAE,CAAC;IAE1E,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACtE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,IAAU,EAAE,IAAY;IACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAC1C,6EAA6E;IAC7E,2EAA2E;IAC3E,MAAM,IAAI,GAAG,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7D,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACtC,CAAC"}
|
|
1
|
+
{"version":3,"file":"section.js","sourceRoot":"","sources":["../../../src/engine/audio/section.ts"],"names":[],"mappings":"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"}
|