Create client.py

This commit is contained in:
dharm pimsen 2023-12-16 13:21:42 +07:00 committed by GitHub
parent 83b3ba460a
commit b1cefb27c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

184
client.py Normal file
View File

@ -0,0 +1,184 @@
import asyncio
from datetime import datetime
import dearpygui.dearpygui as dpg
import threading
import socket
import numpy as np
import pickle
import pyaudio
import websockets
from pyogg import OpusDecoder
class App:
def __init__(self):
self.RDS = None
self.device_name_output = "Speakers (4- USB Audio DAC )"
self.working = False
def connecttoserver(self, sender, data):
dpg.configure_item("connectbutton", show=False)
protocol = dpg.get_value("serverprotocol")
dpg.configure_item("serverstatus", default_value='connecting...', color=(255, 255, 0))
if protocol == "Websocket":
asyncio.create_task(self.WSstream())
return
ip = dpg.get_value("serverip")
port = dpg.get_value("serverport")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
except:
dpg.configure_item("connectbutton", show=True)
self.working = True
p = pyaudio.PyAudio()
device_index_output = 0
for i in range(p.get_device_count()):
dev = p.get_device_info_by_index(i)
if dev['name'] == self.device_name_output:
device_index_output = dev['index']
break
thread = threading.Thread(target=self.stream, args=(s, device_index_output))
thread.start()
def disconnectserver(self, sender, data):
dpg.configure_item("disconnectbutton", show=False)
dpg.configure_item("serverstatus", default_value='disconnecting...', color=(255, 255, 0))
self.working = False
def RDSshow(self):
try:
dpg.configure_item("RDSinfo",
default_value=f'{self.RDS["PS"]} ({self.RDS["ContentInfo"]["Codec"]} {self.RDS["ContentInfo"]["bitrate"] / 1000}Kbps {self.RDS["AudioMode"]})',
show=True)
dpg.configure_item("RDSPS", default_value="PS: " + self.RDS["PS"])
dpg.configure_item("RDSRT", default_value="RT: " + self.RDS["RT"])
dpg.configure_item("RDSCTlocal", default_value="Time Local: " + datetime.fromtimestamp(self.RDS["CT"]["Local"]).strftime('%H:%M:%S'))
dpg.configure_item("RDSCTUTC", default_value="Time UTC: " + datetime.fromtimestamp(self.RDS["CT"]["UTC"]).strftime('%H:%M:%S'))
dpg.configure_item("RDSListener", default_value="Listener: " + str(self.RDS["Listener"]) + " Users")
except Exception as e:
pass
def stream(self, socket, deviceindex):
opus_decoder = OpusDecoder()
streamoutput = None
firstrun = True
while True:
try:
if self.working:
data = socket.recv(650000)
if len(data) == 0:
dpg.configure_item("serverstatus", default_value='lost connected', color=(255, 0, 0))
socket.close()
dpg.configure_item("showRDS", show=False)
dpg.configure_item("RDSinfo", show=False)
dpg.configure_item("disconnectbutton", show=False)
dpg.configure_item("connectbutton", show=True)
break
try:
datadecoded = pickle.loads(data)
except:
pass
if datadecoded["RDS"] != self.RDS:
self.RDS = datadecoded["RDS"]
rdshow = threading.Thread(target=self.RDSshow)
rdshow.start()
if firstrun:
p = pyaudio.PyAudio()
opus_decoder.set_channels(self.RDS["ContentInfo"]["channel"])
opus_decoder.set_sampling_frequency(self.RDS["ContentInfo"]["samplerates"])
streamoutput = p.open(format=pyaudio.paInt16, channels=self.RDS["ContentInfo"]["channel"], rate=self.RDS["ContentInfo"]["samplerates"], output=True, output_device_index=deviceindex)
dpg.configure_item("showRDS", show=True)
dpg.configure_item("serverstatus", default_value='connected', color=(0, 255, 0))
dpg.configure_item("disconnectbutton", show=True)
firstrun = False
decoded_pcm = opus_decoder.decode(memoryview(bytearray(datadecoded["Content"])))
# Check if the decoded PCM is empty or not
if len(decoded_pcm) > 0:
pcm_to_write = np.frombuffer(decoded_pcm, dtype=np.int16)
streamoutput.write(pcm_to_write.tobytes())
else:
print("Decoded PCM is empty")
else:
streamoutput.close()
socket.close()
self.working = False
dpg.configure_item("showRDS", show=False)
dpg.configure_item("RDSinfo", show=False)
dpg.configure_item("connectbutton", show=True)
dpg.configure_item("serverstatus", default_value='disconnected', color=(255, 0, 0))
break
except Exception as e:
if str(e) == "An error occurred while decoding an Opus-encoded packet: corrupted stream":
dpg.configure_item("serverstatus", default_value="Unable to decode audio data", color=(255, 0, 0))
else:
print("connection lost", e)
self.working = False
streamoutput.close()
socket.close()
dpg.configure_item("showRDS", show=False)
dpg.configure_item("RDSinfo", show=False)
dpg.configure_item("connectbutton", show=True)
dpg.configure_item("serverstatus", default_value='disconnected', color=(255, 0, 0))
break
def window(self):
with dpg.window(label="IDRB", width=320):
dpg.add_text("", tag="RDSinfo", show=False)
dpg.add_input_text(label="server ip", tag="serverip", default_value="localhost")
dpg.add_input_int(label="port", tag="serverport", max_value=65535, default_value=6980)
dpg.add_combo(["TCP", "Websocket"], label="protocol", tag="serverprotocol", default_value="TCP")
dpg.add_button(label="connect", callback=self.connecttoserver, tag="connectbutton")
dpg.add_button(label="disconnect", callback=self.disconnectserver, tag="disconnectbutton", show=False)
dpg.add_spacer()
dpg.add_text("not connect", tag="serverstatus", color=(255, 0, 0))
dpg.add_spacer()
with dpg.collapsing_header(label="RDS", show=False, tag="showRDS"):
dpg.add_text("PS: ...", tag="RDSPS")
dpg.add_text("RT: ...", tag="RDSRT")
dpg.add_text("Listener: ...", tag="RDSListener")
dpg.add_text("Time Local: ...", tag="RDSCTlocal")
dpg.add_text("Time UTC: ...", tag="RDSCTUTC")
def init(self):
dpg.create_context()
dpg.create_viewport(title='[ThaiSDR] IDRB (Internet Digital Radio Broadcasting) V1 Beta', width=1280, height=720, small_icon="favicon.ico") # set viewport window
dpg.setup_dearpygui()
# -------------- add code here --------------
self.window()
# -------------------------------------------
dpg.show_viewport()
# Start a separate thread for a task
self.thread_stop_event = threading.Event()
while dpg.is_dearpygui_running():
self.render()
dpg.render_dearpygui_frame()
# Signal the thread to stop and wait for it to finish
self.thread_stop_event.set()
dpg.destroy_context()
def render(self):
# insert here any code you would like to run in the render loop
# you can manually stop by using stop_dearpygui() or self.exit()
pass
def exit(self):
dpg.destroy_context()
app = App()
app.init()