From b0e2ae8e8316b983b7c8d97a2c03ea5390d4516d Mon Sep 17 00:00:00 2001 From: pstruebi Date: Sat, 7 Jun 2025 11:50:11 +0200 Subject: [PATCH] add saving to wav file for test purposes --- .gitignore | 1 + src/auracast/server/multicast_server.py | 57 ++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 6479242..e0a7689 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ __pycache__/ */.env wg_config/wg_confs/ +records/ diff --git a/src/auracast/server/multicast_server.py b/src/auracast/server/multicast_server.py index e11b727..346a169 100644 --- a/src/auracast/server/multicast_server.py +++ b/src/auracast/server/multicast_server.py @@ -1,6 +1,10 @@ import glob +import os import logging as log import uuid + +import numpy as np +from pydantic import BaseModel from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from auracast import multicast_control, auracast_config @@ -8,7 +12,6 @@ from aiortc import RTCPeerConnection, RTCSessionDescription, MediaStreamTrack import av import av.audio.layout from typing import List, Set -from pydantic import BaseModel import traceback app = FastAPI() @@ -120,6 +123,9 @@ async def offer(offer: Offer): id_ = uuid.uuid4().hex[:8] log.info(f"{id_}: new PeerConnection") + # create directory for records - only for testing + os.makedirs("./records", exist_ok=True) + @pc.on("track") async def on_track(track: MediaStreamTrack): log.info(f"{id_}: track {track.kind} received") @@ -134,14 +140,51 @@ async def offer(offer: Offer): f"{id_}: frame sample_rate={frame.sample_rate}, samples_per_channel={frame.samples}, planes={frame.planes}" ) first = False - array = frame.to_ndarray() - log.info(f"array.shape{array.shape}") - log.info(f"array.dtype{array.dtype}") - log.info(f"frame.to_ndarray(){array}") + # in stereo case this is interleaved data format + frame_array = frame.to_ndarray() + log.info(f"array.shape{frame_array.shape}") + log.info(f"array.dtype{frame_array.dtype}") + log.info(f"frame.to_ndarray(){frame_array}") - # TODO: write to file, pipe to ASR, etc. + frame_array = frame.to_ndarray() + log.info(f"frame_array.shape: {frame_array.shape}") + + # Flatten in case it's (1, N) or (N,) + samples = frame_array.reshape(-1) + log.info(f"samples.shape: {samples.shape}") + + if frame.layout.name == 'stereo': + # Interleaved stereo: [L0, R0, L1, R1, ...] + mono_array = samples[::2] # Take left channel + else: + mono_array = samples + + log.info(f"mono_array.shape: {mono_array.shape}") + + # Save to WAV file - only for testing + # if not hasattr(pc, 'wav_writer'): + # import wave + # wav_path = f"./records/auracast_{id_}.wav" + # pc.wav_writer = wave.open(wav_path, "wb") + # pc.wav_writer.setnchannels(1) # mono + # pc.wav_writer.setsampwidth(2) # 16-bit PCM + # pc.wav_writer.setframerate(frame.sample_rate) + + # pcm_data = mono_array.astype(np.int16).tobytes() + # pc.wav_writer.writeframes(pcm_data) + + + except Exception as e: log.error(f"{id_}: Exception in on_track: {e}") + finally: + # Always close the wav file when the track ends or on error + if hasattr(pc, 'wav_writer'): + try: + pc.wav_writer.close() + except Exception: + pass + del pc.wav_writer # --- SDP negotiation --- log.info(f"{id_}: setting remote description") @@ -175,7 +218,7 @@ async def shutdown(): if __name__ == '__main__': import uvicorn log.basicConfig( - level=log.DEBUG, + level=log.INFO, format='%(module)s.py:%(lineno)d %(levelname)s: %(message)s' ) uvicorn.run(app, host="0.0.0.0", port=5000) \ No newline at end of file