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.
amarok/amarok/src/engine/helix/helix-sp/helix-sp.cpp

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 *&copyright,
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;
}
}