Implement play_wav, record_voice, and host-side voice assistant coordinator script
This commit is contained in:
+114
@@ -283,3 +283,117 @@ def play_tone(frequency=440, duration_ms=1000, volume=50):
|
||||
print("Tone playback complete.")
|
||||
return True
|
||||
|
||||
|
||||
def play_wav(filename, volume=50):
|
||||
"""Plays a standard WAV audio file on the board speaker.
|
||||
|
||||
Standard format: 16-bit PCM, 16kHz sample rate (recommended).
|
||||
"""
|
||||
import struct
|
||||
|
||||
print(f"Playing WAV: {filename} (vol={volume})...")
|
||||
|
||||
try:
|
||||
f = open(filename, 'rb')
|
||||
except OSError:
|
||||
print(f"Error: Cannot open WAV file '{filename}'")
|
||||
return False
|
||||
|
||||
try:
|
||||
# 1. Parse WAV header chunk by chunk
|
||||
riff_header = f.read(12)
|
||||
if len(riff_header) < 12 or riff_header[0:4] != b'RIFF' or riff_header[8:12] != b'WAVE':
|
||||
print("Error: Invalid WAV file format")
|
||||
f.close()
|
||||
return False
|
||||
|
||||
channels = 1
|
||||
sample_rate = 16000
|
||||
bits = 16
|
||||
|
||||
while True:
|
||||
chunk_header = f.read(8)
|
||||
if len(chunk_header) < 8:
|
||||
break
|
||||
chunk_id, chunk_size = struct.unpack('<4sI', chunk_header)
|
||||
|
||||
if chunk_id == b'fmt ':
|
||||
fmt_data = f.read(chunk_size)
|
||||
if len(fmt_data) >= 16:
|
||||
audio_format, channels, sample_rate, byte_rate, block_align, bits = struct.unpack('<HHIIHH', fmt_data[:16])
|
||||
if audio_format != 1:
|
||||
print(f"Warning: Non-PCM audio format ({audio_format})")
|
||||
else:
|
||||
print("Error: fmt chunk too small")
|
||||
f.close()
|
||||
return False
|
||||
elif chunk_id == b'data':
|
||||
# Audio data begins immediately after this chunk size
|
||||
break
|
||||
else:
|
||||
# Skip unknown chunk (align to even byte)
|
||||
skip_bytes = (chunk_size + 1) & ~1
|
||||
f.seek(skip_bytes, 1)
|
||||
|
||||
print(f"WAV Info: {sample_rate}Hz, {bits} bits, {'Mono' if channels == 1 else 'Stereo'}")
|
||||
|
||||
# 2. Setup Master Clock (MCLK) on GPIO 16 using PWM
|
||||
mclk_pin = Pin(16, Pin.OUT)
|
||||
mclk_pwm = machine.PWM(mclk_pin)
|
||||
mclk_pwm.freq(12288000)
|
||||
mclk_pwm.duty_u16(32768)
|
||||
|
||||
# 3. Start I2C Control Bus (SDA=13, SCL=14)
|
||||
i2c = I2C(0, sda=Pin(13), scl=Pin(14))
|
||||
|
||||
# 4. Initialize the ES8311 DAC
|
||||
dac = ES8311(i2c)
|
||||
if not dac.init(sample_rate=16000):
|
||||
mclk_pwm.deinit()
|
||||
f.close()
|
||||
return False
|
||||
|
||||
dac.set_volume(volume)
|
||||
|
||||
# 5. Configure I2S TX
|
||||
# Pins: sck=BCLK (GPIO 9), ws=WS/LRCK (GPIO 45), sd=DOUT (GPIO 8)
|
||||
i2s_format = I2S.MONO if channels == 1 else I2S.STEREO
|
||||
i2s = I2S(1,
|
||||
sck=Pin(9),
|
||||
ws=Pin(45),
|
||||
sd=Pin(8),
|
||||
mode=I2S.TX,
|
||||
ibuf=4096,
|
||||
rate=sample_rate,
|
||||
bits=bits,
|
||||
format=i2s_format)
|
||||
|
||||
# 6. Enable Speaker Amplifier (GPIO 46)
|
||||
amp_pin = Pin(46, Pin.OUT, value=1)
|
||||
|
||||
# 7. Read and stream chunks to I2S
|
||||
buf = bytearray(2048)
|
||||
while True:
|
||||
bytes_read = f.readinto(buf)
|
||||
if bytes_read == 0:
|
||||
break
|
||||
i2s.write(buf[:bytes_read])
|
||||
|
||||
# 8. Clean up
|
||||
time.sleep_ms(100) # Let the remaining buffer play out
|
||||
amp_pin.value(0)
|
||||
i2s.deinit()
|
||||
mclk_pwm.deinit()
|
||||
f.close()
|
||||
print("WAV playback complete.")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error during WAV playback: {e}")
|
||||
try:
|
||||
f.close()
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user