feat: cleanup and rework audio system

This commit is contained in:
2020-12-22 16:50:30 +01:00
parent 57b92b2864
commit 56fa2a92a9
12 changed files with 302 additions and 161 deletions

138
Pipfile.lock generated
View File

@@ -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
View File

@@ -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'],

View File

@@ -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",

View File

@@ -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>

View File

@@ -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',
}, },

View File

@@ -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({

View 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>

View File

@@ -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"

View File

@@ -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]

View File

@@ -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'])

View File

@@ -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)

View File

@@ -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