@clipkit/music-analysis 1.0.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/LICENSE +201 -0
- package/README.md +64 -0
- package/dist/analyze.d.ts +18 -0
- package/dist/analyze.d.ts.map +1 -0
- package/dist/analyze.js +54 -0
- package/dist/analyze.js.map +1 -0
- package/dist/beat-map.d.ts +66 -0
- package/dist/beat-map.d.ts.map +1 -0
- package/dist/beat-map.js +20 -0
- package/dist/beat-map.js.map +1 -0
- package/dist/decode-wav.d.ts +6 -0
- package/dist/decode-wav.d.ts.map +1 -0
- package/dist/decode-wav.js +72 -0
- package/dist/decode-wav.js.map +1 -0
- package/dist/dsp.d.ts +13 -0
- package/dist/dsp.d.ts.map +1 -0
- package/dist/dsp.js +295 -0
- package/dist/dsp.js.map +1 -0
- package/dist/fft.d.ts +8 -0
- package/dist/fft.d.ts.map +1 -0
- package/dist/fft.js +67 -0
- package/dist/fft.js.map +1 -0
- package/dist/grid.d.ts +21 -0
- package/dist/grid.d.ts.map +1 -0
- package/dist/grid.js +51 -0
- package/dist/grid.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +2 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +10 -0
- package/dist/node.js.map +1 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for describing the origin of the Work and
|
|
141
|
+
reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2026 Clipkit Contributors
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @clipkit/music-analysis
|
|
2
|
+
|
|
3
|
+
Analyze an audio file into a **beat map** — tempo, beat grid, downbeats,
|
|
4
|
+
transient onsets, and structural sections — so motion graphics can be synced to
|
|
5
|
+
music.
|
|
6
|
+
|
|
7
|
+
Think of it as an importer with the last step removed. The After Effects and
|
|
8
|
+
Lottie importers parse a foreign format into an IR and then *convert* that IR to
|
|
9
|
+
a Clipkit Source. This package parses audio into an IR — the `BeatMap` — and
|
|
10
|
+
**stops there**. It does not emit a Source.
|
|
11
|
+
|
|
12
|
+
## Where it sits
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
audio file ─▶ @clipkit/music-analysis ─▶ BeatMap ─▶ @clipkit/patterns helpers ─▶ keyframes
|
|
16
|
+
(analysis: churns) (stable (mapping: intent → exact
|
|
17
|
+
contract) times) + the AI's taste
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Three jobs, deliberately split:
|
|
21
|
+
|
|
22
|
+
1. **Analysis** (this package) — produce the facts: *where are the beats?*
|
|
23
|
+
2. **Mapping** (`@clipkit/patterns`) — turn intent into exact keyframes:
|
|
24
|
+
*punch the logo on each downbeat.*
|
|
25
|
+
3. **Taste** (the AI agent, via `@clipkit/mcp-server`) — decide *which* moment
|
|
26
|
+
gets *which* move, reading the beat map as authoring context.
|
|
27
|
+
|
|
28
|
+
## Not part of the protocol
|
|
29
|
+
|
|
30
|
+
The beat map is **authoring-time data**. The runtime never reads it. Consumers
|
|
31
|
+
bake it down to ordinary keyframes / pure expressions, so the rendered Source
|
|
32
|
+
stays a deterministic function of time with **no audio dependency** — same
|
|
33
|
+
document, same pixels, on every backend. Resist the urge to let an expression
|
|
34
|
+
sample an audio envelope at render time; bake to keyframes instead.
|
|
35
|
+
|
|
36
|
+
## Two tiers of sync
|
|
37
|
+
|
|
38
|
+
- **Tier 1 — tempo-parametric (pure).** Just `bpm` + `phase` drive a pure
|
|
39
|
+
expression that breathes on the beat (e.g.
|
|
40
|
+
`1 + 0.05 * max(0, sin(TAU * (bpm/60) * (t - phase)))`). ~70% of the feel,
|
|
41
|
+
zero new runtime concepts.
|
|
42
|
+
- **Tier 2 — event-baked (precise).** `downbeats` / `onsets` / `sections` place
|
|
43
|
+
accents and transitions on exact musical time.
|
|
44
|
+
|
|
45
|
+
## Status
|
|
46
|
+
|
|
47
|
+
Scaffold. The `BeatMap` contract (`src/beat-map.ts`) and the `analyzeAudio`
|
|
48
|
+
signature (`src/analyze.ts`) are the stable parts; the analyzer behind them is
|
|
49
|
+
where the experimentation happens. `analyzeAudio` currently returns an empty map
|
|
50
|
+
so the contract and downstream wiring can be built against a real type today.
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// analyzeAudio reads files (node:fs) → import it from the Node-only subpath.
|
|
56
|
+
import { analyzeAudio } from '@clipkit/music-analysis/node';
|
|
57
|
+
import type { BeatMap } from '@clipkit/music-analysis';
|
|
58
|
+
|
|
59
|
+
const map: BeatMap = await analyzeAudio('track.mp3');
|
|
60
|
+
// → feed to a patterns helper, or hand to an agent as authoring context
|
|
61
|
+
|
|
62
|
+
// Browser-safe helpers (beatGrid, decodeWav, analyzePcm, types) live on the
|
|
63
|
+
// main entry: import { beatGrid } from '@clipkit/music-analysis';
|
|
64
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BeatMap } from './beat-map.js';
|
|
2
|
+
export interface AnalyzeOptions {
|
|
3
|
+
/** Decoded mono (or multi-channel) PCM. When given, `source` is only a label
|
|
4
|
+
* and no decoding happens. */
|
|
5
|
+
samples?: Float32Array;
|
|
6
|
+
/** Sample rate of `samples`, Hz. Required when `samples` is given. */
|
|
7
|
+
sampleRate?: number;
|
|
8
|
+
/** Beats per bar for downbeat spacing. Default 4. */
|
|
9
|
+
beatsPerBar?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Analyze an audio file (or raw PCM) into a {@link BeatMap}: detected `bpm`,
|
|
13
|
+
* `phase`, the implied beat/downbeat grid, detected `onsets` (hits), and
|
|
14
|
+
* energy-based `sections`. WAV files decode directly; for other formats pass
|
|
15
|
+
* decoded `samples`/`sampleRate` via {@link AnalyzeOptions}.
|
|
16
|
+
*/
|
|
17
|
+
export declare function analyzeAudio(source: string, options?: AnalyzeOptions): Promise<BeatMap>;
|
|
18
|
+
//# sourceMappingURL=analyze.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAI7C,MAAM,WAAW,cAAc;IAC7B;mCAC+B;IAC/B,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAgBD;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CAqBlB"}
|
package/dist/analyze.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// analyzeAudio — audio in, beat map out. The real analyzer (see dsp.ts):
|
|
2
|
+
// decode → spectral-flux onsets + autocorrelation tempo/phase + energy-novelty
|
|
3
|
+
// sections. Deterministic: same bytes → same map.
|
|
4
|
+
//
|
|
5
|
+
// Decoding is WAV-only here (decode-wav.ts, no deps). For other containers
|
|
6
|
+
// (mp3/aac/…), decode upstream and pass `samples` + `sampleRate` via options —
|
|
7
|
+
// the DSP is container-agnostic. Tempo detection from raw audio lives here;
|
|
8
|
+
// when you already know the BPM, beatGrid() is the cheaper path.
|
|
9
|
+
import { analyzePcm } from './dsp.js';
|
|
10
|
+
import { decodeWav } from './decode-wav.js';
|
|
11
|
+
async function loadWav(source) {
|
|
12
|
+
let bytes;
|
|
13
|
+
if (/^https?:\/\//.test(source)) {
|
|
14
|
+
const res = await fetch(source);
|
|
15
|
+
if (!res.ok)
|
|
16
|
+
throw new Error(`analyzeAudio: fetch ${source} → ${res.status}`);
|
|
17
|
+
bytes = new Uint8Array(await res.arrayBuffer());
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
// Local path — Node only (authoring-time tool).
|
|
21
|
+
const { readFile } = await import('node:fs/promises');
|
|
22
|
+
bytes = new Uint8Array(await readFile(source));
|
|
23
|
+
}
|
|
24
|
+
return decodeWav(bytes);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Analyze an audio file (or raw PCM) into a {@link BeatMap}: detected `bpm`,
|
|
28
|
+
* `phase`, the implied beat/downbeat grid, detected `onsets` (hits), and
|
|
29
|
+
* energy-based `sections`. WAV files decode directly; for other formats pass
|
|
30
|
+
* decoded `samples`/`sampleRate` via {@link AnalyzeOptions}.
|
|
31
|
+
*/
|
|
32
|
+
export async function analyzeAudio(source, options = {}) {
|
|
33
|
+
let samples;
|
|
34
|
+
let sampleRate;
|
|
35
|
+
if (options.samples) {
|
|
36
|
+
samples = options.samples;
|
|
37
|
+
sampleRate = options.sampleRate ?? 44100;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
({ samples, sampleRate } = await loadWav(source));
|
|
41
|
+
}
|
|
42
|
+
const a = analyzePcm(samples, sampleRate, options.beatsPerBar ?? 4);
|
|
43
|
+
return {
|
|
44
|
+
source,
|
|
45
|
+
duration: Math.round((samples.length / sampleRate) * 1000) / 1000,
|
|
46
|
+
bpm: a.bpm,
|
|
47
|
+
phase: a.phase,
|
|
48
|
+
beats: a.beats,
|
|
49
|
+
downbeats: a.downbeats,
|
|
50
|
+
onsets: a.onsets,
|
|
51
|
+
sections: a.sections,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=analyze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,+EAA+E;AAC/E,kDAAkD;AAClD,EAAE;AACF,2EAA2E;AAC3E,+EAA+E;AAC/E,4EAA4E;AAC5E,iEAAiE;AAGjE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAY5C,KAAK,UAAU,OAAO,CAAC,MAAc;IACnC,IAAI,KAAiB,CAAC;IACtB,IAAI,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9E,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IAClD,CAAC;SAAM,CAAC;QACN,gDAAgD;QAChD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QACtD,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAc,EACd,UAA0B,EAAE;IAE5B,IAAI,OAAqB,CAAC;IAC1B,IAAI,UAAkB,CAAC;IACvB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC1B,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,KAAK,CAAC;IAC3C,CAAC;SAAM,CAAC;QACN,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IACpE,OAAO;QACL,MAAM;QACN,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI;QACjE,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,QAAQ,EAAE,CAAC,CAAC,QAAQ;KACrB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** A musical event detected in the audio, at a point in time. */
|
|
2
|
+
export interface Marker {
|
|
3
|
+
/** Time of the event, in seconds from the start of the audio. */
|
|
4
|
+
time: number;
|
|
5
|
+
/**
|
|
6
|
+
* Relative salience of the event, 0..1. For onsets this is transient
|
|
7
|
+
* strength; for beats/downbeats it is detection confidence. Authoring uses
|
|
8
|
+
* it to scale accent magnitude — strong hits get bigger moves.
|
|
9
|
+
*/
|
|
10
|
+
strength: number;
|
|
11
|
+
}
|
|
12
|
+
/** Frequency band an onset's energy is concentrated in. */
|
|
13
|
+
export type Band = 'low' | 'mid' | 'high';
|
|
14
|
+
/** A transient hit — a drum, a stab, a vocal onset. The "hit points" that
|
|
15
|
+
* discrete accents (punch, flash, shake) sync to. */
|
|
16
|
+
export interface Onset extends Marker {
|
|
17
|
+
/** Dominant band of the transient — lets authoring route kicks vs. hats to
|
|
18
|
+
* different motions (bass → scale pulse, highs → shimmer). */
|
|
19
|
+
band: Band;
|
|
20
|
+
}
|
|
21
|
+
/** A structural region of the track (intro / build / drop / verse / …). The
|
|
22
|
+
* unit at which scene changes and major reveals belong. */
|
|
23
|
+
export interface Section {
|
|
24
|
+
/** Start time, seconds. */
|
|
25
|
+
start: number;
|
|
26
|
+
/** End time, seconds (== next section's start, or track end). */
|
|
27
|
+
end: number;
|
|
28
|
+
/**
|
|
29
|
+
* Coarse label when the analyzer can infer one. Free-form on purpose — the
|
|
30
|
+
* analyzer may only know boundaries, not names — so authoring should treat an
|
|
31
|
+
* unknown label as "a boundary worth cutting on", not rely on the string.
|
|
32
|
+
*/
|
|
33
|
+
label?: string;
|
|
34
|
+
/** Mean energy of the region, 0..1. The jump between adjacent sections is the
|
|
35
|
+
* "drop" signal: a low region followed by a high one. */
|
|
36
|
+
energy: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The full analysis of one audio file. Start minimal — tempo + the event
|
|
40
|
+
* tracks below cover Tier-1 tempo-pulse and Tier-2 accents. Add sampled band
|
|
41
|
+
* envelopes (continuous drivers) only once a consumer needs them; an IR field
|
|
42
|
+
* nothing reads is just maintenance.
|
|
43
|
+
*/
|
|
44
|
+
export interface BeatMap {
|
|
45
|
+
/** Source audio URL or path this map was computed from. */
|
|
46
|
+
source: string;
|
|
47
|
+
/** Total duration of the audio, seconds. */
|
|
48
|
+
duration: number;
|
|
49
|
+
/** Estimated global tempo, beats per minute. */
|
|
50
|
+
bpm: number;
|
|
51
|
+
/**
|
|
52
|
+
* Time of the first beat (beat 0), seconds. With `bpm` this defines the whole
|
|
53
|
+
* grid: beat k ≈ phase + k·60/bpm. Tier-1 tempo-pulse needs only these two
|
|
54
|
+
* numbers — a pure expression can breathe on the beat from them alone.
|
|
55
|
+
*/
|
|
56
|
+
phase: number;
|
|
57
|
+
/** Every detected beat (the full grid, including downbeats). */
|
|
58
|
+
beats: Marker[];
|
|
59
|
+
/** Beats that fall on bar boundaries — where the BIG moves go. */
|
|
60
|
+
downbeats: Marker[];
|
|
61
|
+
/** Transient hits, for discrete accents. */
|
|
62
|
+
onsets: Onset[];
|
|
63
|
+
/** Structural regions, for scene changes / major reveals. */
|
|
64
|
+
sections: Section[];
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=beat-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"beat-map.d.ts","sourceRoot":"","sources":["../src/beat-map.ts"],"names":[],"mappings":"AAmBA,iEAAiE;AACjE,MAAM,WAAW,MAAM;IACrB,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,2DAA2D;AAC3D,MAAM,MAAM,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAE1C;sDACsD;AACtD,MAAM,WAAW,KAAM,SAAQ,MAAM;IACnC;mEAC+D;IAC/D,IAAI,EAAE,IAAI,CAAC;CACZ;AAED;4DAC4D;AAC5D,MAAM,WAAW,OAAO;IACtB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;8DAC0D;IAC1D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IAEjB,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd,gEAAgE;IAChE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,kEAAkE;IAClE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,4CAA4C;IAC5C,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,6DAA6D;IAC7D,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB"}
|
package/dist/beat-map.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// The beat map — the intermediate artifact this package produces and the
|
|
2
|
+
// stable seam between audio analysis and motion authoring.
|
|
3
|
+
//
|
|
4
|
+
// Analysis (which DSP/ML approach, JS vs. a server-side analyzer) will churn;
|
|
5
|
+
// the motion side (AI agents, @clipkit/patterns helpers) reads ONLY this shape.
|
|
6
|
+
// Keep the two decoupled the way the importers keep `ir-types` between their
|
|
7
|
+
// parser and converter, so the analyzer can be rewritten without touching the
|
|
8
|
+
// authoring layer.
|
|
9
|
+
//
|
|
10
|
+
// This is AUTHORING-TIME data. It is deliberately NOT part of the Clipkit
|
|
11
|
+
// Protocol: the runtime never reads a beat map. A patterns helper consumes it
|
|
12
|
+
// and emits ordinary keyframes/expressions, so the rendered Source stays a pure,
|
|
13
|
+
// deterministic function of time with no audio dependency.
|
|
14
|
+
//
|
|
15
|
+
// ── Units ──────────────────────────────────────────────────────────────────
|
|
16
|
+
// ALL times are in SECONDS, matching the protocol's element `time` and keyframe
|
|
17
|
+
// `time`. (The AE importer's IR is in frames because AE is; here we line up with
|
|
18
|
+
// the Source directly, so a marker time drops straight into a keyframe.)
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=beat-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"beat-map.js","sourceRoot":"","sources":["../src/beat-map.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,2DAA2D;AAC3D,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,6EAA6E;AAC7E,8EAA8E;AAC9E,mBAAmB;AACnB,EAAE;AACF,0EAA0E;AAC1E,8EAA8E;AAC9E,iFAAiF;AACjF,2DAA2D;AAC3D,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,iFAAiF;AACjF,yEAAyE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode-wav.d.ts","sourceRoot":"","sources":["../src/decode-wav.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAQD,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,CA0DzD"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Minimal WAV decoder → mono Float32 samples. No dependencies; handles PCM
|
|
2
|
+
// (8/16/24/32-bit) and 32-bit float, any channel count (downmixed to mono).
|
|
3
|
+
// Other containers (mp3/aac/ogg) are out of scope here — decode them upstream
|
|
4
|
+
// and hand `analyzeAudio` the raw samples via AnalyzeOptions.
|
|
5
|
+
function str(b, o, n) {
|
|
6
|
+
let s = '';
|
|
7
|
+
for (let i = 0; i < n; i++)
|
|
8
|
+
s += String.fromCharCode(b[o + i]);
|
|
9
|
+
return s;
|
|
10
|
+
}
|
|
11
|
+
export function decodeWav(bytes) {
|
|
12
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
13
|
+
if (str(bytes, 0, 4) !== 'RIFF' || str(bytes, 8, 4) !== 'WAVE') {
|
|
14
|
+
throw new Error('decodeWav: not a RIFF/WAVE file');
|
|
15
|
+
}
|
|
16
|
+
let fmt = null;
|
|
17
|
+
let dataOff = -1;
|
|
18
|
+
let dataLen = 0;
|
|
19
|
+
// Walk chunks.
|
|
20
|
+
let p = 12;
|
|
21
|
+
while (p + 8 <= bytes.length) {
|
|
22
|
+
const id = str(bytes, p, 4);
|
|
23
|
+
const size = dv.getUint32(p + 4, true);
|
|
24
|
+
const body = p + 8;
|
|
25
|
+
if (id === 'fmt ') {
|
|
26
|
+
fmt = {
|
|
27
|
+
audioFormat: dv.getUint16(body, true),
|
|
28
|
+
channels: dv.getUint16(body + 2, true),
|
|
29
|
+
sampleRate: dv.getUint32(body + 4, true),
|
|
30
|
+
bits: dv.getUint16(body + 14, true),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
else if (id === 'data') {
|
|
34
|
+
dataOff = body;
|
|
35
|
+
dataLen = size;
|
|
36
|
+
}
|
|
37
|
+
p = body + size + (size & 1); // chunks are word-aligned
|
|
38
|
+
}
|
|
39
|
+
if (!fmt || dataOff < 0)
|
|
40
|
+
throw new Error('decodeWav: missing fmt/data chunk');
|
|
41
|
+
const { channels, sampleRate, bits, audioFormat } = fmt;
|
|
42
|
+
const bytesPerSample = bits >> 3;
|
|
43
|
+
const frameCount = Math.floor(dataLen / (bytesPerSample * channels));
|
|
44
|
+
const out = new Float32Array(frameCount);
|
|
45
|
+
const isFloat = audioFormat === 3;
|
|
46
|
+
const read = (off) => {
|
|
47
|
+
if (isFloat)
|
|
48
|
+
return bits === 64 ? dv.getFloat64(off, true) : dv.getFloat32(off, true);
|
|
49
|
+
switch (bits) {
|
|
50
|
+
case 8: return (dv.getUint8(off) - 128) / 128; // 8-bit PCM is unsigned
|
|
51
|
+
case 16: return dv.getInt16(off, true) / 32768;
|
|
52
|
+
case 24: {
|
|
53
|
+
const b0 = dv.getUint8(off), b1 = dv.getUint8(off + 1), b2 = dv.getUint8(off + 2);
|
|
54
|
+
let v = b0 | (b1 << 8) | (b2 << 16);
|
|
55
|
+
if (v & 0x800000)
|
|
56
|
+
v -= 0x1000000;
|
|
57
|
+
return v / 8388608;
|
|
58
|
+
}
|
|
59
|
+
case 32: return dv.getInt32(off, true) / 2147483648;
|
|
60
|
+
default: throw new Error(`decodeWav: unsupported bit depth ${bits}`);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
for (let i = 0; i < frameCount; i++) {
|
|
64
|
+
let acc = 0;
|
|
65
|
+
const base = dataOff + i * bytesPerSample * channels;
|
|
66
|
+
for (let c = 0; c < channels; c++)
|
|
67
|
+
acc += read(base + c * bytesPerSample);
|
|
68
|
+
out[i] = acc / channels; // downmix
|
|
69
|
+
}
|
|
70
|
+
return { samples: out, sampleRate };
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=decode-wav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode-wav.js","sourceRoot":"","sources":["../src/decode-wav.ts"],"names":[],"mappings":"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"}
|
package/dist/dsp.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Marker, Onset, Section } from './beat-map.js';
|
|
2
|
+
export interface PcmAnalysis {
|
|
3
|
+
bpm: number;
|
|
4
|
+
phase: number;
|
|
5
|
+
beats: Marker[];
|
|
6
|
+
downbeats: Marker[];
|
|
7
|
+
onsets: Onset[];
|
|
8
|
+
sections: Section[];
|
|
9
|
+
}
|
|
10
|
+
/** Full analysis of mono PCM. Beats/downbeats are the regular grid implied by
|
|
11
|
+
* the detected tempo+phase; onsets are the actual detected transients. */
|
|
12
|
+
export declare function analyzePcm(samples: Float32Array, sr: number, beatsPerBar?: number): PcmAnalysis;
|
|
13
|
+
//# sourceMappingURL=dsp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dsp.d.ts","sourceRoot":"","sources":["../src/dsp.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAQ,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAgNlE,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED;2EAC2E;AAC3E,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,SAAI,GAAG,WAAW,CA8B1F"}
|
package/dist/dsp.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
// The analysis pipeline: PCM samples → tempo, phase, onsets, sections.
|
|
2
|
+
// Deterministic, dependency-free, built on the FFT in fft.ts.
|
|
3
|
+
//
|
|
4
|
+
// STFT → spectral flux ─┬─ peak-pick → onsets (the actual hits, w/ band)
|
|
5
|
+
// └─ autocorrelation → tempo + phase → beat grid
|
|
6
|
+
// frame RMS → novelty ──→ section boundaries (structure)
|
|
7
|
+
//
|
|
8
|
+
// Tuned for music, verified against click tracks of known tempo. Section
|
|
9
|
+
// detection here is ENERGY-based (a build/drop reads as an energy jump); a
|
|
10
|
+
// timbral self-similarity pass is the future refinement.
|
|
11
|
+
import { magnitudeSpectrum } from './fft.js';
|
|
12
|
+
const FRAME = 2048;
|
|
13
|
+
const HOP = 512;
|
|
14
|
+
function hann(n) {
|
|
15
|
+
const w = new Float64Array(n);
|
|
16
|
+
for (let i = 0; i < n; i++)
|
|
17
|
+
w[i] = 0.5 - 0.5 * Math.cos((2 * Math.PI * i) / (n - 1));
|
|
18
|
+
return w;
|
|
19
|
+
}
|
|
20
|
+
/** STFT-derived per-frame features: spectral flux (overall + 3 bands) and RMS. */
|
|
21
|
+
function analyzeFrames(samples, sr) {
|
|
22
|
+
const win = hann(FRAME);
|
|
23
|
+
const nFrames = Math.max(1, Math.floor((samples.length - FRAME) / HOP) + 1);
|
|
24
|
+
const flux = new Float64Array(nFrames);
|
|
25
|
+
const low = new Float64Array(nFrames);
|
|
26
|
+
const mid = new Float64Array(nFrames);
|
|
27
|
+
const high = new Float64Array(nFrames);
|
|
28
|
+
const rms = new Float64Array(nFrames);
|
|
29
|
+
const half = FRAME >> 1;
|
|
30
|
+
// band edges in bins (bin width = sr / FRAME)
|
|
31
|
+
const binHz = sr / FRAME;
|
|
32
|
+
const loEdge = Math.round(200 / binHz);
|
|
33
|
+
const hiEdge = Math.round(5000 / binHz);
|
|
34
|
+
let prev = new Float64Array(half);
|
|
35
|
+
const frame = new Float64Array(FRAME);
|
|
36
|
+
for (let f = 0; f < nFrames; f++) {
|
|
37
|
+
const start = f * HOP;
|
|
38
|
+
let energy = 0;
|
|
39
|
+
for (let i = 0; i < FRAME; i++) {
|
|
40
|
+
const s = samples[start + i] ?? 0;
|
|
41
|
+
energy += s * s;
|
|
42
|
+
frame[i] = s * win[i];
|
|
43
|
+
}
|
|
44
|
+
rms[f] = Math.sqrt(energy / FRAME);
|
|
45
|
+
const mag = magnitudeSpectrum(frame);
|
|
46
|
+
let fl = 0, fLo = 0, fMid = 0, fHi = 0;
|
|
47
|
+
for (let b = 1; b < half; b++) {
|
|
48
|
+
const m = Math.log1p(mag[b]); // log-compress for musical dynamics
|
|
49
|
+
const d = m - prev[b];
|
|
50
|
+
if (d > 0) {
|
|
51
|
+
fl += d;
|
|
52
|
+
if (b < loEdge)
|
|
53
|
+
fLo += d;
|
|
54
|
+
else if (b < hiEdge)
|
|
55
|
+
fMid += d;
|
|
56
|
+
else
|
|
57
|
+
fHi += d;
|
|
58
|
+
}
|
|
59
|
+
prev[b] = m;
|
|
60
|
+
}
|
|
61
|
+
flux[f] = fl;
|
|
62
|
+
low[f] = fLo;
|
|
63
|
+
mid[f] = fMid;
|
|
64
|
+
high[f] = fHi;
|
|
65
|
+
}
|
|
66
|
+
// normalize flux to [0,1]
|
|
67
|
+
let mx = 0;
|
|
68
|
+
for (let f = 0; f < nFrames; f++)
|
|
69
|
+
if (flux[f] > mx)
|
|
70
|
+
mx = flux[f];
|
|
71
|
+
if (mx > 0)
|
|
72
|
+
for (let f = 0; f < nFrames; f++)
|
|
73
|
+
flux[f] /= mx;
|
|
74
|
+
return { flux, bandFlux: [low, mid, high], rms, dt: HOP / sr };
|
|
75
|
+
}
|
|
76
|
+
/** Peak-pick onsets from the flux envelope: local maxima above an adaptive
|
|
77
|
+
* threshold, with a minimum inter-onset gap. */
|
|
78
|
+
function pickOnsets(fr) {
|
|
79
|
+
const { flux, bandFlux, dt } = fr;
|
|
80
|
+
const n = flux.length;
|
|
81
|
+
const w = Math.max(2, Math.round(0.05 / dt)); // local window ~50ms
|
|
82
|
+
const meanW = Math.max(4, Math.round(0.1 / dt));
|
|
83
|
+
const minGap = 0.07; // seconds
|
|
84
|
+
const out = [];
|
|
85
|
+
let lastT = -Infinity;
|
|
86
|
+
for (let i = 1; i < n - 1; i++) {
|
|
87
|
+
const v = flux[i];
|
|
88
|
+
if (v < 0.06)
|
|
89
|
+
continue;
|
|
90
|
+
// local max?
|
|
91
|
+
let isMax = true;
|
|
92
|
+
for (let k = -w; k <= w; k++)
|
|
93
|
+
if (flux[i + k] !== undefined && flux[i + k] > v) {
|
|
94
|
+
isMax = false;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
if (!isMax)
|
|
98
|
+
continue;
|
|
99
|
+
// adaptive threshold: above the local mean by a margin
|
|
100
|
+
let sum = 0, cnt = 0;
|
|
101
|
+
for (let k = -meanW; k <= meanW; k++) {
|
|
102
|
+
const x = flux[i + k];
|
|
103
|
+
if (x !== undefined) {
|
|
104
|
+
sum += x;
|
|
105
|
+
cnt++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (v < (sum / cnt) * 1.4 + 0.03)
|
|
109
|
+
continue;
|
|
110
|
+
const t = i * dt;
|
|
111
|
+
if (t - lastT < minGap)
|
|
112
|
+
continue;
|
|
113
|
+
lastT = t;
|
|
114
|
+
// dominant band at this frame
|
|
115
|
+
const lo = bandFlux[0][i], md = bandFlux[1][i], hi = bandFlux[2][i];
|
|
116
|
+
const band = lo >= md && lo >= hi ? 'low' : hi >= md ? 'high' : 'mid';
|
|
117
|
+
out.push({ time: t, strength: Math.min(1, v), band });
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
/** Autocorrelation tempo over 60–180 BPM, picking the strongest lag. */
|
|
122
|
+
function detectTempo(flux, dt) {
|
|
123
|
+
const n = flux.length;
|
|
124
|
+
// de-mean
|
|
125
|
+
let mean = 0;
|
|
126
|
+
for (let i = 0; i < n; i++)
|
|
127
|
+
mean += flux[i];
|
|
128
|
+
mean /= n;
|
|
129
|
+
const env = new Float64Array(n);
|
|
130
|
+
for (let i = 0; i < n; i++)
|
|
131
|
+
env[i] = flux[i] - mean;
|
|
132
|
+
const lagMin = Math.max(1, Math.round((60 / 200) / dt));
|
|
133
|
+
const lagMax = Math.round((60 / 60) / dt);
|
|
134
|
+
const acf = new Float64Array(lagMax + 1);
|
|
135
|
+
let bestLag = lagMin;
|
|
136
|
+
let bestScore = -Infinity;
|
|
137
|
+
for (let lag = lagMin; lag <= lagMax; lag++) {
|
|
138
|
+
let s = 0;
|
|
139
|
+
for (let i = 0; i + lag < n; i++)
|
|
140
|
+
s += env[i] * env[i + lag];
|
|
141
|
+
acf[lag] = s;
|
|
142
|
+
// Perceptual tempo prior: a log-normal centred on 120 BPM nudges the octave
|
|
143
|
+
// ambiguity toward the tactus humans tap.
|
|
144
|
+
const bpm = 60 / (lag * dt);
|
|
145
|
+
const score = s * Math.exp(-0.5 * (Math.log2(bpm / 120) / 0.8) ** 2);
|
|
146
|
+
if (score > bestScore) {
|
|
147
|
+
bestScore = score;
|
|
148
|
+
bestLag = lag;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Octave-doubling: a four-on-the-floor track correlates strongly at the
|
|
152
|
+
// half-bar (e.g. 60) because the backbeat repeats there — but if the
|
|
153
|
+
// double-tempo pulse (the kick, 120) is well-supported, that's the real beat.
|
|
154
|
+
for (;;) {
|
|
155
|
+
const half = Math.round(bestLag / 2);
|
|
156
|
+
const bpmD = 60 / (half * dt);
|
|
157
|
+
if (half < lagMin || bpmD > 185 || acf[half] < 0.5 * acf[bestLag])
|
|
158
|
+
break;
|
|
159
|
+
bestLag = half;
|
|
160
|
+
}
|
|
161
|
+
return Math.round((60 / (bestLag * dt)) * 10) / 10;
|
|
162
|
+
}
|
|
163
|
+
/** Best phase (time of beat 0) for a known tempo: the grid offset that lands on
|
|
164
|
+
* the most onset-envelope energy. */
|
|
165
|
+
function detectPhase(flux, bpm, dt) {
|
|
166
|
+
const periodF = 60 / bpm / dt; // frames per beat (float)
|
|
167
|
+
const steps = Math.max(1, Math.round(periodF));
|
|
168
|
+
let bestOff = 0;
|
|
169
|
+
let bestScore = -Infinity;
|
|
170
|
+
for (let o = 0; o < steps; o++) {
|
|
171
|
+
let s = 0;
|
|
172
|
+
for (let k = 0; o + k * periodF < flux.length; k++)
|
|
173
|
+
s += flux[Math.round(o + k * periodF)];
|
|
174
|
+
if (s > bestScore) {
|
|
175
|
+
bestScore = s;
|
|
176
|
+
bestOff = o;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return Math.round(bestOff * dt * 1000) / 1000;
|
|
180
|
+
}
|
|
181
|
+
/** Energy-novelty section boundaries: sustained RMS changes split the track. */
|
|
182
|
+
function detectSections(rms, dt, duration) {
|
|
183
|
+
const n = rms.length;
|
|
184
|
+
// smooth RMS over ~0.4s
|
|
185
|
+
const sw = Math.max(1, Math.round(0.4 / dt));
|
|
186
|
+
const sm = new Float64Array(n);
|
|
187
|
+
for (let i = 0; i < n; i++) {
|
|
188
|
+
let s = 0, c = 0;
|
|
189
|
+
for (let k = -sw; k <= sw; k++) {
|
|
190
|
+
const x = rms[i + k];
|
|
191
|
+
if (x !== undefined) {
|
|
192
|
+
s += x;
|
|
193
|
+
c++;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
sm[i] = s / c;
|
|
197
|
+
}
|
|
198
|
+
// novelty = |future mean − past mean| over a ~1.5s half-window
|
|
199
|
+
const hw = Math.max(2, Math.round(1.5 / dt));
|
|
200
|
+
const nov = new Float64Array(n);
|
|
201
|
+
for (let i = 0; i < n; i++) {
|
|
202
|
+
let pa = 0, pc = 0, fa = 0, fc = 0;
|
|
203
|
+
for (let k = 1; k <= hw; k++) {
|
|
204
|
+
if (sm[i - k] !== undefined) {
|
|
205
|
+
pa += sm[i - k];
|
|
206
|
+
pc++;
|
|
207
|
+
}
|
|
208
|
+
if (sm[i + k] !== undefined) {
|
|
209
|
+
fa += sm[i + k];
|
|
210
|
+
fc++;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
nov[i] = pc && fc ? Math.abs(fa / fc - pa / pc) : 0;
|
|
214
|
+
}
|
|
215
|
+
// peak-pick boundaries: local maxima, min section length, relative threshold
|
|
216
|
+
let mx = 0;
|
|
217
|
+
for (let i = 0; i < n; i++)
|
|
218
|
+
if (nov[i] > mx)
|
|
219
|
+
mx = nov[i];
|
|
220
|
+
const minLenF = Math.round(4 / dt); // ≥ 4s sections
|
|
221
|
+
const boundaries = [0];
|
|
222
|
+
if (mx > 0) {
|
|
223
|
+
let last = -minLenF;
|
|
224
|
+
for (let i = hw; i < n - hw; i++) {
|
|
225
|
+
const v = nov[i];
|
|
226
|
+
if (v < mx * 0.35)
|
|
227
|
+
continue;
|
|
228
|
+
let isMax = true;
|
|
229
|
+
for (let k = -hw; k <= hw; k++)
|
|
230
|
+
if (nov[i + k] !== undefined && nov[i + k] > v) {
|
|
231
|
+
isMax = false;
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
if (isMax && i - last >= minLenF) {
|
|
235
|
+
boundaries.push(i);
|
|
236
|
+
last = i;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// build sections, energy normalized to the loudest section
|
|
241
|
+
const segs = [];
|
|
242
|
+
for (let b = 0; b < boundaries.length; b++) {
|
|
243
|
+
const s = boundaries[b];
|
|
244
|
+
const e = b + 1 < boundaries.length ? boundaries[b + 1] : n;
|
|
245
|
+
let sum = 0;
|
|
246
|
+
for (let i = s; i < e; i++)
|
|
247
|
+
sum += sm[i];
|
|
248
|
+
segs.push({ start: s * dt, end: (b + 1 < boundaries.length ? e * dt : duration), energy: sum / Math.max(1, e - s) });
|
|
249
|
+
}
|
|
250
|
+
let emax = 0;
|
|
251
|
+
for (const g of segs)
|
|
252
|
+
if (g.energy > emax)
|
|
253
|
+
emax = g.energy;
|
|
254
|
+
return segs.map((g) => {
|
|
255
|
+
const energy = emax > 0 ? Math.round((g.energy / emax) * 1000) / 1000 : 0;
|
|
256
|
+
const label = energy < 0.34 ? 'low' : energy < 0.7 ? 'mid' : 'high';
|
|
257
|
+
return { start: Math.round(g.start * 1000) / 1000, end: Math.round(g.end * 1000) / 1000, label, energy };
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
/** Full analysis of mono PCM. Beats/downbeats are the regular grid implied by
|
|
261
|
+
* the detected tempo+phase; onsets are the actual detected transients. */
|
|
262
|
+
export function analyzePcm(samples, sr, beatsPerBar = 4) {
|
|
263
|
+
const duration = samples.length / sr;
|
|
264
|
+
const fr = analyzeFrames(samples, sr);
|
|
265
|
+
const onsets = pickOnsets(fr);
|
|
266
|
+
// Tempo/phase run on an ENERGY-flux envelope (half-wave-rectified RMS diff),
|
|
267
|
+
// not the spectral flux: spectral flux over-weights broadband hits (claps,
|
|
268
|
+
// snares) which biases toward the half-bar, while the energy envelope tracks
|
|
269
|
+
// the kick pulse — the tactus. (Spectral flux still drives onset timing.)
|
|
270
|
+
const tempoEnv = new Float64Array(fr.rms.length);
|
|
271
|
+
for (let i = 1; i < fr.rms.length; i++) {
|
|
272
|
+
const d = fr.rms[i] - fr.rms[i - 1];
|
|
273
|
+
tempoEnv[i] = d > 0 ? d : 0;
|
|
274
|
+
}
|
|
275
|
+
const bpm = detectTempo(tempoEnv, fr.dt);
|
|
276
|
+
const phase = detectPhase(tempoEnv, bpm, fr.dt);
|
|
277
|
+
const interval = 60 / bpm;
|
|
278
|
+
const beats = [];
|
|
279
|
+
const downbeats = [];
|
|
280
|
+
for (let k = 0;; k++) {
|
|
281
|
+
const t = phase + k * interval;
|
|
282
|
+
if (t > duration)
|
|
283
|
+
break;
|
|
284
|
+
if (t < 0)
|
|
285
|
+
continue;
|
|
286
|
+
const isDown = k % beatsPerBar === 0;
|
|
287
|
+
const m = { time: Math.round(t * 1000) / 1000, strength: isDown ? 1 : 0.7 };
|
|
288
|
+
beats.push(m);
|
|
289
|
+
if (isDown)
|
|
290
|
+
downbeats.push(m);
|
|
291
|
+
}
|
|
292
|
+
const sections = detectSections(fr.rms, fr.dt, duration);
|
|
293
|
+
return { bpm, phase, beats, downbeats, onsets, sections };
|
|
294
|
+
}
|
|
295
|
+
//# sourceMappingURL=dsp.js.map
|
package/dist/dsp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dsp.js","sourceRoot":"","sources":["../src/dsp.ts"],"names":[],"mappings":"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"}
|
package/dist/fft.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** In-place complex FFT. `re`/`im` length MUST be a power of two. */
|
|
2
|
+
export declare function fft(re: Float64Array, im: Float64Array): void;
|
|
3
|
+
/** Smallest power of two ≥ n. */
|
|
4
|
+
export declare function nextPow2(n: number): number;
|
|
5
|
+
/** Magnitude spectrum (first n/2 bins) of a real, already-windowed frame. The
|
|
6
|
+
* frame is copied and zero-padded to a power of two. */
|
|
7
|
+
export declare function magnitudeSpectrum(frame: Float64Array): Float64Array;
|
|
8
|
+
//# sourceMappingURL=fft.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fft.d.ts","sourceRoot":"","sources":["../src/fft.ts"],"names":[],"mappings":"AAIA,qEAAqE;AACrE,wBAAgB,GAAG,CAAC,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,GAAG,IAAI,CAiC5D;AAED,iCAAiC;AACjC,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI1C;AAED;yDACyD;AACzD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAUnE"}
|
package/dist/fft.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Iterative in-place radix-2 Cooley–Tukey FFT. The one piece of heavy math the
|
|
2
|
+
// analyzer leans on; everything else (flux, tempo, sections) is built on the
|
|
3
|
+
// magnitude spectra this produces. Pure and deterministic.
|
|
4
|
+
/** In-place complex FFT. `re`/`im` length MUST be a power of two. */
|
|
5
|
+
export function fft(re, im) {
|
|
6
|
+
const n = re.length;
|
|
7
|
+
// bit-reversal permutation
|
|
8
|
+
for (let i = 1, j = 0; i < n; i++) {
|
|
9
|
+
let bit = n >> 1;
|
|
10
|
+
for (; j & bit; bit >>= 1)
|
|
11
|
+
j ^= bit;
|
|
12
|
+
j ^= bit;
|
|
13
|
+
if (i < j) {
|
|
14
|
+
const tr = re[i];
|
|
15
|
+
re[i] = re[j];
|
|
16
|
+
re[j] = tr;
|
|
17
|
+
const ti = im[i];
|
|
18
|
+
im[i] = im[j];
|
|
19
|
+
im[j] = ti;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
for (let len = 2; len <= n; len <<= 1) {
|
|
23
|
+
const ang = (-2 * Math.PI) / len;
|
|
24
|
+
const wr = Math.cos(ang);
|
|
25
|
+
const wi = Math.sin(ang);
|
|
26
|
+
const half = len >> 1;
|
|
27
|
+
for (let i = 0; i < n; i += len) {
|
|
28
|
+
let cr = 1;
|
|
29
|
+
let ci = 0;
|
|
30
|
+
for (let k = 0; k < half; k++) {
|
|
31
|
+
const a = i + k;
|
|
32
|
+
const b = a + half;
|
|
33
|
+
const xr = re[b] * cr - im[b] * ci;
|
|
34
|
+
const xi = re[b] * ci + im[b] * cr;
|
|
35
|
+
re[b] = re[a] - xr;
|
|
36
|
+
im[b] = im[a] - xi;
|
|
37
|
+
re[a] = re[a] + xr;
|
|
38
|
+
im[a] = im[a] + xi;
|
|
39
|
+
const ncr = cr * wr - ci * wi;
|
|
40
|
+
ci = cr * wi + ci * wr;
|
|
41
|
+
cr = ncr;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Smallest power of two ≥ n. */
|
|
47
|
+
export function nextPow2(n) {
|
|
48
|
+
let p = 1;
|
|
49
|
+
while (p < n)
|
|
50
|
+
p <<= 1;
|
|
51
|
+
return p;
|
|
52
|
+
}
|
|
53
|
+
/** Magnitude spectrum (first n/2 bins) of a real, already-windowed frame. The
|
|
54
|
+
* frame is copied and zero-padded to a power of two. */
|
|
55
|
+
export function magnitudeSpectrum(frame) {
|
|
56
|
+
const n = nextPow2(frame.length);
|
|
57
|
+
const re = new Float64Array(n);
|
|
58
|
+
const im = new Float64Array(n);
|
|
59
|
+
re.set(frame);
|
|
60
|
+
fft(re, im);
|
|
61
|
+
const half = n >> 1;
|
|
62
|
+
const mag = new Float64Array(half);
|
|
63
|
+
for (let i = 0; i < half; i++)
|
|
64
|
+
mag[i] = Math.hypot(re[i], im[i]);
|
|
65
|
+
return mag;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=fft.js.map
|
package/dist/fft.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fft.js","sourceRoot":"","sources":["../src/fft.ts"],"names":[],"mappings":"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"}
|
package/dist/grid.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BeatMap } from './beat-map.js';
|
|
2
|
+
export interface BeatGridOptions {
|
|
3
|
+
/** Tempo, beats per minute. Must be > 0. */
|
|
4
|
+
bpm: number;
|
|
5
|
+
/** Total span to fill with beats, seconds. */
|
|
6
|
+
duration: number;
|
|
7
|
+
/** Time of beat 0, seconds. Default 0. */
|
|
8
|
+
phase?: number;
|
|
9
|
+
/** Beats per bar — a downbeat lands every Nth beat from beat 0. Default 4. */
|
|
10
|
+
beatsPerBar?: number;
|
|
11
|
+
/** Label recorded as the map's `source`. Default "synthetic:bpm". */
|
|
12
|
+
source?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build a {@link BeatMap} from a known tempo. Populates `bpm`, `phase`,
|
|
16
|
+
* `beats`, and `downbeats`; `onsets` and `sections` are left empty (those need
|
|
17
|
+
* real audio analysis). The first beat at or after `phase` is beat 0, and every
|
|
18
|
+
* `beatsPerBar`-th beat from there is a downbeat.
|
|
19
|
+
*/
|
|
20
|
+
export declare function beatGrid(opts: BeatGridOptions): BeatMap;
|
|
21
|
+
//# sourceMappingURL=grid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../src/grid.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,EAAU,MAAM,eAAe,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAgCvD"}
|
package/dist/grid.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// beatGrid — synthesize a BeatMap from a KNOWN tempo.
|
|
2
|
+
//
|
|
3
|
+
// Tempo detection from raw audio is the analyzer's job (see analyze.ts) and is
|
|
4
|
+
// still ahead of us. But the moment you know the BPM — and you very often do
|
|
5
|
+
// (the track's metadata, a creator who typed it in, a loop at a fixed tempo) —
|
|
6
|
+
// the entire grid is determined: beat k sits at `phase + k·60/bpm`. This builds
|
|
7
|
+
// that grid as a real, fully-formed BeatMap, so motion can be synced to music
|
|
8
|
+
// today while the detector is built behind the same BeatMap contract.
|
|
9
|
+
//
|
|
10
|
+
// Deterministic and pure: same options → same map.
|
|
11
|
+
/**
|
|
12
|
+
* Build a {@link BeatMap} from a known tempo. Populates `bpm`, `phase`,
|
|
13
|
+
* `beats`, and `downbeats`; `onsets` and `sections` are left empty (those need
|
|
14
|
+
* real audio analysis). The first beat at or after `phase` is beat 0, and every
|
|
15
|
+
* `beatsPerBar`-th beat from there is a downbeat.
|
|
16
|
+
*/
|
|
17
|
+
export function beatGrid(opts) {
|
|
18
|
+
const { bpm, duration } = opts;
|
|
19
|
+
const phase = opts.phase ?? 0;
|
|
20
|
+
const beatsPerBar = opts.beatsPerBar ?? 4;
|
|
21
|
+
if (!(bpm > 0))
|
|
22
|
+
throw new RangeError(`beatGrid: bpm must be > 0, got ${bpm}`);
|
|
23
|
+
const interval = 60 / bpm; // seconds per beat
|
|
24
|
+
const beats = [];
|
|
25
|
+
const downbeats = [];
|
|
26
|
+
// Beat 0 is the first beat at or after `phase`; index counts from there so
|
|
27
|
+
// the downbeat phase is stable regardless of where `phase` falls.
|
|
28
|
+
for (let k = 0;; k++) {
|
|
29
|
+
const time = phase + k * interval;
|
|
30
|
+
if (time > duration)
|
|
31
|
+
break;
|
|
32
|
+
if (time < 0)
|
|
33
|
+
continue;
|
|
34
|
+
const isDownbeat = k % beatsPerBar === 0;
|
|
35
|
+
const marker = { time, strength: isDownbeat ? 1 : 0.7 };
|
|
36
|
+
beats.push(marker);
|
|
37
|
+
if (isDownbeat)
|
|
38
|
+
downbeats.push(marker);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
source: opts.source ?? `synthetic:${bpm}bpm`,
|
|
42
|
+
duration,
|
|
43
|
+
bpm,
|
|
44
|
+
phase,
|
|
45
|
+
beats,
|
|
46
|
+
downbeats,
|
|
47
|
+
onsets: [],
|
|
48
|
+
sections: [],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=grid.js.map
|
package/dist/grid.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grid.js","sourceRoot":"","sources":["../src/grid.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,+EAA+E;AAC/E,gFAAgF;AAChF,8EAA8E;AAC9E,sEAAsE;AACtE,EAAE;AACF,mDAAmD;AAiBnD;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAqB;IAC5C,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;IAC9B,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,kCAAkC,GAAG,EAAE,CAAC,CAAC;IAE9E,MAAM,QAAQ,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC,mBAAmB;IAC9C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,2EAA2E;IAC3E,kEAAkE;IAClE,KAAK,IAAI,CAAC,GAAG,CAAC,GAAI,CAAC,EAAE,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,GAAG,QAAQ,CAAC;QAClC,IAAI,IAAI,GAAG,QAAQ;YAAE,MAAM;QAC3B,IAAI,IAAI,GAAG,CAAC;YAAE,SAAS;QACvB,MAAM,UAAU,GAAG,CAAC,GAAG,WAAW,KAAK,CAAC,CAAC;QACzC,MAAM,MAAM,GAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;QAChE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,IAAI,UAAU;YAAE,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAED,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,aAAa,GAAG,KAAK;QAC5C,QAAQ;QACR,GAAG;QACH,KAAK;QACL,KAAK;QACL,SAAS;QACT,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AACxD,mBAAmB,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// @clipkit/music-analysis — audio → beat map.
|
|
2
|
+
//
|
|
3
|
+
// Produces an authoring-time beat map (tempo, downbeats, onsets, sections) that
|
|
4
|
+
// AI agents and @clipkit/patterns helpers read to sync motion to music. The
|
|
5
|
+
// beat map is NOT part of the rendered protocol; consumers bake it down to
|
|
6
|
+
// ordinary keyframes/expressions so renders stay deterministic.
|
|
7
|
+
// analyzeAudio reads files (node:fs) — it lives behind the Node-only
|
|
8
|
+
// '@clipkit/music-analysis/node' subpath so importing the browser-safe helpers
|
|
9
|
+
// below (beatGrid etc.) doesn't drag node:fs into client bundles. Mirrors the
|
|
10
|
+
// node/browser export split in @clipkit/speech-to-text.
|
|
11
|
+
export { beatGrid } from './grid.js';
|
|
12
|
+
export { decodeWav } from './decode-wav.js';
|
|
13
|
+
export { analyzePcm } from './dsp.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,EAAE;AACF,gFAAgF;AAChF,4EAA4E;AAC5E,2EAA2E;AAC3E,gEAAgE;AAEhE,qEAAqE;AACrE,+EAA+E;AAC/E,8EAA8E;AAC9E,wDAAwD;AACxD,OAAO,EAAE,QAAQ,EAAwB,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAqB,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAoB,MAAM,UAAU,CAAC"}
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Node-only entry for @clipkit/music-analysis.
|
|
2
|
+
//
|
|
3
|
+
// analyzeAudio reads audio files from disk (node:fs/promises), so it can't be
|
|
4
|
+
// part of the browser-safe barrel (index.ts) — importing the barrel for a pure
|
|
5
|
+
// helper like beatGrid would otherwise drag node:fs into client bundles and
|
|
6
|
+
// break the web build. Authoring-time / CLI / MCP code imports it from here:
|
|
7
|
+
//
|
|
8
|
+
// import { analyzeAudio } from '@clipkit/music-analysis/node';
|
|
9
|
+
export { analyzeAudio } from './analyze.js';
|
|
10
|
+
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,4EAA4E;AAC5E,6EAA6E;AAC7E,EAAE;AACF,iEAAiE;AAEjE,OAAO,EAAE,YAAY,EAAuB,MAAM,cAAc,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@clipkit/music-analysis",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Analyze an audio file into a Clipkit beat map — tempo, downbeats, onsets, and sections. The beat map is an authoring-time artifact: AI agents and @clipkit/patterns helpers read it to place transitions, accents, and reveals on musical time. It is NOT part of the rendered protocol — the runtime never sees it.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": { "type": "git", "url": "git+https://github.com/clipkit-video/clipkit.git", "directory": "packages/music-analysis" },
|
|
7
|
+
"homepage": "https://clipkit.dev",
|
|
8
|
+
"bugs": "https://github.com/clipkit-video/clipkit/issues",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./node": {
|
|
18
|
+
"import": "./dist/node.js",
|
|
19
|
+
"types": "./dist/node.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"dev": "tsc --watch",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"clean": "rm -rf dist"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^22.0.0",
|
|
36
|
+
"typescript": "^5.7.3"
|
|
37
|
+
}
|
|
38
|
+
}
|