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=\:\:")