feat: cleanup and rework audio system
This commit is contained in:
138
Pipfile.lock
generated
138
Pipfile.lock
generated
@@ -16,6 +16,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"default": {
|
"default": {
|
||||||
|
"bidict": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:4fa46f7ff96dc244abfc437383d987404ae861df797e2fd5b190e233c302be09",
|
||||||
|
"sha256:929d056e8d0d9b17ceda20ba5b24ac388e2a4d39802b87f9f4d3f45ecba070bf"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==0.21.2"
|
||||||
|
},
|
||||||
"click": {
|
"click": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
|
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
|
||||||
@@ -34,11 +42,11 @@
|
|||||||
},
|
},
|
||||||
"eventlet": {
|
"eventlet": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:9faff63631b01277c463ae91cd4ab3f25a2f0f5abe3219d43a386ef1daa6159a",
|
"sha256:19d6f3aa9525221ba60d0ec31b570508021af7ad5497fb77f77501fe9a7c34d3",
|
||||||
"sha256:a07b8c8e1f43bc4c44a255baeb066a4edce783dcfacae213bcabb95fdcd02d8c"
|
"sha256:b33f31ae8d87eb2838dcb8467449211852374ee6dea97113c158fc84d9acff9b"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.29.1"
|
"version": "==0.30.0"
|
||||||
},
|
},
|
||||||
"flask": {
|
"flask": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -50,11 +58,11 @@
|
|||||||
},
|
},
|
||||||
"flask-socketio": {
|
"flask-socketio": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:3668675bf7763c5b5f56689d439f07356e89c0a52e0c9e9cd3cc08563c07b252",
|
"sha256:5c4319f5214ada20807857dc8fdf3dc7d2afe8d6dd38f5c516c72e2be47d2227",
|
||||||
"sha256:36c1d5765010d1f4e4f05b4cc9c20c289d9dc70698c88d1addd0afcfedc5b062"
|
"sha256:5d9a4438bafd806c5a3b832e74b69758781a8ee26fb6c9b1dbdda9b4fced432e"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==4.3.1"
|
"version": "==5.0.1"
|
||||||
},
|
},
|
||||||
"greenlet": {
|
"greenlet": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -136,57 +144,70 @@
|
|||||||
},
|
},
|
||||||
"numpy": {
|
"numpy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:04c7d4ebc5ff93d9822075ddb1751ff392a4375e5885299445fcebf877f179d5",
|
"sha256:08308c38e44cc926bdfce99498b21eec1f848d24c302519e64203a8da99a97db",
|
||||||
"sha256:0bfd85053d1e9f60234f28f63d4a5147ada7f432943c113a11afcf3e65d9d4c8",
|
"sha256:09c12096d843b90eafd01ea1b3307e78ddd47a55855ad402b157b6c4862197ce",
|
||||||
"sha256:0c66da1d202c52051625e55a249da35b31f65a81cb56e4c69af0dfb8fb0125bf",
|
"sha256:13d166f77d6dc02c0a73c1101dd87fdf01339febec1030bd810dcd53fff3b0f1",
|
||||||
"sha256:0d310730e1e793527065ad7dde736197b705d0e4c9999775f212b03c44a8484c",
|
"sha256:141ec3a3300ab89c7f2b0775289954d193cc8edb621ea05f99db9cb181530512",
|
||||||
"sha256:1669ec8e42f169ff715a904c9b2105b6640f3f2a4c4c2cb4920ae8b2785dac65",
|
"sha256:16c1b388cc31a9baa06d91a19366fb99ddbe1c7b205293ed072211ee5bac1ed2",
|
||||||
"sha256:2117536e968abb7357d34d754e3733b0d7113d4c9f1d921f21a3d96dec5ff716",
|
"sha256:18bed2bcb39e3f758296584337966e68d2d5ba6aab7e038688ad53c8f889f757",
|
||||||
"sha256:3733640466733441295b0d6d3dcbf8e1ffa7e897d4d82903169529fd3386919a",
|
"sha256:1aeef46a13e51931c0b1cf8ae1168b4a55ecd282e6688fdb0a948cc5a1d5afb9",
|
||||||
"sha256:4339741994c775396e1a274dba3609c69ab0f16056c1077f18979bec2a2c2e6e",
|
"sha256:27d3f3b9e3406579a8af3a9f262f5339005dd25e0ecf3cf1559ff8a49ed5cbf2",
|
||||||
"sha256:51ee93e1fac3fe08ef54ff1c7f329db64d8a9c5557e6c8e908be9497ac76374b",
|
"sha256:2a2740aa9733d2e5b2dfb33639d98a64c3b0f24765fed86b0fd2aec07f6a0a08",
|
||||||
"sha256:54045b198aebf41bf6bf4088012777c1d11703bf74461d70cd350c0af2182e45",
|
"sha256:4377e10b874e653fe96985c05feed2225c912e328c8a26541f7fc600fb9c637b",
|
||||||
"sha256:58d66a6b3b55178a1f8a5fe98df26ace76260a70de694d99577ddeab7eaa9a9d",
|
"sha256:448ebb1b3bf64c0267d6b09a7cba26b5ae61b6d2dbabff7c91b660c7eccf2bdb",
|
||||||
"sha256:59f3d687faea7a4f7f93bd9665e5b102f32f3fa28514f15b126f099b7997203d",
|
"sha256:50e86c076611212ca62e5a59f518edafe0c0730f7d9195fec718da1a5c2bb1fc",
|
||||||
"sha256:62139af94728d22350a571b7c82795b9d59be77fc162414ada6c8b6a10ef5d02",
|
"sha256:5734bdc0342aba9dfc6f04920988140fb41234db42381cf7ccba64169f9fe7ac",
|
||||||
"sha256:7118f0a9f2f617f921ec7d278d981244ba83c85eea197be7c5a4f84af80a9c3c",
|
"sha256:64324f64f90a9e4ef732be0928be853eee378fd6a01be21a0a8469c4f2682c83",
|
||||||
"sha256:7c6646314291d8f5ea900a7ea9c4261f834b5b62159ba2abe3836f4fa6705526",
|
"sha256:6ae6c680f3ebf1cf7ad1d7748868b39d9f900836df774c453c11c5440bc15b36",
|
||||||
"sha256:967c92435f0b3ba37a4257c48b8715b76741410467e2bdb1097e8391fccfae15",
|
"sha256:6d7593a705d662be5bfe24111af14763016765f43cb6923ed86223f965f52387",
|
||||||
"sha256:9a3001248b9231ed73894c773142658bab914645261275f675d86c290c37f66d",
|
"sha256:8cac8790a6b1ddf88640a9267ee67b1aee7a57dfa2d2dd33999d080bc8ee3a0f",
|
||||||
"sha256:aba1d5daf1144b956bc87ffb87966791f5e9f3e1f6fab3d7f581db1f5b598f7a",
|
"sha256:8ece138c3a16db8c1ad38f52eb32be6086cc72f403150a79336eb2045723a1ad",
|
||||||
"sha256:addaa551b298052c16885fc70408d3848d4e2e7352de4e7a1e13e691abc734c1",
|
"sha256:9eeb7d1d04b117ac0d38719915ae169aa6b61fca227b0b7d198d43728f0c879c",
|
||||||
"sha256:b594f76771bc7fc8a044c5ba303427ee67c17a09b36e1fa32bde82f5c419d17a",
|
"sha256:a09f98011236a419ee3f49cedc9ef27d7a1651df07810ae430a6b06576e0b414",
|
||||||
"sha256:c35a01777f81e7333bcf276b605f39c872e28295441c265cd0c860f4b40148c1",
|
"sha256:a5d897c14513590a85774180be713f692df6fa8ecf6483e561a6d47309566f37",
|
||||||
"sha256:cebd4f4e64cfe87f2039e4725781f6326a61f095bc77b3716502bed812b385a9",
|
"sha256:ad6f2ff5b1989a4899bf89800a671d71b1612e5ff40866d1f4d8bcf48d4e5764",
|
||||||
"sha256:d526fa58ae4aead839161535d59ea9565863bb0b0bdb3cc63214613fb16aced4",
|
"sha256:c42c4b73121caf0ed6cd795512c9c09c52a7287b04d105d112068c1736d7c753",
|
||||||
"sha256:d7ac33585e1f09e7345aa902c281bd777fdb792432d27fca857f39b70e5dd31c",
|
"sha256:cb1017eec5257e9ac6209ac172058c430e834d5d2bc21961dceeb79d111e5909",
|
||||||
"sha256:e6ddbdc5113628f15de7e4911c02aed74a4ccff531842c583e5032f6e5a179bd",
|
"sha256:d6c7bb82883680e168b55b49c70af29b84b84abb161cbac2800e8fcb6f2109b6",
|
||||||
"sha256:eb25c381d168daf351147713f49c626030dcff7a393d5caa62515d415a6071d8"
|
"sha256:e452dc66e08a4ce642a961f134814258a082832c78c90351b75c41ad16f79f63",
|
||||||
|
"sha256:e5b6ed0f0b42317050c88022349d994fe72bfe35f5908617512cd8c8ef9da2a9",
|
||||||
|
"sha256:e9b30d4bd69498fc0c3fe9db5f62fffbb06b8eb9321f92cc970f2969be5e3949",
|
||||||
|
"sha256:ec149b90019852266fec2341ce1db513b843e496d5a8e8cdb5ced1923a92faab",
|
||||||
|
"sha256:edb01671b3caae1ca00881686003d16c2209e07b7ef8b7639f1867852b948f7c",
|
||||||
|
"sha256:f0d3929fe88ee1c155129ecd82f981b8856c5d97bcb0d5f23e9b4242e79d1de3",
|
||||||
|
"sha256:f29454410db6ef8126c83bd3c968d143304633d45dc57b51252afbd79d700893",
|
||||||
|
"sha256:fe45becb4c2f72a0907c1d0246ea6449fe7a9e2293bb0e11c4e9a32bb0930a15",
|
||||||
|
"sha256:fedbd128668ead37f33917820b704784aff695e0019309ad446a6d0b065b57e4"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.6'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==1.19.2"
|
"version": "==1.19.4"
|
||||||
},
|
},
|
||||||
"opencv-python": {
|
"opencv-python": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:16864152aa6ac346ef83588d6f4f5dc974d27851c034d6970fcb7b6a98bbd318",
|
"sha256:0548981fe189e0d57b9cc65066b66fd70d4bc84ea906f349a63d9098e1b911c6",
|
||||||
"sha256:23dade76fe0194139112eea7ecdfa02ae09924b1d8d853f17f387a356519e484",
|
"sha256:117dbb2fd184de28d831f14c1da17864efcee7bb7895e43adf40f5e1da9137fb",
|
||||||
"sha256:27d5b83edd245a12dd6b8562569ad3f23e5ffe30cef8cfcc70756dd24b55d12f",
|
"sha256:135e05b69ab9665cbe2589f56e60895219bc2443a632bdc4bde72fb95eda1582",
|
||||||
"sha256:2a2a7590b99d872b193cda0592b2c1cd6561159c31b361597c0e69e8926c8d16",
|
"sha256:14df77490c8aedceae74e660564d48c04761658aecc93895ac5e974006a89606",
|
||||||
"sha256:46032d4648c74730115f8522effda8ac39bd0385f07edc7aab57b41cc7617933",
|
"sha256:17581c68400f828700e5c6b3b082f50c781bf74cb9a7b972a04f05d26c8e894a",
|
||||||
"sha256:4c195597d5286d1cc7259aeaeb7e6c1cde07fec9bddf26523eab1b15709291aa",
|
"sha256:4af0053c6a70f127a52c26b112341826d3dbfce6955beb9044d3eabd7e14d1cd",
|
||||||
"sha256:69c971fefb633cfd334ed195d58e76e87f267649f98a2394f7400b178e918936",
|
"sha256:51baebb0f8f3cae4cccd30daf018a5bb75cb759d5658aea29100d34cd5cac106",
|
||||||
"sha256:80b5b68e9c5dda29205ca112e6d5bd647b6b43cf917cfa5ce178d61675291bba",
|
"sha256:6022609b67f9c0f14e6807e782660d1d1be94d4f0c7bc1794d7d8f600014acb2",
|
||||||
"sha256:98676d349fdfc17dba9f23b87e9b6a639733d35f5f0ffcccb90e76c8200568f4",
|
"sha256:68a9ec7e32f82cab267b6f757d9862a9a930371062739f9d00472e7c850c5854",
|
||||||
"sha256:9df617736351100879b70d914366b9f9e38aa227885f2590b48badc4a233119d",
|
"sha256:6b1d85cbb64ce20ac5f79ad8e3e76a3dbff53d258c65f2fc0b9411321147a0be",
|
||||||
"sha256:b2147317b00b20e8d7e01201221af2b278aed449fa436316c42bc63f653e8245",
|
"sha256:6b6d23de6d5ddc55e865ac8532bf8062b26ba70305fa1c87c671717027dcd370",
|
||||||
"sha256:d838ee4562f52793b1b10876e5067cae1a6bb1c3c575091644be9b88cf45d255",
|
"sha256:744e9ae2fb4c8574e6d4a762146b4d0984bdec60b98480fc54a363c03a07a1ac",
|
||||||
"sha256:db74a92ef9c2a0810e1436d586b3b15d421a39b72f06263358f15c7a609498e0",
|
"sha256:7fe81d08df4eb5dc4c6aa5f09888b6fd390fce5fa7d5624a98cac890b9aa6181",
|
||||||
"sha256:e100a4ffdeed8c4afac6a5b3f6b4481efe0ad90e0a0ae2d129478abd4bd790bc",
|
"sha256:8a8ebd7ceebc0be9c14ca3e25a1c4ae086016b469848258e998247f2fc855314",
|
||||||
"sha256:e87d88a820050c0e886c9add48eac2f80ff29207a98cca25869a6868c519daa4",
|
"sha256:8aeda9b2c37bf91fa88d67f09b85f2250661eec43d72184ec544783de204e96a",
|
||||||
"sha256:fdf017c5b93d58ad77e2690e59322fd09414705c28d69b52fad4a19985422e6c"
|
"sha256:9659e80059c9f39728c7dcc22032dff0d1d467f07b6cd8e036613393e4b7c71a",
|
||||||
|
"sha256:c1382209a771ca8a25fe89d4a2377875538c6ed3cf8745280e65636cbd0988f2",
|
||||||
|
"sha256:d80db278a07f51811dbf0f9c31ff7cd5b2501822fb7a7587e71f9ff27d5c04bd",
|
||||||
|
"sha256:db874c65654465ef71d6e8618bed8c725722bc90624132b9512bf061abb4eec0",
|
||||||
|
"sha256:e4c072cf4260063ebadc70e34d622fa1127a88e364475ed757709e249ebe990f",
|
||||||
|
"sha256:f69a56e958ecb549ba84e0497a438080932b4d52ded441cec04d80afde71dc0a"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==4.4.0.44"
|
"version": "==4.4.0.46"
|
||||||
},
|
},
|
||||||
"pillow": {
|
"pillow": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -239,17 +260,17 @@
|
|||||||
},
|
},
|
||||||
"python-engineio": {
|
"python-engineio": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:36b33c6aa702d9b6a7f527eec6387a2da1a9a24484ec2f086d76576413cef04b",
|
"sha256:33f7a214be5db35c867e97027bfe63676cb003d82aa17a607612b25ba5d84e5b",
|
||||||
"sha256:cfded18156862f94544a9f8ef37f56727df731c8552d7023f5afee8369be2db6"
|
"sha256:9f34afa4170f5ba6e3d9ff158752ccf8fbb2145f16554b2f0fc84646675be99a"
|
||||||
],
|
],
|
||||||
"version": "==3.13.2"
|
"version": "==4.0.0"
|
||||||
},
|
},
|
||||||
"python-socketio": {
|
"python-socketio": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:358d8fbbc029c4538ea25bcaa283e47f375be0017fcba829de8a3a731c9df25a",
|
"sha256:05d77e7392d261d26bd4a513c35af2678caef10b68f5ab47918c6febfc9cd690",
|
||||||
"sha256:d437f797c44b6efba2f201867cf02b8c96b97dff26d4e4281ac08b45817cd522"
|
"sha256:3794412ea577144ce1bc8f258deb265230da1d9d1ff5427c32fe7e8de484c275"
|
||||||
],
|
],
|
||||||
"version": "==4.6.0"
|
"version": "==5.0.3"
|
||||||
},
|
},
|
||||||
"redis": {
|
"redis": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -269,10 +290,11 @@
|
|||||||
},
|
},
|
||||||
"smbus2": {
|
"smbus2": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:210e66eebe4d0b1fe836b3ec2751841942e1c4918c0b429b20a0e20a222228b4"
|
"sha256:1b5c690715e1efab39e41038147dfba75afc72a10f76b3f0310d783e9f8a83a6",
|
||||||
|
"sha256:39a8d5f84b3b07ced0dd411f8c3d62c6aed93a2f06b270309dd41e82973e2f55"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.3.0"
|
"version": "==0.4.0"
|
||||||
},
|
},
|
||||||
"werkzeug": {
|
"werkzeug": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
|||||||
36
app.py
36
app.py
@@ -1,20 +1,13 @@
|
|||||||
|
import logging
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
from pprint import pprint
|
|
||||||
from threading import Thread
|
|
||||||
|
|
||||||
# import eventlet
|
from flask import Flask, send_file, jsonify
|
||||||
# eventlet.monkey_patch()
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from flask import Flask, jsonify, send_file, Response
|
|
||||||
from flask_socketio import SocketIO
|
from flask_socketio import SocketIO
|
||||||
|
|
||||||
from redis import Redis
|
from redis import Redis
|
||||||
|
|
||||||
# from control.camera import Camera
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
# from control.walle import WallE
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
DATA_URL_PREFIX = 'data:image/jpeg;base64,'
|
DATA_URL_PREFIX = 'data:image/jpeg;base64,'
|
||||||
# DEBUG = True
|
# DEBUG = True
|
||||||
|
|
||||||
@@ -24,9 +17,6 @@ sio = SocketIO(app)
|
|||||||
|
|
||||||
redisdb = Redis()
|
redisdb = Redis()
|
||||||
|
|
||||||
# camera = Camera()
|
|
||||||
# walle = WallE()
|
|
||||||
|
|
||||||
|
|
||||||
class CameraListener:
|
class CameraListener:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -59,10 +49,24 @@ def index(path):
|
|||||||
return send_file('client/dist/index.html')
|
return send_file('client/dist/index.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/sounds')
|
||||||
|
def get_available_sounds():
|
||||||
|
try:
|
||||||
|
sound_data = json.loads(redisdb.get('sound_data'))
|
||||||
|
except ValueError:
|
||||||
|
sound_data = []
|
||||||
|
return jsonify(sound_data)
|
||||||
|
|
||||||
|
|
||||||
|
@sio.on('playSound')
|
||||||
|
def play_sound(name):
|
||||||
|
redisdb.publish('sound', json.dumps({'action': 'play', 'name': name}))
|
||||||
|
|
||||||
|
|
||||||
@sio.on("camera")
|
@sio.on("camera")
|
||||||
def camera_message(directions):
|
def camera_message(directions):
|
||||||
# walle.set_eye_velocity(directions['angle'], directions['force'])
|
# walle.set_eye_velocity(directions['angle'], directions['force'])
|
||||||
print(f"Moving camera in direction {directions['angle']:f} with velocity {directions['force']}")
|
LOG.debug(f"Moving camera in direction {directions['angle']:f} with velocity {directions['force']}")
|
||||||
redisdb.publish('look', json.dumps({
|
redisdb.publish('look', json.dumps({
|
||||||
'force': directions['force'],
|
'force': directions['force'],
|
||||||
'angle': directions['angle'],
|
'angle': directions['angle'],
|
||||||
@@ -72,7 +76,7 @@ def camera_message(directions):
|
|||||||
@sio.on("move")
|
@sio.on("move")
|
||||||
def move_message(directions):
|
def move_message(directions):
|
||||||
# walle.set_movement(directions['angle'], directions['force'])
|
# walle.set_movement(directions['angle'], directions['force'])
|
||||||
print(f"Moving in direction {directions['angle']:f} with velocity {directions['force']}")
|
LOG.debug(f"Moving in direction {directions['angle']:f} with velocity {directions['force']}")
|
||||||
redisdb.publish('move', json.dumps({
|
redisdb.publish('move', json.dumps({
|
||||||
'force': directions['force'],
|
'force': directions['force'],
|
||||||
'angle': directions['angle'],
|
'angle': directions['angle'],
|
||||||
|
|||||||
@@ -9,8 +9,10 @@
|
|||||||
"buildw": "vue-cli-service build --watch"
|
"buildw": "vue-cli-service build --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^0.21.1",
|
||||||
"core-js": "^3.6.4",
|
"core-js": "^3.6.4",
|
||||||
"nipplejs": "^0.8.5",
|
"nipplejs": "^0.8.5",
|
||||||
|
"socket.io-client": "^3.0.4",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-router": "^3.2.0",
|
"vue-router": "^3.2.0",
|
||||||
"vue-slider-component": "^3.2.5",
|
"vue-slider-component": "^3.2.5",
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
<div id="nav">
|
<div id="nav">
|
||||||
<div class="nav-item toggle-button" @click="toggleFullScreen">Toggle Full Screen</div> |
|
<div class="nav-item toggle-button" @click="toggleFullScreen">Toggle Full Screen</div> |
|
||||||
<router-link to="/">Camera</router-link> |
|
<router-link to="/">Camera</router-link> |
|
||||||
<router-link to="/control">Control</router-link>
|
<router-link to="/control">Control</router-link> |
|
||||||
|
<router-link to="/sounds">Sounds</router-link>
|
||||||
</div>
|
</div>
|
||||||
<router-view/>
|
<router-view/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import VueSocketIO from 'vue-socket.io';
|
import VueSocketIO from 'vue-socket.io';
|
||||||
|
import SocketIO from 'socket.io-client';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
|
|
||||||
Vue.use(new VueSocketIO({
|
Vue.use(new VueSocketIO({
|
||||||
debug: true,
|
debug: true,
|
||||||
connection: `${window.location.protocol}//${window.location.host}`,
|
connection: SocketIO(`${window.location.protocol}//${window.location.host}`),
|
||||||
options: {
|
options: {
|
||||||
transport: 'websocket',
|
transport: 'websocket',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ const routes = [
|
|||||||
// which is lazy-loaded when the route is visited.
|
// which is lazy-loaded when the route is visited.
|
||||||
component: () => import(/* webpackChunkName: "control" */ '../views/Control.vue'),
|
component: () => import(/* webpackChunkName: "control" */ '../views/Control.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/sounds',
|
||||||
|
name: 'Sounds',
|
||||||
|
component: () => import(/* webpackChunkName: "sounds" */'../views/SoundBoard.vue'),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const router = new VueRouter({
|
const router = new VueRouter({
|
||||||
|
|||||||
35
client/src/views/SoundBoard.vue
Normal file
35
client/src/views/SoundBoard.vue
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<div class="sounds">
|
||||||
|
<div :key="`sound-${sound.name}`" class="sound" v-for="sound in sounds">
|
||||||
|
<h2 @click="emitSound(sound.name)">{{ sound.name }}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SoundBoard',
|
||||||
|
components: {
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
sounds: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
axios.get('/api/sounds')
|
||||||
|
.then((response) => {
|
||||||
|
this.sounds = response.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
emitSound(soundName) {
|
||||||
|
this.$socket.emit('playSound', soundName);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -836,6 +836,11 @@
|
|||||||
resolved "https://registry.npm.taobao.org/@types/color-name/download/@types/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
resolved "https://registry.npm.taobao.org/@types/color-name/download/@types/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||||
integrity sha1-HBJhu+qhCoBVu8XYq4S3sq/IRqA=
|
integrity sha1-HBJhu+qhCoBVu8XYq4S3sq/IRqA=
|
||||||
|
|
||||||
|
"@types/component-emitter@^1.2.10":
|
||||||
|
version "1.2.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/component-emitter/-/component-emitter-1.2.10.tgz#ef5b1589b9f16544642e473db5ea5639107ef3ea"
|
||||||
|
integrity sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==
|
||||||
|
|
||||||
"@types/events@*":
|
"@types/events@*":
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npm.taobao.org/@types/events/download/@types/events-3.0.0.tgz?cache=0&sync_timestamp=1580843133282&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fevents%2Fdownload%2F%40types%2Fevents-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
resolved "https://registry.npm.taobao.org/@types/events/download/@types/events-3.0.0.tgz?cache=0&sync_timestamp=1580843133282&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fevents%2Fdownload%2F%40types%2Fevents-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
||||||
@@ -1643,6 +1648,13 @@ aws4@^1.8.0:
|
|||||||
resolved "https://registry.npm.taobao.org/aws4/download/aws4-1.9.1.tgz?cache=0&sync_timestamp=1578958168482&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faws4%2Fdownload%2Faws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
|
resolved "https://registry.npm.taobao.org/aws4/download/aws4-1.9.1.tgz?cache=0&sync_timestamp=1578958168482&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faws4%2Fdownload%2Faws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
|
||||||
integrity sha1-fjPY99RJs/ZzzXLeuavcVS2+Uo4=
|
integrity sha1-fjPY99RJs/ZzzXLeuavcVS2+Uo4=
|
||||||
|
|
||||||
|
axios@^0.21.1:
|
||||||
|
version "0.21.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||||
|
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.10.0"
|
||||||
|
|
||||||
babel-eslint@^10.0.3:
|
babel-eslint@^10.0.3:
|
||||||
version "10.1.0"
|
version "10.1.0"
|
||||||
resolved "https://registry.npm.taobao.org/babel-eslint/download/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232"
|
resolved "https://registry.npm.taobao.org/babel-eslint/download/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232"
|
||||||
@@ -1682,6 +1694,11 @@ balanced-match@^1.0.0:
|
|||||||
resolved "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||||
|
|
||||||
|
base64-arraybuffer@0.1.4:
|
||||||
|
version "0.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812"
|
||||||
|
integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=
|
||||||
|
|
||||||
base64-arraybuffer@0.1.5:
|
base64-arraybuffer@0.1.5:
|
||||||
version "0.1.5"
|
version "0.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
|
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
|
||||||
@@ -2403,10 +2420,10 @@ component-emitter@1.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
|
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
|
||||||
integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=
|
integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=
|
||||||
|
|
||||||
component-emitter@^1.2.1:
|
component-emitter@^1.2.1, component-emitter@~1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.npm.taobao.org/component-emitter/download/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
||||||
integrity sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=
|
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
||||||
|
|
||||||
component-inherit@0.0.3:
|
component-inherit@0.0.3:
|
||||||
version "0.0.3"
|
version "0.0.3"
|
||||||
@@ -3233,6 +3250,22 @@ engine.io-client@~3.4.0:
|
|||||||
xmlhttprequest-ssl "~1.5.4"
|
xmlhttprequest-ssl "~1.5.4"
|
||||||
yeast "0.1.2"
|
yeast "0.1.2"
|
||||||
|
|
||||||
|
engine.io-client@~4.0.0:
|
||||||
|
version "4.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-4.0.5.tgz#e12b05a11a7a3cccec6d69f9af8435146e3d507e"
|
||||||
|
integrity sha512-1lkn0QdekHQPMTcxUh8LqIuxQHNtKV5GvqkQzmZ1rYKAvB6puMm13U7K1ps3OQZ4joE46asQiAKrcdL9weNEVw==
|
||||||
|
dependencies:
|
||||||
|
base64-arraybuffer "0.1.4"
|
||||||
|
component-emitter "~1.3.0"
|
||||||
|
debug "~4.1.0"
|
||||||
|
engine.io-parser "~4.0.1"
|
||||||
|
has-cors "1.1.0"
|
||||||
|
parseqs "0.0.6"
|
||||||
|
parseuri "0.0.6"
|
||||||
|
ws "~7.2.1"
|
||||||
|
xmlhttprequest-ssl "~1.5.4"
|
||||||
|
yeast "0.1.2"
|
||||||
|
|
||||||
engine.io-parser@~2.2.0:
|
engine.io-parser@~2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.0.tgz#312c4894f57d52a02b420868da7b5c1c84af80ed"
|
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.0.tgz#312c4894f57d52a02b420868da7b5c1c84af80ed"
|
||||||
@@ -3244,6 +3277,13 @@ engine.io-parser@~2.2.0:
|
|||||||
blob "0.0.5"
|
blob "0.0.5"
|
||||||
has-binary2 "~1.0.2"
|
has-binary2 "~1.0.2"
|
||||||
|
|
||||||
|
engine.io-parser@~4.0.1:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-4.0.2.tgz#e41d0b3fb66f7bf4a3671d2038a154024edb501e"
|
||||||
|
integrity sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==
|
||||||
|
dependencies:
|
||||||
|
base64-arraybuffer "0.1.4"
|
||||||
|
|
||||||
enhanced-resolve@^4.1.0:
|
enhanced-resolve@^4.1.0:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.npm.taobao.org/enhanced-resolve/download/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66"
|
resolved "https://registry.npm.taobao.org/enhanced-resolve/download/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66"
|
||||||
@@ -3895,6 +3935,11 @@ follow-redirects@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
debug "^3.0.0"
|
debug "^3.0.0"
|
||||||
|
|
||||||
|
follow-redirects@^1.10.0:
|
||||||
|
version "1.13.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
|
||||||
|
integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
|
||||||
|
|
||||||
for-in@^1.0.2:
|
for-in@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npm.taobao.org/for-in/download/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
resolved "https://registry.npm.taobao.org/for-in/download/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||||
@@ -6358,6 +6403,11 @@ parseqs@0.0.5:
|
|||||||
dependencies:
|
dependencies:
|
||||||
better-assert "~1.0.0"
|
better-assert "~1.0.0"
|
||||||
|
|
||||||
|
parseqs@0.0.6:
|
||||||
|
version "0.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5"
|
||||||
|
integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==
|
||||||
|
|
||||||
parseuri@0.0.5:
|
parseuri@0.0.5:
|
||||||
version "0.0.5"
|
version "0.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a"
|
resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a"
|
||||||
@@ -6365,6 +6415,11 @@ parseuri@0.0.5:
|
|||||||
dependencies:
|
dependencies:
|
||||||
better-assert "~1.0.0"
|
better-assert "~1.0.0"
|
||||||
|
|
||||||
|
parseuri@0.0.6:
|
||||||
|
version "0.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a"
|
||||||
|
integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==
|
||||||
|
|
||||||
parseurl@~1.3.2, parseurl@~1.3.3:
|
parseurl@~1.3.2, parseurl@~1.3.3:
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
resolved "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
resolved "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||||
@@ -7740,6 +7795,20 @@ socket.io-client@^2.1.1:
|
|||||||
socket.io-parser "~3.3.0"
|
socket.io-parser "~3.3.0"
|
||||||
to-array "0.1.4"
|
to-array "0.1.4"
|
||||||
|
|
||||||
|
socket.io-client@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-3.0.4.tgz#c0203419a9f71e1360ef92a31301e80260e94bb9"
|
||||||
|
integrity sha512-qMvBuS+W9JIN2mkfAWDCxuIt+jpIKDf8C0604zEqx1JrPaPSS6cN0F3B2GYWC83TqBeVJXW66GFxWV3KD88n0Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/component-emitter" "^1.2.10"
|
||||||
|
backo2 "1.0.2"
|
||||||
|
component-bind "1.0.0"
|
||||||
|
component-emitter "~1.3.0"
|
||||||
|
debug "~4.1.0"
|
||||||
|
engine.io-client "~4.0.0"
|
||||||
|
parseuri "0.0.6"
|
||||||
|
socket.io-parser "~4.0.1"
|
||||||
|
|
||||||
socket.io-parser@~3.3.0:
|
socket.io-parser@~3.3.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f"
|
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f"
|
||||||
@@ -7749,6 +7818,15 @@ socket.io-parser@~3.3.0:
|
|||||||
debug "~3.1.0"
|
debug "~3.1.0"
|
||||||
isarray "2.0.1"
|
isarray "2.0.1"
|
||||||
|
|
||||||
|
socket.io-parser@~4.0.1:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.0.2.tgz#3d021a9c86671bb079e7c6c806db6a1d9b1bc780"
|
||||||
|
integrity sha512-Bs3IYHDivwf+bAAuW/8xwJgIiBNtlvnjYRc4PbXgniLmcP1BrakBoq/QhO24rgtgW7VZ7uAaswRGxutUnlAK7g==
|
||||||
|
dependencies:
|
||||||
|
"@types/component-emitter" "^1.2.10"
|
||||||
|
component-emitter "~1.3.0"
|
||||||
|
debug "~4.1.0"
|
||||||
|
|
||||||
sockjs-client@1.4.0:
|
sockjs-client@1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.npm.taobao.org/sockjs-client/download/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5"
|
resolved "https://registry.npm.taobao.org/sockjs-client/download/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5"
|
||||||
@@ -9008,6 +9086,11 @@ ws@~6.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
async-limiter "~1.0.0"
|
async-limiter "~1.0.0"
|
||||||
|
|
||||||
|
ws@~7.2.1:
|
||||||
|
version "7.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.5.tgz#abb1370d4626a5a9cd79d8de404aa18b3465d10d"
|
||||||
|
integrity sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==
|
||||||
|
|
||||||
xmlhttprequest-ssl@~1.5.4:
|
xmlhttprequest-ssl@~1.5.4:
|
||||||
version "1.5.5"
|
version "1.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
|
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
|
||||||
|
|||||||
@@ -1,44 +1,35 @@
|
|||||||
from typing import Optional, NamedTuple
|
|
||||||
from queue import Queue, Empty
|
|
||||||
import wave
|
|
||||||
import uuid
|
|
||||||
import sched
|
|
||||||
import logging
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
|
import sched
|
||||||
|
import uuid
|
||||||
|
import wave
|
||||||
|
from queue import Empty, Queue
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import audioop
|
from pyaudio import PyAudio, Stream, paContinue
|
||||||
from pyaudio import PyAudio, Stream
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
CHUNK_SIZE = 1024
|
CHUNK_SIZE = 1024
|
||||||
INTERVAL_QUEUE = 0.1
|
INTERVAL_QUEUE = 0.1
|
||||||
INTERVAL_STREAM_FAST = 0.0001
|
INTERVAL_STREAM = 0.1
|
||||||
INTERVAL_STREAM_SLOW = 0.001
|
|
||||||
|
|
||||||
|
|
||||||
class PlayEntry:
|
class PlayEntry:
|
||||||
def __init__(self, wave_file: wave.Wave_read, stream: Stream, device_framerate):
|
|
||||||
self.wave_file = wave_file
|
|
||||||
self.stream = stream
|
|
||||||
self.cvstate = None
|
|
||||||
|
|
||||||
self.device_framerate = device_framerate
|
|
||||||
self.sample_width = wave_file.getsampwidth()
|
|
||||||
self.channels = wave_file.getnchannels()
|
|
||||||
self.frame_rate = wave_file.getframerate()
|
|
||||||
|
|
||||||
def get_actual_data(self, nframes):
|
|
||||||
data = self.wave_file.readframes(nframes)
|
|
||||||
new_data, self.cvstate = audioop.ratecv(data, self.sample_width, self.channels, self.frame_rate,
|
|
||||||
self.device_framerate, self.cvstate)
|
|
||||||
return new_data
|
|
||||||
|
|
||||||
wave_file: wave.Wave_read
|
wave_file: wave.Wave_read
|
||||||
stream: Stream
|
stream: Optional[Stream]
|
||||||
cvstate: Optional['audioop.RatecvState']
|
|
||||||
actual_size: int = None
|
def __init__(self, wave_file: wave.Wave_read):
|
||||||
|
self.wave_file = wave_file
|
||||||
|
self.stream = None
|
||||||
|
|
||||||
|
def set_stream(self, stream: Stream):
|
||||||
|
self.stream = stream
|
||||||
|
|
||||||
|
def pyaudio_callback(self, in_data, frame_count, time_info, status):
|
||||||
|
data = self.wave_file.readframes(frame_count)
|
||||||
|
return data, paContinue
|
||||||
|
|
||||||
|
|
||||||
class AudioSystem:
|
class AudioSystem:
|
||||||
@@ -53,7 +44,7 @@ class AudioSystem:
|
|||||||
self.default_sample_rate = int(self.default_output_device['defaultSampleRate'])
|
self.default_sample_rate = int(self.default_output_device['defaultSampleRate'])
|
||||||
self.scheduler = scheduler
|
self.scheduler = scheduler
|
||||||
self.scheduler.enter(0, 2, self.start_streams)
|
self.scheduler.enter(0, 2, self.start_streams)
|
||||||
self.scheduler.enter(0, 1, self.fill_streams)
|
self.scheduler.enter(0, 1, self.stream_cleanup)
|
||||||
with open(sound_file) as f:
|
with open(sound_file) as f:
|
||||||
self.sound_data = json.load(f)
|
self.sound_data = json.load(f)
|
||||||
self.sound_dir = os.path.realpath(self.sound_data['sound_dir'])
|
self.sound_dir = os.path.realpath(self.sound_data['sound_dir'])
|
||||||
@@ -75,50 +66,45 @@ class AudioSystem:
|
|||||||
# drain the queue
|
# drain the queue
|
||||||
while True:
|
while True:
|
||||||
item = self.to_play.get(False)
|
item = self.to_play.get(False)
|
||||||
logger.info(f"Queueing stream for {item!r}")
|
LOG.info(f"Queueing stream for {item!r}")
|
||||||
wave_file = wave.open(item) # type: wave.Wave_read
|
wave_file = wave.open(item) # type: wave.Wave_read
|
||||||
|
entry = PlayEntry(wave_file)
|
||||||
stream = self.audio.open(
|
stream = self.audio.open(
|
||||||
format=self.audio.get_format_from_width(wave_file.getsampwidth()),
|
format=self.audio.get_format_from_width(wave_file.getsampwidth()),
|
||||||
channels=wave_file.getnchannels(),
|
channels=wave_file.getnchannels(),
|
||||||
rate=self.default_sample_rate,
|
rate=wave_file.getframerate(),
|
||||||
output=True,
|
output=True,
|
||||||
|
stream_callback=entry.pyaudio_callback,
|
||||||
)
|
)
|
||||||
|
entry.set_stream(stream)
|
||||||
entry_id = uuid.uuid4()
|
entry_id = uuid.uuid4()
|
||||||
while entry_id in self.playing:
|
while entry_id in self.playing:
|
||||||
entry_id = uuid.uuid4()
|
entry_id = uuid.uuid4()
|
||||||
self.playing[entry_id] = PlayEntry(wave_file, stream, self.default_sample_rate)
|
self.playing[entry_id] = entry
|
||||||
self.to_play.task_done()
|
self.to_play.task_done()
|
||||||
except Empty:
|
except Empty:
|
||||||
pass
|
pass
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
logger.warning('Error in opening stream', exc_info=e)
|
LOG.warning('Error in opening stream', exc_info=e)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error('Fatal error in opening stream', exc_info=e)
|
LOG.error('Fatal error in opening stream', exc_info=e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def fill_streams(self):
|
def stream_cleanup(self):
|
||||||
self.scheduler.enter(INTERVAL_STREAM_FAST if len(self.playing) > 0 else INTERVAL_STREAM_SLOW,
|
self.scheduler.enter(INTERVAL_STREAM, 1, self.stream_cleanup)
|
||||||
1, self.fill_streams)
|
|
||||||
finished_streams = []
|
finished_streams = []
|
||||||
for stream_id, entry in self.playing.items(): # type: uuid.UUID, PlayEntry
|
for stream_id, entry in self.playing.items(): # type: uuid.UUID, PlayEntry
|
||||||
try:
|
try:
|
||||||
if entry.stream.get_write_available() < (CHUNK_SIZE * 1.5):
|
if entry.stream.is_active():
|
||||||
continue # not enough space in buffer, wait for next iteration
|
continue
|
||||||
try:
|
|
||||||
data = entry.get_actual_data(CHUNK_SIZE)
|
|
||||||
except IOError as e:
|
|
||||||
logger.warning('An IO Error occurred while reading WAV file', exc_info=e)
|
|
||||||
data = b''
|
|
||||||
if data:
|
|
||||||
entry.stream.write(data)
|
|
||||||
else:
|
else:
|
||||||
logger.debug("Done playing sound, shutting down stream")
|
LOG.debug("Done playing sound, shutting down stream")
|
||||||
entry.stream.stop_stream()
|
entry.stream.stop_stream()
|
||||||
entry.stream.close()
|
entry.stream.close()
|
||||||
entry.wave_file.close()
|
entry.wave_file.close()
|
||||||
finished_streams.append(stream_id)
|
finished_streams.append(stream_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning('A fatal error occurred while rendering sound', exc_info=e)
|
LOG.warning('A fatal error occurred while rendering sound', exc_info=e)
|
||||||
raise e
|
raise e
|
||||||
for finished in finished_streams:
|
for finished in finished_streams:
|
||||||
del self.playing[finished]
|
del self.playing[finished]
|
||||||
|
|||||||
@@ -20,27 +20,3 @@ class Camera:
|
|||||||
byte_stream = BytesIO()
|
byte_stream = BytesIO()
|
||||||
jpg.save(byte_stream, 'JPEG')
|
jpg.save(byte_stream, 'JPEG')
|
||||||
return bytes(byte_stream.getbuffer())
|
return bytes(byte_stream.getbuffer())
|
||||||
|
|
||||||
def generate_images(self):
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
return_code, image = self.capture.read()
|
|
||||||
if not return_code:
|
|
||||||
continue
|
|
||||||
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
|
||||||
jpg = Image.fromarray(image_rgb)
|
|
||||||
byte_stream = BytesIO()
|
|
||||||
jpg.save(byte_stream, 'JPEG')
|
|
||||||
yield bytes(byte_stream.getbuffer())
|
|
||||||
finally:
|
|
||||||
self.capture.release()
|
|
||||||
|
|
||||||
def mjpeg_stream(self, boundary: bytes):
|
|
||||||
for frame in self.generate_images():
|
|
||||||
if not frame:
|
|
||||||
break
|
|
||||||
yield b''.join([
|
|
||||||
b'--', boundary, b'\r\n',
|
|
||||||
b'Content-Type: image/jpeg\r\n\r\n',
|
|
||||||
frame,
|
|
||||||
b'\r\n'])
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import atexit
|
import atexit
|
||||||
|
import logging
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from RPi import GPIO
|
from RPi import GPIO
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from .mockGpio import GPIO
|
from .mockGpio import GPIO
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
PWM_FREQUENCY = 10_000 # 10 KHz
|
PWM_FREQUENCY = 10_000 # 10 KHz
|
||||||
MOTORS = []
|
MOTORS = []
|
||||||
@@ -17,7 +19,7 @@ def setup_gpio():
|
|||||||
|
|
||||||
|
|
||||||
def cleanup_gpio():
|
def cleanup_gpio():
|
||||||
print("cleaning up")
|
LOG.info("cleaning up")
|
||||||
for motor in MOTORS:
|
for motor in MOTORS:
|
||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
motor._stop()
|
motor._stop()
|
||||||
@@ -49,7 +51,7 @@ class DcMotor:
|
|||||||
self._set_reverse(val)
|
self._set_reverse(val)
|
||||||
absval = abs(val)
|
absval = abs(val)
|
||||||
if absval > 1:
|
if absval > 1:
|
||||||
print(f"clipping {val} to 1")
|
logging.warning(f"clipping {val} to 1")
|
||||||
absval = 1
|
absval = 1
|
||||||
|
|
||||||
self.pwm_enable.ChangeDutyCycle(absval * 100)
|
self.pwm_enable.ChangeDutyCycle(absval * 100)
|
||||||
|
|||||||
@@ -1,23 +1,30 @@
|
|||||||
import sched
|
import sched
|
||||||
import time
|
import time
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
from multiprocessing import Queue
|
||||||
|
from queue import Empty, Full
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
from redis import Redis
|
from redis import Redis
|
||||||
|
|
||||||
from control.walle import WallE
|
from control.walle import WallE
|
||||||
from control.audio import AudioSystem
|
from control.audio import AudioSystem
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
INTERVAL_TICK = 0.025 # 40/s
|
INTERVAL_TICK = 0.025 # 40/s
|
||||||
|
|
||||||
|
sound_action_queue = Queue()
|
||||||
|
|
||||||
class EventListener(Thread):
|
|
||||||
|
class RedisConnection(Thread):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(EventListener, self).__init__()
|
super(RedisConnection, self).__init__()
|
||||||
self.keep_running = True
|
self.keep_running = True
|
||||||
self.dbcon = Redis()
|
self.dbcon = Redis()
|
||||||
self.pubsub = self.dbcon.pubsub()
|
self.pubsub = self.dbcon.pubsub()
|
||||||
self.pubsub.subscribe(['move', 'look'])
|
self.pubsub.subscribe(['move', 'look', 'sound'])
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
while self.keep_running:
|
while self.keep_running:
|
||||||
@@ -45,9 +52,17 @@ class EventListener(Thread):
|
|||||||
walle.set_movement(data_dict['angle'], data_dict['force'])
|
walle.set_movement(data_dict['angle'], data_dict['force'])
|
||||||
elif channel == 'look':
|
elif channel == 'look':
|
||||||
walle.set_eye_velocity(data_dict['angle'], data_dict['force'])
|
walle.set_eye_velocity(data_dict['angle'], data_dict['force'])
|
||||||
|
elif channel == 'sound':
|
||||||
|
try:
|
||||||
|
sound_action_queue.put_nowait(data_dict)
|
||||||
|
except Full:
|
||||||
|
print(f'Queue was full, skipping sound')
|
||||||
else:
|
else:
|
||||||
print(f'Unknown channel {channel}')
|
print(f'Unknown channel {channel}')
|
||||||
|
|
||||||
|
def store_audio(self, sounds):
|
||||||
|
self.dbcon.set('sound_data', json.dumps(sounds))
|
||||||
|
|
||||||
|
|
||||||
scheduler = sched.scheduler()
|
scheduler = sched.scheduler()
|
||||||
audio = AudioSystem(scheduler, 'sounds.json')
|
audio = AudioSystem(scheduler, 'sounds.json')
|
||||||
@@ -58,6 +73,13 @@ walle.setup()
|
|||||||
def walle_tick():
|
def walle_tick():
|
||||||
scheduler.enter(INTERVAL_TICK, 3, walle_tick)
|
scheduler.enter(INTERVAL_TICK, 3, walle_tick)
|
||||||
walle.tick()
|
walle.tick()
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
item = sound_action_queue.get_nowait()
|
||||||
|
except Empty:
|
||||||
|
break
|
||||||
|
if item['action'] == 'play':
|
||||||
|
audio.queue_sound(item['name'])
|
||||||
|
|
||||||
|
|
||||||
scheduler.enter(INTERVAL_TICK, 3, walle_tick)
|
scheduler.enter(INTERVAL_TICK, 3, walle_tick)
|
||||||
@@ -66,10 +88,12 @@ audio.queue_sound('walle.boot')
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
redisdb = Redis()
|
redisdb = Redis()
|
||||||
event_listener = EventListener()
|
redis_connection = RedisConnection()
|
||||||
event_listener.start()
|
redis_connection.store_audio([{'name': sound['name'], 'title': sound.get('title', sound['name'])}
|
||||||
|
for sound in audio.sound_data['sounds']])
|
||||||
|
redis_connection.start()
|
||||||
try:
|
try:
|
||||||
scheduler.run()
|
scheduler.run()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
event_listener.stop()
|
redis_connection.stop()
|
||||||
raise
|
raise
|
||||||
|
|||||||
Reference in New Issue
Block a user