Initial commit
This commit is contained in:
14
vendor/github.com/mafik/pulseaudio/.gitignore
generated
vendored
Normal file
14
vendor/github.com/mafik/pulseaudio/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
.idea/
|
||||
21
vendor/github.com/mafik/pulseaudio/LICENSE
generated
vendored
Normal file
21
vendor/github.com/mafik/pulseaudio/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Marek Rogalski
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
34
vendor/github.com/mafik/pulseaudio/README.md
generated
vendored
Normal file
34
vendor/github.com/mafik/pulseaudio/README.md
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# pulseaudio [](https://godoc.org/mrogalski.eu/go/pulseaudio)
|
||||
Package pulseaudio is a pure-Go (no libpulse) implementation of the PulseAudio native protocol.
|
||||
|
||||
Download:
|
||||
```shell
|
||||
go get mrogalski.eu/go/pulseaudio
|
||||
```
|
||||
|
||||
* * *
|
||||
Package pulseaudio is a pure-Go (no libpulse) implementation of the PulseAudio native protocol.
|
||||
|
||||
Rather than exposing the PulseAudio protocol directly this library attempts to hide
|
||||
the PulseAudio complexity behind a Go interface.
|
||||
Some of the things which are deliberately not exposed in the API are:
|
||||
|
||||
→ backwards compatibility for old PulseAudio servers
|
||||
|
||||
→ transport mechanism used for the connection (Unix sockets / memfd / shm)
|
||||
|
||||
→ encoding used in the pulseaudio-native protocol
|
||||
|
||||
## Working features
|
||||
Querying and setting the volume.
|
||||
|
||||
Listing audio outputs.
|
||||
|
||||
Changing the default audio output.
|
||||
|
||||
Notifications on config updates.
|
||||
|
||||
|
||||
|
||||
* * *
|
||||
Automatically generated by [autoreadme](https://github.com/jimmyfrasche/autoreadme) on 2018.06.22
|
||||
326
vendor/github.com/mafik/pulseaudio/client.go
generated
vendored
Normal file
326
vendor/github.com/mafik/pulseaudio/client.go
generated
vendored
Normal file
@@ -0,0 +1,326 @@
|
||||
// Package pulseaudio is a pure-Go (no libpulse) implementation of the PulseAudio native protocol.
|
||||
//
|
||||
// Rather than exposing the PulseAudio protocol directly this library attempts to hide
|
||||
// the PulseAudio complexity behind a Go interface.
|
||||
// Some of the things which are deliberately not exposed in the API are:
|
||||
//
|
||||
// → backwards compatibility for old PulseAudio servers
|
||||
//
|
||||
// → transport mechanism used for the connection (Unix sockets / memfd / shm)
|
||||
//
|
||||
// → encoding used in the pulseaudio-native protocol
|
||||
//
|
||||
// Working features
|
||||
//
|
||||
// Querying and setting the volume.
|
||||
//
|
||||
// Listing audio outputs.
|
||||
//
|
||||
// Changing the default audio output.
|
||||
//
|
||||
// Notifications on config updates.
|
||||
package pulseaudio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
)
|
||||
|
||||
const version = 32
|
||||
|
||||
var defaultAddr = fmt.Sprintf("/run/user/%d/pulse/native", os.Getuid())
|
||||
|
||||
type packetResponse struct {
|
||||
buff *bytes.Buffer
|
||||
err error
|
||||
}
|
||||
|
||||
type packet struct {
|
||||
requestBytes []byte
|
||||
responseChan chan<- packetResponse
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Cmd string
|
||||
Code uint32
|
||||
}
|
||||
|
||||
func (err *Error) Error() string {
|
||||
return fmt.Sprintf("PulseAudio error: %s -> %s", err.Cmd, errors[err.Code])
|
||||
}
|
||||
|
||||
// Client maintains a connection to the PulseAudio server.
|
||||
type Client struct {
|
||||
conn net.Conn
|
||||
clientIndex int
|
||||
packets chan packet
|
||||
updates chan struct{}
|
||||
}
|
||||
|
||||
// NewClient establishes a connection to the PulseAudio server.
|
||||
func NewClient(addressArr ...string) (*Client, error) {
|
||||
if len(addressArr) < 1 {
|
||||
addressArr = []string{defaultAddr}
|
||||
}
|
||||
|
||||
conn, err := net.Dial("unix", addressArr[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &Client{
|
||||
conn: conn,
|
||||
packets: make(chan packet),
|
||||
updates: make(chan struct{}, 1),
|
||||
}
|
||||
|
||||
go c.processPackets()
|
||||
|
||||
err = c.auth()
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.setName()
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
const frameSizeMaxAllow = 1024 * 1024 * 16
|
||||
|
||||
func (c *Client) processPackets() {
|
||||
recv := make(chan *bytes.Buffer)
|
||||
go func(recv chan<- *bytes.Buffer) {
|
||||
var err error
|
||||
for {
|
||||
var b bytes.Buffer
|
||||
if _, err = io.CopyN(&b, c.conn, 4); err != nil {
|
||||
break
|
||||
}
|
||||
n := binary.BigEndian.Uint32(b.Bytes())
|
||||
if n > frameSizeMaxAllow {
|
||||
err = fmt.Errorf("Response size %d is too long (only %d allowed)", n, frameSizeMaxAllow)
|
||||
break
|
||||
}
|
||||
b.Grow(int(n) + 20)
|
||||
if _, err = io.CopyN(&b, c.conn, int64(n)+16); err != nil {
|
||||
break
|
||||
}
|
||||
b.Next(20) // skip the header
|
||||
recv <- &b
|
||||
}
|
||||
close(recv)
|
||||
}(recv)
|
||||
|
||||
pending := make(map[uint32]packet)
|
||||
tag := uint32(0)
|
||||
var err error
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case p, ok := <-c.packets: // Outgoing request
|
||||
if !ok {
|
||||
// Client was closed
|
||||
break loop
|
||||
}
|
||||
// Find an unused tag
|
||||
for {
|
||||
_, exists := pending[tag]
|
||||
if !exists {
|
||||
break
|
||||
}
|
||||
tag++
|
||||
if tag == 0xffffffff { // reserved for subscription events
|
||||
tag = 0
|
||||
}
|
||||
}
|
||||
if len(p.requestBytes) < 26 {
|
||||
p.responseChan <- packetResponse{
|
||||
buff: nil,
|
||||
err: fmt.Errorf("request too short. Needs at least 26 bytes"),
|
||||
}
|
||||
continue
|
||||
}
|
||||
binary.BigEndian.PutUint32(p.requestBytes, uint32(len(p.requestBytes))-20)
|
||||
binary.BigEndian.PutUint32(p.requestBytes[26:], tag) // fix tag
|
||||
_, err = c.conn.Write(p.requestBytes)
|
||||
if err != nil {
|
||||
p.responseChan <- packetResponse{
|
||||
buff: nil,
|
||||
err: fmt.Errorf("couldn't send request: %s", err),
|
||||
}
|
||||
} else {
|
||||
pending[tag] = p
|
||||
}
|
||||
case buff, ok := <-recv: // Incoming request
|
||||
if !ok {
|
||||
// Client was closed
|
||||
break loop
|
||||
}
|
||||
var tag uint32
|
||||
var rsp command
|
||||
err = bread(buff, uint32Tag, &rsp, uint32Tag, &tag)
|
||||
if err != nil {
|
||||
// We've got a weird request from PulseAudio - that should never happen.
|
||||
// We could ignore it and continue but it may hide some errors so let's panic.
|
||||
panic(err)
|
||||
}
|
||||
if rsp == commandSubscribeEvent && tag == 0xffffffff {
|
||||
select {
|
||||
case c.updates <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
continue
|
||||
}
|
||||
p, ok := pending[tag]
|
||||
if !ok {
|
||||
// Another case, similar to the one above.
|
||||
// We could ignore it and continue but it may hide errors so let's panic.
|
||||
panic(fmt.Sprintf("No pending requests for tag %d (%s)", tag, rsp))
|
||||
}
|
||||
delete(pending, tag)
|
||||
if rsp == commandError {
|
||||
var code uint32
|
||||
bread(buff, uint32Tag, &code)
|
||||
cmd := command(binary.BigEndian.Uint32(p.requestBytes[21:]))
|
||||
p.responseChan <- packetResponse{
|
||||
buff: nil,
|
||||
err: &Error{Cmd: cmd.String(), Code: code},
|
||||
}
|
||||
continue
|
||||
}
|
||||
if rsp == commandReply {
|
||||
p.responseChan <- packetResponse{
|
||||
buff: buff,
|
||||
err: nil,
|
||||
}
|
||||
continue
|
||||
}
|
||||
p.responseChan <- packetResponse{
|
||||
buff: nil,
|
||||
err: fmt.Errorf("expected Reply or Error but got: %s", rsp),
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, p := range pending {
|
||||
p.responseChan <- packetResponse{
|
||||
buff: nil,
|
||||
err: fmt.Errorf("PulseAudio client was closed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) request(cmd command, args ...interface{}) (*bytes.Buffer, error) {
|
||||
var b bytes.Buffer
|
||||
args = append([]interface{}{uint32(0), // dummy length -- we'll overwrite at the end when we know our final length
|
||||
uint32(0xffffffff), // channel
|
||||
uint32(0), uint32(0), // offset high & low
|
||||
uint32(0), // flags
|
||||
uint32Tag, uint32(cmd), // command
|
||||
uint32Tag, uint32(0), // tag
|
||||
}, args...)
|
||||
err := bwrite(&b, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if b.Len() > frameSizeMaxAllow {
|
||||
return nil, fmt.Errorf("Request size %d is too long (only %d allowed)", b.Len(), frameSizeMaxAllow)
|
||||
}
|
||||
responseChan := make(chan packetResponse)
|
||||
|
||||
err = c.addPacket(packet{
|
||||
requestBytes: b.Bytes(),
|
||||
responseChan: responseChan,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := <-responseChan
|
||||
return response.buff, response.err
|
||||
}
|
||||
|
||||
func (c *Client) addPacket(data packet) (err error) {
|
||||
defer func() {
|
||||
if recover() != nil {
|
||||
err = fmt.Errorf("connection closed")
|
||||
}
|
||||
}()
|
||||
c.packets <- data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) auth() error {
|
||||
const protocolVersionMask = 0x0000FFFF
|
||||
cookiePath := os.Getenv("HOME") + "/.config/pulse/cookie"
|
||||
cookie, err := ioutil.ReadFile(cookiePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
const cookieLength = 256
|
||||
if len(cookie) != cookieLength {
|
||||
return fmt.Errorf("pulse audio client cookie has incorrect length %d: Expected %d (path %#v)",
|
||||
len(cookie), cookieLength, cookiePath)
|
||||
}
|
||||
b, err := c.request(commandAuth,
|
||||
uint32Tag, uint32(version),
|
||||
arbitraryTag, uint32(len(cookie)), cookie)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var serverVersion uint32
|
||||
err = bread(b, uint32Tag, &serverVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serverVersion &= protocolVersionMask
|
||||
if serverVersion < version {
|
||||
return fmt.Errorf("pulseAudio server supports version %d but minimum required is %d", serverVersion, version)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) setName() error {
|
||||
props := map[string]string{
|
||||
"application.name": path.Base(os.Args[0]),
|
||||
"application.process.id": fmt.Sprintf("%d", os.Getpid()),
|
||||
"application.process.binary": os.Args[0],
|
||||
"application.language": "en_US.UTF-8",
|
||||
"window.x11.display": os.Getenv("DISPLAY"),
|
||||
}
|
||||
if current, err := user.Current(); err == nil {
|
||||
props["application.process.user"] = current.Username
|
||||
}
|
||||
if hostname, err := os.Hostname(); err == nil {
|
||||
props["application.process.host"] = hostname
|
||||
}
|
||||
b, err := c.request(commandSetClientName, props)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var clientIndex uint32
|
||||
err = bread(b, uint32Tag, &clientIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.clientIndex = int(clientIndex)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the connection to PulseAudio server and makes the Client unusable.
|
||||
func (c *Client) Close() {
|
||||
close(c.packets)
|
||||
c.conn.Close()
|
||||
}
|
||||
150
vendor/github.com/mafik/pulseaudio/command.go
generated
vendored
Normal file
150
vendor/github.com/mafik/pulseaudio/command.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
package pulseaudio
|
||||
|
||||
type command uint32
|
||||
|
||||
//go:generate stringer -type=command
|
||||
const (
|
||||
/* Generic commands */
|
||||
commandError command = iota
|
||||
commandTimeout
|
||||
commandReply // 2
|
||||
|
||||
/* CLIENT->SERVER */
|
||||
commandCreatePlaybackStream // 3
|
||||
commandDeletePlaybackStream
|
||||
commandCreateRecordStream
|
||||
commandDeleteRecordStream
|
||||
commandExit
|
||||
commandAuth // 8
|
||||
commandSetClientName
|
||||
commandLookupSink
|
||||
commandLookupSource
|
||||
commandDrainPlaybackStream
|
||||
commandStat
|
||||
commandGetPlaybackLatency
|
||||
commandCreateUploadStream
|
||||
commandDeleteUploadStream
|
||||
commandFinishUploadStream
|
||||
commandPlaySample
|
||||
commandRemoveSample // 19
|
||||
|
||||
commandGetServerInfo
|
||||
commandGetSinkInfo
|
||||
commandGetSinkInfoList
|
||||
commandGetSourceInfo
|
||||
commandGetSourceInfoList
|
||||
commandGetModuleInfo
|
||||
commandGetModuleInfoList
|
||||
commandGetClientInfo
|
||||
commandGetClientInfoList
|
||||
commandGetSinkInputInfo
|
||||
commandGetSinkInputInfoList
|
||||
commandGetSourceOutputInfo
|
||||
commandGetSourceOutputInfoList
|
||||
commandGetSampleInfo
|
||||
commandGetSampleInfoList
|
||||
commandSubscribe
|
||||
|
||||
commandSetSinkVolume
|
||||
commandSetSinkInputVolume
|
||||
commandSetSourceVolume
|
||||
|
||||
commandSetSinkMute
|
||||
commandSetSourceMute // 40
|
||||
|
||||
commandCorkPlaybackStream
|
||||
commandFlushPlaybackStream
|
||||
commandTriggerPlaybackStream // 43
|
||||
|
||||
commandSetDefaultSink
|
||||
commandSetDefaultSource // 45
|
||||
|
||||
commandSetPlaybackStreamName
|
||||
commandSetRecordStreamName // 47
|
||||
|
||||
commandKillClient
|
||||
commandKillSinkInput
|
||||
commandKillSourceOutput // 50
|
||||
|
||||
commandLoadModule
|
||||
commandUnloadModule // 52
|
||||
|
||||
commandAddAutoloadObsolete
|
||||
commandRemoveAutoloadObsolete
|
||||
commandGetAutoloadInfoObsolete
|
||||
commandGetAutoloadInfoListObsolete //56
|
||||
|
||||
commandGetRecordLatency
|
||||
commandCorkRecordStream
|
||||
commandFlushRecordStream
|
||||
commandPrebufPlaybackStream // 60
|
||||
|
||||
/* SERVER->CLIENT */
|
||||
commandRequest // 61
|
||||
commandOverflow
|
||||
commandUnderflow
|
||||
commandPlaybackStreamKilled
|
||||
commandRecordStreamKilled
|
||||
commandSubscribeEvent
|
||||
|
||||
/* A few more client->server commands */
|
||||
|
||||
commandMoveSinkInput
|
||||
commandMoveSourceOutput
|
||||
commandSetSinkInputMute
|
||||
commandSuspendSink
|
||||
commandSuspendSource
|
||||
|
||||
commandSetPlaybackStreamBufferAttr
|
||||
commandSetRecordStreamBufferAttr
|
||||
|
||||
commandUpdatePlaybackStreamSampleRate
|
||||
commandUpdateRecordStreamSampleRate
|
||||
|
||||
/* SERVER->CLIENT */
|
||||
commandPlaybackStreamSuspended
|
||||
commandRecordStreamSuspended
|
||||
commandPlaybackStreamMoved
|
||||
commandRecordStreamMoved
|
||||
|
||||
commandUpdateRecordStreamProplist
|
||||
commandUpdatePlaybackStreamProplist
|
||||
commandUpdateClientProplist
|
||||
commandRemoveRecordStreamProplist
|
||||
commandRemovePlaybackStreamProplist
|
||||
commandRemoveClientProplist
|
||||
|
||||
/* SERVER->CLIENT */
|
||||
commandStarted
|
||||
|
||||
commandExtension
|
||||
|
||||
commandGetCardInfo
|
||||
commandGetCardInfoList
|
||||
commandSetCardProfile
|
||||
|
||||
commandClientEvent
|
||||
commandPlaybackStreamEvent
|
||||
commandRecordStreamEvent
|
||||
|
||||
/* SERVER->CLIENT */
|
||||
commandPlaybackBufferAttrChanged
|
||||
commandRecordBufferAttrChanged
|
||||
|
||||
commandSetSinkPort
|
||||
commandSetSourcePort
|
||||
|
||||
commandSetSourceOutputVolume
|
||||
commandSetSourceOutputMute
|
||||
|
||||
commandSetPortLatencyOffset
|
||||
|
||||
/* BOTH DIRECTIONS */
|
||||
commandEnableSrbchannel
|
||||
commandDisableSrbchannel
|
||||
|
||||
/* BOTH DIRECTIONS */
|
||||
commandRegisterMemfdShmid
|
||||
|
||||
commandMax
|
||||
)
|
||||
16
vendor/github.com/mafik/pulseaudio/command_string.go
generated
vendored
Normal file
16
vendor/github.com/mafik/pulseaudio/command_string.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Code generated by "stringer -type=command"; DO NOT EDIT.
|
||||
|
||||
package pulseaudio
|
||||
|
||||
import "strconv"
|
||||
|
||||
const _command_name = "commandErrorcommandTimeoutcommandReplycommandCreatePlaybackStreamcommandDeletePlaybackStreamcommandCreateRecordStreamcommandDeleteRecordStreamcommandExitcommandAuthcommandSetClientNamecommandLookupSinkcommandLookupSourcecommandDrainPlaybackStreamcommandStatcommandGetPlaybackLatencycommandCreateUploadStreamcommandDeleteUploadStreamcommandFinishUploadStreamcommandPlaySamplecommandRemoveSamplecommandGetServerInfocommandGetSinkInfocommandGetSinkInfoListcommandGetSourceInfocommandGetSourceInfoListcommandGetModuleInfocommandGetModuleInfoListcommandGetClientInfocommandGetClientInfoListcommandGetSinkInputInfocommandGetSinkInputInfoListcommandGetSourceOutputInfocommandGetSourceOutputInfoListcommandGetSampleInfocommandGetSampleInfoListcommandSubscribecommandSetSinkVolumecommandSetSinkInputVolumecommandSetSourceVolumecommandSetSinkMutecommandSetSourceMutecommandCorkPlaybackStreamcommandFlushPlaybackStreamcommandTriggerPlaybackStreamcommandSetDefaultSinkcommandSetDefaultSourcecommandSetPlaybackStreamNamecommandSetRecordStreamNamecommandKillClientcommandKillSinkInputcommandKillSourceOutputcommandLoadModulecommandUnloadModulecommandAddAutoloadObsoletecommandRemoveAutoloadObsoletecommandGetAutoloadInfoObsoletecommandGetAutoloadInfoListObsoletecommandGetRecordLatencycommandCorkRecordStreamcommandFlushRecordStreamcommandPrebufPlaybackStreamcommandRequestcommandOverflowcommandUnderflowcommandPlaybackStreamKilledcommandRecordStreamKilledcommandSubscribeEventcommandMoveSinkInputcommandMoveSourceOutputcommandSetSinkInputMutecommandSuspendSinkcommandSuspendSourcecommandSetPlaybackStreamBufferAttrcommandSetRecordStreamBufferAttrcommandUpdatePlaybackStreamSampleRatecommandUpdateRecordStreamSampleRatecommandPlaybackStreamSuspendedcommandRecordStreamSuspendedcommandPlaybackStreamMovedcommandRecordStreamMovedcommandUpdateRecordStreamProplistcommandUpdatePlaybackStreamProplistcommandUpdateClientProplistcommandRemoveRecordStreamProplistcommandRemovePlaybackStreamProplistcommandRemoveClientProplistcommandStartedcommandExtensioncommandGetCardInfocommandGetCardInfoListcommandSetCardProfilecommandClientEventcommandPlaybackStreamEventcommandRecordStreamEventcommandPlaybackBufferAttrChangedcommandRecordBufferAttrChangedcommandSetSinkPortcommandSetSourcePortcommandSetSourceOutputVolumecommandSetSourceOutputMutecommandSetPortLatencyOffsetcommandEnableSrbchannelcommandDisableSrbchannelcommandRegisterMemfdShmidcommandMax"
|
||||
|
||||
var _command_index = [...]uint16{0, 12, 26, 38, 65, 92, 117, 142, 153, 164, 184, 201, 220, 246, 257, 282, 307, 332, 357, 374, 393, 413, 431, 453, 473, 497, 517, 541, 561, 585, 608, 635, 661, 691, 711, 735, 751, 771, 796, 818, 836, 856, 881, 907, 935, 956, 979, 1007, 1033, 1050, 1070, 1093, 1110, 1129, 1155, 1184, 1214, 1248, 1271, 1294, 1318, 1345, 1359, 1374, 1390, 1417, 1442, 1463, 1483, 1506, 1529, 1547, 1567, 1601, 1633, 1670, 1705, 1735, 1763, 1789, 1813, 1846, 1881, 1908, 1941, 1976, 2003, 2017, 2033, 2051, 2073, 2094, 2112, 2138, 2162, 2194, 2224, 2242, 2262, 2290, 2316, 2343, 2366, 2390, 2415, 2425}
|
||||
|
||||
func (i command) String() string {
|
||||
if i >= command(len(_command_index)-1) {
|
||||
return "command(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _command_name[_command_index[i]:_command_index[i+1]]
|
||||
}
|
||||
31
vendor/github.com/mafik/pulseaudio/errors.go
generated
vendored
Normal file
31
vendor/github.com/mafik/pulseaudio/errors.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package pulseaudio
|
||||
|
||||
var errors = []string{
|
||||
"OK",
|
||||
"Access denied",
|
||||
"Unknown command",
|
||||
"Invalid argument",
|
||||
"Entity exists",
|
||||
"No such entity",
|
||||
"Connection refused",
|
||||
"Protocol error",
|
||||
"Timeout",
|
||||
"No authentication key",
|
||||
"Internal error",
|
||||
"Connection terminated",
|
||||
"Entity killed",
|
||||
"Invalid server",
|
||||
"Module initialization failed",
|
||||
"Bad state",
|
||||
"No data",
|
||||
"Incompatible protocol version",
|
||||
"Too large",
|
||||
"Not supported",
|
||||
"Unknown error code",
|
||||
"No such extension",
|
||||
"Obsolete functionality",
|
||||
"Missing implementation",
|
||||
"Client forked",
|
||||
"Input/Output error",
|
||||
"Device or resource busy",
|
||||
}
|
||||
229
vendor/github.com/mafik/pulseaudio/format.go
generated
vendored
Normal file
229
vendor/github.com/mafik/pulseaudio/format.go
generated
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
package pulseaudio
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type tagType byte
|
||||
|
||||
const (
|
||||
invalidTag tagType = 0
|
||||
stringTag tagType = 't'
|
||||
stringNullTag tagType = 'N'
|
||||
uint32Tag tagType = 'L'
|
||||
uint8Tag tagType = 'B'
|
||||
uint64Tag tagType = 'R'
|
||||
int64Tag tagType = 'r'
|
||||
sampleSpecTag tagType = 'a'
|
||||
arbitraryTag tagType = 'x'
|
||||
trueTag tagType = '1'
|
||||
falseTag tagType = '0'
|
||||
timeTag tagType = 'T'
|
||||
usecTag tagType = 'U'
|
||||
channelMapTag tagType = 'm'
|
||||
cvolumeTag tagType = 'v'
|
||||
propListTag tagType = 'P'
|
||||
volumeTag tagType = 'V'
|
||||
formatInfoTag tagType = 'f'
|
||||
)
|
||||
|
||||
func (t tagType) String() string {
|
||||
switch t {
|
||||
case invalidTag:
|
||||
return "invalidTag"
|
||||
case stringTag:
|
||||
return "stringTag"
|
||||
case stringNullTag:
|
||||
return "stringNullTag"
|
||||
case uint32Tag:
|
||||
return "uint32Tag"
|
||||
case uint8Tag:
|
||||
return "uint8Tag"
|
||||
case uint64Tag:
|
||||
return "uint64Tag"
|
||||
case int64Tag:
|
||||
return "int64Tag"
|
||||
case sampleSpecTag:
|
||||
return "sampleSpecTag"
|
||||
case arbitraryTag:
|
||||
return "arbitraryTag"
|
||||
case trueTag:
|
||||
return "trueTag"
|
||||
case falseTag:
|
||||
return "falseTag"
|
||||
case timeTag:
|
||||
return "timeTag"
|
||||
case usecTag:
|
||||
return "usecTag"
|
||||
case channelMapTag:
|
||||
return "channelMapTag"
|
||||
case cvolumeTag:
|
||||
return "cvolumeTag"
|
||||
case propListTag:
|
||||
return "propListTag"
|
||||
case volumeTag:
|
||||
return "volumeTag"
|
||||
case formatInfoTag:
|
||||
return "formatInfoTag"
|
||||
default:
|
||||
return fmt.Sprintf("UnknownValue(%d)", t)
|
||||
}
|
||||
}
|
||||
|
||||
type binaryReader interface {
|
||||
readFrom(r io.Reader, c *Client) error
|
||||
}
|
||||
|
||||
func bwrite(w io.Writer, data ...interface{}) error {
|
||||
for _, v := range data {
|
||||
if propList, ok := v.(map[string]string); ok {
|
||||
err := bwrite(w, propListTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range propList {
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
l := uint32(len(v) + 1) // +1 for null at the end of string
|
||||
err := bwrite(w,
|
||||
stringTag, []byte(k), byte(0),
|
||||
uint32Tag, l,
|
||||
arbitraryTag, l,
|
||||
[]byte(v), byte(0),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = bwrite(w, stringNullTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if cvolume, ok := v.(cvolume); ok {
|
||||
arr := []uint32(cvolume)
|
||||
err := bwrite(w, cvolumeTag, byte(len(arr)), arr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if err := binary.Write(w, binary.BigEndian, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func bread(r io.Reader, data ...interface{}) error {
|
||||
for _, v := range data {
|
||||
t, ok := v.(tagType)
|
||||
if ok {
|
||||
var tt tagType
|
||||
if err := binary.Read(r, binary.BigEndian, &tt); err != nil {
|
||||
return err
|
||||
}
|
||||
if tt != t {
|
||||
return fmt.Errorf("Protcol error: Got type %s but expected %s", tt, t)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
sptr, ok := v.(*string)
|
||||
if ok {
|
||||
buf := make([]byte, 1024) // max string length i guess.
|
||||
i := 0
|
||||
for {
|
||||
_, err := r.Read(buf[i : i+1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if buf[i] == 0 {
|
||||
*sptr = string(buf[:i])
|
||||
break
|
||||
} else {
|
||||
if i > len(buf) {
|
||||
return fmt.Errorf("String is too long (max %d bytes)", len(buf))
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
propList, ok := v.(*map[string]string)
|
||||
if ok {
|
||||
*propList = make(map[string]string)
|
||||
err := bread(r, propListTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
var t tagType
|
||||
if err = bread(r, &t); err != nil {
|
||||
return err
|
||||
}
|
||||
if t == stringNullTag {
|
||||
// end of the proplist.
|
||||
break
|
||||
}
|
||||
if t != stringTag {
|
||||
return fmt.Errorf("Protcol error: Got type %s but expected %s", t, stringTag)
|
||||
}
|
||||
|
||||
var k, v string
|
||||
var l1, l2 uint32
|
||||
if err = bread(r,
|
||||
&k,
|
||||
uint32Tag, &l1,
|
||||
arbitraryTag, &l2,
|
||||
&v,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(v) != int(l1-1) || len(v) != int(l2-1) {
|
||||
return fmt.Errorf("Protocol error: Proplist value length mismatch (len %d, arb len %d, value len %d)",
|
||||
l1, l2, len(v))
|
||||
}
|
||||
(*propList)[k] = v
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
rdr, ok := v.(io.ReaderFrom)
|
||||
if ok {
|
||||
if _, err := rdr.ReadFrom(r); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
bptr, ok := v.(*bool)
|
||||
if ok {
|
||||
var tt tagType
|
||||
if err := binary.Read(r, binary.BigEndian, &tt); err != nil {
|
||||
return err
|
||||
}
|
||||
if tt == trueTag {
|
||||
*bptr = true
|
||||
} else if tt == falseTag {
|
||||
*bptr = false
|
||||
} else {
|
||||
return fmt.Errorf("Protcol error: Got type %s but expected boolean true or false", tt)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if err := binary.Read(r, binary.BigEndian, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
327
vendor/github.com/mafik/pulseaudio/info.go
generated
vendored
Normal file
327
vendor/github.com/mafik/pulseaudio/info.go
generated
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
package pulseaudio
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
PackageName string
|
||||
PackageVersion string
|
||||
User string
|
||||
Hostname string
|
||||
SampleSpec sampleSpec
|
||||
DefaultSink string
|
||||
DefaultSource string
|
||||
Cookie uint32
|
||||
ChannelMap channelMap
|
||||
}
|
||||
|
||||
func (s *Server) ReadFrom(r io.Reader) (int64, error) {
|
||||
return 0, bread(r,
|
||||
stringTag, &s.PackageName,
|
||||
stringTag, &s.PackageVersion,
|
||||
stringTag, &s.User,
|
||||
stringTag, &s.Hostname,
|
||||
&s.SampleSpec,
|
||||
stringTag, &s.DefaultSink,
|
||||
stringTag, &s.DefaultSource,
|
||||
uint32Tag, &s.Cookie,
|
||||
&s.ChannelMap)
|
||||
}
|
||||
|
||||
type sink struct {
|
||||
Index uint32
|
||||
Name string
|
||||
Description string
|
||||
SampleSpec sampleSpec
|
||||
ChannelMap channelMap
|
||||
ModuleIndex uint32
|
||||
Cvolume cvolume
|
||||
Muted bool
|
||||
MonitorSourceIndex uint32
|
||||
MonitorSourceName string
|
||||
Latency uint64
|
||||
Driver string
|
||||
Flags uint32
|
||||
PropList map[string]string
|
||||
RequestedLatency uint64
|
||||
BaseVolume uint32
|
||||
SinkState uint32
|
||||
NVolumeSteps uint32
|
||||
CardIndex uint32
|
||||
Ports []sinkPort
|
||||
ActivePortName string
|
||||
Formats []formatInfo
|
||||
}
|
||||
|
||||
func (s *sink) ReadFrom(r io.Reader) (int64, error) {
|
||||
var portCount uint32
|
||||
err := bread(r,
|
||||
uint32Tag, &s.Index,
|
||||
stringTag, &s.Name,
|
||||
stringTag, &s.Description,
|
||||
&s.SampleSpec,
|
||||
&s.ChannelMap,
|
||||
uint32Tag, &s.ModuleIndex,
|
||||
&s.Cvolume,
|
||||
&s.Muted,
|
||||
uint32Tag, &s.MonitorSourceIndex,
|
||||
stringTag, &s.MonitorSourceName,
|
||||
usecTag, &s.Latency,
|
||||
stringTag, &s.Driver,
|
||||
uint32Tag, &s.Flags,
|
||||
&s.PropList,
|
||||
usecTag, &s.RequestedLatency,
|
||||
volumeTag, &s.BaseVolume,
|
||||
uint32Tag, &s.SinkState,
|
||||
uint32Tag, &s.NVolumeSteps,
|
||||
uint32Tag, &s.CardIndex,
|
||||
uint32Tag, &portCount)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
s.Ports = make([]sinkPort, portCount)
|
||||
for i := uint32(0); i < portCount; i++ {
|
||||
err = bread(r, &s.Ports[i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if portCount == 0 {
|
||||
err = bread(r, stringNullTag)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
err = bread(r, stringTag, &s.ActivePortName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
var formatCount uint8
|
||||
err = bread(r,
|
||||
uint8Tag, &formatCount)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
s.Formats = make([]formatInfo, formatCount)
|
||||
for i := uint8(0); i < formatCount; i++ {
|
||||
err = bread(r, &s.Formats[i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
type formatInfo struct {
|
||||
Encoding byte
|
||||
PropList map[string]string
|
||||
}
|
||||
|
||||
func (i *formatInfo) ReadFrom(r io.Reader) (int64, error) {
|
||||
return 0, bread(r, formatInfoTag, uint8Tag, &i.Encoding, &i.PropList)
|
||||
}
|
||||
|
||||
type sinkPort struct {
|
||||
Name, Description string
|
||||
Pririty uint32
|
||||
Available uint32
|
||||
}
|
||||
|
||||
func (p *sinkPort) ReadFrom(r io.Reader) (int64, error) {
|
||||
return 0, bread(r,
|
||||
stringTag, &p.Name,
|
||||
stringTag, &p.Description,
|
||||
uint32Tag, &p.Pririty,
|
||||
uint32Tag, &p.Available)
|
||||
}
|
||||
|
||||
type cvolume []uint32
|
||||
|
||||
func (v *cvolume) ReadFrom(r io.Reader) (int64, error) {
|
||||
var n byte
|
||||
err := bread(r, cvolumeTag, &n)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
*v = make([]uint32, n)
|
||||
return 0, bread(r, []uint32(*v))
|
||||
}
|
||||
|
||||
type channelMap []byte
|
||||
|
||||
func (m *channelMap) ReadFrom(r io.Reader) (int64, error) {
|
||||
var n byte
|
||||
err := bread(r, channelMapTag, &n)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
*m = make([]byte, n)
|
||||
_, err = r.Read(*m)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
type sampleSpec struct {
|
||||
Format byte
|
||||
Channels byte
|
||||
Rate uint32
|
||||
}
|
||||
|
||||
func (s *sampleSpec) ReadFrom(r io.Reader) (int64, error) {
|
||||
return 0, bread(r, sampleSpecTag, &s.Format, &s.Channels, &s.Rate)
|
||||
}
|
||||
|
||||
type Card struct {
|
||||
Index uint32
|
||||
Name string
|
||||
Module uint32
|
||||
Driver string
|
||||
Profiles map[string]*profile
|
||||
ActiveProfile *profile
|
||||
PropList map[string]string
|
||||
Ports []port
|
||||
}
|
||||
|
||||
type profile struct {
|
||||
Name, Description string
|
||||
Nsinks, Nsources uint32
|
||||
Priority uint32
|
||||
Available uint32
|
||||
}
|
||||
|
||||
type port struct {
|
||||
Card *Card
|
||||
Name, Description string
|
||||
Pririty uint32
|
||||
Available uint32
|
||||
Direction byte
|
||||
PropList map[string]string
|
||||
Profiles []*profile
|
||||
LatencyOffset int64
|
||||
}
|
||||
|
||||
func (p *port) ReadFrom(r io.Reader) (int64, error) {
|
||||
err := bread(r,
|
||||
stringTag, &p.Name,
|
||||
stringTag, &p.Description,
|
||||
uint32Tag, &p.Pririty,
|
||||
uint32Tag, &p.Available,
|
||||
uint8Tag, &p.Direction,
|
||||
&p.PropList)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var portProfileCount uint32
|
||||
err = bread(r, uint32Tag, &portProfileCount)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for j := uint32(0); j < portProfileCount; j++ {
|
||||
var profileName string
|
||||
err = bread(r, stringTag, &profileName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
p.Profiles = append(p.Profiles, p.Card.Profiles[profileName])
|
||||
}
|
||||
return 0, bread(r, int64Tag, &p.LatencyOffset)
|
||||
}
|
||||
|
||||
func (c *Client) sinks() ([]sink, error) {
|
||||
b, err := c.request(commandGetSinkInfoList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var sinks []sink
|
||||
for b.Len() > 0 {
|
||||
var sink sink
|
||||
err = bread(b, &sink)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sinks = append(sinks, sink)
|
||||
}
|
||||
return sinks, nil
|
||||
}
|
||||
|
||||
func (c *Client) Cards() ([]Card, error) {
|
||||
b, err := c.request(commandGetCardInfoList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cards []Card
|
||||
for b.Len() > 0 {
|
||||
var card Card
|
||||
var profileCount uint32
|
||||
err := bread(b,
|
||||
uint32Tag, &card.Index,
|
||||
stringTag, &card.Name,
|
||||
uint32Tag, &card.Module,
|
||||
stringTag, &card.Driver,
|
||||
uint32Tag, &profileCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
card.Profiles = make(map[string]*profile)
|
||||
for i := uint32(0); i < profileCount; i++ {
|
||||
var profile profile
|
||||
err = bread(b,
|
||||
stringTag, &profile.Name,
|
||||
stringTag, &profile.Description,
|
||||
uint32Tag, &profile.Nsinks,
|
||||
uint32Tag, &profile.Nsources,
|
||||
uint32Tag, &profile.Priority,
|
||||
uint32Tag, &profile.Available)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
card.Profiles[profile.Name] = &profile
|
||||
}
|
||||
var portCount uint32
|
||||
var activeProfileName string
|
||||
err = bread(b,
|
||||
stringTag, &activeProfileName,
|
||||
&card.PropList,
|
||||
uint32Tag, &portCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
card.ActiveProfile = card.Profiles[activeProfileName]
|
||||
card.Ports = make([]port, portCount)
|
||||
for i := uint32(0); i < portCount; i++ {
|
||||
card.Ports[i].Card = &card
|
||||
err = bread(b, &card.Ports[i])
|
||||
}
|
||||
cards = append(cards, card)
|
||||
}
|
||||
return cards, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetCardProfile(cardIndex uint32, profileName string) error {
|
||||
_, err := c.request(commandSetCardProfile,
|
||||
uint32Tag, cardIndex,
|
||||
stringNullTag,
|
||||
stringTag, []byte(profileName), byte(0))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) setDefaultSink(sinkName string) error {
|
||||
_, err := c.request(commandSetDefaultSink,
|
||||
stringTag, []byte(sinkName), byte(0))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) ServerInfo() (*Server, error) {
|
||||
r, err := c.request(commandGetServerInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var s Server
|
||||
err = bread(r, &s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
153
vendor/github.com/mafik/pulseaudio/outputs.go
generated
vendored
Normal file
153
vendor/github.com/mafik/pulseaudio/outputs.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
package pulseaudio
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Output represents PulseAudio output.
|
||||
type Output struct {
|
||||
client *Client
|
||||
CardID string
|
||||
PortID string
|
||||
CardName string
|
||||
PortName string
|
||||
Available bool
|
||||
}
|
||||
|
||||
// Activate sets this output as the main one.
|
||||
func (o Output) Activate() error {
|
||||
c := o.client
|
||||
cards, err := c.Cards()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.CardID == "all" && o.PortID == "none" {
|
||||
for _, otherCard := range cards {
|
||||
err = c.SetCardProfile(otherCard.Index, "off")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var found bool
|
||||
var card Card
|
||||
for _, card = range cards {
|
||||
if card.Name == o.CardID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("PulseAudio error: card %s is no longer available", o.CardID)
|
||||
}
|
||||
|
||||
found = false
|
||||
var port port
|
||||
for _, port = range card.Ports {
|
||||
if port.Name == o.PortID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("PulseAudio error: port %s is no longer available", o.PortID)
|
||||
}
|
||||
|
||||
for _, otherCard := range cards {
|
||||
if otherCard.Index == card.Index {
|
||||
continue
|
||||
}
|
||||
err = c.SetCardProfile(otherCard.Index, "off")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
bestProfile := port.Profiles[0]
|
||||
for _, profile := range port.Profiles {
|
||||
if profile.Priority > bestProfile.Priority {
|
||||
bestProfile = profile
|
||||
}
|
||||
}
|
||||
err = c.SetCardProfile(card.Index, bestProfile.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sinks, err := c.sinks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s, err := c.ServerInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, sink := range sinks {
|
||||
if sink.CardIndex != card.Index {
|
||||
continue
|
||||
}
|
||||
if s.DefaultSink == sink.Name {
|
||||
continue
|
||||
}
|
||||
return c.setDefaultSink(sink.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Outputs returns a list of all audio outputs and an index of the active audio output.
|
||||
//
|
||||
// The last audio output is always called "None" and indicates that audio is disabled.
|
||||
func (c *Client) Outputs() (outputs []Output, activeIndex int, err error) {
|
||||
s, err := c.ServerInfo()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
sinks, err := c.sinks()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cards, err := c.Cards()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
activeIndex = -1
|
||||
for _, card := range cards {
|
||||
for _, port := range card.Ports {
|
||||
if port.Direction != 1 {
|
||||
continue
|
||||
}
|
||||
for _, sink := range sinks {
|
||||
if sink.Name != s.DefaultSink {
|
||||
continue
|
||||
}
|
||||
if sink.CardIndex != card.Index {
|
||||
continue
|
||||
}
|
||||
if sink.ActivePortName != port.Name {
|
||||
continue
|
||||
}
|
||||
activeIndex = len(outputs)
|
||||
}
|
||||
outputs = append(outputs, Output{
|
||||
client: c,
|
||||
CardID: card.Name,
|
||||
CardName: card.PropList["device.description"],
|
||||
PortID: port.Name,
|
||||
PortName: port.Description,
|
||||
Available: port.Available != 1,
|
||||
})
|
||||
}
|
||||
}
|
||||
if activeIndex == -1 {
|
||||
activeIndex = len(outputs)
|
||||
}
|
||||
outputs = append(outputs, Output{
|
||||
client: c,
|
||||
CardID: "all",
|
||||
CardName: "All",
|
||||
PortID: "none",
|
||||
PortName: "None",
|
||||
Available: false,
|
||||
})
|
||||
return
|
||||
}
|
||||
11
vendor/github.com/mafik/pulseaudio/updates.go
generated
vendored
Normal file
11
vendor/github.com/mafik/pulseaudio/updates.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package pulseaudio
|
||||
|
||||
// Updates returns a channel with PulseAudio updates.
|
||||
func (c *Client) Updates() (updates <-chan struct{}, err error) {
|
||||
const subscriptionMaskAll = 0x02ff
|
||||
_, err = c.request(commandSubscribe, uint32Tag, uint32(subscriptionMaskAll))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.updates, nil
|
||||
}
|
||||
92
vendor/github.com/mafik/pulseaudio/volume.go
generated
vendored
Normal file
92
vendor/github.com/mafik/pulseaudio/volume.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
package pulseaudio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const pulseVolumeMax = 0xffff
|
||||
|
||||
// Volume returns current audio volume as a number from 0 to 1 (or more than 1 - if volume is boosted).
|
||||
func (c *Client) Volume() (float32, error) {
|
||||
s, err := c.ServerInfo()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sinks, err := c.sinks()
|
||||
for _, sink := range sinks {
|
||||
if sink.Name != s.DefaultSink {
|
||||
continue
|
||||
}
|
||||
return float32(sink.Cvolume[0]) / pulseVolumeMax, nil
|
||||
}
|
||||
return 0, fmt.Errorf("PulseAudio error: couldn't query volume - sink %s not found", s.DefaultSink)
|
||||
}
|
||||
|
||||
// SetVolume changes the current volume to a specified value from 0 to 1 (or more than 1 - if volume should be boosted).
|
||||
func (c *Client) SetVolume(volume float32) error {
|
||||
s, err := c.ServerInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.setSinkVolume(s.DefaultSink, cvolume{uint32(volume * 0xffff)})
|
||||
}
|
||||
|
||||
func (c *Client) SetSinkVolume(sinkName string, volume float32) error {
|
||||
return c.setSinkVolume(sinkName, cvolume{uint32(volume * 0xffff)})
|
||||
}
|
||||
|
||||
func (c *Client) setSinkVolume(sinkName string, cvolume cvolume) error {
|
||||
_, err := c.request(commandSetSinkVolume, uint32Tag, uint32(0xffffffff), stringTag, []byte(sinkName), byte(0), cvolume)
|
||||
return err
|
||||
}
|
||||
|
||||
// ToggleMute reverse mute status
|
||||
func (c *Client) ToggleMute() (bool, error) {
|
||||
s, err := c.ServerInfo()
|
||||
if err != nil || s == nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
muted, err := c.Mute()
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
err = c.SetMute(!muted)
|
||||
return !muted, err
|
||||
}
|
||||
|
||||
// ToggleMute reverse mute status
|
||||
func (c *Client) SetMute(b bool) error {
|
||||
s, err := c.ServerInfo()
|
||||
if err != nil || s == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
muteCmd := '0'
|
||||
if b {
|
||||
muteCmd = '1'
|
||||
}
|
||||
_, err = c.request(commandSetSinkMute, uint32Tag, uint32(0xffffffff), stringTag, []byte(s.DefaultSink), byte(0), uint8(muteCmd))
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
func (c *Client) Mute() (bool, error) {
|
||||
s, err := c.ServerInfo()
|
||||
if err != nil || s == nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
sinks, err := c.sinks()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, sink := range sinks {
|
||||
if sink.Name != s.DefaultSink {
|
||||
continue
|
||||
}
|
||||
return sink.Muted, nil
|
||||
}
|
||||
return true, fmt.Errorf("couldn't find sink")
|
||||
}
|
||||
Reference in New Issue
Block a user