diff options
author | Paul W. Frields <stickster@gmail.com> | 2009-05-23 17:16:46 -0400 |
---|---|---|
committer | Paul W. Frields <stickster@gmail.com> | 2009-05-23 17:16:46 -0400 |
commit | 1846a632d0f450839af538c2105f1a30ec1c6398 (patch) | |
tree | 056d4f9610d4e2cb5973da009e77c607e774c7df /pulseaudio | |
parent | ccf4ecaaf1136cb01bc10bffb337acad88165d1d (diff) | |
download | pulsecaster-1846a632d0f450839af538c2105f1a30ec1c6398.tar.gz pulsecaster-1846a632d0f450839af538c2105f1a30ec1c6398.tar.xz pulsecaster-1846a632d0f450839af538c2105f1a30ec1c6398.zip |
Add simple PulseAudio bindings for Python
Thank you to Harry Karvonen <harry.karvonen@gmail.com> for providing
this very helpful code!
Diffstat (limited to 'pulseaudio')
-rw-r--r-- | pulseaudio/PulseClient.py | 66 | ||||
-rw-r--r-- | pulseaudio/PulseClient.pyc | bin | 0 -> 2021 bytes | |||
-rw-r--r-- | pulseaudio/PulseObj.py | 589 | ||||
-rw-r--r-- | pulseaudio/PulseObj.pyc | bin | 0 -> 9836 bytes | |||
-rw-r--r-- | pulseaudio/PulseSink.py | 213 | ||||
-rw-r--r-- | pulseaudio/PulseSink.pyc | bin | 0 -> 7116 bytes | |||
-rw-r--r-- | pulseaudio/PulseSource.py | 187 | ||||
-rw-r--r-- | pulseaudio/PulseSource.pyc | bin | 0 -> 5088 bytes | |||
-rw-r--r-- | pulseaudio/PulseVolume.py | 114 | ||||
-rw-r--r-- | pulseaudio/PulseVolume.pyc | bin | 0 -> 3553 bytes | |||
-rw-r--r-- | pulseaudio/__init__.py | 29 | ||||
-rw-r--r-- | pulseaudio/lib_pulseaudio.py | 513 | ||||
-rw-r--r-- | pulseaudio/lib_pulseaudio.pyc | bin | 0 -> 7423 bytes |
13 files changed, 1711 insertions, 0 deletions
diff --git a/pulseaudio/PulseClient.py b/pulseaudio/PulseClient.py new file mode 100644 index 0000000..6705080 --- /dev/null +++ b/pulseaudio/PulseClient.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# vi: et sw=2 +# +# PulseClient.py +# Copyright (C) 2009 Harry Karvonen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# +# Author: Harry Karvonen <harry.karvonen@gmail.com> +# + +from lib_pulseaudio import * + +# This class contains all basic client features +class PulseClient: + def __init__(self, name, index = 0): + self.index = index + self.name = name + return + + ### + + def printDebug(self): + print "self.index:", self.index + print "self.name:", self.name + return + + ### + + def __str__(self): + return "Client-ID: " + str(self.index) + ", Name: \"" + self.name + "\"" + +################################################################################ + +# This class is used with Ctypes +class PulseClientCtypes(PulseClient): + def __init__(self, pa_client): + PulseClient.__init__(self, pa_client.name, pa_client.index) + + self.owner_module = pa_client.owner_module + self.driver = pa_client.driver + #self.proplist = pa_sink_input_info.proplist + return + + ### + + def printDebug(self): + print "PulseClientCtypes" + PulseClient.printDebug(self) + print "self.owner_module:", self.owner_module + print "self.driver:", self.driver + #print "self.proplist:", self.proplist + return + diff --git a/pulseaudio/PulseClient.pyc b/pulseaudio/PulseClient.pyc Binary files differnew file mode 100644 index 0000000..47b7e37 --- /dev/null +++ b/pulseaudio/PulseClient.pyc diff --git a/pulseaudio/PulseObj.py b/pulseaudio/PulseObj.py new file mode 100644 index 0000000..db6e866 --- /dev/null +++ b/pulseaudio/PulseObj.py @@ -0,0 +1,589 @@ +#!/usr/bin/python +# vi: et sw=2 +# +# PulseObj.py +# Copyright (C) 2009 Harry Karvonen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# +# Author: Harry Karvonen <harry.karvonen@gmail.com> +# + +from lib_pulseaudio import * +from PulseSink import PulseSinkInputInfo, PulseSinkInfo +from PulseSource import PulseSourceOutputInfo, PulseSourceInfo +from PulseClient import PulseClientCtypes + +################################################################################ +# +# Classes +# +################################################################################ + +class PulseObj: + "Basic PulseAudio object" + + ############################################################################## + # + # Init + # + ############################################################################## + + def __init__(self, server = None, retry = False): + "Initialise pulseaudio connection" + + # Variables + self.server = server + self.mainloop = None + self.mainloop_api = None + self.context = None + self.ret = None + self.retry = retry + self.operation = None + self.connected = False + self.action_done = False + self.data = None + + # Init + + # + # Callbacks + # + self.PA_SIGNAL_CB = PA_SIGNAL_CB_T(self.py_signal_cb) + + self.PA_STATE_CB = PA_STATE_CB_T(self.py_state_cb) + # + # Mainloop init + # + self.mainloop = pa_mainloop_new() + + self.mainloop_api = pa_mainloop_get_api(self.mainloop) + + # + # Signal binding + # + r = pa_signal_init(self.mainloop_api) + + if r != 0: + # FIXME + print "FIXME Do something. Something is wrong" + + # SIGINT + pa_signal_new(2, self.PA_SIGNAL_CB, None) + + # SIGTERM + pa_signal_new(15, self.PA_SIGNAL_CB, None) + + # + # Context creating + # + self.context = pa_context_new(self.mainloop_api, "PulseApplet") + + pa_context_set_state_callback(self.context, self.PA_STATE_CB, None) + + self.start_action() + + # + # Connect + # + if 0 > pa_context_connect(self.context, + self.server, + 0, + None): + if self.retry: + pa_context_disconnect(self.context) + return + self.pulse_context_error() + + self.pulse_iterate() + + return + + ############################################################################## + # + # Callback methods + # + # FIXME: rename methods better + # + ############################################################################## + + def py_signal_cb(self, api, e, sig, userdata): + #print "py_signal_cb:", api, e, sig, userdata + + if sig == 2: + self.pulse_disconnect() + elif sig == 15: + self.pulse_disconnect() + + return 0 + + ### + + def py_state_cb(self, c, b): + #print "py_state_cb:", c[0]._opaque_struct, b + state = pa_context_get_state(c); + + + if state == 0: + None + #print "py_state_cb: Unconnected" + + elif state == 1: + None + #print "py_state_cb: Connecting" + + elif state == 2: + None + #print "py_state_cb: Authorizing" + + elif state == 3: + None + #print "py_state_cb: Setting name" + + elif state == 4: + #print "py_state_cb: Ready" + self.complete_action() + self.connected = True + + elif state == 5: + None + #print "py_state_cb: Failed" + + elif state == 6: + None + #print "py_state_cb: Terminated" + if not self.retry: + import sys + sys.exit(pa_context_errno(c)) + + self.complete_action() + + else: + None + #print "py_state_cb: Unknown state", state + + + #print "py_state_cb:", pa_strerror(pa_context_errno(c)) + return 0 + + ### + + def py_client_cb(self, c, client_info, endofdata, userdata): + "Sink callback" + #print "py_client_cb:", c, client_info, endofdata, userdata + + if (endofdata): + self.complete_action() + return 0 + + if self.data == None: + self.data = [ PulseClientCtypes(client_info[0]) ] + else: + self.data.append(PulseClientCtypes(client_info[0])) + + return 0 + + ### + + def py_sink_input_cb(self, c, sink_input_info, endofdata, userdata): + "Sink input callback" + #print "py_sink_input_cb:", c, sink_input_info, endofdata, userdata + + if (endofdata): + self.complete_action() + return 0 + + if self.data == None: + self.data = [ PulseSinkInputInfo(sink_input_info[0]) ] + else: + self.data.append(PulseSinkInputInfo(sink_input_info[0])) + + return 0 + + ### + + def py_sink_cb(self, c, sink_info, endofdata, userdata): + "Sink callback" + #print "py_sink_cb:", c, sink_info, endofdata, userdata + + if (endofdata): + self.complete_action() + return 0 + + if self.data == None: + self.data = [ PulseSinkInfo(sink_info[0]) ] + else: + self.data.append(PulseSinkInfo(sink_info[0])) + + return 0 + ### + + def py_source_output_cb(self, c, source_output_info, endofdata, userdata): + "Source output callback" + #print "py_source_output_cb:", c, source_output_info, endofdata, userdata + + if (endofdata): + self.complete_action() + return 0 + + if self.data == None: + self.data = [ PulseSourceOutputInfo(source_output_info[0]) ] + else: + self.data.append(PulseSourceOutputInfo(source_output_info[0])) + + return 0 + + ### + + def py_source_cb(self, c, source_info, endofdata, userdata): + "Source callback" + #print "py_source_cb:", c, source_info, endofdata, userdata + + if (endofdata): + self.complete_action() + return 0 + + if self.data == None: + self.data = [ PulseSourceInfo(source_info[0]) ] + else: + self.data.append(PulseSourceInfo(source_info[0])) + + return 0 + ### + + def py_drain_cb(self, c, userdata): + #print "py_drain_cb: called" + return + + ### + + def py_context_success(self, c, success, userdata): + if success == 0: + None + #print "py_context_success: Failed" + else: + None + #print "py_context_success: Success" + + self.complete_action() + return 0 + + ############################################################################## + # + ############################################################################## + + def complete_action(self): + "Completed action" + #print "complete_action: Called" + self.action_done = True + return + + ### + + def start_action(self): + "Call every time when starting action" + #print "start_action: Called" + self.action_done = False + return + + ### + + def pulse_disconnect(self): + "Call when disconnect object" + + #print "pulse_disconnect: Disconnecting" + pa_context_disconnect(self.context) + pa_mainloop_free(self.mainloop) + return + + ### + + def pulse_context_error(self): + "Print context error msg" + + #print "pulse_context_error:", pa_strerror(pa_context_errno(self.context)) + self.pulse_disconnect() + return + + ### + + def pulse_sink_input_list(self): + "List all sink input" + #print "pulse_sink_input_list: Called"; + + return_data = None + + self.start_action() + + # Callback function + SINK_INPUT_LIST_CB = PA_SINK_INPUT_INFO_CB_T(self.py_sink_input_cb) + + self.operation = pa_context_get_sink_input_info_list(self.context, + SINK_INPUT_LIST_CB, + None) + self.pulse_iterate() + + #print "pulse_sink_input_list:", self.data + return_data = self.data + self.data = None + + return return_data + + ### + + def pulse_sink_list(self): + "List all sinks" + #print "pulse_sink_list: Called"; + + return_data = None + + self.start_action() + + # Callback function + SINK_LIST_CB = PA_SINK_INFO_CB_T(self.py_sink_cb) + + self.operation = pa_context_get_sink_info_list(self.context, + SINK_LIST_CB, + None) + self.pulse_iterate() + + #print "pulse_sink_list:", self.data + return_data = self.data + self.data = None + + return return_data + + ### + + def pulse_source_output_list(self): + "List all source outputs" + #print "pulse_source_output_list: Called"; + + return_data = None + + self.start_action() + + # Callback function + SOURCE_OUTPUT_LIST_CB = PA_SOURCE_OUTPUT_INFO_CB_T(self.py_source_output_cb) + + self.operation = pa_context_get_source_output_info_list(self.context, + SOURCE_OUTPUT_LIST_CB, + None) + self.pulse_iterate() + + #print "pulse_source_output_list:", self.data + return_data = self.data + self.data = None + + return return_data + + ### + + def pulse_source_list(self): + "List all sources" + #print "pulse_source_list: Called"; + + return_data = None + + self.start_action() + + # Callback function + SOURCE_LIST_CB = PA_SOURCE_INFO_CB_T(self.py_source_cb) + + self.operation = pa_context_get_source_info_list(self.context, + SOURCE_LIST_CB, + None) + self.pulse_iterate() + + #print "pulse_source_list:", self.data + return_data = self.data + self.data = None + + return return_data + + ### + def pulse_client_list(self): + "Fetch all clients" + + self.start_action() + + CLIENT_INFO_CB = PA_CLIENT_INFO_CB_T(self.py_client_cb) + + self.operation = pa_context_get_client_info_list(self.context, + CLIENT_INFO_CB, + None) + + self.pulse_iterate() + + #print "pulse_client_list:", self.data + return_data = self.data + self.data = None + + return return_data + + ### + + def pulse_sink_input_mute(self, index, mute): + "Mute one stream by index" + + self.start_action() + + CONTEXT_SUCCESS = PA_CONTEXT_SUCCESS_CB_T(self.py_context_success) + + self.operation = pa_context_set_sink_input_mute(self.context, + index, + mute, # Mute = 1 + CONTEXT_SUCCESS, + None) + + self.pulse_iterate() + + return + + ### + + def pulse_sink_mute(self, index, mute): + "Mute sink by index" + + self.start_action() + + CONTEXT_SUCCESS = PA_CONTEXT_SUCCESS_CB_T(self.py_context_success) + + self.operation = pa_context_set_sink_mute_by_index(self.context, + index, + mute, # Mute = 1 + CONTEXT_SUCCESS, + None) + + self.pulse_iterate() + + return + + ### + + def pulse_mute_stream(self, index): + self.pulse_sink_input_mute(index, 1) + return + + ### + + def pulse_unmute_stream(self, index): + self.pulse_sink_input_mute(index, 0) + return + + ### + + def pulse_mute_sink(self, index): + self.pulse_sink_mute(index, 1) + return + + ### + + def pulse_unmute_sink(self, index): + self.pulse_sink_mute(index, 0) + return + + ### + + def pulse_set_sink_input_volume(self, index, vol): + "Set volume by index" + self.start_action() + + #print "pulse_set_sink_input_volume:", index, "Vol:", vol + + #print vol.values + #for a in vol.toCtypes().values: + # print a + #return + + PA_CONTEXT_SUCCESS_CB = PA_CONTEXT_SUCCESS_CB_T(self.py_context_success) + + self.opertarion = pa_context_set_sink_input_volume(self.context, + index, + vol.toCtypes(), + PA_CONTEXT_SUCCESS_CB, + None) + + self.pulse_iterate() + + return + + ### + + def pulse_set_sink_volume(self, index, vol): + "Set volume by index" + self.start_action() + + #print "pulse_set_sink_volume:", index, "Vol:", vol + + PA_CONTEXT_SUCCESS_CB = PA_CONTEXT_SUCCESS_CB_T(self.py_context_success) + + self.opertarion = pa_context_set_sink_volume_by_index(self.context, + index, + vol.toCtypes(), + PA_CONTEXT_SUCCESS_CB, + None) + + self.pulse_iterate() + + return + + ### + + def reconnect(self): + self.context = pa_context_new(self.mainloop_api, "PulseApplet") + + pa_context_set_state_callback(self.context, self.PA_STATE_CB, None) + + self.start_action() + + + if 0 > pa_context_connect(self.context, + self.server, + 0, + None): + if self.retry: + pa_context_disconnect(self.context) + #print "bar" + return + self.pulse_context_error() + #print "foo" + + self.pulse_iterate() + + return + + ### + + def pulse_iterate(self, times = 1): + "Runs queries" + #print "pulse_iterate: Called" + self.ret = pointer(c_int()) + + pa_mainloop_iterate(self.mainloop, times, self.ret) + + while not self.action_done: + pa_mainloop_iterate(self.mainloop, times, self.ret) + + return + + ### + + def pulse_run(self): + self.ret = pointer(c_int(0)) + + #pa_mainloop_iterate(self.mainloop, 11, self.ret) + pa_mainloop_run(self.mainloop, self.ret) + return diff --git a/pulseaudio/PulseObj.pyc b/pulseaudio/PulseObj.pyc Binary files differnew file mode 100644 index 0000000..22d7342 --- /dev/null +++ b/pulseaudio/PulseObj.pyc diff --git a/pulseaudio/PulseSink.py b/pulseaudio/PulseSink.py new file mode 100644 index 0000000..e76c386 --- /dev/null +++ b/pulseaudio/PulseSink.py @@ -0,0 +1,213 @@ +#!/usr/bin/python +# vi: et sw=2 +# +# PulseSink.py +# Copyright (C) 2009 Harry Karvonen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# +# Author: Harry Karvonen <harry.karvonen@gmail.com> +# + +from lib_pulseaudio import * + +from PulseClient import PulseClient +from PulseVolume import PulseVolumeCtypes + +# This class contains all commons features from PulseSinkInputInfo and +# PulseSinkInfo +class PulseSink: + def __init__(self, index, name, mute, volume, client): + self.index = index + self.name = name + self.mute = mute + self.volume = volume + self.client = client + return + + # PROTOTYPE + def unmuteStream(self): + raise Exception("ABSTRACT METHOD CALLED") + return + + # PROTOTYPE + def muteStream(self): + raise Exception("ABSTRACT METHOD CALLED") + return + + # PROTOTYPE + def setVolume(self): + raise Exception("ABSTRACT METHOD CALLED") + return + + def printDebug(self): + print "self.index:", self.index + print "self.name:", self.name + print "self.mute:", self.mute + print "self.volume:", self.volume + print "self.client:", self.client + return + +################################################################################ + +class PulseSinkInfo(PulseSink): + def __init__(self, pa_sink_info): + PulseSink.__init__(self, pa_sink_info.index, + pa_sink_info.name, + pa_sink_info.mute, + PulseVolumeCtypes(pa_sink_info.volume), + PulseClient("Selected Sink")) + + self.desctiption = pa_sink_info.description + self.sample_spec = pa_sink_info.sample_spec + self.channel_map = pa_sink_info.channel_map + self.owner_module = pa_sink_info.owner_module + self.monitor_source = pa_sink_info.monitor_source + self.monitor_source_name = pa_sink_info.monitor_source_name + self.latency = pa_sink_info.latency + self.driver = pa_sink_info.driver + self.flags = pa_sink_info.flags + self.proplist = pa_sink_info.proplist + self.configured_latency = pa_sink_info.configured_latency + + return + + ### + # + # Define PROTOTYPE functions + + def unmuteStream(self, pulseInterface): + pulseInterface.pulse_unmute_sink(self.index) + + self.mute = 0 + return + + ### + + def muteStream(self, pulseInterface): + pulseInterface.pulse_mute_sink(self.index) + + self.mute = 1 + return + + ### + + def setVolume(self, pulseInterface, volume): + pulseInterface.pulse_set_sink_volume(self.index, volume) + + self.volume = volume + return + + ### + + def printDebug(self): + print "PulseSinkInfo" + PulseSink.printDebug(self) + print "self.desctiption", self.desctiption + print "self.sample_spec", self.sample_spec + print "self.channel_map", self.channel_map + print "self.owner_module", self.owner_module + print "self.monitor_source", self.monitor_source + print "self.monitor_source_name", self.monitor_source_name + print "self.latency", self.latency + print "self.driver", self.driver + print "self.flags", self.flags + print "self.proplist", self.proplist + print "self.configured_latency", self.configured_latency + return + + ### + + def __str__(self): + return "ID: " + str(self.index) + ", Name: \"" + \ + self.name + "\"" + +################################################################################ + +class PulseSinkInputInfo(PulseSink): + def __init__(self, pa_sink_input_info): + PulseSink.__init__(self, pa_sink_input_info.index, + pa_sink_input_info.name, + pa_sink_input_info.mute, + PulseVolumeCtypes(pa_sink_input_info.volume), + PulseClient("Unknown client")) + self.owner_module = pa_sink_input_info.owner_module + self.client_id = pa_sink_input_info.client + self.sink = pa_sink_input_info.sink + self.sample_spec = pa_sink_input_info.sample_spec + self.channel_map = pa_sink_input_info.channel_map + self.buffer_usec = pa_sink_input_info.buffer_usec + self.sink_usec = pa_sink_input_info.sink_usec + self.resample_method = pa_sink_input_info.resample_method + self.driver = pa_sink_input_info.driver + #self.proplist = pa_sink_input_info.proplist + + return + + ### + + def setClient(self, c): + self.client = c + + ### + # + # Define PROTOTYPE functions + + def unmuteStream(self, pulseInterface): + pulseInterface.pulse_unmute_stream(self.index) + + self.mute = 0 + return + + ### + + def muteStream(self, pulseInterface): + pulseInterface.pulse_mute_stream(self.index) + + self.mute = 1 + return + + ### + + def setVolume(self, pulseInterface, volume): + pulseInterface.pulse_set_sink_input_volume(self.index, volume) + + self.volume = volume + return + + ### + + def printDebug(self): + print "PulseSinkInputInfo" + PulseSink.printDebug(self) + + print "self.owner_module:", self.owner_module + print "self.client_id:", self.client_id + print "self.sink:", self.sink + print "self.sample_spec:", self.sample_spec + print "self.channel_map:", self.channel_map + print "self.buffer_usec:", self.buffer_usec + print "self.sink_usec:", self.sink_usec + print "self.resample_method:", self.resample_method + print "self.driver:", self.driver + + ### + + def __str__(self): + if self.client: + return "ID: " + str(self.index) + ", Name: \"" + \ + self.name + "\", mute: " + str(self.mute) + ", " + str(self.client) + return "ID: " + str(self.index) + ", Name: \"" + \ + self.name + "\", mute: " + str(self.mute) diff --git a/pulseaudio/PulseSink.pyc b/pulseaudio/PulseSink.pyc Binary files differnew file mode 100644 index 0000000..6b63538 --- /dev/null +++ b/pulseaudio/PulseSink.pyc diff --git a/pulseaudio/PulseSource.py b/pulseaudio/PulseSource.py new file mode 100644 index 0000000..4856477 --- /dev/null +++ b/pulseaudio/PulseSource.py @@ -0,0 +1,187 @@ +#!/usr/bin/python +# vi: et sw=2 +# +# PulseSource.py +# Copyright (C) 2009 Harry Karvonen, Paul W. Frields +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# +# Author: Harry Karvonen <harry.karvonen@gmail.com> +# Paul W. Frields <stickster@gmail.com> +# + +from lib_pulseaudio import * + +from PulseClient import PulseClient +from PulseVolume import PulseVolumeCtypes + +# This class contains all commons features from PulseSourceInputInfo and +# PulseSourceInfo +class PulseSource: + def __init__(self, index, name, mute, volume, client): + self.index = index + self.name = name + self.mute = mute + self.volume = volume + self.client = client + return + + # PROTOTYPE + def unmuteStream(self): + raise Exception("ABSTRACT METHOD CALLED") + return + + # PROTOTYPE + def muteStream(self): + raise Exception("ABSTRACT METHOD CALLED") + return + + # PROTOTYPE + def setVolume(self): + raise Exception("ABSTRACT METHOD CALLED") + return + + def printDebug(self): + print "self.index:", self.index + print "self.name:", self.name + print "self.mute:", self.mute + print "self.volume:", self.volume + print "self.client:", self.client + return + +################################################################################ + +class PulseSourceInfo(PulseSource): + def __init__(self, pa_source_info): + PulseSource.__init__(self, pa_source_info.index, + pa_source_info.name, + pa_source_info.mute, + PulseVolumeCtypes(pa_source_info.volume), + PulseClient("Selected Source")) + + self.description = pa_source_info.description + self.sample_spec = pa_source_info.sample_spec + self.channel_map = pa_source_info.channel_map + self.owner_module = pa_source_info.owner_module + self.latency = pa_source_info.latency + self.driver = pa_source_info.driver + self.flags = pa_source_info.flags + self.proplist = pa_source_info.proplist + self.configured_latency = pa_source_info.configured_latency + + return + + ### + # + # Define PROTOTYPE functions + + def unmuteStream(self, pulseInterface): + pulseInterface.pulse_unmute_source(self.index) + + self.mute = 0 + return + + ### + + def muteStream(self, pulseInterface): + pulseInterface.pulse_mute_source(self.index) + + self.mute = 1 + return + + ### + + def setVolume(self, pulseInterface, volume): + pulseInterface.pulse_set_source_volume(self.index, volume) + + self.volume = volume + return + + ### + + def printDebug(self): + print "PulseSourceInfo" + PulseSource.printDebug(self) + print "self.description", self.description + print "self.sample_spec", self.sample_spec + print "self.channel_map", self.channel_map + print "self.owner_module", self.owner_module + print "self.monitor_of_sink", self.monitor_of_sink + print "self.monitor_of_sink_name", self.monitor_of_sink_name + print "self.latency", self.latency + print "self.driver", self.driver + print "self.flags", self.flags + print "self.proplist", self.proplist + print "self.configured_latency", self.configured_latency + return + + ### + + def __str__(self): + return "ID: " + str(self.index) + ", Name: \"" + \ + self.name + "\"" + +################################################################################ + +class PulseSourceOutputInfo(PulseSource): + def __init__(self, pa_source_output_info): + PulseSource.__init__(self, pa_source_output_info.index, + pa_source_output_info.name, + pa_source_output_info.mute, + PulseVolumeCtypes(pa_source_output_info.volume), + PulseClient("Unknown client")) + self.owner_module = pa_source_output_info.owner_module + self.client_id = pa_source_output_info.client + self.source = pa_source_output_info.source + self.sample_spec = pa_source_output_info.sample_spec + self.channel_map = pa_source_output_info.channel_map + self.buffer_usec = pa_source_output_info.buffer_usec + self.source_usec = pa_source_output_info.source_usec + self.resample_method = pa_source_output_info.resample_method + self.driver = pa_source_output_info.driver + #self.proplist = pa_source_output_info.proplist + + return + + ### + + def setClient(self, c): + self.client = c + + ### + + def printDebug(self): + print "PulseSourceInputInfo" + PulseSource.printDebug(self) + + print "self.owner_module:", self.owner_module + print "self.client_id:", self.client_id + print "self.source:", self.source + print "self.sample_spec:", self.sample_spec + print "self.channel_map:", self.channel_map + print "self.buffer_usec:", self.buffer_usec + print "self.source_usec:", self.source_usec + print "self.resample_method:", self.resample_method + print "self.driver:", self.driver + + ### + + def __str__(self): + if self.client: + return "ID: " + str(self.index) + ", Name: \"" + \ + self.name + "\", mute: " + str(self.mute) + ", " + str(self.client) + return "ID: " + str(self.index) + ", Name: \"" + \ + self.name + "\", mute: " + str(self.mute) + diff --git a/pulseaudio/PulseSource.pyc b/pulseaudio/PulseSource.pyc Binary files differnew file mode 100644 index 0000000..98fb391 --- /dev/null +++ b/pulseaudio/PulseSource.pyc diff --git a/pulseaudio/PulseVolume.py b/pulseaudio/PulseVolume.py new file mode 100644 index 0000000..1ff6b7a --- /dev/null +++ b/pulseaudio/PulseVolume.py @@ -0,0 +1,114 @@ +#!/usr/bin/python +# vi: et sw=2 +# +# PulseVolume.py +# Copyright (C) 2009 Harry Karvonen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# +# Author: Harry Karvonen <harry.karvonen@gmail.com> +# + +from lib_pulseaudio import * +import math + +# This contains all basic volume features +class PulseVolume: + def __init__(self, vol = 0, channels = 2): + self.channels = channels + + if vol > 100 or vol < 0: + print "WARNING: Volume is invalid!" + vol = 0 + + self.values = [vol] * self.channels + + return + + ############################## + # + # Type conversions + # + #def fromCtypes(self, pa_cvolume): + # self.channels = pa_cvolume.channels + # self.values = map(lambda x: (math.ceil(float(x) * 100 / PA_VOLUME_NORM)), + # pa_cvolume.values[0:self.channels]) + # return self + + def toCtypes(self): + ct = PA_CVOLUME() + ct.channels = self.channels + + for x in range(0, self.channels): + ct.values[x] = (self.values[x] * PA_VOLUME_NORM) / 100 + + return ct + + ### + + def printDebug(self): + print "PulseVolume" + print "self.channels:", self.channels + print "self.values:", self.values + #print "self.proplist:", self.proplist + + ### + + def incVolume(self, vol): + "Increment volume level (mono only)" + vol += sum(self.values) / len(self.values) + + vol = int(vol) + + if vol > 100: + vol = 100 + elif vol < 0: + vol = 0 + + self.setVolume(vol) + + return + + ### + + def setVolume(self, vol, balance = None): + if not balance: + self.values = [vol] * self.channels + else: + self.values[balance] = vol + + return + + ### + + def getVolume(self): + "Return mono volume" + return int(sum(self.values) / len(self.values)) + + ### + + def __str__(self): + return "Channels: " + str(self.channels) + \ + ", values: \"" + str(map(lambda x: str(x) + "%", self.values)) + "\"" + +################################################################################ + +class PulseVolumeCtypes(PulseVolume): + def __init__(self, pa_cvolume): + self.channels = pa_cvolume.channels + self.values = map(lambda x: (math.ceil(float(x) * 100 / PA_VOLUME_NORM)), + pa_cvolume.values[0:self.channels]) + return + diff --git a/pulseaudio/PulseVolume.pyc b/pulseaudio/PulseVolume.pyc Binary files differnew file mode 100644 index 0000000..f900dc7 --- /dev/null +++ b/pulseaudio/PulseVolume.pyc diff --git a/pulseaudio/__init__.py b/pulseaudio/__init__.py new file mode 100644 index 0000000..8fa7217 --- /dev/null +++ b/pulseaudio/__init__.py @@ -0,0 +1,29 @@ +#!/usr/bin/python +# vi: et sw=2 +# +# __init__.py +# Copyright (C) 2009 Harry Karvonen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# +# Author: Harry Karvonen <harry.karvonen@gmail.com> +# + + + +from PulseObj import * +from PulseVolume import * +from PulseClient import * +from PulseSink import * diff --git a/pulseaudio/lib_pulseaudio.py b/pulseaudio/lib_pulseaudio.py new file mode 100644 index 0000000..9892374 --- /dev/null +++ b/pulseaudio/lib_pulseaudio.py @@ -0,0 +1,513 @@ +#!/usr/bin/python +# vi: et sw=2 +# +# lib_pulseaudio.py +# Copyright (C) 2009 Harry Karvonen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# +# Provides pulseaudio interface for python +# +# Author: Harry Karvonen <harry.karvonen@gmail.com> +# + +from ctypes import * + +pulse = CDLL("libpulse.so.0"); + +################################################################################ +# +# Constrants +# +################################################################################ + +PA_CHANNELS_MAX = 32 +PA_VOLUME_NORM = 0x10000 + +################################################################################ +# +# Structs +# +################################################################################ + +class PA_IO_EVENT(Structure): + _fields_ = [("_opaque_struct", c_int)] + +class PA_MAINLOOP(Structure): + _fields_ = [("_opaque_struct", c_int)] + +class PA_MAINLOOP_API(Structure): + _fields_ = [("_opaque_struct", c_int)] + +class PA_CONTEXT(Structure): + _fields_ = [("_opaque_struct", c_int)] + +class PA_OPERATION(Structure): + _fields_ = [("_opaque_struct", c_int)] + +class PA_SAMPLE_SPEC(Structure): + _fields_ = [ + ("format", c_int), # FIXME check this + ("rate", c_uint32), + ("channels", c_uint32) + ] + +class PA_CHANNEL_MAP(Structure): + _fields_ = [ + ("channels", c_uint8), + ("map", c_int * PA_CHANNELS_MAX) + ] + +class PA_CVOLUME(Structure): + _fields_ = [ + ("channels", c_uint8), + ("values", c_uint32 * PA_CHANNELS_MAX) + ] + +PA_USEC_T = c_uint64 + +class PA_SINK_INPUT_INFO(Structure): + __slots__ = [ + 'index', + 'name', + 'owner_module', + 'client', + 'sink', + 'sample_spec', + 'channel_map', + 'volume', + 'buffer_usec', + 'sink_usec', + 'resample_method', + 'driver', + 'mute', + ] + _fields_ = [ + ("index", c_uint32), + ("name", c_char_p), + ("owner_module", c_uint32), + ("client", c_uint32), + ("sink", c_uint32), + ("sample_spec", PA_SAMPLE_SPEC), + ("channel_map", PA_CHANNEL_MAP), + ("volume", PA_CVOLUME), + ("buffer_usec", PA_USEC_T), + ("sink_usec", PA_USEC_T), + ("resample_method", c_char_p), + ("driver", c_char_p), + ("mute", c_int) + #("proplist", POINTER(c_int)) + ] + +class PA_SINK_INFO(Structure): + _fields_ = [ + ("name", c_char_p), + ("index", c_uint32), + ("description", c_char_p), + ("sample_spec", PA_SAMPLE_SPEC), + ("channel_map", PA_CHANNEL_MAP), + ("owner_module", c_uint32), + ("volume", PA_CVOLUME), + ("mute", c_int), + ("monitor_source", c_uint32), + ("monitor_source_name", c_char_p), + ("latency", PA_USEC_T), + ("driver", c_char_p), + ("flags", c_int), + ("proplist", POINTER(c_int)), + ("configured_latency", PA_USEC_T) + ] + +class PA_SOURCE_OUTPUT_INFO(Structure): + __slots__ = [ + 'index', + 'name', + 'owner_module', + 'client', + 'source', + 'sample_spec', + 'channel_map', + 'volume', + 'buffer_usec', + 'sink_usec', + 'resample_method', + 'driver', + 'mute', + ] + _fields_ = [ + ("index", c_uint32), + ("name", c_char_p), + ("owner_module", c_uint32), + ("client", c_uint32), + ("source", c_uint32), + ("sample_spec", PA_SAMPLE_SPEC), + ("channel_map", PA_CHANNEL_MAP), + ("volume", PA_CVOLUME), + ("buffer_usec", PA_USEC_T), + ("sink_usec", PA_USEC_T), + ("resample_method", c_char_p), + ("driver", c_char_p), + ("mute", c_int) + #("proplist", POINTER(c_int)) + ] + +class PA_SOURCE_INFO(Structure): + _fields_ = [ + ("name", c_char_p), + ("index", c_uint32), + ("description", c_char_p), + ("sample_spec", PA_SAMPLE_SPEC), + ("channel_map", PA_CHANNEL_MAP), + ("owner_module", c_uint32), + ("volume", PA_CVOLUME), + ("mute", c_int), + ("monitor_of_sink", c_uint32), + ("monitor_of_sink_name", c_char_p), + ("latency", PA_USEC_T), + ("driver", c_char_p), + ("flags", c_int), + ("proplist", POINTER(c_int)), + ("configured_latency", PA_USEC_T) + ] + +class PA_CLIENT_INFO(Structure): + _fields_ = [ + ("index", c_uint32), + ("name", c_char_p), + ("owner_module", c_uint32), + ("driver", c_char_p) + #("proplist", POINTER(c_int)) + ] + +################################################################################ +# +# Callback types +# +################################################################################ + +#PA_IO_EVENT_CB_T = CFUNCTYPE(c_void_p, +# POINTER(PA_MAINLOOP_API), +# POINTER(PA_IO_EVENT), +# c_int, +# c_int, +# c_void_p) + +# SIGNAL +PA_SIGNAL_CB_T = CFUNCTYPE(c_void_p, + POINTER(PA_MAINLOOP_API), + POINTER(c_int), # FIXME wrong type + c_int, + c_void_p) + +# STATE +PA_STATE_CB_T = CFUNCTYPE(c_int, + POINTER(PA_CONTEXT), + c_void_p) + +# CLIENT +PA_CLIENT_INFO_CB_T = CFUNCTYPE(c_void_p, + POINTER(PA_CONTEXT), + POINTER(PA_CLIENT_INFO), + c_int, + c_void_p) +# SINK INPUT +PA_SINK_INPUT_INFO_CB_T = CFUNCTYPE(c_int, #FIXME wrong type + POINTER(PA_CONTEXT), + POINTER(PA_SINK_INPUT_INFO), + c_int, + c_void_p) + +# SINK +PA_SINK_INFO_CB_T = CFUNCTYPE(c_int, #FIXME wrong type + POINTER(PA_CONTEXT), + POINTER(PA_SINK_INFO), + c_int, + c_void_p) +# SOURCE OUTPUT +PA_SOURCE_OUTPUT_INFO_CB_T = CFUNCTYPE(c_int, #FIXME wrong type + POINTER(PA_CONTEXT), + POINTER(PA_SOURCE_OUTPUT_INFO), + c_int, + c_void_p) + +# SOURCE +PA_SOURCE_INFO_CB_T = CFUNCTYPE(c_int, #FIXME wrong type + POINTER(PA_CONTEXT), + POINTER(PA_SOURCE_INFO), + c_int, + c_void_p) +# CONTEXT +PA_CONTEXT_DRAIN_CB_T = CFUNCTYPE(c_void_p, + POINTER(PA_CONTEXT), + c_void_p) + +PA_CONTEXT_SUCCESS_CB_T = CFUNCTYPE(c_void_p, + POINTER(PA_CONTEXT), + c_int, + c_void_p) + +################################################################################ +# +# Functions +# +################################################################################ + +# +# pa_strerror +pa_strerror = pulse.pa_strerror +pa_strerror.restype = c_char_p +pa_strerror.argtypes = [ + c_int +] + +# +# pa_mainloop_* +pa_mainloop_new = pulse.pa_mainloop_new +pa_mainloop_new.restype = POINTER(PA_MAINLOOP) +pa_mainloop_new.argtypes = [ +] + +pa_mainloop_get_api = pulse.pa_mainloop_get_api +pa_mainloop_get_api.restype = POINTER(PA_MAINLOOP_API) +pa_mainloop_get_api.argtypes = [ + POINTER(PA_MAINLOOP) +] + +pa_mainloop_run = pulse.pa_mainloop_run +pa_mainloop_run.restype = c_int +pa_mainloop_run.argtypes = [ + POINTER(PA_MAINLOOP), + POINTER(c_int) +] + +pa_mainloop_iterate = pulse.pa_mainloop_iterate +pa_mainloop_iterate.restype = c_int +pa_mainloop_iterate.argtypes = [ + POINTER(PA_MAINLOOP), + c_int, + POINTER(c_int) +] + +pa_mainloop_quit = pulse.pa_mainloop_quit +pa_mainloop_quit.restype = c_int +pa_mainloop_quit.argtypes = [ + POINTER(PA_MAINLOOP), + c_int +] + +pa_mainloop_dispatch = pulse.pa_mainloop_dispatch +pa_mainloop_dispatch.restype = c_int +pa_mainloop_dispatch.argtypes = [ + POINTER(PA_MAINLOOP) +] + +pa_mainloop_free = pulse.pa_mainloop_run +pa_mainloop_free.restype = c_int +pa_mainloop_free.argtypes = [ + POINTER(PA_MAINLOOP) +] + +# +# pa_signal_* +pa_signal_init = pulse.pa_signal_init +pa_signal_init.restype = c_int +pa_signal_init.argtypes = [ + POINTER(PA_MAINLOOP_API) +] + +pa_signal_new = pulse.pa_signal_new +pa_signal_new.restype = None #POINTER(c_int) #FIXME PA_SIGNAL_EVENT) +pa_signal_new.argtypes = [ + c_int, + PA_SIGNAL_CB_T, + POINTER(c_int) +] + +# +# pa_context_* +pa_context_errno = pulse.pa_context_errno +pa_context_errno.restype = c_int +pa_context_errno.argtypes = [ + POINTER(PA_CONTEXT) +] + +pa_context_new = pulse.pa_context_new +pa_context_new.restype = POINTER(PA_CONTEXT) +pa_context_new.argtypes = [ + POINTER(PA_MAINLOOP_API), + c_char_p +] + + +pa_context_set_state_callback = pulse.pa_context_set_state_callback +pa_context_set_state_callback.restype = None +pa_context_set_state_callback.argtypes = [ + POINTER(PA_CONTEXT), + PA_STATE_CB_T, + c_void_p +] + +pa_context_connect = pulse.pa_context_connect +pa_context_connect.restype = c_int +pa_context_connect.argtypes = [ + POINTER(PA_CONTEXT), + c_char_p, + c_int, #FIXME | isn't correct + POINTER(c_int) +] + +pa_context_get_state = pulse.pa_context_get_state +pa_context_get_state.restype = c_int; +pa_context_get_state.argtypes = [ + POINTER(PA_CONTEXT) +] + +pa_context_drain = pulse.pa_context_drain +pa_context_drain.restype = POINTER(PA_OPERATION) +pa_context_drain.argtypes = [ + POINTER(PA_CONTEXT), + PA_CONTEXT_DRAIN_CB_T, + c_void_p +] + +pa_context_disconnect = pulse.pa_context_disconnect +pa_context_disconnect.restype = c_int; +pa_context_disconnect.argtypes = [ + POINTER(PA_CONTEXT) +] + +# +# pa_context_*_sink_* +pa_context_get_sink_input_info_list = pulse.pa_context_get_sink_input_info_list +pa_context_get_sink_input_info_list.restype = POINTER(c_int) +pa_context_get_sink_input_info_list.argtypes = [ + POINTER(PA_CONTEXT), + PA_SINK_INPUT_INFO_CB_T, + c_void_p +] + +pa_context_get_sink_info_list = pulse.pa_context_get_sink_info_list +pa_context_get_sink_info_list.restype = POINTER(c_int) +pa_context_get_sink_info_list.argtypes = [ + POINTER(PA_CONTEXT), + PA_SINK_INFO_CB_T, + c_void_p +] + +pa_context_set_sink_mute_by_index = pulse.pa_context_set_sink_mute_by_index +pa_context_set_sink_mute_by_index.restype = POINTER(c_int) +pa_context_set_sink_mute_by_index.argtypes = [ + POINTER(PA_CONTEXT), + c_uint32, + c_int, + PA_CONTEXT_SUCCESS_CB_T, + c_void_p +] + +pa_context_set_sink_input_mute = pulse.pa_context_set_sink_input_mute +pa_context_set_sink_input_mute.restype = POINTER(c_int) +pa_context_set_sink_input_mute.argtypes = [ + POINTER(PA_CONTEXT), + c_uint32, + c_int, + PA_CONTEXT_SUCCESS_CB_T, + c_void_p +] + +pa_context_set_sink_volume_by_index = pulse.pa_context_set_sink_volume_by_index +pa_context_set_sink_volume_by_index.restype = POINTER(c_int) +pa_context_set_sink_volume_by_index.argtypes = [ + POINTER(PA_CONTEXT), + c_uint32, + POINTER(PA_CVOLUME), + PA_CONTEXT_SUCCESS_CB_T, + c_void_p +] + +pa_context_set_sink_input_volume = pulse.pa_context_set_sink_input_volume +pa_context_set_sink_input_volume.restype = POINTER(c_int) +pa_context_set_sink_input_volume.argtypes = [ + POINTER(PA_CONTEXT), + c_uint32, + POINTER(PA_CVOLUME), + PA_CONTEXT_SUCCESS_CB_T, + c_void_p +] + +# +# pa_context_*_source_* +pa_context_get_source_output_info_list = pulse.pa_context_get_source_output_info_list +pa_context_get_source_output_info_list.restype = POINTER(c_int) +pa_context_get_source_output_info_list.argtypes = [ + POINTER(PA_CONTEXT), + PA_SOURCE_OUTPUT_INFO_CB_T, + c_void_p +] + +pa_context_get_source_info_list = pulse.pa_context_get_source_info_list +pa_context_get_source_info_list.restype = POINTER(c_int) +pa_context_get_source_info_list.argtypes = [ + POINTER(PA_CONTEXT), + PA_SOURCE_INFO_CB_T, + c_void_p +] + +pa_context_set_source_mute_by_index = pulse.pa_context_set_source_mute_by_index +pa_context_set_source_mute_by_index.restype = POINTER(c_int) +pa_context_set_source_mute_by_index.argtypes = [ + POINTER(PA_CONTEXT), + c_uint32, + c_int, + PA_CONTEXT_SUCCESS_CB_T, + c_void_p +] + +pa_context_set_source_volume_by_index = pulse.pa_context_set_source_volume_by_index +pa_context_set_source_volume_by_index.restype = POINTER(c_int) +pa_context_set_source_volume_by_index.argtypes = [ + POINTER(PA_CONTEXT), + c_uint32, + POINTER(PA_CVOLUME), + PA_CONTEXT_SUCCESS_CB_T, + c_void_p +] + +# +# pa_context_*_client_* + +pa_context_get_client_info_list = pulse.pa_context_get_client_info_list +pa_context_get_client_info_list.restype = POINTER(c_int) +pa_context_get_client_info_list.argtypes = [ + POINTER(PA_CONTEXT), + PA_CLIENT_INFO_CB_T, + c_void_p +] +pa_context_get_client_info = pulse.pa_context_get_client_info +pa_context_get_client_info.restype = POINTER(c_int) +pa_context_get_client_info.argtypes = [ + POINTER(PA_CONTEXT), + c_uint32, + PA_CLIENT_INFO_CB_T, + c_void_p +] + +# +# pa_operation_* +pa_operation_unref = pulse.pa_operation_unref +pa_operation_unref.restype = c_int +pa_operation_unref.argtypes = [ + POINTER(PA_OPERATION) +] + diff --git a/pulseaudio/lib_pulseaudio.pyc b/pulseaudio/lib_pulseaudio.pyc Binary files differnew file mode 100644 index 0000000..88b2c10 --- /dev/null +++ b/pulseaudio/lib_pulseaudio.pyc |