opennx/PulseAudio.cpp
2025-08-08 20:34:09 +02:00

835 lines
28 KiB
C++

// $Id: PulseAudio.cpp 688 2012-02-18 02:36:07Z felfert $
//
// Copyright (C) 2006 The OpenNX Team
// Author: Fritz Elfert
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as
// published by the Free Software Foundation; either version 2 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 Library General Public
// License along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "PulseAudio.h"
#endif
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "PulseAudio.h"
#ifndef PA_NOLIB
#include "MyDynlib.h"
#endif
#include <wx/log.h>
#include <wx/utils.h>
#include <wx/regex.h>
#include <wx/config.h>
#include <wx/wfstream.h>
#include <wx/txtstrm.h>
#include <wx/process.h>
#include <wx/tokenzr.h>
#ifdef APP_OPENNX
# include "opennxApp.h"
#endif
#ifdef APP_PULSETEST
# include "pulseTest.h"
#endif
#include "osdep.h"
#include "trace.h"
ENABLE_TRACE;
#if defined(HAVE_PULSE_PULSEAUDIO_H) || defined(__WXMSW__) || defined(__WXMAC__)
# define WITH_PULSEAUDIO
#endif
#ifdef WITH_PULSEAUDIO
#include <pulse/pulseaudio.h>
#undef PA_ADEBUG
#ifndef PA_NOLIB
typedef pa_threaded_mainloop* (*Tpa_threaded_mainloop_new)(void);
typedef pa_mainloop_api* (*Tpa_threaded_mainloop_get_api)(pa_threaded_mainloop*);
typedef int (*Tpa_threaded_mainloop_start)(pa_threaded_mainloop *);
typedef void (*Tpa_threaded_mainloop_stop)(pa_threaded_mainloop *);
typedef void (*Tpa_threaded_mainloop_free)(pa_threaded_mainloop *);
typedef pa_context* (*Tpa_context_new)(pa_mainloop_api *, const char *);
typedef void (*Tpa_context_set_state_callback)(pa_context *, pa_context_notify_cb_t, void *);
typedef int (*Tpa_context_connect)(pa_context *, const char *, pa_context_flags_t, const pa_spawn_api *);
typedef void (*Tpa_context_disconnect)(pa_context *);
typedef pa_operation* (*Tpa_context_drain)(pa_context *, pa_context_notify_cb_t, void *);
typedef pa_context_state_t (*Tpa_context_get_state)(pa_context *);
typedef pa_operation* (*Tpa_context_get_server_info)(pa_context *, pa_server_info_cb_t, void *);
typedef pa_operation* (*Tpa_context_get_module_info_list)(pa_context *, pa_module_info_cb_t, void *);
typedef pa_operation* (*Tpa_context_load_module)(pa_context *, const char*, const char *, pa_context_index_cb_t, void *);
typedef pa_operation* (*Tpa_context_unload_module)(pa_context *, uint32_t, pa_context_success_cb_t, void *);
typedef int (*Tpa_context_errno)(pa_context *);
typedef void (*Tpa_context_unref)(pa_context *);
typedef void (*Tpa_operation_unref)(pa_operation *);
typedef const char* (*Tpa_strerror)(int);
typedef void (*Tpa_xfree)(void *p);
#define LOADPTR(name) p = dll->GetSymbol(wxT(#name)); if (NULL == p) return 0; P##name = (T##name)p
#define FPTR(name) static T##name P##name = NULL
FPTR(pa_threaded_mainloop_new);
FPTR(pa_threaded_mainloop_get_api);
FPTR(pa_threaded_mainloop_start);
FPTR(pa_threaded_mainloop_stop);
FPTR(pa_threaded_mainloop_free);
FPTR(pa_context_new);
FPTR(pa_context_set_state_callback);
FPTR(pa_context_connect);
FPTR(pa_context_disconnect);
FPTR(pa_context_drain);
FPTR(pa_context_get_state);
FPTR(pa_context_unref);
FPTR(pa_operation_unref);
FPTR(pa_context_get_server_info);
FPTR(pa_context_get_module_info_list);
FPTR(pa_context_load_module);
FPTR(pa_context_unload_module);
FPTR(pa_context_errno);
FPTR(pa_strerror);
FPTR(pa_xfree);
static int _set_pasyms(MyDynamicLibrary *dll) {
void *p;
LOADPTR(pa_threaded_mainloop_new);
LOADPTR(pa_threaded_mainloop_get_api);
LOADPTR(pa_threaded_mainloop_start);
LOADPTR(pa_threaded_mainloop_stop);
LOADPTR(pa_threaded_mainloop_free);
LOADPTR(pa_context_new);
LOADPTR(pa_context_set_state_callback);
LOADPTR(pa_context_connect);
LOADPTR(pa_context_disconnect);
LOADPTR(pa_context_drain);
LOADPTR(pa_context_get_state);
LOADPTR(pa_context_unref);
LOADPTR(pa_operation_unref);
LOADPTR(pa_context_get_server_info);
LOADPTR(pa_context_get_module_info_list);
LOADPTR(pa_context_load_module);
LOADPTR(pa_context_unload_module);
LOADPTR(pa_context_errno);
LOADPTR(pa_strerror);
LOADPTR(pa_xfree);
return 1;
}
#endif
// On windows, calling myLogTrace() from within libpulse's
// connection loop apparently crashes libpulse for some
// unknown reason. Therefore, we use native OutputDebugStringA
// in this case.
#ifdef __WXMSW__
# define STATE_TRACE(msg) OutputDebugStringA(msg)
#else
# define STATE_TRACE(msg) myLogTrace(MYTRACETAG, wxT(msg))
#endif
#ifdef PA_NOLIB
# include "PAWrapperSimple.cpp"
#else
class pawrapper {
private:
typedef enum {
NONE,
LIST,
LOAD_MODULE,
UNLOAD_MODULE,
SET_SINK_VOLUME,
SET_SOURCE_VOLUME,
SET_SINK_INPUT_VOLUME,
} eAction;
public:
pawrapper()
: m_bConnected(false), m_pLoop(NULL), m_pApi(NULL), m_pContext(NULL)
{
m_bError = false;
m_bConnected = false;
#ifdef __WXMSW__
const char * server = "127.0.0.1";
#else
const char * server = NULL;
#endif
m_pLoop = Ppa_threaded_mainloop_new();
m_pApi = Ppa_threaded_mainloop_get_api(m_pLoop);
m_pContext = Ppa_context_new(m_pApi, "OpenNX");
Ppa_context_set_state_callback(m_pContext, context_state_callback_if, this);
int retry = 3;
do {
m_bError = false;
myLogTrace(MYTRACETAG, wxT("pa_context_connect try %d"), 4 - retry);
if (0 <= Ppa_context_connect(m_pContext, server, PA_CONTEXT_NOAUTOSPAWN, NULL)) {
Ppa_threaded_mainloop_start(m_pLoop);
while (!(m_bConnected || m_bError))
::wxGetApp().Yield(true);
}
if (m_bConnected)
break;
} while (retry-- > 0);
}
~pawrapper()
{
#ifndef __WXMSW__
if (m_bConnected)
Ppa_context_disconnect(m_pContext);
#endif
if (m_pLoop) {
Ppa_threaded_mainloop_stop(m_pLoop);
Ppa_threaded_mainloop_free(m_pLoop);
}
if (NULL != m_pContext)
Ppa_context_unref(m_pContext);
}
bool getdefaults(wxString &Sink, wxString &Source)
{
m_bComplete = false;
Ppa_operation_unref(Ppa_context_get_server_info(m_pContext, get_server_info_callback_if, this));
bool ret = waitcmd();
if (ret && m_bComplete) {
Sink = m_DefSink;
Source = m_DefSource;
}
return ret && m_bComplete;
}
bool findmodules(const wxChar *name, wxArrayString &indexes, wxArrayString &args)
{
m_sStr = name;
m_bSearch = true;
m_bFound = false;
m_bComplete = false;
m_bError = false;
m_asIndexes.Empty(); m_asArgs.Empty();
Ppa_operation_unref(Ppa_context_get_module_info_list(m_pContext, get_module_info_callback_if, this));
bool ret = waitcmd();
if (ret && m_bFound) {
args = m_asArgs;
indexes = m_asIndexes;
}
return ret && m_bFound;
}
bool loadmodule(const wxString name, const wxString args)
{
m_bComplete = false;
m_bError = false;
Ppa_operation_unref(Ppa_context_load_module(m_pContext, name.mb_str(), args.mb_str(), index_callback_if, this));
return waitcmd();
}
bool unloadmodule(int index)
{
m_bComplete = false;
m_bError = false;
Ppa_operation_unref(Ppa_context_unload_module(m_pContext, index, simple_callback_if, this));
return waitcmd();
}
bool isConnected()
{
return m_bConnected;
}
private:
bool waitcmd() {
while (!(m_bError || m_bComplete)) {
::wxGetApp().Yield(true);
}
return !m_bError;
}
#if 0
void runcmd(eAction a)
{
m_bComplete = false;
m_bError = false;
switch (a) {
case SET_SINK_VOLUME:
{
pa_cvolume v;
pa_cvolume_set(&v, 1, volume);
Ppa_operation_unref(pa_context_set_sink_volume_by_name(m_pContext, sink_name, &v, simple_callback_if, NULL));
break;
}
case SET_SOURCE_VOLUME:
{
pa_cvolume v;
pa_cvolume_set(&v, 1, volume);
Ppa_operation_unref(pa_context_set_source_volume_by_name(m_pContext, source_name, &v, simple_callback_if, NULL));
break;
}
case SET_SINK_INPUT_VOLUME:
{
pa_cvolume v;
pa_cvolume_set(&v, 1, volume);
Ppa_operation_unref(pa_context_set_sink_input_volume(m_pContext, sink_input_idx, &v, simple_callback_if, NULL));
break;
}
}
}
#endif
void drain(void) {
pa_operation *o = Ppa_context_drain(m_pContext, context_drain_complete_if, this);
if (NULL == o) {
myLogTrace(MYTRACETAG, wxT("drain_complete"));
m_bComplete = true;
} else
Ppa_operation_unref(o);
}
void context_state_callback(pa_context *c)
{
if (NULL == c)
return;
switch (Ppa_context_get_state(c)) {
case PA_CONTEXT_UNCONNECTED:
STATE_TRACE("PA_CONTEXT_UNCONNECTED");
break;
case PA_CONTEXT_CONNECTING:
STATE_TRACE("PA_CONTEXT_CONNECTING");
break;
case PA_CONTEXT_AUTHORIZING:
STATE_TRACE("PA_CONTEXT_AUTHORIZING");
break;
case PA_CONTEXT_SETTING_NAME:
STATE_TRACE("PA_CONTEXT_SETTING_NAME");
break;
case PA_CONTEXT_READY:
STATE_TRACE("PA_CONTEXT_READY");
m_bConnected = true;
break;
case PA_CONTEXT_TERMINATED:
STATE_TRACE("PA_CONTEXT_TERMINATED");
break;
case PA_CONTEXT_FAILED:
STATE_TRACE("PA_CONTEXT_FAILED");
m_bError = true;
break;
default:
STATE_TRACE("PA_CONTEXT_default");
m_bError = true;
break;
}
}
void get_server_info_callback(pa_context *c, const pa_server_info *i)
{
if (!i) {
myLogTrace(MYTRACETAG, wxT("Failed to get server information: %s"), Ppa_strerror(Ppa_context_errno(c)));
m_bError = true;
m_pApi->quit(m_pApi, 0);
return;
}
wxString dsink(i->default_sink_name ? i->default_sink_name : "", wxConvUTF8);
wxString dsrc(i->default_source_name ? i->default_source_name : "", wxConvUTF8);
m_DefSink = dsink; m_DefSource = dsrc; m_bComplete = true;
}
void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last)
{
if (is_last < 0) {
myLogTrace(MYTRACETAG, wxT("Failed to get module information: %s"), Ppa_strerror(Ppa_context_errno(c)));
m_bError = true;
m_pApi->quit(m_pApi, 0);
return;
}
if (is_last) {
drain();
return;
}
if (NULL != i) {
wxString name(i->name, wxConvUTF8);
wxString args(i->argument ? i->argument : "", wxConvUTF8);
if (name.IsSameAs(m_sStr)) {
m_bFound = true;
myLogTrace(MYTRACETAG, wxT("Found module[%u] %s %s"),
i->index, VMB(name), VMB(args));
m_asIndexes.Add(wxString::Format(wxT("%d"),i->index));
m_asArgs.Add(args);
}
}
}
void index_callback(pa_context *c, uint32_t idx)
{
if (idx == PA_INVALID_INDEX) {
myLogTrace(MYTRACETAG, wxT("Index failure: %s"), Ppa_strerror(Ppa_context_errno(c)));
m_bError = true;
m_pApi->quit(m_pApi, 0);
return;
}
m_iIndex = idx;
drain();
}
void simple_callback(pa_context *c, int success)
{
if (!success) {
m_bError = true;
myLogTrace(MYTRACETAG, wxT("Simple failure: %s"), Ppa_strerror(Ppa_context_errno(c)));
m_pApi->quit(m_pApi, 0);
return;
}
drain();
}
void context_drain_complete(pa_context *)
{
myLogTrace(MYTRACETAG, wxT("context_drain_complete"));
m_bComplete = true;
}
static void simple_callback_if(pa_context *c, int success, void *udata) {
if (NULL == udata)
return;
static_cast<pawrapper *>(udata)->simple_callback(c, success);
}
static void index_callback_if(pa_context *c, uint32_t idx, void *udata) {
if (NULL == udata)
return;
static_cast<pawrapper *>(udata)->index_callback(c, idx);
}
static void context_drain_complete_if(pa_context *c, void *udata) {
if (NULL == udata)
return;
static_cast<pawrapper *>(udata)->context_drain_complete(c);
}
static void get_module_info_callback_if(pa_context *c, const pa_module_info *i, int last, void *udata) {
if (NULL == udata)
return;
static_cast<pawrapper *>(udata)->get_module_info_callback(c, i, last);
}
static void get_server_info_callback_if(pa_context *c, const pa_server_info *i, void *udata) {
if (NULL == udata)
return;
static_cast<pawrapper *>(udata)->get_server_info_callback(c, i);
}
static void context_state_callback_if(pa_context *c, void *udata) {
if (NULL == udata)
return;
static_cast<pawrapper *>(udata)->context_state_callback(c);
}
volatile bool m_bConnected;
volatile bool m_bComplete;
volatile bool m_bError;
volatile bool m_bSearch;
volatile bool m_bFound;
unsigned int m_iIndex;
pa_threaded_mainloop *m_pLoop;
pa_mainloop_api *m_pApi;
pa_context *m_pContext;
wxString m_sStr;
wxString m_DefSink;
wxString m_DefSource;
wxArrayString m_asIndexes;
wxArrayString m_asArgs;
};
#endif
# if defined(__WXMSW__) || defined(__WXMAC__)
# ifdef __WXMAC__
extern "C" {
extern const char *getMacMachineID();
};
# endif
static wxString MachineID() {
# ifdef __WXMSW__
return ::wxGetHostName().Lower();
# else
return wxString(getMacMachineID(), wxConvUTF8);
# endif
}
# endif // defined(__WXMSW__) || defined(__WXMAC__)
#endif // WITH_PULSEAUDIO
bool PulseAudio::AutoSpawn()
{
#ifdef WITH_PULSEAUDIO
# if defined(__WXMSW__) || defined(__WXMAC__)
int papid;
int retry = 3;
// On windows and mac, we do our own autospawn
wxString piddir = ::wxGetHomeDir() + wxFileName::GetPathSeparator()
+ wxT(".config") + wxFileName::GetPathSeparator()
+ wxT("pulse") + wxFileName::GetPathSeparator()
+ MachineID() + wxT("-runtime");
wxString pidfile = piddir + wxFileName::GetPathSeparator() + wxT("pid");
wxString pacmd;
wxConfigBase::Get()->Read(wxT("Config/SystemNxDir"), &pacmd);
pacmd << wxFileName::GetPathSeparator() << wxT("bin")
<< wxFileName::GetPathSeparator() << wxT("pulseaudio");
# ifdef __WXMSW__
pacmd << wxT(".exe --exit-idle-time=-1 --log-target=file:\"")
<< ::wxGetHomeDir() << wxFileName::GetPathSeparator()
<< wxT(".config") << wxFileName::GetPathSeparator()
<< wxT("pulse") << wxFileName::GetPathSeparator()
<< wxT("pa.log\"");
# endif
do {
# ifdef __WXMSW__
myLogTrace(MYTRACETAG, wxT("PulseAudio::AutoSpawn: checking pulseaudio process"));
papid = getpidof("pulseaudio.exe");
if (papid != 0) {
myLogTrace(MYTRACETAG, wxT("PulseAudio::AutoSpawn: process %d is running"), papid);
return true;
}
# else
myLogTrace(MYTRACETAG, wxT("PulseAudio::AutoSpawn: checking '%s'"), VMB(pidfile));
wxFileInputStream sPid(pidfile);
if (sPid.IsOk()) {
myLogTrace(MYTRACETAG, wxT("PulseAudio::AutoSpawn: PID file exists"));
wxTextInputStream tis(sPid);
tis >> papid;
myLogTrace(MYTRACETAG, wxT("PulseAudio::AutoSpawn: PID=%d"), papid);
if ((papid != 0) && ::wxProcess::Exists(papid)) {
myLogTrace(MYTRACETAG, wxT("PulseAudio::AutoSpawn: process %d is running"), papid);
return true;
}
}
# endif
myLogTrace(MYTRACETAG, wxT("PulseAudio::AutoSpawn: trying to start '%s'"), VMB(pacmd));
# ifdef __WXMSW__
wxProcess *nxpa = wxProcess::Open(pacmd,
wxEXEC_ASYNC|wxEXEC_MAKE_GROUP_LEADER);
wxThread::Sleep(100);
if (nxpa) {
nxpa->CloseOutput();
nxpa->Detach();
}
# else
::wxExecute(pacmd, wxEXEC_ASYNC|wxEXEC_MAKE_GROUP_LEADER);
# endif
wxThread::Sleep(1000);
} while (retry-- > 0);
myLogTrace(MYTRACETAG, wxT("PulseAudio::AutoSpawn: spawn failed"));
return false;
# else
myLogTrace(MYTRACETAG, wxT("Not spawning pulseaudio on this platform"));
return true;
# endif // defined(__WXMSW__) || defined(__WXMAC__)
#else
return true;
#endif // WITH_PULSEAUDIO
}
#ifndef PA_NOLIB
PulseAudio::PulseAudio()
: pa(NULL), dll(NULL), m_bPulseAvailable(false)
{
m_iPortEsound = 0; m_iPortNative = 0;
#ifdef WITH_PULSEAUDIO
wxLogNull ignoreErrors;
if (AutoSpawn()) {
dll = new MyDynamicLibrary();
# ifdef __WXMSW__
wxString pdll = wxT("libpulse-0");
# else
wxString pdll = wxT("libpulse");
# endif
if (dll->Load(pdll)) {
myLogTrace(MYTRACETAG, wxT("libpulse loaded"));
if (0 != _set_pasyms(dll)) {
myLogTrace(MYTRACETAG, wxT("libpulse functions loaded"));
pa = new pawrapper();
if (pa->isConnected()) {
m_bPulseAvailable = true;
myLogTrace(MYTRACETAG, wxT("connected to pulseaudio daemon"));
}
}
}
}
#else
myLogTrace(MYTRACETAG, wxT("No pulseaudio support"));
#endif
}
#else
PulseAudio::PulseAudio()
: pa(NULL), m_bPulseAvailable(false)
{
m_iPortEsound = 0; m_iPortNative = 0;
# ifdef WITH_PULSEAUDIO
if (AutoSpawn()) {
pa = new pawrapper();
if (pa->isConnected()) {
m_bPulseAvailable = true;
myLogTrace(MYTRACETAG, wxT("connected to pulseaudio daemon"));
}
}
# else
myLogTrace(MYTRACETAG, wxT("No pulseaudio support"));
# endif
}
#endif
PulseAudio::~PulseAudio()
{
#ifdef WITH_PULSEAUDIO
delete pa;
# ifndef PA_NOLIB
delete dll;
# endif
#endif
}
bool PulseAudio::IsAvailable()
{
myLogTrace(MYTRACETAG, wxT("IsAvailable:%s"),
# ifdef __WXMSW__
m_bPulseAvailable ? wxT("true") : wxT("false"));
# else
m_bPulseAvailable ? "true" : "false");
# endif
return m_bPulseAvailable;
}
int PulseAudio::FoundModuleIDs(wxString modname, wxString s_argstpl,
wxArrayString &a_indexes, wxArrayString &a_args,
bool HardAccordance = false)
{
#ifdef WITH_PULSEAUDIO
wxRegEx re;
wxArrayString indexes, args_args; indexes.Empty(); args_args.Empty();
if (!pa->findmodules(modname, indexes, args_args))
return 0;
wxArrayString argstpl = ::wxStringTokenize(s_argstpl);
myLogTrace(MYTRACETAG, wxT("Found modules named %s (argstpl=%d): idx/args_args count = %d/%d"),
VMB(modname),(int)argstpl.GetCount(),(int)indexes.GetCount(),
(int)args_args.GetCount());
a_indexes.Empty(); a_args.Empty(); int c = 0;
for (int i = 0; i < indexes.GetCount(); i++)
{
if (argstpl.IsEmpty() && !HardAccordance) {
a_indexes.Add(indexes[i]); a_args.Add(args_args[i]); c++;
continue;
}
wxArrayString margs = ::wxStringTokenize(args_args[i]);
#ifdef PA_ADEBUG
myLogTrace(MYTRACETAG, wxT("Checking mod[%s], in args = '%s'; count = %d "),
VMB(indexes[i]), VMB(args_args[i]),(int)margs.GetCount());
#endif
if (HardAccordance && (argstpl.GetCount() != margs.GetCount()))
continue;
bool Accordance = true;
for (int j = 0; j < argstpl.GetCount(); j++) {
#ifdef PA_ADEBUG
myLogTrace(MYTRACETAG, wxT("Checking template '%s'"), VMB(argstpl[j]));
#endif
re.Compile(argstpl[j], wxRE_ADVANCED);
bool rfound = false;
for (int k = 0; k < margs.GetCount(); k++) {
rfound = re.Matches(margs[k]);
#ifdef PA_ADEBUG
myLogTrace(MYTRACETAG, wxT("Checking arg '%s' -> res = %d"),
VMB(margs[k]), rfound);
#endif
if (rfound)
break;
}
if (!rfound) {
Accordance = false; break;
}
}
if (Accordance) {
a_indexes.Add(indexes[i]); a_args.Add(args_args[i]); c++;
}
}
myLogTrace(MYTRACETAG, wxT("Matches %d modules named %s: idxs/args = %d/%d"),
c, VMB(modname), (int)a_indexes.GetCount(),(int)a_args.GetCount());
return c;
#else
wxUnusedVar(modname,s_argstpl,HardAccordance);
return -1;
#endif
}
bool PulseAudio::UnloadExistingModules(wxString modname, wxString s_argstpl,
bool HardAccordance = false)
{
if (!m_bPulseAvailable)
return false;
#ifdef WITH_PULSEAUDIO
wxArrayString mis,mas; mis.Empty(); mas.Empty();
bool res = true; bool res0;
if (FoundModuleIDs(modname,s_argstpl,mis,mas,HardAccordance) > 0) {
for (int i = 0; i < mis.GetCount(); i++) {
long mid; mis[i].ToLong(&mid);
res0 = pa->unloadmodule(mid);
myLogTrace(MYTRACETAG, wxT("unloading %s module[%d] -> res = %d"),
VMB(modname), (int)mid, (int)res);
res = res ? res0 : res;
}
}
return res;
#else
wxUnusedVar(modname,s_argstpl,HardAccordance);
return false;
#endif
}
bool PulseAudio::ActivateEsound(int port)
{
if (!m_bPulseAvailable)
return false;
#ifdef WITH_PULSEAUDIO
wxString mname = wxT("module-esound-protocol-tcp");
UnloadExistingModules(mname,wxT(""));
wxString ma = wxString::Format(wxT("port=%d"), port);
UnloadExistingModules(wxT("module-native-protocol-tcp"),ma);
ma.Append(wxString::Format(wxT(" listen=127.0.0.1 auth-anonymous=1"), port));
bool res = pa->loadmodule(mname,ma);
myLogTrace(MYTRACETAG, wxT("loading %s module -> res = %d"),
VMB(mname), res);
return res;
#else
wxUnusedVar(port);
return false;
#endif
}
bool PulseAudio::ActivateNative(int port, int rrate, bool mono)
{
if (!m_bPulseAvailable)
return false;
#ifdef WITH_PULSEAUDIO
UnloadExistingModules(wxT("module-esound-protocol-tcp"),wxT(""));
wxString mname = wxT("module-native-protocol-tcp");
wxString ma = wxString::Format(wxT("port=%d"), port);
wxArrayString mis,mas; bool res;
bool bNativeModulePort = (FoundModuleIDs(mname,ma,mis,mas) > 0);
wxString mname_fake = wxT("module-null-sink");
wxString mname_conn = wxT("module-loopback");
if (!bNativeModulePort) {
ma.Append(wxString::Format(wxT(" listen=127.0.0.1 auth-anonymous=1")));
res = pa->loadmodule(mname,ma);
myLogTrace(MYTRACETAG, wxT("loading %s module -> res = %d (args = '%s')"),
VMB(mname), (int)res, VMB(ma));
}
if (!res)
return res;
wxString ma_fake = wxString::Format(wxT(" rate=%d channels=%s"),rrate,
mono ?wxT("1"):wxT("2"));
if ((rrate == 0) || (FoundModuleIDs(mname_fake,ma_fake,mis,mas) == 0)) {
wxString deltpl = wxT("(sink|sink_name|source)=(ts_sender|ts_receiver)");
UnloadExistingModules(mname_conn,deltpl);
UnloadExistingModules(mname_fake,deltpl);
if (rrate == 0)
return res;
}
#ifndef __WXMSW__
wxString dsi,dso; dsi.Empty(); dso.Empty();
pa->getdefaults(dsi,dso);
myLogTrace(MYTRACETAG, wxT("Get defaults: Sink ='%s'; Source ='%s'"),
VMB(dsi), VMB(dso));
if ((dsi.IsEmpty()) || (dso.IsEmpty()))
return true; // modules for resample are optional now
#else
// on win we check availability of sink and source only
// because old resample scheme not work
// on systems >=w7 is necessary try to load sink and source
// separately since audio devices are present according to
// speakers/headset/microphone connect status
// Also these modules always must be reloaded in case
// recent jacks reconnection
bool IsSink, IsSource;
mname = wxT("module-waveout");
ma = wxT("sink_name=output");
UnloadExistingModules(mname,ma);
ma.Append(wxT(" record=0"));
IsSink = pa->loadmodule(mname,ma);
myLogTrace(MYTRACETAG, wxT("loading %s module -> res = %d (args = '%s')"),
VMB(mname), (int)IsSink, VMB(ma));
ma = wxT("source_name=input");
UnloadExistingModules(mname,ma);
ma.Append(wxT(" playback=0"));
IsSource = pa->loadmodule(mname,ma);
myLogTrace(MYTRACETAG, wxT("loading %s module -> res = %d (args = '%s')"),
VMB(mname), (int)IsSource, VMB(ma));
res = (IsSink || IsSource);
return res;
#endif
#ifndef __WXMSW__
ma = wxT("sink_name=ts_sender") + ma_fake;
if (!FoundModuleIDs(mname_fake,wxT("sink_name=ts_sender"),mis,mas)) {
res = pa->loadmodule(mname_fake,ma);
myLogTrace(MYTRACETAG, wxT("loading %s module -> res = %d (args = '%s')"),
VMB(mname_fake), (int)res, VMB(ma));
}
ma = wxT("source=") + dso + wxT(" sink=ts_sender");
if (!FoundModuleIDs(mname_conn,wxT("sink=ts_sender"),mis,mas)) {
res = pa->loadmodule(mname_conn,ma);
myLogTrace(MYTRACETAG, wxT("loading %s module -> res = %d (args = '%s')"),
VMB(mname_conn), (int)res, VMB(ma));
}
ma = wxT("sink_name=ts_receiver") + ma_fake;
if (!FoundModuleIDs(mname_fake,wxT("sink_name=ts_receiver"),mis,mas)) {
res = pa->loadmodule(mname_fake,ma);
myLogTrace(MYTRACETAG, wxT("loading %s module -> res = %d (args = '%s')"),
VMB(mname_fake), (int)res, VMB(ma));
}
ma = wxT("source=ts_receiver.monitor sink=") + dsi;
if (!FoundModuleIDs(mname_conn,wxT("source=ts_receiver.monitor"),mis,mas)) {
res = pa->loadmodule(mname_conn,ma);
myLogTrace(MYTRACETAG, wxT("loading %s module -> res = %d (args = '%s')"),
VMB(mname_conn), (int)res, VMB(ma));
}
return true;
#endif
#else
wxUnusedVar(port,rrate,mono);
return false;
#endif
}