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-engine.cpp

899 lines
24 KiB

/***************************************************************************
* Copyright (C) 2005 Paul Cifarelli *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <tqthread.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <climits>
#include <cmath>
#include <stdarg.h>
#include <cstring>
#include <config.h>
#include <iostream>
#include "debug.h"
#include <klocale.h>
#include <kmessagebox.h>
#include <tqapplication.h>
#include <tqdir.h>
#include <tqstringlist.h>
#include "helix-engine.h"
#include "helix-configdialog.h"
#include "config/helixconfig.h"
#include "helix-errors.h"
#include "helix-sp.h"
#include "hxplayercontrol.h"
#include "amarokconfig.h"
AMAROK_EXPORT_PLUGIN( HelixEngine )
#define DEBUG_PREFIX "helix-engine"
using namespace std;
extern "C"
{
#include <unistd.h>
}
#define HELIX_ENGINE_TIMER 10 // 10 ms timer
#define SCOPE_MAX_BEHIND 200 // 200 postmix buffers
#ifndef LLONG_MAX
#define LLONG_MAX 9223372036854775807LL
#endif
///returns the configuration we will use
static inline TQCString configPath() { return TQFile::encodeName( TQDir::homeDirPath() + "/.helix/config" ); }
HelixEngine::HelixEngine()
: EngineBase(), PlayerControl(),
m_state(Engine::Empty),
m_coredir(HELIX_LIBS "/common"),
m_pluginsdir(HELIX_LIBS "/plugins"),
m_codecsdir(HELIX_LIBS "/codecs"),
m_inited(false),
m_scopeplayerlast(0),
m_sfps(0.0),
m_scopedelta(0),
m_sframes(0),
m_lframes(0)
{
addPluginProperty( "HasConfigure", "true" );
addPluginProperty( "HasEqualizer", "true" );
addPluginProperty( "HasCrossfade", "true" );
addPluginProperty( "HasCDDA", "false");
memset(&m_md, 0, sizeof(m_md));
memset(hscope, 0, 2*sizeof(HelixScope));
memset(&m_scopetm, 0, sizeof(struct timeval));
memset(m_pfade, 0, 2*sizeof(FadeTrack));
}
HelixEngine::~HelixEngine()
{
m_mimes.clear();
}
int HelixEngine::print2stdout(const char *fmt, ...)
{
va_list args;
char buf[1024];
va_start(args, fmt);
int ret = vsprintf(buf, fmt, args);
debug() << buf;
va_end(args);
return ret;
}
int HelixEngine::print2stderr(const char *fmt, ...)
{
va_list args;
char buf[1024];
va_start(args, fmt);
int ret = vsprintf(buf, fmt, args);
debug() << buf;
va_end(args);
return ret;
}
void HelixEngine::notifyUser(unsigned long code, const char *moreinfo, const char *moreinfourl)
{
TQString *err = HelixErrors::errorText(code);
if (err)
emit statusText(i18n("Helix Core returned error: %1 %2 %3").arg(TQString(*err)).arg(TQString(moreinfo)).arg(TQString(moreinfourl)));
else
emit statusText(i18n("Helix Core returned error: <unknown>"));
}
void HelixEngine::interruptUser(unsigned long code, const char *moreinfo, const char *moreinfourl)
{
TQString *err = HelixErrors::errorText(code);
if (err)
emit infoMessage(i18n("Helix Core returned error: %1 %1 %1").arg(TQString(*err)).arg(TQString(moreinfo)).arg(TQString(moreinfourl)));
else
emit infoMessage(i18n("Helix Core returned error: <unknown>"));
// since this is a serious error, emit trackEnded so amarok knows to move on
play_finished( m_current );
}
void HelixEngine::onContacting(const char *host)
{
emit statusText( i18n("Contacting: %1").arg( TQString(host) ) );
}
void HelixEngine::onBuffering(int pcnt)
{
if (pcnt != 100) // let's not report that...
emit statusText( i18n( "Buffering %1%" ).arg( pcnt ) );
}
Amarok::PluginConfig*
HelixEngine::configure() const
{
debug() << "Starting HelixConfigDialog\n";
return new HelixConfigDialog( (HelixEngine *)this );
}
int HelixEngine::fallbackToOSS()
{
KMessageBox::information( 0, i18n("The helix library you have configured does not support ALSA, the helix-engine has fallen back to OSS") );
debug() << "Falling back to OSS\n";
return (HelixConfigDialog::setSoundSystem( (int) HelixSimplePlayer::OSS ));
}
bool
HelixEngine::init()
{
debug() << "Initializing HelixEngine\n";
struct stat s;
bool exists = false;
stop();
m_state = Engine::Empty;
m_numPlayers = 2;
m_current = 1;
m_coredir = HelixConfig::coreDirectory();
if (m_coredir.isEmpty())
m_coredir = HELIX_LIBS "/common";
m_pluginsdir = HelixConfig::pluginDirectory();
if (m_pluginsdir.isEmpty())
m_pluginsdir = HELIX_LIBS "/plugins";
m_codecsdir = HelixConfig::codecsDirectory();
if (m_codecsdir.isEmpty())
m_codecsdir = HELIX_LIBS "/codecs";
if (HelixConfig::outputplugin() == "oss")
setOutputSink( HelixSimplePlayer::OSS );
else
{
setOutputSink( HelixSimplePlayer::ALSA );
if (HelixConfig::deviceenabled())
setDevice( HelixConfig::device().utf8() );
else
setDevice("default");
}
if (!stat(m_coredir.utf8(), &s) && !stat(m_pluginsdir.utf8(), &s) && !stat(m_codecsdir.utf8(), &s))
{
long vol=0;
bool eqenabled=false;
int savedpreamp=0;
TQValueList<int> savedequalizerGains;
if (m_inited)
{
vol = PlayerControl::getVolume();
eqenabled = PlayerControl::isEQenabled();
for (unsigned int i=0; i < m_equalizerGains.size(); i++)
savedequalizerGains.append(m_equalizerGains[i]);
savedpreamp = m_preamp;
PlayerControl::tearDown();
}
PlayerControl::init(m_coredir.utf8(), m_pluginsdir.utf8(), m_codecsdir.utf8(), 2);
if (PlayerControl::initDirectSS())
{
fallbackToOSS();
PlayerControl::initDirectSS();
}
if (m_inited)
{
PlayerControl::setVolume(vol);
setEqualizerEnabled(eqenabled);
setEqualizerParameters(savedpreamp, savedequalizerGains);
}
m_inited = exists = true;
}
if (!exists || PlayerControl::getError())
{
KMessageBox::error( 0, i18n("The Helix Engine requires the RealPlayer(tm) or HelixPlayer libraries to be installed. Please make sure one is installed, and adjust the paths in \"Amarok Settings\" -> \"Engine\"") );
// we need to return true here so that the user has an oppportunity to change the directory
//return false;
return true;
}
// create a list of mime types and ext for use in canDecode()
m_mimes.resize( getMimeListLen() );
int i = 0;
const MimeList *ml = getMimeList();
MimeEntry *entry;
while (ml)
{
TQString mt = ml->mimetypes;
TQString me = ml->mimeexts;
entry = new MimeEntry;
entry->type = TQStringList::split('|', mt);
entry->ext = TQStringList::split('|', me);
m_mimes[i] = *entry;
debug() << ml->mimetypes << endl;
i++;
ml = ml->fwd;
}
debug() << "Succussful init\n";
return true;
}
bool
HelixEngine::load( const KURL &url, bool isStream )
{
debug() << "In load " << url.url() << endl;
if (!m_inited)
return false;
if (!canDecode(url))
{
const TQString path = url.path();
const TQString ext = path.mid( path.findRev( '.' ) + 1 ).lower();
emit statusText( i18n("No plugin found for the %1 format").arg(ext) );
return false;
}
debug() << "xfadeLength is " << m_xfadeLength << endl;
if( m_xfadeLength > 0 && m_state == Engine::Playing && !isStream &&
( m_xfadeNextTrack || //set by engine controller when switching tracks automatically
(uint) AmarokConfig::crossfadeType() == 0 || //crossfade always
(uint) AmarokConfig::crossfadeType() == 2 ) ) //crossfade when switching tracks manually)
{
//set m_xfadeNextTrack true here regardless to play() will work correctly; disable in there
m_xfadeNextTrack = true;
int nextPlayer = m_current ? 0 : 1;
// prepare the next player
PlayerControl::stop(nextPlayer);
resetScope(nextPlayer);
memset(&hscope[nextPlayer], 0, sizeof(HelixScope));
memset(&m_pfade[nextPlayer], 0, sizeof(FadeTrack));
if (isPlaying(m_current))
{
m_pfade[m_current].m_fadeactive = true;
m_pfade[m_current].m_startfadetime = PlayerControl::where(m_current);
setFadeout(true, m_xfadeLength, m_current);
}
Engine::Base::load( url, false ); // we don't crossfade streams ?? do we load the base here ??
PlayerControl::setURL( TQFile::encodeName( url.url() ), nextPlayer, !isStream );
m_isStream = false;
}
else
cleanup();
m_isStream = isStream;
int nextPlayer;
nextPlayer = m_current ? 0 : 1;
Engine::Base::load( url, isStream || url.protocol() == "http" );
m_state = Engine::Idle;
emit stateChanged( Engine::Idle );
m_url = url;
if (url.isLocalFile())
PlayerControl::setURL( TQFile::encodeName( url.url() ), nextPlayer, !m_isStream );
else
{
m_isStream = true;
PlayerControl::setURL( TQFile::encodeName( url.url() ), nextPlayer, !m_isStream );
}
return true;
}
bool
HelixEngine::play( uint offset )
{
debug() << "In play" << endl;
int nextPlayer;
if (!m_inited)
return false;
if (m_state != Engine::Playing)
{
struct timezone tz;
memset(&tz, 0, sizeof(struct timezone));
gettimeofday(&m_scopetm, &tz);
startTimer(HELIX_ENGINE_TIMER);
}
nextPlayer = m_current ? 0 : 1;
if (m_xfadeLength && m_xfadeNextTrack && !offset && isPlaying(m_current))
{
m_xfadeNextTrack = false;
PlayerControl::start(nextPlayer, true, m_xfadeLength);
}
else
PlayerControl::start(nextPlayer);
if (offset)
PlayerControl::seek( offset, nextPlayer );
if (!PlayerControl::getError())
{
if (m_state != Engine::Playing)
{
m_state = Engine::Playing;
emit stateChanged( Engine::Playing );
}
m_current = nextPlayer;
return true;
}
cleanup();
m_state = Engine::Empty;
emit stateChanged( Engine::Empty );
return false;
}
void
HelixEngine::cleanup()
{
if (!m_inited)
return;
m_url = KURL();
PlayerControl::stop(); // stop all players
resetScope(0);
resetScope(1);
killTimers();
m_isStream = false;
memset(&m_md, 0, sizeof(m_md));
memset(hscope, 0, 2*sizeof(HelixScope));
memset(m_pfade, 0, 2*sizeof(FadeTrack));
}
void
HelixEngine::stop()
{
if (!m_inited)
return;
debug() << "In stop where=" << where(m_current) << " duration=" << duration(m_current) << endl;
if( AmarokConfig::fadeout() && !m_pfade[m_current].m_fadeactive && state() == Engine::Playing )
{
debug() << "fading out...\n";
m_state = Engine::Empty;
emit stateChanged( Engine::Empty ); // tell the controller not to bother you anymore
m_pfade[m_current].m_fadeactive = true;
m_pfade[m_current].m_stopfade = true;
m_pfade[m_current].m_startfadetime = PlayerControl::where(m_current);
setFadeout(true, AmarokConfig::fadeoutLength(), m_current);
}
else
{
debug() << "Stopping immediately\n";
cleanup();
cleanUpStream(m_current);
m_state = Engine::Empty;
emit stateChanged( m_state );
}
}
void HelixEngine::play_finished(int playerIndex)
{
debug() << "Ok, finished playing the track\n";
cleanUpStream(playerIndex);
resetScope(playerIndex);
memset(&hscope[playerIndex], 0, sizeof(HelixScope));
memset(&m_pfade[playerIndex], 0, sizeof(FadeTrack));
if (playerIndex == m_current && !m_pfade[playerIndex].m_stopfade && !m_pfade[playerIndex].m_fadeactive)
{
m_state = Engine::Idle;
emit stateChanged( m_state );
emit trackEnded();
}
}
void
HelixEngine::pause()
{
if (!m_inited)
return;
// TODO: PAUSE in XFADE
debug() << "In pause\n";
if( m_state == Engine::Playing )
{
PlayerControl::pause(m_current);
m_state = Engine::Paused;
emit stateChanged( Engine::Paused );
}
}
void
HelixEngine::unpause()
{
if (!m_inited)
return;
// TODO: PAUSE in XFADE
debug() << "In unpause\n";
if ( m_state == Engine::Paused )
{
PlayerControl::resume(m_current);
m_state = Engine::Playing;
emit stateChanged( Engine::Playing );
}
}
Engine::State
HelixEngine::state() const
{
//debug() << "In state, state is " << m_state << endl;
if (!m_inited || m_url.isEmpty())
return (Engine::Empty);
return m_state;
}
uint
HelixEngine::position() const
{
if (!m_inited)
return 0;
return PlayerControl::where(m_current);
}
uint
HelixEngine::length() const
{
if (!m_inited)
return 0;
return PlayerControl::duration(m_current);
}
void
HelixEngine::seek( uint ms )
{
if (!m_inited)
return;
debug() << "In seek\n";
resetScope(0);
resetScope(1);
PlayerControl::seek(ms, m_current);
}
void
HelixEngine::setVolumeSW( uint vol )
{
if (!m_inited)
return;
debug() << "In setVolumeSW\n";
PlayerControl::setVolume(vol); // set the volume in all players!
}
bool
HelixEngine::canDecode( const KURL &url ) const
{
if (!m_inited)
return false;
debug() << "In canDecode " << url.prettyURL() << endl;
if (url.protocol() == "http" || url.protocol() == "rtsp")
return true;
const TQString path = url.path();
const TQString ext = path.mid( path.findRev( '.' ) + 1 ).lower();
if (ext != "txt")
for (int i=0; i<(int)m_mimes.size(); i++)
{
if (m_mimes[i].type.grep("audio").count() ||
m_mimes[i].type.grep("video").count() ||
m_mimes[i].type.grep("application").count())
if (m_mimes[i].ext.grep(ext).count())
{
return true;
}
}
return false;
}
void
HelixEngine::timerEvent( TQTimerEvent * )
{
PlayerControl::dispatch(); // dispatch the players
if ( m_xfadeLength <= 0 && m_state == Engine::Playing && PlayerControl::done(m_current) )
play_finished(m_current);
else if ( m_xfadeLength > 0 || AmarokConfig::fadeout() )
{
if ( m_state == Engine::Playing && isPlaying(m_current?0:1) && PlayerControl::done(m_current?0:1) )
hscope[m_current?0:1].m_lasttime = 0;
// fade on stop finished
if ( m_pfade[m_current].m_stopfade && m_pfade[m_current].m_fadeactive &&
(PlayerControl::where(m_current) > m_pfade[m_current].m_startfadetime + (unsigned)AmarokConfig::fadeoutLength() ||
PlayerControl::done(m_current)) )
{
debug() << "Stop fade end\n";
stop();
}
// crossfade finished
if ( m_pfade[m_current?0:1].m_fadeactive &&
PlayerControl::where(m_current?0:1) > m_pfade[m_current?0:1].m_startfadetime + (unsigned)m_xfadeLength)
play_finished(m_current?0:1);
}
// prune the scope(s)
prune();
struct timeval tm;
struct timezone tz;
memset(&tz, 0, sizeof(struct timezone));
gettimeofday(&tm, &tz);
m_scopedelta = (tm.tv_sec - m_scopetm.tv_sec) * 1000 + (tm.tv_usec - m_scopetm.tv_usec) / 1000; // ms
m_scopetm.tv_sec = tm.tv_sec;
m_scopetm.tv_usec = tm.tv_usec;
hscope[m_current].m_lasttime += m_scopedelta;
HelixSimplePlayer::metaData *md = getMetaData(m_current);
if (m_isStream &&
(strcmp(m_md.title, md->title) || strcmp(m_md.artist, md->artist)))
{
memcpy(&m_md, md, sizeof(m_md));
debug() << "{Title}: " << md->title << " {Artist}: " << md->artist << " {Bitrate}: " << md->bitrate << endl;
/* Real Radio One (and Rhapsody?) streams have their own format, where title is:
* clipinfo:title=<title>|artist name=<artist>|Album name=<album>|Artist:Next artist=<next artist>| \
* ordinal=<some number>|duration=<in secs>|Track:Rhapsody Track Id=<some number>
*
* for all other streams helix sends the title of the song in the artist string.
* this prevents context lookup, so we split it here (the artist and title are separated by a '-'
* we'll put the 'title' in album instead...
*/
Engine::SimpleMetaBundle bndl;
bndl.album = TQString::fromUtf8( m_md.title );
if ( bndl.album.startsWith( TQString("clipinfo:") ) )
{
bndl.album = bndl.album.remove(0, 9);
TQStringList sl = TQStringList::split('|', bndl.album);
for ( TQStringList::Iterator it = sl.begin(); it != sl.end(); ++it )
{
if ((*it).startsWith("title="))
bndl.title = (*it).section('=', 1, 1);
if ((*it).startsWith("artist name="))
bndl.artist = (*it).section('=', 1, 1);
if ((*it).startsWith("Album name="))
bndl.album = (*it).section('=', 1, 1);
if ((*it).startsWith("duration="))
bndl.length = (*it).section('=', 1, 1);
}
//debug() << "Title: " << bndl.title << endl;
//debug() << "Artist: " << bndl.artist << endl;
//debug() << "Album: " << bndl.album << endl;
//debug() << "length: " << bndl.length << endl;
}
else
{
char c,*tmp = strchr(m_md.artist, '-');
if (tmp)
{
tmp--;
c = *tmp;
*tmp = '\0';
bndl.artist = TQString::fromUtf8( m_md.artist );
*tmp = c;
tmp+=3;
bndl.title = TQString::fromUtf8( tmp );
bndl.album = TQString::fromUtf8( m_md.title );
}
else // just copy them as is...
{
bndl.title = TQString::fromUtf8( m_md.title );
bndl.artist = TQString::fromUtf8( m_md.artist );
}
}
bndl.bitrate = TQString::number( m_md.bitrate / 1000 );
emit EngineBase::metaData( bndl );
}
}
int HelixEngine::prune()
{
int err = 0;
err |= prune(0);
err |= prune(1);
return err;
}
int HelixEngine::prune(int playerIndex)
{
//
// this bit is to help us keep more accurate time than helix provides
/////////////////////////////////////////////////////////////////////
unsigned long hpos = PlayerControl::where(playerIndex);
if (hpos != hscope[playerIndex].m_lastpos
&& hpos - hscope[playerIndex].m_lastpos < hscope[playerIndex].m_lasttime - hscope[playerIndex].m_lastpos)
hscope[playerIndex].m_lasttime = hpos;
if (hpos > hscope[playerIndex].m_lasttime)
{
hscope[playerIndex].m_w = hpos;
hscope[playerIndex].m_lasttime = hpos;
}
else
hscope[playerIndex].m_w = hscope[playerIndex].m_lasttime;
hscope[playerIndex].m_lastpos = hpos;
if ( getScopeCount(playerIndex) > SCOPE_MAX_BEHIND ) // protect against naughty streams
{
resetScope(playerIndex);
return 0;
}
if (!hscope[playerIndex].m_w || !hscope[playerIndex].m_item)
return 0;
// prune, unless the player is still starting
while (hpos && hscope[playerIndex].m_item && hscope[playerIndex].m_w > hscope[playerIndex].m_item->etime)
{
//debug() << "pruning " << hpos << "," << hscope[playerIndex].m_w << "," << hscope[playerIndex].m_lasttime
// << "," << hscope[playerIndex].m_item->time << ":" << hscope[playerIndex].m_item->etime << endl;
if (hscope[playerIndex].m_item && hscope[playerIndex].m_item->allocd)
delete hscope[playerIndex].m_item;
hscope[playerIndex].m_item = getScopeBuf(playerIndex);
}
if (!hscope[playerIndex].m_item)
return 0;
if (hscope[playerIndex].m_w < hscope[playerIndex].m_item->time) // wait for the player to catchup
{
//debug() << "waiting for player to catchup " << hpos << "," << hscope[playerIndex].m_w << "," << hscope[playerIndex].m_lasttime
// << "," << hscope[playerIndex].m_item->time << ":" << hscope[playerIndex].m_item->etime << endl;
return 0;
}
return 1;
}
const Engine::Scope &HelixEngine::scope()
{
if (isPlaying(0) && isPlaying(1)) // crossfading
{
if (m_scopeplayerlast)
scope(m_current);
else
scope(m_current?0:1);
m_scopeplayerlast = !m_scopeplayerlast;
}
else
scope(m_current);
return m_scope;
}
int HelixEngine::scope(int playerIndex)
{
int i;
unsigned long t;
if (!m_inited)
return 0;
if (!hscope[playerIndex].m_item && !peekScopeTime(t, playerIndex))
hscope[playerIndex].m_item = getScopeBuf(playerIndex);
if (!prune(playerIndex))
return 0;
if (hscope[playerIndex].m_item->nchan > 2)
return 0;
int j,k=0;
short int *pint;
unsigned char b[4];
// calculate the starting offset into the buffer
int off = (hscope[playerIndex].m_item->spb * (hscope[playerIndex].m_w - hscope[playerIndex].m_item->time) /
(hscope[playerIndex].m_item->etime - hscope[playerIndex].m_item->time)) *
hscope[playerIndex].m_item->nchan * hscope[playerIndex].m_item->bps;
k = off;
while (hscope[playerIndex].m_item && hscope[playerIndex].m_scopeindex < SCOPESIZE)
{
while (k < (int) hscope[playerIndex].m_item->len)
{
for (j=0; j<hscope[playerIndex].m_item->nchan; j++)
{
switch (hscope[playerIndex].m_item->bps)
{
case 1:
b[1] = 0;
b[0] = hscope[playerIndex].m_item->buf[k];
break;
case 2:
b[1] = hscope[playerIndex].m_item->buf[k+1];
b[0] = hscope[playerIndex].m_item->buf[k];
break;
}
pint = (short *) &b[0];
if (hscope[playerIndex].m_item->nchan == 1) // duplicate mono samples
{
hscope[playerIndex].m_currentScope[hscope[playerIndex].m_scopeindex] = *pint;
hscope[playerIndex].m_scopeindex++;
hscope[playerIndex].m_currentScope[hscope[playerIndex].m_scopeindex] = *pint;
hscope[playerIndex].m_scopeindex++;
}
else
{
hscope[playerIndex].m_currentScope[hscope[playerIndex].m_scopeindex] = *pint;
hscope[playerIndex].m_scopeindex++;
}
k += hscope[playerIndex].m_item->bps;
}
if (hscope[playerIndex].m_scopeindex >= SCOPESIZE)
{
hscope[playerIndex].m_scopeindex = SCOPESIZE;
break;
}
}
// as long as we know there's another buffer...otherwise we need to wait for another
if (hscope[playerIndex].m_scopeindex < SCOPESIZE)
{
if (hscope[playerIndex].m_item && hscope[playerIndex].m_item->allocd)
delete hscope[playerIndex].m_item;
hscope[playerIndex].m_item = getScopeBuf(playerIndex);
k = 0;
if (!hscope[playerIndex].m_item)
return 0; // wait until there are some more buffers available
}
else
{
if (k >= (int) hscope[playerIndex].m_item->len)
{
if (hscope[playerIndex].m_item && hscope[playerIndex].m_item->allocd)
delete hscope[playerIndex].m_item;
hscope[playerIndex].m_item = getScopeBuf(playerIndex);
}
break; // done with the scope buffer, so hand it off
}
}
// ok, we must have a full buffer here, give it to the scope
for (i=0; i<SCOPESIZE; i++)
m_scope[i] = hscope[playerIndex].m_currentScope[i];
hscope[playerIndex].m_scopeindex = 0;
return 1;
}
void
HelixEngine::resetScope(int playerIndex)
{
if (playerIndex >=0 && playerIndex < numPlayers())
{
// make sure the scope is clear of old buffers
clearScopeQ(playerIndex);
hscope[playerIndex].m_scopeindex = 0;
if (hscope[playerIndex].m_item && hscope[playerIndex].m_item->allocd)
delete hscope[playerIndex].m_item;
hscope[playerIndex].m_w = 0;
hscope[playerIndex].m_item = 0;
}
}
void
HelixEngine::setEqualizerEnabled( bool enabled ) //SLOT
{
enableEQ(enabled);
}
// ok, this is lifted from gst... but why mess with what works?
void
HelixEngine::setEqualizerParameters( int preamp, const TQValueList<int>& bandGains ) //SLOT
{
m_preamp = ( preamp + 100 ) / 2;
m_equalizerGains.resize( bandGains.count() );
for ( uint i = 0; i < bandGains.count(); i++ )
m_equalizerGains[i] = ( *bandGains.at( i ) + 100 ) / 2;
updateEQgains();
}
namespace Debug
{
#undef helix_indent
TQCString helix_indent;
}
#include "helix-engine.moc"