new xHE-Opus v2

new xHE-Opus v2 with parametric stereo
This commit is contained in:
dharm pimsen 2024-06-09 14:21:40 +07:00
parent f3fef7d5da
commit 45f27fabfd
8 changed files with 803 additions and 27 deletions

Binary file not shown.

42
gui.py
View File

@ -47,6 +47,14 @@ class App:
if int(dpg.get_value("opusframesize")) > 60:
dpg.configure_item("opusframesize", default_value="60")
def changeprofileopus(self, sender, data):
if data == "xHE-Opus v2":
dpg.configure_item("opusstereomode", show=False)
dpg.configure_item("opusbitrate", min_value=2.5, max_value=510, min_clamped=True, max_clamped=True, step_fast=1, default_value=64)
else:
dpg.configure_item("opusstereomode", show=True)
dpg.configure_item("opusbitrate", min_value=5, max_value=1020, min_clamped=True, max_clamped=True, step_fast=1, default_value=64)
def selectdeoutputpath(self, sender, data):
file_path = easygui.diropenbox()
dpg.set_value("deoutpathshow", f"output: {file_path}")
@ -80,6 +88,8 @@ class App:
dpg.configure_item("deplayconvert", show=False)
def convert(self):
signaltype = str(dpg.get_value("opussignaltype")).lower()
profile = str(dpg.get_value("opusprofile")).strip().lower()
stereomode = str(dpg.get_value("opusstereomode")).lower()
if stereomode == "stereo l/r":
stereomode = 1
@ -88,20 +98,40 @@ class App:
else:
stereomode = 2
if signaltype == "music":
signalauto = False
signalvoice = False
elif signaltype == "voice":
signalauto = False
signalvoice = True
else:
signalauto = True
signalvoice = False
try:
total = 0
current = 0
filename = os.path.splitext(os.path.basename(self.inputfilepath))[0]
dpg.set_value("convertstatus", "init encoder...")
encoder = libxheopus.DualOpusEncoder(dpg.get_value("opusapp"), 48000, dpg.get_value("opusversion"))
print(profile)
if profile == "xhe-opus v1":
encoder = libxheopus.DualOpusEncoder(dpg.get_value("opusapp"), 48000, dpg.get_value("opusversion"))
else:
encoder = libxheopus.PSOpusEncoder(dpg.get_value("opusapp"), 48000, dpg.get_value("opusversion"))
encoder.set_bitrate_mode(dpg.get_value("opusbitmode"))
encoder.set_bandwidth(dpg.get_value("opusbandwidth"))
encoder.set_bitrates(int(dpg.get_value("opusbitrate")*1000))
encoder.set_compression(dpg.get_value("opuscompression"))
encoder.set_packet_loss(dpg.get_value("opuspacketloss"))
encoder.set_stereo_mode(stereomode, dpg.get_value("opusenajoint"))
if profile != "xhe-opus v2":
encoder.set_stereo_mode(stereomode, dpg.get_value("opusenajoint"))
encoder.set_feature(dpg.get_value("opusenapred"), False, dpg.get_value("opusenadtx"))
encoder.enable_voice_mode(signalvoice, signalauto)
desired_frame_size = encoder.set_frame_size(int(dpg.get_value("opusframesize")))
dpg.set_value("convertstatus", "init writer...")
@ -168,7 +198,7 @@ class App:
self.delen = metadata["footer"]["length"]
def playaudiothread(self):
self.decoder = libxheopus.DualOpusDecoder()
self.decoder = libxheopus.xOpusDecoder()
for data in self.derender.decode(self.decoder, True, self.depausepos):
if self.deplay:
@ -229,7 +259,7 @@ class App:
outwav.setsampwidth(2) # 2 bytes (16 bits) per sample
outwav.setframerate(48000)
self.decoder = libxheopus.DualOpusDecoder()
self.decoder = libxheopus.xOpusDecoder()
for data in self.derender.decode(self.decoder, True, self.depausepos):
self.decurrentplay += 1
@ -260,18 +290,20 @@ class App:
dpg.add_text("output:", tag="outpathshow")
dpg.add_button(label="Select Input File", callback=self.selectinputfile)
dpg.add_button(label="Select Output Path", callback=self.selectoutputpath)
dpg.add_combo(["xHE-Opus v1", "xHE-Opus v2"], label="Profile", default_value="xHE-Opus v1", tag="opusprofile", callback=self.changeprofileopus)
dpg.add_combo(["hev2", "exper", "stable", "old"], label="Version", default_value="hev2", tag="opusversion", callback=self.changeversionopus)
dpg.add_combo(["120", "100", "80", "60", "40", "20", "10", "5"], label="Frame Size (ms)", tag="opusframesize", default_value="120")
dpg.add_combo(["voip", "audio", "restricted_lowdelay"], label="Application", default_value="restricted_lowdelay", tag="opusapp")
dpg.add_combo(["VBR", "CVBR", "CBR"], label="Bitrate Mode", default_value="CVBR", tag="opusbitmode")
dpg.add_combo(["auto", "fullband", "superwideband", "wideband", "mediumband", "narrowband"], label="Bandwidth", tag="opusbandwidth", default_value="fullband")
dpg.add_combo(["Stereo L/R", "Stereo Mid/Side"], label="Stereo Mode", tag="opusstereomode", default_value="Stereo L/R")
dpg.add_combo(["Auto", "Voice", "Music"], label="Signal Type", tag="opussignaltype", default_value="Auto")
dpg.add_input_float(label="Bitrates", min_value=5, max_value=1020, min_clamped=True, max_clamped=True, step_fast=1, default_value=64, tag="opusbitrate")
dpg.add_input_int(label="Compression Level", max_clamped=True, min_clamped=True, min_value=0, max_value=10, default_value=10, tag="opuscompression")
dpg.add_input_int(label="Packet Loss", max_clamped=True, min_clamped=True, min_value=0, max_value=100, default_value=0, tag="opuspacketloss")
dpg.add_checkbox(label="Prediction", tag="opusenapred")
dpg.add_checkbox(label="DTX", tag="opusenadtx")
dpg.add_checkbox(label="Joint", tag="opusenajoint")
dpg.add_checkbox(label="Auto Mono (Mid/Side Encoding)", tag="opusenajoint")
dpg.add_button(label="Convert", callback=self.startconvert)
with dpg.window(label="converting", show=False, tag="convertingwindow", modal=True, no_resize=True, no_move=True, no_title_bar=True, width=320):

BIN
icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

280
icon.svg Normal file
View File

@ -0,0 +1,280 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="48px"
viewBox="0 -960 960 960"
width="48px"
fill="#e8eaed"
version="1.1"
id="svg1"
sodipodi:docname="icon.svg"
xml:space="preserve"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
inkscape:export-filename="icon.png"
inkscape:export-xdpi="819.20001"
inkscape:export-ydpi="819.20001"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1"><linearGradient
id="linearGradient33"
inkscape:collect="always"><stop
style="stop-color:#434243;stop-opacity:1;"
offset="0"
id="stop33" /><stop
style="stop-color:#060506;stop-opacity:1;"
offset="1"
id="stop34" /></linearGradient><linearGradient
id="linearGradient31"
inkscape:collect="always"><stop
style="stop-color:#444243;stop-opacity:1;"
offset="0"
id="stop31" /><stop
style="stop-color:#050505;stop-opacity:1;"
offset="1"
id="stop32" /></linearGradient><linearGradient
id="linearGradient29"
inkscape:collect="always"><stop
style="stop-color:#454344;stop-opacity:1;"
offset="0"
id="stop29" /><stop
style="stop-color:#090909;stop-opacity:1;"
offset="1"
id="stop30" /></linearGradient><linearGradient
id="linearGradient27"
inkscape:collect="always"><stop
style="stop-color:#464445;stop-opacity:1;"
offset="0"
id="stop27" /><stop
style="stop-color:#0a0a0a;stop-opacity:1;"
offset="1"
id="stop28" /></linearGradient><linearGradient
id="linearGradient24"
inkscape:collect="always"><stop
style="stop-color:#454344;stop-opacity:1;"
offset="0"
id="stop24" /><stop
style="stop-color:#111111;stop-opacity:1;"
offset="1"
id="stop25" /></linearGradient><linearGradient
id="linearGradient21"
inkscape:collect="always"><stop
style="stop-color:#474546;stop-opacity:1;"
offset="0"
id="stop21" /><stop
style="stop-color:#080808;stop-opacity:1;"
offset="1"
id="stop22" /></linearGradient><linearGradient
id="_Linear2"
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,48.2549,-48.2549,0,231.618,118.208)"><stop
offset="0%"
style="stop-color:rgb(73,71,72);stop-opacity:1"
id="stop8" /><stop
offset="0%"
style="stop-color:rgb(73,71,72);stop-opacity:1"
id="stop9" /><stop
offset="100%"
style="stop-color:black;stop-opacity:1"
id="stop10" /></linearGradient><linearGradient
id="_Linear3"
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,49.7919,-49.7919,0,45.9897,117.439)"><stop
offset="0%"
style="stop-color:rgb(73,71,72);stop-opacity:1"
id="stop11" /><stop
offset="0%"
style="stop-color:rgb(73,71,72);stop-opacity:1"
id="stop12" /><stop
offset="100%"
style="stop-color:black;stop-opacity:1"
id="stop13" /></linearGradient><linearGradient
id="_Linear4"
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,49.5634,-49.5634,0,313.285,117.719)"><stop
offset="0%"
style="stop-color:rgb(73,71,72);stop-opacity:1"
id="stop14" /><stop
offset="0%"
style="stop-color:rgb(73,71,72);stop-opacity:1"
id="stop15" /><stop
offset="100%"
style="stop-color:black;stop-opacity:1"
id="stop16" /></linearGradient><linearGradient
id="_Linear5"
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,88.9072,-88.9072,0,131.199,117.579)"><stop
offset="0%"
style="stop-color:rgb(73,71,72);stop-opacity:1"
id="stop17" /><stop
offset="0%"
style="stop-color:rgb(73,71,72);stop-opacity:1"
id="stop18" /><stop
offset="100%"
style="stop-color:black;stop-opacity:1"
id="stop19" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient21"
id="linearGradient20"
x1="85.493027"
y1="107.23618"
x2="47.430016"
y2="159.73979"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient21"
id="linearGradient22"
x1="152.74756"
y1="90.268875"
x2="104.83708"
y2="161.27939"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient24"
id="linearGradient25"
x1="208.53099"
y1="91.856384"
x2="170.58093"
y2="158.41898"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient27"
id="linearGradient28"
x1="247.16783"
y1="125.09132"
x2="233.85489"
y2="202.95941"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient29"
id="linearGradient30"
x1="283.84119"
y1="125.34251"
x2="271.03064"
y2="184.37154"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient31"
id="linearGradient32"
x1="320.77448"
y1="126.64043"
x2="309.05182"
y2="161.1868"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient33"
id="linearGradient34"
x1="357.6275"
y1="126.97522"
x2="346.19846"
y2="141.29291"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="16"
inkscape:cx="18.09375"
inkscape:cy="23.25"
inkscape:window-width="1672"
inkscape:window-height="950"
inkscape:window-x="50"
inkscape:window-y="33"
inkscape:window-maximized="0"
inkscape:current-layer="svg1" /><path
d="m 237.69,-100 c -15.68667,0 -29.22333,-5.69333 -40.61,-17.08 C 185.69333,-128.46667 180,-142.00333 180,-157.69 v -644.62 c 0,-15.68667 5.69333,-29.22333 17.08,-40.61 11.38667,-11.38667 24.92333,-17.08 40.61,-17.08 H 585.23 L 780,-665.23 v 507.54 c 0,15.68667 -5.69333,29.22333 -17.08,40.61 -11.38667,11.38667 -24.92333,17.08 -40.61,17.08 z M 562.54,-644.77 V -814.61 H 237.69 c -3.07333,0 -5.89333,1.28 -8.46,3.84 -2.56,2.56667 -3.84,5.38667 -3.84,8.46 v 644.62 c 0,3.07333 1.28,5.89333 3.84,8.46 2.56667,2.56 5.38667,3.84 8.46,3.84 h 484.62 c 3.07333,0 5.89333,-1.28 8.46,-3.84 2.56,-2.56667 3.84,-5.38667 3.84,-8.46 v -487.08 c -65.24678,5.26008 -120.07899,3.57711 -172.07,0 z M 225.39,-814.61 v 169.84 -169.84 669.22 z"
id="path1"
sodipodi:nodetypes="ssssssccssssccscsscsscsccccccc" /><path
style="fill:#454344;stroke-width:1.18081"
d="m 327.21902,-454.75826 c -8.67942,-1.386 -20.82226,-5.87842 -29.14992,-10.7842 -9.41336,-5.54536 -23.71188,-20.07002 -29.72482,-30.19494 -16.51428,-27.8076 -16.95202,-64.04278 -1.1004,-91.08834 5.9727,-10.19044 23.5213,-27.13586 33.44182,-32.29232 27.39902,-14.24146 61.31846,-12.82076 86.19926,3.61042 4.54612,3.00224 8.6642,5.63622 9.15128,5.8533 0.487,0.217 0.8856,-40.56474 0.8856,-90.62624 v -91.02092 h 73.21032 73.21034 v 27.74908 27.74908 h -56.05202 -56.052 l -0.3548,104.79704 c -0.3982,117.59698 0.184,109.62208 -9.41992,129.0037 -4.05164,8.17652 -7.06064,12.12558 -16.2337,21.3055 -12.63448,12.64392 -23.479,19.17334 -39.04158,23.5067 -10.07486,2.8053 -29.17016,3.99708 -38.96942,2.43214 z"
id="path2" /><path
style="fill:#454344;stroke-width:0.0590406"
d="M 11.306273,42.917109 C 10.232134,42.708883 9.2423232,41.667944 9.0657808,40.560886 8.980653,40.027068 8.980653,7.9729322 9.0657808,7.4391144 9.1538473,6.886869 9.4020781,6.4007019 9.8290076,5.9443129 10.24328,5.5014543 10.812947,5.1785993 11.358612,5.0774193 c 0.202637,-0.037574 3.501528,-0.058969 9.092384,-0.058969 h 8.774363 l 4.885476,4.8857414 4.885475,4.8857413 -4.46e-4,12.693595 c -2.98e-4,8.472493 -0.0208,12.82121 -0.06164,13.077358 -0.08807,0.552245 -0.336297,1.038412 -0.763227,1.494801 -0.414272,0.442859 -0.983939,0.765713 -1.529604,0.866894 -0.399658,0.07411 -24.951963,0.0688 -25.335115,-0.0055 z m 25.224161,-2.351378 0.192813,-0.172276 0.01517,-12.314809 0.01517,-12.314808 H 32.443218 28.132841 V 11.512915 7.2619926 H 19.897611 11.66238 L 11.469566,7.4342714 11.276753,7.6065501 V 24 40.39345 l 0.192813,0.172279 0.192814,0.172278 H 24 36.337621 Z"
id="path3"
transform="matrix(20,0,0,20,0,-960)" /><g
id="layer2"
inkscape:label="Layer 2"
transform="matrix(1.421733,0,0,1.421733,175.04404,-471.21546)"><path
style="fill:url(#linearGradient20);fill-opacity:1;stroke-width:0.264584"
d="m 42.772863,109.31583 15.215648,22.93703 -16.709705,25.41672 13.677827,0.16149 11.189814,-17.86307 10.6397,17.92037 15.625433,-0.0845 -16.563129,-25.44156 14.446739,-22.98913 -13.509043,-0.224 -9.314427,15.65949 -9.595734,-15.65949 z"
id="path1-4"
inkscape:label="x"
sodipodi:nodetypes="ccccccccccccc" /><path
style="fill:url(#linearGradient22);fill-opacity:1;stroke-width:0.264584"
d="m 100.99431,91.977063 v 65.802947 l 13.22154,0.0838 0.0823,-28.79451 26.28765,-0.24383 0.32002,28.79451 13.1484,0.0762 0.15544,-65.802942 H 140.5858 l 0.24687,26.295272 -26.36994,0.0762 0.10264,-26.311838 z"
id="path2-7"
sodipodi:nodetypes="ccccccccccccc"
inkscape:label="H" /><path
style="fill:url(#linearGradient25);fill-opacity:1;stroke-width:0.264584"
d="m 169.86479,92.036653 0.0249,65.595117 39.28245,-0.0503 V 147.4961 h -26.17493 v -18.69955 h 23.78516 v -10.15926 h -23.79058 v -16.51593 h 25.86902 v -9.944951 z"
id="path3-9"
inkscape:label="E" /><path
style="display:inline;fill:url(#linearGradient28);fill-opacity:1;stroke-width:0.264584"
d="m 235.53041,127.67943 h 10.09779 v 64.19197 l -10.09598,5.82892 z"
id="path4"
sodipodi:nodetypes="ccccc" /><path
style="fill:url(#linearGradient30);fill-opacity:1;stroke-width:0.264584"
d="m 272.51786,127.66402 10.18464,0.001 v 45.10013 l -10.18142,5.87824 z"
id="path5"
sodipodi:nodetypes="ccccc" /><path
style="fill:url(#linearGradient32);fill-opacity:1;stroke-width:0.264584"
d="m 309.59307,127.67404 h 10.41031 v 25.99613 l -10.4105,6.0105 z"
id="path6"
sodipodi:nodetypes="ccccc" /><path
style="display:inline;fill:url(#linearGradient34);fill-opacity:1;stroke-width:0.264584"
d="m 346.85062,127.61804 h 10.27822 v 6.80536 l -10.27696,5.93339 z"
id="path7"
sodipodi:nodetypes="ccccc" /><g
id="path3716"
transform="matrix(0.49366852,0,0,0.49366852,212.40542,15.350723)"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421">
<path
d="m 271.531,141.499 c -2.511,7.718 -8.23,13.807 -17.156,18.27 -8.926,4.463 -20.223,6.694 -33.891,6.694 -13.484,0 -23.292,-2.208 -29.43,-6.626 -6.136,-4.415 -7.904,-10.528 -5.299,-18.338 l 7.53,-23.291 h 25.663 l -7.252,23.151 c -0.931,2.883 -1.232,5.278 -0.906,7.183 0.326,1.907 1.046,3.417 2.162,4.533 1.394,1.113 2.95,1.88 4.672,2.299 1.72,0.419 3.788,0.629 6.207,0.629 2.417,0 4.742,-0.255 6.974,-0.769 2.231,-0.51 4.275,-1.323 6.138,-2.438 1.858,-1.116 3.508,-2.602 4.951,-4.463 1.44,-1.859 2.626,-4.186 3.557,-6.974 l 7.532,-23.151 h 25.663 z"
style="fill:url(#_Linear2);fill-rule:nonzero"
id="path2-4" />
</g><g
id="path3723"
transform="matrix(0.49366852,0,0,0.49366852,212.40542,15.350723)"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421">
<path
d="m 88.875,142.404 c 2.51,-7.717 0.743,-13.808 -5.301,-18.271 -6.044,-4.463 -15.899,-6.694 -29.567,-6.694 -13.483,0 -24.686,2.21 -33.611,6.625 -8.928,4.417 -14.693,10.53 -17.295,18.34 -2.51,7.72 -0.722,13.786 5.37,18.201 6.089,4.418 15.922,6.626 29.498,6.626 13.575,0 24.826,-2.208 33.753,-6.626 8.924,-4.415 14.642,-10.481 17.153,-18.201 z m -26.082,0.14 c -0.931,2.978 -2.069,5.3 -3.417,6.974 -1.349,1.675 -3.046,3.116 -5.09,4.323 -1.768,1.116 -3.765,1.883 -5.997,2.302 -2.232,0.419 -4.463,0.627 -6.696,0.627 -2.697,0 -4.999,-0.23 -6.903,-0.696 -1.907,-0.465 -3.417,-1.256 -4.533,-2.371 -1.21,-1.116 -1.906,-2.626 -2.092,-4.533 -0.188,-1.904 0.14,-4.114 0.977,-6.625 0.929,-2.88 2.161,-5.275 3.696,-7.183 1.534,-1.904 3.229,-3.417 5.09,-4.533 2.138,-1.115 4.206,-1.882 6.207,-2.301 1.999,-0.419 4.207,-0.627 6.625,-0.627 2.416,0 4.603,0.257 6.555,0.767 1.953,0.512 3.486,1.325 4.603,2.44 1.115,1.116 1.789,2.605 2.021,4.463 0.231,1.86 -0.117,4.185 -1.046,6.973 z"
style="fill:url(#_Linear3);fill-rule:nonzero"
id="path3-7" />
</g><g
id="path3730"
transform="matrix(0.49366852,0,0,0.49366852,212.40542,15.350723)"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421">
<path
d="m 312.14,128.807 c 2.928,-0.698 9.041,-1.046 18.339,-1.046 4.833,0 9.506,0.487 14.018,1.465 4.508,0.976 9.042,2.209 13.598,3.696 L 362,121.066 c -2.698,-0.744 -6.625,-1.487 -11.787,-2.231 -5.159,-0.744 -10.669,-1.116 -16.526,-1.116 -17.574,0 -30.405,1.513 -38.493,4.533 -8.089,3.022 -12.879,6.812 -14.366,11.366 -1.115,3.44 -0.348,6.346 2.302,8.717 2.65,2.371 7.322,4.115 14.016,5.229 2.511,0.467 6.624,0.838 12.343,1.117 5.718,0.279 9.46,0.557 11.228,0.836 3.717,0.373 6.205,0.837 7.461,1.396 1.254,0.558 1.695,1.394 1.325,2.511 -0.467,1.303 -1.976,2.279 -4.533,2.928 -2.559,0.652 -6.3,0.977 -11.227,0.977 -0.77,0 -1.513,-0.003 -2.241,-0.007 -1.846,-0.101 -3.858,-0.272 -5.791,-0.476 -2.06,-0.22 -4.118,-0.485 -6.162,-0.795 -4.089,-0.62 -8.132,-1.419 -12.058,-2.439 -3.921,-1.022 -7.734,-2.267 -11.26,-3.813 -0.474,-0.208 -0.932,-0.433 -1.394,-0.654 -2.476,3.979 -5.905,7.451 -10.27,10.396 2.259,1.085 4.539,1.976 6.807,2.742 4.52,1.506 9.034,2.52 13.525,3.266 4.494,0.741 8.969,1.203 13.431,1.472 2.231,0.133 4.459,0.215 6.691,0.248 1.966,0.026 3.882,0.02 5.902,-0.045 12.216,-0.072 22.318,-1.53 30.294,-4.386 8.18,-2.929 13.062,-6.81 14.644,-11.645 1.116,-3.349 0.441,-6.138 -2.021,-8.369 -2.466,-2.231 -6.813,-3.857 -13.041,-4.882 -2.883,-0.371 -5.768,-0.719 -8.647,-1.046 -2.884,-0.324 -5.533,-0.628 -7.951,-0.906 -9.577,-0.65 -14.924,-1.255 -16.039,-1.813 -1.116,-0.558 -1.488,-1.394 -1.116,-2.511 0.467,-1.209 2.164,-2.163 5.094,-2.859 z"
style="fill:url(#_Linear4);fill-rule:nonzero"
id="path4-6" />
</g><g
id="path3737"
transform="matrix(0.49366852,0,0,0.49366852,212.40542,15.350723)"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421">
<path
d="m 174.838,124.204 c -6.091,-4.415 -15.924,-6.625 -29.499,-6.625 -13.577,0 -24.826,2.21 -33.751,6.625 -8.926,4.417 -14.4,10.415 -16.911,18.131 l -0.105,0.349 -12.692,39.19 c -5.26,17.631 17.526,24.612 17.526,24.612 l 7.58,-24.612 0.656,-2.106 c 0.592,-1.982 1.192,-3.964 1.781,-5.948 l 1.686,-5.531 c 0.603,-1.832 1.207,-3.662 1.85,-5.481 l 3.979,-10.875 1.993,-5.436 1.429,-3.718 c 0.051,-0.172 0.099,-0.339 0.155,-0.514 0.836,-2.509 1.953,-4.718 3.347,-6.624 1.396,-1.904 3.069,-3.417 5.021,-4.533 1.859,-1.115 3.882,-1.904 6.067,-2.371 2.183,-0.464 4.625,-0.696 7.322,-0.696 2.231,0 4.325,0.208 6.277,0.627 1.952,0.419 3.438,1.186 4.463,2.301 1.301,1.209 2.068,2.65 2.3,4.323 0.231,1.675 -0.117,3.999 -1.046,6.974 -0.931,2.79 -2.116,5.116 -3.557,6.974 -1.442,1.862 -3.091,3.348 -4.951,4.464 -1.861,1.115 -3.905,1.931 -6.136,2.44 -2.231,0.512 -4.558,0.767 -6.973,0.767 -2.419,0 -4.487,-0.208 -6.207,-0.627 -1.721,-0.419 -3.326,-1.186 -4.812,-2.302 -0.112,-0.112 -0.201,-0.247 -0.305,-0.366 l -3.674,10.658 c -0.206,0.613 -0.403,1.228 -0.601,1.842 3.479,0.708 7.507,1.13 12.111,1.256 13.668,0 24.965,-2.231 33.893,-6.694 8.926,-4.464 14.643,-10.552 17.154,-18.271 2.511,-7.719 0.718,-13.786 -5.37,-18.203 z"
style="fill:url(#_Linear5);fill-rule:nonzero"
id="path5-0" />
</g></g></svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -4,6 +4,7 @@ import struct
import pyogg
import os
import numpy as np
from scipy.signal import butter, filtfilt
def float32_to_int16(data_float32):
data_int16 = (data_float32 * 32767).astype(np.int16)
@ -51,7 +52,8 @@ class DualOpusEncoder:
self.version = version
self.samplerate = samplerate
self.stereomode = 1 #0 = mono, 1 = stereo LR, 2 = stereo Mid/Side
self.enablejoint = False
self.audiomono = False
os.environ["pyogg_win_libopus_version"] = version
importlib.reload(pyogg.opus)
@ -97,20 +99,25 @@ class DualOpusEncoder:
"""
narrowband:
Narrowband typically refers to a limited range of frequencies suitable for voice communication.
mediumband (unsupported in libopus 1.3+):
Mediumband extends the frequency range compared to narrowband, providing better audio quality.
wideband:
Wideband offers an even broader frequency range, resulting in higher audio fidelity compared to narrowband and mediumband.
superwideband:
Superwideband extends the frequency range beyond wideband, further enhancing audio quality.
fullband (default):
Fullband provides the widest frequency range among the listed options, offering the highest audio quality.
auto: opus is working auto not force
"""
self.Lencoder.set_bandwidth(bandwidth)
self.Rencoder.set_bandwidth(bandwidth)
def set_stereo_mode(self, mode=1, enablejoint=False):
def set_stereo_mode(self, mode=1, audiomono=False):
"""
0 = mono
1 = stereo LR
@ -120,7 +127,7 @@ class DualOpusEncoder:
mode = 1
self.stereomode = mode
self.enablejoint = enablejoint
self.audiomono = audiomono
def set_frame_size(self, size=60):
""" Set the desired frame duration (in milliseconds).
@ -162,6 +169,10 @@ class DualOpusEncoder:
self.Rencoder.CTL(pyogg.opus.OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST, int(phaseinvert))
self.Rencoder.CTL(pyogg.opus.OPUS_SET_DTX_REQUEST, int(DTX))
def enable_voice_mode(self, enable=True, auto=False):
self.Lencoder.enable_voice_enhance(enable, auto)
self.Rencoder.enable_voice_enhance(enable, auto)
def encode(self, pcmbytes, directpcm=False):
"""input: pcm bytes accept float32/int16 only
x74 is mono
@ -188,9 +199,9 @@ class DualOpusEncoder:
intmono = float32_to_int16(mono)
Mencoded_packet = self.Lencoder.buffered_encode(memoryview(bytearray(intmono)), flush=True)[0][0].tobytes()
midencoded_packet = self.Lencoder.buffered_encode(memoryview(bytearray(intmono)), flush=True)[0][0].tobytes()
dual_encoded_packet = (Mencoded_packet + b'\\x64\\x74')
dual_encoded_packet = (midencoded_packet + b'\\x64\\x74')
elif self.stereomode == 2:
# stereo mid/side (Joint encoding)
# convert to float32
@ -214,7 +225,7 @@ class DualOpusEncoder:
except:
loudnessside = 0
if (loudnessside) <= -50 and self.enablejoint:
if (loudnessside) <= -50 and self.audiomono:
sideencoded_packet = b"\\xnl"
else:
sideencoded_packet = self.Rencoder.buffered_encode(memoryview(bytearray(intside)), flush=True)[0][0].tobytes()
@ -232,7 +243,194 @@ class DualOpusEncoder:
return dual_encoded_packet
class DualOpusDecoder:
class PSOpusEncoder:
def __init__(self, app="audio", samplerate=48000, version="stable"):
"""
This version is xHE-Opus v2 (Parametric Stereo)
----------------------------- version--------------------------
hev2: libopus 1.5.1 (fre:ac)
exper: libopus 1.5.1
stable: libopus 1.4
old: libopus 1.3.1
custom: custom opus path you can use "pyogg_win_libopus_custom_path" env to change opus version (windows only)
------------------------- App----------------------------------
Set the encoding mode.
This must be one of 'voip', 'audio', or 'restricted_lowdelay'.
'voip': Gives best quality at a given bitrate for voice
signals. It enhances the input signal by high-pass
filtering and emphasizing formants and
harmonics. Optionally it includes in-band forward error
correction to protect against packet loss. Use this mode
for typical VoIP applications. Because of the enhancement,
even at high bitrates the output may sound different from
the input.
'audio': Gives best quality at a given bitrate for most
non-voice signals like music. Use this mode for music and
mixed (music/voice) content, broadcast, and applications
requiring less than 15 ms of coding delay.
'restricted_lowdelay': configures low-delay mode that
disables the speech-optimized mode in exchange for
slightly reduced delay. This mode can only be set on an
newly initialized encoder because it changes the codec
delay.
"""
self.version = version
self.samplerate = samplerate
os.environ["pyogg_win_libopus_version"] = version
importlib.reload(pyogg.opus)
self.encoder = pyogg.OpusBufferedEncoder()
self.encoder.set_application(app)
self.encoder.set_sampling_frequency(samplerate)
self.encoder.set_channels(1)
self.set_frame_size()
self.set_compression()
self.set_feature()
self.set_bitrate_mode()
self.set_bitrates()
self.set_bandwidth()
self.set_packet_loss()
def set_compression(self, level=10):
"""complex 0-10 low-hires"""
self.encoder.set_compresion_complex(level)
def set_bitrates(self, bitrates=64000):
"""input birate unit: bps"""
if bitrates <= 2500:
bitrates = 2500
self.encoder.set_bitrates(bitrates)
def set_bandwidth(self, bandwidth="fullband"):
"""
narrowband:
Narrowband typically refers to a limited range of frequencies suitable for voice communication.
mediumband (unsupported in libopus 1.3+):
Mediumband extends the frequency range compared to narrowband, providing better audio quality.
wideband:
Wideband offers an even broader frequency range, resulting in higher audio fidelity compared to narrowband and mediumband.
superwideband:
Superwideband extends the frequency range beyond wideband, further enhancing audio quality.
fullband (default):
Fullband provides the widest frequency range among the listed options, offering the highest audio quality.
auto: opus is working auto not force
"""
self.encoder.set_bandwidth(bandwidth)
def set_frame_size(self, size=60):
""" Set the desired frame duration (in milliseconds).
Valid options are 2.5, 5, 10, 20, 40, or 60ms.
Exclusive for HE opus v2 (freac opus) 80, 100 or 120ms.
@return chunk size
"""
if self.version != "hev2" and size > 60:
raise ValueError("non hev2 can't use framesize > 60")
self.encoder.set_frame_size(size)
return int((size / 1000) * self.samplerate)
def set_packet_loss(self, loss=0):
"""input: % percent"""
if loss > 100:
raise ValueError("percent must <=100")
self.encoder.set_packets_loss(loss)
def set_bitrate_mode(self, mode="CVBR"):
"""VBR, CVBR, CBR
VBR in 1.5.x replace by CVBR
"""
self.encoder.set_bitrate_mode(mode)
def set_feature(self, prediction=False, phaseinvert=False, DTX=False):
self.encoder.CTL(pyogg.opus.OPUS_SET_PREDICTION_DISABLED_REQUEST, int(prediction))
self.encoder.CTL(pyogg.opus.OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST, int(phaseinvert))
self.encoder.CTL(pyogg.opus.OPUS_SET_DTX_REQUEST, int(DTX))
def enable_voice_mode(self, enable=True, auto=False):
self.encoder.enable_voice_enhance(enable, auto)
def __parameterization(self, stereo_signal):
# Convert int16 to float32 for processing
stereo_signal = stereo_signal.astype(np.float32) / 32768.0
# Reshape stereo_signal into a 2D array with two channels
stereo_signal = stereo_signal.reshape((-1, 2))
# Calculate the magnitude spectrogram for each channel
mag_left = np.abs(np.fft.fft(stereo_signal[:, 0]))
mag_right = np.abs(np.fft.fft(stereo_signal[:, 1]))
# Calculate the phase difference between the left and right channels
phase_diff = np.angle(stereo_signal[:, 0]) - np.angle(stereo_signal[:, 1])
# Compute other spatial features
# Calculate stereo width
stereo_width = np.mean(np.correlate(mag_left, mag_right, mode='full'))
# Calculate phase coherence
phase_coherence = np.mean(np.cos(phase_diff))
# Calculate stereo panning
stereo_panning_left = np.mean(mag_left / (mag_left + mag_right))
stereo_panning_right = np.mean(mag_right / (mag_left + mag_right))
pan = stereo_panning_right - stereo_panning_left
# Return the derived parameters
return (int(stereo_width), phase_coherence, pan)
def encode(self, pcmbytes, directpcm=False):
"""input: pcm bytes accept float32/int16 only
x74 is mono
x75 is stereo LR
x76 is stereo mid/side
xnl is no side audio
"""
if directpcm:
if pcmbytes.dtype == np.float32:
pcm = (pcmbytes * 32767).astype(np.int16)
elif pcmbytes.dtype == np.int16:
pcm = pcmbytes.astype(np.int16)
else:
raise TypeError("accept only int16/float32")
else:
pcm = np.frombuffer(pcmbytes, dtype=np.int16)
pcmreshaped = pcm.reshape(-1, 2)
mono_data = np.mean(pcmreshaped * 0.5, axis=1, dtype=np.int16)
stereodata = self.__parameterization(pcmreshaped)
packedstereodata = struct.pack('iff', *stereodata)
encoded_packet = self.encoder.buffered_encode(memoryview(bytearray(mono_data)), flush=True)[0][0].tobytes()
encoded_packet = (encoded_packet + b'\\x21\\x75' + packedstereodata)
return encoded_packet
class xOpusDecoder:
def __init__(self, sample_rate=48000):
self.Ldecoder = pyogg.OpusDecoder()
self.Rdecoder = pyogg.OpusDecoder()
@ -243,22 +441,189 @@ class DualOpusDecoder:
self.Ldecoder.set_sampling_frequency(sample_rate)
self.Rdecoder.set_sampling_frequency(sample_rate)
self.__prev_pan = 0.0
def __smooth(self, value, prev_value, alpha=0.1):
return alpha * value + (1 - alpha) * prev_value
def __expand_and_pan(self, input_signal, pan_value, expansion_factor, gain):
"""
Apply stereo expansion and panning to an input audio signal.
Parameters:
- input_signal: Input audio signal (numpy array of int16).
- expansion_factor: Factor to expand the stereo width (0 to 1).
- pan_value: Pan value (-1 to 1, where -1 is full left, 1 is full right).
- gain: Gain factor to adjust the volume.
Returns:
- output_signal: Processed audio signal (stereo, numpy array of int16).
"""
# Convert int16 to float32 for processing
input_signal_float = input_signal.astype(np.float32) / 32768.0
# Separate the channels
left_channel = input_signal_float[:, 0]
right_channel = input_signal_float[:, 1]
# Apply panning
pan_left = (1 - pan_value) / 2
pan_right = (1 + pan_value) / 2
left_channel *= pan_left
right_channel *= pan_right
# Apply stereo expansion
center = (left_channel + right_channel) / 2
left_channel = center + (left_channel - center) * expansion_factor
right_channel = center + (right_channel - center) * expansion_factor
# Apply gain
left_channel *= gain
right_channel *= gain
# Ensure no clipping by normalizing if necessary
max_val = max(np.max(np.abs(left_channel)), np.max(np.abs(right_channel)))
if max_val > 1.0:
left_channel /= max_val
right_channel /= max_val
# Merge the channels
output_signal = np.stack((left_channel, right_channel), axis=-1)
return (output_signal * 32767).astype(np.int16)
def __mix_stereo_signals(self, signal1, signal2, volume1=1.0, volume2=1.0):
# Ensure both signals have the same length
length = max(len(signal1), len(signal2))
signal1 = np.pad(signal1, ((0, length - len(signal1)), (0, 0)), mode='constant')
signal2 = np.pad(signal2, ((0, length - len(signal2)), (0, 0)), mode='constant')
# Convert signals to float
signal1 = signal1.astype(np.float32)
signal2 = signal2.astype(np.float32)
# Adjust volume
signal1 *= volume1
signal2 *= volume2
# Mix the signals
mixed_signal = signal1 + signal2
# Normalize the mixed signal to prevent clipping
max_amplitude = np.max(np.abs(mixed_signal))
if max_amplitude > 32767:
mixed_signal = (mixed_signal / max_amplitude) * 32767
return mixed_signal.astype(np.int16)
def __apply_smoothing_window(self, audio_data, window_size):
"""
Apply a smoothing window to the beginning and end of the audio data.
Parameters:
- audio_data: 2D numpy array with shape (num_samples, 2)
- window_size: Size of the smoothing window in samples
Returns:
- smoothed_audio_data: 2D numpy array with the smoothing window applied
"""
window = np.hanning(window_size * 2)
fade_in = window[:window_size]
fade_out = window[-window_size:]
audio_data[:window_size, :] *= fade_in[:, np.newaxis]
audio_data[-window_size:, :] *= fade_out[:, np.newaxis]
return audio_data
def __stereo_widening_effect(self, data, delay_samples=10, gain=0.8, window_size=100):
audio_data = data.reshape(-1, 2)
# Convert int16 to float32 for processing
audio_data = audio_data.astype(np.float32)
# Apply delay to the right channel
right_channel = np.roll(audio_data[:, 1], delay_samples)
# Apply gain to both channels
audio_data[:, 0] *= gain
right_channel *= gain
# Combine channels back into stereo
widened_audio_data = np.stack((audio_data[:, 0], right_channel), axis=1)
# Apply smoothing window to reduce clicks
widened_audio_data = self.__apply_smoothing_window(widened_audio_data, window_size)
# Clip to avoid overflow
widened_audio_data = np.clip(widened_audio_data, -32768, 32767)
# Convert float32 back to int16
widened_audio_data = widened_audio_data.astype(np.int16)
return widened_audio_data
def __apply_phase_coherence_to_stereo(self, signal, phase_coherence):
# Convert phase coherence to phase shift in radians
phase_shift = np.arccos(phase_coherence)
# Apply phase shift to both channels
return self.__apply_phase_shift(signal, phase_shift)
# Function to apply phase shift to one channel
def __apply_phase_shift(self, signal, phase_shift):
# Convert to complex
signal_complex = signal.astype(np.complex64)
# Apply phase shift
shifted_signal = signal_complex * np.exp(1j * phase_shift)
return shifted_signal.astype(np.int16)
def __butter_lowpass_filter_stereo(self, data, cutoff, fs, order=5):
nyq = 0.5 * fs
normal_cutoff = cutoff / nyq
b, a = butter(order, normal_cutoff, btype='low', analog=False)
filtered_data = np.apply_along_axis(lambda x: filtfilt(b, a, x), axis=0, arr=data)
return filtered_data.astype(np.int16)
def __synthstereo(self, mono_signal, stereodata):
pan = stereodata[2]
# Smooth the pan value
pan = self.__smooth(pan, self.__prev_pan, alpha=0.25)
self.__prev_pan = pan
stereo_exp = stereodata[0] / 10000
try:
delayed = self.__stereo_widening_effect(mono_signal, int(stereo_exp), 1, int(stereo_exp) * 2)
except:
delayed = mono_signal
l1 = self.__expand_and_pan(mono_signal, pan, 1, 2)
stereo_signal_shifted = self.__apply_phase_coherence_to_stereo(delayed, stereodata[1])
return self.__mix_stereo_signals(l1, stereo_signal_shifted, volume1=1, volume2=0.5).astype(np.int16)
def decode(self, dualopusbytes: bytes, outputformat=np.int16):
# mode check
if b"\\x64\\x74" in dualopusbytes:
mode = 0
dualopusbytespilted = dualopusbytes.split(b'\\x64\\x74')
xopusbytespilted = dualopusbytes.split(b'\\x64\\x74')
elif b"\\x64\\x76" in dualopusbytes:
mode = 2
dualopusbytespilted = dualopusbytes.split(b'\\x64\\x76')
xopusbytespilted = dualopusbytes.split(b'\\x64\\x76')
elif b"\\x64\\x75" in dualopusbytes:
mode = 1
dualopusbytespilted = dualopusbytes.split(b'\\x64\\x75')
xopusbytespilted = dualopusbytes.split(b'\\x64\\x75')
elif b"\\x21\\x75" in dualopusbytes:
mode = 3 # v2
xopusbytespilted = dualopusbytes.split(b'\\x21\\x75')
else:
raise TypeError("this is not dual opus")
raise TypeError("this is not xopus bytes")
if mode == 0: # mono
Mencoded_packet = dualopusbytespilted[0]
Mencoded_packet = xopusbytespilted[0]
decoded_left_channel_pcm = self.Ldecoder.decode(memoryview(bytearray(Mencoded_packet)))
Mpcm = np.frombuffer(decoded_left_channel_pcm, dtype=np.int16)
@ -266,8 +631,8 @@ class DualOpusDecoder:
elif mode == 2:
# stereo mid/side (Joint encoding)
Mencoded_packet = dualopusbytespilted[0]
Sencoded_packet = dualopusbytespilted[1]
Mencoded_packet = xopusbytespilted[0]
Sencoded_packet = xopusbytespilted[1]
decoded_mid_channel_pcm = self.Ldecoder.decode(memoryview(bytearray(Mencoded_packet)))
Mpcm = np.frombuffer(decoded_mid_channel_pcm, dtype=np.int16)
@ -279,18 +644,34 @@ class DualOpusDecoder:
Mpcm = int16_to_float32(Mpcm)
Spcm = int16_to_float32(Spcm)
L = (Mpcm + Spcm) / 1.5
R = (Mpcm - Spcm) / 1.5
L = Mpcm + Spcm
R = Mpcm - Spcm
stereo_signal = np.column_stack((L, R))
max_amplitude = np.max(np.abs(stereo_signal))
if max_amplitude > 1.0:
stereo_signal /= max_amplitude
stereo_signal = float32_to_int16(stereo_signal)
else:
stereo_signal = np.column_stack((Mpcm, Mpcm))
elif mode == 3:
Mencoded_packet = xopusbytespilted[0]
stereodatapacked = xopusbytespilted[1]
stereodata = struct.unpack('iff', stereodatapacked)
mono_channel_pcm = self.Ldecoder.decode(memoryview(bytearray(Mencoded_packet)))
Mpcm = np.frombuffer(mono_channel_pcm, dtype=np.int16)
stereo_audio = np.stack((Mpcm, Mpcm)).T.reshape(-1, 2)
stereo_signal = self.__synthstereo(stereo_audio, stereodata)
else:
# stereo LR
Lencoded_packet = dualopusbytespilted[0]
Rencoded_packet = dualopusbytespilted[1]
Lencoded_packet = xopusbytespilted[0]
Rencoded_packet = xopusbytespilted[1]
decoded_left_channel_pcm = self.Ldecoder.decode(memoryview(bytearray(Lencoded_packet)))
decoded_right_channel_pcm = self.Rdecoder.decode(memoryview(bytearray(Rencoded_packet)))
@ -368,10 +749,13 @@ class FooterContainer:
return loudness_avg, length
class XopusWriter:
def __init__(self, file, encoder: DualOpusEncoder, metadata={}):
def __init__(self, file, encoder: DualOpusEncoder, metadata=None):
self.file = file
self.encoder = encoder
if metadata is None:
metadata = {}
systemmetadata = {
"format": "Xopus",
"audio": {
@ -445,7 +829,8 @@ class XopusReader:
else:
try:
yield decoder.decode(data)
except:
except Exception as e:
#print(e)
yield b""
else:
decodedlist = []

View File

@ -1,5 +1,5 @@
import pyaudio
from libxheopus import DualOpusDecoder, XopusReader
from libxheopus import xOpusDecoder, XopusReader
import argparse
from tqdm import tqdm
import wave
@ -16,7 +16,7 @@ progress = tqdm()
# Initialize PyAudio
p = pyaudio.PyAudio()
decoder = DualOpusDecoder()
decoder = xOpusDecoder()
xopusdecoder = XopusReader(args.input)

79
realtime.py Normal file
View File

@ -0,0 +1,79 @@
import numpy as np
import pyaudio
import os
from libxheopus import DualOpusEncoder, xOpusDecoder
encoder = DualOpusEncoder("restricted_lowdelay", 48000, "hev2")
encoder.set_bitrates(24000)
encoder.set_bitrate_mode("CVBR")
encoder.set_bandwidth("fullband")
encoder.set_compression(10)
desired_frame_size = encoder.set_frame_size(120)
decoder = xOpusDecoder(48000)
p = pyaudio.PyAudio()
device_name_input = "Line 5 (Virtual Audio Cable)"
device_index_input = 0
for i in range(p.get_device_count()):
dev = p.get_device_info_by_index(i)
if dev['name'] == device_name_input:
device_index_input = dev['index']
break
device_name_output = "Speakers (2- USB Audio DAC )"
device_index_output = 0
for i in range(p.get_device_count()):
dev = p.get_device_info_by_index(i)
if dev['name'] == device_name_output:
device_index_output = dev['index']
break
streaminput = p.open(format=pyaudio.paInt16, channels=2, rate=48000, input=True, input_device_index=device_index_input)
streamoutput = p.open(format=pyaudio.paInt16, channels=2, rate=48000, output=True, output_device_index=device_index_output)
print(desired_frame_size)
try:
while True:
try:
pcm = np.frombuffer(streaminput.read(desired_frame_size, exception_on_overflow=False), dtype=np.int16)
if len(pcm) == 0:
# If PCM is empty, break the loop
break
encoded_packets = encoder.encode(pcm)
print(len(pcm), "-encoded->", len(encoded_packets))
# print(encoded_packet)
try:
decoded_pcm = decoder.decode(encoded_packets)
except Exception as e:
decoded_pcm = b""
# 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.astype(np.int16).tobytes())
else:
print("Decoded PCM is empty")
except Exception as e:
print(e)
raise
except KeyboardInterrupt:
print("Interrupted by user")
finally:
# Clean up PyAudio streams and terminate PyAudio
streaminput.stop_stream()
streaminput.close()
streamoutput.stop_stream()
streamoutput.close()
p.terminate()