77 lines
2.7 KiB
Python
77 lines
2.7 KiB
Python
|
|
import asyncio
|
||
|
|
import websockets
|
||
|
|
import json
|
||
|
|
from pydub import AudioSegment
|
||
|
|
import os
|
||
|
|
|
||
|
|
# Configuración de tu Bocina IP
|
||
|
|
IP_BOCINA = "192.168.1.142"
|
||
|
|
PUERTO_WS = 81
|
||
|
|
|
||
|
|
async def _enviar_audio_ws(pcm_data):
|
||
|
|
url = f"ws://{IP_BOCINA}:{PUERTO_WS}"
|
||
|
|
chunk_size = 1024
|
||
|
|
|
||
|
|
try:
|
||
|
|
async with websockets.connect(url, ping_timeout=None) as websocket:
|
||
|
|
# Esperamos el mensaje de bienvenida
|
||
|
|
await websocket.recv()
|
||
|
|
|
||
|
|
chunks_enviados = 0
|
||
|
|
for i in range(0, len(pcm_data), chunk_size):
|
||
|
|
chunk = pcm_data[i:i + chunk_size]
|
||
|
|
await websocket.send(chunk)
|
||
|
|
chunks_enviados += 1
|
||
|
|
|
||
|
|
# ⚡ EL TRUCO MAESTRO: CONTROL DE FLUJO
|
||
|
|
if chunks_enviados % 10 == 0:
|
||
|
|
try:
|
||
|
|
# Python se pausa hasta que el ESP32 mande el "ACK"
|
||
|
|
await asyncio.wait_for(websocket.recv(), timeout=1.0)
|
||
|
|
except asyncio.TimeoutError:
|
||
|
|
pass # Si hay lag en la red, forzamos a seguir para no cortar
|
||
|
|
else:
|
||
|
|
await asyncio.sleep(0.002) # Micro-pausa entre paquetes individuales
|
||
|
|
|
||
|
|
# Le damos 1 segundo al ESP32 para reproducir los últimos datos en su memoria
|
||
|
|
await asyncio.sleep(1.0)
|
||
|
|
await websocket.send(json.dumps({"cmd": "STOP"}))
|
||
|
|
return True
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f" Error conectando a la bocina IP {IP_BOCINA}: {e}")
|
||
|
|
return False
|
||
|
|
|
||
|
|
def enviar_archivos_bocina(lista_archivos):
|
||
|
|
"""
|
||
|
|
Recibe una lista de rutas de archivos MP3, los une en un solo track,
|
||
|
|
los convierte al formato del ESP32 y los transmite.
|
||
|
|
"""
|
||
|
|
if not lista_archivos:
|
||
|
|
return
|
||
|
|
|
||
|
|
try:
|
||
|
|
audio_final = AudioSegment.empty()
|
||
|
|
for archivo in lista_archivos:
|
||
|
|
if os.path.exists(archivo):
|
||
|
|
segmento = AudioSegment.from_file(archivo)
|
||
|
|
audio_final += segmento
|
||
|
|
|
||
|
|
audio_mono = audio_final.split_to_mono()[0]
|
||
|
|
|
||
|
|
# ⚡ 2. SUBIMOS LA CALIDAD (A 22050 Hz)
|
||
|
|
audio_final = audio_mono.set_frame_rate(22050).set_sample_width(2)
|
||
|
|
|
||
|
|
# 3. Quitamos el clipping (saturación).
|
||
|
|
# Como ya no estamos sumando canales, podemos dejarle más volumen. Le bajaremos solo 2 dB.
|
||
|
|
audio_final = audio_final - 2
|
||
|
|
|
||
|
|
# 4. Obtenemos los datos puros
|
||
|
|
pcm_data = audio_final.raw_data
|
||
|
|
|
||
|
|
# ⚡ 4. TRANSMITIMOS A LA BOCINA (¡Te faltaba esta parte!)
|
||
|
|
print(f" Transmitiendo audio a {IP_BOCINA}...")
|
||
|
|
asyncio.run(_enviar_audio_ws(pcm_data))
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f"Error procesando el audio para la bocina: {e}")
|