Initial commit

This commit is contained in:
eleksir
2024-01-06 18:33:44 +03:00
commit d85dc5d5bb
657 changed files with 262300 additions and 0 deletions

14
vendor/github.com/mafik/pulseaudio/.gitignore generated vendored Normal file
View 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
View 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
View File

@@ -0,0 +1,34 @@
# pulseaudio [![GoDoc](https://godoc.org/mrogalski.eu/go/pulseaudio?status.svg)](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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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")
}