-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathsimple.go
177 lines (156 loc) · 4.42 KB
/
simple.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// pulse-simple wraps PulseAudio's Simple API using cgo,
// for easy audio playback and capture via PulseAudio.
//
// Basic usage is to request a playback or capture stream,
// then write bytes to or read bytes from it.
//
// Reading and writing will block until the given byte slice
// is completely consumed or filled, or an error occurs.
//
// The format of the data will be as requested on stream creation.
//
// ss := pulse.SampleSpec{pulse.SAMPLE_S16LE, 44100, 2}
// stream, _ := pulse.Playback("my app", "my stream", &ss)
// defer stream.Free()
// defer stream.Drain()
// stream.Write(data)
//
// More example usage can be found in the examples folder.
//
// For more information, see the PulseAudio Simple API documentation at
// http://www.freedesktop.org/software/pulseaudio/doxygen/simple.html
package pulse
/*
#cgo pkg-config: libpulse-simple
#include <stdlib.h>
#include <pulse/simple.h>
*/
import "C"
import "unsafe"
type StreamDirection C.pa_stream_direction_t
const (
STREAM_NODIRECTION StreamDirection = C.PA_STREAM_NODIRECTION
STREAM_PLAYBACK StreamDirection = C.PA_STREAM_PLAYBACK
STREAM_RECORD StreamDirection = C.PA_STREAM_RECORD
STREAM_UPLOAD StreamDirection = C.PA_STREAM_UPLOAD
)
type Stream struct {
simple *C.pa_simple
}
// Capture creates a new stream for recording and returns its pointer.
func Capture(clientName, streamName string, spec *SampleSpec) (*Stream, error) {
return NewStream("", clientName, STREAM_RECORD, "", streamName, spec, nil, nil)
}
// Playback creates a new stream for playback and returns its pointer.
func Playback(clientName, streamName string, spec *SampleSpec) (*Stream, error) {
return NewStream("", clientName, STREAM_PLAYBACK, "", streamName, spec, nil, nil)
}
func NewStream(
serverName, clientName string,
dir StreamDirection,
deviceName, streamName string,
spec *SampleSpec,
cmap *ChannelMap,
battr *BufferAttr,
) (*Stream, error) {
s := new(Stream)
var server *C.char
if serverName != "" {
server = C.CString(serverName)
defer C.free(unsafe.Pointer(server))
}
var dev *C.char
if deviceName != "" {
dev = C.CString(deviceName)
defer C.free(unsafe.Pointer(dev))
}
name := C.CString(clientName)
defer C.free(unsafe.Pointer(name))
stream_name := C.CString(streamName)
defer C.free(unsafe.Pointer(stream_name))
var err C.int
s.simple = C.pa_simple_new(
server,
name,
C.pa_stream_direction_t(dir),
dev,
stream_name,
spec.toC(),
cmap.toC(),
battr.toC(),
&err,
)
if err == C.PA_OK {
return s, nil
}
return s, errorFromCode(err)
}
// Stream.Free closes the stream and frees the associated memory.
// The stream becomes invalid after this has been called.
// This should usually be deferred immediately after obtaining a stream.
func (s *Stream) Free() {
C.pa_simple_free(s.simple)
}
// Stream.Drain blocks until all buffered data has finished playing.
func (s *Stream) Drain() (error) {
var err C.int
_ = C.pa_simple_drain(s.simple, &err)
if err == C.PA_OK {
return nil
}
return errorFromCode(err)
}
// Stream.Flush flushes the playback buffer, discarding any audio therein
func (s *Stream) Flush() (error) {
var err C.int
_ = C.pa_simple_flush(s.simple, &err)
if err == C.PA_OK {
return nil
}
return errorFromCode(err)
}
// Stream.Write writes the given data to the stream,
// blocking until the data has been written.
func (s *Stream) Write(data []byte) (int, error) {
var err C.int
_ = C.pa_simple_write(
s.simple,
unsafe.Pointer(&data[0]),
C.size_t(len(data)),
&err,
)
// pulse simple does not return the number of bytes written,
// so we must assume that all is written on success,
// and nothing is written on failure.
if err == C.PA_OK {
return len(data), nil
}
return 0, errorFromCode(err)
}
// Stream.Read reads data from the stream,
// blocking until it has filled the provided slice.
func (s *Stream) Read(data []byte) (int, error) {
var err C.int
_ = C.pa_simple_read(
s.simple,
unsafe.Pointer(&data[0]),
C.size_t(len(data)),
&err,
)
// pulse simple does not return the number of bytes read,
// so we must assume that all is read on success,
// and nothing is read on failure.
if err == C.PA_OK {
return len(data), nil
}
return 0, errorFromCode(err)
}
// Stream.Latency returns the playback latency in microseconds.
func (s *Stream) Latency() (uint64, error) {
var err C.int
lat := C.pa_simple_get_latency(s.simple, &err)
if err == C.PA_OK {
return uint64(lat), nil
}
return uint64(lat), errorFromCode(err)
}