music-segment-detector 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -1
- package/dist/audioAnalyzer.d.ts +7 -3
- package/dist/audioAnalyzer.js +41 -8
- package/dist/audioAnalyzer.js.map +1 -1
- package/dist/audioAnalyzer.worker.d.ts +1 -0
- package/dist/audioAnalyzer.worker.js +123 -0
- package/dist/audioAnalyzer.worker.js.map +1 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js.map +1 -1
- package/dist/segmentDetector.d.ts +0 -1
- package/dist/workerPool.d.ts +43 -0
- package/dist/workerPool.js +126 -0
- package/dist/workerPool.js.map +1 -0
- package/package.json +1 -1
- package/dist/audioAnalyzer.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/segmentDetector.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -27,6 +27,20 @@ const features = await analyzeAudio("audio.wav", 2048, 512, (progress) => {
|
|
|
27
27
|
console.log(`Analysis progress: ${(progress * 100).toFixed(1)}%`);
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
// 1-2. Use Worker parallel processing (recommended for large files, 2.5-3x faster)
|
|
31
|
+
const features = await analyzeAudio(
|
|
32
|
+
"audio.wav",
|
|
33
|
+
2048,
|
|
34
|
+
512,
|
|
35
|
+
(progress) => {
|
|
36
|
+
console.log(`Analysis progress: ${(progress * 100).toFixed(1)}%`);
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
useWorkers: true, // Enable Worker parallel processing
|
|
40
|
+
numWorkers: 4, // Optional, defaults to CPU core count
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
|
|
30
44
|
// 2. Detect music segments
|
|
31
45
|
const segments = detectMusicSegments(features, {
|
|
32
46
|
energyPercentile: 50, // Energy percentile threshold (0-100)
|
|
@@ -45,7 +59,7 @@ segments.forEach((segment) => {
|
|
|
45
59
|
|
|
46
60
|
## API
|
|
47
61
|
|
|
48
|
-
### `analyzeAudio(audioPath, windowSize?, hopSize?, onProgress?)`
|
|
62
|
+
### `analyzeAudio(audioPath, windowSize?, hopSize?, onProgress?, options?)`
|
|
49
63
|
|
|
50
64
|
Analyze a WAV audio file and extract features.
|
|
51
65
|
|
|
@@ -53,6 +67,9 @@ Analyze a WAV audio file and extract features.
|
|
|
53
67
|
- `windowSize`: Analysis window size (default: 2048)
|
|
54
68
|
- `hopSize`: Window hop size (default: 512)
|
|
55
69
|
- `onProgress`: Optional progress callback function `(progress: number) => void`, parameter is progress value between 0-1
|
|
70
|
+
- `options`: Optional configuration
|
|
71
|
+
- `useWorkers`: Enable Worker parallel processing (default: false)
|
|
72
|
+
- `numWorkers`: Number of workers (default: CPU core count, max 8)
|
|
56
73
|
- Returns: `Promise<AudioFeatures[]>`
|
|
57
74
|
|
|
58
75
|
### `detectMusicSegments(features, config?)`
|
|
@@ -99,6 +116,11 @@ interface AudioFeatures {
|
|
|
99
116
|
spectralFlatness: number;
|
|
100
117
|
}
|
|
101
118
|
|
|
119
|
+
interface AnalyzeAudioOptions {
|
|
120
|
+
useWorkers?: boolean; // Enable Worker parallel processing
|
|
121
|
+
numWorkers?: number; // Number of workers (defaults to CPU core count)
|
|
122
|
+
}
|
|
123
|
+
|
|
102
124
|
interface MusicSegment {
|
|
103
125
|
startTime: number;
|
|
104
126
|
endTime: number;
|
|
@@ -128,11 +150,22 @@ interface DetectionConfig {
|
|
|
128
150
|
- Spectral rolloff
|
|
129
151
|
3. **Post-processing**: Smoothing, merging adjacent segments, filtering short segments
|
|
130
152
|
|
|
153
|
+
## Performance Optimization
|
|
154
|
+
|
|
155
|
+
For large audio files, it's recommended to enable Worker parallel processing for better performance:
|
|
156
|
+
|
|
157
|
+
- **Single-threaded mode** (default): Suitable for small files (< 1 minute) or low-spec environments
|
|
158
|
+
- **Worker parallel mode**: Suitable for large files, 2.5-3x performance boost (on 4-core CPU)
|
|
159
|
+
- Automatically adjusts worker count based on file size
|
|
160
|
+
- Automatically falls back to single-threaded mode if workers fail
|
|
161
|
+
- Supports progress aggregation and error handling
|
|
162
|
+
|
|
131
163
|
## Notes
|
|
132
164
|
|
|
133
165
|
- Only supports WAV format audio files
|
|
134
166
|
- For other formats, use tools like ffmpeg to convert to WAV first
|
|
135
167
|
- Detection accuracy depends on audio quality and configuration parameters
|
|
168
|
+
- Worker mode requires Node.js 16+ version
|
|
136
169
|
|
|
137
170
|
## License
|
|
138
171
|
|
package/dist/audioAnalyzer.d.ts
CHANGED
|
@@ -5,19 +5,24 @@ export interface AudioFeatures {
|
|
|
5
5
|
zcr: number;
|
|
6
6
|
spectralEnergy: number;
|
|
7
7
|
variance: number;
|
|
8
|
-
mfcc: number[];
|
|
8
|
+
mfcc: number[] | Float32Array;
|
|
9
9
|
spectralCentroid: number;
|
|
10
10
|
spectralRolloff: number;
|
|
11
11
|
spectralFlatness: number;
|
|
12
12
|
}
|
|
13
|
+
export interface AnalyzeAudioOptions {
|
|
14
|
+
useWorkers?: boolean;
|
|
15
|
+
numWorkers?: number;
|
|
16
|
+
}
|
|
13
17
|
/**
|
|
14
18
|
* 计算音频特征
|
|
15
19
|
* @param audioPath WAV 音频文件路径
|
|
16
20
|
* @param windowSize 分析窗口大小(样本数)
|
|
17
21
|
* @param hopSize 窗口跳跃大小(样本数)
|
|
18
22
|
* @param onProgress 进度回调函数,参数为 0-1 之间的进度值
|
|
23
|
+
* @param options 可选配置项,包括是否使用 Worker 并行处理
|
|
19
24
|
*/
|
|
20
|
-
export declare function analyzeAudio(audioPath: string, windowSize?: number, hopSize?: number, onProgress?: (progress: number) => void): Promise<AudioFeatures[]>;
|
|
25
|
+
export declare function analyzeAudio(audioPath: string, windowSize?: number, hopSize?: number, onProgress?: (progress: number) => void, options?: AnalyzeAudioOptions): Promise<AudioFeatures[]>;
|
|
21
26
|
/**
|
|
22
27
|
* 计算特征的统计信息(用于调试和阈值设定)
|
|
23
28
|
*/
|
|
@@ -77,4 +82,3 @@ export declare function getFeatureStats(features: AudioFeatures[]): {
|
|
|
77
82
|
median: number;
|
|
78
83
|
}>;
|
|
79
84
|
};
|
|
80
|
-
//# sourceMappingURL=audioAnalyzer.d.ts.map
|
package/dist/audioAnalyzer.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "fs/promises";
|
|
2
2
|
import * as WavDecoder from "wav-decoder";
|
|
3
3
|
import Meyda from "meyda";
|
|
4
|
+
import { WorkerPool } from "./workerPool.js";
|
|
4
5
|
/**
|
|
5
6
|
* 从 WAV 文件读取音频数据
|
|
6
7
|
*/
|
|
@@ -8,8 +9,13 @@ async function readWavFile(filePath) {
|
|
|
8
9
|
const buffer = await fs.readFile(filePath);
|
|
9
10
|
const audioData = await WavDecoder.decode(buffer.buffer);
|
|
10
11
|
// 如果是多声道,只取第一个声道
|
|
11
|
-
const
|
|
12
|
+
const originalData = audioData.channelData[0];
|
|
12
13
|
const sampleRate = audioData.sampleRate;
|
|
14
|
+
// 将数据转换为SharedArrayBuffer以在Worker间共享
|
|
15
|
+
const dataSize = originalData.length * originalData.BYTES_PER_ELEMENT;
|
|
16
|
+
const sharedBuffer = new SharedArrayBuffer(dataSize);
|
|
17
|
+
const channelData = new Float32Array(sharedBuffer);
|
|
18
|
+
channelData.set(originalData);
|
|
13
19
|
return { sampleRate, channelData };
|
|
14
20
|
}
|
|
15
21
|
/**
|
|
@@ -59,14 +65,24 @@ function calculateVariance(samples, mean) {
|
|
|
59
65
|
return sum / samples.length;
|
|
60
66
|
}
|
|
61
67
|
/**
|
|
62
|
-
*
|
|
63
|
-
* @param audioPath WAV 音频文件路径
|
|
64
|
-
* @param windowSize 分析窗口大小(样本数)
|
|
65
|
-
* @param hopSize 窗口跳跃大小(样本数)
|
|
66
|
-
* @param onProgress 进度回调函数,参数为 0-1 之间的进度值
|
|
68
|
+
* 使用 Worker 并行计算音频特征
|
|
67
69
|
*/
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
+
async function analyzeAudioParallel(channelData, sampleRate, windowSize, hopSize, onProgress, numWorkers) {
|
|
71
|
+
const pool = new WorkerPool({ numWorkers });
|
|
72
|
+
try {
|
|
73
|
+
const result = await pool.execute(channelData, windowSize, hopSize, sampleRate, onProgress);
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// Worker 失败时回退到单线程模式
|
|
78
|
+
console.warn("Worker parallel processing failed, falling back to single-threaded mode:", error);
|
|
79
|
+
return analyzeAudioSingleThread(channelData, sampleRate, windowSize, hopSize, onProgress);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 单线程模式计算音频特征
|
|
84
|
+
*/
|
|
85
|
+
function analyzeAudioSingleThread(channelData, sampleRate, windowSize, hopSize, onProgress) {
|
|
70
86
|
const features = [];
|
|
71
87
|
// 滑动窗口分析
|
|
72
88
|
const totalWindows = Math.floor((channelData.length - windowSize) / hopSize);
|
|
@@ -116,6 +132,23 @@ export async function analyzeAudio(audioPath, windowSize = 2048, hopSize = 512,
|
|
|
116
132
|
}
|
|
117
133
|
return features;
|
|
118
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* 计算音频特征
|
|
137
|
+
* @param audioPath WAV 音频文件路径
|
|
138
|
+
* @param windowSize 分析窗口大小(样本数)
|
|
139
|
+
* @param hopSize 窗口跳跃大小(样本数)
|
|
140
|
+
* @param onProgress 进度回调函数,参数为 0-1 之间的进度值
|
|
141
|
+
* @param options 可选配置项,包括是否使用 Worker 并行处理
|
|
142
|
+
*/
|
|
143
|
+
export async function analyzeAudio(audioPath, windowSize = 2048, hopSize = 512, onProgress, options) {
|
|
144
|
+
const { sampleRate, channelData } = await readWavFile(audioPath);
|
|
145
|
+
// 如果启用 Worker 并行处理
|
|
146
|
+
if (options?.useWorkers) {
|
|
147
|
+
return analyzeAudioParallel(channelData, sampleRate, windowSize, hopSize, onProgress, options.numWorkers);
|
|
148
|
+
}
|
|
149
|
+
// 默认使用单线程模式
|
|
150
|
+
return analyzeAudioSingleThread(channelData, sampleRate, windowSize, hopSize, onProgress);
|
|
151
|
+
}
|
|
119
152
|
/**
|
|
120
153
|
* 计算特征的统计信息(用于调试和阈值设定)
|
|
121
154
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audioAnalyzer.js","sourceRoot":"","sources":["../src/audioAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,KAAK,UAAU,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"audioAnalyzer.js","sourceRoot":"","sources":["../src/audioAnalyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { parentPort } from "worker_threads";
|
|
2
|
+
import Meyda from "meyda";
|
|
3
|
+
/**
|
|
4
|
+
* 计算 RMS(均方根)
|
|
5
|
+
*/
|
|
6
|
+
function calculateRMS(samples) {
|
|
7
|
+
let sum = 0;
|
|
8
|
+
for (let i = 0; i < samples.length; i++) {
|
|
9
|
+
sum += samples[i] * samples[i];
|
|
10
|
+
}
|
|
11
|
+
return Math.sqrt(sum / samples.length);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 计算过零率
|
|
15
|
+
*/
|
|
16
|
+
function calculateZCR(samples) {
|
|
17
|
+
let count = 0;
|
|
18
|
+
for (let i = 1; i < samples.length; i++) {
|
|
19
|
+
if ((samples[i] >= 0 && samples[i - 1] < 0) ||
|
|
20
|
+
(samples[i] < 0 && samples[i - 1] >= 0)) {
|
|
21
|
+
count++;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return count / samples.length;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 简单的 FFT 能量分布计算(高频能量)
|
|
28
|
+
*/
|
|
29
|
+
function calculateSpectralEnergy(samples) {
|
|
30
|
+
// 简化版:计算高频分量(通过差分近似)
|
|
31
|
+
let highFreqEnergy = 0;
|
|
32
|
+
for (let i = 1; i < samples.length; i++) {
|
|
33
|
+
const diff = samples[i] - samples[i - 1];
|
|
34
|
+
highFreqEnergy += diff * diff;
|
|
35
|
+
}
|
|
36
|
+
return highFreqEnergy / samples.length;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 计算方差
|
|
40
|
+
*/
|
|
41
|
+
function calculateVariance(samples, mean) {
|
|
42
|
+
let sum = 0;
|
|
43
|
+
for (let i = 0; i < samples.length; i++) {
|
|
44
|
+
const diff = samples[i] - mean;
|
|
45
|
+
sum += diff * diff;
|
|
46
|
+
}
|
|
47
|
+
return sum / samples.length;
|
|
48
|
+
}
|
|
49
|
+
if (!parentPort) {
|
|
50
|
+
throw new Error("This file must be run as a worker thread");
|
|
51
|
+
}
|
|
52
|
+
parentPort.on("message", (msg) => {
|
|
53
|
+
const { channelData, windowSize, hopSize, startIdx, endIdx, sampleRate } = msg;
|
|
54
|
+
const features = [];
|
|
55
|
+
const totalWindows = endIdx - startIdx;
|
|
56
|
+
try {
|
|
57
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
58
|
+
const start = i * hopSize;
|
|
59
|
+
const end = start + windowSize;
|
|
60
|
+
const window = channelData.slice(start, end);
|
|
61
|
+
const timestamp = start / sampleRate;
|
|
62
|
+
// 时域特征
|
|
63
|
+
const rms = calculateRMS(window);
|
|
64
|
+
const energy = rms * rms;
|
|
65
|
+
const zcr = calculateZCR(window);
|
|
66
|
+
const spectralEnergy = calculateSpectralEnergy(window);
|
|
67
|
+
// 计算方差
|
|
68
|
+
let mean = 0;
|
|
69
|
+
for (let j = 0; j < window.length; j++) {
|
|
70
|
+
mean += Math.abs(window[j]);
|
|
71
|
+
}
|
|
72
|
+
mean /= window.length;
|
|
73
|
+
const variance = calculateVariance(window, mean);
|
|
74
|
+
// 使用 Meyda 一次性提取多个特征(更高效)
|
|
75
|
+
const meydaFeatures = Meyda.extract(["mfcc", "spectralCentroid", "spectralRolloff", "spectralFlatness"], window);
|
|
76
|
+
const mfcc = meydaFeatures?.mfcc || new Array(13).fill(0);
|
|
77
|
+
const spectralCentroid = meydaFeatures?.spectralCentroid || 0;
|
|
78
|
+
const spectralRolloff = meydaFeatures?.spectralRolloff || 0;
|
|
79
|
+
const spectralFlatness = meydaFeatures?.spectralFlatness || 0;
|
|
80
|
+
// 将mfcc转换为Float32Array以便后续可以转移内存
|
|
81
|
+
features.push({
|
|
82
|
+
timestamp,
|
|
83
|
+
rms,
|
|
84
|
+
energy,
|
|
85
|
+
zcr,
|
|
86
|
+
spectralEnergy,
|
|
87
|
+
variance,
|
|
88
|
+
mfcc: new Float32Array(mfcc),
|
|
89
|
+
spectralCentroid,
|
|
90
|
+
spectralRolloff,
|
|
91
|
+
spectralFlatness,
|
|
92
|
+
});
|
|
93
|
+
// 发送进度更新(每 50 个窗口)
|
|
94
|
+
if ((i - startIdx) % 50 === 0) {
|
|
95
|
+
parentPort.postMessage({
|
|
96
|
+
type: "progress",
|
|
97
|
+
current: i - startIdx,
|
|
98
|
+
total: totalWindows,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// 发送最终结果,转移所有 MFCC 数组的 ArrayBuffer 以减少内存占用
|
|
103
|
+
const transferables = [];
|
|
104
|
+
// 收集所有可转移的 ArrayBuffer
|
|
105
|
+
for (const feature of features) {
|
|
106
|
+
if (feature.mfcc instanceof Float32Array) {
|
|
107
|
+
transferables.push(feature.mfcc.buffer);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
parentPort.postMessage({
|
|
111
|
+
type: "result",
|
|
112
|
+
features,
|
|
113
|
+
}, transferables);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
// 发送错误信息
|
|
117
|
+
parentPort.postMessage({
|
|
118
|
+
type: "error",
|
|
119
|
+
error: error instanceof Error ? error.message : String(error),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
//# sourceMappingURL=audioAnalyzer.worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audioAnalyzer.worker.js","sourceRoot":"","sources":["../src/audioAnalyzer.worker.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
export { analyzeAudio, AudioFeatures, getFeatureStats, } from "./audioAnalyzer.js";
|
|
1
|
+
export { analyzeAudio, AudioFeatures, AnalyzeAudioOptions, getFeatureStats, } from "./audioAnalyzer.js";
|
|
2
2
|
export { detectMusicSegments, MusicSegment, DetectionConfig, } from "./segmentDetector.js";
|
|
3
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,EACL,YAAY,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,EACL,YAAY,EAGZ,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,mBAAmB,GAGpB,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AudioFeatures } from "./audioAnalyzer.js";
|
|
2
|
+
export interface WorkerTask {
|
|
3
|
+
channelData: Float32Array;
|
|
4
|
+
windowSize: number;
|
|
5
|
+
hopSize: number;
|
|
6
|
+
startIdx: number;
|
|
7
|
+
endIdx: number;
|
|
8
|
+
sampleRate: number;
|
|
9
|
+
}
|
|
10
|
+
export interface WorkerResult {
|
|
11
|
+
features: AudioFeatures[];
|
|
12
|
+
workerId: number;
|
|
13
|
+
}
|
|
14
|
+
export interface WorkerPoolOptions {
|
|
15
|
+
numWorkers?: number;
|
|
16
|
+
onProgress?: (progress: number) => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Worker 池管理器
|
|
20
|
+
* 用于并行处理音频特征提取
|
|
21
|
+
*/
|
|
22
|
+
export declare class WorkerPool {
|
|
23
|
+
private workers;
|
|
24
|
+
private numWorkers;
|
|
25
|
+
private workerPath;
|
|
26
|
+
constructor(options?: WorkerPoolOptions);
|
|
27
|
+
/**
|
|
28
|
+
* 执行并行音频分析
|
|
29
|
+
*/
|
|
30
|
+
execute(channelData: Float32Array, windowSize: number, hopSize: number, sampleRate: number, onProgress?: (progress: number) => void): Promise<AudioFeatures[]>;
|
|
31
|
+
/**
|
|
32
|
+
* 创建指定数量的 workers
|
|
33
|
+
*/
|
|
34
|
+
private createWorkers;
|
|
35
|
+
/**
|
|
36
|
+
* 执行所有任务
|
|
37
|
+
*/
|
|
38
|
+
private executeTasks;
|
|
39
|
+
/**
|
|
40
|
+
* 终止所有 workers
|
|
41
|
+
*/
|
|
42
|
+
private terminate;
|
|
43
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Worker } from "worker_threads";
|
|
2
|
+
import { cpus } from "os";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import path from "path";
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
/**
|
|
8
|
+
* Worker 池管理器
|
|
9
|
+
* 用于并行处理音频特征提取
|
|
10
|
+
*/
|
|
11
|
+
export class WorkerPool {
|
|
12
|
+
workers = [];
|
|
13
|
+
numWorkers;
|
|
14
|
+
workerPath;
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
// 默认使用 CPU 核心数,但不超过 8 个
|
|
17
|
+
this.numWorkers = options.numWorkers || Math.min(cpus().length, 8);
|
|
18
|
+
this.workerPath = path.join(__dirname, "audioAnalyzer.worker.js");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 执行并行音频分析
|
|
22
|
+
*/
|
|
23
|
+
async execute(channelData, windowSize, hopSize, sampleRate, onProgress) {
|
|
24
|
+
const totalWindows = Math.floor((channelData.length - windowSize) / hopSize);
|
|
25
|
+
// 如果窗口数太少,不值得使用多个 worker
|
|
26
|
+
if (totalWindows < this.numWorkers * 10) {
|
|
27
|
+
this.numWorkers = Math.max(1, Math.floor(totalWindows / 10));
|
|
28
|
+
}
|
|
29
|
+
// 计算每个 worker 处理的窗口范围
|
|
30
|
+
const windowsPerWorker = Math.ceil(totalWindows / this.numWorkers);
|
|
31
|
+
const tasks = [];
|
|
32
|
+
for (let i = 0; i < this.numWorkers; i++) {
|
|
33
|
+
const startIdx = i * windowsPerWorker;
|
|
34
|
+
const endIdx = Math.min((i + 1) * windowsPerWorker, totalWindows);
|
|
35
|
+
if (startIdx >= totalWindows)
|
|
36
|
+
break;
|
|
37
|
+
// 不创建副本,直接传递原始数据的引用
|
|
38
|
+
// Worker 只读取需要的部分,不修改原始数据
|
|
39
|
+
tasks.push({
|
|
40
|
+
channelData,
|
|
41
|
+
windowSize,
|
|
42
|
+
hopSize,
|
|
43
|
+
startIdx,
|
|
44
|
+
endIdx,
|
|
45
|
+
sampleRate,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// 创建 workers
|
|
49
|
+
this.createWorkers(tasks.length);
|
|
50
|
+
// 执行任务并收集结果
|
|
51
|
+
const results = await this.executeTasks(tasks, onProgress);
|
|
52
|
+
// 清理 workers
|
|
53
|
+
this.terminate();
|
|
54
|
+
// 按窗口索引排序并合并结果
|
|
55
|
+
return results.flatMap((r) => r.features);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 创建指定数量的 workers
|
|
59
|
+
*/
|
|
60
|
+
createWorkers(count) {
|
|
61
|
+
for (let i = 0; i < count; i++) {
|
|
62
|
+
const worker = new Worker(this.workerPath, {
|
|
63
|
+
// 支持 ESM 模块
|
|
64
|
+
// Node.js 16+ 需要显式指定
|
|
65
|
+
});
|
|
66
|
+
this.workers.push(worker);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 执行所有任务
|
|
71
|
+
*/
|
|
72
|
+
async executeTasks(tasks, onProgress) {
|
|
73
|
+
const progressMap = new Map();
|
|
74
|
+
const totalProgress = tasks.reduce((sum, t) => sum + (t.endIdx - t.startIdx), 0);
|
|
75
|
+
// 为每个 worker 分配任务并等待结果
|
|
76
|
+
const promises = tasks.map((task, idx) => {
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
const worker = this.workers[idx];
|
|
79
|
+
const handleMessage = (msg) => {
|
|
80
|
+
if (msg.type === "progress") {
|
|
81
|
+
// 更新进度
|
|
82
|
+
progressMap.set(idx, msg.current);
|
|
83
|
+
if (onProgress) {
|
|
84
|
+
const currentTotal = Array.from(progressMap.values()).reduce((sum, val) => sum + val, 0);
|
|
85
|
+
onProgress(currentTotal / totalProgress);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else if (msg.type === "result") {
|
|
89
|
+
worker.off("message", handleMessage);
|
|
90
|
+
worker.off("error", handleError);
|
|
91
|
+
resolve({
|
|
92
|
+
features: msg.features,
|
|
93
|
+
workerId: idx,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
else if (msg.type === "error") {
|
|
97
|
+
worker.off("message", handleMessage);
|
|
98
|
+
worker.off("error", handleError);
|
|
99
|
+
reject(new Error(`Worker ${idx} error: ${msg.error}`));
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const handleError = (error) => {
|
|
103
|
+
worker.off("message", handleMessage);
|
|
104
|
+
worker.off("error", handleError);
|
|
105
|
+
reject(error);
|
|
106
|
+
};
|
|
107
|
+
worker.on("message", handleMessage);
|
|
108
|
+
worker.on("error", handleError);
|
|
109
|
+
// 发送任务,不转移channelData的所有权
|
|
110
|
+
// 多个worker可以共享同一个ArrayBuffer进行只读访问
|
|
111
|
+
worker.postMessage(task);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
return Promise.all(promises);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 终止所有 workers
|
|
118
|
+
*/
|
|
119
|
+
terminate() {
|
|
120
|
+
for (const worker of this.workers) {
|
|
121
|
+
worker.terminate();
|
|
122
|
+
}
|
|
123
|
+
this.workers = [];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=workerPool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerPool.js","sourceRoot":"","sources":["../src/workerPool.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"audioAnalyzer.d.ts","sourceRoot":"","sources":["../src/audioAnalyzer.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAsED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,MAAa,EACzB,OAAO,GAAE,MAAY,EACrB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GACtC,OAAO,CAAC,aAAa,EAAE,CAAC,CAgE1B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAUzC,KAAK,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;EA+FL"}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"segmentDetector.d.ts","sourceRoot":"","sources":["../src/segmentDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAmB,MAAM,oBAAoB,CAAC;AAEpE,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAE9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAgCD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,aAAa,EAAE,EACzB,MAAM,GAAE,eAAoB,GAC3B,YAAY,EAAE,CAkMhB"}
|