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": {
"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": [

36
app.py
View File

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

View File

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

View File

@@ -3,7 +3,8 @@
<div id="nav">
<div class="nav-item toggle-button" @click="toggleFullScreen">Toggle Full Screen</div> |
<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>
<router-view/>
</div>

View File

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

View File

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

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

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

View File

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

View File

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

View File

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