├── Makefile ├── hello.go └── src └── github.com └── moriyoshi └── pulsego └── pulsego.go /Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.inc 2 | 3 | TARG=pulse 4 | CGOFILES=\ 5 | pulse.go 6 | CGO_LDFLAGS=-lpulse 7 | CLEANFILES+=hello 8 | 9 | include $(GOROOT)/src/Make.pkg 10 | 11 | %: install %.go 12 | $(GC) $*.go 13 | $(LD) -o $@ $*.$O 14 | -------------------------------------------------------------------------------- /hello.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/moriyoshi/pulsego" 5 | "fmt" 6 | ) 7 | 8 | func send(ch chan int, val int) { 9 | ch <- val 10 | } 11 | 12 | func myMain(sync_ch chan int, pa *pulsego.PulseMainLoop) { 13 | defer send(sync_ch, 0) 14 | ctx := pa.NewContext("default", 0) 15 | if ctx == nil { 16 | fmt.Println("Failed to create a new context") 17 | return 18 | } 19 | defer ctx.Dispose(); 20 | st := ctx.NewStream("default", &pulsego.PulseSampleSpec { 21 | Format:pulsego.SAMPLE_FLOAT32LE, Rate:22500, Channels: 1 }) 22 | if st == nil { 23 | fmt.Println("Failed to create a new stream") 24 | return 25 | } 26 | defer st.Dispose() 27 | st.ConnectToSink() 28 | var samples []float32 = make([]float32, 65536) 29 | period := 40 30 | count := 0 31 | v := -1 32 | for { 33 | for i := range samples { 34 | if count < period / 2 { 35 | samples[i] = -0.8 36 | } else { 37 | samples[i] = 0.8 38 | } 39 | count += 1; 40 | if (count >= period) { 41 | count = 0 42 | } 43 | if i % 40 == 0 { 44 | if period < 10 || period > 100 { v = -v } 45 | period += v 46 | } 47 | } 48 | st.Write(samples, pulsego.SEEK_RELATIVE); 49 | } 50 | } 51 | 52 | func main() { 53 | pa := pulsego.NewPulseMainLoop(); 54 | defer pa.Dispose() 55 | pa.Start() 56 | 57 | sync_ch := make(chan int) 58 | go myMain(sync_ch, pa) 59 | <-sync_ch 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/github.com/moriyoshi/pulsego/pulsego.go: -------------------------------------------------------------------------------- 1 | package pulsego 2 | 3 | /* 4 | #cgo LDFLAGS: -lpulse 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | void cfree(void*); 13 | 14 | typedef struct { 15 | pa_threaded_mainloop *pa; 16 | int done; 17 | int status; 18 | } state_cb_param_t; 19 | 20 | static void context_on_completion(pa_context *ctx, int status, state_cb_param_t *param) 21 | { 22 | param->status = status; 23 | param->done = 1; 24 | pa_threaded_mainloop_signal(param->pa, 0); 25 | } 26 | 27 | static void context_on_state_change(pa_context *ctx, pa_threaded_mainloop *pa) 28 | { 29 | pa_threaded_mainloop_signal(pa, 0); 30 | } 31 | 32 | static void stream_on_state_change(pa_stream *st, pa_threaded_mainloop *pa) 33 | { 34 | pa_threaded_mainloop_signal(pa, 0); 35 | } 36 | 37 | int context_poll_unless(pa_threaded_mainloop *pa, pa_context *ctx, pa_context_state_t state) 38 | { 39 | pa_context_state_t s; 40 | pa_threaded_mainloop_lock(pa); 41 | for (;;) { 42 | s = pa_context_get_state(ctx); 43 | if (s == state || s == PA_CONTEXT_FAILED || s == PA_CONTEXT_TERMINATED) 44 | break; 45 | pa_threaded_mainloop_wait(pa); 46 | } 47 | pa_threaded_mainloop_unlock(pa); 48 | return s; 49 | } 50 | 51 | int context_poll_until_done(state_cb_param_t *param, pa_context *ctx) 52 | { 53 | for (;;) { 54 | if (param->done) 55 | break; 56 | pa_threaded_mainloop_wait(param->pa); 57 | } 58 | return param->status; 59 | } 60 | 61 | int stream_poll_unless(pa_threaded_mainloop *pa, pa_stream *st, pa_stream_state_t state) 62 | { 63 | pa_stream_state_t s; 64 | pa_threaded_mainloop_lock(pa); 65 | for (;;) { 66 | s = pa_stream_get_state(st); 67 | if (s == state || s == PA_STREAM_FAILED || s == PA_STREAM_TERMINATED) 68 | break; 69 | pa_threaded_mainloop_wait(pa); 70 | } 71 | pa_threaded_mainloop_unlock(pa); 72 | return s; 73 | } 74 | 75 | int context_set_default_sink(pa_threaded_mainloop *pa, pa_context *ctx, const char *name) 76 | { 77 | pa_operation *op; 78 | state_cb_param_t param = { pa, 0, 0 }; 79 | 80 | pa_threaded_mainloop_lock(pa); 81 | op = pa_context_set_default_sink(ctx, name, (pa_context_success_cb_t)context_on_completion, pa); 82 | pa_threaded_mainloop_unlock(pa); 83 | context_poll_until_done(¶m, ctx); 84 | pa_operation_unref(op); 85 | return param.status; 86 | } 87 | 88 | int context_set_default_source(pa_threaded_mainloop *pa, pa_context *ctx, const char *name) 89 | { 90 | pa_operation *op; 91 | state_cb_param_t param = { pa, 0, 0 }; 92 | 93 | pa_threaded_mainloop_lock(pa); 94 | op = pa_context_set_default_source(ctx, name, (pa_context_success_cb_t)context_on_completion, pa); 95 | pa_threaded_mainloop_unlock(pa); 96 | context_poll_until_done(¶m, ctx); 97 | pa_operation_unref(op); 98 | return param.status; 99 | } 100 | 101 | int context_exit_daemon(pa_threaded_mainloop *pa, pa_context *ctx) 102 | { 103 | pa_operation *op; 104 | state_cb_param_t param = { pa, 0, 0 }; 105 | 106 | pa_threaded_mainloop_lock(pa); 107 | op = pa_context_exit_daemon(ctx, (pa_context_success_cb_t)context_on_completion, pa); 108 | pa_threaded_mainloop_unlock(pa); 109 | context_poll_until_done(¶m, ctx); 110 | if (op) 111 | pa_operation_unref(op); 112 | return param.status; 113 | } 114 | 115 | int context_drain(pa_threaded_mainloop *pa, pa_context *ctx) 116 | { 117 | pa_operation *op; 118 | state_cb_param_t param = { pa, 0, 0 }; 119 | 120 | pa_threaded_mainloop_lock(pa); 121 | op = pa_context_drain(ctx, (pa_context_notify_cb_t)context_on_state_change, pa); 122 | pa_threaded_mainloop_unlock(pa); 123 | pa_threaded_mainloop_wait(pa); 124 | if (op) 125 | pa_operation_unref(op); 126 | return param.status; 127 | } 128 | 129 | pa_context *context_new(pa_threaded_mainloop *pa, const char *name, pa_context_flags_t flags) 130 | { 131 | pa_mainloop_api *api = pa_threaded_mainloop_get_api(pa); 132 | pa_context *ctx = pa_context_new(api, name); 133 | int err = PA_OK; 134 | 135 | pa_context_set_state_callback(ctx, 136 | (pa_context_notify_cb_t)context_on_state_change, pa); 137 | 138 | { 139 | pa_threaded_mainloop_lock(pa); 140 | err = pa_context_connect(ctx, NULL, flags, NULL); 141 | pa_threaded_mainloop_unlock(pa); 142 | if (err < 0) 143 | return NULL; 144 | } 145 | 146 | if (context_poll_unless(pa, ctx, PA_CONTEXT_READY) != PA_CONTEXT_READY) { 147 | pa_context_unref(ctx); 148 | return NULL; 149 | } 150 | return ctx; 151 | } 152 | 153 | pa_stream *stream_new(pa_threaded_mainloop *pa, pa_context *ctx, const char *name, pa_sample_format_t format, int rate, int channels) 154 | { 155 | pa_stream *st = NULL; 156 | { 157 | pa_threaded_mainloop_lock(pa); 158 | { 159 | pa_sample_spec ss; 160 | ss.format = format; 161 | ss.rate = rate; 162 | ss.channels = channels; 163 | st = pa_stream_new(ctx, name, &ss, NULL); 164 | if (!st) { 165 | pa_threaded_mainloop_unlock(pa); 166 | return NULL; 167 | } 168 | } 169 | 170 | pa_stream_set_state_callback(st, 171 | (pa_stream_notify_cb_t)stream_on_state_change, pa); 172 | pa_threaded_mainloop_unlock(pa); 173 | } 174 | 175 | { 176 | int s = context_poll_unless(pa, ctx, PA_CONTEXT_READY); 177 | if (s != PA_CONTEXT_READY) { 178 | pa_stream_unref(st); 179 | return NULL; 180 | } 181 | } 182 | return st; 183 | } 184 | 185 | int stream_write(pa_threaded_mainloop *pa, pa_stream *p, const void *data, size_t nbytes, pa_seek_mode_t seek) 186 | { 187 | int retval; 188 | pa_threaded_mainloop_lock(pa); 189 | retval = pa_stream_write(p, data, nbytes, NULL, 0, seek); 190 | pa_threaded_mainloop_unlock(pa); 191 | return retval; 192 | } 193 | */ 194 | import "C" 195 | import ( 196 | "unsafe" 197 | "reflect" 198 | ) 199 | 200 | const ( 201 | CONTEXT_UNCONNECTED = C.PA_CONTEXT_UNCONNECTED; 202 | CONTEXT_CONNECTING = C.PA_CONTEXT_CONNECTING; 203 | CONTEXT_AUTHORIZING = C.PA_CONTEXT_AUTHORIZING; 204 | CONTEXT_SETTING_NAME = C.PA_CONTEXT_SETTING_NAME; 205 | CONTEXT_READY = C.PA_CONTEXT_READY; 206 | CONTEXT_FAILED = C.PA_CONTEXT_FAILED; 207 | CONTEXT_TERMINATED = C.PA_CONTEXT_TERMINATED 208 | ) 209 | 210 | const ( 211 | STREAM_UNCONNECTED = C.PA_STREAM_UNCONNECTED; 212 | STREAM_CREATING = C.PA_STREAM_CREATING; 213 | STREAM_READY = C.PA_STREAM_READY; 214 | STREAM_FAILED = C.PA_STREAM_FAILED; 215 | STREAM_TERMINATED = C.PA_STREAM_TERMINATED 216 | ) 217 | 218 | /* 219 | const ( 220 | OPERATION_RUNNING = C.PA_OPERATION_RUNNING; 221 | OPERATION_DONE = C.PA_OPERATION_DONE; 222 | OPERATION_CANCELLED = C.PA_OPERATION_CANCELLED 223 | ) 224 | */ 225 | 226 | const ( 227 | CONTEXT_NOFLAGS = C.PA_CONTEXT_NOFLAGS; 228 | CONTEXT_NOAUTOSPAWN = C.PA_CONTEXT_NOAUTOSPAWN; 229 | CONTEXT_NOFAIL = C.PA_CONTEXT_NOFAIL 230 | ) 231 | 232 | /* 233 | const ( 234 | STREAM_NODIRECTION = C.PA_STREAM_NODIRECTION; 235 | STREAM_PLAYBACK = C.PA_STREAM_PLAYBACK; 236 | STREAM_RECORD = C.PA_STREAM_RECORD; 237 | STREAM_UPLOAD = C.PA_STREAM_UPLOAD 238 | ) 239 | */ 240 | 241 | const ( 242 | STREAM_NOFLAGS = C.PA_STREAM_NOFLAGS; 243 | STREAM_START_CORKED = C.PA_STREAM_START_CORKED; 244 | STREAM_INTERPOLATE_TIMING = C.PA_STREAM_INTERPOLATE_TIMING; 245 | STREAM_NOT_MONOTONIC = C.PA_STREAM_NOT_MONOTONIC; 246 | STREAM_AUTO_TIMING_UPDATE = C.PA_STREAM_AUTO_TIMING_UPDATE; 247 | STREAM_NO_REMAP_CHANNELS = C.PA_STREAM_NO_REMAP_CHANNELS; 248 | STREAM_NO_REMIX_CHANNELS = C.PA_STREAM_NO_REMIX_CHANNELS; 249 | STREAM_FIX_FORMAT = C.PA_STREAM_FIX_FORMAT; 250 | STREAM_FIX_RATE = C.PA_STREAM_FIX_RATE; 251 | STREAM_FIX_CHANNELS = C.PA_STREAM_FIX_CHANNELS; 252 | STREAM_DONT_MOVE = C.PA_STREAM_DONT_MOVE; 253 | STREAM_VARIABLE_RATE = C.PA_STREAM_VARIABLE_RATE; 254 | STREAM_PEAK_DETECT = C.PA_STREAM_PEAK_DETECT; 255 | STREAM_START_MUTED = C.PA_STREAM_START_MUTED; 256 | STREAM_ADJUST_LATENCY = C.PA_STREAM_ADJUST_LATENCY; 257 | STREAM_EARLY_REQUESTS = C.PA_STREAM_EARLY_REQUESTS; 258 | STREAM_DONT_INHIBIT_AUTO_SUSPEND = C.PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND; 259 | STREAM_START_UNMUTED = C.PA_STREAM_START_UNMUTED; 260 | STREAM_FAIL_ON_SUSPEND = C.PA_STREAM_FAIL_ON_SUSPEND 261 | ) 262 | 263 | const ( 264 | OK = C.PA_OK; 265 | ERR_ACCESS = C.PA_ERR_ACCESS; 266 | ERR_COMMAND = C.PA_ERR_COMMAND; 267 | ERR_INVALID = C.PA_ERR_INVALID; 268 | ERR_EXIST = C.PA_ERR_EXIST; 269 | ERR_NOENTITY = C.PA_ERR_NOENTITY; 270 | ERR_CONNECTIONREFUSED = C.PA_ERR_CONNECTIONREFUSED; 271 | ERR_PROTOCOL = C.PA_ERR_PROTOCOL; 272 | ERR_TIMEOUT = C.PA_ERR_TIMEOUT; 273 | ERR_AUTHKEY = C.PA_ERR_AUTHKEY; 274 | ERR_INTERNAL = C.PA_ERR_INTERNAL; 275 | ERR_CONNECTIONTERMINATED = C.PA_ERR_CONNECTIONTERMINATED; 276 | ERR_KILLED = C.PA_ERR_KILLED; 277 | ERR_INVALIDSERVER = C.PA_ERR_INVALIDSERVER; 278 | ERR_MODINITFAILED = C.PA_ERR_MODINITFAILED; 279 | ERR_BADSTATE = C.PA_ERR_BADSTATE; 280 | ERR_NODATA = C.PA_ERR_NODATA; 281 | ERR_VERSION = C.PA_ERR_VERSION; 282 | ERR_TOOLARGE = C.PA_ERR_TOOLARGE; 283 | ERR_NOTSUPPORTED = C.PA_ERR_NOTSUPPORTED; 284 | ERR_UNKNOWN = C.PA_ERR_UNKNOWN; 285 | ERR_NOEXTENSION = C.PA_ERR_NOEXTENSION; 286 | ERR_OBSOLETE = C.PA_ERR_OBSOLETE; 287 | ERR_NOTIMPLEMENTED = C.PA_ERR_NOTIMPLEMENTED; 288 | ERR_FORKED = C.PA_ERR_FORKED; 289 | ERR_IO = C.PA_ERR_IO; 290 | ERR_BUSY = C.PA_ERR_BUSY; 291 | ERR_MAX = C.PA_ERR_MAX 292 | ) 293 | 294 | /* 295 | const ( 296 | SUBSCRIPTION_MASK_NULL = C.PA_SUBSCRIPTION_MASK_NULL; 297 | SUBSCRIPTION_MASK_SINK = C.PA_SUBSCRIPTION_MASK_SINK; 298 | SUBSCRIPTION_MASK_SOURCE = C.PA_SUBSCRIPTION_MASK_SOURCE; 299 | SUBSCRIPTION_MASK_SINK_INPUT = C.PA_SUBSCRIPTION_MASK_SINK_INPUT; 300 | SUBSCRIPTION_MASK_SOURCE_OUTPUT = C.PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT; 301 | SUBSCRIPTION_MASK_MODULE = C.PA_SUBSCRIPTION_MASK_MODULE; 302 | SUBSCRIPTION_MASK_CLIENT = C.PA_SUBSCRIPTION_MASK_CLIENT; 303 | SUBSCRIPTION_MASK_SAMPLE_CACHE = C.PA_SUBSCRIPTION_MASK_SAMPLE_CACHE; 304 | SUBSCRIPTION_MASK_SERVER = C.PA_SUBSCRIPTION_MASK_SERVER; 305 | SUBSCRIPTION_MASK_AUTOLOAD = C.PA_SUBSCRIPTION_MASK_AUTOLOAD; 306 | SUBSCRIPTION_MASK_CARD = C.PA_SUBSCRIPTION_MASK_CARD; 307 | SUBSCRIPTION_MASK_ALL = C.PA_SUBSCRIPTION_MASK_ALL 308 | ) 309 | 310 | const ( 311 | SUBSCRIPTION_EVENT_SINK = C.PA_SUBSCRIPTION_EVENT_SINK; 312 | SUBSCRIPTION_EVENT_SOURCE = C.PA_SUBSCRIPTION_EVENT_SOURCE; 313 | SUBSCRIPTION_EVENT_SINK_INPUT = C.PA_SUBSCRIPTION_EVENT_SINK_INPUT; 314 | SUBSCRIPTION_EVENT_SOURCE_OUTPUT = C.PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT; 315 | SUBSCRIPTION_EVENT_MODULE = C.PA_SUBSCRIPTION_EVENT_MODULE; 316 | SUBSCRIPTION_EVENT_CLIENT = C.PA_SUBSCRIPTION_EVENT_CLIENT; 317 | SUBSCRIPTION_EVENT_SAMPLE_CACHE = C.PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE; 318 | SUBSCRIPTION_EVENT_SERVER = C.PA_SUBSCRIPTION_EVENT_SERVER; 319 | SUBSCRIPTION_EVENT_AUTOLOAD = C.PA_SUBSCRIPTION_EVENT_AUTOLOAD; 320 | SUBSCRIPTION_EVENT_CARD = C.PA_SUBSCRIPTION_EVENT_CARD; 321 | SUBSCRIPTION_EVENT_FACILITY_MASK = C.PA_SUBSCRIPTION_EVENT_FACILITY_MASK; 322 | SUBSCRIPTION_EVENT_NEW = C.PA_SUBSCRIPTION_EVENT_NEW; 323 | SUBSCRIPTION_EVENT_CHANGE = C.PA_SUBSCRIPTION_EVENT_CHANGE; 324 | SUBSCRIPTION_EVENT_REMOVE = C.PA_SUBSCRIPTION_EVENT_REMOVE; 325 | SUBSCRIPTION_EVENT_TYPE_MASK = C.PA_SUBSCRIPTION_EVENT_TYPE_MASK 326 | ) 327 | */ 328 | 329 | const ( 330 | SEEK_RELATIVE = C.PA_SEEK_RELATIVE; 331 | SEEK_ABSOLUTE = C.PA_SEEK_ABSOLUTE; 332 | SEEK_RELATIVE_ON_READ = C.PA_SEEK_RELATIVE_ON_READ; 333 | SEEK_RELATIVE_END = C.PA_SEEK_RELATIVE_END 334 | ) 335 | 336 | /* 337 | const ( 338 | SINK_NOFLAGS = C.PA_SINK_NOFLAGS; 339 | SINK_HW_VOLUME_CTRL = C.PA_SINK_HW_VOLUME_CTRL; 340 | SINK_LATENCY = C.PA_SINK_LATENCY; 341 | SINK_HARDWARE = C.PA_SINK_HARDWARE; 342 | SINK_NETWORK = C.PA_SINK_NETWORK; 343 | SINK_HW_MUTE_CTRL = C.PA_SINK_HW_MUTE_CTRL; 344 | SINK_DECIBEL_VOLUME = C.PA_SINK_DECIBEL_VOLUME; 345 | SINK_FLAT_VOLUME = C.PA_SINK_FLAT_VOLUME; 346 | SINK_DYNAMIC_LATENCY = C.PA_SINK_DYNAMIC_LATENCY 347 | ) 348 | const ( 349 | SINK_INVALID_STATE = C.PA_SINK_INVALID_STATE; 350 | SINK_RUNNING = C.PA_SINK_RUNNING; 351 | SINK_IDLE = C.PA_SINK_IDLE; 352 | SINK_SUSPENDED = C.PA_SINK_SUSPENDED; 353 | SINK_INIT = C.PA_SINK_INIT; 354 | SINK_UNLINKED = C.PA_SINK_UNLINKED 355 | ) 356 | 357 | const ( 358 | SOURCE_NOFLAGS = C.PA_SOURCE_NOFLAGS; 359 | SOURCE_HW_VOLUME_CTRL = C.PA_SOURCE_HW_VOLUME_CTRL; 360 | SOURCE_LATENCY = C.PA_SOURCE_LATENCY; 361 | SOURCE_HARDWARE = C.PA_SOURCE_HARDWARE; 362 | SOURCE_NETWORK = C.PA_SOURCE_NETWORK; 363 | SOURCE_HW_MUTE_CTRL = C.PA_SOURCE_HW_MUTE_CTRL; 364 | SOURCE_DECIBEL_VOLUME = C.PA_SOURCE_DECIBEL_VOLUME; 365 | SOURCE_DYNAMIC_LATENCY = C.PA_SOURCE_DYNAMIC_LATENCY 366 | ) 367 | 368 | const ( 369 | SOURCE_INVALID_STATE = C.PA_SOURCE_INVALID_STATE; 370 | SOURCE_RUNNING = C.PA_SOURCE_RUNNING; 371 | SOURCE_IDLE = C.PA_SOURCE_IDLE; 372 | SOURCE_SUSPENDED = C.PA_SOURCE_SUSPENDED; 373 | SOURCE_INIT = C.PA_SOURCE_INIT; 374 | SOURCE_UNLINKED = C.PA_SOURCE_UNLINKED 375 | ) 376 | */ 377 | 378 | const ( 379 | SAMPLE_U8 = C.PA_SAMPLE_U8; 380 | SAMPLE_ALAW = C.PA_SAMPLE_ALAW; 381 | SAMPLE_ULAW = C.PA_SAMPLE_ULAW; 382 | SAMPLE_S16LE = C.PA_SAMPLE_S16LE; 383 | SAMPLE_S16BE = C.PA_SAMPLE_S16BE; 384 | SAMPLE_FLOAT32LE = C.PA_SAMPLE_FLOAT32LE; 385 | SAMPLE_FLOAT32BE = C.PA_SAMPLE_FLOAT32BE; 386 | SAMPLE_S32LE = C.PA_SAMPLE_S32LE; 387 | SAMPLE_S32BE = C.PA_SAMPLE_S32BE; 388 | SAMPLE_S24LE = C.PA_SAMPLE_S24LE; 389 | SAMPLE_S24BE = C.PA_SAMPLE_S24BE; 390 | SAMPLE_S24_32LE = C.PA_SAMPLE_S24_32LE; 391 | SAMPLE_S24_32BE = C.PA_SAMPLE_S24_32BE; 392 | SAMPLE_MAX = C.PA_SAMPLE_MAX; 393 | SAMPLE_INVALID = C.PA_SAMPLE_INVALID 394 | ) 395 | 396 | type PulseMainLoop struct { 397 | pa *C.pa_threaded_mainloop; 398 | } 399 | 400 | type PulseContext struct { 401 | MainLoop *PulseMainLoop; 402 | ctx *C.pa_context; 403 | } 404 | 405 | type PulseStream struct { 406 | Context *PulseContext; 407 | st *C.pa_stream; 408 | } 409 | 410 | type PulseSampleSpec struct { 411 | Format int; 412 | Rate int; 413 | Channels int; 414 | } 415 | 416 | func (self *PulseStream) Disconnect() { 417 | C.pa_threaded_mainloop_lock(self.Context.MainLoop.pa); 418 | C.pa_stream_disconnect(self.st); 419 | C.pa_threaded_mainloop_unlock(self.Context.MainLoop.pa) 420 | } 421 | 422 | func (self *PulseStream) Dispose() { 423 | self.Disconnect(); 424 | C.pa_stream_unref(self.st) 425 | } 426 | 427 | func (self *PulseStream) ConnectToSink() int { 428 | C.pa_threaded_mainloop_lock(self.Context.MainLoop.pa); 429 | err := C.pa_stream_connect_playback(self.st, nil, nil, 0, nil, nil); 430 | C.pa_threaded_mainloop_unlock(self.Context.MainLoop.pa); 431 | if err == OK { 432 | err = C.stream_poll_unless(self.Context.MainLoop.pa, self.st, STREAM_READY) 433 | } 434 | return int(err) 435 | } 436 | 437 | func (self *PulseStream) GetSampleSpec() PulseSampleSpec { 438 | spec := C.pa_stream_get_sample_spec(self.st); 439 | return PulseSampleSpec { 440 | Format: int(spec.format), 441 | Rate: int(spec.rate), 442 | Channels: int(spec.channels), 443 | } 444 | } 445 | 446 | func (self *PulseStream) Write(data interface{}, flags int) int { 447 | typ := reflect.TypeOf(data); 448 | format := self.GetSampleSpec().Format; 449 | samples := reflect.ValueOf(data) 450 | nsamples := samples.Len(); 451 | ptr := unsafe.Pointer(samples.Index(0).UnsafeAddr()); 452 | switch typ.Elem().Kind() { 453 | case reflect.Int32: 454 | if format != SAMPLE_S32LE && format != SAMPLE_S24_32LE { 455 | return ERR_INVALID 456 | } 457 | case reflect.Float32: 458 | if format != SAMPLE_FLOAT32LE { 459 | return ERR_INVALID 460 | } 461 | case reflect.Int16: 462 | if format != SAMPLE_S16LE { 463 | return ERR_INVALID 464 | } 465 | case reflect.Uint8: 466 | if format != SAMPLE_U8 { 467 | return ERR_INVALID 468 | } 469 | } 470 | retval := int(C.stream_write(self.Context.MainLoop.pa, self.st, ptr, C.size_t(typ.Elem().Size() * uintptr(nsamples)), C.pa_seek_mode_t(flags))); 471 | return retval 472 | } 473 | 474 | func (self *PulseContext) Disconnect() { 475 | C.pa_threaded_mainloop_lock(self.MainLoop.pa); 476 | C.pa_context_disconnect(self.ctx); 477 | C.pa_threaded_mainloop_unlock(self.MainLoop.pa) 478 | } 479 | 480 | func (self *PulseContext) Dispose() { 481 | self.Disconnect(); 482 | C.pa_context_unref(self.ctx) 483 | } 484 | 485 | func (self *PulseContext) Drain() int { 486 | return int(C.context_drain(self.MainLoop.pa, self.ctx)) 487 | } 488 | 489 | func (self *PulseContext) ExitDaemon() int { 490 | return int(C.context_exit_daemon(self.MainLoop.pa, self.ctx)) 491 | } 492 | 493 | func (self *PulseContext) SetDefaultSource(name string) int { 494 | name_ := C.CString(name); 495 | retval := int(C.context_set_default_source(self.MainLoop.pa, self.ctx, name_)); 496 | C.cfree(unsafe.Pointer(name_)); 497 | return retval 498 | } 499 | 500 | func (self *PulseContext) SetDefaultSink(name string) int { 501 | name_ := C.CString(name); 502 | retval := int(C.context_set_default_sink(self.MainLoop.pa, self.ctx, name_)); 503 | C.cfree(unsafe.Pointer(name_)); 504 | return retval 505 | } 506 | 507 | func (self *PulseContext) NewStream(name string, spec *PulseSampleSpec) *PulseStream { 508 | name_ := C.CString(name); 509 | st := C.stream_new(self.MainLoop.pa, self.ctx, name_, C.pa_sample_format_t(spec.Format), C.int(spec.Rate), C.int(spec.Channels)); 510 | var retval *PulseStream = nil; 511 | if st != nil { 512 | retval = &PulseStream { Context: self, st: st } 513 | } 514 | C.cfree(unsafe.Pointer(name_)); 515 | return retval 516 | } 517 | 518 | func (self *PulseMainLoop) NewContext(name string, flags int) *PulseContext { 519 | name_ := C.CString(name); 520 | ctx := C.context_new(self.pa, name_, C.pa_context_flags_t(flags)); 521 | var retval *PulseContext = nil; 522 | if ctx != nil { 523 | retval = &PulseContext { MainLoop: self, ctx: ctx } 524 | } 525 | C.cfree(unsafe.Pointer(name_)); 526 | return retval 527 | } 528 | 529 | func (self *PulseMainLoop) Start() int { 530 | return int(C.pa_threaded_mainloop_start(self.pa)); 531 | } 532 | 533 | func (self *PulseMainLoop) Dispose() { 534 | C.pa_threaded_mainloop_free(self.pa); 535 | } 536 | 537 | func NewPulseMainLoop() *PulseMainLoop { 538 | pa := C.pa_threaded_mainloop_new(); 539 | if pa == nil { return nil; } 540 | return &PulseMainLoop { pa: pa }; 541 | } 542 | --------------------------------------------------------------------------------