1
0
Fork 0
mirror of https://github.com/Luzifer/streamdeck.git synced 2024-10-18 13:04:25 +00:00

Handle mute and absent devices

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2020-08-09 15:44:34 +02:00
parent 661edb591a
commit 2fd1279eb3
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E
2 changed files with 37 additions and 19 deletions

View file

@ -30,8 +30,10 @@ func (d displayElementPulseVolume) Display(ctx context.Context, idx int, attribu
sinkInputOK = sinkInputOK && sinkInputMatch != "" sinkInputOK = sinkInputOK && sinkInputMatch != ""
var ( var (
err error err error
volume float64 mute bool
notPresent bool
volume float64
) )
switch { switch {
@ -40,21 +42,35 @@ func (d displayElementPulseVolume) Display(ctx context.Context, idx int, attribu
return errors.New("Exactly one of 'sink' and 'sink_input' must be specified") return errors.New("Exactly one of 'sink' and 'sink_input' must be specified")
case sinkInputOK: case sinkInputOK:
volume, err = pulseClient.GetSinkInputVolume(sinkInputMatch) volume, mute, err = pulseClient.GetSinkInputVolume(sinkInputMatch)
case sinkOK: case sinkOK:
volume, err = pulseClient.GetSinkVolume(sinkMatch) volume, mute, err = pulseClient.GetSinkVolume(sinkMatch)
} }
if err != nil { if err == errPulseNoSuchDevice {
notPresent = true
} else if err != nil {
return errors.Wrap(err, "Unable to get volume") return errors.Wrap(err, "Unable to get volume")
} }
img := newTextOnImageRenderer() img := newTextOnImageRenderer()
var (
text = fmt.Sprintf("%.0f%%", volume*100)
textColor color.Color = color.RGBA{0xff, 0xff, 0xff, 0xff}
)
if notPresent {
text = "--"
textColor = color.RGBA{0xff, 0x0, 0x0, 0x0}
} else if mute {
text = "M"
textColor = color.RGBA{0xff, 0x0, 0x0, 0x0}
}
// Initialize color // Initialize color
var textColor color.Color = color.RGBA{0xff, 0xff, 0xff, 0xff}
if rgba, ok := attributes["color"].([]interface{}); ok { if rgba, ok := attributes["color"].([]interface{}); ok {
if len(rgba) != 4 { if len(rgba) != 4 {
return errors.New("RGBA color definition needs 4 hex values") return errors.New("RGBA color definition needs 4 hex values")
@ -85,7 +101,7 @@ func (d displayElementPulseVolume) Display(ctx context.Context, idx int, attribu
border = v border = v
} }
if err = img.DrawBigText(fmt.Sprintf("%.0f%%", volume*100), fontsize, border, textColor); err != nil { if err = img.DrawBigText(text, fontsize, border, textColor); err != nil {
return errors.Wrap(err, "Unable to draw text") return errors.Wrap(err, "Unable to draw text")
} }

View file

@ -21,6 +21,8 @@ func init() {
} }
} }
var errPulseNoSuchDevice = errors.New("No such device")
type pulseAudioClient struct { type pulseAudioClient struct {
client *pulse.Client client *pulse.Client
} }
@ -36,42 +38,42 @@ func newPulseAudioClient() (*pulseAudioClient, error) {
func (p pulseAudioClient) Close() { p.client.Close() } func (p pulseAudioClient) Close() { p.client.Close() }
func (p pulseAudioClient) GetSinkInputVolume(match string) (float64, error) { func (p pulseAudioClient) GetSinkInputVolume(match string) (float64, bool, error) {
m, err := regexp.Compile(match) m, err := regexp.Compile(match)
if err != nil { if err != nil {
return 0, errors.Wrap(err, "Unable to compile given match RegEx") return 0, false, errors.Wrap(err, "Unable to compile given match RegEx")
} }
var resp proto.GetSinkInputInfoListReply var resp proto.GetSinkInputInfoListReply
if err := p.client.RawRequest(&proto.GetSinkInputInfoList{}, &resp); err != nil { if err := p.client.RawRequest(&proto.GetSinkInputInfoList{}, &resp); err != nil {
return 0, errors.Wrap(err, "Unable to list sink inputs") return 0, false, errors.Wrap(err, "Unable to list sink inputs")
} }
for _, info := range resp { for _, info := range resp {
if !m.MatchString(info.MediaName) && !m.Match(info.Properties["application.name"]) { if !m.MatchString(info.MediaName) && !m.Match(info.Properties["application.name"]) || info.Corked {
continue continue
} }
sinkBase, err := p.getSinkBaseVolumeByIndex(info.SinkIndex) sinkBase, err := p.getSinkBaseVolumeByIndex(info.SinkIndex)
if err != nil { if err != nil {
return 0, errors.Wrap(err, "Unable to get sink base volume") return 0, false, errors.Wrap(err, "Unable to get sink base volume")
} }
return p.unifyChannelVolumes(info.ChannelVolumes) / sinkBase, nil return p.unifyChannelVolumes(info.ChannelVolumes) / sinkBase, info.Muted, nil
} }
return 0, errors.New("No such sink") return 0, false, errPulseNoSuchDevice
} }
func (p pulseAudioClient) GetSinkVolume(match string) (float64, error) { func (p pulseAudioClient) GetSinkVolume(match string) (float64, bool, error) {
m, err := regexp.Compile(match) m, err := regexp.Compile(match)
if err != nil { if err != nil {
return 0, errors.Wrap(err, "Unable to compile given match RegEx") return 0, false, errors.Wrap(err, "Unable to compile given match RegEx")
} }
var resp proto.GetSinkInfoListReply var resp proto.GetSinkInfoListReply
if err := p.client.RawRequest(&proto.GetSinkInfoList{}, &resp); err != nil { if err := p.client.RawRequest(&proto.GetSinkInfoList{}, &resp); err != nil {
return 0, errors.Wrap(err, "Unable to list sinks") return 0, false, errors.Wrap(err, "Unable to list sinks")
} }
for _, info := range resp { for _, info := range resp {
@ -79,10 +81,10 @@ func (p pulseAudioClient) GetSinkVolume(match string) (float64, error) {
continue continue
} }
return p.unifyChannelVolumes(info.ChannelVolumes) / float64(info.BaseVolume), nil return p.unifyChannelVolumes(info.ChannelVolumes) / float64(info.BaseVolume), info.Mute, nil
} }
return 0, errors.New("No such sink") return 0, false, errPulseNoSuchDevice
} }
func (p pulseAudioClient) getSinkBaseVolumeByIndex(idx uint32) (float64, error) { func (p pulseAudioClient) getSinkBaseVolumeByIndex(idx uint32) (float64, error) {