Add stream-desktop-audio

This commit is contained in:
Ventilaar 2024-09-26 15:10:21 +02:00
parent 2af98808e0
commit 833b32dc2b
No known key found for this signature in database
4 changed files with 106 additions and 0 deletions

View File

@ -0,0 +1,18 @@
# stream-desktop-audio
Just as the name implies, this light piece of software makes your windows output device audio stream available on a http webserver.
By starting the program it loads up a simple webserver listening on all addresses port 5000. The stream URL can be found at /stream.wav
Once a request is made the default output device is opened and a WAV header is generated. The header is passed through to the request and the PCM audio stream is appended in chunks.
## Why
The radioplayer at work can open any arbitrary URL as long as the codec is supported. Sometimes people want to requests songs. And being able to play it on the computer which the radioplayer reads from is much easier.
Otherwise we are still limited to online radio stations. The beautiful part is that this works locally and is quite responsive. Also some gags can be played out on the work PA system by a TTS voice (not included functionality).
## Limitations
- Works only on Windows
- Only uses the default output device
- Streams are PCM16LE only, no transcoding
- Stream address and port are fixed
So pretty much no configurability at all (for now)

BIN
stream-desktop-audio/dist/stream.exe vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
@echo off
pyinstaller --clean --onefile .\stream.py
rd /S /Q build
del /F /Q stream.spec
pause

View File

@ -0,0 +1,83 @@
import pyaudiowpatch as pyaudio
from flask import Flask, Response
import socket
app = Flask(__name__)
p = pyaudio.PyAudio()
def genHeader(sampleRate, bitsPerSample, channels):
datasize = 2000*10**6
o = bytes("RIFF",'ascii') # (4byte) Marks file as RIFF
o += (datasize + 36).to_bytes(4,'little') # (4byte) File size in bytes excluding this and RIFF marker
o += bytes("WAVE",'ascii') # (4byte) File type
o += bytes("fmt ",'ascii') # (4byte) Format Chunk Marker
o += (16).to_bytes(4,'little') # (4byte) Length of above format data
o += (1).to_bytes(2,'little') # (2byte) Format type (1 - PCM)
o += (channels).to_bytes(2,'little') # (2byte)
o += (sampleRate).to_bytes(4,'little') # (4byte)
o += (sampleRate * channels * bitsPerSample // 8).to_bytes(4,'little') # (4byte)
o += (channels * bitsPerSample // 8).to_bytes(2,'little') # (2byte)
o += (bitsPerSample).to_bytes(2,'little') # (2byte)
o += bytes("data",'ascii') # (4byte) Data Chunk Marker
o += (datasize).to_bytes(4,'little') # (4byte) Data size in bytes
return o
@app.route('/stream.wav')
def audio():
def sound():
# Test if WASAPI is available and get default device
try:
wasapi_info = p.get_host_api_info_by_type(pyaudio.paWASAPI)
default_speakers = p.get_device_info_by_index(wasapi_info["defaultOutputDevice"])
except OSError:
print("Looks like WASAPI is not available on the system. Exiting...")
exit(1)
# Get the WASAPI loopback device
if not default_speakers["isLoopbackDevice"]:
for loopback in p.get_loopback_device_info_generator():
if default_speakers["name"] in loopback["name"]:
default_speakers = loopback
break
else:
print("Default loopback output device not found.\n\nRun `python -m pyaudiowpatch` to check available devices.\nExiting...\n")
exit(1)
# Setup all required params
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = default_speakers["maxInputChannels"]
RATE = int(default_speakers["defaultSampleRate"])
wav_header = genHeader(RATE, 16, CHANNELS)
first_run = True
# Setup the stream
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
input_device_index=default_speakers["index"],
frames_per_buffer=CHUNK
)
while True:
# If the request is the first one reply with WAV header otherwise yield with the plain stream data
if first_run:
data = wav_header + stream.read(CHUNK)
first_run = False
else:
data = stream.read(CHUNK)
yield(data)
return Response(sound())
if __name__ == "__main__":
print('Welcome to the desktop audio streamer 1.0!')
print('Your desktop audio is now being made available at the following URL:')
print(f'http://{socket.gethostbyname(socket.gethostname())}:5000/stream.wav')
print('')
print('Source code: https://git.ventilaar.nl/ventilaar/random-scripts/src/branch/master/stream-desktop-audio')
print('-'*79)
app.run(host='0.0.0.0', threaded=True,port=5000)