Change Windows 10 Application Audio Mix in Background (preferrably with Python) - python

I'm looking for a way to change the audio mix of different applications in Windows (ie. Firefox audio level vs. Foobar audio level) without taking focus away from the current application. Python is my most familiar language, but I'd use another language if that'd make things easier on me. The code will be used to interface to an external HID device with some volume knobs and buttons on it. Focus needs to stay on the current window because there will be a VR game running overtop of the audio mixer, and I can't refocus on that if the script tabs away.
I've succeeded in frankensteining some older code together that uses the comtypes module, but from there I can only change left/right balance, and not application specific audio levels.
I've tried to cut my way through the relevant windows documentation on MSDN (WASAPI in particular)but it usually ends up sending me down a microsoft rabbit hole and I get in way over my head (I'm still a novice programmer at best).
Am I going about this the wrong way completely?

So the "older code" you provided is still relevant in the API on how the Windows 10 sound API works.
A great example for this type of code is currently in C#, which can be found here:
StackOverflow - c# - Controling Volume Mixer:
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
namespace SetAppVolumne
{
class Program
{
static void Main(string[] args)
{
const string app = "Mozilla Firefox";
foreach (string name in EnumerateApplications())
{
Console.WriteLine("name:" + name);
if (name == app)
{
// display mute state & volume level (% of master)
Console.WriteLine("Mute:" + GetApplicationMute(app));
Console.WriteLine("Volume:" + GetApplicationVolume(app));
// mute the application
SetApplicationMute(app, true);
// set the volume to half of master volume (50%)
SetApplicationVolume(app, 50);
}
}
}
public static float? GetApplicationVolume(string name)
{
ISimpleAudioVolume volume = GetVolumeObject(name);
if (volume == null)
return null;
float level;
volume.GetMasterVolume(out level);
return level * 100;
}
public static bool? GetApplicationMute(string name)
{
ISimpleAudioVolume volume = GetVolumeObject(name);
if (volume == null)
return null;
bool mute;
volume.GetMute(out mute);
return mute;
}
public static void SetApplicationVolume(string name, float level)
{
ISimpleAudioVolume volume = GetVolumeObject(name);
if (volume == null)
return;
Guid guid = Guid.Empty;
volume.SetMasterVolume(level / 100, ref guid);
}
public static void SetApplicationMute(string name, bool mute)
{
ISimpleAudioVolume volume = GetVolumeObject(name);
if (volume == null)
return;
Guid guid = Guid.Empty;
volume.SetMute(mute, ref guid);
}
public static IEnumerable<string> EnumerateApplications()
{
// get the speakers (1st render + multimedia) device
IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
IMMDevice speakers;
deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out speakers);
// activate the session manager. we need the enumerator
Guid IID_IAudioSessionManager2 = typeof(IAudioSessionManager2).GUID;
object o;
speakers.Activate(ref IID_IAudioSessionManager2, 0, IntPtr.Zero, out o);
IAudioSessionManager2 mgr = (IAudioSessionManager2)o;
// enumerate sessions for on this device
IAudioSessionEnumerator sessionEnumerator;
mgr.GetSessionEnumerator(out sessionEnumerator);
int count;
sessionEnumerator.GetCount(out count);
for (int i = 0; i < count; i++)
{
IAudioSessionControl ctl;
sessionEnumerator.GetSession(i, out ctl);
string dn;
ctl.GetDisplayName(out dn);
yield return dn;
Marshal.ReleaseComObject(ctl);
}
Marshal.ReleaseComObject(sessionEnumerator);
Marshal.ReleaseComObject(mgr);
Marshal.ReleaseComObject(speakers);
Marshal.ReleaseComObject(deviceEnumerator);
}
private static ISimpleAudioVolume GetVolumeObject(string name)
{
// get the speakers (1st render + multimedia) device
IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
IMMDevice speakers;
deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out speakers);
// activate the session manager. we need the enumerator
Guid IID_IAudioSessionManager2 = typeof(IAudioSessionManager2).GUID;
object o;
speakers.Activate(ref IID_IAudioSessionManager2, 0, IntPtr.Zero, out o);
IAudioSessionManager2 mgr = (IAudioSessionManager2)o;
// enumerate sessions for on this device
IAudioSessionEnumerator sessionEnumerator;
mgr.GetSessionEnumerator(out sessionEnumerator);
int count;
sessionEnumerator.GetCount(out count);
// search for an audio session with the required name
// NOTE: we could also use the process id instead of the app name (with IAudioSessionControl2)
ISimpleAudioVolume volumeControl = null;
for (int i = 0; i < count; i++)
{
IAudioSessionControl ctl;
sessionEnumerator.GetSession(i, out ctl);
string dn;
ctl.GetDisplayName(out dn);
if (string.Compare(name, dn, StringComparison.OrdinalIgnoreCase) == 0)
{
volumeControl = ctl as ISimpleAudioVolume;
break;
}
Marshal.ReleaseComObject(ctl);
}
Marshal.ReleaseComObject(sessionEnumerator);
Marshal.ReleaseComObject(mgr);
Marshal.ReleaseComObject(speakers);
Marshal.ReleaseComObject(deviceEnumerator);
return volumeControl;
}
}
[ComImport]
[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
internal class MMDeviceEnumerator
{
}
internal enum EDataFlow
{
eRender,
eCapture,
eAll,
EDataFlow_enum_count
}
internal enum ERole
{
eConsole,
eMultimedia,
eCommunications,
ERole_enum_count
}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IMMDeviceEnumerator
{
int NotImpl1();
[PreserveSig]
int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice ppDevice);
// the rest is not implemented
}
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IMMDevice
{
[PreserveSig]
int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
// the rest is not implemented
}
[Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAudioSessionManager2
{
int NotImpl1();
int NotImpl2();
[PreserveSig]
int GetSessionEnumerator(out IAudioSessionEnumerator SessionEnum);
// the rest is not implemented
}
[Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAudioSessionEnumerator
{
[PreserveSig]
int GetCount(out int SessionCount);
[PreserveSig]
int GetSession(int SessionCount, out IAudioSessionControl Session);
}
[Guid("F4B1A599-7266-4319-A8CA-E70ACB11E8CD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAudioSessionControl
{
int NotImpl1();
[PreserveSig]
int GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal);
// the rest is not implemented
}
[Guid("87CE5498-68D6-44E5-9215-6DA47EF883D8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ISimpleAudioVolume
{
[PreserveSig]
int SetMasterVolume(float fLevel, ref Guid EventContext);
[PreserveSig]
int GetMasterVolume(out float pfLevel);
[PreserveSig]
int SetMute(bool bMute, ref Guid EventContext);
[PreserveSig]
int GetMute(out bool pbMute);
}
}
A library that imports all the Windows Core Audio APIs COMTypes into Python that I found would be pycaw and that would be a good start if you want to learn how to port the Windows API into Python.

Related

How to get Device Power State of an Adapter?

I'm looking for a programmatic way of querying an Display Adapter's current power state (d-state) in Windows. It's not a USB device or ACPI device. I couldn't find much on how I could access the current d-state through cmd line or an API. It could also be seen in the device manager adapter's detail info.
Display Adapter's detail tab showing the device power state
logic that i have written :
#include <windows.h>
#include <setupapi.h>
#include <devguid.h>
#pragma comment (lib, "SetupAPI.lib")
int DeviceManager::GetDeviceDriverPowerData() {
int res = 0;
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData = { sizeof(DeviceInfoData) };
// get device class information handle
hDevInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_ADAPTER, 0, 0, DIGCF_PRESENT /*| DIGCF_PROFILE*/);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
res = GetLastError();
return res;
}
// enumerute device information
DWORD required_size = 0;
for (int i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); i++)
{
DWORD DataT;
CM_POWER_DATA cmPowerData = { 0 };
// get device description information
if (!SetupDiGetDeviceRegistryPropertyA(hDevInfo, &DeviceInfoData, SPDRP_DEVICE_POWER_DATA, &DataT, (PBYTE)&cmPowerData, sizeof(cmPowerData), &req_bufsize))
{
res = GetLastError();
continue;
}
printf("PowerState:%d.\n", cmPowerData.PD_MostRecentPowerState);
}
return 0;
}
but it doesn't work .

Fails convertion from std::vector<double> to Python list (boost python)

I want to create custom conversions from std::vector to Python list using boost python. For that I follow the to_python_converter approach. I used a typical converter structure, i.e.
template <class T, bool NoProxy = true>
struct vector_to_list {
static PyObject* convert(const std::vector<T>& vec) {
typedef typename std::vector<T>::const_iterator const_iter;
bp::list* l = new boost::python::list();
for (const_iter it = vec.begin(); it != vec.end(); ++it) {
if (NoProxy) {
l->append(boost::ref(*it));
} else {
l->append(*it);
}
}
return l->ptr();
}
static PyTypeObject const* get_pytype() { return &PyList_Type; }
};
which I can use successfully in plenty of cases but it doesn't work with std::vector<double>. This is the way how I declare this conversion in my boost python module as:
BOOST_PYTHON_MODULE(libmymodule_pywrap) {
.
.
.
bp::to_python_converter<std::vector<double, std::allocator<double> >,
vector_to_list<double, false>, true>(); // this doesn't work
bp::to_python_converter<std::vector<Eigen::VectorXd,
std::allocator<Eigen::VectorXd> >,
vector_to_list<Eigen::VectorXd, false>, true>(); // this works well
}
And I get the following compilation error:
/usr/include/boost/python/object/make_instance.hpp:27:9: error: no matching function for call to ‘assertion_failed(mpl_::failed************ boost::mpl::or_<boost::is_class<double>, boost::is_union<double>, mpl_::bool_<false>, mpl_::bool_<false>, mpl_::bool_<false> >::************)’
BOOST_MPL_ASSERT((mpl::or_<is_class<T>, is_union<T> >));
^
/usr/include/boost/mpl/assert.hpp:83:5: note: candidate: template<bool C> int mpl_::assertion_failed(typename mpl_::assert<C>::type)
int assertion_failed( typename assert<C>::type );
^
/usr/include/boost/mpl/assert.hpp:83:5: note: template argument deduction/substitution failed:
/usr/include/boost/python/object/make_instance.hpp:27:9: note: cannot convert ‘mpl_::assert_arg<boost::mpl::or_<boost::is_class<double>, boost::is_union<double>, mpl_::bool_<false>, mpl_::bool_<false>, mpl_::bool_<false> > >(0u, 1)’ (type ‘mpl_::failed************ boost::mpl::or_<boost::is_class<double>, boost::is_union<double>, mpl_::bool_<false>, mpl_::bool_<false>, mpl_::bool_<false> >::************’) to type ‘mpl_::assert<false>::type {aka mpl_::assert<false>}’
BOOST_MPL_ASSERT((mpl::or_<is_class<T>, is_union<T> >));
Do somebody understand what it's going on?
I am learning Boost::Python as well and unfortunately don't understand how to solve that error, but this example seems to avoid the error message, which you may be able to modify to your own needs.
template<typename T>
struct vector_to_list
{
static PyObject* convert(const std::vector<T>& src)
{
boost::python::list result;
for (int i = 0; i < src.size(); i++)
{
result.append(src[i]);
}
return boost::python::incref(result.ptr());
}
};
...
boost::python::to_python_converter<std::vector<double>, vector_to_list<double> >();
...
However, if this is to provide functionality like, for example:
getData() is declared in C++:
vector<double> getData() { return m_Data; }
where, for example, vector<double> m_Data = {1.0, 2.0, 3.0};
and you wanted in Python:
data = example.getData()
print (data)
[1.0, 2.0, 3.0]
You could implement it by creating a generic container and register each like this (courtesy of this answer):
/// #brief Type that allows for registration of conversions from
/// python iterable types.
struct iterable_converter
{
/// #note Registers converter from a python interable type to the
/// provided type.
template <typename Container>
iterable_converter&
from_python()
{
boost::python::converter::registry::push_back(
&iterable_converter::convertible,
&iterable_converter::construct<Container>,
boost::python::type_id<Container>());
// Support chaining.
return *this;
}
/// #brief Check if PyObject is iterable.
static void* convertible(PyObject* object)
{
return PyObject_GetIter(object) ? object : NULL;
}
/// #brief Convert iterable PyObject to C++ container type.
///
/// Container Concept requirements:
///
/// * Container::value_type is CopyConstructable.
/// * Container can be constructed and populated with two iterators.
/// I.e. Container(begin, end)
template <typename Container>
static void construct(
PyObject* object,
boost::python::converter::rvalue_from_python_stage1_data* data)
{
namespace python = boost::python;
// Object is a borrowed reference, so create a handle indicting it is
// borrowed for proper reference counting.
python::handle<> handle(python::borrowed(object));
// Obtain a handle to the memory block that the converter has allocated
// for the C++ type.
typedef python::converter::rvalue_from_python_storage<Container>
storage_type;
void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes;
typedef python::stl_input_iterator<typename Container::value_type>
iterator;
// Allocate the C++ type into the converter's memory block, and assign
// its handle to the converter's convertible variable. The C++
// container is populated by passing the begin and end iterators of
// the python object to the container's constructor.
new (storage) Container(
iterator(python::object(handle)), // begin
iterator()); // end
data->convertible = storage;
}
};
BOOST_PYTHON_MODULE(example)
{
// Register interable conversions.
iterable_converter()
.from_python<std::vector<double> > ()
.from_python<std::vector<Eigen::VectorXd> >()
;
}
Which allows for chaining, nested vectors and an API that is more Pythonic than with indexed_vector_suite cases like:
data = example.doubleVector()
data[:] = example.getData()
you can simply use:
data = example.getData()

Python properly and effectively obtaining DBUS object path (e.g. /org/bluez/dev_XX_XX_XX_XX_XX_XX/playerY)

I would like to get the device path for bluez-based A2DP bluetooth player that I am creating. I am stuck implementing Play/Pause/Next/Previous commands efficiently, because the dbus availability and player path changes depending on the media player you choose. Furthermore, bluez sometimes decides to send a lot of useless information (for me) such as playlist details that makes the payload bigger for my application to handle. So the goal here is to obtain /org/bluez/dev_XX_XX_XX_XX_XX_XX/playerY when a function is called.
def update_player():
manager = dbus.Interface(self.bus.get_object("org.bluez", "/"), "org.freedesktop.DBus.ObjectManager")
objects = manager.GetManagedObjects()
player_path = getFromDict(objects,[self.devicepath,"org.bluez.MediaControl1", "Player"])
When I try to change the media player, or bluez sends some logs (so 5% of the time in general), dbus org.freedesktop.DBus.ObjectManager receives a lot of information which makes manager.GetManagedObjects() get stuck for 10~20 seconds.
Is there a way of determining bluez object path without having to receive the entire org.freedesktop.DBus.ObjectManager objects; or Is there a way to just limit the amount of message sent by bluez. I really would like to get the object path efficiently. Any help is greately appreciated.
EDIT:
Although I did not test it in the embedded system that had the problem with ObjectManager being populated, thanks to Partiban's great suggestion, I was able to use InterfacesAdded and some regex in order to match the path I needed.
self.bus.add_signal_receiver(self.objectPathHandler,
bus_name="org.bluez",
dbus_interface="org.freedesktop.DBus.ObjectManager",
signal_name="InterfacesAdded",
path_keyword="path")
def objectPathHandler(self, interface, changed, path):
iface = interface[interface.rfind(".") + 1:]
#print("InterfacesAdded: {}; changed: {}; path {}".format(iface, changed, path))
self.playerpath = re.findall('/org/bluez/hci[0-9]/dev_[\dA-F]{2}_[\dA-F]{2}_[\dA-F]{2}_[\dA-F]{2}_[\dA-F]{2}_[\dA-F]{2}/player[0-9]+', iface)[0]
print "Object path:"
print self.playerpath
def update_player(self):
print "Updating player"
if self.devicepath != "None" and self.playerpath != "None":
if self.playerpath:
self.connected = 1
self.getPlayer (self.playerpath)
player_properties = self.player.GetAll(PLAYER_IFACE, dbus_interface="org.freedesktop.DBus.Properties")
You should not be using org.freedesktop.DBus.ObjectManager.GetManagedObjects to get the object path every time. This GetManagedObjects is meant to get existing or previously available interface and it's details when your application starts.
For example, assuming Bluez is started and 1 end device is connected. Later your application starts, during init/start of your application you may need to get all the available/connected devices, so you can use GetManagedObjects to get it.
For the purpose of runtime creation of interfaces, object path you should rely on signals InterfacesAdded and InterfacesRemoved of the objectmanager.
I don't have examples in python, but the below example in C typically does the StartDiscovery and monitor for new devices using signals. So you adapt to similar example in python using signals. The below example is just for clarity purpose (more details on this example is here in Linumiz).
/*
* bluez_adapter_scan.c - Scan for bluetooth devices
* - This example scans for new devices after powering the adapter, if any devices
* appeared in /org/hciX/dev_XX_YY_ZZ_AA_BB_CC, it is monitered using "InterfaceAdded"
* signal and all the properties of the device is printed
* - Scanning continues to run until any device is disappered, this happens after 180 seconds
* automatically if the device is not used.
* gcc `pkg-config --cflags glib-2.0 gio-2.0` -Wall -Wextra -o ./bin/bluez_adapter_scan ./bluez_adapter_scan.c `pkg-config --libs glib-2.0 gio-2.0`
*/
#include <glib.h>
#include <gio/gio.h>
GDBusConnection *con;
static void bluez_property_value(const gchar *key, GVariant *value)
{
const gchar *type = g_variant_get_type_string(value);
g_print("\t%s : ", key);
switch(*type) {
case 'o':
case 's':
g_print("%s\n", g_variant_get_string(value, NULL));
break;
case 'b':
g_print("%d\n", g_variant_get_boolean(value));
break;
case 'u':
g_print("%d\n", g_variant_get_uint32(value));
break;
case 'a':
/* TODO Handling only 'as', but not array of dicts */
if(g_strcmp0(type, "as"))
break;
g_print("\n");
const gchar *uuid;
GVariantIter i;
g_variant_iter_init(&i, value);
while(g_variant_iter_next(&i, "s", &uuid))
g_print("\t\t%s\n", uuid);
break;
default:
g_print("Other\n");
break;
}
}
static void bluez_device_appeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
(void)user_data;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
GVariant *properties;
g_variant_get(parameters, "(&oa{sa{sv}})", &object, &interfaces);
while(g_variant_iter_next(interfaces, "{&s#a{sv}}", &interface_name, &properties)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
g_print("[ %s ]\n", object);
const gchar *property_name;
GVariantIter i;
GVariant *prop_val;
g_variant_iter_init(&i, properties);
while(g_variant_iter_next(&i, "{&sv}", &property_name, &prop_val))
bluez_property_value(property_name, prop_val);
g_variant_unref(prop_val);
}
g_variant_unref(properties);
}
return;
}
#define BT_ADDRESS_STRING_SIZE 18
static void bluez_device_disappeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
char address[BT_ADDRESS_STRING_SIZE] = {'\0'};
g_variant_get(parameters, "(&oas)", &object, &interfaces);
while(g_variant_iter_next(interfaces, "s", &interface_name)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
int i;
char *tmp = g_strstr_len(object, -1, "dev_") + 4;
for(i = 0; *tmp != '\0'; i++, tmp++) {
if(*tmp == '_') {
address[i] = ':';
continue;
}
address[i] = *tmp;
}
g_print("\nDevice %s removed\n", address);
g_main_loop_quit((GMainLoop *)user_data);
}
}
return;
}
static void bluez_signal_adapter_changed(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
const gchar *interface,
const gchar *signal,
GVariant *params,
void *userdata)
{
(void)conn;
(void)sender;
(void)path;
(void)interface;
(void)userdata;
GVariantIter *properties = NULL;
GVariantIter *unknown = NULL;
const char *iface;
const char *key;
GVariant *value = NULL;
const gchar *signature = g_variant_get_type_string(params);
if(g_strcmp0(signature, "(sa{sv}as)") != 0) {
g_print("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)");
goto done;
}
g_variant_get(params, "(&sa{sv}as)", &iface, &properties, &unknown);
while(g_variant_iter_next(properties, "{&sv}", &key, &value)) {
if(!g_strcmp0(key, "Powered")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter is Powered \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
if(!g_strcmp0(key, "Discovering")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter scan \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
}
done:
if(properties != NULL)
g_variant_iter_free(properties);
if(value != NULL)
g_variant_unref(value);
}
static int bluez_adapter_call_method(const char *method)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
/* TODO Find the adapter path runtime */
"/org/bluez/hci0",
"org.bluez.Adapter1",
method,
NULL,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL)
return 1;
g_variant_unref(result);
return 0;
}
static int bluez_adapter_set_property(const char *prop, GVariant *value)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
"/org/bluez/hci0",
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new("(ssv)", "org.bluez.Adapter1", prop, value),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL)
return 1;
g_variant_unref(result);
return 0;
}
int main(void)
{
GMainLoop *loop;
int rc;
guint prop_changed;
guint iface_added;
guint iface_removed;
con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
if(con == NULL) {
g_print("Not able to get connection to system bus\n");
return 1;
}
loop = g_main_loop_new(NULL, FALSE);
prop_changed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
NULL,
"org.bluez.Adapter1",
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_signal_adapter_changed,
NULL,
NULL);
iface_added = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesAdded",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_appeared,
loop,
NULL);
iface_removed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesRemoved",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_disappeared,
loop,
NULL);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", TRUE));
if(rc) {
g_print("Not able to enable the adapter\n");
goto fail;
}
rc = bluez_adapter_call_method("StartDiscovery");
if(rc) {
g_print("Not able to scan for new devices\n");
goto fail;
}
g_main_loop_run(loop);
rc = bluez_adapter_call_method("StopDiscovery");
if(rc)
g_print("Not able to stop scanning\n");
g_usleep(100);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", FALSE));
if(rc)
g_print("Not able to disable the adapter\n");
fail:
g_dbus_connection_signal_unsubscribe(con, prop_changed);
g_dbus_connection_signal_unsubscribe(con, iface_added);
g_dbus_connection_signal_unsubscribe(con, iface_removed);
g_object_unref(con);
return 0;
}
In this example of scanning of devices using StartDiscovery I have used both the signals InterfaceRemoved and InterfaceAdded to demonstrate. So when new devices appeared on /org/hciX/, bluez_device_appeared is called and removal happens in the same way.
If you have more then one bluetooth adapter connected, you can filter them under g_dbus_connection_signal_subscribe by specifying the adapters path e.g as /org/bluez/hciX.
All the DBUS based daemons use signals to notify the clients on the bus, so we see lots messaged on the bus. So we need to subscribe based on the exact need. This filter is applied at dbus daemon level and messages are forwarded.
To add MediaControl1 interface of bluez is outdated and deprecated. All new applications should use MediaPlayer as defined here.

Avoid garbage collection of C written Python library

I'm struggling with my C written Python library. The code is meant to write a register in a cp210x in order to control a Relay card. The code works, however Python clears the object somehow.
With other words, the C variable ttyPort is cleared after ending a function.
Here's the C code:
#include <Python.h>
#include <fcntl.h>
#include <stropts.h>
// C variable that holds the tty address (e.g. /dev/ttyUSB0)
const char* ttyPort;
int setRelay(int action, int relayNumber)
{
/* more magic over here */
printf("Port :: %s\n", ttyPort);
}
// All python wrappers below
static PyObject*setPort(PyObject* self, PyObject* args)
{
Py_INCREF(self)
// Copy python argument to ttyPort
if (!PyArg_ParseTuple(args, "s", &ttyPort))
return NULL;
printf("RelayModule :: Port set (%s)\n", ttyPort);
Py_RETURN_NONE;
}
static PyObject*fanOff(PyObject* self, PyObject* args)
{
//fanMode = 0;
printf("RelayModule :: Fan off %s\n",ttyPort);
if (setRelay(RELAY_OFF, 0) == 1){
// more magic
}
Py_RETURN_NONE;
}
/* more functions over here */
static PyMethodDef RelayMethods[] =
{
{"setPort", setPort, METH_VARARGS, "Set tty port."},
{"fanOff", fanOff, METH_NOARGS, "Fan off."},
{"fanHalf", fanHalf, METH_NOARGS, "Fan half speed."},
{"fanFull", fanFull, METH_NOARGS, "Fan full on."},
{"pumpOn", pumpOn, METH_NOARGS, "Pump on."},
{"pumpOff", pumpOff, METH_NOARGS, "Pump off."},
{"isPumpOn", isPumpOn, METH_NOARGS, "Check if pump is on."},
{"getFanMode", getFanMode, METH_NOARGS, "Get fan mode."},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef RelayDefs = {
PyModuleDef_HEAD_INIT,"RelayModule","A Python module that controls the Conrad 4ch relay card.", -1, RelayMethods
};
PyMODINIT_FUNC PyInit_Relay(void)
{
Py_Initialize();
return PyModule_Create(&RelayDefs);
}
The python code for example:
import Relay
def settty():
Relay.setPort("/dev/ttyUSB0")
def gettty():
Relay.fanOff()
def doAll():
Relay.setPort("/dev/ttyUSB0")
Relay.fanOff()
settty()
gettty() #Error, prints 'Port :: [garbage]'
doAll() #works!
How can I somehow declare an object? In other languages I'd do:
RelayObj = new Relay()
Or how can I store a variable correctly?
One possible fix would be to use 'es' format specifier in PyArg_ParseTuple(args, "s", &ttyPort) instead of 's'. From documentation:
In general, when a format sets a pointer to a buffer, the buffer is managed by the corresponding Python object, and the buffer shares the lifetime of this object
unless you use 'es'. You will have to free memory manually, however.

OpenCV: memory leak with Python interface but not in the C version

I am asking here because I haven't gotten any help from the OpenCV developers so far. I reduced the problem to a very simple test case so probably anyone with some background with CPython could help here.
This C code does not leak:
int main() {
while(true) {
int hist_size[] = {40};
float range[] = {0.0f,255.0f};
float* ranges[] = {range};
CvHistogram* hist = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
cvReleaseHist(&hist);
}
}
This Python code does leak:
while True: cv.CreateHist([40], cv.CV_HIST_ARRAY, [[0,255]], 1)
I searched through the CPython code (of OpenCVs current SVN trunk code) and found this:
struct cvhistogram_t {
PyObject_HEAD
CvHistogram h;
PyObject *bins;
};
...
/* cvhistogram */
static void cvhistogram_dealloc(PyObject *self)
{
cvhistogram_t *cvh = (cvhistogram_t*)self;
Py_DECREF(cvh->bins);
PyObject_Del(self);
}
static PyTypeObject cvhistogram_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0, /*size*/
MODULESTR".cvhistogram", /*name*/
sizeof(cvhistogram_t), /*basicsize*/
};
static PyObject *cvhistogram_getbins(cvhistogram_t *cvh)
{
Py_INCREF(cvh->bins);
return cvh->bins;
}
static PyGetSetDef cvhistogram_getseters[] = {
{(char*)"bins", (getter)cvhistogram_getbins, (setter)NULL, (char*)"bins", NULL},
{NULL} /* Sentinel */
};
static void cvhistogram_specials(void)
{
cvhistogram_Type.tp_dealloc = cvhistogram_dealloc;
cvhistogram_Type.tp_getset = cvhistogram_getseters;
}
...
static PyObject *pycvCreateHist(PyObject *self, PyObject *args, PyObject *kw)
{
const char *keywords[] = { "dims", "type", "ranges", "uniform", NULL };
PyObject *dims;
int type;
float **ranges = NULL;
int uniform = 1;
if (!PyArg_ParseTupleAndKeywords(args, kw, "Oi|O&i", (char**)keywords, &dims, &type, convert_to_floatPTRPTR, (void*)&ranges, &uniform)) {
return NULL;
}
cvhistogram_t *h = PyObject_NEW(cvhistogram_t, &cvhistogram_Type);
args = Py_BuildValue("Oi", dims, CV_32FC1);
h->bins = pycvCreateMatND(self, args);
Py_DECREF(args);
if (h->bins == NULL) {
return NULL;
}
h->h.type = CV_HIST_MAGIC_VAL;
if (!convert_to_CvArr(h->bins, &(h->h.bins), "bins"))
return NULL;
ERRWRAP(cvSetHistBinRanges(&(h->h), ranges, uniform));
return (PyObject*)h;
}
And from the OpenCV C headers:
typedef struct CvHistogram
{
int type;
CvArr* bins;
float thresh[CV_MAX_DIM][2]; /* For uniform histograms. */
float** thresh2; /* For non-uniform histograms. */
CvMatND mat; /* Embedded matrix header for array histograms. */
}
CvHistogram;
I don't exactly understand everything because I never worked with the C-interface to Python before. But probably the bug I am searching for is somewhere in this code.
Am I right? Or where should I search for the bug? How would I fix it?
(Note for people who have seen an earlier version of this question: I looked at the wrong code. Their SWIG interface was deprecated and not used anymore (but the code was still there in SVN, this is why I confused it. So don't look into interfaces/swig, this code is old and not used. The current code lives in modules/python.)
Upstream bug report: memleak in OpenCV Python CreateHist
It has been fixed.
Changed 3 weeks ago by jamesb
status changed from accepted to closed
resolution set to fixed
Fixed in r4526
The ranges parameters were not being freed, and the iterator over ranges was not being DECREF'ed. Regressions now pass, and original loop does not leak.
I think you have garbage collection issue, in that you never leave the loop.
Does this work more as expected?
while True:
cv.CreateHist([40], cv.CV_HIST_ARRAY, [[0,255]], 1)
cv = None

Categories