Skip to content

Commit 300071b

Browse files
committed
feat: implement audio streaming functionality with AudioStream and AsyncAudioStream classes
1 parent 6228352 commit 300071b

File tree

8 files changed

+420
-229
lines changed

8 files changed

+420
-229
lines changed

playground.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import asyncio
2+
from fishaudio import AsyncFishAudio
3+
from fishaudio.utils import play, save
4+
5+
6+
async def main():
7+
# Initialize async client
8+
client = AsyncFishAudio(api_key="5103b6a04a6443628e0289aec1f08750")
9+
10+
# Generate and play audio
11+
audio = await client.tts.convert(text="Hello, playing from Fish Audio!")
12+
play(audio)
13+
14+
# Generate and save audio
15+
audio = await client.tts.convert(text="Saving this audio to a file!")
16+
save(audio, "output.mp3")
17+
18+
19+
asyncio.run(main())

src/fishaudio/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
from ._version import __version__
3030
from .client import AsyncFishAudio, FishAudio
31+
from .core.iterators import AsyncAudioStream, AudioStream
3132
from .exceptions import (
3233
APIError,
3334
AuthenticationError,
@@ -52,6 +53,9 @@
5253
"play",
5354
"save",
5455
"stream",
56+
# Audio streams
57+
"AudioStream",
58+
"AsyncAudioStream",
5559
# Types
5660
"FlushEvent",
5761
"TextEvent",

src/fishaudio/core/iterators.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""Audio stream wrappers with collection utilities."""
2+
3+
from typing import AsyncIterator, Iterator
4+
5+
6+
class AudioStream:
7+
"""Wrapper for sync audio byte streams with collection utilities.
8+
9+
This class wraps an iterator of audio bytes and provides a convenient
10+
`.collect()` method to gather all chunks into a single bytes object.
11+
12+
Examples:
13+
```python
14+
from fishaudio import FishAudio
15+
16+
client = FishAudio(api_key="...")
17+
18+
# Collect all audio at once
19+
audio = client.tts.convert(text="Hello!").collect()
20+
21+
# Or stream chunks manually
22+
for chunk in client.tts.convert(text="Hello!"):
23+
process_chunk(chunk)
24+
```
25+
"""
26+
27+
def __init__(self, iterator: Iterator[bytes]):
28+
"""Initialize the audio iterator wrapper.
29+
30+
Args:
31+
iterator: The underlying iterator of audio bytes
32+
"""
33+
self._iter = iterator
34+
35+
def __iter__(self) -> Iterator[bytes]:
36+
"""Allow direct iteration over audio chunks."""
37+
return self._iter
38+
39+
def collect(self) -> bytes:
40+
"""Collect all audio chunks into a single bytes object.
41+
42+
This consumes the iterator and returns all audio data as bytes.
43+
After calling this method, the iterator cannot be used again.
44+
45+
Returns:
46+
Complete audio data as bytes
47+
48+
Examples:
49+
```python
50+
audio = client.tts.convert(text="Hello!").collect()
51+
with open("output.mp3", "wb") as f:
52+
f.write(audio)
53+
```
54+
"""
55+
chunks = []
56+
for chunk in self._iter:
57+
chunks.append(chunk)
58+
return b"".join(chunks)
59+
60+
61+
class AsyncAudioStream:
62+
"""Wrapper for async audio byte streams with collection utilities.
63+
64+
This class wraps an async iterator of audio bytes and provides a convenient
65+
`.collect()` method to gather all chunks into a single bytes object.
66+
67+
Examples:
68+
```python
69+
from fishaudio import AsyncFishAudio
70+
71+
client = AsyncFishAudio(api_key="...")
72+
73+
# Collect all audio at once
74+
audio = await client.tts.convert(text="Hello!").collect()
75+
76+
# Or stream chunks manually
77+
async for chunk in client.tts.convert(text="Hello!"):
78+
await process_chunk(chunk)
79+
```
80+
"""
81+
82+
def __init__(self, async_iterator: AsyncIterator[bytes]):
83+
"""Initialize the async audio iterator wrapper.
84+
85+
Args:
86+
async_iterator: The underlying async iterator of audio bytes
87+
"""
88+
self._iter = async_iterator
89+
90+
def __aiter__(self) -> AsyncIterator[bytes]:
91+
"""Allow direct async iteration over audio chunks."""
92+
return self._iter
93+
94+
async def collect(self) -> bytes:
95+
"""Collect all audio chunks into a single bytes object.
96+
97+
This consumes the async iterator and returns all audio data as bytes.
98+
After calling this method, the iterator cannot be used again.
99+
100+
Returns:
101+
Complete audio data as bytes
102+
103+
Examples:
104+
```python
105+
audio = await client.tts.convert(text="Hello!").collect()
106+
with open("output.mp3", "wb") as f:
107+
f.write(audio)
108+
```
109+
"""
110+
chunks = []
111+
async for chunk in self._iter:
112+
chunks.append(chunk)
113+
return b"".join(chunks)

0 commit comments

Comments
 (0)