t
This commit is contained in:
75
audio.py
75
audio.py
@@ -42,33 +42,70 @@ class AudioPlayer:
|
||||
self.i2s = None
|
||||
|
||||
def play_tone(self, frequency, duration_ms, volume=0.5):
|
||||
"""播放指定频率的音调"""
|
||||
"""播放指定频率的音调 (优化内存版)"""
|
||||
if self.i2s is None: return
|
||||
|
||||
sample_rate = self.config.get('sample_rate', 24000)
|
||||
n_samples = int(sample_rate * duration_ms / 1000)
|
||||
|
||||
if frequency <= 0:
|
||||
# 静音处理
|
||||
time.sleep_ms(duration_ms)
|
||||
return
|
||||
|
||||
# 振幅
|
||||
amplitude = int(32767 * volume)
|
||||
|
||||
# STEREO: 每个采样 2 个声道 (L+R),每个声道 2 字节 (16-bit) -> 4 字节/帧
|
||||
buffer = bytearray(n_samples * 4)
|
||||
if frequency > 0:
|
||||
period = sample_rate // frequency
|
||||
half_period = period // 2
|
||||
# 计算单周期采样数
|
||||
period = sample_rate // frequency
|
||||
|
||||
# 目标 buffer 大小约 2048 字节 (防止 buffer 只有几字节导致 underrun)
|
||||
target_size = 2048
|
||||
frame_size = 4 # 16bit stereo
|
||||
|
||||
# 计算 buffer 中包含多少个完整周期
|
||||
period_bytes = period * frame_size
|
||||
repeats = max(1, target_size // period_bytes)
|
||||
buffer_bytes = repeats * period_bytes
|
||||
|
||||
buffer = bytearray(buffer_bytes)
|
||||
|
||||
# 填充 buffer
|
||||
half_period = period // 2
|
||||
|
||||
# 预计算采样值的高低字节
|
||||
pos_val = amplitude
|
||||
neg_val = -amplitude
|
||||
|
||||
pos_low = pos_val & 0xFF
|
||||
pos_high = (pos_val >> 8) & 0xFF
|
||||
neg_low = neg_val & 0xFF
|
||||
neg_high = (neg_val >> 8) & 0xFF
|
||||
|
||||
for i in range(period * repeats):
|
||||
# 方波:前半周期高电平,后半周期低电平
|
||||
if (i % period) < half_period:
|
||||
low, high = pos_low, pos_high
|
||||
else:
|
||||
low, high = neg_low, neg_high
|
||||
|
||||
for i in range(n_samples):
|
||||
# 方波:前半周期高电平,后半周期低电平
|
||||
sample = amplitude if (i % period) < half_period else -amplitude
|
||||
# 左声道
|
||||
struct.pack_into('<h', buffer, i * 4, sample)
|
||||
# 右声道
|
||||
struct.pack_into('<h', buffer, i * 4 + 2, sample)
|
||||
else:
|
||||
# 静音,缓冲区默认为0
|
||||
pass
|
||||
idx = i * 4
|
||||
buffer[idx] = low
|
||||
buffer[idx+1] = high
|
||||
buffer[idx+2] = low
|
||||
buffer[idx+3] = high
|
||||
|
||||
# 计算总共需要写入的数据量
|
||||
total_bytes = int((sample_rate * duration_ms / 1000) * frame_size)
|
||||
|
||||
written = 0
|
||||
try:
|
||||
# 写入多次以确保缓冲区填满并开始播放
|
||||
self.i2s.write(buffer)
|
||||
while written < total_bytes:
|
||||
to_write = min(len(buffer), total_bytes - written)
|
||||
if to_write == len(buffer):
|
||||
self.i2s.write(buffer)
|
||||
else:
|
||||
self.i2s.write(buffer[:to_write])
|
||||
written += to_write
|
||||
except Exception as e:
|
||||
print(f"Write error: {e}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user