- Python
- Python (Async)
- TypeScript
同期コードでは両方のコンテキストから同時並行に受信できないため、順次収集します。ただし、出典: cartesia-python/examples/examples.py:395
receive() の遅延ルーティングにより、コンテキスト 1 を読んでいる間に消費されたイベントはコンテキスト 2 用にキューイングされます(逆も同様)。def tts_websocket_concurrent_receives(client: Cartesia) -> None:
"""Two contexts on one connection, each using ctx.receive() to get their own audio.
Since sync code can't receive from both contexts concurrently, we collect
them sequentially — but the lazy-routing in receive() ensures that events
consumed while reading context 1 are queued for context 2 (and vice-versa).
"""
from cartesia.types import RawOutputFormatParam
output_format: RawOutputFormatParam = {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 44100}
with client.tts.websocket_connect() as connection:
ctx1 = connection.context(
model_id="sonic-latest",
voice={"mode": "id", "id": "6ccbfb76-1fc6-48f7-b71d-91ac6298247b"},
output_format=output_format,
)
ctx2 = connection.context(
model_id="sonic-latest",
voice={"mode": "id", "id": "6ccbfb76-1fc6-48f7-b71d-91ac6298247b"},
output_format=output_format,
)
# Send to both contexts before receiving
ctx1.push(
"Context one is speaking now. This is a longer transcript to ensure that audio chunks from both contexts are interleaved on the wire. The quick brown fox jumps over the lazy dog."
)
ctx1.no_more_inputs()
ctx2.push(
"Context two has a different message. We want to verify that the routing logic correctly separates the audio streams. Pack my box with five dozen liquor jugs."
)
ctx2.no_more_inputs()
import datetime
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
# Receive from ctx1 — any ctx2 events read from the wire get queued
filename1 = f"tts_concurrent_ctx1_{timestamp}.pcm"
with open(filename1, "wb") as f:
for response in ctx1.receive():
if response.type == "chunk" and response.audio:
f.write(response.audio)
elif response.type == "error":
print(f"error: {response.message or response.title}")
# Receive from ctx2 — picks up queued events first
filename2 = f"tts_concurrent_ctx2_{timestamp}.pcm"
with open(filename2, "wb") as f:
for response in ctx2.receive():
if response.type == "chunk" and response.audio:
f.write(response.audio)
elif response.type == "error":
print(f"error: {response.message or response.title}")
print(f"Saved context 1 audio to {filename1}")
print(f"Saved context 2 audio to {filename2}")
print(f"Play with:")
print(f" ffplay -f s16le -ar 44100 {filename1}")
print(f" ffplay -f s16le -ar 44100 {filename2}")
非同期版では、送信側と受信側の両方を 出典: cartesia-python/examples/async_examples.py:309
asyncio.gather を通じて並行実行します。接続のバックグラウンドリスナーがイベントをコンテキストごとのキューにルーティングし、各 ctx.receive() イテレータがそれぞれのキューを消費します。async def tts_websocket_concurrent_receives_async(client: AsyncCartesia) -> None:
"""Two contexts on one connection, each using ctx.receive() concurrently via tasks.
The lazy-routing in receive() ensures that whichever task happens to read an
event from the wire routes it to the correct context's queue.
"""
import asyncio
import datetime
from cartesia.types import RawOutputFormatParam
from cartesia.resources.tts import AsyncWebSocketContext
output_format: RawOutputFormatParam = {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 44100}
async with client.tts.websocket_connect() as connection:
ctx1 = connection.context(
model_id="sonic-latest",
voice={"mode": "id", "id": "6ccbfb76-1fc6-48f7-b71d-91ac6298247b"},
output_format=output_format,
)
ctx2 = connection.context(
model_id="sonic-latest",
voice={"mode": "id", "id": "6ccbfb76-1fc6-48f7-b71d-91ac6298247b"},
output_format=output_format,
)
# Send to both contexts
await ctx1.push(
"Context one is speaking now. This is a longer transcript to ensure that audio chunks from both contexts are interleaved on the wire. The quick brown fox jumps over the lazy dog."
)
await ctx1.no_more_inputs()
await ctx2.push(
"Context two has a different message. We want to verify that the routing logic correctly separates the audio streams. Pack my box with five dozen liquor jugs."
)
await ctx2.no_more_inputs()
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
# Receive concurrently via tasks, writing to files
async def collect(ctx: AsyncWebSocketContext, filename: str) -> None:
with open(filename, "wb") as f:
async for response in ctx.receive():
if response.type == "chunk" and response.audio:
f.write(response.audio)
elif response.type == "error":
print(f"error: {response.message or response.title}")
filename1 = f"tts_concurrent_async_ctx1_{timestamp}.pcm"
filename2 = f"tts_concurrent_async_ctx2_{timestamp}.pcm"
await asyncio.gather(
collect(ctx1, filename1),
collect(ctx2, filename2),
)
print(f"Saved context 1 audio to {filename1}")
print(f"Saved context 2 audio to {filename2}")
print(f"Play with:")
print(f" ffplay -f s16le -ar 44100 {filename1}")
print(f" ffplay -f s16le -ar 44100 {filename2}")
async function ttsWebsocketConcurrentContexts(client: Cartesia): Promise<void> {
const ws = await client.tts.websocket();
ws.on('error', (err) => console.error('WS error:', err.message));
try {
const ctx1 = ws.context({
model_id: 'sonic-latest',
voice: { mode: 'id', id: '6ccbfb76-1fc6-48f7-b71d-91ac6298247b' },
output_format: { container: 'raw', encoding: 'pcm_f32le', sample_rate: 44100 },
language: 'en',
});
const ctx2 = ws.context({
model_id: 'sonic-latest',
voice: { mode: 'id', id: '6ccbfb76-1fc6-48f7-b71d-91ac6298247b' },
output_format: { container: 'raw', encoding: 'pcm_f32le', sample_rate: 44100 },
language: 'en',
});
// Send to both contexts before receiving.
await ctx1.push({
transcript:
'Context one is speaking now. This is a longer transcript to ensure that ' +
'audio chunks from both contexts are interleaved on the wire. ' +
'The quick brown fox jumps over the lazy dog.',
});
await ctx1.no_more_inputs();
await ctx2.push({
transcript:
'Context two has a different message. We want to verify that the routing ' +
'logic correctly separates the audio streams. ' +
'Pack my box with five dozen liquor jugs.',
});
await ctx2.no_more_inputs();
const ts = timestamp();
async function collect(ctx: { receive: typeof ctx1.receive }, filename: string): Promise<void> {
const file = fs.createWriteStream(filename);
for await (const event of ctx.receive()) {
if (event.type === 'chunk' && event.audio) {
file.write(event.audio);
} else if (event.type === 'error') {
throw new Error(`${event.title}: ${event.message}`);
}
}
file.end();
}
const filename1 = `tts_concurrent_ctx1_${ts}.pcm`;
const filename2 = `tts_concurrent_ctx2_${ts}.pcm`;
await Promise.all([collect(ctx1, filename1), collect(ctx2, filename2)]);
console.log(`Saved context 1 audio to ${filename1}`);
console.log(`Saved context 2 audio to ${filename2}`);
console.log('Play with:');
console.log(` ffplay -f f32le -ar 44100 ${filename1}`);
console.log(` ffplay -f f32le -ar 44100 ${filename2}`);
} finally {
ws.close();
}
}
このサンプルを実行する
- Python
- Python (Async)
- TypeScript
git clone --branch v3.2.0 https://github.com/cartesia-ai/cartesia-python
cd cartesia-python
uv sync
CARTESIA_API_KEY=YOUR_KEY uv run examples/examples.py tts_websocket_concurrent_receives
git clone --branch v3.2.0 https://github.com/cartesia-ai/cartesia-python
cd cartesia-python
uv sync
CARTESIA_API_KEY=YOUR_KEY uv run examples/async_examples.py tts_websocket_concurrent_receives_async
git clone --branch v3.2.0 https://github.com/cartesia-ai/cartesia-js
cd cartesia-js
pnpm i
CARTESIA_API_KEY=YOUR_KEY pnpm tsn examples/node_examples.ts ttsWebsocketConcurrentContexts