Add stream-desktop-audio
This commit is contained in:
parent
2af98808e0
commit
833b32dc2b
18
stream-desktop-audio/README.md
Normal file
18
stream-desktop-audio/README.md
Normal 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
BIN
stream-desktop-audio/dist/stream.exe
vendored
Normal file
Binary file not shown.
5
stream-desktop-audio/generate_exe.bat
Normal file
5
stream-desktop-audio/generate_exe.bat
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
@echo off
|
||||||
|
pyinstaller --clean --onefile .\stream.py
|
||||||
|
rd /S /Q build
|
||||||
|
del /F /Q stream.spec
|
||||||
|
pause
|
83
stream-desktop-audio/stream.py
Normal file
83
stream-desktop-audio/stream.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user