|
|
|
@ -1,7 +1,6 @@ |
|
|
|
|
# This python file use the following encoding: utf-8 |
|
|
|
|
|
|
|
|
|
import os |
|
|
|
|
import sys |
|
|
|
|
import dbus |
|
|
|
|
from urllib.parse import unquote as url_unquote |
|
|
|
|
|
|
|
|
@ -24,14 +23,48 @@ __gadget_vapi__ = 2 |
|
|
|
|
__gadget_opts__ = {'popup_on_desktop': True} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# def DBG(msg): |
|
|
|
|
# print("AUDIO: %s" % msg) |
|
|
|
|
# sys.stdout.flush() |
|
|
|
|
def DBG(*args): |
|
|
|
|
import sys |
|
|
|
|
print("AUDIO:", *args) |
|
|
|
|
sys.stdout.flush() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_instance = None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PlayerBase(object): |
|
|
|
|
""" Define the interface that players must implement """ |
|
|
|
|
name = 'Player name' |
|
|
|
|
label = 'Player label' |
|
|
|
|
metadata = {} # metadata dict as per mpris2 specs |
|
|
|
|
playback_status = 'Stopped' # or 'Playing' or 'Paused' |
|
|
|
|
|
|
|
|
|
def play(self): |
|
|
|
|
raise NotImplemented |
|
|
|
|
|
|
|
|
|
def prev(self): |
|
|
|
|
raise NotImplemented |
|
|
|
|
|
|
|
|
|
def next(self): |
|
|
|
|
raise NotImplemented |
|
|
|
|
|
|
|
|
|
def rais(self): |
|
|
|
|
raise NotImplemented |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChannelBase(object): |
|
|
|
|
""" Define the interface that volume channels must implement """ |
|
|
|
|
name = 'Channel name' |
|
|
|
|
volumes = [0.0, 0.0] # left/right channels, range: 0.0 - 1.0 |
|
|
|
|
muted = True |
|
|
|
|
|
|
|
|
|
def volume_set(self, vols): |
|
|
|
|
raise NotImplemented |
|
|
|
|
|
|
|
|
|
def mute_toggle(self): |
|
|
|
|
raise NotImplemented |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Gadget(e.Gadget): |
|
|
|
|
|
|
|
|
|
def __init__(self): |
|
|
|
@ -67,20 +100,21 @@ class Gadget(e.Gadget): |
|
|
|
|
def speaker_update(self, speaker): |
|
|
|
|
if self.pulse and len(self.pulse.channels) > 0: |
|
|
|
|
ch = self.pulse.channels[0] |
|
|
|
|
left = right = ch.volume / 655 |
|
|
|
|
left, right = ch.volumes[0] * 100, ch.volumes[1] * 100 |
|
|
|
|
speaker.message_send(0, (ch.muted, left, right)) |
|
|
|
|
|
|
|
|
|
def speaker_wheel_cb(self, obj, sig, source): |
|
|
|
|
if self.pulse.channels: |
|
|
|
|
ch = self.pulse.channels[0] |
|
|
|
|
vol = ch.volume |
|
|
|
|
vol = (ch.volumes[0] + ch.volumes[1]) / 2 |
|
|
|
|
if sig == 'mouse,wheel,0,1': |
|
|
|
|
new_vol = vol - 3000 |
|
|
|
|
new_vol = vol - 0.03 |
|
|
|
|
elif sig == 'mouse,wheel,0,-1': |
|
|
|
|
new_vol = vol + 3000 |
|
|
|
|
new_vol = vol + 0.03 |
|
|
|
|
else: |
|
|
|
|
return |
|
|
|
|
ch.volume_set(min(max(0, new_vol), 65500)) |
|
|
|
|
new_vol = min(max(0.0, new_vol), 1.0) |
|
|
|
|
ch.volume_set([new_vol, new_vol]) |
|
|
|
|
|
|
|
|
|
def popup_created(self, elm_parent): |
|
|
|
|
# DBG("POPUP CREATED") |
|
|
|
@ -100,11 +134,11 @@ class Gadget(e.Gadget): |
|
|
|
|
main_box.data['players_box'] = players_box |
|
|
|
|
main_box.data['volumes_box'] = volumes_box |
|
|
|
|
|
|
|
|
|
# add all the available players to the popup edje box |
|
|
|
|
# add all the available mpris players to the popup edje box |
|
|
|
|
for player in self.mpris.players: |
|
|
|
|
self.popup_player_add(main_box, player) |
|
|
|
|
|
|
|
|
|
# add all the channel sliders |
|
|
|
|
# add all the pulse channels sliders |
|
|
|
|
if self.pulse.conn is not None: |
|
|
|
|
for ch in self.pulse.channels: |
|
|
|
|
self.popup_volume_add(main_box, ch) |
|
|
|
@ -162,7 +196,7 @@ class Gadget(e.Gadget): |
|
|
|
|
self.player_objs[player].append(o) |
|
|
|
|
|
|
|
|
|
def player_changed(self, player): |
|
|
|
|
# the mpris player has changed, update all the relative edje objects |
|
|
|
|
# the player has changed, update all the relative edje objects |
|
|
|
|
for o in self.player_objs.get(player, []): |
|
|
|
|
self.player_update(o, player) |
|
|
|
|
|
|
|
|
@ -213,11 +247,11 @@ class Gadget(e.Gadget): |
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
def popup_volume_add(self, popup, channel): |
|
|
|
|
sl = elm.Slider(popup, text=channel.name, min_max=(0, 65500), |
|
|
|
|
sl = elm.Slider(popup, text=channel.name, min_max=(0.0, 1.0), |
|
|
|
|
span_size=150, indicator_show=False, |
|
|
|
|
size_hint_expand=EXPAND_HORIZ, |
|
|
|
|
size_hint_fill=FILL_HORIZ) |
|
|
|
|
sl.value = channel.volume |
|
|
|
|
sl.value = (channel.volumes[0] + channel.volumes[1]) / 2 |
|
|
|
|
sl.disabled = True if channel.muted else False |
|
|
|
|
sl.callback_changed_add(self.popup_slider_changed_cb, channel) |
|
|
|
|
sl.callback_slider_drag_start_add(self.popup_slider_drag_start_cb) |
|
|
|
@ -236,7 +270,7 @@ class Gadget(e.Gadget): |
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
|
def popup_slider_changed_cb(slider, channel): |
|
|
|
|
channel.volume_set(slider.value) |
|
|
|
|
channel.volume_set([slider.value, slider.value]) |
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
|
def popup_slider_click_cb(slider, event, channel): |
|
|
|
@ -256,7 +290,7 @@ class Gadget(e.Gadget): |
|
|
|
|
if channel in self.channel_objs: |
|
|
|
|
for sl in self.channel_objs[channel]: |
|
|
|
|
if 'dragging' not in sl.data: |
|
|
|
|
sl.value = channel.volume |
|
|
|
|
sl.value = (channel.volumes[0] + channel.volumes[1]) / 2 |
|
|
|
|
# update all the speakers |
|
|
|
|
for speaker in self._instances: |
|
|
|
|
self.speaker_update(speaker) |
|
|
|
@ -318,7 +352,7 @@ class Mpris2_Client(object): |
|
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Mpris2_Player(object): |
|
|
|
|
class Mpris2_Player(PlayerBase): |
|
|
|
|
MAIN_IFACE = 'org.mpris.MediaPlayer2' |
|
|
|
|
PLAYER_IFACE = 'org.mpris.MediaPlayer2.Player' |
|
|
|
|
|
|
|
|
@ -362,21 +396,22 @@ class Mpris2_Player(object): |
|
|
|
|
self.proxy.Raise(dbus_interface=self.MAIN_IFACE) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AudioChannel(object): |
|
|
|
|
def __init__(self, obj, iface, name, volume, muted): |
|
|
|
|
class PulseAudioChannel(ChannelBase): |
|
|
|
|
def __init__(self, obj, iface, name, volumes, muted): |
|
|
|
|
self.obj = obj |
|
|
|
|
self.iface = iface |
|
|
|
|
self.name = name |
|
|
|
|
self.volume = int(volume[0]) |
|
|
|
|
self.volumes = [float(volumes[0]) / 65536, float(volumes[1]) / 65536] |
|
|
|
|
self.muted = muted |
|
|
|
|
|
|
|
|
|
# This do not work, only connection on the main pulse obj work... |
|
|
|
|
# so for the moment dispatch the callback from there |
|
|
|
|
# obj.connect_to_signal('VolumeUpdated', self.volume_changed_signal_cb) |
|
|
|
|
|
|
|
|
|
def volume_set(self, value): |
|
|
|
|
self.volume = value |
|
|
|
|
self.obj.Set(self.iface, 'Volume', [dbus.UInt32(value)], |
|
|
|
|
def volume_set(self, vols): # values 0.0 - 1.0 |
|
|
|
|
self.volumes = vols |
|
|
|
|
values = [dbus.UInt32(vols[0] * 65536), dbus.UInt32(vols[1] * 65536)] |
|
|
|
|
self.obj.Set(self.iface, 'Volume', values, |
|
|
|
|
dbus_interface=dbus.PROPERTIES_IFACE) |
|
|
|
|
|
|
|
|
|
def mute_toggle(self): |
|
|
|
@ -385,17 +420,13 @@ class AudioChannel(object): |
|
|
|
|
dbus_interface=dbus.PROPERTIES_IFACE) |
|
|
|
|
|
|
|
|
|
def volume_changed_signal_cb(self, volume): |
|
|
|
|
self.volume = int(volume[0]) |
|
|
|
|
self.volumes = [volume[0] / 65536, volume[1] / 65536] |
|
|
|
|
_instance.volume_changed(self) |
|
|
|
|
|
|
|
|
|
def mute_changed_signal_cb(self, muted): |
|
|
|
|
self.muted = muted |
|
|
|
|
_instance.mute_changed(self) |
|
|
|
|
|
|
|
|
|
def __str__(self): |
|
|
|
|
return '[%s]: "%s" volume: %s' % \ |
|
|
|
|
(self.iface.split('.')[-1], self.name, self.volume[:]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PulseAudio_Client(object): |
|
|
|
|
PULSE_OBJ = '/org/pulseaudio/core1' |
|
|
|
@ -541,8 +572,8 @@ class PulseAudio_Client(object): |
|
|
|
|
def stream_add(self, obj_path): |
|
|
|
|
try: |
|
|
|
|
obj = self.conn.get_object(self.STREAM_IFACE, obj_path) |
|
|
|
|
volume = obj.Get(self.STREAM_IFACE, 'Volume', |
|
|
|
|
dbus_interface=dbus.PROPERTIES_IFACE) |
|
|
|
|
volumes = obj.Get(self.STREAM_IFACE, 'Volume', |
|
|
|
|
dbus_interface=dbus.PROPERTIES_IFACE) |
|
|
|
|
mute = obj.Get(self.STREAM_IFACE, 'Mute', |
|
|
|
|
dbus_interface=dbus.PROPERTIES_IFACE) |
|
|
|
|
props = obj.Get(self.STREAM_IFACE, 'PropertyList', |
|
|
|
@ -555,7 +586,7 @@ class PulseAudio_Client(object): |
|
|
|
|
except: |
|
|
|
|
name = 'Unknown app' |
|
|
|
|
|
|
|
|
|
ch = AudioChannel(obj, self.STREAM_IFACE, name, volume, mute) |
|
|
|
|
ch = PulseAudioChannel(obj, self.STREAM_IFACE, name, volumes, mute) |
|
|
|
|
self.channels.append(ch) |
|
|
|
|
_instance.channel_added(ch) |
|
|
|
|
return ch |
|
|
|
@ -563,8 +594,8 @@ class PulseAudio_Client(object): |
|
|
|
|
def sink_add(self, obj_path): |
|
|
|
|
try: |
|
|
|
|
obj = self.conn.get_object(self.DEVICE_IFACE, obj_path) |
|
|
|
|
volume = obj.Get(self.DEVICE_IFACE, 'Volume', |
|
|
|
|
dbus_interface=dbus.PROPERTIES_IFACE) |
|
|
|
|
volumes = obj.Get(self.DEVICE_IFACE, 'Volume', |
|
|
|
|
dbus_interface=dbus.PROPERTIES_IFACE) |
|
|
|
|
mute = obj.Get(self.DEVICE_IFACE, 'Mute', |
|
|
|
|
dbus_interface=dbus.PROPERTIES_IFACE) |
|
|
|
|
props = obj.Get(self.DEVICE_IFACE, 'PropertyList', |
|
|
|
@ -580,7 +611,7 @@ class PulseAudio_Client(object): |
|
|
|
|
except: |
|
|
|
|
name = 'Unknown device' |
|
|
|
|
|
|
|
|
|
ch = AudioChannel(obj, self.DEVICE_IFACE, name, volume, mute) |
|
|
|
|
ch = PulseAudioChannel(obj, self.DEVICE_IFACE, name, volumes, mute) |
|
|
|
|
self.channels.append(ch) |
|
|
|
|
_instance.channel_added(ch) |
|
|
|
|
return ch |
|
|
|
|