You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2109 lines
57 KiB
2109 lines
57 KiB
/* **********
|
|
*
|
|
* This software is released under the provisions of the GPL version 2.
|
|
* see file "COPYING". If that file is not available, the full statement
|
|
* of the license can be found at
|
|
*
|
|
* http://www.fsf.org/licensing/licenses/gpl.txt
|
|
*
|
|
* Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved.
|
|
* Portions Copyright (c) Paul Cifarelli 2005
|
|
*
|
|
* ********** */
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "hxcomm.h"
|
|
#include "hxcore.h"
|
|
#include "hxclsnk.h"
|
|
#include "hxerror.h"
|
|
#include "hxauth.h"
|
|
#include "hxprefs.h"
|
|
#include "hxstrutl.h"
|
|
#include "hxvsrc.h"
|
|
#include "hxresult.h"
|
|
#include "hxplugn.h"
|
|
|
|
#include "hspadvisesink.h"
|
|
#include "hsperror.h"
|
|
#include "hspauthmgr.h"
|
|
#include "hspcontext.h"
|
|
#include <X11/Xlib.h>
|
|
#include <dlfcn.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include "hxausvc.h"
|
|
|
|
#include "dllpath.h"
|
|
|
|
#include <config.h>
|
|
|
|
#include "helix-sp.h"
|
|
#include "hspvoladvise.h"
|
|
#include "utils.h"
|
|
#include "hsphook.h"
|
|
#include "hxfiles.h"
|
|
|
|
#ifdef USE_HELIX_ALSA
|
|
#include <alsa/asoundlib.h>
|
|
#include "hspalsadevice.h"
|
|
#endif
|
|
|
|
#ifdef HX_LOG_SUBSYSTEM
|
|
#include "hxtlogutil.h"
|
|
#endif
|
|
|
|
#ifdef __FreeBSD__
|
|
#define PTHREAD_MUTEX_FAST_NP PTHREAD_MUTEX_NORMAL
|
|
#endif
|
|
|
|
#if !defined(__NetBSD__) && !defined(__OpenBSD__)
|
|
#include <sys/soundcard.h>
|
|
#else
|
|
#include <soundcard.h>
|
|
#endif
|
|
|
|
typedef HX_RESULT (HXEXPORT_PTR FPRMSETDLLACCESSPATH) (const char*);
|
|
|
|
class HelixSimplePlayerAudioStreamInfoResponse : public IHXAudioStreamInfoResponse
|
|
{
|
|
public:
|
|
HelixSimplePlayerAudioStreamInfoResponse(HelixSimplePlayer *player, int playerIndex) :
|
|
m_Player(player), m_index(playerIndex), m_lRefCount(0) {}
|
|
virtual ~HelixSimplePlayerAudioStreamInfoResponse() {}
|
|
|
|
/*
|
|
* IUnknown methods
|
|
*/
|
|
STDTQT_METHOD(QueryInterface) (THIS_
|
|
REFIID riid,
|
|
void** ppvObj);
|
|
|
|
STDMETHOD_(ULONG32,AddRef) (THIS);
|
|
|
|
STDMETHOD_(ULONG32,Release) (THIS);
|
|
|
|
/*
|
|
* IHXAudioStreamInfoResponse methods
|
|
*/
|
|
STDTQT_METHOD(OnStream) (THIS_
|
|
IHXAudioStream *pAudioStream
|
|
);
|
|
private:
|
|
HelixSimplePlayer *m_Player;
|
|
IHXAudioStream *m_Stream;
|
|
int m_index;
|
|
LONG32 m_lRefCount;
|
|
HXAudioFormat m_audiofmt;
|
|
};
|
|
|
|
STDMETHODIMP
|
|
HelixSimplePlayerAudioStreamInfoResponse::QueryInterface(REFIID riid, void**ppvObj)
|
|
{
|
|
if(IsEqualIID(riid, IID_IUnknown))
|
|
{
|
|
AddRef();
|
|
*ppvObj = (IUnknown*)(IHXAudioStreamInfoResponse *)this;
|
|
return HXR_OK;
|
|
}
|
|
else if(IsEqualIID(riid, IID_IHXAudioStreamInfoResponse))
|
|
{
|
|
AddRef();
|
|
*ppvObj = (IHXAudioStreamInfoResponse*)this;
|
|
return HXR_OK;
|
|
}
|
|
*ppvObj = NULL;
|
|
return HXR_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP_(UINT32)
|
|
HelixSimplePlayerAudioStreamInfoResponse::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_lRefCount);
|
|
}
|
|
|
|
STDMETHODIMP_(UINT32)
|
|
HelixSimplePlayerAudioStreamInfoResponse::Release()
|
|
{
|
|
if (InterlockedDecrement(&m_lRefCount) > 0)
|
|
{
|
|
return m_lRefCount;
|
|
}
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
STDMETHODIMP HelixSimplePlayerAudioStreamInfoResponse::OnStream(IHXAudioStream *pAudioStream)
|
|
{
|
|
m_Player->print2stderr("Stream Added on player %d, stream duration %ld, sources %d\n", m_index,
|
|
m_Player->duration(m_index), m_Player->ppctrl[m_index]->pPlayer->GetSourceCount());
|
|
|
|
m_Player->ppctrl[m_index]->pStream = pAudioStream;
|
|
m_Player->ppctrl[m_index]->pPreMixHook = new HSPPreMixAudioHook(m_Player, m_index, pAudioStream,
|
|
m_Player->ppctrl[m_index]->bFadeIn,
|
|
m_Player->ppctrl[m_index]->ulFadeLength);
|
|
|
|
// addpremixhook adds another ref
|
|
pAudioStream->AddPreMixHook(m_Player->ppctrl[m_index]->pPreMixHook, false);
|
|
m_Player->ppctrl[m_index]->pPreMixHook->Release(); // release the ref added in the premixhook constructor
|
|
|
|
m_Player->ppctrl[m_index]->bStarting = false;
|
|
|
|
return HXR_OK;
|
|
}
|
|
|
|
// Constants
|
|
const int DEFAULT_TIME_DELTA = 2000;
|
|
const int DEFAULT_STOP_TIME = -1;
|
|
const int SLEEP_TIME = 10;
|
|
const int GUID_LEN = 64;
|
|
|
|
// *** IUnknown methods ***
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Method:
|
|
// IUnknown::QueryInterface
|
|
// Purpose:
|
|
// Implement this to export the interfaces supported by your
|
|
// object.
|
|
//
|
|
STDMETHODIMP HelixSimplePlayerVolumeAdvice::QueryInterface(REFIID riid, void** ppvObj)
|
|
{
|
|
if (IsEqualIID(riid, IID_IUnknown))
|
|
{
|
|
AddRef();
|
|
*ppvObj = (IUnknown*)(IHXClientAdviseSink*)this;
|
|
return HXR_OK;
|
|
}
|
|
else if (IsEqualIID(riid, IID_IHXVolumeAdviseSink))
|
|
{
|
|
AddRef();
|
|
*ppvObj = (IHXVolumeAdviseSink*)this;
|
|
return HXR_OK;
|
|
}
|
|
|
|
*ppvObj = NULL;
|
|
return HXR_NOINTERFACE;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Method:
|
|
// IUnknown::AddRef
|
|
// Purpose:
|
|
// Everyone usually implements this the same... feel free to use
|
|
// this implementation.
|
|
//
|
|
STDMETHODIMP_(ULONG32) HelixSimplePlayerVolumeAdvice::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_lRefCount);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Method:
|
|
// IUnknown::Release
|
|
// Purpose:
|
|
// Everyone usually implements this the same... feel free to use
|
|
// this implementation.
|
|
//
|
|
STDMETHODIMP_(ULONG32) HelixSimplePlayerVolumeAdvice::Release()
|
|
{
|
|
if (InterlockedDecrement(&m_lRefCount) > 0)
|
|
{
|
|
return m_lRefCount;
|
|
}
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
STDMETHODIMP HelixSimplePlayerVolumeAdvice::OnVolumeChange(const UINT16 /*uVolume*/)
|
|
{
|
|
m_Player->onVolumeChange(m_index);
|
|
#ifdef HELIX_SW_VOLUME_INTERFACE
|
|
m_Player->ppctrl[m_index]->volume = uVolume;
|
|
#endif
|
|
return HXR_OK;
|
|
}
|
|
|
|
STDMETHODIMP HelixSimplePlayerVolumeAdvice::OnMuteChange(const BOOL bMute)
|
|
{
|
|
m_Player->onMuteChange(m_index);
|
|
m_Player->ppctrl[m_index]->ismute = bMute;
|
|
return HXR_OK;
|
|
}
|
|
|
|
|
|
int HelixSimplePlayer::print2stdout(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char buf[1024];
|
|
|
|
va_start(args, fmt);
|
|
|
|
int ret = vsprintf(buf, fmt, args);
|
|
std::cout << buf;
|
|
|
|
va_end(args);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int HelixSimplePlayer::print2stderr(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char buf[1024];
|
|
|
|
va_start(args, fmt);
|
|
|
|
int ret = vsprintf(buf, fmt, args);
|
|
std::cerr << buf;
|
|
|
|
va_end(args);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void HelixSimplePlayer::setFadeout(bool fadeout, unsigned long fadelength, int playerIndex )
|
|
{
|
|
if (playerIndex == ALL_PLAYERS)
|
|
{
|
|
for (int i = 0; i<nNumPlayers; i++)
|
|
setFadeout(fadeout, fadelength, i);
|
|
}
|
|
else
|
|
{
|
|
if (playerIndex >=0 && playerIndex < nNumPlayers && ppctrl[playerIndex]->pPreMixHook)
|
|
{
|
|
ppctrl[playerIndex]->ulFadeLength = fadelength;
|
|
((HSPPreMixAudioHook *)ppctrl[playerIndex]->pPreMixHook)->setFadelength(ppctrl[playerIndex]->ulFadeLength);
|
|
((HSPPreMixAudioHook *)ppctrl[playerIndex]->pPreMixHook)->setFadeout(fadeout);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void HelixSimplePlayer::cleanUpStream(int playerIndex)
|
|
{
|
|
//print2stderr("CLEANUPSTREAM\n");
|
|
stop(playerIndex);
|
|
}
|
|
|
|
|
|
void HelixSimplePlayer::updateEQgains()
|
|
{
|
|
for (int i = 0; i<nNumPlayers; i++)
|
|
if (pFinalAudioHook && isEQenabled())
|
|
((HSPFinalAudioHook *)pFinalAudioHook)->updateEQgains(m_preamp, m_equalizerGains);
|
|
}
|
|
|
|
/*
|
|
* handle one event
|
|
*/
|
|
void HelixSimplePlayer::DoEvent()
|
|
{
|
|
struct _HXxEvent *pNothing = 0x0;
|
|
struct timeval mtime;
|
|
|
|
mtime.tv_sec = 0;
|
|
mtime.tv_usec = SLEEP_TIME * 1000;
|
|
usleep(SLEEP_TIME*1000);
|
|
pEngine->EventOccurred(pNothing);
|
|
}
|
|
|
|
/*
|
|
* handle events for at most nTimeDelta milliseconds
|
|
*/
|
|
void HelixSimplePlayer::DoEvents(int)
|
|
{
|
|
DoEvent();
|
|
}
|
|
|
|
/*
|
|
* return the number of milliseconds since the epoch
|
|
*/
|
|
UINT32 HelixSimplePlayer::GetTime()
|
|
{
|
|
timeval t;
|
|
gettimeofday(&t, NULL);
|
|
|
|
// FIXME:
|
|
// the fact that the result is bigger than a UINT32 is really irrelevant;
|
|
// we can still play a stream for many many years...
|
|
return (UINT32)((t.tv_sec * 1000) + (t.tv_usec / 1000));
|
|
}
|
|
|
|
char* HelixSimplePlayer::RemoveWrappingQuotes(char* str)
|
|
{
|
|
int len = strlen(str);
|
|
if (len > 0)
|
|
{
|
|
if (str[len-1] == '"') str[--len] = 0;
|
|
if (str[0] == '"') {int i = 0; do { str[i] = str[i+1]; ++i; } while(--len); }
|
|
}
|
|
return str;
|
|
}
|
|
|
|
|
|
HelixSimplePlayer::HelixSimplePlayer() :
|
|
theErr(HXR_FAILED),
|
|
pErrorSink(NULL),
|
|
pErrorSinkControl(NULL),
|
|
pPluginE(0),
|
|
pPlugin2Handler(0),
|
|
ppctrl(NULL),
|
|
bURLFound(false),
|
|
nNumPlayers(0),
|
|
nNumPlayRepeats(1),
|
|
nTimeDelta(DEFAULT_TIME_DELTA),
|
|
nStopTime(DEFAULT_STOP_TIME),
|
|
bStopTime(true),
|
|
bStopping(false),
|
|
nPlay(0),
|
|
bEnableAdviceSink(false),
|
|
bEnableVerboseMode(false),
|
|
pEngine(NULL),
|
|
pEngineContext(NULL),
|
|
m_pszUsername(NULL),
|
|
m_pszPassword(NULL),
|
|
m_pszGUIDFile(NULL),
|
|
m_pszGUIDList(NULL),
|
|
m_Error(0),
|
|
m_ulNumSecondsPlayed(0),
|
|
mimehead(0),
|
|
mimelistlen(0),
|
|
m_preamp(0),
|
|
m_outputsink(OSS),
|
|
m_device(0),
|
|
#ifdef USE_HELIX_ALSA
|
|
m_direct(ALSA), // TODO: out why my alsa direct HW reader doesn't pickup changes in kmix (the whole purpose of this!)
|
|
#else
|
|
m_direct(OSS),
|
|
#endif
|
|
m_AlsaCapableCore(false),
|
|
m_nDevID(-1),
|
|
m_pAlsaMixerHandle(NULL),
|
|
m_pAlsaMasterMixerElem(NULL),
|
|
m_pAlsaPCMMixerElem(NULL),
|
|
m_alsaDevice("default"),
|
|
m_urlchanged(0),
|
|
m_volBefore(-1),
|
|
m_volAtStart(-1),
|
|
m_MvolBefore(-1),
|
|
m_MvolAtStart(-1)
|
|
{
|
|
|
|
pthread_mutexattr_t ma;
|
|
|
|
pthread_mutexattr_init(&ma);
|
|
pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_FAST_NP); // note this is not portable outside linux and a few others
|
|
pthread_mutex_init(&m_engine_m, &ma);
|
|
}
|
|
|
|
void HelixSimplePlayer::init(const char *corelibhome, const char *pluginslibhome, const char *codecshome, int numPlayers)
|
|
{
|
|
int i;
|
|
|
|
theErr = HXR_OK;
|
|
|
|
FPRMCREATEENGINE fpCreateEngine;
|
|
FPRMSETDLLACCESSPATH fpSetDLLAccessPath;
|
|
|
|
SafeSprintf(mCoreLibPath, MAX_PATH, "%s/%s", corelibhome, "clntcore.so");
|
|
|
|
// Allocate arrays to keep track of players and client
|
|
// context pointers
|
|
ppctrl = new struct playerCtrl *[MAX_PLAYERS];
|
|
memset(ppctrl, 0, sizeof(struct playerCtrl *) * MAX_PLAYERS);
|
|
|
|
if (!ppctrl)
|
|
{
|
|
print2stderr("Error: Out of Memory.\n");
|
|
theErr = HXR_UNEXPECTED;
|
|
return;
|
|
}
|
|
|
|
fpCreateEngine = NULL;
|
|
|
|
// prepare/load the HXCore module
|
|
//print2stdout("Simpleplayer is looking for the client core at %s\n", mCoreLibPath );
|
|
|
|
core_handle = dlopen(mCoreLibPath, RTLD_LAZY | RTLD_GLOBAL);
|
|
if (!core_handle)
|
|
{
|
|
print2stderr("splayer: failed to open corelib, errno %d\n", errno);
|
|
theErr = HXR_FAILED;
|
|
return;
|
|
}
|
|
fpCreateEngine = (FPRMCREATEENGINE) dlsym(core_handle, "CreateEngine");
|
|
fpSetDLLAccessPath = (FPRMSETDLLACCESSPATH) dlsym(core_handle, "SetDLLAccessPath");
|
|
|
|
if (fpCreateEngine == NULL ||
|
|
fpSetDLLAccessPath == NULL )
|
|
{
|
|
theErr = HXR_FAILED;
|
|
return;
|
|
}
|
|
|
|
//Now tell the client core where to find the plugins and codecs it
|
|
//will be searching for.
|
|
if (NULL != fpSetDLLAccessPath)
|
|
{
|
|
//Create a null delimited, double-null terminated string
|
|
//containing the paths to the encnet library (DT_Common) and
|
|
//the sdpplin library (DT_Plugins)...
|
|
char pPaths[256]; /* Flawfinder: ignore */
|
|
char* pPathNextPosition = pPaths;
|
|
memset(pPaths, 0, 256);
|
|
UINT32 ulBytesLeft = 256;
|
|
|
|
char* pNextPath = new char[256];
|
|
memset(pNextPath, 0, 256);
|
|
|
|
SafeSprintf(pNextPath, 256, "DT_Common=%s", corelibhome);
|
|
//print2stderr("Common DLL path %s\n", pNextPath );
|
|
UINT32 ulBytesToCopy = strlen(pNextPath) + 1;
|
|
if (ulBytesToCopy <= ulBytesLeft)
|
|
{
|
|
memcpy(pPathNextPosition, pNextPath, ulBytesToCopy);
|
|
pPathNextPosition += ulBytesToCopy;
|
|
ulBytesLeft -= ulBytesToCopy;
|
|
}
|
|
|
|
SafeSprintf(pNextPath, 256, "DT_Plugins=%s", pluginslibhome);
|
|
//print2stderr("Plugin path %s\n", pNextPath );
|
|
ulBytesToCopy = strlen(pNextPath) + 1;
|
|
if (ulBytesToCopy <= ulBytesLeft)
|
|
{
|
|
memcpy(pPathNextPosition, pNextPath, ulBytesToCopy);
|
|
pPathNextPosition += ulBytesToCopy;
|
|
ulBytesLeft -= ulBytesToCopy;
|
|
}
|
|
|
|
SafeSprintf(pNextPath, 256, "DT_Codecs=%s", codecshome);
|
|
//print2stderr("Codec path %s\n", pNextPath );
|
|
ulBytesToCopy = strlen(pNextPath) + 1;
|
|
if (ulBytesToCopy <= ulBytesLeft)
|
|
{
|
|
memcpy(pPathNextPosition, pNextPath, ulBytesToCopy);
|
|
pPathNextPosition += ulBytesToCopy;
|
|
ulBytesLeft -= ulBytesToCopy;
|
|
*pPathNextPosition='\0';
|
|
}
|
|
|
|
fpSetDLLAccessPath((char*)pPaths);
|
|
|
|
HX_VECTOR_DELETE(pNextPath);
|
|
}
|
|
|
|
// create client engine
|
|
if (HXR_OK != fpCreateEngine((IHXClientEngine**)&pEngine))
|
|
{
|
|
theErr = HXR_FAILED;
|
|
return;
|
|
}
|
|
|
|
pCommonClassFactory = 0;
|
|
// get the common class factory
|
|
pEngine->QueryInterface(IID_IHXCommonClassFactory, (void **) &pCommonClassFactory);
|
|
if (!pCommonClassFactory)
|
|
print2stderr("no CommonClassFactory\n");
|
|
|
|
// get the engine setup interface
|
|
IHXClientEngineSetup *pEngineSetup = 0;
|
|
pEngine->QueryInterface(IID_IHXClientEngineSetup, (void **) &pEngineSetup);
|
|
if (!pEngineSetup)
|
|
print2stderr("no engine setup interface\n");
|
|
else
|
|
{
|
|
pEngineContext = new HSPEngineContext(this, pCommonClassFactory);
|
|
pEngineContext->AddRef();
|
|
#ifdef HX_LOG_SUBSYSTEM
|
|
HX_ENABLE_LOGGING(pEngineContext);
|
|
#endif
|
|
pEngineSetup->Setup(pEngineContext);
|
|
pEngineSetup->Release();
|
|
}
|
|
|
|
// get the client engine selector
|
|
pCEselect = 0;
|
|
pEngine->QueryInterface(IID_IHXClientEngineSelector, (void **) &pCEselect);
|
|
if (!pCEselect)
|
|
print2stderr("no CE selector\n");
|
|
|
|
pPluginE = 0;
|
|
// get the plugin enumerator
|
|
pEngine->QueryInterface(IID_IHXPluginEnumerator, (void **) &pPluginE);
|
|
if (!pPluginE)
|
|
print2stderr("no plugin enumerator\n");
|
|
|
|
pPlugin2Handler = 0;
|
|
// get the plugin2handler
|
|
pEngine->QueryInterface(IID_IHXPlugin2Handler, (void **) &pPlugin2Handler);
|
|
if (!pPlugin2Handler)
|
|
print2stderr("no plugin enumerator\n");
|
|
|
|
pAudioDeviceManager = 0;
|
|
// get the audio device mananger
|
|
pEngine->QueryInterface(IID_IHXAudioDeviceManager, (void **) &pAudioDeviceManager);
|
|
if (!pAudioDeviceManager)
|
|
print2stderr("no audio device manager\n");
|
|
|
|
// create players
|
|
for (i = 0; i < numPlayers; i++)
|
|
{
|
|
addPlayer();
|
|
}
|
|
|
|
#ifdef USE_HELIX_ALSA
|
|
pAudioDevice = 0;
|
|
if ( m_outputsink == ALSA && pAudioDeviceManager && !m_AlsaCapableCore)
|
|
{
|
|
pAudioDevice = new HSPAudioDevice(this, m_device);
|
|
|
|
// change the AudioDevice if we are using alsa (player 0's audion device is special...)
|
|
pAudioDeviceManager->Replace(pAudioDevice);
|
|
}
|
|
#endif
|
|
|
|
pAudioDeviceResponse = 0;
|
|
pEngine->QueryInterface(IID_IHXAudioDeviceResponse, (void**) &pAudioDeviceResponse);
|
|
|
|
pAudioHookManager = 0;
|
|
pFinalAudioHook = 0;
|
|
pEngine->QueryInterface(IID_IHXAudioHookManager, (void **) &pAudioHookManager);
|
|
if (!pAudioHookManager)
|
|
print2stderr("no audio device hook manager\n");
|
|
|
|
|
|
// install hook for visualizations, equalizer, and volume control - for use with streams
|
|
// the time in the packets is the presentation time - which maps better to streams
|
|
HSPFinalAudioHook *pPMAH = new HSPFinalAudioHook(this);
|
|
pAudioHookManager->AddHook(pPMAH);
|
|
pFinalAudioHook = pPMAH;
|
|
|
|
if (pPlugin2Handler)
|
|
{
|
|
IHXValues* pPluginProps;
|
|
const char* szPropName;
|
|
IHXBuffer* pPropValue;
|
|
char *value;
|
|
HX_RESULT ret;
|
|
MimeList *ml;
|
|
mimehead = 0;
|
|
char mime[1024];
|
|
char ext[1024];
|
|
bool hasmime, hasexts;
|
|
|
|
pPluginProps = 0;
|
|
int n = pPlugin2Handler->GetNumOfPlugins2();
|
|
m_numPlugins = n;
|
|
print2stderr("Got the plugin2 handler: numplugins = %d\n", n);
|
|
m_pluginInfo = new pluginInfo* [n];
|
|
for (i=0; i<n; i++)
|
|
{
|
|
m_pluginInfo[i] = new pluginInfo;
|
|
m_pluginInfo[i]->description = "";
|
|
m_pluginInfo[i]->copyright = "";
|
|
m_pluginInfo[i]->moreinfourl = "";
|
|
hasmime = hasexts = false;
|
|
pPlugin2Handler->GetPluginInfo(i, pPluginProps);
|
|
if (pPluginProps)
|
|
{
|
|
ret = pPluginProps->GetFirstPropertyCString(szPropName, pPropValue);
|
|
while(SUCCEEDED(ret))
|
|
{
|
|
value = (char*)pPropValue->GetBuffer();
|
|
if (!strcmp(szPropName, "FileMime"))
|
|
{
|
|
strcpy(mime, value);
|
|
hasmime = true;
|
|
}
|
|
|
|
if (!strcmp(szPropName, "FileExtensions"))
|
|
{
|
|
strcpy(ext, value);
|
|
hasexts = true;
|
|
}
|
|
|
|
if (!strcmp(szPropName, "Description"))
|
|
{
|
|
m_pluginInfo[i]->description = new char[ strlen(value) + 1 ];
|
|
strcpy(m_pluginInfo[i]->description, value);
|
|
}
|
|
|
|
if (!strcmp(szPropName, "Copyright"))
|
|
{
|
|
m_pluginInfo[i]->copyright = new char[ strlen(value) + 1 ];
|
|
strcpy(m_pluginInfo[i]->copyright, value);
|
|
}
|
|
|
|
|
|
if (!strcmp(szPropName, "PlgCopy"))
|
|
{
|
|
m_pluginInfo[i]->moreinfourl = new char[ strlen(value) + 1 ];
|
|
strcpy(m_pluginInfo[i]->moreinfourl, value);
|
|
}
|
|
|
|
ret = pPluginProps->GetNextPropertyCString(szPropName, pPropValue);
|
|
}
|
|
HX_RELEASE(pPluginProps);
|
|
if (hasmime && hasexts)
|
|
{
|
|
mimelistlen++;
|
|
ml = new MimeList(mime, ext);
|
|
ml->fwd = mimehead;
|
|
mimehead = ml;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int HelixSimplePlayer::initDirectSS()
|
|
{
|
|
if (m_outputsink == ALSA)
|
|
{
|
|
closeAudioDevice();
|
|
m_direct = ALSA;
|
|
openAudioDevice();
|
|
}
|
|
else
|
|
{
|
|
closeAudioDevice();
|
|
m_direct = OSS;
|
|
openAudioDevice();
|
|
}
|
|
|
|
#ifdef USE_HELIX_ALSA
|
|
m_MvolBefore = m_MvolAtStart = getDirectMasterVolume();
|
|
print2stderr("***Master VolAtStart is %d\n", m_MvolAtStart);
|
|
setDirectMasterVolume(m_MvolAtStart);
|
|
#endif
|
|
|
|
m_volBefore = m_volAtStart = getDirectPCMVolume();
|
|
print2stderr("***VolAtStart is %d\n", m_volAtStart);
|
|
setDirectPCMVolume(m_volAtStart);
|
|
return 0;
|
|
}
|
|
|
|
int HelixSimplePlayer::addPlayer()
|
|
{
|
|
if ((nNumPlayers+1) == MAX_PLAYERS)
|
|
{
|
|
print2stderr("MAX_PLAYERS: %d nNumPlayers: %d\n", MAX_PLAYERS, nNumPlayers);
|
|
return -1;
|
|
}
|
|
|
|
ppctrl[nNumPlayers] = new struct playerCtrl;
|
|
memset(ppctrl[nNumPlayers], 0, sizeof(struct playerCtrl));
|
|
|
|
pthread_mutexattr_t ma;
|
|
|
|
pthread_mutexattr_init(&ma);
|
|
pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_FAST_NP); // note this is not portable outside linux and a few others
|
|
pthread_mutex_init(&ppctrl[nNumPlayers]->m_scope_m, &ma);
|
|
|
|
ppctrl[nNumPlayers]->bPlaying = false;
|
|
ppctrl[nNumPlayers]->bStarting = false;
|
|
ppctrl[nNumPlayers]->bFadeIn = false;
|
|
ppctrl[nNumPlayers]->bFadeOut = false;
|
|
ppctrl[nNumPlayers]->ulFadeLength = 0;
|
|
ppctrl[nNumPlayers]->pStream = 0;
|
|
ppctrl[nNumPlayers]->pszURL = 0;
|
|
|
|
memset(&ppctrl[nNumPlayers]->md, 0, sizeof(ppctrl[nNumPlayers]->md));
|
|
|
|
ppctrl[nNumPlayers]->pHSPContext = new HSPClientContext(nNumPlayers, this);
|
|
if (!ppctrl[nNumPlayers]->pHSPContext)
|
|
{
|
|
print2stdout("Error: Out of Memory. num players is %d\n", nNumPlayers);
|
|
theErr = HXR_UNEXPECTED;
|
|
return -1;
|
|
}
|
|
ppctrl[nNumPlayers]->pHSPContext->AddRef();
|
|
|
|
//initialize the example context
|
|
|
|
char pszGUID[GUID_LEN + 1]; /* Flawfinder: ignore */ // add 1 for terminator
|
|
//char* token = NULL;
|
|
IHXPreferences* pPreferences = NULL;
|
|
|
|
if (HXR_OK != pEngine->CreatePlayer(ppctrl[nNumPlayers]->pPlayer))
|
|
{
|
|
theErr = HXR_FAILED;
|
|
return -1;
|
|
}
|
|
|
|
pszGUID[0] = '\0';
|
|
|
|
// disable for now - I don't know what I was thinking
|
|
#ifdef _DISABLE_CUZ_I_MUST_HAVE_BEEN_NUTS_
|
|
if (m_pszGUIDList)
|
|
{
|
|
// Get next GUID from the GUID list
|
|
if (nNumPlayers == 0)
|
|
{
|
|
token = strtok(m_pszGUIDList, "\n\0");
|
|
}
|
|
else
|
|
{
|
|
token = strtok(NULL, "\n\0");
|
|
}
|
|
if (token)
|
|
{
|
|
strncpy(pszGUID, token, GUID_LEN); /* Flawfinder: ignore */
|
|
pszGUID[GUID_LEN] = '\0';
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ppctrl[nNumPlayers]->pPlayer->QueryInterface(IID_IHXPreferences, (void**) &pPreferences);
|
|
ppctrl[nNumPlayers]->pHSPContext->Init(ppctrl[nNumPlayers]->pPlayer, pPreferences, pszGUID);
|
|
ppctrl[nNumPlayers]->pPlayer->SetClientContext(ppctrl[nNumPlayers]->pHSPContext);
|
|
HX_RELEASE(pPreferences);
|
|
|
|
ppctrl[nNumPlayers]->pPlayer->QueryInterface(IID_IHXErrorSinkControl, (void**) &pErrorSinkControl);
|
|
if (pErrorSinkControl)
|
|
{
|
|
ppctrl[nNumPlayers]->pHSPContext->QueryInterface(IID_IHXErrorSink, (void**) &pErrorSink);
|
|
if (pErrorSink)
|
|
pErrorSinkControl->AddErrorSink(pErrorSink, HXLOG_EMERG, HXLOG_INFO);
|
|
HX_RELEASE(pErrorSink);
|
|
}
|
|
|
|
HX_RELEASE(pErrorSinkControl);
|
|
|
|
// Get the Player2 interface
|
|
ppctrl[nNumPlayers]->pPlayer->QueryInterface(IID_IHXPlayer2, (void**) &ppctrl[nNumPlayers]->pPlayer2);
|
|
if (!ppctrl[nNumPlayers]->pPlayer2)
|
|
print2stderr("no player2 device\n");
|
|
|
|
// Get the Audio Player
|
|
ppctrl[nNumPlayers]->pPlayer->QueryInterface(IID_IHXAudioPlayer, (void**) &ppctrl[nNumPlayers]->pAudioPlayer);
|
|
if (ppctrl[nNumPlayers]->pAudioPlayer)
|
|
{
|
|
// ...and now the volume interface
|
|
//ppctrl[nNumPlayers]->pVolume = ppctrl[nNumPlayers]->pAudioPlayer->GetAudioVolume();
|
|
//ppctrl[nNumPlayers]->pVolume = ppctrl[nNumPlayers]->pAudioPlayer->GetDeviceVolume();
|
|
if (ppctrl[nNumPlayers]->pVolume)
|
|
{
|
|
#ifndef HELIX_SW_VOLUME_INTERFACE
|
|
HelixSimplePlayerVolumeAdvice *pVA = new HelixSimplePlayerVolumeAdvice(this, nNumPlayers);
|
|
pVA->AddRef();
|
|
ppctrl[nNumPlayers]->pVolume->AddAdviseSink((IHXVolumeAdviseSink *)pVA);
|
|
ppctrl[nNumPlayers]->pVolumeAdvise = pVA;
|
|
ppctrl[nNumPlayers]->volume = 50; // should get volume advise, which will set this properly
|
|
#else
|
|
// set the helix sw interface volume to 100, we'll control the volume either ourselves post equalization or by
|
|
// amaorok using direct hardware volume
|
|
//ppctrl[nNumPlayers]->pVolume->SetVolume(100);
|
|
ppctrl[nNumPlayers]->pVolume->SetVolume(100);
|
|
ppctrl[nNumPlayers]->pVolumeAdvise = 0;
|
|
#endif
|
|
}
|
|
|
|
// add the IHXAudioStreamInfoResponse it the AudioPlayer
|
|
HelixSimplePlayerAudioStreamInfoResponse *pASIR = new HelixSimplePlayerAudioStreamInfoResponse(this, nNumPlayers);
|
|
ppctrl[nNumPlayers]->pAudioPlayer->SetStreamInfoResponse(pASIR);
|
|
ppctrl[nNumPlayers]->pStreamInfoResponse = pASIR;
|
|
|
|
// ...and get the CrossFader
|
|
ppctrl[nNumPlayers]->pAudioPlayer->QueryInterface(IID_IHXAudioCrossFade, (void **) &(ppctrl[nNumPlayers]->pCrossFader));
|
|
if (!ppctrl[nNumPlayers]->pCrossFader)
|
|
print2stderr("CrossFader not available\n");
|
|
|
|
// install hook for visualizations, equalizer, and volume control - for use with local files
|
|
// a FinalAudioHook is used for streams
|
|
HSPPostMixAudioHook *pPMAH = new HSPPostMixAudioHook(this, nNumPlayers);
|
|
ppctrl[nNumPlayers]->pAudioPlayer->AddPostMixHook(pPMAH, false, true);
|
|
ppctrl[nNumPlayers]->pPostMixHook = pPMAH;
|
|
}
|
|
else
|
|
print2stderr("No AudioPlayer Found - how can we play music!!\n");
|
|
|
|
++nNumPlayers;
|
|
|
|
//print2stderr("Added player, total is %d\n",nNumPlayers);
|
|
return 0;
|
|
}
|
|
|
|
HelixSimplePlayer::~HelixSimplePlayer()
|
|
{
|
|
tearDown();
|
|
|
|
// only now invalidate the device, not whenever we teardown
|
|
delete [] m_device;
|
|
}
|
|
|
|
void HelixSimplePlayer::tearDown()
|
|
{
|
|
int i;
|
|
FPRMCLOSEENGINE fpCloseEngine;
|
|
|
|
if (theErr != HXR_OK) // failed to initialize properly
|
|
return;
|
|
|
|
// make sure all players are stopped,
|
|
stop();
|
|
|
|
print2stderr("TEARDOWN\n");
|
|
|
|
for (i=nNumPlayers-1; i>=0; i--)
|
|
{
|
|
if (ppctrl[i]->pCrossFader)
|
|
ppctrl[i]->pCrossFader->Release();
|
|
|
|
if (ppctrl[i]->pAudioPlayer)
|
|
{
|
|
ppctrl[i]->pAudioPlayer->RemovePostMixHook( ppctrl[i]->pPostMixHook );
|
|
ppctrl[i]->pPostMixHook->Release();
|
|
|
|
ppctrl[i]->pAudioPlayer->RemoveStreamInfoResponse((IHXAudioStreamInfoResponse *) ppctrl[i]->pStreamInfoResponse);
|
|
|
|
if (ppctrl[i]->pVolume)
|
|
{
|
|
if (ppctrl[i]->pVolumeAdvise)
|
|
{
|
|
ppctrl[i]->pVolume->RemoveAdviseSink(ppctrl[i]->pVolumeAdvise);
|
|
ppctrl[i]->pVolumeAdvise->Release();
|
|
}
|
|
ppctrl[i]->pVolume->Release();
|
|
}
|
|
|
|
ppctrl[i]->pAudioPlayer->Release();
|
|
}
|
|
|
|
if ( ppctrl[i]->pszURL )
|
|
delete [] ppctrl[i]->pszURL;
|
|
|
|
if (ppctrl[i]->pHSPContext)
|
|
ppctrl[i]->pHSPContext->Release();
|
|
|
|
if (ppctrl[i]->pPlayer2)
|
|
ppctrl[i]->pPlayer2->Release();
|
|
|
|
if (ppctrl[i]->pPlayer && pEngine)
|
|
{
|
|
pEngine->ClosePlayer(ppctrl[i]->pPlayer);
|
|
ppctrl[i]->pPlayer->Release();
|
|
}
|
|
|
|
delete ppctrl[i];
|
|
}
|
|
|
|
if (pAudioDevice)
|
|
pAudioDevice->Release();
|
|
|
|
if (pAudioDeviceResponse)
|
|
pAudioDeviceResponse->Release();
|
|
|
|
delete [] ppctrl;
|
|
|
|
if (pCommonClassFactory)
|
|
pCommonClassFactory->Release();
|
|
if (pCEselect)
|
|
pCEselect->Release();
|
|
if (pPluginE)
|
|
pPluginE->Release();
|
|
if (pPlugin2Handler)
|
|
pPlugin2Handler->Release();
|
|
if (pAudioDeviceManager)
|
|
pAudioDeviceManager->Release();
|
|
if (pAudioHookManager)
|
|
{
|
|
pAudioHookManager->RemoveHook(pFinalAudioHook);
|
|
pFinalAudioHook->Release();
|
|
pAudioHookManager->Release();
|
|
}
|
|
if (pEngineContext)
|
|
pEngineContext->Release();
|
|
|
|
fpCloseEngine = (FPRMCLOSEENGINE) dlsym(core_handle, "CloseEngine");
|
|
if (fpCloseEngine && pEngine)
|
|
{
|
|
fpCloseEngine(pEngine);
|
|
pEngine = NULL;
|
|
}
|
|
|
|
dlclose(core_handle);
|
|
|
|
if (m_pszUsername)
|
|
{
|
|
delete [] m_pszUsername;
|
|
}
|
|
if (m_pszPassword)
|
|
{
|
|
delete [] m_pszPassword;
|
|
}
|
|
if (m_pszGUIDFile)
|
|
{
|
|
delete [] m_pszGUIDFile;
|
|
}
|
|
if (m_pszGUIDList)
|
|
{
|
|
delete [] m_pszGUIDList;
|
|
}
|
|
|
|
for (i=0; i<m_numPlugins; i++)
|
|
delete m_pluginInfo[i];
|
|
delete [] m_pluginInfo;
|
|
|
|
if (bEnableVerboseMode)
|
|
{
|
|
print2stdout("\nDone.\n");
|
|
}
|
|
|
|
MimeList *ml = mimehead, *mh;
|
|
while (ml)
|
|
{
|
|
mh = ml->fwd;
|
|
delete ml;
|
|
ml = mh;
|
|
}
|
|
|
|
closeAudioDevice();
|
|
|
|
theErr = HXR_FAILED;
|
|
pErrorSink = NULL;
|
|
pErrorSinkControl = NULL;
|
|
pPluginE = 0;
|
|
pPlugin2Handler = 0;
|
|
ppctrl = NULL;
|
|
bURLFound = false;
|
|
nNumPlayers = 0;
|
|
nNumPlayRepeats = 1;
|
|
nTimeDelta = DEFAULT_TIME_DELTA;
|
|
nStopTime = DEFAULT_STOP_TIME;
|
|
bStopTime = true;
|
|
bStopping = false;
|
|
nPlay = 0;
|
|
bEnableAdviceSink = false;
|
|
bEnableVerboseMode = false;
|
|
pEngine = NULL;
|
|
m_pszUsername = NULL;
|
|
m_pszPassword = NULL;
|
|
m_pszGUIDFile = NULL;
|
|
m_pszGUIDList = NULL;
|
|
m_Error = 0;
|
|
m_ulNumSecondsPlayed = 0;
|
|
mimehead = 0;
|
|
m_preamp = 0;
|
|
}
|
|
|
|
|
|
void HelixSimplePlayer::setOutputSink( HelixSimplePlayer::AUDIOAPI out )
|
|
{
|
|
#ifdef USE_HELIX_ALSA
|
|
m_outputsink = out;
|
|
#else
|
|
m_outputsink = OSS;
|
|
#endif
|
|
}
|
|
|
|
HelixSimplePlayer::AUDIOAPI HelixSimplePlayer::getOutputSink()
|
|
{
|
|
return m_outputsink;
|
|
}
|
|
|
|
void HelixSimplePlayer::setDevice( const char *dev )
|
|
{
|
|
delete [] m_device;
|
|
|
|
int len = strlen(dev);
|
|
m_device = new char [len + 1];
|
|
strcpy(m_device, dev);
|
|
}
|
|
|
|
const char *HelixSimplePlayer::getDevice()
|
|
{
|
|
return m_device;
|
|
}
|
|
|
|
|
|
#define MAX_DEV_NAME 255
|
|
#define HX_VOLUME SOUND_MIXER_PCM
|
|
void HelixSimplePlayer::openAudioDevice()
|
|
{
|
|
switch (m_direct)
|
|
{
|
|
case OSS:
|
|
{
|
|
//Check the environmental variable to let user overide default device.
|
|
char *pszOverrideName = getenv( "AUDIO" ); /* Flawfinder: ignore */
|
|
char szDevName[MAX_DEV_NAME]; /* Flawfinder: ignore */
|
|
|
|
// Use defaults if no environment variable is set.
|
|
if ( pszOverrideName && strlen(pszOverrideName)>0 )
|
|
{
|
|
SafeStrCpy( szDevName, pszOverrideName, MAX_DEV_NAME );
|
|
}
|
|
else
|
|
{
|
|
SafeStrCpy( szDevName, "/dev/mixer", MAX_DEV_NAME );
|
|
}
|
|
|
|
// Open the audio device if it isn't already open
|
|
if ( m_nDevID < 0 )
|
|
{
|
|
m_nDevID = ::open( szDevName, O_WRONLY );
|
|
}
|
|
|
|
if ( m_nDevID < 0 )
|
|
{
|
|
print2stderr("Failed to open audio(%s)!!!!!!! Code is: %d errno: %d\n",
|
|
szDevName, m_nDevID, errno );
|
|
|
|
//Error opening device.
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ALSA:
|
|
{
|
|
#ifdef USE_HELIX_ALSA
|
|
int err;
|
|
|
|
print2stderr("Opening ALSA mixer device PCM\n");
|
|
|
|
err = snd_mixer_open(&m_pAlsaMixerHandle, 0);
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_open: %s\n", snd_strerror (err));
|
|
|
|
if (err == 0)
|
|
{
|
|
err = snd_mixer_attach(m_pAlsaMixerHandle, m_alsaDevice);
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_attach: %s\n", snd_strerror (err));
|
|
}
|
|
|
|
if (err == 0)
|
|
{
|
|
err = snd_mixer_selem_register(m_pAlsaMixerHandle, NULL, NULL);
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_selem_register: %s\n", snd_strerror (err));
|
|
}
|
|
|
|
if (err == 0)
|
|
{
|
|
err = snd_mixer_load(m_pAlsaMixerHandle);
|
|
if(err < 0 )
|
|
print2stderr("snd_mixer_load: %s\n", snd_strerror (err));
|
|
}
|
|
|
|
if (err == 0)
|
|
{
|
|
/* Find the mixer element */
|
|
snd_mixer_elem_t* elem = snd_mixer_first_elem(m_pAlsaMixerHandle);
|
|
snd_mixer_elem_type_t type;
|
|
const char* elem_name = NULL;
|
|
snd_mixer_selem_id_t *sid = NULL;
|
|
|
|
snd_mixer_selem_id_alloca(&sid);
|
|
|
|
while (elem)
|
|
{
|
|
type = snd_mixer_elem_get_type(elem);
|
|
if (type == SND_MIXER_ELEM_SIMPLE)
|
|
{
|
|
snd_mixer_selem_get_id(elem, sid);
|
|
|
|
/* We're only interested in playback volume controls */
|
|
if(snd_mixer_selem_has_playback_volume(elem) && !snd_mixer_selem_has_common_volume(elem) )
|
|
{
|
|
elem_name = snd_mixer_selem_id_get_name(sid);
|
|
if (!m_pAlsaPCMMixerElem && strcmp(elem_name, "Master") == 0)
|
|
m_pAlsaMasterMixerElem = elem;
|
|
|
|
if (!m_pAlsaPCMMixerElem && strcmp(elem_name, "PCM") == 0)
|
|
m_pAlsaPCMMixerElem = elem;
|
|
|
|
if (m_pAlsaMasterMixerElem && m_pAlsaPCMMixerElem)
|
|
break;
|
|
}
|
|
}
|
|
elem = snd_mixer_elem_next(elem);
|
|
}
|
|
|
|
if (!elem)
|
|
{
|
|
print2stderr("Could not find a usable mixer element\n");
|
|
err = -1;
|
|
}
|
|
}
|
|
|
|
|
|
if (err != 0)
|
|
{
|
|
if(m_pAlsaMixerHandle)
|
|
{
|
|
snd_mixer_close(m_pAlsaMixerHandle);
|
|
m_pAlsaMixerHandle = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
default:
|
|
print2stderr("Unknown audio interface in openAudioDevice()\n");
|
|
}
|
|
}
|
|
|
|
void HelixSimplePlayer::closeAudioDevice()
|
|
{
|
|
switch (m_direct)
|
|
{
|
|
case OSS:
|
|
{
|
|
if( m_nDevID >= 0 )
|
|
{
|
|
::close( m_nDevID );
|
|
m_nDevID = -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ALSA:
|
|
{
|
|
#ifdef USE_HELIX_ALSA
|
|
int err = 0;
|
|
|
|
if (m_pAlsaMixerHandle && m_pAlsaMasterMixerElem)
|
|
{
|
|
err = snd_mixer_detach(m_pAlsaMixerHandle, "Master");
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_detach: %s\n", snd_strerror(err));
|
|
}
|
|
|
|
if (m_pAlsaMixerHandle && m_pAlsaPCMMixerElem)
|
|
{
|
|
err = snd_mixer_detach(m_pAlsaMixerHandle, "PCM");
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_detach: %s\n", snd_strerror(err));
|
|
}
|
|
|
|
|
|
if (m_pAlsaMixerHandle)
|
|
{
|
|
if(err == 0)
|
|
{
|
|
err = snd_mixer_close(m_pAlsaMixerHandle);
|
|
if(err < 0)
|
|
print2stderr("snd_mixer_close: %s\n", snd_strerror (err));
|
|
}
|
|
|
|
if(err == 0)
|
|
{
|
|
m_pAlsaMixerHandle = NULL;
|
|
m_pAlsaPCMMixerElem = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
default:
|
|
print2stderr("Unknown audio interface in closeAudioDevice()\n");
|
|
}
|
|
}
|
|
|
|
// it seems the master volume only gets reset on track change when using ALSA
|
|
// sheez, I thought Amarok wasnt supposed to be a mixer??
|
|
// all this code is so that you can actually *use* a mixer and have it work
|
|
// the way you expect...
|
|
#ifdef USE_HELIX_ALSA
|
|
int HelixSimplePlayer::getDirectMasterVolume()
|
|
{
|
|
int nRetVolume = 0;
|
|
|
|
switch (m_direct)
|
|
{
|
|
case ALSA:
|
|
{
|
|
if (!m_pAlsaMasterMixerElem)
|
|
return nRetVolume;
|
|
|
|
snd_mixer_elem_type_t type;
|
|
int err = 0;
|
|
type = snd_mixer_elem_get_type(m_pAlsaMasterMixerElem);
|
|
|
|
if (type == SND_MIXER_ELEM_SIMPLE)
|
|
{
|
|
long volumeL, volumeR, min_volume, max_volume;
|
|
|
|
if(snd_mixer_selem_has_playback_volume(m_pAlsaMasterMixerElem) ||
|
|
snd_mixer_selem_has_playback_volume_joined(m_pAlsaMasterMixerElem))
|
|
{
|
|
err = snd_mixer_selem_get_playback_volume(m_pAlsaMasterMixerElem,
|
|
SND_MIXER_SCHN_FRONT_LEFT,
|
|
&volumeL);
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_selem_get_playback_volume (L): %s\n", snd_strerror (err));
|
|
else
|
|
{
|
|
if ( snd_mixer_selem_is_playback_mono ( m_pAlsaMasterMixerElem ))
|
|
volumeR = volumeL;
|
|
else
|
|
{
|
|
err = snd_mixer_selem_get_playback_volume(m_pAlsaMasterMixerElem,
|
|
SND_MIXER_SCHN_FRONT_RIGHT,
|
|
&volumeR);
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_selem_get_playback_volume (R): %s\n", snd_strerror (err));
|
|
}
|
|
}
|
|
|
|
if (err == 0)
|
|
{
|
|
snd_mixer_selem_get_playback_volume_range(m_pAlsaMasterMixerElem,
|
|
&min_volume,
|
|
&max_volume);
|
|
|
|
if(max_volume > min_volume)
|
|
nRetVolume = (UINT16) (0.5 + (100.0 * (double)(volumeL + volumeR) / (2.0 * (max_volume - min_volume))));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
print2stderr("Unknown audio interface in getDirectMasterVolume()\n");
|
|
}
|
|
|
|
return nRetVolume;
|
|
}
|
|
|
|
void HelixSimplePlayer::setDirectMasterVolume(int vol)
|
|
{
|
|
switch (m_direct)
|
|
{
|
|
case ALSA:
|
|
{
|
|
if (!m_pAlsaMasterMixerElem)
|
|
return;
|
|
|
|
snd_mixer_elem_type_t type;
|
|
int err = 0;
|
|
type = snd_mixer_elem_get_type(m_pAlsaMasterMixerElem);
|
|
|
|
|
|
if (type == SND_MIXER_ELEM_SIMPLE)
|
|
{
|
|
long volume, min_volume, max_volume, range;
|
|
|
|
if(snd_mixer_selem_has_playback_volume(m_pAlsaMasterMixerElem) ||
|
|
snd_mixer_selem_has_playback_volume_joined(m_pAlsaMasterMixerElem))
|
|
{
|
|
snd_mixer_selem_get_playback_volume_range(m_pAlsaMasterMixerElem,
|
|
&min_volume,
|
|
&max_volume);
|
|
|
|
range = max_volume - min_volume;
|
|
volume = (long) (((double)vol / 100) * range + min_volume);
|
|
|
|
|
|
err = snd_mixer_selem_set_playback_volume( m_pAlsaMasterMixerElem,
|
|
SND_MIXER_SCHN_FRONT_LEFT,
|
|
volume);
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_selem_set_playback_volume: %s\n", snd_strerror (err));
|
|
|
|
if (!snd_mixer_selem_is_playback_mono (m_pAlsaMasterMixerElem))
|
|
{
|
|
/* Set the right channel too */
|
|
err = snd_mixer_selem_set_playback_volume( m_pAlsaMasterMixerElem,
|
|
SND_MIXER_SCHN_FRONT_RIGHT,
|
|
volume);
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_selem_set_playback_volume: %s\n", snd_strerror (err));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
print2stderr("Unknown audio interface in setDirectMasterVolume()\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int HelixSimplePlayer::getDirectPCMVolume()
|
|
{
|
|
int nRetVolume = 0;
|
|
|
|
switch (m_direct)
|
|
{
|
|
case OSS:
|
|
{
|
|
int nVolume = 0;
|
|
int nLeftVolume = 0;
|
|
int nRightVolume = 0;
|
|
|
|
if (m_nDevID < 0 || (::ioctl( m_nDevID, MIXER_READ(HX_VOLUME), &nVolume) < 0))
|
|
{
|
|
print2stderr("ioctl fails when reading HW volume: mnDevID=%d, errno=%d\n", m_nDevID, errno);
|
|
nRetVolume = 50; // sensible default
|
|
}
|
|
else
|
|
{
|
|
nLeftVolume = (nVolume & 0x000000ff);
|
|
nRightVolume = (nVolume & 0x0000ff00) >> 8;
|
|
|
|
//Which one to use? Average them?
|
|
nRetVolume = nLeftVolume ;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ALSA:
|
|
{
|
|
#ifdef USE_HELIX_ALSA
|
|
if (!m_pAlsaPCMMixerElem)
|
|
return nRetVolume;
|
|
|
|
snd_mixer_elem_type_t type;
|
|
int err = 0;
|
|
type = snd_mixer_elem_get_type(m_pAlsaPCMMixerElem);
|
|
|
|
if (type == SND_MIXER_ELEM_SIMPLE)
|
|
{
|
|
long volumeL, volumeR, min_volume, max_volume;
|
|
|
|
if(snd_mixer_selem_has_playback_volume(m_pAlsaPCMMixerElem) ||
|
|
snd_mixer_selem_has_playback_volume_joined(m_pAlsaPCMMixerElem))
|
|
{
|
|
err = snd_mixer_selem_get_playback_volume(m_pAlsaPCMMixerElem,
|
|
SND_MIXER_SCHN_FRONT_LEFT,
|
|
&volumeL);
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_selem_get_playback_volume (L): %s\n", snd_strerror (err));
|
|
else
|
|
{
|
|
if ( snd_mixer_selem_is_playback_mono ( m_pAlsaPCMMixerElem ))
|
|
volumeR = volumeL;
|
|
else
|
|
{
|
|
err = snd_mixer_selem_get_playback_volume(m_pAlsaPCMMixerElem,
|
|
SND_MIXER_SCHN_FRONT_RIGHT,
|
|
&volumeR);
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_selem_get_playback_volume (R): %s\n", snd_strerror (err));
|
|
}
|
|
}
|
|
|
|
if (err == 0)
|
|
{
|
|
snd_mixer_selem_get_playback_volume_range(m_pAlsaPCMMixerElem,
|
|
&min_volume,
|
|
&max_volume);
|
|
|
|
if(max_volume > min_volume)
|
|
nRetVolume = (UINT16) (0.5 + (100.0 * (double)(volumeL + volumeR) / (2.0 * (max_volume - min_volume))));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
default:
|
|
print2stderr("Unknown audio interface in getDirectPCMVolume()\n");
|
|
}
|
|
|
|
return nRetVolume;
|
|
}
|
|
|
|
void HelixSimplePlayer::setDirectPCMVolume(int vol)
|
|
{
|
|
switch (m_direct)
|
|
{
|
|
case OSS:
|
|
{
|
|
int nNewVolume=0;
|
|
|
|
//Set both left and right volumes.
|
|
nNewVolume = (vol & 0xff) | ((vol & 0xff) << 8);
|
|
|
|
if (::ioctl( m_nDevID, MIXER_WRITE(HX_VOLUME), &nNewVolume) < 0)
|
|
print2stderr("Unable to set direct HW volume\n");
|
|
}
|
|
break;
|
|
|
|
case ALSA:
|
|
{
|
|
#ifdef USE_HELIX_ALSA
|
|
if (!m_pAlsaPCMMixerElem)
|
|
return;
|
|
|
|
snd_mixer_elem_type_t type;
|
|
int err = 0;
|
|
type = snd_mixer_elem_get_type(m_pAlsaPCMMixerElem);
|
|
|
|
|
|
if (type == SND_MIXER_ELEM_SIMPLE)
|
|
{
|
|
long volume, min_volume, max_volume, range;
|
|
|
|
if(snd_mixer_selem_has_playback_volume(m_pAlsaPCMMixerElem) ||
|
|
snd_mixer_selem_has_playback_volume_joined(m_pAlsaPCMMixerElem))
|
|
{
|
|
snd_mixer_selem_get_playback_volume_range(m_pAlsaPCMMixerElem,
|
|
&min_volume,
|
|
&max_volume);
|
|
|
|
range = max_volume - min_volume;
|
|
volume = (long) (((double)vol / 100) * range + min_volume);
|
|
|
|
|
|
err = snd_mixer_selem_set_playback_volume( m_pAlsaPCMMixerElem,
|
|
SND_MIXER_SCHN_FRONT_LEFT,
|
|
volume);
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_selem_set_playback_volume: %s\n", snd_strerror (err));
|
|
|
|
if (!snd_mixer_selem_is_playback_mono (m_pAlsaPCMMixerElem))
|
|
{
|
|
/* Set the right channel too */
|
|
err = snd_mixer_selem_set_playback_volume( m_pAlsaPCMMixerElem,
|
|
SND_MIXER_SCHN_FRONT_RIGHT,
|
|
volume);
|
|
if (err < 0)
|
|
print2stderr("snd_mixer_selem_set_playback_volume: %s\n", snd_strerror (err));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
default:
|
|
print2stderr("Unknown audio interface in setDirectPCMVolume()\n");
|
|
}
|
|
}
|
|
|
|
|
|
int HelixSimplePlayer::setURL(const char *file, int playerIndex, bool islocal)
|
|
{
|
|
if (playerIndex == ALL_PLAYERS)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<nNumPlayers; i++)
|
|
setURL(file, i);
|
|
}
|
|
else
|
|
{
|
|
int len = strlen(file);
|
|
if (len >= MAXPATHLEN)
|
|
return -1;;
|
|
|
|
print2stderr("SETURL MASTER VOL: %d\n",getDirectMasterVolume());
|
|
|
|
if (ppctrl[playerIndex]->pszURL)
|
|
delete [] ppctrl[playerIndex]->pszURL;
|
|
|
|
// see if the file is already in the form of a url
|
|
char *tmp = strstr(file, "://");
|
|
if (!tmp)
|
|
{
|
|
char pszURLOrig[MAXPATHLEN];
|
|
const char* pszAddOn;
|
|
|
|
strcpy(pszURLOrig, file);
|
|
RemoveWrappingQuotes(pszURLOrig);
|
|
pszAddOn = "file://";
|
|
|
|
ppctrl[playerIndex]->pszURL = new char[strlen(pszURLOrig)+strlen(pszAddOn)+1];
|
|
if ( (len + strlen(pszAddOn)) < MAXPATHLEN )
|
|
{
|
|
sprintf( ppctrl[playerIndex]->pszURL, "%s%s", pszAddOn, pszURLOrig );
|
|
islocal = true; // diesnt matter what we were told...
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
ppctrl[playerIndex]->pszURL = new char[len + 1];
|
|
if (ppctrl[playerIndex]->pszURL)
|
|
strcpy(ppctrl[playerIndex]->pszURL, file);
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
ppctrl[playerIndex]->isLocal = islocal;
|
|
|
|
print2stderr("opening %s on player %d, src cnt %d\n",
|
|
ppctrl[playerIndex]->pszURL, playerIndex, ppctrl[playerIndex]->pPlayer->GetSourceCount());
|
|
|
|
#ifdef __NOCROSSFADER__
|
|
|
|
if (HXR_OK == ppctrl[playerIndex]->pPlayer->OpenURL(ppctrl[playerIndex]->pszURL))
|
|
{
|
|
print2stderr("opened player on %d src cnt %d\n", playerIndex, ppctrl[playerIndex]->pPlayer->GetSourceCount());
|
|
m_urlchanged = true;
|
|
}
|
|
#else
|
|
/* try OpenRequest instead... */
|
|
IHXRequest *ireq = 0;
|
|
pthread_mutex_lock(&m_engine_m);
|
|
pCommonClassFactory->CreateInstance(CLSID_IHXRequest, (void **)&ireq);
|
|
if (ireq)
|
|
{
|
|
//print2stderr("GOT THE IHXRequest Interface!!\n");
|
|
ireq->SetURL(ppctrl[playerIndex]->pszURL);
|
|
ppctrl[playerIndex]->pPlayer2->OpenRequest(ireq);
|
|
m_urlchanged = true;
|
|
ireq->Release();
|
|
}
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int HelixSimplePlayer::numPlugins() const
|
|
{
|
|
if (pPluginE)
|
|
{
|
|
return ((int)pPluginE->GetNumOfPlugins());
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int HelixSimplePlayer::getPluginInfo(int index,
|
|
const char *&description,
|
|
const char *©right,
|
|
const char *&moreinfourl) const
|
|
{
|
|
if (index < m_numPlugins)
|
|
{
|
|
description = m_pluginInfo[index]->description;
|
|
copyright = m_pluginInfo[index]->copyright;
|
|
moreinfourl = m_pluginInfo[index]->moreinfourl;
|
|
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
void HelixSimplePlayer::play(const char *file, int playerIndex, bool fadein, bool fadeout, unsigned long fadetime)
|
|
{
|
|
if (!setURL(file, playerIndex))
|
|
play(playerIndex, fadein, fadeout, fadetime);
|
|
}
|
|
|
|
void HelixSimplePlayer::play(int playerIndex, bool fadein, bool fadeout, unsigned long fadetime)
|
|
{
|
|
int i;
|
|
int firstPlayer = playerIndex == ALL_PLAYERS ? 0 : playerIndex;
|
|
int lastPlayer = playerIndex == ALL_PLAYERS ? nNumPlayers : playerIndex + 1;
|
|
|
|
nPlay = 0;
|
|
nNumPlayRepeats=1;
|
|
while(nPlay < nNumPlayRepeats)
|
|
{
|
|
nPlay++;
|
|
if (bEnableVerboseMode)
|
|
{
|
|
print2stdout("Starting play #%d...\n", nPlay);
|
|
}
|
|
//print2stderr("firstplayer = %d lastplayer=%d\n",firstPlayer,lastPlayer);
|
|
|
|
UINT32 starttime=0, endtime=0, now=0;
|
|
for (i = firstPlayer; i < lastPlayer; i++)
|
|
{
|
|
// start is already protected...
|
|
start(i, fadein, fadetime);
|
|
|
|
starttime = GetTime();
|
|
endtime = starttime + nTimeDelta;
|
|
while (1)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
DoEvents(nTimeDelta);
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
now = GetTime();
|
|
if (now >= endtime)
|
|
break;
|
|
if (fadeout && !ppctrl[i]->bFadeOut && now > endtime - fadetime)
|
|
{
|
|
ppctrl[i]->bFadeOut = true;
|
|
((HSPPreMixAudioHook *)ppctrl[i]->pPreMixHook)->setFadelength(fadetime);
|
|
((HSPPreMixAudioHook *)ppctrl[i]->pPreMixHook)->setFadeout(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
starttime = GetTime();
|
|
if (nStopTime == -1)
|
|
{
|
|
bStopTime = false;
|
|
}
|
|
else
|
|
{
|
|
endtime = starttime + nStopTime;
|
|
}
|
|
bStopping = false;
|
|
// Handle events coming from all of the players
|
|
while (!done(playerIndex))
|
|
{
|
|
now = GetTime();
|
|
if (!bStopping && bStopTime && now >= endtime)
|
|
{
|
|
// Stop all of the players, as they should all be done now
|
|
if (bEnableVerboseMode)
|
|
{
|
|
print2stdout("\nEnd (Stop) time reached. Stopping...\n");
|
|
}
|
|
stop(playerIndex);
|
|
bStopping = true;
|
|
}
|
|
pthread_mutex_lock(&m_engine_m);
|
|
DoEvent();
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
}
|
|
|
|
// Stop all of the players, as they should all be done now
|
|
if (bEnableVerboseMode)
|
|
{
|
|
print2stdout("\nPlayback complete. Stopping all players...\n");
|
|
}
|
|
stop(playerIndex);
|
|
|
|
// repeat until nNumRepeats
|
|
}
|
|
}
|
|
|
|
void HelixSimplePlayer::start(int playerIndex, bool fadein, unsigned long fadetime)
|
|
{
|
|
if (playerIndex == ALL_PLAYERS)
|
|
{
|
|
int i;
|
|
for (i=0; i<nNumPlayers; i++)
|
|
start(i, fadein, fadetime);
|
|
}
|
|
else
|
|
{
|
|
if (!ppctrl[playerIndex]->pszURL)
|
|
return;
|
|
|
|
print2stderr("START MASTER VOL: %d\n",getDirectMasterVolume());
|
|
|
|
if (bEnableVerboseMode)
|
|
{
|
|
print2stdout("Starting player %d...\n", playerIndex);
|
|
}
|
|
|
|
ppctrl[playerIndex]->bFadeIn = fadein;
|
|
ppctrl[playerIndex]->bFadeOut = false; // assume we'll only fade out if we have another track
|
|
ppctrl[playerIndex]->ulFadeLength = fadetime;
|
|
if (!ppctrl[playerIndex]->bPlaying)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
ppctrl[playerIndex]->pPlayer->Begin();
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
|
|
ppctrl[playerIndex]->bPlaying = true;
|
|
ppctrl[playerIndex]->bStarting = true;
|
|
//print2stderr("Begin player %d\n", playerIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void HelixSimplePlayer::start(const char *file, int playerIndex, bool fadein, unsigned long fadetime)
|
|
{
|
|
setURL(file, playerIndex);
|
|
start(playerIndex, fadein, fadetime);
|
|
}
|
|
|
|
|
|
|
|
bool HelixSimplePlayer::done(int playerIndex)
|
|
{
|
|
BOOL bAllDone = true;
|
|
|
|
if (playerIndex == ALL_PLAYERS)
|
|
// Start checking at the end of the array since those players
|
|
// were started last and are therefore more likely to not be
|
|
// finished yet.
|
|
for (int i = nNumPlayers - 1; i >= 0 && bAllDone; i--)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
if (ppctrl[i]->bStarting || !ppctrl[i]->pPlayer->IsDone())
|
|
ppctrl[i]->bPlaying = (bAllDone = false);
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
}
|
|
else
|
|
{
|
|
if (playerIndex < nNumPlayers)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
if ((bAllDone = (!ppctrl[playerIndex]->bStarting && ppctrl[playerIndex]->pPlayer->IsDone())))
|
|
ppctrl[playerIndex]->bPlaying = false;
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
}
|
|
}
|
|
|
|
return bAllDone;
|
|
}
|
|
|
|
void HelixSimplePlayer::stop(int playerIndex)
|
|
{
|
|
if (playerIndex == ALL_PLAYERS)
|
|
for (int i = 0; i < nNumPlayers; i++)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
ppctrl[i]->pPlayer->Stop();
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
|
|
ppctrl[i]->bPlaying = false;
|
|
ppctrl[i]->bStarting = false;
|
|
ppctrl[i]->isLocal = false;
|
|
}
|
|
else
|
|
{
|
|
if (playerIndex < nNumPlayers)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
ppctrl[playerIndex]->pPlayer->Stop();
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
ppctrl[playerIndex]->bPlaying = false;
|
|
ppctrl[playerIndex]->bStarting = false;
|
|
ppctrl[playerIndex]->isLocal = false;
|
|
memset(&ppctrl[playerIndex]->md, 0, sizeof(ppctrl[playerIndex]->md));
|
|
}
|
|
}
|
|
}
|
|
|
|
HelixSimplePlayer::metaData *HelixSimplePlayer::getMetaData(int playerIndex)
|
|
{
|
|
return &ppctrl[playerIndex]->md;
|
|
}
|
|
|
|
|
|
void HelixSimplePlayer::dispatch()
|
|
{
|
|
struct _HXxEvent *pNothing = 0x0;
|
|
struct timeval tv;
|
|
int volAfter = 0;
|
|
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = SLEEP_TIME*1000;
|
|
|
|
if (m_urlchanged)
|
|
{
|
|
#ifdef USE_HELIX_ALSA
|
|
m_MvolBefore = getDirectMasterVolume();
|
|
print2stderr("Master Volume is: %d\n", m_MvolBefore);
|
|
#endif
|
|
m_volBefore = getDirectPCMVolume();
|
|
m_urlchanged = false;
|
|
print2stderr("Volume is: %d\n", m_volBefore);
|
|
}
|
|
pEngine->EventOccurred(pNothing);
|
|
#ifdef USE_HELIX_ALSA
|
|
if (m_MvolBefore > 0 && m_MvolAtStart != m_MvolBefore && (volAfter = getDirectMasterVolume()) != m_MvolBefore)
|
|
{
|
|
print2stderr("RESETTING MASTER VOLUME TO: %d\n", m_MvolBefore);
|
|
setDirectMasterVolume(m_volBefore);
|
|
print2stderr("Now Master Volume is %d\n", getDirectMasterVolume());
|
|
m_MvolBefore = -1;
|
|
}
|
|
#endif
|
|
if (m_volBefore > 0 && m_volAtStart != m_volBefore && (volAfter = getDirectPCMVolume()) != m_volBefore)
|
|
{
|
|
print2stderr("RESETTING VOLUME TO: %d\n", m_volBefore);
|
|
setDirectPCMVolume(m_volBefore);
|
|
print2stderr("Now Volume is %d\n", getDirectPCMVolume());
|
|
m_volBefore = -1;
|
|
}
|
|
}
|
|
|
|
|
|
bool HelixSimplePlayer::isPlaying(int playerIndex) const
|
|
{
|
|
if (playerIndex < nNumPlayers)
|
|
return ppctrl[playerIndex]->bPlaying;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool HelixSimplePlayer::isLocal(int playerIndex) const
|
|
{
|
|
if (playerIndex < nNumPlayers)
|
|
return (ppctrl[playerIndex]->isLocal && duration(playerIndex));
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void HelixSimplePlayer::pause(int playerIndex)
|
|
{
|
|
int i;
|
|
|
|
if (playerIndex == ALL_PLAYERS)
|
|
for (i=0; i<nNumPlayers; i++)
|
|
pause(i);
|
|
else
|
|
if (playerIndex < nNumPlayers)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
ppctrl[playerIndex]->pPlayer->Pause();
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
ppctrl[playerIndex]->bPlaying = false;
|
|
}
|
|
}
|
|
|
|
void HelixSimplePlayer::resume(int playerIndex)
|
|
{
|
|
int i;
|
|
|
|
if (playerIndex == ALL_PLAYERS)
|
|
for (i=0; i<nNumPlayers; i++)
|
|
resume(i);
|
|
else
|
|
if (playerIndex < nNumPlayers)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
ppctrl[playerIndex]->pPlayer->Begin();
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
ppctrl[playerIndex]->bPlaying = true;
|
|
}
|
|
}
|
|
|
|
|
|
void HelixSimplePlayer::seek(unsigned long pos, int playerIndex)
|
|
{
|
|
int i;
|
|
|
|
if (playerIndex == ALL_PLAYERS)
|
|
for (i=0; i<nNumPlayers; i++)
|
|
seek(pos, i);
|
|
else
|
|
if (playerIndex < nNumPlayers)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
ppctrl[playerIndex]->pPlayer->Seek(pos);
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
}
|
|
}
|
|
|
|
unsigned long HelixSimplePlayer::where(int playerIndex) const
|
|
{
|
|
if (playerIndex < nNumPlayers && ppctrl[playerIndex]->pHSPContext)
|
|
//return ppctrl[playerIndex]->pHSPContext->position();
|
|
return ppctrl[playerIndex]->pPlayer->GetCurrentPlayTime();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
unsigned long HelixSimplePlayer::duration(int playerIndex) const
|
|
{
|
|
if (playerIndex < nNumPlayers && ppctrl[playerIndex]->pHSPContext)
|
|
return ppctrl[playerIndex]->pHSPContext->duration();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
unsigned long HelixSimplePlayer::getVolume(int playerIndex)
|
|
{
|
|
unsigned long vol;
|
|
|
|
if (playerIndex < nNumPlayers)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
vol = ppctrl[playerIndex]->volume;
|
|
//if (ppctrl[playerIndex]->pVolume)
|
|
//pVolume->GetVolume();
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
|
|
return (vol);
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void HelixSimplePlayer::setVolume(unsigned long vol, int playerIndex)
|
|
{
|
|
int i;
|
|
|
|
if (playerIndex == ALL_PLAYERS)
|
|
{
|
|
for (i=0; i<nNumPlayers; i++)
|
|
setVolume(vol, i);
|
|
}
|
|
else
|
|
if (playerIndex < nNumPlayers)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
#ifndef HELIX_SW_VOLUME_INTERFACE
|
|
ppctrl[playerIndex]->volume = vol;
|
|
((HSPFinalAudioHook *)pFinalAudioHook)->setGain(vol);
|
|
#else
|
|
ppctrl[playerIndex]->pVolume->SetVolume(vol);
|
|
#endif
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
}
|
|
}
|
|
|
|
void HelixSimplePlayer::setMute(bool mute, int playerIndex)
|
|
{
|
|
int i;
|
|
|
|
if (playerIndex == ALL_PLAYERS)
|
|
{
|
|
for (i=0; i<nNumPlayers; i++)
|
|
setMute(mute, i);
|
|
}
|
|
else
|
|
if (playerIndex < nNumPlayers)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
ppctrl[playerIndex]->pVolume->SetMute(mute);
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
}
|
|
}
|
|
|
|
|
|
bool HelixSimplePlayer::getMute(int playerIndex)
|
|
{
|
|
bool ismute;
|
|
|
|
if (playerIndex < nNumPlayers)
|
|
{
|
|
pthread_mutex_lock(&m_engine_m);
|
|
ismute = ppctrl[playerIndex]->ismute;
|
|
//pVolume->GetMute();
|
|
pthread_mutex_unlock(&m_engine_m);
|
|
|
|
return ismute;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool HelixSimplePlayer::ReadGUIDFile()
|
|
{
|
|
BOOL bSuccess = false;
|
|
FILE* pFile = NULL;
|
|
int nNumRead = 0;
|
|
int readSize = 10000;
|
|
char* pszBuffer = new char[readSize];
|
|
|
|
if (m_pszGUIDFile)
|
|
{
|
|
if((pFile = fopen(m_pszGUIDFile, "r")) != NULL)
|
|
{
|
|
// Read in the entire file
|
|
nNumRead = fread(pszBuffer, sizeof(char), readSize, pFile);
|
|
pszBuffer[nNumRead] = '\0';
|
|
|
|
// Store it for later parsing
|
|
m_pszGUIDList = new char[nNumRead + 1];
|
|
strcpy(m_pszGUIDList, pszBuffer); /* Flawfinder: ignore */
|
|
|
|
fclose(pFile);
|
|
pFile = NULL;
|
|
|
|
if (nNumRead > 0)
|
|
{
|
|
bSuccess = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
delete [] pszBuffer;
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
|
|
void HelixSimplePlayer::addScopeBuf(struct DelayQueue *item, int playerIndex)
|
|
{
|
|
if (playerIndex >=0 && playerIndex < nNumPlayers)
|
|
{
|
|
pthread_mutex_lock(&ppctrl[playerIndex]->m_scope_m);
|
|
|
|
if (ppctrl[playerIndex]->scopebuftail)
|
|
{
|
|
item->fwd = 0;
|
|
ppctrl[playerIndex]->scopebuftail->fwd = item;
|
|
ppctrl[playerIndex]->scopebuftail = item;
|
|
ppctrl[playerIndex]->scopecount++;
|
|
}
|
|
else
|
|
{
|
|
item->fwd = 0;
|
|
ppctrl[playerIndex]->scopebufhead = item;
|
|
ppctrl[playerIndex]->scopebuftail = item;
|
|
ppctrl[playerIndex]->scopecount = 1;
|
|
}
|
|
pthread_mutex_unlock(&ppctrl[playerIndex]->m_scope_m);
|
|
}
|
|
}
|
|
|
|
struct DelayQueue *HelixSimplePlayer::getScopeBuf(int playerIndex)
|
|
{
|
|
if (playerIndex >=0 && playerIndex < nNumPlayers)
|
|
{
|
|
pthread_mutex_lock(&ppctrl[playerIndex]->m_scope_m);
|
|
|
|
struct DelayQueue *item = ppctrl[playerIndex]->scopebufhead;
|
|
|
|
if (item)
|
|
{
|
|
ppctrl[playerIndex]->scopebufhead = item->fwd;
|
|
ppctrl[playerIndex]->scopecount--;
|
|
if (!ppctrl[playerIndex]->scopebufhead)
|
|
ppctrl[playerIndex]->scopebuftail = 0;
|
|
}
|
|
|
|
pthread_mutex_unlock(&ppctrl[playerIndex]->m_scope_m);
|
|
|
|
return item;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int HelixSimplePlayer::peekScopeTime(unsigned long &t, int playerIndex)
|
|
{
|
|
if (playerIndex >=0 && playerIndex < nNumPlayers)
|
|
{
|
|
if (ppctrl[playerIndex]->scopebufhead)
|
|
t = ppctrl[playerIndex]->scopebufhead->time;
|
|
else
|
|
return -1;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void HelixSimplePlayer::clearScopeQ(int playerIndex)
|
|
{
|
|
if (playerIndex < 0)
|
|
{
|
|
for (int i=0; i<nNumPlayers; i++)
|
|
clearScopeQ(i);
|
|
}
|
|
else
|
|
{
|
|
struct DelayQueue *item;
|
|
while ((item = getScopeBuf(playerIndex)))
|
|
delete item;
|
|
}
|
|
}
|
|
|