2017-10-04 06:28:44 +00:00
|
|
|
# Copyright 2017 Mycroft AI Inc.
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
#
|
2016-12-09 15:54:25 +00:00
|
|
|
import time
|
2017-04-10 14:20:01 +00:00
|
|
|
from os.path import abspath
|
2017-09-18 19:14:21 +00:00
|
|
|
|
Initial commit of audio service
Three backends have been added, mopidy, vlc and mpg123. Depending on uri type an apporpriate service is selected when media playback is requested. (for example mopidy service can handle spotify://... and local://...)
A playback Control skill can pause, resume, stop change track, etc any started media. So for example if the NPR news skill used this after starting playing the news the user can say "Hey Mycroft, pause" and the playback will pause. The playback control also handles stuff like lowering the volume of the playback if mycroft is asked another question.
Currently the different backend runs under the playbeck control, this was made most for convenience and the services should be moved in the future.
Usage:
The user needs to import the audioservice interface
`from mycroft.skills.audioservice import AudioService`
and initialize an instance in the skill `initialize` method
`self.audio_service = AudioService(self.emitter)`
Then playing an uri is as simple as
`self.audio_service.play(uri)`
TODO:
* Configuration (Alias for the different backends, service specific config, active services, etc.)
* Manual selection of backend (This is prepared in the audioservice interface biut not implemented)
* More feature complete audio service interface (playback control, get trackname etc)
* Separate audio services from the playback control
* Probably lots more
2016-12-02 06:12:21 +00:00
|
|
|
from mycroft.messagebus.message import Message
|
2017-12-18 23:24:21 +00:00
|
|
|
# Python 2+3 compatibility
|
|
|
|
from past.builtins import basestring
|
Initial commit of audio service
Three backends have been added, mopidy, vlc and mpg123. Depending on uri type an apporpriate service is selected when media playback is requested. (for example mopidy service can handle spotify://... and local://...)
A playback Control skill can pause, resume, stop change track, etc any started media. So for example if the NPR news skill used this after starting playing the news the user can say "Hey Mycroft, pause" and the playback will pause. The playback control also handles stuff like lowering the volume of the playback if mycroft is asked another question.
Currently the different backend runs under the playbeck control, this was made most for convenience and the services should be moved in the future.
Usage:
The user needs to import the audioservice interface
`from mycroft.skills.audioservice import AudioService`
and initialize an instance in the skill `initialize` method
`self.audio_service = AudioService(self.emitter)`
Then playing an uri is as simple as
`self.audio_service.play(uri)`
TODO:
* Configuration (Alias for the different backends, service specific config, active services, etc.)
* Manual selection of backend (This is prepared in the audioservice interface biut not implemented)
* More feature complete audio service interface (playback control, get trackname etc)
* Separate audio services from the playback control
* Probably lots more
2016-12-02 06:12:21 +00:00
|
|
|
|
|
|
|
|
2017-04-10 14:20:01 +00:00
|
|
|
def ensure_uri(s):
|
2017-04-11 18:20:45 +00:00
|
|
|
"""
|
|
|
|
Interprete paths as file:// uri's
|
|
|
|
|
|
|
|
Args:
|
|
|
|
s: string to be checked
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
if s is uri, s is returned otherwise file:// is prepended
|
|
|
|
"""
|
2017-04-10 14:20:01 +00:00
|
|
|
if '://' not in s:
|
|
|
|
return 'file://' + abspath(s)
|
|
|
|
else:
|
|
|
|
return s
|
|
|
|
|
|
|
|
|
Initial commit of audio service
Three backends have been added, mopidy, vlc and mpg123. Depending on uri type an apporpriate service is selected when media playback is requested. (for example mopidy service can handle spotify://... and local://...)
A playback Control skill can pause, resume, stop change track, etc any started media. So for example if the NPR news skill used this after starting playing the news the user can say "Hey Mycroft, pause" and the playback will pause. The playback control also handles stuff like lowering the volume of the playback if mycroft is asked another question.
Currently the different backend runs under the playbeck control, this was made most for convenience and the services should be moved in the future.
Usage:
The user needs to import the audioservice interface
`from mycroft.skills.audioservice import AudioService`
and initialize an instance in the skill `initialize` method
`self.audio_service = AudioService(self.emitter)`
Then playing an uri is as simple as
`self.audio_service.play(uri)`
TODO:
* Configuration (Alias for the different backends, service specific config, active services, etc.)
* Manual selection of backend (This is prepared in the audioservice interface biut not implemented)
* More feature complete audio service interface (playback control, get trackname etc)
* Separate audio services from the playback control
* Probably lots more
2016-12-02 06:12:21 +00:00
|
|
|
class AudioService():
|
2017-04-11 18:20:45 +00:00
|
|
|
"""
|
|
|
|
AudioService object for interacting with the audio subsystem
|
|
|
|
|
|
|
|
Args:
|
|
|
|
emitter: eventemitter or websocket object
|
|
|
|
"""
|
2017-09-18 19:14:21 +00:00
|
|
|
|
Initial commit of audio service
Three backends have been added, mopidy, vlc and mpg123. Depending on uri type an apporpriate service is selected when media playback is requested. (for example mopidy service can handle spotify://... and local://...)
A playback Control skill can pause, resume, stop change track, etc any started media. So for example if the NPR news skill used this after starting playing the news the user can say "Hey Mycroft, pause" and the playback will pause. The playback control also handles stuff like lowering the volume of the playback if mycroft is asked another question.
Currently the different backend runs under the playbeck control, this was made most for convenience and the services should be moved in the future.
Usage:
The user needs to import the audioservice interface
`from mycroft.skills.audioservice import AudioService`
and initialize an instance in the skill `initialize` method
`self.audio_service = AudioService(self.emitter)`
Then playing an uri is as simple as
`self.audio_service.play(uri)`
TODO:
* Configuration (Alias for the different backends, service specific config, active services, etc.)
* Manual selection of backend (This is prepared in the audioservice interface biut not implemented)
* More feature complete audio service interface (playback control, get trackname etc)
* Separate audio services from the playback control
* Probably lots more
2016-12-02 06:12:21 +00:00
|
|
|
def __init__(self, emitter):
|
|
|
|
self.emitter = emitter
|
2017-07-30 07:02:07 +00:00
|
|
|
self.emitter.on('mycroft.audio.service.track_info_reply',
|
|
|
|
self._track_info)
|
2016-12-09 15:54:25 +00:00
|
|
|
self.info = None
|
|
|
|
|
|
|
|
def _track_info(self, message=None):
|
2017-04-11 18:20:45 +00:00
|
|
|
"""
|
|
|
|
Handler for catching returning track info
|
|
|
|
"""
|
2016-12-18 08:43:55 +00:00
|
|
|
self.info = message.data
|
Initial commit of audio service
Three backends have been added, mopidy, vlc and mpg123. Depending on uri type an apporpriate service is selected when media playback is requested. (for example mopidy service can handle spotify://... and local://...)
A playback Control skill can pause, resume, stop change track, etc any started media. So for example if the NPR news skill used this after starting playing the news the user can say "Hey Mycroft, pause" and the playback will pause. The playback control also handles stuff like lowering the volume of the playback if mycroft is asked another question.
Currently the different backend runs under the playbeck control, this was made most for convenience and the services should be moved in the future.
Usage:
The user needs to import the audioservice interface
`from mycroft.skills.audioservice import AudioService`
and initialize an instance in the skill `initialize` method
`self.audio_service = AudioService(self.emitter)`
Then playing an uri is as simple as
`self.audio_service.play(uri)`
TODO:
* Configuration (Alias for the different backends, service specific config, active services, etc.)
* Manual selection of backend (This is prepared in the audioservice interface biut not implemented)
* More feature complete audio service interface (playback control, get trackname etc)
* Separate audio services from the playback control
* Probably lots more
2016-12-02 06:12:21 +00:00
|
|
|
|
2017-11-02 18:54:51 +00:00
|
|
|
def queue(self, tracks=None):
|
2017-10-23 06:34:37 +00:00
|
|
|
""" Queue up a track to playing playlist.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
tracks: track uri or list of track uri's
|
|
|
|
"""
|
2017-11-02 18:54:51 +00:00
|
|
|
tracks = tracks or []
|
2017-10-23 06:34:37 +00:00
|
|
|
if isinstance(tracks, basestring):
|
|
|
|
tracks = [tracks]
|
|
|
|
elif not isinstance(tracks, list):
|
|
|
|
raise ValueError
|
|
|
|
tracks = [ensure_uri(t) for t in tracks]
|
|
|
|
self.emitter.emit(Message('mycroft.audio.service.queue',
|
|
|
|
data={'tracks': tracks}))
|
|
|
|
|
2017-11-02 18:54:51 +00:00
|
|
|
def play(self, tracks=None, utterance=''):
|
2017-04-10 14:20:01 +00:00
|
|
|
""" Start playback.
|
2017-04-11 18:20:45 +00:00
|
|
|
|
|
|
|
Args:
|
|
|
|
tracks: track uri or list of track uri's
|
|
|
|
utterance: forward utterance for further processing by the
|
|
|
|
audio service.
|
2017-04-10 14:20:01 +00:00
|
|
|
"""
|
2017-11-02 18:54:51 +00:00
|
|
|
tracks = tracks or []
|
2017-04-08 09:28:48 +00:00
|
|
|
if isinstance(tracks, basestring):
|
|
|
|
tracks = [tracks]
|
|
|
|
elif not isinstance(tracks, list):
|
|
|
|
raise ValueError
|
2017-04-10 14:20:01 +00:00
|
|
|
tracks = [ensure_uri(t) for t in tracks]
|
2017-05-25 15:05:38 +00:00
|
|
|
self.emitter.emit(Message('mycroft.audio.service.play',
|
2016-12-18 08:43:55 +00:00
|
|
|
data={'tracks': tracks,
|
|
|
|
'utterance': utterance}))
|
Initial commit of audio service
Three backends have been added, mopidy, vlc and mpg123. Depending on uri type an apporpriate service is selected when media playback is requested. (for example mopidy service can handle spotify://... and local://...)
A playback Control skill can pause, resume, stop change track, etc any started media. So for example if the NPR news skill used this after starting playing the news the user can say "Hey Mycroft, pause" and the playback will pause. The playback control also handles stuff like lowering the volume of the playback if mycroft is asked another question.
Currently the different backend runs under the playbeck control, this was made most for convenience and the services should be moved in the future.
Usage:
The user needs to import the audioservice interface
`from mycroft.skills.audioservice import AudioService`
and initialize an instance in the skill `initialize` method
`self.audio_service = AudioService(self.emitter)`
Then playing an uri is as simple as
`self.audio_service.play(uri)`
TODO:
* Configuration (Alias for the different backends, service specific config, active services, etc.)
* Manual selection of backend (This is prepared in the audioservice interface biut not implemented)
* More feature complete audio service interface (playback control, get trackname etc)
* Separate audio services from the playback control
* Probably lots more
2016-12-02 06:12:21 +00:00
|
|
|
|
2017-11-01 23:57:54 +00:00
|
|
|
def stop(self):
|
|
|
|
""" Stop the track. """
|
|
|
|
self.emitter.emit(Message('mycroft.audio.service.stop'))
|
|
|
|
|
2017-04-10 14:20:01 +00:00
|
|
|
def next(self):
|
|
|
|
""" Change to next track. """
|
2017-05-25 15:05:38 +00:00
|
|
|
self.emitter.emit(Message('mycroft.audio.service.next'))
|
2017-04-10 14:20:01 +00:00
|
|
|
|
|
|
|
def prev(self):
|
|
|
|
""" Change to previous track. """
|
2017-05-25 15:05:38 +00:00
|
|
|
self.emitter.emit(Message('mycroft.audio.service.prev'))
|
2017-04-10 14:20:01 +00:00
|
|
|
|
|
|
|
def pause(self):
|
|
|
|
""" Pause playback. """
|
2017-05-25 15:05:38 +00:00
|
|
|
self.emitter.emit(Message('mycroft.audio.service.pause'))
|
2017-04-10 14:20:01 +00:00
|
|
|
|
|
|
|
def resume(self):
|
|
|
|
""" Resume paused playback. """
|
2017-05-25 15:05:38 +00:00
|
|
|
self.emitter.emit(Message('mycroft.audio.service.resume'))
|
2017-04-10 14:20:01 +00:00
|
|
|
|
2016-12-09 15:54:25 +00:00
|
|
|
def track_info(self):
|
2017-04-11 18:20:45 +00:00
|
|
|
""" Request information of current playing track.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Dict with track info.
|
|
|
|
"""
|
2016-12-09 15:54:25 +00:00
|
|
|
self.info = None
|
2017-05-25 15:05:38 +00:00
|
|
|
self.emitter.emit(Message('mycroft.audio.service.track_info'))
|
2017-07-30 07:02:07 +00:00
|
|
|
wait = 5.0
|
|
|
|
while self.info is None and wait >= 0:
|
2016-12-09 15:54:25 +00:00
|
|
|
time.sleep(0.1)
|
2017-07-30 07:02:07 +00:00
|
|
|
wait -= 0.1
|
|
|
|
|
|
|
|
return self.info or {}
|
2017-07-31 11:36:28 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def is_playing(self):
|
|
|
|
return self.track_info() != {}
|