termux-stt 1.5.0 → 2.0.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 +17 -50
- package/README.pypi.md +7 -7
- package/index.d.ts +8 -3
- package/index.js +8 -6
- package/lib/hybrid.js +1 -1
- package/lib/sherpa.js +97 -0
- package/package.json +2 -2
- package/lib/vosk.js +0 -60
package/README.md
CHANGED
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/termux-stt)
|
|
7
7
|
[](https://github.com/uno-km/termux-stt)
|
|
8
8
|
|
|
9
|
-
> **디바이스 리소스를 활용한 통합 온디바이스 음성인식(STT) 및
|
|
10
|
-
> *Unified On-Device Speech-to-Text
|
|
9
|
+
> **디바이스 리소스를 활용한 통합 온디바이스 음성인식(STT) 및 차세대 신경망 화자 분리(PyAnnote 3.0 + CAM++ 192d + TS-VAD) 프레임워크**
|
|
10
|
+
> *Unified On-Device Speech-to-Text & Neural Multi-Speaker Diarization (PyAnnote 3.0 + CAM++ 192d + TS-VAD Overlap Resolver)*
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
## Architecture & Overview
|
|
15
15
|
|
|
16
|
-
Whisper.cpp
|
|
16
|
+
Whisper.cpp(네이티브 Vulkan GPU / NEON CPU) 및 Sherpa-ONNX 듀얼 모던 엔진을 통합하고, PyAnnote 3.0 신경망 세그멘테이션, 3D-Speaker CAM++ 192차원 성문 임베딩, TS-VAD 동시 발화(마이크 물림) 분리기를 결합하여 완전 온디바이스 실시간 다중 화자 음성인식을 구현합니다.
|
|
17
17
|
|
|
18
|
-
Integrates Whisper.cpp
|
|
18
|
+
Integrates Whisper.cpp (Native Vulkan GPU / NEON CPU) and Sherpa-ONNX with PyAnnote 3.0 neural segmentation, 3D-Speaker CAM++ 192-dim embeddings, and TS-VAD overlapped speech resolution with zero cloud egress.
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
@@ -28,17 +28,17 @@ pip install termux-stt
|
|
|
28
28
|
```python
|
|
29
29
|
from termux_stt import create_engine
|
|
30
30
|
|
|
31
|
-
# 1. Initialize Engine with
|
|
32
|
-
engine = create_engine("whisper", model="small", lang="
|
|
31
|
+
# 1. Initialize Whisper Engine with Vulkan GPU / CPU Hybrid Acceleration
|
|
32
|
+
engine = create_engine("whisper", model="small", lang="ko", threads=4, split_mode=True)
|
|
33
33
|
|
|
34
34
|
# 2. Transcribe Audio directly into Subtitles
|
|
35
35
|
result = engine.transcribe("samples/jfk_1min.wav")
|
|
36
36
|
print("Transcript:\n", result.text)
|
|
37
37
|
print("SRT Subtitles:\n", result.to_srt())
|
|
38
38
|
|
|
39
|
-
# 3.
|
|
40
|
-
hybrid = create_engine("hybrid", lang="
|
|
41
|
-
diar_result = hybrid.diarize("samples/
|
|
39
|
+
# 3. Multi-Speaker Neural Diarization (PyAnnote 3.0 + CAM++ 192d + TS-VAD)
|
|
40
|
+
hybrid = create_engine("hybrid", lang="ko", num_speakers=2)
|
|
41
|
+
diar_result = hybrid.diarize("samples/kor_diarization.wav")
|
|
42
42
|
for seg in diar_result.segments:
|
|
43
43
|
print(f"[{seg.speaker}] ({seg.start:.1f}s -> {seg.end:.1f}s): {seg.text}")
|
|
44
44
|
|
|
@@ -53,12 +53,19 @@ const { createEngine } = require("termux-stt");
|
|
|
53
53
|
|
|
54
54
|
async function main() {
|
|
55
55
|
// 1. Initialize Whisper Engine
|
|
56
|
-
const engine = createEngine("whisper", { model: "tiny", lang: "
|
|
56
|
+
const engine = createEngine("whisper", { model: "tiny", lang: "ko", threads: 4 });
|
|
57
57
|
|
|
58
58
|
// 2. Transcribe Audio
|
|
59
59
|
const result = await engine.transcribe("samples/jfk_1min.wav");
|
|
60
60
|
console.log("Transcript:", result.text);
|
|
61
61
|
console.log("SRT Subtitles:\n", result.toSrt());
|
|
62
|
+
|
|
63
|
+
// 3. Neural Diarization via Hybrid Engine
|
|
64
|
+
const hybrid = createEngine("hybrid", { lang: "ko", numSpeakers: 2 });
|
|
65
|
+
const diarResult = await hybrid.diarize("samples/kor_diarization.wav");
|
|
66
|
+
for (const seg of diarResult.segments) {
|
|
67
|
+
console.log(`[${seg.speaker}] (${seg.start.toFixed(1)}s -> ${seg.end.toFixed(1)}s): ${seg.text}`);
|
|
68
|
+
}
|
|
62
69
|
}
|
|
63
70
|
main();
|
|
64
71
|
|
|
@@ -66,46 +73,6 @@ main();
|
|
|
66
73
|
|
|
67
74
|
---
|
|
68
75
|
|
|
69
|
-
## Distributed Clustering & Memory Pooling (AMEVA-Cluster)
|
|
70
|
-
|
|
71
|
-
Termux-STT natively integrates with **AMEVA-Cluster** (`pip install ameva-cluster`) for distributed large-model speech recognition (e.g. `whisper-large-v3-turbo`) across interconnected mobile fleets.
|
|
72
|
-
|
|
73
|
-
### 1. Install Cluster Runtime
|
|
74
|
-
```bash
|
|
75
|
-
pip install ameva-cluster
|
|
76
|
-
# or Node.js:
|
|
77
|
-
npm install @ameva/cluster
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### 2. Launch Worker Node on Remote Phone
|
|
81
|
-
```bash
|
|
82
|
-
# On remote worker device (e.g. Galaxy A53):
|
|
83
|
-
ameva-cluster worker --port 50052
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### 3. Distributed Transcription via Master Node
|
|
87
|
-
```bash
|
|
88
|
-
# Master node sharding Whisper-Large layers across remote phone RAM:
|
|
89
|
-
termux-stt transcribe meeting.wav \
|
|
90
|
-
--model whisper-large-v3-turbo.bin \
|
|
91
|
-
--rpc 192.0.2.10:50052,192.0.2.11:50052
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
```python
|
|
95
|
-
from termux_stt import create_engine
|
|
96
|
-
|
|
97
|
-
# Python SDK Distributed STT
|
|
98
|
-
engine = create_engine(
|
|
99
|
-
"whisper",
|
|
100
|
-
model="large-v3-turbo",
|
|
101
|
-
cluster_rpc_servers="192.0.2.10:50052,192.0.2.11:50052"
|
|
102
|
-
)
|
|
103
|
-
result = engine.transcribe("meeting.wav")
|
|
104
|
-
print(result.text)
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
76
|
## Official Documentation & Benchmarks
|
|
110
77
|
- [Official Architecture & API Reference](https://uno-km.vercel.app/lib/stt/)
|
|
111
78
|
- [Ecosystem Metrics & Registry Stats](https://uno-km.vercel.app/foundation/metrics)
|
package/README.pypi.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://pypi.org/project/termux-stt/)
|
|
5
5
|
[](https://github.com/uno-km/termux-stt)
|
|
6
6
|
|
|
7
|
-
> Unified On-Device Speech-to-Text
|
|
7
|
+
> Unified On-Device Speech-to-Text & Neural Multi-Speaker Diarization (PyAnnote 3.0 + CAM++ 192d + TS-VAD Overlap Resolver)
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
@@ -17,24 +17,24 @@ pip install termux-stt
|
|
|
17
17
|
```python
|
|
18
18
|
from termux_stt import create_engine
|
|
19
19
|
|
|
20
|
-
# 1. Initialize Engine with
|
|
21
|
-
engine = create_engine("whisper", model="small", lang="
|
|
20
|
+
# 1. Initialize Whisper Engine with Vulkan GPU / CPU Hybrid Acceleration
|
|
21
|
+
engine = create_engine("whisper", model="small", lang="ko", threads=4, split_mode=True)
|
|
22
22
|
|
|
23
23
|
# 2. Transcribe Audio directly into Subtitles
|
|
24
24
|
result = engine.transcribe("samples/jfk_1min.wav")
|
|
25
25
|
print("Transcript:\n", result.text)
|
|
26
26
|
print("SRT Subtitles:\n", result.to_srt())
|
|
27
27
|
|
|
28
|
-
# 3.
|
|
29
|
-
hybrid = create_engine("hybrid", lang="
|
|
30
|
-
diar_result = hybrid.diarize("samples/
|
|
28
|
+
# 3. Multi-Speaker Neural Diarization (PyAnnote 3.0 + CAM++ 192d + TS-VAD)
|
|
29
|
+
hybrid = create_engine("hybrid", lang="ko", num_speakers=2)
|
|
30
|
+
diar_result = hybrid.diarize("samples/kor_diarization.wav")
|
|
31
31
|
for seg in diar_result.segments:
|
|
32
32
|
print(f"[{seg.speaker}] ({seg.start:.1f}s -> {seg.end:.1f}s): {seg.text}")
|
|
33
33
|
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
## Description
|
|
37
|
-
Integrates Whisper.cpp
|
|
37
|
+
Integrates Whisper.cpp (Native Vulkan GPU / NEON CPU) and Sherpa-ONNX with PyAnnote 3.0 neural segmentation, 3D-Speaker CAM++ 192-dim embeddings, and TS-VAD overlapped speech resolution with zero cloud egress.
|
|
38
38
|
|
|
39
39
|
## Documentation
|
|
40
40
|
- [Official Documentation & API Reference](https://uno-km.vercel.app/lib/stt/)
|
package/index.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export class WhisperEngine extends Engine {
|
|
|
45
45
|
diarize(audioPath: string, numSpeakers?: number): Promise<TranscriptResult>;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
export class
|
|
48
|
+
export class SherpaEngine extends Engine {
|
|
49
49
|
transcribe(audioPath: string, options?: any): Promise<TranscriptResult>;
|
|
50
50
|
diarize(audioPath: string, numSpeakers?: number): Promise<TranscriptResult>;
|
|
51
51
|
}
|
|
@@ -56,8 +56,13 @@ export class HybridEngine extends Engine {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export function createEngine(
|
|
59
|
-
engineName?: 'whisper' | '
|
|
59
|
+
engineName?: 'whisper' | 'sherpa' | 'hybrid' | string,
|
|
60
60
|
options?: EngineOptions
|
|
61
61
|
): Engine;
|
|
62
62
|
|
|
63
|
-
export
|
|
63
|
+
export class TermuxSTT {
|
|
64
|
+
engine: Engine;
|
|
65
|
+
constructor(options?: EngineOptions & { engine?: string });
|
|
66
|
+
transcribe(filePath: string, options?: any): Promise<TranscriptResult>;
|
|
67
|
+
diarize(filePath: string, options?: any): Promise<TranscriptResult>;
|
|
68
|
+
}
|
package/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* termux-stt Node.js entry point
|
|
2
|
+
* termux-stt Node.js entry point (v2.0.0)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const { Engine, TranscriptResult, Segment, formatTime } = require('./lib/engine');
|
|
6
6
|
const { WhisperEngine } = require('./lib/whisper');
|
|
7
|
-
const {
|
|
7
|
+
const { SherpaEngine } = require('./lib/sherpa');
|
|
8
8
|
const { HybridEngine } = require('./lib/hybrid');
|
|
9
9
|
|
|
10
10
|
function createEngine(engineName = 'whisper', options = {}) {
|
|
@@ -12,12 +12,14 @@ function createEngine(engineName = 'whisper', options = {}) {
|
|
|
12
12
|
switch (name) {
|
|
13
13
|
case 'whisper':
|
|
14
14
|
return new WhisperEngine(options);
|
|
15
|
-
case '
|
|
16
|
-
return new
|
|
15
|
+
case 'sherpa':
|
|
16
|
+
return new SherpaEngine(options);
|
|
17
17
|
case 'hybrid':
|
|
18
18
|
return new HybridEngine(options);
|
|
19
|
+
case 'vosk':
|
|
20
|
+
throw new Error("Vosk has been deprecated and completely removed in v2.0.0. Please use 'sherpa' (SenseVoice) or 'whisper'.");
|
|
19
21
|
default:
|
|
20
|
-
throw new Error(`Unknown engine: ${engineName}. Available: whisper,
|
|
22
|
+
throw new Error(`Unknown engine: ${engineName}. Available: whisper, sherpa, hybrid`);
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -41,7 +43,7 @@ module.exports = {
|
|
|
41
43
|
TranscriptResult,
|
|
42
44
|
Segment,
|
|
43
45
|
WhisperEngine,
|
|
44
|
-
|
|
46
|
+
SherpaEngine,
|
|
45
47
|
HybridEngine,
|
|
46
48
|
formatTime
|
|
47
49
|
};
|
package/lib/hybrid.js
CHANGED
package/lib/sherpa.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SherpaEngine for Node.js - Non-Autoregressive SenseVoice STT & Neural Diarization
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
const { Engine, TranscriptResult, Segment } = require('./engine');
|
|
7
|
+
|
|
8
|
+
class SherpaEngine extends Engine {
|
|
9
|
+
constructor(config = {}) {
|
|
10
|
+
super(config);
|
|
11
|
+
this.model = config.model || 'sensevoice-small-int8';
|
|
12
|
+
this.lang = config.lang || 'ko';
|
|
13
|
+
this.threads = config.threads || 4;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async transcribe(audioPath, options = {}) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const args = [
|
|
19
|
+
'-m', 'termux_stt.cli.main',
|
|
20
|
+
'transcribe',
|
|
21
|
+
'--engine', 'sherpa',
|
|
22
|
+
'--model', this.model,
|
|
23
|
+
'--lang', this.lang,
|
|
24
|
+
'--format', 'json',
|
|
25
|
+
audioPath
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const proc = spawn('python', args, { env: process.env });
|
|
29
|
+
let stdout = '';
|
|
30
|
+
let stderr = '';
|
|
31
|
+
|
|
32
|
+
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
33
|
+
proc.stderr.on('data', (d) => { stderr += d.toString(); });
|
|
34
|
+
|
|
35
|
+
proc.on('close', (code) => {
|
|
36
|
+
if (code !== 0) {
|
|
37
|
+
return reject(new Error(`Sherpa transcription failed (code ${code}): ${stderr}`));
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(stdout);
|
|
41
|
+
const segments = (parsed.segments || []).map(
|
|
42
|
+
s => new Segment(s.start, s.end, s.text, s.speaker, s.confidence)
|
|
43
|
+
);
|
|
44
|
+
resolve(new TranscriptResult(parsed.text, segments, parsed.language || this.lang, parsed.duration));
|
|
45
|
+
} catch (e) {
|
|
46
|
+
resolve(new TranscriptResult(stdout.trim(), [new Segment(0, 0, stdout.trim())], this.lang));
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async diarize(audioPath, numSpeakers = 2) {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const args = [
|
|
55
|
+
'-m', 'termux_stt.cli.main',
|
|
56
|
+
'diarize',
|
|
57
|
+
'--engine', 'sherpa',
|
|
58
|
+
'--speakers', String(numSpeakers),
|
|
59
|
+
'--format', 'json',
|
|
60
|
+
audioPath
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const proc = spawn('python', args, { env: process.env });
|
|
64
|
+
let stdout = '';
|
|
65
|
+
let stderr = '';
|
|
66
|
+
|
|
67
|
+
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
68
|
+
proc.stderr.on('data', (d) => { stderr += d.toString(); });
|
|
69
|
+
|
|
70
|
+
proc.on('close', (code) => {
|
|
71
|
+
if (code !== 0) {
|
|
72
|
+
return reject(new Error(`Sherpa diarization failed (code ${code}): ${stderr}`));
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const parsed = JSON.parse(stdout);
|
|
76
|
+
const segments = (parsed.segments || []).map(
|
|
77
|
+
s => new Segment(s.start, s.end, s.text, s.speaker, s.confidence)
|
|
78
|
+
);
|
|
79
|
+
resolve(new TranscriptResult(parsed.text, segments, parsed.language || this.lang, parsed.duration));
|
|
80
|
+
} catch (e) {
|
|
81
|
+
resolve(new TranscriptResult(stdout.trim(), [new Segment(0, 0, stdout.trim())], this.lang));
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getInfo() {
|
|
88
|
+
return {
|
|
89
|
+
name: 'Sherpa-ONNX (Node.js)',
|
|
90
|
+
model: this.model,
|
|
91
|
+
language: this.lang,
|
|
92
|
+
threads: this.threads
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { SherpaEngine };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "termux-stt",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "On-device Speech-to-Text & speaker diarization framework
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "On-device Speech-to-Text & neural speaker diarization framework for Android Termux (Whisper Vulkan GPU & Sherpa-ONNX)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"bin": {
|
package/lib/vosk.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* VoskEngine for Node.js
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const { spawn } = require('child_process');
|
|
6
|
-
const { Engine, TranscriptResult, Segment } = require('./engine');
|
|
7
|
-
|
|
8
|
-
class VoskEngine extends Engine {
|
|
9
|
-
constructor(config = {}) {
|
|
10
|
-
super(config);
|
|
11
|
-
this.model = config.model || 'small-ko-0.22';
|
|
12
|
-
this.lang = config.lang || 'ko';
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async transcribe(audioPath, options = {}) {
|
|
16
|
-
return new Promise((resolve, reject) => {
|
|
17
|
-
const args = [
|
|
18
|
-
'-m', 'termux_stt.cli.main',
|
|
19
|
-
'transcribe',
|
|
20
|
-
'--engine', 'vosk',
|
|
21
|
-
'--model', this.model,
|
|
22
|
-
'--lang', this.lang,
|
|
23
|
-
'--format', 'json',
|
|
24
|
-
audioPath
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
const proc = spawn('python', args, { env: process.env });
|
|
28
|
-
let stdout = '';
|
|
29
|
-
let stderr = '';
|
|
30
|
-
|
|
31
|
-
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
32
|
-
proc.stderr.on('data', (d) => { stderr += d.toString(); });
|
|
33
|
-
|
|
34
|
-
proc.on('close', (code) => {
|
|
35
|
-
if (code !== 0) {
|
|
36
|
-
return reject(new Error(`Vosk transcription failed (code ${code}): ${stderr}`));
|
|
37
|
-
}
|
|
38
|
-
try {
|
|
39
|
-
const parsed = JSON.parse(stdout);
|
|
40
|
-
const segments = (parsed.segments || []).map(
|
|
41
|
-
s => new Segment(s.start, s.end, s.text, s.speaker, s.confidence)
|
|
42
|
-
);
|
|
43
|
-
resolve(new TranscriptResult(parsed.text, segments, parsed.language || this.lang, parsed.duration));
|
|
44
|
-
} catch (e) {
|
|
45
|
-
resolve(new TranscriptResult(stdout.trim(), [new Segment(0, 0, stdout.trim())], this.lang));
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
getInfo() {
|
|
52
|
-
return {
|
|
53
|
-
name: 'Vosk (Node.js)',
|
|
54
|
-
model: this.model,
|
|
55
|
-
language: this.lang
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
module.exports = { VoskEngine };
|