Mercurial > code > home > repos > homeauto
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/service/playSound/playSound.py Thu Dec 19 17:33:24 2019 -0800 @@ -0,0 +1,102 @@ +""" +play sounds according to POST requests. +""" +from docopt import docopt +import cyclone.web +import os, sys, tempfile, itertools +from twisted.internet import reactor +from cyclone.httpclient import fetch +from twisted.web.static import File +from standardservice.logsetup import log, verboseLogging + +soundCount = itertools.count() + +class LOADING(object): pass + +class SoundEffects(object): + def __init__(self): + self.buffers = {} # URIRef : pygame.mixer.Sound + self.playingSources = [] + self.queued = [] + self.volume = 1 # level for the next sound that's played (or existing instances of the same sound) + + def _getSound(self, uri): + def done(resp): + path = '/tmp/sound_%s' % next(soundCount) + with open(path, 'w') as out: + out.write(resp.body) + log.info('write %s bytes to %s', len(resp.body), path) + self.buffers[uri] = pygame.mixer.Sound(path) + + return fetch(uri).addCallback(done).addErrback(log.error) + + def playEffect(self, uri): + if uri not in self.buffers: + self.buffers[uri] = LOADING + self._getSound(uri).addCallback(lambda ret: self.playEffect(uri)) + return + if self.buffers[uri] is LOADING: + # The first playback loads then plays, but any attempts + # during that load are dropped, not queued. + return + snd = self.buffers[uri] + snd.set_volume(self.volume) + return self.playBuffer(snd) + + def done(self, src): + try: + self.playingSources.remove(src) + except ValueError: + pass + + def stopAll(self): + while self.playingSources: + self.playingSources.pop().stop() + for q in self.queued: + q.cancel() + # doesn't cover the callLater ones + + +class Index(cyclone.web.RequestHandler): + def get(self): + self.render('index.html') + +class PlaySound(cyclone.web.RequestHandler): + def post(self): + uri = self.get_argument('uri') + self.settings.sfx.playEffect(uri) + return "ok" + +class Volume(cyclone.web.RequestHandler): + def put(self): + self.settings.sfx.setVolume(float(self.get_argument('v'))) + return "ok" + +class StopAll(cyclone.web.RequestHandler): + def post(self): + self.settings.sfx.stopAll() + return "ok" + + +if __name__ == '__main__': + arg = docopt(''' + Usage: playSound.py [options] + + -v Verbose + ''') + verboseLogging(arg['-v']) + + import pygame + print('mixer init pulse') + import pygame.mixer + pygame.mixer.init() + sfx = SoundEffects() + + reactor.listenTCP(9049, cyclone.web.Application(handlers=[ + (r'/', Index), + (r'/playSound', PlaySound), + (r'/volume', Volume), + (r'/stopAll', StopAll), + ], template_path='.', sfx=sfx)) + reactor.run() +server.app.run(endpoint_description=r"tcp6:port=9049:interface=\:\:")