Akatuki25 commited on
Commit
28892a1
·
1 Parent(s): ab1cada

Update API spec: add preset reference audio documentation

Browse files

- Document default_female and default_male presets
- Add usage examples for both preset and custom reference audio
- Update best practices section
- Clarify ref_preset_id parameter usage

Files changed (1) hide show
  1. API_SPECIFICATION.md +681 -0
API_SPECIFICATION.md ADDED
@@ -0,0 +1,681 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Seed-VC Streaming API 仕様書
2
+
3
+ ## 概要
4
+
5
+ Seed-VC Streaming APIは、ゼロショット音声変換をチャンク単位で処理するHTTP APIサーバーです。
6
+ クライアントが音声を小さなチャンク(例: 500ms)に分割して順次送信し、サーバーが各チャンクを変換して返すことで、低レイテンシなストリーミング処理を実現します。
7
+
8
+ - **ベースURL**: `https://akatuki25-seed-vc-streaming.hf.space`
9
+ - **モデル**: Seed-VC (Plachtaa/seed-vc)
10
+ - **入力サンプルレート**: 16000Hz (推奨)
11
+ - **出力サンプルレート**: 22050Hz
12
+ - **推奨チャンクサイズ**: 500ms (overlap 100ms)
13
+ - **プリセット参照音声**: デフォルトで`default_female`が利用可能(カスタム音声のアップロードも可)
14
+
15
+ ---
16
+
17
+ ## アーキテクチャ
18
+
19
+ ### ストリーミング処理フロー
20
+
21
+ ```
22
+ クライアント側:
23
+ 1. 音声をチャンク分割 (500ms × N個)
24
+ 2. セッション作成 → session_id取得
25
+ 3. (オプション) カスタム参照音声アップロード
26
+ ※プリセット参照音声を使う場合はスキップ
27
+ 4. チャンクを順次送信 (chunk_0, chunk_1, ...)
28
+ 5. 各レスポンスを受信・結合
29
+ 6. セッション終了
30
+
31
+ サーバー側:
32
+ 1. セッション管理 (参照音声の特徴量をキャッシュ)
33
+ ※プリセット使用時はHF Datasetから自動ダウンロード
34
+ 2. 各チャンクを独立に変換
35
+ 3. クロスフェード処理 (overlap_msで指定)
36
+ 4. 変換後チャンクを即座に返却
37
+ ```
38
+
39
+ ### 重要な設計ポイント
40
+
41
+ - **チャンク単位処理**: `/chunk`エンドポイントは1回のリクエストで1チャンクのみ処理・返却
42
+ - **クライアント側結合**: 全チャンクを受信後、クライアントが`np.concatenate()`等で結合
43
+ - **サーバー側クロスフェード**: `overlap_ms`で指定した重複部分を自動的にクロスフェード
44
+ - **セッション状態**: 参照音声の特徴量、前回チャンクの末尾を保持
45
+
46
+ ---
47
+
48
+ ## エンドポイント仕様
49
+
50
+ ### 1. GET /health
51
+
52
+ ヘルスチェック用エンドポイント
53
+
54
+ **リクエスト**
55
+ ```bash
56
+ GET /health
57
+ ```
58
+
59
+ **レスポンス**
60
+ ```json
61
+ {
62
+ "status": "ok"
63
+ }
64
+ ```
65
+
66
+ ---
67
+
68
+ ### 2. POST /session
69
+
70
+ 新しい変換セッションを作成
71
+
72
+ **リクエスト**
73
+ ```bash
74
+ POST /session
75
+ Content-Type: application/json
76
+
77
+ {
78
+ "sample_rate": 16000,
79
+ "tgt_speaker_id": null,
80
+ "ref_preset_id": null,
81
+ "use_uploaded_ref": true,
82
+ "chunk_len_ms": 500,
83
+ "overlap_ms": 100
84
+ }
85
+ ```
86
+
87
+ **パラメータ**
88
+ | フィールド | 型 | 必須 | デフォルト | 説明 |
89
+ |-----------|-----|------|-----------|------|
90
+ | `sample_rate` | int | No | 16000 | 入力音声のサンプルレート (Hz) |
91
+ | `tgt_speaker_id` | str | No | null | ターゲット話者ID (未使用) |
92
+ | `ref_preset_id` | str | No | "default_female" | プリセット参照音声ID ("default_female", "default_male") |
93
+ | `use_uploaded_ref` | bool | No | false | 参照音声をアップロードする場合true。falseの場合はref_preset_idを使用 |
94
+ | `chunk_len_ms` | int | No | 1000 | チャンク長 (ミリ秒) |
95
+ | `overlap_ms` | int | No | 200 | チャンク間のオーバーラップ (ミリ秒) |
96
+
97
+ **レスポンス**
98
+ ```json
99
+ {
100
+ "session_id": "550e8400-e29b-41d4-a716-446655440000",
101
+ "sample_rate": 16000,
102
+ "chunk_len_ms": 500,
103
+ "overlap_ms": 100
104
+ }
105
+ ```
106
+
107
+ ---
108
+
109
+ ### 3. POST /session/ref
110
+
111
+ 参照音声(ターゲット話者音声)をアップロード
112
+
113
+ **リクエスト**
114
+ ```bash
115
+ POST /session/ref
116
+ Content-Type: multipart/form-data
117
+
118
+ session_id: <session_id>
119
+ ref_audio: <WAVファイル>
120
+ ```
121
+
122
+ **パラメータ**
123
+ | フィールド | 型 | 必須 | 説明 |
124
+ |-----------|-----|------|------|
125
+ | `session_id` | str | Yes | セッションID |
126
+ | `ref_audio` | file | Yes | 参照音声WAVファイル (任意のサンプルレート、自動リサンプル) |
127
+
128
+ **レスポンス**
129
+ ```json
130
+ {
131
+ "status": "ok"
132
+ }
133
+ ```
134
+
135
+ **処理内容**
136
+ - 参照音声を22050Hzにリサンプル
137
+ - 最大25秒に切り詰め
138
+ - Whisperセマンティック特徴量を抽出
139
+ - CAMPPlusスタイル埋め込みを計算
140
+ - メルスペクトログラムを生成
141
+ - セッションに紐付けて保存
142
+
143
+ ---
144
+
145
+ ### 4. POST /chunk
146
+
147
+ 音声チャンクを変換
148
+
149
+ **リクエスト**
150
+ ```bash
151
+ POST /chunk
152
+ Content-Type: multipart/form-data
153
+
154
+ session_id: <session_id>
155
+ chunk_id: <chunk_id>
156
+ audio: <WAVファイル>
157
+ ```
158
+
159
+ **パラメータ**
160
+ | フィールド | 型 | 必須 | 説明 |
161
+ |-----------|-----|------|------|
162
+ | `session_id` | str | Yes | セッションID |
163
+ | `chunk_id` | int | Yes | チャンクID (0始まりの連番) |
164
+ | `audio` | file | Yes | 音声チャンクWAVファイル |
165
+
166
+ **レスポンス**
167
+ ```
168
+ Content-Type: audio/wav
169
+ X-Chunk-Id: <chunk_id>
170
+
171
+ <WAVバイナリデータ>
172
+ ```
173
+
174
+ **処理フロー**
175
+ 1. 音声チャンクを読み込み (セッションのsample_rateと一致確認)
176
+ 2. Seed-VCで音声変換
177
+ - Whisper��マンティック特徴抽出
178
+ - Length Regulator適用
179
+ - CFM (Conditional Flow Matching) で推論
180
+ - BigVGAN Vocoderで音声生成
181
+ 3. 前回チャンクの末尾とクロスフェード (`overlap_ms`分)
182
+ 4. 変換後チャンクを返却 (22050Hz WAV)
183
+
184
+ **重要**: このエンドポイントは**1チャンクのみ**を返します。全体音声を得るにはクライアント側で結合が必要です。
185
+
186
+ ---
187
+
188
+ ### 5. POST /end
189
+
190
+ セッションを終了
191
+
192
+ **リクエスト**
193
+ ```bash
194
+ POST /end
195
+ Content-Type: application/json
196
+
197
+ {
198
+ "session_id": "<session_id>"
199
+ }
200
+ ```
201
+
202
+ **レスポンス**
203
+ ```json
204
+ {
205
+ "status": "ended"
206
+ }
207
+ ```
208
+
209
+ ---
210
+
211
+ ## 使用例
212
+
213
+ ### Python完全実装例
214
+
215
+ #### パターンA: プリセット参照音声を使用(推奨)
216
+
217
+ ```python
218
+ import requests
219
+ import numpy as np
220
+ import soundfile as sf
221
+ import io
222
+
223
+ # ====================
224
+ # 設定
225
+ # ====================
226
+ API_BASE = "https://akatuki25-seed-vc-streaming.hf.space"
227
+ SOURCE_AUDIO = "source.wav" # 変換したい音声
228
+ OUTPUT_AUDIO = "output.wav"
229
+
230
+ SAMPLE_RATE = 16000
231
+ CHUNK_LEN_MS = 500
232
+ OVERLAP_MS = 100
233
+
234
+ # ====================
235
+ # 1. 音声読み込み
236
+ # ====================
237
+ source, sr = sf.read(SOURCE_AUDIO)
238
+ if sr != SAMPLE_RATE:
239
+ import librosa
240
+ source = librosa.resample(source, orig_sr=sr, target_sr=SAMPLE_RATE)
241
+
242
+ # ====================
243
+ # 2. セッション作成(プリセット参照音声使用)
244
+ # ====================
245
+ resp = requests.post(f"{API_BASE}/session", json={
246
+ "sample_rate": SAMPLE_RATE,
247
+ "use_uploaded_ref": False, # プリセットを使用
248
+ "ref_preset_id": "default_female", # 省略可(デフォルト)
249
+ "chunk_len_ms": CHUNK_LEN_MS,
250
+ "overlap_ms": OVERLAP_MS
251
+ })
252
+ session_id = resp.json()["session_id"]
253
+ print(f"Session created: {session_id}")
254
+
255
+ # 3. 参照音声アップロードは不要(プリセット使用時)
256
+
257
+ # ====================
258
+ # 4. チャンク分割
259
+ # ====================
260
+ chunk_len_samples = int(SAMPLE_RATE * CHUNK_LEN_MS / 1000)
261
+ chunks = []
262
+ for i in range(0, len(source), chunk_len_samples):
263
+ chunk = source[i:i + chunk_len_samples]
264
+ chunks.append(chunk)
265
+
266
+ print(f"Split into {len(chunks)} chunks")
267
+
268
+ # ====================
269
+ # 5. チャンク順次送信・受信
270
+ # ====================
271
+ output_chunks = []
272
+
273
+ for chunk_id, chunk in enumerate(chunks):
274
+ # WAVバイト列に変換
275
+ buffer = io.BytesIO()
276
+ sf.write(buffer, chunk, SAMPLE_RATE, format="WAV", subtype="PCM_16")
277
+ buffer.seek(0)
278
+
279
+ # POSTリクエスト
280
+ resp = requests.post(f"{API_BASE}/chunk",
281
+ data={"session_id": session_id, "chunk_id": chunk_id},
282
+ files={"audio": ("chunk.wav", buffer, "audio/wav")})
283
+
284
+ # 変換後チャンク取得
285
+ converted_chunk, conv_sr = sf.read(io.BytesIO(resp.content))
286
+ output_chunks.append(converted_chunk)
287
+
288
+ print(f"Chunk {chunk_id}/{len(chunks)-1} processed")
289
+
290
+ # ====================
291
+ # 6. チャンク結合
292
+ # ====================
293
+ output_audio = np.concatenate(output_chunks)
294
+ sf.write(OUTPUT_AUDIO, output_audio, 22050)
295
+ print(f"Output saved: {OUTPUT_AUDIO}")
296
+
297
+ # ====================
298
+ # 7. セッション終了
299
+ # ====================
300
+ requests.post(f"{API_BASE}/end", json={"session_id": session_id})
301
+ print("Session ended")
302
+ ```
303
+
304
+ #### パターンB: カスタム参照音声をアップロード
305
+
306
+ ```python
307
+ import requests
308
+ import numpy as np
309
+ import soundfile as sf
310
+ import io
311
+
312
+ # ====================
313
+ # 設定
314
+ # ====================
315
+ API_BASE = "https://akatuki25-seed-vc-streaming.hf.space"
316
+ SOURCE_AUDIO = "source.wav" # 変換したい音声
317
+ REF_AUDIO = "target_speaker.wav" # ターゲット話者の参照音声
318
+ OUTPUT_AUDIO = "output.wav"
319
+
320
+ SAMPLE_RATE = 16000
321
+ CHUNK_LEN_MS = 500
322
+ OVERLAP_MS = 100
323
+
324
+ # ====================
325
+ # 1. 音声読み込み
326
+ # ====================
327
+ source, sr = sf.read(SOURCE_AUDIO)
328
+ if sr != SAMPLE_RATE:
329
+ import librosa
330
+ source = librosa.resample(source, orig_sr=sr, target_sr=SAMPLE_RATE)
331
+
332
+ # ====================
333
+ # 2. セッション作成(カスタム参照音声)
334
+ # ====================
335
+ resp = requests.post(f"{API_BASE}/session", json={
336
+ "sample_rate": SAMPLE_RATE,
337
+ "use_uploaded_ref": True, # カスタム参照音声を使用
338
+ "chunk_len_ms": CHUNK_LEN_MS,
339
+ "overlap_ms": OVERLAP_MS
340
+ })
341
+ session_id = resp.json()["session_id"]
342
+ print(f"Session created: {session_id}")
343
+
344
+ # ====================
345
+ # 3. 参照音声アップロード
346
+ # ====================
347
+ with open(REF_AUDIO, "rb") as f:
348
+ resp = requests.post(f"{API_BASE}/session/ref",
349
+ data={"session_id": session_id},
350
+ files={"ref_audio": f})
351
+ print("Reference audio uploaded")
352
+
353
+ # 4〜7は同じ (チャンク分割、送信、結合、終了)
354
+ # ...
355
+ ```
356
+
357
+ ### curlを使った例
358
+
359
+ #### パターンA: プリセット参照音声を使用
360
+
361
+ ```bash
362
+ #!/bin/bash
363
+
364
+ API_BASE="https://akatuki25-seed-vc-streaming.hf.space"
365
+
366
+ # 1. セッション作成(プリセット参照音声使用)
367
+ SESSION=$(curl -s -X POST "$API_BASE/session" \
368
+ -H "Content-Type: application/json" \
369
+ -d '{"sample_rate":16000,"use_uploaded_ref":false,"ref_preset_id":"default_female","chunk_len_ms":500,"overlap_ms":100}' \
370
+ | jq -r '.session_id')
371
+
372
+ echo "Session: $SESSION"
373
+
374
+ # 2. 参照音声アップロードは不要
375
+
376
+ # 3. チャンク送信 (例: chunk_0)
377
+ curl -X POST "$API_BASE/chunk" \
378
+ -F "session_id=$SESSION" \
379
+ -F "chunk_id=0" \
380
+ -F "audio=@chunk_0.wav" \
381
+ -o output_chunk_0.wav
382
+
383
+ # 4. セッション終了
384
+ curl -X POST "$API_BASE/end" \
385
+ -H "Content-Type: application/json" \
386
+ -d "{\"session_id\":\"$SESSION\"}"
387
+ ```
388
+
389
+ #### パターンB: カスタム参照音声をアップロード
390
+
391
+ ```bash
392
+ #!/bin/bash
393
+
394
+ API_BASE="https://akatuki25-seed-vc-streaming.hf.space"
395
+
396
+ # 1. セッション作成
397
+ SESSION=$(curl -s -X POST "$API_BASE/session" \
398
+ -H "Content-Type: application/json" \
399
+ -d '{"sample_rate":16000,"use_uploaded_ref":true,"chunk_len_ms":500,"overlap_ms":100}' \
400
+ | jq -r '.session_id')
401
+
402
+ echo "Session: $SESSION"
403
+
404
+ # 2. 参照音声アップロード
405
+ curl -X POST "$API_BASE/session/ref" \
406
+ -F "session_id=$SESSION" \
407
+ -F "ref_audio=@target_speaker.wav"
408
+
409
+ # 3. チャンク送信 (例: chunk_0)
410
+ curl -X POST "$API_BASE/chunk" \
411
+ -F "session_id=$SESSION" \
412
+ -F "chunk_id=0" \
413
+ -F "audio=@chunk_0.wav" \
414
+ -o output_chunk_0.wav
415
+
416
+ # 4. セッション終了
417
+ curl -X POST "$API_BASE/end" \
418
+ -H "Content-Type: application/json" \
419
+ -d "{\"session_id\":\"$SESSION\"}"
420
+ ```
421
+
422
+ ---
423
+
424
+ ## クロスフェード処理
425
+
426
+ サーバー側で自動的に処理されます。
427
+
428
+ ### 仕組み
429
+
430
+ ```
431
+ チャンク0: [=============================]
432
+ ↓ overlap_ms (100ms)
433
+ チャンク1: [=============================]
434
+ |<-fade->|
435
+
436
+ 出力0: [========================] (fade-outなし)
437
+ 出力1: [==|fade-in|==================]
438
+
439
+ 最終結合: [========================================]
440
+ ```
441
+
442
+ ### パラメータ調整
443
+
444
+ | overlap_ms | 効果 | 推奨用途 |
445
+ |-----------|------|---------|
446
+ | 0 | クロスフェードなし | デバッグ用 |
447
+ | 50 | 最小限の平滑化 | 超低レイテンシ優先 |
448
+ | 100 | 標準 | バランス型 |
449
+ | 200 | 高品質 | 音質優先 |
450
+
451
+ ---
452
+
453
+ ## パフォーマンス特性
454
+
455
+ ### レイテンシ測定結果
456
+
457
+ **環境**: Hugging Face Spaces (NVIDIA T4 GPU)
458
+
459
+ | チャンクサイズ | 初回処理時間 | 2回目以降 | RTF (Real-Time Factor) |
460
+ |--------------|-------------|----------|----------------------|
461
+ | 100ms | ~2.0秒 | ~0.5秒 | ~5.0x |
462
+ | 200ms | ~2.0秒 | ~0.7秒 | ~3.5x |
463
+ | 500ms | ~2.0秒 | ~1.0秒 | ~2.0x |
464
+ | 1000ms | ~2.5秒 | ~1.5秒 | ~1.5x |
465
+
466
+ **RTF**: レイテンシ ÷ 入力音声長。1.0未満でリアルタイム処理可能。
467
+
468
+ ### 推奨設定
469
+
470
+ ```json
471
+ {
472
+ "chunk_len_ms": 500,
473
+ "overlap_ms": 100
474
+ }
475
+ ```
476
+
477
+ **理由**:
478
+ - 初回ウォームアップ後、RTF ~2.0x (実用的)
479
+ - 適度なクロスフェード品質
480
+ - ネットワークオーバーヘッドとのバランス
481
+
482
+ ---
483
+
484
+ ## エラーハンドリング
485
+
486
+ ### HTTP 400 エラー
487
+
488
+ ```json
489
+ {
490
+ "detail": "Invalid session_id"
491
+ }
492
+ ```
493
+
494
+ **原因**:
495
+ - セッションIDが存在しない
496
+ - セッションが期限切れ (600秒無操作)
497
+
498
+ **対処**: 新しいセッションを作成
499
+
500
+ ---
501
+
502
+ ```json
503
+ {
504
+ "detail": "Sample rate mismatch: expected 16000, got 44100"
505
+ }
506
+ ```
507
+
508
+ **原因**: チャンクのサンプルレートがセッション作成時と異なる
509
+
510
+ **対処**: 音声を正しいサンプルレートにリサンプル
511
+
512
+ ---
513
+
514
+ ### HTTP 500 エラー
515
+
516
+ **原因**: サーバー内部エラー (モデル推論失敗等)
517
+
518
+ **対処**:
519
+ 1. チャンク長を変更して再試行
520
+ 2. 参照音声を別のものに変更
521
+ 3. 数秒待ってリトライ
522
+
523
+ ---
524
+
525
+ ## ベストプラクティス
526
+
527
+ ### 1. 参照音声の選び方
528
+
529
+ #### プリセット参照音声を使う場合(推奨)
530
+
531
+ ```python
532
+ # デフォルトプリセット使用(最も簡単)
533
+ resp = requests.post(f"{API_BASE}/session", json={
534
+ "sample_rate": 16000,
535
+ "use_uploaded_ref": False # プリセット使用
536
+ })
537
+
538
+ # または明示的に指定
539
+ resp = requests.post(f"{API_BASE}/session", json={
540
+ "sample_rate": 16000,
541
+ "use_uploaded_ref": False,
542
+ "ref_preset_id": "default_female" # or "default_male"
543
+ })
544
+ ```
545
+
546
+ **メリット**:
547
+ - アップロード不要で即座に利用可能
548
+ - 安定した品質の参照音声
549
+ - ネットワーク帯域を節約
550
+
551
+ #### カスタム参照音声をアップロードする場合
552
+
553
+ - **長さ**: 3〜10秒推奨 (最大25秒まで自動切り詰め)
554
+ - **品質**: クリーンな音声 (ノイズ・エコー少ない)
555
+ - **内容**: 単一話者、自然な発話
556
+
557
+ ### 2. チャンク分割
558
+
559
+ ```python
560
+ # ❌ 悪い例: オーバーラップ考慮なし
561
+ chunks = [audio[i:i+chunk_len] for i in range(0, len(audio), chunk_len)]
562
+
563
+ # ✅ 良い例: オーバーラップなし(サーバー側で処理)
564
+ chunk_len_samples = int(SAMPLE_RATE * CHUNK_LEN_MS / 1000)
565
+ chunks = [audio[i:i+chunk_len_samples]
566
+ for i in range(0, len(audio), chunk_len_samples)]
567
+ ```
568
+
569
+ **重要**: クライアント側でオーバーラップを持たせる必要はありません。サーバーが前回チャンクの末尾を保持してクロスフェード処理します。
570
+
571
+ ### 3. セッション管理
572
+
573
+ ```python
574
+ # セッション再利用(同一話者の複数音声変換)
575
+ for source_file in source_files:
576
+ # チャンク処理...
577
+ pass
578
+ # 最後に1回だけ終了
579
+ requests.post(f"{API_BASE}/end", json={"session_id": session_id})
580
+ ```
581
+
582
+ ### 4. エラーリトライ
583
+
584
+ ```python
585
+ import time
586
+
587
+ MAX_RETRIES = 3
588
+ for attempt in range(MAX_RETRIES):
589
+ try:
590
+ resp = requests.post(f"{API_BASE}/chunk", ...)
591
+ resp.raise_for_status()
592
+ break
593
+ except requests.RequestException as e:
594
+ if attempt == MAX_RETRIES - 1:
595
+ raise
596
+ time.sleep(2 ** attempt) # Exponential backoff
597
+ ```
598
+
599
+ ---
600
+
601
+ ## 技術詳細
602
+
603
+ ### モデルコンポーネント
604
+
605
+ 1. **Whisper (semantic feature extractor)**
606
+ - 入力: 16kHz音声
607
+ - 出力: セマンティック特徴量
608
+
609
+ 2. **CAMPPlus (speaker encoder)**
610
+ - 入力: 16kHz音声のFbank特徴量
611
+ - 出力: 話者埋め込みベクトル
612
+
613
+ 3. **DiT-based Flow Matching Model**
614
+ - 入力: セマンティック特徴 + 話者埋め込み
615
+ - 出力: メルスペクトログラム
616
+ - 推論ステップ数: 10
617
+ - CFG rate: 0.7
618
+
619
+ 4. **BigVGAN Vocoder**
620
+ - 入力: メルスペクトログラム
621
+ - 出力: 22050Hz音声波形
622
+
623
+ ### サンプルレート変換フロー
624
+
625
+ ```
626
+ 入力音声 (16kHz)
627
+
628
+ Seed-VC内部リサンプル (22050Hz)
629
+
630
+ Whisper用ダウンサンプル (16kHz)
631
+
632
+ 推論処理 (22050Hz mel)
633
+
634
+ Vocoder出力 (22050Hz)
635
+ ```
636
+
637
+ ---
638
+
639
+ ## 制限事項
640
+
641
+ 1. **リアルタイム性**: GPU環境でもRTF > 1.0 (完全なリアルタイム処理は不可)
642
+ 2. **セッションタイムアウト**: 600秒無操作で自動削除
643
+ 3. **参照音声長**: 最大25秒まで
644
+ 4. **同時セッション数**: Hugging Face Spacesの制限に依存
645
+ 5. **GPU必須**: CPU環境ではRTF 20〜60x (実用不可)
646
+
647
+ ---
648
+
649
+ ## FAQ
650
+
651
+ ### Q: チャンクサイズを小さくすればレイテンシは下がる?
652
+
653
+ A: 初回コールドスタートのオーバーヘッド(~2秒)が支配的なため、100ms以下にしても劇的な改善はありません。500msが推奨です。
654
+
655
+ ### Q: クライアント側でクロスフェードする必要は?
656
+
657
+ A: 不要です。サーバーが`overlap_ms`に基づいて自動処理します。受信したチャンクをそのまま結合してください。
658
+
659
+ ### Q: 複数セッションを同時に使える?
660
+
661
+ A: 可能ですが、各セッションは独立してGPUメモリを消費します。Hugging Face Spacesの無料枠では同時1〜2セッションが現実的です。
662
+
663
+ ### Q: CPUモードで動作する?
664
+
665
+ A: 動作しますが、RTF 20〜60xと実用的ではありません。GPU環境必須です。
666
+
667
+ ---
668
+
669
+ ## サポート・問い合わせ
670
+
671
+ - **リポジトリ**: https://huggingface.co/spaces/akatukiseed/seed-vc-streaming
672
+ - **ベースモデル**: https://github.com/Plachtaa/seed-vc
673
+ - **Hugging Face Space**: https://akatukiseed-seed-vc-streaming.hf.space
674
+
675
+ ---
676
+
677
+ ## 変更履歴
678
+
679
+ | バージョン | 日付 | 変更内容 |
680
+ |----------|------|---------|
681
+ | 1.0.0 | 2025-11-22 | 初版リリース |