{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "06d270dd-24cf-4485-b644-c4805759c5dd", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "374867a5eb154d569061b5f3fd2d48ce", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Audio(value=b'', format='wav')" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Streaming with 6.857142857142857s buffer: http://localhost:8000/stream.mp3\n" ] } ], "source": [ "import io\n", "import wave\n", "import time\n", "import av\n", "import numpy as np\n", "import ipywidgets as widgets\n", "from IPython.display import display\n", "#from FoxDot import *\n", "\n", "#################\n", "Clock_bpm=140\n", "Num_patterns=8.0\n", "#################\n", "\n", "bpm_old=Clock_bpm\n", "np_old=Num_patterns\n", "\n", "# Setup the widget\n", "audio_widget = widgets.Audio(value=b'', format='wav', autoplay=True)\n", "display(audio_widget)\n", "\n", "def stream_buffered_audio(url, buffer_seconds=Num_patterns*120/Clock_bpm):\n", " global bpm_old, Clock_bpm\n", " global np_old, Num_patterns\n", " container = av.open(url)\n", " # Standardize output format\n", " resampler = av.audio.resampler.AudioResampler(format='s16', layout='stereo', rate=44100)\n", " \n", " # Use a FIFO to collect frames into a larger chunk\n", " fifo = av.audio.fifo.AudioFifo()\n", " \n", " # Calculate how many samples we want per \"push\" to the widget\n", " samples_per_push = int(44100 * buffer_seconds)\n", " \n", " print(f\"Streaming with {buffer_seconds}s buffer: {url}\")\n", " start_time = time.perf_counter()\n", " pushed_duration = 0.0\n", "\n", " for frame in container.decode(audio=0):\n", " # 1. Resample and push to FIFO\n", " resampled = resampler.resample(frame)\n", " if resampled:\n", " for f in resampled:\n", " fifo.write(f)\n", " \n", " # 2. Once FIFO has enough data, create a chunk\n", " while fifo.samples >= samples_per_push:\n", " chunk = fifo.read(samples_per_push)\n", " \n", " # Convert to WAV bytes\n", " buffer = io.BytesIO()\n", " with wave.open(buffer, 'wb') as wav_file:\n", " wav_file.setnchannels(2)\n", " wav_file.setsampwidth(2)\n", " wav_file.setframerate(44100)\n", " wav_file.writeframes(chunk.to_ndarray().tobytes())\n", " \n", " # 3. Precise Timing Logic\n", " actual_elapsed = time.perf_counter() - start_time\n", " # Wait until it's actually time to play this chunk\n", " wait_time = pushed_duration - actual_elapsed\n", " if wait_time > 0:\n", " time.sleep(wait_time)\n", " \n", " # 4. Push to widget\n", " audio_widget.value = buffer.getvalue()\n", " pushed_duration += buffer_seconds\n", " if (Num_patterns!=np_old) or (Clock_bpm!=bpm_old):\n", " samples_per_push = int(44100 * buffer_seconds)\n", " np_old = Num_patterns\n", " bpm_old = Clock_bpm\n", "\n", "# Test URL\n", "try:\n", " stream_buffered_audio(\"http://localhost:8000/stream.mp3\", buffer_seconds=Num_patterns*120/Clock_bpm)\n", "except KeyboardInterrupt:\n", " print(\"\\nStream stopped.\")" ] }, { "cell_type": "code", "execution_count": null, "id": "90dd4afb-0596-4559-8780-a7e35cb039be", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "e02d1ed2-7011-493a-bed0-eac4d802d920", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.5" } }, "nbformat": 4, "nbformat_minor": 5 }