Skip to content

Commit

Permalink
[AE] core: add PulseAudio engine
Browse files Browse the repository at this point in the history
  • Loading branch information
topfs2 authored and Jonathan Marshall committed May 9, 2012
1 parent 74d6a48 commit f540c34
Show file tree
Hide file tree
Showing 6 changed files with 1,372 additions and 0 deletions.
271 changes: 271 additions & 0 deletions xbmc/cores/AudioEngine/Engines/PulseAE.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
/*
* Copyright (C) 2010-2012 Team XBMC
* http://xbmc.org
*
* 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 2, 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 XBMC; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/

#include "system.h"
#ifdef HAS_PULSEAUDIO

#include "PulseAE.h"
#include "PulseAEStream.h"
#include "PulseAESound.h"
#include "threads/SingleLock.h"
#include "utils/log.h"
#include "settings/Settings.h"
#include <pulse/pulseaudio.h>

/* Static helpers */
static const char *ContextStateToString(pa_context_state s)
{
switch (s)
{
case PA_CONTEXT_UNCONNECTED:
return "unconnected";
case PA_CONTEXT_CONNECTING:
return "connecting";
case PA_CONTEXT_AUTHORIZING:
return "authorizing";
case PA_CONTEXT_SETTING_NAME:
return "setting name";
case PA_CONTEXT_READY:
return "ready";
case PA_CONTEXT_FAILED:
return "failed";
case PA_CONTEXT_TERMINATED:
return "terminated";
default:
return "none";
}
}

#if 0
static const char *StreamStateToString(pa_stream_state s)
{
switch(s)
{
case PA_STREAM_UNCONNECTED:
return "unconnected";
case PA_STREAM_CREATING:
return "creating";
case PA_STREAM_READY:
return "ready";
case PA_STREAM_FAILED:
return "failed";
case PA_STREAM_TERMINATED:
return "terminated";
default:
return "none";
}
}
#endif

CPulseAE::CPulseAE()
{
m_Context = NULL;
m_MainLoop = NULL;
}

CPulseAE::~CPulseAE()
{
if (m_MainLoop)
pa_threaded_mainloop_stop(m_MainLoop);

if (m_Context)
{
pa_context_disconnect(m_Context);
pa_context_unref(m_Context);
m_Context = NULL;
}
}

bool CPulseAE::Initialize()
{
m_Volume = g_settings.m_fVolumeLevel;

if ((m_MainLoop = pa_threaded_mainloop_new()) == NULL)
{
CLog::Log(LOGERROR, "PulseAudio: Failed to allocate main loop");
return false;
}

if ((m_Context = pa_context_new(pa_threaded_mainloop_get_api(m_MainLoop), "XBMC")) == NULL)
{
CLog::Log(LOGERROR, "PulseAudio: Failed to allocate context");
return false;
}

pa_context_set_state_callback(m_Context, ContextStateCallback, m_MainLoop);

if (pa_context_connect(m_Context, NULL, (pa_context_flags_t)0, NULL) < 0)
{
CLog::Log(LOGERROR, "PulseAudio: Failed to connect context");
return false;
}

pa_threaded_mainloop_lock(m_MainLoop);
if (pa_threaded_mainloop_start(m_MainLoop) < 0)
{
CLog::Log(LOGERROR, "PulseAudio: Failed to start MainLoop");
pa_threaded_mainloop_unlock(m_MainLoop);
return false;
}

/* Wait until the context is ready */
do
{
pa_threaded_mainloop_wait(m_MainLoop);
CLog::Log(LOGDEBUG, "PulseAudio: Context %s", ContextStateToString(pa_context_get_state(m_Context)));
}
while (pa_context_get_state(m_Context) != PA_CONTEXT_READY && pa_context_get_state(m_Context) != PA_CONTEXT_FAILED);

if (pa_context_get_state(m_Context) == PA_CONTEXT_FAILED)
{
CLog::Log(LOGERROR, "PulseAudio: Waited for the Context but it failed");
pa_threaded_mainloop_unlock(m_MainLoop);
return false;
}

pa_threaded_mainloop_unlock(m_MainLoop);
return true;
}

void CPulseAE::OnSettingsChange(std::string setting)
{
}

float CPulseAE::GetVolume()
{
return m_Volume;
}

void CPulseAE::SetVolume(float volume)
{
CSingleLock lock(m_lock);
m_Volume = volume;
std::list<CPulseAEStream*>::iterator itt;
for (itt = m_streams.begin(); itt != m_streams.end(); ++itt)
(*itt)->UpdateVolume(volume);
}

IAEStream *CPulseAE::MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, CAEChannelInfo channelLayout, unsigned int options)
{
CPulseAEStream *st = new CPulseAEStream(m_Context, m_MainLoop, dataFormat, sampleRate, channelLayout, options);

CSingleLock lock(m_lock);
m_streams.push_back(st);
return st;
}

void CPulseAE::RemoveStream(IAEStream *stream)
{
std::list<CPulseAEStream*>::iterator itt;

m_streams.remove((CPulseAEStream *)stream);

for (itt = m_streams.begin(); itt != m_streams.end(); ++itt)
{
if (*itt == stream)
{
m_streams.erase(itt);
return;
}
}
}

IAEStream *CPulseAE::FreeStream(IAEStream *stream)
{
RemoveStream(stream);

CPulseAEStream *istream = (CPulseAEStream *)stream;

delete istream;

return NULL;
}

IAESound *CPulseAE::MakeSound(const std::string& file)
{
CSingleLock lock(m_lock);

CPulseAESound *sound = new CPulseAESound(file, m_Context, m_MainLoop);
if (!sound->Initialize())
{
delete sound;
return NULL;
}

m_sounds.push_back(sound);
return sound;
}

void CPulseAE::FreeSound(IAESound *sound)
{
if (!sound)
return;

sound->Stop();
CSingleLock lock(m_lock);
for (std::list<CPulseAESound*>::iterator itt = m_sounds.begin(); itt != m_sounds.end(); ++itt)
if (*itt == sound)
{
m_sounds.erase(itt);
break;
}

delete (CPulseAESound*)sound;
}

void CPulseAE::GarbageCollect()
{
CSingleLock lock(m_lock);
std::list<CPulseAEStream*>::iterator itt;
for (itt = m_streams.begin(); itt != m_streams.end();)
{
if ((*itt)->IsDestroyed())
{
delete (*itt);
itt = m_streams.erase(itt);
continue;
}
++itt;
}
}

void CPulseAE::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
{
}

void CPulseAE::ContextStateCallback(pa_context *c, void *userdata)
{
pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
switch (pa_context_get_state(c))
{
case PA_CONTEXT_READY:
case PA_CONTEXT_TERMINATED:
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
case PA_CONTEXT_FAILED:
pa_threaded_mainloop_signal(m, 0);
break;
}
}

#endif
77 changes: 77 additions & 0 deletions xbmc/cores/AudioEngine/Engines/PulseAE.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#pragma once
/*
* Copyright (C) 2010-2012 Team XBMC
* http://xbmc.org
*
* 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 2, 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 XBMC; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/

#include "system.h"
#ifdef HAS_PULSEAUDIO

#include "Interfaces/AE.h"
#include "PulseAEStream.h"
#include "PulseAESound.h"
#include "threads/CriticalSection.h"
#include <list>

struct pa_context;
struct pa_threaded_mainloop;
struct pa_stream;

class CPulseAEStream;
class CPulseAESound;
class CPulseAE : public IAE
{
protected:
friend class CAEFactory;
CPulseAE();
virtual ~CPulseAE();

public:
virtual bool Initialize ();
virtual void OnSettingsChange(std::string setting);

virtual float GetVolume();
virtual void SetVolume(float volume);

/* returns a new stream for data in the specified format */
virtual IAEStream *MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, CAEChannelInfo channelLayout, unsigned int options = 0);
virtual IAEStream *FreeStream(IAEStream *stream);
void RemoveStream(IAEStream *stream);

/* returns a new sound object */
virtual IAESound *MakeSound(const std::string& file);
virtual void FreeSound(IAESound *sound);

/* free's sounds that have expired */
virtual void GarbageCollect();

virtual void EnumerateOutputDevices(AEDeviceList &devices, bool passthrough);
private:
CCriticalSection m_lock;
std::list<CPulseAEStream*> m_streams;
std::list<CPulseAESound* > m_sounds;

static void ContextStateCallback(pa_context *c, void *userdata);

pa_context *m_Context;
pa_threaded_mainloop *m_MainLoop;
float m_Volume;
};

#endif
Loading

0 comments on commit f540c34

Please sign in to comment.