|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +from typing import AsyncIterator |
| 4 | + |
3 | 5 | from .audio_frame import AudioFrame |
4 | 6 |
|
5 | 7 |
|
6 | | -__all__ = ["combine_audio_frames"] |
| 8 | +__all__ = ["combine_audio_frames", "sine_wave_generator"] |
7 | 9 |
|
8 | 10 |
|
9 | 11 | def combine_audio_frames(buffer: AudioFrame | list[AudioFrame]) -> AudioFrame: |
@@ -83,3 +85,54 @@ def combine_audio_frames(buffer: AudioFrame | list[AudioFrame]) -> AudioFrame: |
83 | 85 | num_channels=num_channels, |
84 | 86 | samples_per_channel=total_samples_per_channel, |
85 | 87 | ) |
| 88 | + |
| 89 | + |
| 90 | +async def sine_wave_generator( |
| 91 | + freq: float, |
| 92 | + duration: float, |
| 93 | + sample_rate: int = 48000, |
| 94 | + amplitude: float = 0.3, |
| 95 | +) -> AsyncIterator[AudioFrame]: |
| 96 | + """ |
| 97 | + Generate sine wave audio frames. |
| 98 | +
|
| 99 | + Useful for testing audio pipelines and generating test signals. |
| 100 | +
|
| 101 | + Args: |
| 102 | + freq: Frequency of the sine wave in Hz. |
| 103 | + duration: Duration of the audio in seconds. |
| 104 | + sample_rate: Sample rate in Hz (default: 48000). |
| 105 | + amplitude: Amplitude of the sine wave, range [0.0, 1.0] (default: 0.3). |
| 106 | +
|
| 107 | + Yields: |
| 108 | + AudioFrame: Audio frames containing sine wave data. |
| 109 | +
|
| 110 | + Example: |
| 111 | + >>> import asyncio |
| 112 | + >>> async def generate_audio(): |
| 113 | + ... async for frame in sine_wave_generator(440, 1.0): |
| 114 | + ... print(f"Generated frame with {frame.samples_per_channel} samples") |
| 115 | + >>> asyncio.run(generate_audio()) |
| 116 | + """ |
| 117 | + try: |
| 118 | + import numpy as np |
| 119 | + except ImportError: |
| 120 | + raise ImportError( |
| 121 | + "numpy is required for sine_wave_generator. Install it with: pip install numpy" |
| 122 | + ) |
| 123 | + |
| 124 | + blocksize = sample_rate // 10 |
| 125 | + total_frames = int((duration * sample_rate) // blocksize) |
| 126 | + t_frame = np.arange(blocksize) / sample_rate |
| 127 | + |
| 128 | + for i in range(total_frames): |
| 129 | + t = t_frame + i * blocksize / sample_rate |
| 130 | + signal = amplitude * np.sin(2 * np.pi * freq * t) |
| 131 | + signal_int16 = np.int16(signal * 32767) |
| 132 | + frame = AudioFrame( |
| 133 | + signal_int16.tobytes(), |
| 134 | + sample_rate, |
| 135 | + 1, |
| 136 | + blocksize, |
| 137 | + ) |
| 138 | + yield frame |
0 commit comments