@mingxy/ocosay 1.1.4 → 1.1.6
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.
|
@@ -1,31 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Naudiodon Backend - 基于 PortAudio 的跨平台音频播放后端
|
|
3
3
|
* 支持真正的流式播放(边收边播)
|
|
4
|
+
*
|
|
5
|
+
* 注意:naudiodon v2 API 变化:
|
|
6
|
+
* - v1: new naudiodon({sampleRate, channels, bitDepth})
|
|
7
|
+
* - v2: AudioIO({outOptions: {sampleRate, channelCount, sampleFormat}})
|
|
4
8
|
*/
|
|
5
9
|
import { AudioBackend, BackendOptions } from './base';
|
|
6
|
-
interface
|
|
10
|
+
interface IoStreamWrite {
|
|
7
11
|
start(): void;
|
|
8
|
-
write(chunk: Buffer): void;
|
|
12
|
+
write(chunk: Buffer, callback?: (err?: Error) => void): void;
|
|
9
13
|
end(): void;
|
|
10
|
-
quit(): void;
|
|
11
|
-
on(event:
|
|
14
|
+
quit(callback?: () => void): void;
|
|
15
|
+
on(event: 'error', callback: (error: Error) => void): void;
|
|
16
|
+
on(event: 'close', callback: () => void): void;
|
|
17
|
+
}
|
|
18
|
+
interface AudioOptions {
|
|
19
|
+
sampleRate?: number;
|
|
20
|
+
channelCount?: number;
|
|
21
|
+
sampleFormat?: number;
|
|
12
22
|
}
|
|
13
|
-
interface
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
bitDepth?: number;
|
|
18
|
-
}): NaudiodonAudioOutput;
|
|
23
|
+
interface AudioIO {
|
|
24
|
+
(options: {
|
|
25
|
+
outOptions: AudioOptions;
|
|
26
|
+
}): IoStreamWrite;
|
|
19
27
|
}
|
|
20
28
|
declare global {
|
|
21
29
|
interface NodeModule {
|
|
22
|
-
require(id: 'naudiodon'):
|
|
30
|
+
require(id: 'naudiodon'): {
|
|
31
|
+
AudioIO: AudioIO;
|
|
32
|
+
};
|
|
23
33
|
}
|
|
24
34
|
}
|
|
25
35
|
export declare class NaudiodonBackend implements AudioBackend {
|
|
26
36
|
readonly name = "naudiodon";
|
|
27
37
|
readonly supportsStreaming = true;
|
|
28
|
-
private
|
|
38
|
+
private audioStream?;
|
|
29
39
|
private events?;
|
|
30
40
|
private _started;
|
|
31
41
|
private _paused;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Naudiodon Backend - 基于 PortAudio 的跨平台音频播放后端
|
|
3
3
|
* 支持真正的流式播放(边收边播)
|
|
4
|
+
*
|
|
5
|
+
* 注意:naudiodon v2 API 变化:
|
|
6
|
+
* - v1: new naudiodon({sampleRate, channels, bitDepth})
|
|
7
|
+
* - v2: AudioIO({outOptions: {sampleRate, channelCount, sampleFormat}})
|
|
4
8
|
*/
|
|
5
9
|
class UnsupportedError extends Error {
|
|
6
10
|
constructor(message) {
|
|
@@ -11,7 +15,7 @@ class UnsupportedError extends Error {
|
|
|
11
15
|
export class NaudiodonBackend {
|
|
12
16
|
name = 'naudiodon';
|
|
13
17
|
supportsStreaming = true;
|
|
14
|
-
|
|
18
|
+
audioStream;
|
|
15
19
|
events;
|
|
16
20
|
_started = false;
|
|
17
21
|
_paused = false;
|
|
@@ -31,16 +35,17 @@ export class NaudiodonBackend {
|
|
|
31
35
|
return;
|
|
32
36
|
try {
|
|
33
37
|
const naudiodon = require('naudiodon');
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
this.audioStream = naudiodon.AudioIO({
|
|
39
|
+
outOptions: {
|
|
40
|
+
sampleRate: this.sampleRate,
|
|
41
|
+
channelCount: this.channels,
|
|
42
|
+
sampleFormat: 16
|
|
43
|
+
}
|
|
39
44
|
});
|
|
40
|
-
this.
|
|
45
|
+
this.audioStream.on('error', (error) => {
|
|
41
46
|
this.handleError(error);
|
|
42
47
|
});
|
|
43
|
-
this.
|
|
48
|
+
this.audioStream.start();
|
|
44
49
|
this._started = true;
|
|
45
50
|
this._stopped = false;
|
|
46
51
|
this.events?.onStart?.();
|
|
@@ -55,16 +60,16 @@ export class NaudiodonBackend {
|
|
|
55
60
|
write(chunk) {
|
|
56
61
|
if (!this._started || this._stopped)
|
|
57
62
|
return;
|
|
58
|
-
if (this.
|
|
63
|
+
if (this.audioStream) {
|
|
59
64
|
const adjustedChunk = this.adjustVolume(chunk);
|
|
60
|
-
this.
|
|
65
|
+
this.audioStream.write(adjustedChunk);
|
|
61
66
|
this.bytesWritten += chunk.length;
|
|
62
67
|
this.events?.onProgress?.(this.bytesWritten);
|
|
63
68
|
}
|
|
64
69
|
}
|
|
65
70
|
end() {
|
|
66
|
-
if (this.
|
|
67
|
-
this.
|
|
71
|
+
if (this.audioStream) {
|
|
72
|
+
this.audioStream.end();
|
|
68
73
|
}
|
|
69
74
|
}
|
|
70
75
|
pause() {
|
|
@@ -82,14 +87,14 @@ export class NaudiodonBackend {
|
|
|
82
87
|
this._stopped = true;
|
|
83
88
|
this._started = false;
|
|
84
89
|
this._paused = false;
|
|
85
|
-
if (this.
|
|
90
|
+
if (this.audioStream) {
|
|
86
91
|
try {
|
|
87
|
-
this.
|
|
92
|
+
this.audioStream.quit();
|
|
88
93
|
}
|
|
89
94
|
catch (e) {
|
|
90
95
|
// 忽略退出错误
|
|
91
96
|
}
|
|
92
|
-
this.
|
|
97
|
+
this.audioStream = undefined;
|
|
93
98
|
}
|
|
94
99
|
this.bytesWritten = 0;
|
|
95
100
|
this.events?.onStop?.();
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mingxy/ocosay",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "OpenCode TTS 播放插件 - 支持豆包模式边接收边朗读",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/plugin.js",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"zod": "^4.3.6"
|
|
49
49
|
},
|
|
50
50
|
"optionalDependencies": {
|
|
51
|
-
"naudiodon": "^
|
|
51
|
+
"naudiodon": "^2.0.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/howler": "^2.2.12",
|
package/dist/plugin.js
CHANGED
|
@@ -6750,7 +6750,7 @@ var UnsupportedError = class extends Error {
|
|
|
6750
6750
|
var NaudiodonBackend = class {
|
|
6751
6751
|
name = "naudiodon";
|
|
6752
6752
|
supportsStreaming = true;
|
|
6753
|
-
|
|
6753
|
+
audioStream;
|
|
6754
6754
|
events;
|
|
6755
6755
|
_started = false;
|
|
6756
6756
|
_paused = false;
|
|
@@ -6769,16 +6769,17 @@ var NaudiodonBackend = class {
|
|
|
6769
6769
|
if (this._started) return;
|
|
6770
6770
|
try {
|
|
6771
6771
|
const naudiodon = __require("naudiodon");
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6772
|
+
this.audioStream = naudiodon.AudioIO({
|
|
6773
|
+
outOptions: {
|
|
6774
|
+
sampleRate: this.sampleRate,
|
|
6775
|
+
channelCount: this.channels,
|
|
6776
|
+
sampleFormat: 16
|
|
6777
|
+
}
|
|
6777
6778
|
});
|
|
6778
|
-
this.
|
|
6779
|
+
this.audioStream.on("error", (error) => {
|
|
6779
6780
|
this.handleError(error);
|
|
6780
6781
|
});
|
|
6781
|
-
this.
|
|
6782
|
+
this.audioStream.start();
|
|
6782
6783
|
this._started = true;
|
|
6783
6784
|
this._stopped = false;
|
|
6784
6785
|
this.events?.onStart?.();
|
|
@@ -6791,16 +6792,16 @@ var NaudiodonBackend = class {
|
|
|
6791
6792
|
}
|
|
6792
6793
|
write(chunk) {
|
|
6793
6794
|
if (!this._started || this._stopped) return;
|
|
6794
|
-
if (this.
|
|
6795
|
+
if (this.audioStream) {
|
|
6795
6796
|
const adjustedChunk = this.adjustVolume(chunk);
|
|
6796
|
-
this.
|
|
6797
|
+
this.audioStream.write(adjustedChunk);
|
|
6797
6798
|
this.bytesWritten += chunk.length;
|
|
6798
6799
|
this.events?.onProgress?.(this.bytesWritten);
|
|
6799
6800
|
}
|
|
6800
6801
|
}
|
|
6801
6802
|
end() {
|
|
6802
|
-
if (this.
|
|
6803
|
-
this.
|
|
6803
|
+
if (this.audioStream) {
|
|
6804
|
+
this.audioStream.end();
|
|
6804
6805
|
}
|
|
6805
6806
|
}
|
|
6806
6807
|
pause() {
|
|
@@ -6816,12 +6817,12 @@ var NaudiodonBackend = class {
|
|
|
6816
6817
|
this._stopped = true;
|
|
6817
6818
|
this._started = false;
|
|
6818
6819
|
this._paused = false;
|
|
6819
|
-
if (this.
|
|
6820
|
+
if (this.audioStream) {
|
|
6820
6821
|
try {
|
|
6821
|
-
this.
|
|
6822
|
+
this.audioStream.quit();
|
|
6822
6823
|
} catch (e) {
|
|
6823
6824
|
}
|
|
6824
|
-
this.
|
|
6825
|
+
this.audioStream = void 0;
|
|
6825
6826
|
}
|
|
6826
6827
|
this.bytesWritten = 0;
|
|
6827
6828
|
this.events?.onStop?.();
|
|
@@ -9577,7 +9578,7 @@ function markNaudiodonSkipped() {
|
|
|
9577
9578
|
}
|
|
9578
9579
|
async function ensureNaudiodonCompiled() {
|
|
9579
9580
|
if (shouldSkipNaudiodon()) {
|
|
9580
|
-
logger7.info("naudiodon skipped previously
|
|
9581
|
+
logger7.info("naudiodon skipped previously");
|
|
9581
9582
|
return;
|
|
9582
9583
|
}
|
|
9583
9584
|
try {
|
|
@@ -9585,12 +9586,12 @@ async function ensureNaudiodonCompiled() {
|
|
|
9585
9586
|
logger7.info("naudiodon already compiled");
|
|
9586
9587
|
return;
|
|
9587
9588
|
} catch {
|
|
9588
|
-
logger7.info("naudiodon not compiled,
|
|
9589
|
+
logger7.info("naudiodon not compiled, will attempt to compile");
|
|
9590
|
+
notificationService.info("\u6B63\u5728\u7F16\u8BD1 naudiodon...", "Ocosay \u97F3\u9891\u540E\u7AEF");
|
|
9589
9591
|
}
|
|
9590
9592
|
try {
|
|
9591
9593
|
const naudiodonPath = dirname2(require2.resolve("naudiodon"));
|
|
9592
|
-
logger7.info({ naudiodonPath }, "found naudiodon
|
|
9593
|
-
notificationService.info("\u6B63\u5728\u7F16\u8BD1 naudiodon...", "Ocosay \u97F3\u9891\u540E\u7AEF");
|
|
9594
|
+
logger7.info({ naudiodonPath }, "found naudiodon, rebuilding");
|
|
9594
9595
|
execSync("npm rebuild naudiodon", {
|
|
9595
9596
|
cwd: naudiodonPath,
|
|
9596
9597
|
stdio: "inherit"
|
|
@@ -9598,7 +9599,7 @@ async function ensureNaudiodonCompiled() {
|
|
|
9598
9599
|
logger7.info("naudiodon compiled successfully");
|
|
9599
9600
|
notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA");
|
|
9600
9601
|
} catch (err) {
|
|
9601
|
-
logger7.warn({ err }, "naudiodon rebuild failed, checking for PortAudio
|
|
9602
|
+
logger7.warn({ err }, "naudiodon rebuild failed, checking for PortAudio");
|
|
9602
9603
|
notificationService.warning("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u6B63\u5728\u5C1D\u8BD5\u5B89\u88C5 PortAudio...");
|
|
9603
9604
|
const installed = installPortAudio();
|
|
9604
9605
|
if (installed) {
|
|
@@ -9612,12 +9613,13 @@ async function ensureNaudiodonCompiled() {
|
|
|
9612
9613
|
logger7.info("naudiodon compiled successfully after PortAudio install");
|
|
9613
9614
|
notificationService.success("naudiodon \u7F16\u8BD1\u6210\u529F", "\u97F3\u9891\u540E\u7AEF\u5DF2\u5C31\u7EEA");
|
|
9614
9615
|
} catch (retryErr) {
|
|
9615
|
-
logger7.error({ err: retryErr }, "
|
|
9616
|
-
notificationService.error("naudiodon \u7F16\u8BD1\u5931\u8D25", "\
|
|
9616
|
+
logger7.error({ err: retryErr }, "naudiodon compile failed even after PortAudio install");
|
|
9617
|
+
notificationService.error("naudiodon \u7F16\u8BD1\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5");
|
|
9617
9618
|
markNaudiodonSkipped();
|
|
9618
9619
|
}
|
|
9619
9620
|
} else {
|
|
9620
|
-
|
|
9621
|
+
logger7.error("PortAudio install failed");
|
|
9622
|
+
notificationService.error("PortAudio \u5B89\u88C5\u5931\u8D25", "\u81EA\u52A8\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u5C1D\u8BD5\u624B\u52A8\u5B89\u88C5");
|
|
9621
9623
|
markNaudiodonSkipped();
|
|
9622
9624
|
}
|
|
9623
9625
|
}
|
|
@@ -9771,7 +9773,6 @@ var server = (async (input, _options) => {
|
|
|
9771
9773
|
const opencodeTui = input.client?.tui;
|
|
9772
9774
|
global.__opencode_tui__ = opencodeTui;
|
|
9773
9775
|
notificationService.setTui(opencodeTui);
|
|
9774
|
-
await ensureNaudiodonCompiled();
|
|
9775
9776
|
const config = loadOrCreateConfig();
|
|
9776
9777
|
try {
|
|
9777
9778
|
await initialize({
|
|
@@ -9788,6 +9789,7 @@ var server = (async (input, _options) => {
|
|
|
9788
9789
|
initError = err instanceof Error ? err : new Error(String(err));
|
|
9789
9790
|
logger7.error({ error: initError }, "initialization failed");
|
|
9790
9791
|
}
|
|
9792
|
+
await ensureNaudiodonCompiled();
|
|
9791
9793
|
setTimeout(() => {
|
|
9792
9794
|
if (initError) {
|
|
9793
9795
|
notificationService.error(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mingxy/ocosay",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "OpenCode TTS 播放插件 - 支持豆包模式边接收边朗读",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/plugin.js",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"zod": "^4.3.6"
|
|
49
49
|
},
|
|
50
50
|
"optionalDependencies": {
|
|
51
|
-
"naudiodon": "^
|
|
51
|
+
"naudiodon": "^2.0.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/howler": "^2.2.12",
|