Last active
October 15, 2024 16:55
-
-
Save MawKKe/c68c061cb0b69e5011a5cc419bb968fa to your computer and use it in GitHub Desktop.
Demo - Playback of generated audio stream with ffplay (ffmpeg) in real time
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"2024-10-15 19:54:26.653 ffplay[98661:3541394] +[IMKClient subclass]: chose IMKClient_Legacy\n", | |
"2024-10-15 19:54:26.653 ffplay[98661:3541394] +[IMKInputSession subclass]: chose IMKInputSession_Legacy\n" | |
] | |
}, | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"\n", | |
"total bytes: 1327104 (requested: 1323000), total samples: 663552.0\n", | |
"took: 15.47s\n" | |
] | |
}, | |
{ | |
"data": { | |
"image/png": "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", | |
"text/plain": [ | |
"<Figure size 640x480 with 1 Axes>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"import subprocess\n", | |
"import typing as t\n", | |
"import time\n", | |
"import io\n", | |
"import contextlib\n", | |
"from dataclasses import dataclass\n", | |
"\n", | |
"import numpy as np\n", | |
"import numpy.typing as npt\n", | |
"import pandas as pd\n", | |
"\n", | |
"\n", | |
"\"\"\"\n", | |
"Playback of generated audio stream with ffplay (ffmpeg) in real time\n", | |
"\n", | |
"Audio processing is implemented as a pipeline, which keeps memory\n", | |
"usage minimal.\n", | |
"\n", | |
"NOTE: assumes ffplay (ffmpeg) is installed and available via $PATH.\n", | |
"\n", | |
"Setup:\n", | |
" $ pip install numpy pandas\n", | |
"\n", | |
"Run:\n", | |
" Open the notebook in jupyter or VSCode (with jupyter extension\n", | |
" installed). Or copy-paste the code into normal .py file and\n", | |
" run as usual.\n", | |
"\n", | |
"Author:\n", | |
" Markus H (MawKKe) 2024-10-15\n", | |
" https://github.com/MawKKe\n", | |
"\"\"\"\n", | |
"\n", | |
"\n", | |
"def make_tone(freq: float, sample_rate: int, num_samples: int) -> npt.NDArray:\n", | |
" t = np.arange(0, num_samples) / sample_rate\n", | |
" return np.sin(2 * np.pi * freq * t)\n", | |
"\n", | |
"\n", | |
"def gen_audio_tone_chunked(freq: int, sample_rate: int, chunk_size: int = 4096):\n", | |
" assert chunk_size <= 2 * sample_rate # magic constants\n", | |
"\n", | |
" y = make_tone(freq=freq, sample_rate=sample_rate, num_samples=2 * sample_rate)\n", | |
"\n", | |
" t = 0\n", | |
" while True:\n", | |
" yield y[t : t + chunk_size]\n", | |
" t = (t + chunk_size) % sample_rate\n", | |
"\n", | |
"\n", | |
"def convert_to_s16le(audio: npt.NDArray[np.float32]) -> bytes:\n", | |
" return (audio * (2**15 - 1)).astype('<i2').tobytes()\n", | |
"\n", | |
"\n", | |
"@contextlib.contextmanager\n", | |
"def ffplay(audio_format: str, sample_rate: int) -> t.Generator:\n", | |
" cmd = [\n", | |
" 'ffplay',\n", | |
" '-hide_banner',\n", | |
" '-loglevel',\n", | |
" 'error',\n", | |
" '-f',\n", | |
" str(audio_format),\n", | |
" '-ar',\n", | |
" str(sample_rate),\n", | |
" '-vn',\n", | |
" '-autoexit',\n", | |
" '-i',\n", | |
" '-',\n", | |
" ]\n", | |
" try:\n", | |
" proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)\n", | |
" # suppress mypy complaint 'NoneType has no .write'\n", | |
" proc.stdin = t.cast(io.BufferedWriter, proc.stdin)\n", | |
"\n", | |
" def player(chunk: bytes) -> int:\n", | |
" # return number of bytes written\n", | |
" return proc.stdin.raw.write(chunk)\n", | |
"\n", | |
" yield player\n", | |
" finally:\n", | |
" proc.stdin.flush()\n", | |
" proc.stdin.close()\n", | |
" proc.wait()\n", | |
"\n", | |
"\n", | |
"@dataclass\n", | |
"class AudioFormat:\n", | |
" name: str\n", | |
" sample_size: int\n", | |
" converter: t.Callable[[npt.NDArray], bytes]\n", | |
"\n", | |
"\n", | |
"S16LE = AudioFormat('s16le', 2, convert_to_s16le)\n", | |
"\n", | |
"\n", | |
"def main():\n", | |
" volume = 0.1\n", | |
" chunk_size = 4096\n", | |
" sample_rate = 44100\n", | |
" freq = 440\n", | |
" duration = 15\n", | |
"\n", | |
" # infinite stream of audio samples of sine at 'freq', chunked\n", | |
" audio_chunks = gen_audio_tone_chunked(freq=freq, sample_rate=sample_rate, chunk_size=chunk_size)\n", | |
"\n", | |
" def run_player(chunk_stream, afmt: AudioFormat, max_samples: int):\n", | |
" start = time.time()\n", | |
" tot_bytes = 0\n", | |
" with ffplay(audio_format=afmt.name, sample_rate=sample_rate) as play:\n", | |
" for chunk in chunk_stream:\n", | |
" if tot_bytes >= (afmt.sample_size * max_samples):\n", | |
" break\n", | |
"\n", | |
" tot_bytes += play(afmt.converter(volume * chunk))\n", | |
"\n", | |
" yield {\n", | |
" 't': time.time() - start,\n", | |
" 'bytes': tot_bytes,\n", | |
" 'samples': tot_bytes // afmt.sample_size,\n", | |
" }\n", | |
"\n", | |
" print(\n", | |
" f'total bytes: {tot_bytes} (requested: {2 * max_samples}), total samples: {tot_bytes/2}'\n", | |
" )\n", | |
"\n", | |
" start_t = time.time()\n", | |
"\n", | |
" # DataFrame.from_records is the \"sink\" that consumes the generator pipeline\n", | |
" df = pd.DataFrame.from_records(\n", | |
" run_player(chunk_stream=audio_chunks, afmt=S16LE, max_samples=duration * sample_rate)\n", | |
" )\n", | |
"\n", | |
" end_t = time.time()\n", | |
"\n", | |
" print(f'took: {end_t-start_t:.2f}s')\n", | |
"\n", | |
" return df\n", | |
"\n", | |
"\n", | |
"def plot_stdin_datarate(df: pd.DataFrame) -> None:\n", | |
" ax = df.plot(x='t', y='samples')\n", | |
" ax.set_title('ffplay stdin datarate')\n", | |
" ax.set_xlabel('Time(s)')\n", | |
" ax.set_ylabel('Samples')\n", | |
" ax.grid(visible=True)\n", | |
"\n", | |
"\n", | |
"df = main()\n", | |
"plot_stdin_datarate(df)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Clearly there is initial burst transfer of data into the ffplay process; after the buffer is saturated,\n", | |
"the writes start to block in the producer (python). Roughly eyeballing the slope of the figure between\n", | |
"$2..8s$ results in 44k-45k samples per second, which matches the audio sink rate. Neat 🙂" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "venv", | |
"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.13.0" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment