@kajidog/mcp-tts-voicevox 0.0.5 → 0.0.7

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 CHANGED
@@ -2,81 +2,63 @@
2
2
 
3
3
  VOICEVOXを使用した音声合成MCPサーバー
4
4
 
5
+ ## 特徴
6
+
7
+ - **キュー管理機能** - 複数の音声合成リクエストを効率的に処理
8
+ - **プリフェッチ** - 次の音声を事前に生成し、再生をスムーズに
9
+
5
10
  ## 必要条件
6
11
 
7
12
  - Node.js
8
13
  - [VOICEVOXエンジン](https://voicevox.hiroshiba.jp/)
9
14
 
10
- ## 機能概要
11
-
12
- - テキストから音声を合成して再生
13
- - テキストから音声合成用クエリを生成
14
- - 音声合成用クエリから音声ファイルを生成
15
- - テキストまたはクエリを音声生成キューに追加
16
-
17
- ## 使い方
18
-
19
- ### インストール
15
+ ## インストール
20
16
 
21
17
  ```bash
22
18
  npm install -g @kajidog/mcp-tts-voicevox
23
19
  ```
24
20
 
25
- ### 実行
21
+ ## 使い方
22
+
23
+ ### MCPサーバーとして
26
24
 
27
25
  1. VOICEVOXエンジンを起動
28
- 2. 以下のコマンドを実行
26
+ 2. MCPサーバーを起動
29
27
 
30
28
  ```bash
31
29
  npx @kajidog/mcp-tts-voicevox
32
30
  ```
33
31
 
34
- ### MCPツールとして使用
32
+ ### ライブラリとして
35
33
 
36
- #### 1. テキストを音声に変換して再生
34
+ プロジェクトに直接インポートして使用することも可能です:
37
35
 
38
- ```typescript
39
- await mcp.invoke("speak", {
40
- text: "こんにちは!", // 読み上げるテキスト
41
- speaker: 1 // 話者ID(オプション)
42
- });
36
+ ```bash
37
+ npm install @kajidog/mcp-tts-voicevox
43
38
  ```
44
39
 
45
- #### 2. テキストから音声合成用クエリを生成
40
+ ```javascript
41
+ import { VoicevoxClient } from "@kajidog/mcp-tts-voicevox";
46
42
 
47
- ```typescript
48
- const queryResult = await mcp.invoke("generate_query", {
49
- text: "こんにちは!", // 音声合成するテキスト
50
- speaker: 1 // 話者ID(オプション)
43
+ // クライアントを初期化
44
+ const client = new VoicevoxClient({
45
+ url: "http://localhost:50021", // VOICEVOXエンジンのURL
46
+ defaultSpeaker: 1, // デフォルト話者ID(オプション)
47
+ defaultSpeedScale: 1.0 // デフォルト速度(オプション)
51
48
  });
52
49
 
53
- // 返されたテキストをJSONにパース
54
- const query = JSON.parse(queryResult.content[0].text);
55
- ```
56
-
57
- #### 3. 音声合成用クエリから音声ファイルを生成
50
+ // テキストを音声に変換して再生
51
+ await client.speak("こんにちは");
58
52
 
59
- ```typescript
60
- const fileResult = await mcp.invoke("synthesize_file", {
61
- query: query, // 音声合成用クエリ
62
- output: "/path/to/output.wav", // 出力ファイルパス
63
- speaker: 1 // 話者ID(オプション)
64
- });
65
-
66
- // 生成された音声ファイルのパス
67
- const filePath = fileResult.content[0].text;
53
+ // テキストから音声ファイルを生成
54
+ const filePath = await client.generateAudioFile("こんにちは", "./output.wav");
68
55
  ```
69
56
 
70
- ## 活用例
71
-
72
- 1. **テキストを直接音声に変換**
73
- - `speak` - 短いテキストをすぐに読み上げたいとき
74
-
75
- 2. **細かい音声設定をカスタマイズ**
76
- - `generate_query` → クエリ編集 → `synthesize_file` の流れで高度な調整が可能
57
+ ## 主な機能
77
58
 
78
- 3. **バッチ処理で大量の音声ファイルを作成**
79
- - テキストをクエリに変換してから一括で音声ファイルを生成
59
+ - **テキスト読み上げ** (`speak`) - テキストを音声に変換して再生
60
+ - **クエリ生成** (`generate_query`) - 音声合成用クエリの作成
61
+ - **ファイル生成** (`synthesize_file`) - クエリから音声ファイルを生成
80
62
 
81
63
  ## 環境変数
82
64
 
package/dist/index.js CHANGED
@@ -2,128 +2,129 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.VoicevoxError = void 0;
5
- exports.initVoicevox = initVoicevox;
6
5
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
7
6
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
8
7
  const zod_1 = require("zod");
9
8
  const voicevox_1 = require("./voicevox");
10
9
  const types_1 = require("./voicevox/types");
11
10
  Object.defineProperty(exports, "VoicevoxError", { enumerable: true, get: function () { return types_1.VoicevoxError; } });
12
- /**
13
- * VOICEVOX音声合成クライアントを初期化します
14
- * @param config 設定オブジェクト
15
- * @returns VoicevoxClientのインスタンス
16
- */
17
- function initVoicevox(config) {
18
- return new voicevox_1.VoicevoxClient(config);
19
- }
20
- // デフォルトのVOICEVOXクライアント
21
- const voicevoxClient = initVoicevox({
22
- url: process.env.VOICEVOX_URL || "http://localhost:50021",
23
- defaultSpeaker: Number(process.env.VOICEVOX_DEFAULT_SPEAKER || "1"),
24
- defaultSpeedScale: Number(process.env.VOICEVOX_DEFAULT_SPEED_SCALE || "1.0"),
25
- });
26
11
  const server = new mcp_js_1.McpServer({
27
12
  name: "MCP TTS Voicevox",
28
- version: "0.0.5",
13
+ version: "0.0.7",
29
14
  description: "A Voicevox server that converts text to speech for playback and saving.",
30
- functions: {
31
- // 既存の関数
32
- speak: {
33
- parameters: zod_1.z.object({
34
- text: zod_1.z
35
- .string()
36
- .describe("Text to be spoken (if both query and text are provided, query takes precedence)"),
37
- speaker: zod_1.z
38
- .number()
39
- .optional()
40
- .describe("Speaker ID (optional, used if text is provided)"),
41
- query: zod_1.z.string().optional().describe("Voice synthesis query"),
42
- speedScale: zod_1.z
43
- .number()
44
- .optional()
45
- .describe("Playback speed (optional, default is from environment variable)"),
46
- }),
47
- handler: async ({ text, speaker, query, speedScale, }) => {
48
- try {
49
- if (query) {
50
- // クエリが提供されている場合はそれを使用
51
- const audioQuery = JSON.parse(query);
52
- if (speedScale !== undefined) {
53
- audioQuery.speedScale = speedScale;
54
- }
55
- return await voicevoxClient.enqueueAudioGeneration(audioQuery, speaker);
56
- }
57
- else {
58
- // テキストからの通常の発話
59
- return await voicevoxClient.speak(text, speaker, speedScale);
60
- }
61
- }
62
- catch (error) {
63
- console.error("VOICEVOX speak error:", error);
64
- return `Error: ${error instanceof Error ? error.message : String(error)}`;
65
- }
66
- },
67
- },
68
- generate_query: {
69
- parameters: zod_1.z.object({
70
- text: zod_1.z.string().describe("Text for voice synthesis"),
71
- speaker: zod_1.z.number().optional().describe("Speaker ID (optional)"),
72
- speedScale: zod_1.z
73
- .number()
74
- .optional()
75
- .describe("Playback speed (optional, default is from environment variable)"),
76
- }),
77
- handler: async ({ text, speaker, speedScale, }) => {
78
- try {
79
- const query = await voicevoxClient.generateQuery(text, speaker, speedScale);
80
- return JSON.stringify(query);
81
- }
82
- catch (error) {
83
- console.error("VOICEVOX generate query error:", error);
84
- return `Error: ${error instanceof Error ? error.message : String(error)}`;
85
- }
86
- },
87
- },
88
- synthesize_file: {
89
- parameters: zod_1.z.object({
90
- text: zod_1.z
91
- .string()
92
- .optional()
93
- .describe("Text for voice synthesis (if both query and text are provided, query takes precedence)"),
94
- query: zod_1.z.string().optional().describe("Voice synthesis query"),
95
- speaker: zod_1.z.number().optional().describe("Speaker ID (optional)"),
96
- output: zod_1.z.string().describe("Output path for the audio file"),
97
- speedScale: zod_1.z
98
- .number()
99
- .optional()
100
- .describe("Playback speed (optional, default is from environment variable)"),
101
- }),
102
- handler: async ({ text, query, speaker, output, speedScale, }) => {
103
- try {
104
- if (query) {
105
- // クエリが提供されている場合はそれを使用
106
- const audioQuery = JSON.parse(query);
107
- if (speedScale !== undefined) {
108
- audioQuery.speedScale = speedScale;
109
- }
110
- return await voicevoxClient.generateAudioFile(audioQuery, output, speaker);
111
- }
112
- else if (text) {
113
- // テキストから音声ファイルを生成
114
- return await voicevoxClient.generateAudioFile(text, output, speaker, speedScale);
115
- }
116
- else {
117
- return "Error: Either text or query must be provided";
118
- }
119
- }
120
- catch (error) {
121
- console.error("VOICEVOX synthesize file error:", error);
122
- return `Error: ${error instanceof Error ? error.message : String(error)}`;
123
- }
124
- },
125
- },
126
- },
15
+ });
16
+ // VoicevoxClientを一度だけインスタンス化
17
+ const voicevoxClient = new voicevox_1.VoicevoxClient({
18
+ url: process.env.VOICEVOX_URL ?? "http://localhost:50021",
19
+ defaultSpeaker: Number(process.env.VOICEVOX_DEFAULT_SPEAKER || "1"),
20
+ defaultSpeedScale: Number(process.env.VOICEVOX_DEFAULT_SPEED_SCALE || "1.0"),
21
+ });
22
+ // 共通のエラーハンドリング関数
23
+ const handleError = (error) => {
24
+ const errorMessage = error instanceof Error ? error.message : String(error);
25
+ console.error("エラーが発生しました:", error);
26
+ return {
27
+ content: [{ type: "text", text: `エラー: ${errorMessage}` }],
28
+ };
29
+ };
30
+ // テキストを音声に変換して再生
31
+ server.tool("speak", "Convert text to speech and play it", {
32
+ text: zod_1.z
33
+ .string()
34
+ .describe("Text to be spoken (if both query and text are provided, query takes precedence)"),
35
+ speaker: zod_1.z
36
+ .number()
37
+ .optional()
38
+ .describe("Speaker ID (optional, used if text is provided)"),
39
+ query: zod_1.z.string().optional().describe("Voice synthesis query"),
40
+ speedScale: zod_1.z
41
+ .number()
42
+ .optional()
43
+ .describe("Playback speed (optional, default is from environment variable)"),
44
+ }, async ({ text, speaker, query, speedScale }) => {
45
+ try {
46
+ if (query) {
47
+ // クエリが提供されている場合はそれを使用
48
+ const audioQuery = JSON.parse(query);
49
+ if (speedScale !== undefined) {
50
+ audioQuery.speedScale = speedScale;
51
+ }
52
+ const result = await voicevoxClient.enqueueAudioGeneration(audioQuery, speaker);
53
+ return {
54
+ content: [{ type: "text", text: result }],
55
+ };
56
+ }
57
+ else {
58
+ // テキストからの通常の発話
59
+ const result = await voicevoxClient.speak(text, speaker, speedScale);
60
+ return {
61
+ content: [{ type: "text", text: result }],
62
+ };
63
+ }
64
+ }
65
+ catch (error) {
66
+ return handleError(error);
67
+ }
68
+ });
69
+ // クエリ生成ツール
70
+ server.tool("generate_query", "Generate a query for voice synthesis", {
71
+ text: zod_1.z.string().describe("Text for voice synthesis"),
72
+ speaker: zod_1.z.number().optional().describe("Speaker ID (optional)"),
73
+ speedScale: zod_1.z
74
+ .number()
75
+ .optional()
76
+ .describe("Playback speed (optional, default is from environment variable)"),
77
+ }, async ({ text, speaker, speedScale }) => {
78
+ try {
79
+ const query = await voicevoxClient.generateQuery(text, speaker, speedScale);
80
+ return {
81
+ content: [{ type: "text", text: JSON.stringify(query) }],
82
+ };
83
+ }
84
+ catch (error) {
85
+ return handleError(error);
86
+ }
87
+ });
88
+ // 音声ファイル生成ツール
89
+ server.tool("synthesize_file", "Generate an audio file and return its absolute path", {
90
+ text: zod_1.z
91
+ .string()
92
+ .optional()
93
+ .describe("Text for voice synthesis (if both query and text are provided, query takes precedence)"),
94
+ query: zod_1.z.string().optional().describe("Voice synthesis query"),
95
+ speaker: zod_1.z.number().optional().describe("Speaker ID (optional)"),
96
+ output: zod_1.z.string().describe("Output path for the audio file"),
97
+ speedScale: zod_1.z
98
+ .number()
99
+ .optional()
100
+ .describe("Playback speed (optional, default is from environment variable)"),
101
+ }, async ({ text, query, speaker, output, speedScale }) => {
102
+ try {
103
+ if (query) {
104
+ // クエリが提供されている場合はそれを使用
105
+ const audioQuery = JSON.parse(query);
106
+ if (speedScale !== undefined) {
107
+ audioQuery.speedScale = speedScale;
108
+ }
109
+ const filePath = await voicevoxClient.generateAudioFile(audioQuery, output, speaker);
110
+ return {
111
+ content: [{ type: "text", text: filePath }],
112
+ };
113
+ }
114
+ else if (text) {
115
+ // テキストから音声ファイルを生成
116
+ const filePath = await voicevoxClient.generateAudioFile(text, output, speaker, speedScale);
117
+ return {
118
+ content: [{ type: "text", text: filePath }],
119
+ };
120
+ }
121
+ else {
122
+ throw new Error("queryパラメータとtextパラメータのどちらかを指定してください");
123
+ }
124
+ }
125
+ catch (error) {
126
+ return handleError(error);
127
+ }
127
128
  });
128
129
  server.connect(new stdio_js_1.StdioServerTransport()).catch((error) => {
129
130
  console.error("Error connecting to MCP transport:", error);
@@ -16,7 +16,7 @@ class VoicevoxApi {
16
16
  */
17
17
  async generateQuery(text, speaker = 1) {
18
18
  try {
19
- const endpoint = `/audio_query?text=${encodeURIComponent(text)}&speaker=${speaker}`;
19
+ const endpoint = `/audio_query?text=${encodeURIComponent(text)}&speaker=${encodeURIComponent(speaker.toString())}`;
20
20
  const query = await this.makeRequest("post", endpoint, null, {
21
21
  "Content-Type": "application/json",
22
22
  });
@@ -31,7 +31,7 @@ class VoicevoxApi {
31
31
  */
32
32
  async synthesize(query, speaker = 1) {
33
33
  try {
34
- return await this.makeRequest("post", `/synthesis?speaker=${speaker}`, query, {
34
+ return await this.makeRequest("post", `/synthesis?speaker=${encodeURIComponent(speaker.toString())}`, query, {
35
35
  "Content-Type": "application/json",
36
36
  Accept: "audio/wav",
37
37
  }, "arraybuffer");
@@ -45,7 +45,7 @@ class VoicevoxApi {
45
45
  */
46
46
  async generateQueryFromPreset(text, presetId, coreVersion) {
47
47
  try {
48
- let endpoint = `/audio_query_from_preset?text=${encodeURIComponent(text)}&preset_id=${presetId}`;
48
+ let endpoint = `/audio_query_from_preset?text=${encodeURIComponent(text)}&preset_id=${encodeURIComponent(presetId.toString())}`;
49
49
  if (coreVersion) {
50
50
  endpoint += `&core_version=${encodeURIComponent(coreVersion)}`;
51
51
  }
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ErrorHandler = exports.VoicevoxError = exports.VoicevoxErrorCode = void 0;
3
4
  exports.handleError = handleError;
4
5
  exports.formatError = formatError;
5
6
  /**
@@ -24,3 +25,91 @@ function formatError(message, error) {
24
25
  console.error(`${message}: ${errorMsg}`, error);
25
26
  return `${message}: ${errorMsg}`;
26
27
  }
28
+ /**
29
+ * VOICEVOX関連のエラーコード
30
+ */
31
+ var VoicevoxErrorCode;
32
+ (function (VoicevoxErrorCode) {
33
+ VoicevoxErrorCode["API_CONNECTION_ERROR"] = "api_connection_error";
34
+ VoicevoxErrorCode["QUERY_GENERATION_ERROR"] = "query_generation_error";
35
+ VoicevoxErrorCode["SYNTHESIS_ERROR"] = "synthesis_error";
36
+ VoicevoxErrorCode["FILE_OPERATION_ERROR"] = "file_operation_error";
37
+ VoicevoxErrorCode["PLAYBACK_ERROR"] = "playback_error";
38
+ VoicevoxErrorCode["QUEUE_OPERATION_ERROR"] = "queue_operation_error";
39
+ VoicevoxErrorCode["UNKNOWN_ERROR"] = "unknown_error";
40
+ })(VoicevoxErrorCode || (exports.VoicevoxErrorCode = VoicevoxErrorCode = {}));
41
+ /**
42
+ * VOICEVOXエラークラス
43
+ */
44
+ class VoicevoxError extends Error {
45
+ constructor(message, code = VoicevoxErrorCode.UNKNOWN_ERROR, originalError) {
46
+ super(message);
47
+ this.name = "VoicevoxError";
48
+ this.code = code;
49
+ this.originalError = originalError;
50
+ }
51
+ /**
52
+ * エラー発生箇所とスタックトレースを含むエラーの詳細情報を取得
53
+ */
54
+ getDetailedMessage() {
55
+ let details = `${this.message} [${this.code}]`;
56
+ if (this.originalError instanceof Error) {
57
+ details += `\nOriginal Error: ${this.originalError.message}`;
58
+ if (this.originalError.stack) {
59
+ details += `\nStack: ${this.originalError.stack}`;
60
+ }
61
+ }
62
+ return details;
63
+ }
64
+ }
65
+ exports.VoicevoxError = VoicevoxError;
66
+ /**
67
+ * エラーハンドリングユーティリティクラス
68
+ * アプリケーション全体で統一されたエラーハンドリングを提供
69
+ */
70
+ class ErrorHandler {
71
+ /**
72
+ * エラーをVoicevoxError形式に変換して例外をスロー
73
+ */
74
+ static throw(message, code = VoicevoxErrorCode.UNKNOWN_ERROR, originalError) {
75
+ console.error(`[${code}] ${message}`, originalError);
76
+ throw new VoicevoxError(message, code, originalError);
77
+ }
78
+ /**
79
+ * APIエラーを処理
80
+ */
81
+ static handleApiError(message, error) {
82
+ return this.throw(message, VoicevoxErrorCode.API_CONNECTION_ERROR, error);
83
+ }
84
+ /**
85
+ * クエリ生成エラーを処理
86
+ */
87
+ static handleQueryGenerationError(message, error) {
88
+ return this.throw(message, VoicevoxErrorCode.QUERY_GENERATION_ERROR, error);
89
+ }
90
+ /**
91
+ * 音声合成エラーを処理
92
+ */
93
+ static handleSynthesisError(message, error) {
94
+ return this.throw(message, VoicevoxErrorCode.SYNTHESIS_ERROR, error);
95
+ }
96
+ /**
97
+ * ファイル操作エラーを処理
98
+ */
99
+ static handleFileError(message, error) {
100
+ return this.throw(message, VoicevoxErrorCode.FILE_OPERATION_ERROR, error);
101
+ }
102
+ /**
103
+ * 再生エラーを処理
104
+ */
105
+ static handlePlaybackError(message, error) {
106
+ return this.throw(message, VoicevoxErrorCode.PLAYBACK_ERROR, error);
107
+ }
108
+ /**
109
+ * キュー操作エラーを処理
110
+ */
111
+ static handleQueueError(message, error) {
112
+ return this.throw(message, VoicevoxErrorCode.QUEUE_OPERATION_ERROR, error);
113
+ }
114
+ }
115
+ exports.ErrorHandler = ErrorHandler;
@@ -18,12 +18,14 @@ exports.VoicevoxClient = void 0;
18
18
  const player_1 = require("./player");
19
19
  const utils_1 = require("./utils");
20
20
  const error_1 = require("./error");
21
+ const api_1 = require("./api");
21
22
  class VoicevoxClient {
22
23
  constructor(config) {
23
24
  this.validateConfig(config);
24
25
  this.defaultSpeaker = config.defaultSpeaker ?? 1;
25
26
  this.defaultSpeedScale = config.defaultSpeedScale ?? 1.0;
26
27
  this.maxSegmentLength = 150;
28
+ this.api = new api_1.VoicevoxApi(config.url);
27
29
  this.player = new player_1.VoicevoxPlayer(config.url);
28
30
  }
29
31
  /**
@@ -38,10 +40,30 @@ class VoicevoxClient {
38
40
  const speakerId = this.getSpeakerId(speaker);
39
41
  const speed = this.getSpeedScale(speedScale);
40
42
  const segments = (0, utils_1.splitText)(text, this.maxSegmentLength);
41
- for (const segment of segments) {
42
- const query = await this.player.generateQuery(segment, speakerId);
43
- query.speedScale = speed;
44
- await this.player.enqueueWithQuery(query, speakerId);
43
+ const queueManager = this.player.getQueueManager();
44
+ if (segments.length === 0) {
45
+ return "テキストが空です";
46
+ }
47
+ // 最初のセグメントを優先的に処理して再生を早く開始
48
+ if (segments.length > 0) {
49
+ const firstQuery = await this.generateQuery(segments[0], speakerId);
50
+ firstQuery.speedScale = speed;
51
+ await queueManager.enqueueQuery(firstQuery, speakerId);
52
+ }
53
+ // 残りのセグメントは非同期で処理
54
+ if (segments.length > 1) {
55
+ // 残りのセグメントを非同期で処理する関数
56
+ const processRemainingSegments = async () => {
57
+ for (let i = 1; i < segments.length; i++) {
58
+ const query = await this.generateQuery(segments[i], speakerId);
59
+ query.speedScale = speed;
60
+ await queueManager.enqueueQuery(query, speakerId);
61
+ }
62
+ };
63
+ // 非同期で開始するが結果を待たない
64
+ processRemainingSegments().catch((error) => {
65
+ console.error("残りのセグメント処理中にエラーが発生しました:", error);
66
+ });
45
67
  }
46
68
  return `音声生成キューに追加しました: ${text}`;
47
69
  }
@@ -59,7 +81,8 @@ class VoicevoxClient {
59
81
  async generateQuery(text, speaker, speedScale) {
60
82
  try {
61
83
  const speakerId = this.getSpeakerId(speaker);
62
- const query = await this.player.generateQuery(text, speakerId);
84
+ // 直接APIを使用してクエリを生成
85
+ const query = await this.api.generateQuery(text, speakerId);
63
86
  query.speedScale = this.getSpeedScale(speedScale);
64
87
  return query;
65
88
  }
@@ -79,14 +102,33 @@ class VoicevoxClient {
79
102
  try {
80
103
  const speakerId = this.getSpeakerId(speaker);
81
104
  const speed = this.getSpeedScale(speedScale);
105
+ // キューマネージャーにアクセス
106
+ const queueManager = this.player.getQueueManager();
82
107
  if (typeof textOrQuery === "string") {
108
+ // テキストからクエリを生成
83
109
  const query = await this.generateQuery(textOrQuery, speakerId);
84
110
  query.speedScale = speed;
85
- return await this.player.synthesizeToFile(query, outputPath, speakerId);
111
+ // 直接APIで音声合成
112
+ const audioData = await this.api.synthesize(query, speakerId);
113
+ // 一時ファイル保存またはパス指定の保存
114
+ if (!outputPath) {
115
+ return await queueManager.saveTempAudioFile(audioData);
116
+ }
117
+ else {
118
+ return await this.player.synthesizeToFile(query, outputPath, speakerId);
119
+ }
86
120
  }
87
121
  else {
122
+ // クエリを使って音声合成
88
123
  const query = { ...textOrQuery, speedScale: speed };
89
- return await this.player.synthesizeToFile(query, outputPath, speakerId);
124
+ const audioData = await this.api.synthesize(query, speakerId);
125
+ // 一時ファイル保存またはパス指定の保存
126
+ if (!outputPath) {
127
+ return await queueManager.saveTempAudioFile(audioData);
128
+ }
129
+ else {
130
+ return await this.player.synthesizeToFile(query, outputPath, speakerId);
131
+ }
90
132
  }
91
133
  }
92
134
  catch (error) {
@@ -98,12 +140,40 @@ class VoicevoxClient {
98
140
  try {
99
141
  const speakerId = this.getSpeakerId(speaker);
100
142
  const speed = this.getSpeedScale(speedScale);
143
+ const queueManager = this.player.getQueueManager();
101
144
  if (typeof textOrQuery === "string") {
102
- return await this.speak(textOrQuery, speakerId, speed);
145
+ // テキストの場合:分割して処理
146
+ const segments = (0, utils_1.splitText)(textOrQuery, this.maxSegmentLength);
147
+ if (segments.length === 0) {
148
+ return "テキストが空です";
149
+ }
150
+ // 最初のセグメントを優先処理
151
+ if (segments.length > 0) {
152
+ const firstQuery = await this.generateQuery(segments[0], speakerId);
153
+ firstQuery.speedScale = speed;
154
+ await queueManager.enqueueQuery(firstQuery, speakerId);
155
+ }
156
+ // 残りのセグメントは非同期で処理
157
+ if (segments.length > 1) {
158
+ // 非同期で処理する関数
159
+ const processRemainingSegments = async () => {
160
+ for (let i = 1; i < segments.length; i++) {
161
+ const query = await this.generateQuery(segments[i], speakerId);
162
+ query.speedScale = speed;
163
+ await queueManager.enqueueQuery(query, speakerId);
164
+ }
165
+ };
166
+ // 非同期で開始するが結果を待たない
167
+ processRemainingSegments().catch((error) => {
168
+ console.error("残りのセグメント処理中にエラーが発生しました:", error);
169
+ });
170
+ }
171
+ return `テキストをキューに追加しました: ${textOrQuery}`;
103
172
  }
104
173
  else {
174
+ // クエリをキューに追加
105
175
  const query = { ...textOrQuery, speedScale: speed };
106
- await this.player.enqueueWithQuery(query, speakerId);
176
+ await queueManager.enqueueQuery(query, speakerId);
107
177
  return "クエリをキューに追加しました";
108
178
  }
109
179
  }