Beispiel #1
0
class SpeechSynthesizer(object):
    STATES = {'PLAYING', 'FINISHED'}

    def __init__(self, alexa):
        self.alexa = alexa
        self.token = ''
        self.state = 'FINISHED'
        self.finished = threading.Event()

        self.player = Player()
        self.player.add_callback('eos', self.SpeechFinished)
        self.player.add_callback('error', self.SpeechFinished)

    def stop(self):
        self.finished.set()
        self.player.stop()
        self.state = 'FINISHED'

    # {
    #     "directive": {
    #         "header": {
    #             "namespace": "SpeechSynthesizer",
    #             "name": "Speak",
    #             "messageId": "{{STRING}}",
    #             "dialogRequestId": "{{STRING}}"
    #         },
    #         "payload": {
    #             "url": "{{STRING}}",
    #             "format": "AUDIO_MPEG",
    #             "token": "{{STRING}}"
    #         }
    #     }
    # }
    # Content-Type: application/octet-stream
    # Content-ID: {{Audio Item CID}}
    # {{BINARY AUDIO ATTACHMENT}}
    def Speak(self, directive):
        # directive from dueros may not have the dialogRequestId
        if 'dialogRequestId' in directive['header']:
            dialog_request_id = directive['header']['dialogRequestId']
            if self.alexa.SpeechRecognizer.dialog_request_id != dialog_request_id:
                return

        self.token = directive['payload']['token']
        url = directive['payload']['url']
        if url.startswith('cid:'):
            filename = base64.urlsafe_b64encode(url[4:])
            filename = hashlib.md5(filename).hexdigest()
            mp3_file = os.path.join(tempfile.gettempdir(), filename + '.mp3')
            if os.path.isfile(mp3_file):
                self.finished.clear()
                # os.system('mpv "{}"'.format(mp3_file))
                self.player.play('file://{}'.format(mp3_file))
                self.SpeechStarted()

                self.alexa.state_listener.on_speaking()

                # will be set at SpeechFinished() if the player reaches the End Of Stream or gets a error
                self.finished.wait()

                os.system('rm -rf "{}"'.format(mp3_file))

    def SpeechStarted(self):
        self.state = 'PLAYING'
        event = {
            "header": {
                "namespace": "SpeechSynthesizer",
                "name": "SpeechStarted",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token
            }
        }
        self.alexa.send_event(event)

    def SpeechFinished(self):
        self.alexa.state_listener.on_finished()

        self.finished.set()
        self.state = 'FINISHED'
        event = {
            "header": {
                "namespace": "SpeechSynthesizer",
                "name": "SpeechFinished",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token
            }
        }
        self.alexa.send_event(event)

    @property
    def context(self):
        offset = self.player.position if self.state == 'PLAYING' else 0

        return {
            "header": {
                "namespace": "SpeechSynthesizer",
                "name": "SpeechState"
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": offset,
                "playerActivity": self.state
            }
        }
Beispiel #2
0
class AudioPlayer(object):
    STATES = {
        'IDLE', 'PLAYING', 'STOPPED', 'PAUSED', 'BUFFER_UNDERRUN', 'FINISHED'
    }

    def __init__(self, alexa):
        self.alexa = alexa
        self.token = ''
        self.state = 'IDLE'

        self.player = Player()
        self.player.add_callback('eos', self.PlaybackFinished)
        self.player.add_callback('error', self.PlaybackFailed)

    # {
    #     "directive": {
    #         "header": {
    #             "namespace": "AudioPlayer",
    #             "name": "Play",
    #             "messageId": "{{STRING}}",
    #             "dialogRequestId": "{{STRING}}"
    #         },
    #         "payload": {
    #             "playBehavior": "{{STRING}}",
    #             "audioItem": {
    #                 "audioItemId": "{{STRING}}",
    #                 "stream": {
    #                     "url": "{{STRING}}",
    #                     "streamFormat": "AUDIO_MPEG"
    #                     "offsetInMilliseconds": {{LONG}},
    #                     "expiryTime": "{{STRING}}",
    #                     "progressReport": {
    #                         "progressReportDelayInMilliseconds": {{LONG}},
    #                         "progressReportIntervalInMilliseconds": {{LONG}}
    #                     },
    #                     "token": "{{STRING}}",
    #                     "expectedPreviousToken": "{{STRING}}"
    #                 }
    #             }
    #         }
    #     }
    # }
    def Play(self, directive):
        behavior = directive['payload']['playBehavior']
        self.token = directive['payload']['audioItem']['stream']['token']
        audio_url = directive['payload']['audioItem']['stream']['url']
        if audio_url.startswith('cid:'):
            mp3_file = os.path.join(tempfile.gettempdir(),
                                    audio_url[4:] + '.mp3')
            if os.path.isfile(mp3_file):
                # os.system('mpv "{}"'.format(mp3_file))
                # os.system('rm -rf "{}"'.format(mp3_file))
                self.player.play('file://{}'.format(mp3_file))
                self.PlaybackStarted()
        else:
            # os.system('mpv {}'.format(audio_url))
            self.player.play(audio_url)
            self.PlaybackStarted()

    def PlaybackStarted(self):
        self.state = 'PLAYING'

        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackStarted",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": self.player.position
            }
        }
        self.alexa.send_event(event)

    def PlaybackNearlyFinished(self):
        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackNearlyFinished",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": self.player.position
            }
        }
        self.alexa.send_event(event)

    def ProgressReportDelayElapsed(self):
        pass

    def ProgressReportIntervalElapsed(self):
        pass

    def PlaybackStutterStarted(self):
        pass

    def PlaybackStutterFinished(self):
        pass

    def PlaybackFinished(self):
        self.state = 'FINISHED'

        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackFinished",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": self.player.position
            }
        }
        self.alexa.send_event(event)

    def PlaybackFailed(self):
        self.state = 'STOPPED'

    # {
    #     "directive": {
    #         "header": {
    #             "namespace": "AudioPlayer",
    #             "name": "Stop",
    #             "messageId": "{{STRING}}",
    #             "dialogRequestId": "{{STRING}}"
    #         },
    #         "payload": {
    #         }
    #     }
    # }
    def Stop(self, directive):
        self.player.stop()
        self.PlaybackStopped()

    def PlaybackStopped(self):
        self.state = 'STOPPED'
        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackStopped",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": self.player.position
            }
        }
        self.alexa.send_event(event)

    def pause(self):
        self.player.pause()
        self.PlaybackPaused()

    def PlaybackPaused(self):
        self.state = 'PAUSED'
        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackPaused",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": self.player.position
            }
        }
        self.alexa.send_event(event)

    def resume(self):
        self.player.resume()
        self.PlaybackResumed()

    def PlaybackResumed(self):
        self.state = 'PLAYING'
        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackResumed",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": self.player.position
            }
        }
        self.alexa.send_event(event)

    # {
    #     "directive": {
    #         "header": {
    #             "namespace": "AudioPlayer",
    #             "name": "ClearQueue",
    #             "messageId": "{{STRING}}",
    #             "dialogRequestId": "{{STRING}}"
    #         },
    #         "payload": {
    #             "clearBehavior": "{{STRING}}"
    #         }
    #     }
    # }
    def ClearQueue(self, directive):
        self.PlaybackQueueCleared()
        behavior = directive['payload']['clearBehavior']
        if behavior == 'CLEAR_ALL':
            self.player.stop()
        elif behavior == 'CLEAR_ENQUEUED':
            pass

    def PlaybackQueueCleared(self):
        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackQueueCleared",
                "messageId": uuid.uuid4().hex
            },
            "payload": {}
        }
        self.alexa.send_event(event)

    def StreamMetadataExtracted(self):
        pass

    @property
    def context(self):
        if self.state != 'PLAYING':
            offset = 0
        else:
            offset = self.player.position

        return {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackState"
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": offset,
                "playerActivity": self.state
            }
        }
Beispiel #3
0
class AudioPlayer(object):
    STATES = {
        'IDLE', 'PLAYING', 'STOPPED', 'PAUSED', 'BUFFER_UNDERRUN', 'FINISHED'
    }

    def __init__(self, alexa):
        self.alexa = alexa
        self.token = ''
        self.state = 'IDLE'

        self.player = Player()
        self.player.add_callback('eos', self.PlaybackFinished)
        self.player.add_callback('error', self.PlaybackFailed)

    # {
    #     "directive": {
    #         "header": {
    #             "namespace": "AudioPlayer",
    #             "name": "Play",
    #             "messageId": "{{STRING}}",
    #             "dialogRequestId": "{{STRING}}"
    #         },
    #         "payload": {
    #             "playBehavior": "{{STRING}}",
    #             "audioItem": {
    #                 "audioItemId": "{{STRING}}",
    #                 "stream": {
    #                     "url": "{{STRING}}",
    #                     "streamFormat": "AUDIO_MPEG"
    #                     "offsetInMilliseconds": {{LONG}},
    #                     "expiryTime": "{{STRING}}",
    #                     "progressReport": {
    #                         "progressReportDelayInMilliseconds": {{LONG}},
    #                         "progressReportIntervalInMilliseconds": {{LONG}}
    #                     },
    #                     "token": "{{STRING}}",
    #                     "expectedPreviousToken": "{{STRING}}"
    #                 }
    #             }
    #         }
    #     }
    # }
    def Play(self, directive):
        if self.alexa.SpeechSynthesizer.state == 'PLAYING':
            self.alexa.SpeechSynthesizer.wait()

        behavior = directive['payload']['playBehavior']
        self.token = directive['payload']['audioItem']['stream']['token']
        audio_url = get_audio_url(
            directive['payload']['audioItem']['stream']['url'])

        self.player.play(audio_url)
        self.PlaybackStarted()

        logger.info('audio player is playing')

    def PlaybackStarted(self):
        self.state = 'PLAYING'

        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackStarted",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": self.player.position
            }
        }

        self.alexa.send_event(event)

    def PlaybackNearlyFinished(self):
        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackNearlyFinished",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": self.player.position
            }
        }
        self.alexa.send_event(event)

    def ProgressReportDelayElapsed(self):
        pass

    def ProgressReportIntervalElapsed(self):
        pass

    def PlaybackStutterStarted(self):
        pass

    def PlaybackStutterFinished(self):
        pass

    def PlaybackFinished(self):
        self.state = 'FINISHED'
        logger.info('playback is finished')

        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackFinished",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": self.player.position
            }
        }
        self.alexa.send_event(event)

    def PlaybackFailed(self):
        self.state = 'STOPPED'

    # {
    #     "directive": {
    #         "header": {
    #             "namespace": "AudioPlayer",
    #             "name": "Stop",
    #             "messageId": "{{STRING}}",
    #             "dialogRequestId": "{{STRING}}"
    #         },
    #         "payload": {
    #         }
    #     }
    # }
    def Stop(self, directive):
        if self.state == 'PLAYING' or self.state == 'PAUSED':
            self.player.stop()
            self.PlaybackStopped()

            logger.info('audio player is stoped')

    def PlaybackStopped(self):
        self.state = 'STOPPED'
        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackStopped",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": self.player.position
            }
        }
        self.alexa.send_event(event)

    def pause(self):
        self.player.pause()
        self.PlaybackPaused()

        logger.info('audio player is paused')

    def PlaybackPaused(self):
        self.state = 'PAUSED'
        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackPaused",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": self.player.position
            }
        }
        self.alexa.send_event(event)

    def resume(self):
        self.player.resume()
        self.PlaybackResumed()

        logger.info('audio player is resumed')

    def PlaybackResumed(self):
        self.state = 'PLAYING'
        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackResumed",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": self.player.position
            }
        }
        self.alexa.send_event(event)

    # {
    #     "directive": {
    #         "header": {
    #             "namespace": "AudioPlayer",
    #             "name": "ClearQueue",
    #             "messageId": "{{STRING}}",
    #             "dialogRequestId": "{{STRING}}"
    #         },
    #         "payload": {
    #             "clearBehavior": "{{STRING}}"
    #         }
    #     }
    # }
    def ClearQueue(self, directive):
        self.PlaybackQueueCleared()
        behavior = directive['payload']['clearBehavior']
        if behavior == 'CLEAR_ALL':
            self.player.stop()
        elif behavior == 'CLEAR_ENQUEUED':
            pass

    def PlaybackQueueCleared(self):
        event = {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackQueueCleared",
                "messageId": uuid.uuid4().hex
            },
            "payload": {}
        }
        self.alexa.send_event(event)

    def StreamMetadataExtracted(self):
        pass

    @property
    def context(self):
        if self.state != 'PLAYING':
            offset = 0
        else:
            offset = self.player.position

        return {
            "header": {
                "namespace": "AudioPlayer",
                "name": "PlaybackState"
            },
            "payload": {
                "token": self.token,
                "offsetInMilliseconds": offset,
                "playerActivity": self.state
            }
        }
Beispiel #4
0
class Alerts(object):
    STATES = {'IDLE', 'FOREGROUND', 'BACKGROUND'}

    def __init__(self, alexa):
        self.alexa = alexa
        self.player = Player()

        self.player.add_callback('eos', self._stop)
        self.player.add_callback('error', self._stop)

        alarm = os.path.realpath(
            os.path.join(os.path.dirname(__file__), '../resources/alarm.mp3'))
        self.alarm_uri = 'file://{}'.format(alarm)

        self.all_alerts = {}
        self.active_alerts = {}

        self.state = 'IDLE'
        self.end_event = Event()

    def _stop(self):
        """
        Stop all active alerts
        """
        for token in self.active_alerts.keys():
            self.AlertStopped(token)

        self.active_alerts = {}
        self.state = 'IDLE'

        self.end_event.set()

    def stop(self):
        self.player.stop()
        self._stop()

    def enter_background(self):
        if self.state == 'FOREGROUND':
            self.state = 'BACKGROUND'
            self.player.pause()

    def enter_foreground(self):
        if self.state == 'BACKGROUND':
            self.state = 'FOREGROUND'
            self.player.resume()

    def _start_alert(self, token):
        if token in self.all_alerts:
            while self.alexa.SpeechRecognizer.conversation:
                time.sleep(1)

            if self.alexa.AudioPlayer.state == 'PLAYING':
                self.alexa.AudioPlayer.pause()

            self.AlertStarted(token)

            self.end_event.clear()

            # TODO: repeat play alarm until user stops it or timeout
            self.player.play(self.alarm_uri)

            if not self.end_event.wait(timeout=600):
                self.player.stop()

            if not self.alexa.SpeechRecognizer.conversation:
                if self.alexa.AudioPlayer.state == 'PAUSED':
                    self.alexa.AudioPlayer.resume()

    # {
    #     "directive": {
    #         "header": {
    #             "namespace": "Alerts",
    #             "name": "SetAlert",
    #             "messageId": "{{STRING}}",
    #             "dialogRequestId": "{{STRING}}"
    #         },
    #         "payload": {
    #             "token": "{{STRING}}",
    #             "type": "{{STRING}}",
    #             "scheduledTime": "2017-08-07T09:02:58+0000",
    #         }
    #     }
    # }
    def SetAlert(self, directive):
        payload = directive['payload']
        token = payload['token']
        scheduled_time = dateutil.parser.parse(payload['scheduledTime'])

        # Update the alert
        if token in self.all_alerts:
            pass

        self.all_alerts[token] = payload

        interval = scheduled_time - datetime.datetime.now(
            scheduled_time.tzinfo)
        Timer(interval.seconds, self._start_alert, (token, )).start()

        self.SetAlertSucceeded(token)

    def SetAlertSucceeded(self, token):
        event = {
            "header": {
                "namespace": "Alerts",
                "name": "SetAlertSucceeded",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": token
            }
        }

        self.alexa.send_event(event)

    def SetAlertFailed(self, token):
        event = {
            "header": {
                "namespace": "Alerts",
                "name": "SetAlertFailed",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": token
            }
        }

        self.alexa.send_event(event)

    # {
    #     "directive": {
    #         "header": {
    #             "namespace": "Alerts",
    #             "name": "DeleteAlert",
    #             "messageId": "{{STRING}}",
    #             "dialogRequestId": "{{STRING}}"
    #         },
    #         "payload": {
    #             "token": "{{STRING}}"
    #         }
    #     }
    # }
    def DeleteAlert(self, directive):
        token = directive['payload']['token']

        if token in self.active_alerts:
            self.AlertStopped(token)

        if token in self.all_alerts:
            del self.all_alerts[token]

        self.DeleteAlertSucceeded(token)

    def DeleteAlertSucceeded(self, token):
        event = {
            "header": {
                "namespace": "Alerts",
                "name": "DeleteAlertSucceeded",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": token
            }
        }

        self.alexa.send_event(event)

    def DeleteAlertFailed(self, token):
        event = {
            "header": {
                "namespace": "Alerts",
                "name": "DeleteAlertFailed",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": token
            }
        }

        self.alexa.send_event(event)

    def AlertStarted(self, token):
        if self.state == 'IDLE':
            self.state = 'FOREGROUND'

        self.active_alerts[token] = self.all_alerts[token]

        event = {
            "header": {
                "namespace": "Alerts",
                "name": "AlertStarted",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": token
            }
        }

        self.alexa.send_event(event)

    def AlertStopped(self, token):
        if token in self.active_alerts:
            del self.active_alerts[token]

        if token in self.all_alerts:
            del self.all_alerts[token]

        if not self.active_alerts:
            self.state = 'IDLE'

        event = {
            "header": {
                "namespace": "Alerts",
                "name": "AlertStopped",
                "messageId": "{STRING}"
            },
            "payload": {
                "token": token
            }
        }

        self.alexa.send_event(event)

    def AlertEnteredForeground(self, token):
        event = {
            "header": {
                "namespace": "Alerts",
                "name": "AlertEnteredForeground",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": token
            }
        }

        self.alexa.send_event(event)

    def AlertEnteredBackground(self, token):
        event = {
            "header": {
                "namespace": "Alerts",
                "name": "AlertEnteredBackground",
                "messageId": uuid.uuid4().hex
            },
            "payload": {
                "token": token
            }
        }

        self.alexa.send_event(event)

    @property
    def context(self):
        return {
            "header": {
                "namespace": "Alerts",
                "name": "AlertsState"
            },
            "payload": {
                "allAlerts": list(self.all_alerts.values()),
                "activeAlerts": list(self.active_alerts.values())
            }
        }