diff --git a/Pipfile.lock b/Pipfile.lock
index 2ef5ff0..2ae7724 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -16,6 +16,14 @@
]
},
"default": {
+ "bidict": {
+ "hashes": [
+ "sha256:4fa46f7ff96dc244abfc437383d987404ae861df797e2fd5b190e233c302be09",
+ "sha256:929d056e8d0d9b17ceda20ba5b24ac388e2a4d39802b87f9f4d3f45ecba070bf"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==0.21.2"
+ },
"click": {
"hashes": [
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
@@ -34,11 +42,11 @@
},
"eventlet": {
"hashes": [
- "sha256:9faff63631b01277c463ae91cd4ab3f25a2f0f5abe3219d43a386ef1daa6159a",
- "sha256:a07b8c8e1f43bc4c44a255baeb066a4edce783dcfacae213bcabb95fdcd02d8c"
+ "sha256:19d6f3aa9525221ba60d0ec31b570508021af7ad5497fb77f77501fe9a7c34d3",
+ "sha256:b33f31ae8d87eb2838dcb8467449211852374ee6dea97113c158fc84d9acff9b"
],
"index": "pypi",
- "version": "==0.29.1"
+ "version": "==0.30.0"
},
"flask": {
"hashes": [
@@ -50,11 +58,11 @@
},
"flask-socketio": {
"hashes": [
- "sha256:3668675bf7763c5b5f56689d439f07356e89c0a52e0c9e9cd3cc08563c07b252",
- "sha256:36c1d5765010d1f4e4f05b4cc9c20c289d9dc70698c88d1addd0afcfedc5b062"
+ "sha256:5c4319f5214ada20807857dc8fdf3dc7d2afe8d6dd38f5c516c72e2be47d2227",
+ "sha256:5d9a4438bafd806c5a3b832e74b69758781a8ee26fb6c9b1dbdda9b4fced432e"
],
"index": "pypi",
- "version": "==4.3.1"
+ "version": "==5.0.1"
},
"greenlet": {
"hashes": [
@@ -136,57 +144,70 @@
},
"numpy": {
"hashes": [
- "sha256:04c7d4ebc5ff93d9822075ddb1751ff392a4375e5885299445fcebf877f179d5",
- "sha256:0bfd85053d1e9f60234f28f63d4a5147ada7f432943c113a11afcf3e65d9d4c8",
- "sha256:0c66da1d202c52051625e55a249da35b31f65a81cb56e4c69af0dfb8fb0125bf",
- "sha256:0d310730e1e793527065ad7dde736197b705d0e4c9999775f212b03c44a8484c",
- "sha256:1669ec8e42f169ff715a904c9b2105b6640f3f2a4c4c2cb4920ae8b2785dac65",
- "sha256:2117536e968abb7357d34d754e3733b0d7113d4c9f1d921f21a3d96dec5ff716",
- "sha256:3733640466733441295b0d6d3dcbf8e1ffa7e897d4d82903169529fd3386919a",
- "sha256:4339741994c775396e1a274dba3609c69ab0f16056c1077f18979bec2a2c2e6e",
- "sha256:51ee93e1fac3fe08ef54ff1c7f329db64d8a9c5557e6c8e908be9497ac76374b",
- "sha256:54045b198aebf41bf6bf4088012777c1d11703bf74461d70cd350c0af2182e45",
- "sha256:58d66a6b3b55178a1f8a5fe98df26ace76260a70de694d99577ddeab7eaa9a9d",
- "sha256:59f3d687faea7a4f7f93bd9665e5b102f32f3fa28514f15b126f099b7997203d",
- "sha256:62139af94728d22350a571b7c82795b9d59be77fc162414ada6c8b6a10ef5d02",
- "sha256:7118f0a9f2f617f921ec7d278d981244ba83c85eea197be7c5a4f84af80a9c3c",
- "sha256:7c6646314291d8f5ea900a7ea9c4261f834b5b62159ba2abe3836f4fa6705526",
- "sha256:967c92435f0b3ba37a4257c48b8715b76741410467e2bdb1097e8391fccfae15",
- "sha256:9a3001248b9231ed73894c773142658bab914645261275f675d86c290c37f66d",
- "sha256:aba1d5daf1144b956bc87ffb87966791f5e9f3e1f6fab3d7f581db1f5b598f7a",
- "sha256:addaa551b298052c16885fc70408d3848d4e2e7352de4e7a1e13e691abc734c1",
- "sha256:b594f76771bc7fc8a044c5ba303427ee67c17a09b36e1fa32bde82f5c419d17a",
- "sha256:c35a01777f81e7333bcf276b605f39c872e28295441c265cd0c860f4b40148c1",
- "sha256:cebd4f4e64cfe87f2039e4725781f6326a61f095bc77b3716502bed812b385a9",
- "sha256:d526fa58ae4aead839161535d59ea9565863bb0b0bdb3cc63214613fb16aced4",
- "sha256:d7ac33585e1f09e7345aa902c281bd777fdb792432d27fca857f39b70e5dd31c",
- "sha256:e6ddbdc5113628f15de7e4911c02aed74a4ccff531842c583e5032f6e5a179bd",
- "sha256:eb25c381d168daf351147713f49c626030dcff7a393d5caa62515d415a6071d8"
+ "sha256:08308c38e44cc926bdfce99498b21eec1f848d24c302519e64203a8da99a97db",
+ "sha256:09c12096d843b90eafd01ea1b3307e78ddd47a55855ad402b157b6c4862197ce",
+ "sha256:13d166f77d6dc02c0a73c1101dd87fdf01339febec1030bd810dcd53fff3b0f1",
+ "sha256:141ec3a3300ab89c7f2b0775289954d193cc8edb621ea05f99db9cb181530512",
+ "sha256:16c1b388cc31a9baa06d91a19366fb99ddbe1c7b205293ed072211ee5bac1ed2",
+ "sha256:18bed2bcb39e3f758296584337966e68d2d5ba6aab7e038688ad53c8f889f757",
+ "sha256:1aeef46a13e51931c0b1cf8ae1168b4a55ecd282e6688fdb0a948cc5a1d5afb9",
+ "sha256:27d3f3b9e3406579a8af3a9f262f5339005dd25e0ecf3cf1559ff8a49ed5cbf2",
+ "sha256:2a2740aa9733d2e5b2dfb33639d98a64c3b0f24765fed86b0fd2aec07f6a0a08",
+ "sha256:4377e10b874e653fe96985c05feed2225c912e328c8a26541f7fc600fb9c637b",
+ "sha256:448ebb1b3bf64c0267d6b09a7cba26b5ae61b6d2dbabff7c91b660c7eccf2bdb",
+ "sha256:50e86c076611212ca62e5a59f518edafe0c0730f7d9195fec718da1a5c2bb1fc",
+ "sha256:5734bdc0342aba9dfc6f04920988140fb41234db42381cf7ccba64169f9fe7ac",
+ "sha256:64324f64f90a9e4ef732be0928be853eee378fd6a01be21a0a8469c4f2682c83",
+ "sha256:6ae6c680f3ebf1cf7ad1d7748868b39d9f900836df774c453c11c5440bc15b36",
+ "sha256:6d7593a705d662be5bfe24111af14763016765f43cb6923ed86223f965f52387",
+ "sha256:8cac8790a6b1ddf88640a9267ee67b1aee7a57dfa2d2dd33999d080bc8ee3a0f",
+ "sha256:8ece138c3a16db8c1ad38f52eb32be6086cc72f403150a79336eb2045723a1ad",
+ "sha256:9eeb7d1d04b117ac0d38719915ae169aa6b61fca227b0b7d198d43728f0c879c",
+ "sha256:a09f98011236a419ee3f49cedc9ef27d7a1651df07810ae430a6b06576e0b414",
+ "sha256:a5d897c14513590a85774180be713f692df6fa8ecf6483e561a6d47309566f37",
+ "sha256:ad6f2ff5b1989a4899bf89800a671d71b1612e5ff40866d1f4d8bcf48d4e5764",
+ "sha256:c42c4b73121caf0ed6cd795512c9c09c52a7287b04d105d112068c1736d7c753",
+ "sha256:cb1017eec5257e9ac6209ac172058c430e834d5d2bc21961dceeb79d111e5909",
+ "sha256:d6c7bb82883680e168b55b49c70af29b84b84abb161cbac2800e8fcb6f2109b6",
+ "sha256:e452dc66e08a4ce642a961f134814258a082832c78c90351b75c41ad16f79f63",
+ "sha256:e5b6ed0f0b42317050c88022349d994fe72bfe35f5908617512cd8c8ef9da2a9",
+ "sha256:e9b30d4bd69498fc0c3fe9db5f62fffbb06b8eb9321f92cc970f2969be5e3949",
+ "sha256:ec149b90019852266fec2341ce1db513b843e496d5a8e8cdb5ced1923a92faab",
+ "sha256:edb01671b3caae1ca00881686003d16c2209e07b7ef8b7639f1867852b948f7c",
+ "sha256:f0d3929fe88ee1c155129ecd82f981b8856c5d97bcb0d5f23e9b4242e79d1de3",
+ "sha256:f29454410db6ef8126c83bd3c968d143304633d45dc57b51252afbd79d700893",
+ "sha256:fe45becb4c2f72a0907c1d0246ea6449fe7a9e2293bb0e11c4e9a32bb0930a15",
+ "sha256:fedbd128668ead37f33917820b704784aff695e0019309ad446a6d0b065b57e4"
],
"markers": "python_version >= '3.6'",
- "version": "==1.19.2"
+ "version": "==1.19.4"
},
"opencv-python": {
"hashes": [
- "sha256:16864152aa6ac346ef83588d6f4f5dc974d27851c034d6970fcb7b6a98bbd318",
- "sha256:23dade76fe0194139112eea7ecdfa02ae09924b1d8d853f17f387a356519e484",
- "sha256:27d5b83edd245a12dd6b8562569ad3f23e5ffe30cef8cfcc70756dd24b55d12f",
- "sha256:2a2a7590b99d872b193cda0592b2c1cd6561159c31b361597c0e69e8926c8d16",
- "sha256:46032d4648c74730115f8522effda8ac39bd0385f07edc7aab57b41cc7617933",
- "sha256:4c195597d5286d1cc7259aeaeb7e6c1cde07fec9bddf26523eab1b15709291aa",
- "sha256:69c971fefb633cfd334ed195d58e76e87f267649f98a2394f7400b178e918936",
- "sha256:80b5b68e9c5dda29205ca112e6d5bd647b6b43cf917cfa5ce178d61675291bba",
- "sha256:98676d349fdfc17dba9f23b87e9b6a639733d35f5f0ffcccb90e76c8200568f4",
- "sha256:9df617736351100879b70d914366b9f9e38aa227885f2590b48badc4a233119d",
- "sha256:b2147317b00b20e8d7e01201221af2b278aed449fa436316c42bc63f653e8245",
- "sha256:d838ee4562f52793b1b10876e5067cae1a6bb1c3c575091644be9b88cf45d255",
- "sha256:db74a92ef9c2a0810e1436d586b3b15d421a39b72f06263358f15c7a609498e0",
- "sha256:e100a4ffdeed8c4afac6a5b3f6b4481efe0ad90e0a0ae2d129478abd4bd790bc",
- "sha256:e87d88a820050c0e886c9add48eac2f80ff29207a98cca25869a6868c519daa4",
- "sha256:fdf017c5b93d58ad77e2690e59322fd09414705c28d69b52fad4a19985422e6c"
+ "sha256:0548981fe189e0d57b9cc65066b66fd70d4bc84ea906f349a63d9098e1b911c6",
+ "sha256:117dbb2fd184de28d831f14c1da17864efcee7bb7895e43adf40f5e1da9137fb",
+ "sha256:135e05b69ab9665cbe2589f56e60895219bc2443a632bdc4bde72fb95eda1582",
+ "sha256:14df77490c8aedceae74e660564d48c04761658aecc93895ac5e974006a89606",
+ "sha256:17581c68400f828700e5c6b3b082f50c781bf74cb9a7b972a04f05d26c8e894a",
+ "sha256:4af0053c6a70f127a52c26b112341826d3dbfce6955beb9044d3eabd7e14d1cd",
+ "sha256:51baebb0f8f3cae4cccd30daf018a5bb75cb759d5658aea29100d34cd5cac106",
+ "sha256:6022609b67f9c0f14e6807e782660d1d1be94d4f0c7bc1794d7d8f600014acb2",
+ "sha256:68a9ec7e32f82cab267b6f757d9862a9a930371062739f9d00472e7c850c5854",
+ "sha256:6b1d85cbb64ce20ac5f79ad8e3e76a3dbff53d258c65f2fc0b9411321147a0be",
+ "sha256:6b6d23de6d5ddc55e865ac8532bf8062b26ba70305fa1c87c671717027dcd370",
+ "sha256:744e9ae2fb4c8574e6d4a762146b4d0984bdec60b98480fc54a363c03a07a1ac",
+ "sha256:7fe81d08df4eb5dc4c6aa5f09888b6fd390fce5fa7d5624a98cac890b9aa6181",
+ "sha256:8a8ebd7ceebc0be9c14ca3e25a1c4ae086016b469848258e998247f2fc855314",
+ "sha256:8aeda9b2c37bf91fa88d67f09b85f2250661eec43d72184ec544783de204e96a",
+ "sha256:9659e80059c9f39728c7dcc22032dff0d1d467f07b6cd8e036613393e4b7c71a",
+ "sha256:c1382209a771ca8a25fe89d4a2377875538c6ed3cf8745280e65636cbd0988f2",
+ "sha256:d80db278a07f51811dbf0f9c31ff7cd5b2501822fb7a7587e71f9ff27d5c04bd",
+ "sha256:db874c65654465ef71d6e8618bed8c725722bc90624132b9512bf061abb4eec0",
+ "sha256:e4c072cf4260063ebadc70e34d622fa1127a88e364475ed757709e249ebe990f",
+ "sha256:f69a56e958ecb549ba84e0497a438080932b4d52ded441cec04d80afde71dc0a"
],
"index": "pypi",
- "version": "==4.4.0.44"
+ "version": "==4.4.0.46"
},
"pillow": {
"hashes": [
@@ -239,17 +260,17 @@
},
"python-engineio": {
"hashes": [
- "sha256:36b33c6aa702d9b6a7f527eec6387a2da1a9a24484ec2f086d76576413cef04b",
- "sha256:cfded18156862f94544a9f8ef37f56727df731c8552d7023f5afee8369be2db6"
+ "sha256:33f7a214be5db35c867e97027bfe63676cb003d82aa17a607612b25ba5d84e5b",
+ "sha256:9f34afa4170f5ba6e3d9ff158752ccf8fbb2145f16554b2f0fc84646675be99a"
],
- "version": "==3.13.2"
+ "version": "==4.0.0"
},
"python-socketio": {
"hashes": [
- "sha256:358d8fbbc029c4538ea25bcaa283e47f375be0017fcba829de8a3a731c9df25a",
- "sha256:d437f797c44b6efba2f201867cf02b8c96b97dff26d4e4281ac08b45817cd522"
+ "sha256:05d77e7392d261d26bd4a513c35af2678caef10b68f5ab47918c6febfc9cd690",
+ "sha256:3794412ea577144ce1bc8f258deb265230da1d9d1ff5427c32fe7e8de484c275"
],
- "version": "==4.6.0"
+ "version": "==5.0.3"
},
"redis": {
"hashes": [
@@ -269,10 +290,11 @@
},
"smbus2": {
"hashes": [
- "sha256:210e66eebe4d0b1fe836b3ec2751841942e1c4918c0b429b20a0e20a222228b4"
+ "sha256:1b5c690715e1efab39e41038147dfba75afc72a10f76b3f0310d783e9f8a83a6",
+ "sha256:39a8d5f84b3b07ced0dd411f8c3d62c6aed93a2f06b270309dd41e82973e2f55"
],
"index": "pypi",
- "version": "==0.3.0"
+ "version": "==0.4.0"
},
"werkzeug": {
"hashes": [
diff --git a/app.py b/app.py
index 379268b..2b025f6 100644
--- a/app.py
+++ b/app.py
@@ -1,20 +1,13 @@
+import logging
import base64
import json
-from pprint import pprint
-from threading import Thread
-# import eventlet
-# eventlet.monkey_patch()
-from typing import List
-
-from flask import Flask, jsonify, send_file, Response
+from flask import Flask, send_file, jsonify
from flask_socketio import SocketIO
-
from redis import Redis
-# from control.camera import Camera
-# from control.walle import WallE
-
+logging.basicConfig(level=logging.DEBUG)
+LOG = logging.getLogger(__name__)
DATA_URL_PREFIX = 'data:image/jpeg;base64,'
# DEBUG = True
@@ -24,9 +17,6 @@ sio = SocketIO(app)
redisdb = Redis()
-# camera = Camera()
-# walle = WallE()
-
class CameraListener:
def __init__(self):
@@ -59,10 +49,24 @@ def index(path):
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")
def camera_message(directions):
# 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({
'force': directions['force'],
'angle': directions['angle'],
@@ -72,7 +76,7 @@ def camera_message(directions):
@sio.on("move")
def move_message(directions):
# 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({
'force': directions['force'],
'angle': directions['angle'],
diff --git a/client/package.json b/client/package.json
index c8f5404..322c3a9 100644
--- a/client/package.json
+++ b/client/package.json
@@ -9,8 +9,10 @@
"buildw": "vue-cli-service build --watch"
},
"dependencies": {
+ "axios": "^0.21.1",
"core-js": "^3.6.4",
"nipplejs": "^0.8.5",
+ "socket.io-client": "^3.0.4",
"vue": "^2.6.11",
"vue-router": "^3.2.0",
"vue-slider-component": "^3.2.5",
diff --git a/client/src/App.vue b/client/src/App.vue
index a811b0a..917c198 100644
--- a/client/src/App.vue
+++ b/client/src/App.vue
@@ -3,7 +3,8 @@
Toggle Full Screen
|
Camera |
-
Control
+
Control |
+
Sounds
diff --git a/client/src/main.js b/client/src/main.js
index f6cd920..f7fd900 100644
--- a/client/src/main.js
+++ b/client/src/main.js
@@ -1,11 +1,12 @@
import Vue from 'vue';
import VueSocketIO from 'vue-socket.io';
+import SocketIO from 'socket.io-client';
import App from './App.vue';
import router from './router';
Vue.use(new VueSocketIO({
debug: true,
- connection: `${window.location.protocol}//${window.location.host}`,
+ connection: SocketIO(`${window.location.protocol}//${window.location.host}`),
options: {
transport: 'websocket',
},
diff --git a/client/src/router/index.js b/client/src/router/index.js
index dd156ca..25a4746 100644
--- a/client/src/router/index.js
+++ b/client/src/router/index.js
@@ -18,6 +18,11 @@ const routes = [
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "control" */ '../views/Control.vue'),
},
+ {
+ path: '/sounds',
+ name: 'Sounds',
+ component: () => import(/* webpackChunkName: "sounds" */'../views/SoundBoard.vue'),
+ },
];
const router = new VueRouter({
diff --git a/client/src/views/SoundBoard.vue b/client/src/views/SoundBoard.vue
new file mode 100644
index 0000000..3f976b3
--- /dev/null
+++ b/client/src/views/SoundBoard.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
diff --git a/client/yarn.lock b/client/yarn.lock
index 2bf63b6..1d75842 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -836,6 +836,11 @@
resolved "https://registry.npm.taobao.org/@types/color-name/download/@types/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
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@*":
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"
@@ -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"
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:
version "10.1.0"
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"
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:
version "0.1.5"
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"
integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=
-component-emitter@^1.2.1:
+component-emitter@^1.2.1, component-emitter@~1.3.0:
version "1.3.0"
- resolved "https://registry.npm.taobao.org/component-emitter/download/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
- integrity sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=
+ resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
+ integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
component-inherit@0.0.3:
version "0.0.3"
@@ -3233,6 +3250,22 @@ engine.io-client@~3.4.0:
xmlhttprequest-ssl "~1.5.4"
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:
version "2.2.0"
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"
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:
version "4.1.1"
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:
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:
version "1.0.2"
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:
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:
version "0.0.5"
resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a"
@@ -6365,6 +6415,11 @@ parseuri@0.0.5:
dependencies:
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:
version "1.3.3"
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"
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:
version "3.3.0"
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"
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:
version "1.4.0"
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:
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:
version "1.5.5"
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
diff --git a/control/audio.py b/control/audio.py
index 5defe5e..a8d9a7f 100644
--- a/control/audio.py
+++ b/control/audio.py
@@ -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 logging
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
+from pyaudio import PyAudio, Stream, paContinue
-logger = logging.getLogger(__name__)
+LOG = logging.getLogger(__name__)
CHUNK_SIZE = 1024
INTERVAL_QUEUE = 0.1
-INTERVAL_STREAM_FAST = 0.0001
-INTERVAL_STREAM_SLOW = 0.001
+INTERVAL_STREAM = 0.1
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
- stream: Stream
- cvstate: Optional['audioop.RatecvState']
- actual_size: int = None
+ stream: Optional[Stream]
+
+ 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:
@@ -53,7 +44,7 @@ class AudioSystem:
self.default_sample_rate = int(self.default_output_device['defaultSampleRate'])
self.scheduler = scheduler
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:
self.sound_data = json.load(f)
self.sound_dir = os.path.realpath(self.sound_data['sound_dir'])
@@ -75,50 +66,45 @@ class AudioSystem:
# drain the queue
while True:
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
+ entry = PlayEntry(wave_file)
stream = self.audio.open(
format=self.audio.get_format_from_width(wave_file.getsampwidth()),
channels=wave_file.getnchannels(),
- rate=self.default_sample_rate,
+ rate=wave_file.getframerate(),
output=True,
+ stream_callback=entry.pyaudio_callback,
)
+ entry.set_stream(stream)
entry_id = uuid.uuid4()
while entry_id in self.playing:
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()
except Empty:
pass
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:
- logger.error('Fatal error in opening stream', exc_info=e)
+ LOG.error('Fatal error in opening stream', exc_info=e)
raise e
- def fill_streams(self):
- self.scheduler.enter(INTERVAL_STREAM_FAST if len(self.playing) > 0 else INTERVAL_STREAM_SLOW,
- 1, self.fill_streams)
+ def stream_cleanup(self):
+ self.scheduler.enter(INTERVAL_STREAM, 1, self.stream_cleanup)
finished_streams = []
for stream_id, entry in self.playing.items(): # type: uuid.UUID, PlayEntry
try:
- if entry.stream.get_write_available() < (CHUNK_SIZE * 1.5):
- continue # not enough space in buffer, wait for next iteration
- 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)
+ if entry.stream.is_active():
+ continue
else:
- logger.debug("Done playing sound, shutting down stream")
+ LOG.debug("Done playing sound, shutting down stream")
entry.stream.stop_stream()
entry.stream.close()
entry.wave_file.close()
finished_streams.append(stream_id)
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
for finished in finished_streams:
del self.playing[finished]
diff --git a/control/camera.py b/control/camera.py
index d253ebc..c141813 100644
--- a/control/camera.py
+++ b/control/camera.py
@@ -20,27 +20,3 @@ class Camera:
byte_stream = BytesIO()
jpg.save(byte_stream, 'JPEG')
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'])
diff --git a/control/dc_motor_controller.py b/control/dc_motor_controller.py
index c250c16..be80cc4 100644
--- a/control/dc_motor_controller.py
+++ b/control/dc_motor_controller.py
@@ -1,10 +1,12 @@
import atexit
+import logging
try:
from RPi import GPIO
except ImportError:
from .mockGpio import GPIO
+LOG = logging.getLogger(__name__)
PWM_FREQUENCY = 10_000 # 10 KHz
MOTORS = []
@@ -17,7 +19,7 @@ def setup_gpio():
def cleanup_gpio():
- print("cleaning up")
+ LOG.info("cleaning up")
for motor in MOTORS:
# noinspection PyProtectedMember
motor._stop()
@@ -49,7 +51,7 @@ class DcMotor:
self._set_reverse(val)
absval = abs(val)
if absval > 1:
- print(f"clipping {val} to 1")
+ logging.warning(f"clipping {val} to 1")
absval = 1
self.pwm_enable.ChangeDutyCycle(absval * 100)
diff --git a/tickworker.py b/tickworker.py
index a96d189..481cee3 100644
--- a/tickworker.py
+++ b/tickworker.py
@@ -1,23 +1,30 @@
import sched
import time
from threading import Thread
+from multiprocessing import Queue
+from queue import Empty, Full
import json
+import logging
from redis import Redis
from control.walle import WallE
from control.audio import AudioSystem
+logging.basicConfig(level=logging.DEBUG)
+
INTERVAL_TICK = 0.025 # 40/s
+sound_action_queue = Queue()
-class EventListener(Thread):
+
+class RedisConnection(Thread):
def __init__(self):
- super(EventListener, self).__init__()
+ super(RedisConnection, self).__init__()
self.keep_running = True
self.dbcon = Redis()
self.pubsub = self.dbcon.pubsub()
- self.pubsub.subscribe(['move', 'look'])
+ self.pubsub.subscribe(['move', 'look', 'sound'])
def run(self) -> None:
while self.keep_running:
@@ -45,9 +52,17 @@ class EventListener(Thread):
walle.set_movement(data_dict['angle'], data_dict['force'])
elif channel == 'look':
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:
print(f'Unknown channel {channel}')
+ def store_audio(self, sounds):
+ self.dbcon.set('sound_data', json.dumps(sounds))
+
scheduler = sched.scheduler()
audio = AudioSystem(scheduler, 'sounds.json')
@@ -58,6 +73,13 @@ walle.setup()
def walle_tick():
scheduler.enter(INTERVAL_TICK, 3, 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)
@@ -66,10 +88,12 @@ audio.queue_sound('walle.boot')
if __name__ == '__main__':
redisdb = Redis()
- event_listener = EventListener()
- event_listener.start()
+ redis_connection = RedisConnection()
+ redis_connection.store_audio([{'name': sound['name'], 'title': sound.get('title', sound['name'])}
+ for sound in audio.sound_data['sounds']])
+ redis_connection.start()
try:
scheduler.run()
except KeyboardInterrupt:
- event_listener.stop()
+ redis_connection.stop()
raise