Mercurial > code > home > repos > homeauto
comparison service/playSound/playSound.py @ 1467:667794accec2
drop speech synth, try to get pulse client working
Ignore-this: f032ddd2169cbb668c11ed7d48f7de23
darcs-hash:66edd441656c5f73888aff4627729b33d416d586
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Thu, 19 Dec 2019 17:33:24 -0800 |
parents | |
children | 32f12665a620 |
comparison
equal
deleted
inserted
replaced
1466:bf05f33c3b3a | 1467:667794accec2 |
---|---|
1 """ | |
2 play sounds according to POST requests. | |
3 """ | |
4 from docopt import docopt | |
5 import cyclone.web | |
6 import os, sys, tempfile, itertools | |
7 from twisted.internet import reactor | |
8 from cyclone.httpclient import fetch | |
9 from twisted.web.static import File | |
10 from standardservice.logsetup import log, verboseLogging | |
11 | |
12 soundCount = itertools.count() | |
13 | |
14 class LOADING(object): pass | |
15 | |
16 class SoundEffects(object): | |
17 def __init__(self): | |
18 self.buffers = {} # URIRef : pygame.mixer.Sound | |
19 self.playingSources = [] | |
20 self.queued = [] | |
21 self.volume = 1 # level for the next sound that's played (or existing instances of the same sound) | |
22 | |
23 def _getSound(self, uri): | |
24 def done(resp): | |
25 path = '/tmp/sound_%s' % next(soundCount) | |
26 with open(path, 'w') as out: | |
27 out.write(resp.body) | |
28 log.info('write %s bytes to %s', len(resp.body), path) | |
29 self.buffers[uri] = pygame.mixer.Sound(path) | |
30 | |
31 return fetch(uri).addCallback(done).addErrback(log.error) | |
32 | |
33 def playEffect(self, uri): | |
34 if uri not in self.buffers: | |
35 self.buffers[uri] = LOADING | |
36 self._getSound(uri).addCallback(lambda ret: self.playEffect(uri)) | |
37 return | |
38 if self.buffers[uri] is LOADING: | |
39 # The first playback loads then plays, but any attempts | |
40 # during that load are dropped, not queued. | |
41 return | |
42 snd = self.buffers[uri] | |
43 snd.set_volume(self.volume) | |
44 return self.playBuffer(snd) | |
45 | |
46 def done(self, src): | |
47 try: | |
48 self.playingSources.remove(src) | |
49 except ValueError: | |
50 pass | |
51 | |
52 def stopAll(self): | |
53 while self.playingSources: | |
54 self.playingSources.pop().stop() | |
55 for q in self.queued: | |
56 q.cancel() | |
57 # doesn't cover the callLater ones | |
58 | |
59 | |
60 class Index(cyclone.web.RequestHandler): | |
61 def get(self): | |
62 self.render('index.html') | |
63 | |
64 class PlaySound(cyclone.web.RequestHandler): | |
65 def post(self): | |
66 uri = self.get_argument('uri') | |
67 self.settings.sfx.playEffect(uri) | |
68 return "ok" | |
69 | |
70 class Volume(cyclone.web.RequestHandler): | |
71 def put(self): | |
72 self.settings.sfx.setVolume(float(self.get_argument('v'))) | |
73 return "ok" | |
74 | |
75 class StopAll(cyclone.web.RequestHandler): | |
76 def post(self): | |
77 self.settings.sfx.stopAll() | |
78 return "ok" | |
79 | |
80 | |
81 if __name__ == '__main__': | |
82 arg = docopt(''' | |
83 Usage: playSound.py [options] | |
84 | |
85 -v Verbose | |
86 ''') | |
87 verboseLogging(arg['-v']) | |
88 | |
89 import pygame | |
90 print('mixer init pulse') | |
91 import pygame.mixer | |
92 pygame.mixer.init() | |
93 sfx = SoundEffects() | |
94 | |
95 reactor.listenTCP(9049, cyclone.web.Application(handlers=[ | |
96 (r'/', Index), | |
97 (r'/playSound', PlaySound), | |
98 (r'/volume', Volume), | |
99 (r'/stopAll', StopAll), | |
100 ], template_path='.', sfx=sfx)) | |
101 reactor.run() | |
102 server.app.run(endpoint_description=r"tcp6:port=9049:interface=\:\:") |