2009-12-24 20:16:07 +01:00
|
|
|
/***************************************************************************
|
|
|
|
* Copyright (C) 2005 Christophe Thommeret <hftom@free.fr> *
|
|
|
|
* (C) 2005 Ian Monroe <ian@monroe.nu> *
|
|
|
|
* (C) 2005,6 Mark Kretschmann <markey@web.de> *
|
|
|
|
* (C) 2004,5 Max Howell <max.howell@methylblue.com> *
|
|
|
|
* (C) 2003,4 J. Kofler <kaffeine@gmx.net> *
|
|
|
|
* *
|
|
|
|
* 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. *
|
|
|
|
* *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#define DEBUG_PREFIX "xine-engine"
|
|
|
|
|
|
|
|
#include "xine-engine.h"
|
|
|
|
#include "xine-scope.h"
|
|
|
|
|
|
|
|
#include <climits>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
#include <QtDebug>
|
|
|
|
#include <QMessageBox>
|
|
|
|
#include <QApplication>
|
|
|
|
#include <QDir>
|
|
|
|
#include <QTime>
|
|
|
|
#include <QMutex>
|
|
|
|
#include <QMutexLocker>
|
2010-02-14 00:17:36 +01:00
|
|
|
#include <QLocale>
|
2010-03-22 00:11:34 +01:00
|
|
|
#include <QTimer>
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
extern "C"
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
#include <unistd.h>
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef LLONG_MAX
|
|
|
|
#define LLONG_MAX 9223372036854775807LL
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
//define this to use xine in a more standard way
|
2010-02-14 21:08:24 +01:00
|
|
|
#ifdef Q_OS_WIN32
|
|
|
|
#define XINE_SAFE_MODE
|
|
|
|
#endif
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
|
|
|
|
///some logging static globals
|
|
|
|
namespace Log
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
static uint bufferCount = 0;
|
|
|
|
static uint scopeCallCount = 1; //prevent divideByZero
|
|
|
|
static uint noSuitableBuffer = 0;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static Fader *s_fader = 0;
|
|
|
|
static OutFader *s_outfader = 0;
|
|
|
|
|
|
|
|
|
2010-02-10 16:06:33 +01:00
|
|
|
XineEngine::XineEngine()
|
|
|
|
: EngineBase()
|
2010-04-15 14:39:34 +02:00
|
|
|
, xine_( 0 )
|
|
|
|
, stream_( 0 )
|
|
|
|
, audioPort_( 0 )
|
|
|
|
, eventQueue_( 0 )
|
|
|
|
, post_( 0 )
|
|
|
|
, preamp_( 1.0 )
|
|
|
|
, stopFader_( false )
|
|
|
|
, fadeOutRunning_ ( false )
|
|
|
|
, equalizerEnabled_( false )
|
2010-03-22 00:11:34 +01:00
|
|
|
, prune_(NULL)
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
settings_.beginGroup(kSettingsGroup);
|
2009-12-24 20:16:07 +01:00
|
|
|
reloadSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
XineEngine::~XineEngine()
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
// Wait until the fader thread is done
|
|
|
|
if( s_fader ) {
|
2010-04-15 14:39:34 +02:00
|
|
|
stopFader_ = true;
|
2010-02-10 16:04:15 +01:00
|
|
|
s_fader->resume(); // safety call if the engine is in the pause state
|
|
|
|
s_fader->wait();
|
|
|
|
}
|
|
|
|
|
2010-03-22 00:11:34 +01:00
|
|
|
// Wait until the prune scope thread is done
|
|
|
|
if (prune_) {
|
|
|
|
prune_->exit();
|
|
|
|
prune_->wait();
|
|
|
|
}
|
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
delete s_fader;
|
|
|
|
delete s_outfader;
|
2010-03-22 00:11:34 +01:00
|
|
|
delete prune_;
|
2010-02-10 16:04:15 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( fadeoutOnExit_ ) {
|
2010-02-10 16:04:15 +01:00
|
|
|
bool terminateFader = false;
|
2010-04-15 14:39:34 +02:00
|
|
|
fadeOut( fadeoutDuration_, &terminateFader, true ); // true == exiting
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
//if( xine_ ) xine_config_save( xine_, configPath() );
|
2010-02-10 16:04:15 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( stream_ ) xine_close( stream_ );
|
|
|
|
if( eventQueue_ ) xine_event_dispose_queue( eventQueue_ );
|
|
|
|
if( stream_ ) xine_dispose( stream_ );
|
|
|
|
if( audioPort_ ) xine_close_audio_driver( xine_, audioPort_ );
|
|
|
|
if( post_ ) xine_post_dispose( xine_, post_ );
|
|
|
|
if( xine_ ) xine_exit( xine_ );
|
2010-02-10 16:04:15 +01:00
|
|
|
|
|
|
|
qDebug() << "xine closed";
|
|
|
|
|
|
|
|
qDebug() << "Scope statistics:";
|
|
|
|
qDebug() << " Average list size: " << Log::bufferCount / Log::scopeCallCount;
|
|
|
|
qDebug() << " Buffer failure: " << double(Log::noSuitableBuffer*100) / Log::scopeCallCount << "%";
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void XineEngine::reloadSettings() {
|
2010-04-15 14:39:34 +02:00
|
|
|
currentAudioPlugin_ = settings_.value("XineAudioOutput", "auto").toString();
|
|
|
|
fadeoutEnabled_ = settings_.value("FadeoutEnabled", true).toBool();
|
|
|
|
fadeoutOnExit_ = settings_.value("FadeoutOnExit", true).toBool();
|
|
|
|
fadeoutDuration_ = settings_.value("FadeoutDuration", 2000).toInt();
|
|
|
|
crossfadeEnabled_ = settings_.value("CrossfadeEnabled", true).toBool();
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-04-15 14:39:34 +02:00
|
|
|
XineEngine::Init()
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
qDebug() << "'Bringing joy to small mexican gerbils, a few weeks at a time.'";
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
#ifdef Q_OS_WIN32
|
2010-02-13 19:58:01 +01:00
|
|
|
putenv(QString("XINE_PLUGIN_PATH=" + QCoreApplication::applicationDirPath() + "/xine/plugins").toAscii().constData());
|
2010-02-10 14:15:18 +01:00
|
|
|
#endif // Q_OS_WIN32
|
|
|
|
|
|
|
|
#ifdef Q_OS_DARWIN
|
2010-02-10 16:04:15 +01:00
|
|
|
setenv("XINE_PLUGIN_PATH", QString(QCoreApplication::applicationDirPath() + "/../PlugIns/xine").toAscii().constData(), 1);
|
|
|
|
#endif // Q_OS_DARWIN
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
QMutexLocker l(&initMutex_);
|
2010-02-04 00:12:21 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_ = xine_new();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( !xine_ ) {
|
|
|
|
emit Error("Could not initialize xine.");
|
2010-02-10 16:04:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
#ifdef XINE_SAFE_MODE
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_engine_set_param( xine_, XINE_ENGINE_PARAM_VERBOSITY, 99 );
|
2010-02-10 16:04:15 +01:00
|
|
|
#endif
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
//xine_config_load( xine_, configPath() );
|
2010-02-10 16:04:15 +01:00
|
|
|
//debug() << "w00t" << configPath() << endl;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_init( xine_ );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
makeNewStream();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
#ifndef XINE_SAFE_MODE
|
2010-03-22 00:11:34 +01:00
|
|
|
prune_ = new PruneScopeThread(this);
|
|
|
|
prune_->start();
|
2010-02-10 16:04:15 +01:00
|
|
|
#endif
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
return true;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
XineEngine::makeNewStream()
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
audioPort_ = xine_open_audio_driver( xine_, currentAudioPlugin_.toLocal8Bit().constData(), NULL );
|
|
|
|
if( !audioPort_ ) {
|
2010-02-10 16:04:15 +01:00
|
|
|
//TODO make engine method that is the same but parents the dialog for us
|
2010-04-15 14:39:34 +02:00
|
|
|
emit Error("xine was unable to initialize any audio drivers.");
|
2010-02-10 16:04:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
stream_ = xine_stream_new( xine_, audioPort_, NULL );
|
|
|
|
if( !stream_ ) {
|
|
|
|
xine_close_audio_driver( xine_, audioPort_ );
|
|
|
|
audioPort_ = NULL;
|
|
|
|
emit Error("Could not create a new xine stream");
|
2010-02-10 16:04:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( eventQueue_ )
|
|
|
|
xine_event_dispose_queue( eventQueue_ );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
xine_event_create_listener_thread(
|
2010-04-15 14:39:34 +02:00
|
|
|
eventQueue_ = xine_event_new_queue( stream_ ),
|
2010-02-10 16:04:15 +01:00
|
|
|
&XineEngine::XineEventListener,
|
|
|
|
(void*)this );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
#ifndef XINE_SAFE_MODE
|
|
|
|
//implemented in xine-scope.h
|
2010-04-15 14:39:34 +02:00
|
|
|
post_ = scope_plugin_new( xine_, audioPort_ );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_set_param( stream_, XINE_PARAM_METRONOM_PREBUFFER, 6000 );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_IGNORE_VIDEO, 1 );
|
2010-02-10 16:04:15 +01:00
|
|
|
#endif
|
2009-12-24 20:16:07 +01:00
|
|
|
#ifdef XINE_PARAM_EARLY_FINISHED_EVENT
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( xine_check_version(1,1,1) && !(xfadeLength_ > 0) ) {
|
2010-02-10 16:04:15 +01:00
|
|
|
// enable gapless playback
|
|
|
|
qDebug() << "gapless playback enabled.";
|
2010-04-15 14:39:34 +02:00
|
|
|
//xine_set_param(stream_, XINE_PARAM_EARLY_FINISHED_EVENT, 1 );
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
#endif
|
2010-02-10 16:04:15 +01:00
|
|
|
return true;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Makes sure an audio port and a stream exist.
|
|
|
|
bool
|
|
|
|
XineEngine::ensureStream()
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if( !stream_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
return makeNewStream();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
return true;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-04-15 14:39:34 +02:00
|
|
|
XineEngine::Load( const QUrl &url ,Engine::TrackChangeType change )
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
if( !ensureStream() )
|
|
|
|
return false;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
Engine::Base::Load( url, change );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
if( s_outfader ) {
|
|
|
|
s_outfader->finish();
|
|
|
|
delete s_outfader;
|
|
|
|
}
|
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( xfadeLength_ > 0 && xine_get_status( stream_ ) == XINE_STATUS_PLAY &&
|
2010-02-10 16:04:15 +01:00
|
|
|
url.scheme().toLower() == "file" &&
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_get_param( stream_, XINE_PARAM_SPEED ) != XINE_SPEED_PAUSE &&
|
|
|
|
( xfadeNextTrack_ || //set by engine controller when switching tracks automatically
|
|
|
|
crossfadeEnabled_))
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
xfadeNextTrack_ = false;
|
2010-02-10 16:04:15 +01:00
|
|
|
// Stop a probably running fader
|
|
|
|
if( s_fader ) {
|
2010-04-15 14:39:34 +02:00
|
|
|
stopFader_ = true;
|
2010-02-10 16:04:15 +01:00
|
|
|
s_fader->finish(); // makes the fader stop abruptly
|
|
|
|
delete s_fader;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-04-15 14:39:34 +02:00
|
|
|
s_fader = new Fader( this, xfadeLength_ );
|
|
|
|
setEqualizerParameters( intPreamp_, equalizerGains_ );
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
// for users who stubbonly refuse to use DMIX or buy a good soundcard
|
|
|
|
// why doesn't xine do this? I cannot say.
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_close( stream_ );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
qDebug() << "Before xine_open() *****";
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( xine_open( stream_, url.toEncoded() ) )
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
|
|
|
qDebug() << "After xine_open() *****";
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
#ifndef XINE_SAFE_MODE
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_post_out_t *source = xine_get_audio_source( stream_ );
|
|
|
|
xine_post_in_t *target = (xine_post_in_t*)xine_post_input( post_, const_cast<char*>("audio in") );
|
2010-02-10 16:04:15 +01:00
|
|
|
xine_post_wire( source, target );
|
|
|
|
#endif
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
playlistChanged();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifdef XINE_PARAM_GAPLESS_SWITCH
|
2010-04-15 14:39:34 +02:00
|
|
|
//if ( xine_check_version(1,1,1) && !(xfadeLength_ > 0) )
|
|
|
|
//xine_set_param( stream_, XINE_PARAM_GAPLESS_SWITCH, 0);
|
2010-02-10 16:04:15 +01:00
|
|
|
#endif
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
// FAILURE to load!
|
|
|
|
//s_fader will delete itself
|
|
|
|
determineAndShowErrorMessage();
|
|
|
|
|
|
|
|
return false;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-04-15 14:39:34 +02:00
|
|
|
XineEngine::Play( uint offset )
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
if( !ensureStream() )
|
|
|
|
return false;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
const bool has_audio = xine_get_stream_info( stream_, XINE_STREAM_INFO_HAS_AUDIO );
|
|
|
|
const bool audio_handled = xine_get_stream_info( stream_, XINE_STREAM_INFO_AUDIO_HANDLED );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if (has_audio && audio_handled && xine_play( stream_, 0, offset ))
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
|
|
|
if( s_fader )
|
|
|
|
s_fader->start( QThread::LowestPriority );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
emit StateChanged( Engine::Playing );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
return true;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
//we need to stop the track that is prepped for crossfade
|
|
|
|
delete s_fader;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
emit StateChanged( Engine::Empty );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
determineAndShowErrorMessage();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_close( stream_ );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
return false;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
XineEngine::determineAndShowErrorMessage()
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
QString body;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
qDebug() << "xine_get_error()";
|
2010-04-15 14:39:34 +02:00
|
|
|
switch (xine_get_error( stream_ )) {
|
2010-02-10 16:04:15 +01:00
|
|
|
case XINE_ERROR_NO_INPUT_PLUGIN:
|
|
|
|
body = "No suitable input plugin. This often means that the url's protocol is not supported. Network failures are other possible causes.";
|
|
|
|
break;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
case XINE_ERROR_NO_DEMUX_PLUGIN:
|
|
|
|
body = "No suitable demux plugin. This often means that the file format is not supported.";
|
|
|
|
break;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
case XINE_ERROR_DEMUX_FAILED:
|
|
|
|
body = "Demuxing failed.";
|
|
|
|
break;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
case XINE_ERROR_INPUT_FAILED:
|
|
|
|
body = "Could not open file.";
|
|
|
|
break;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
case XINE_ERROR_MALFORMED_MRL:
|
|
|
|
body = "The location is malformed.";
|
|
|
|
break;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
case XINE_ERROR_NONE:
|
|
|
|
// xine is thick. xine doesn't think there is an error
|
|
|
|
// but there may be! We check for other errors below.
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
default:
|
2010-04-15 14:39:34 +02:00
|
|
|
if (!xine_get_stream_info( stream_, XINE_STREAM_INFO_AUDIO_HANDLED ))
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
|
|
|
// xine can read the plugin but it didn't find any codec
|
|
|
|
// THUS xine=daft for telling us it could handle the format in canDecode!
|
|
|
|
body = "There is no available decoder.";
|
2010-04-15 14:39:34 +02:00
|
|
|
QString const ext = QFileInfo(url_.path()).completeSuffix();
|
2010-02-10 16:04:15 +01:00
|
|
|
// TODO
|
|
|
|
//if (ext == "mp3" && EngineController::installDistroCodec( "xine-engine" ))
|
|
|
|
// return;
|
|
|
|
}
|
2010-04-15 14:39:34 +02:00
|
|
|
else if (!xine_get_stream_info( stream_, XINE_STREAM_INFO_HAS_AUDIO ))
|
2010-02-10 16:04:15 +01:00
|
|
|
body = "There is no audio channel!";
|
|
|
|
break;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
// TODO
|
2010-03-10 22:39:25 +01:00
|
|
|
qWarning() << body;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-04-15 14:39:34 +02:00
|
|
|
XineEngine::Stop()
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
if( s_fader && s_fader->isRunning())
|
|
|
|
s_fader->resume(); // safety call if the engine is in the pause state
|
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( !stream_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
return;
|
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( (fadeoutEnabled_ && !fadeOutRunning_) || state() == Engine::Paused )
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
s_outfader = new OutFader( this, fadeoutDuration_ );
|
2010-02-10 16:04:15 +01:00
|
|
|
s_outfader->start();
|
|
|
|
::usleep( 100 ); //to be sure engine state won't be changed before it is checked in fadeOut()
|
2010-04-15 14:39:34 +02:00
|
|
|
url_ = QUrl(); //to ensure we return Empty from state()
|
2010-02-10 16:04:15 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
std::fill( scope_.begin(), scope_.end(), 0 );
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2010-04-15 14:39:34 +02:00
|
|
|
else if( !fadeOutRunning_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_stop( stream_ );
|
|
|
|
xine_close( stream_ );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_AUDIO_CLOSE_DEVICE, 1);
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
emit StateChanged( Engine::Empty );
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
void
|
2010-04-15 14:39:34 +02:00
|
|
|
XineEngine::Pause()
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( !stream_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
return;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( xine_get_param( stream_, XINE_PARAM_SPEED ) != XINE_SPEED_PAUSE )
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
|
|
|
if( s_fader && s_fader->isRunning() )
|
|
|
|
s_fader->pause();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_set_param( stream_, XINE_PARAM_SPEED, XINE_SPEED_PAUSE );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_AUDIO_CLOSE_DEVICE, 1);
|
|
|
|
emit StateChanged( Engine::Paused );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-04-15 14:39:34 +02:00
|
|
|
XineEngine::Unpause()
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( !stream_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
return;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( xine_get_param( stream_, XINE_PARAM_SPEED ) == XINE_SPEED_PAUSE )
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
|
|
|
if( s_fader && s_fader->isRunning() )
|
|
|
|
s_fader->resume();
|
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_set_param( stream_, XINE_PARAM_SPEED, XINE_SPEED_NORMAL );
|
|
|
|
emit StateChanged( Engine::Playing );
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Engine::State
|
|
|
|
XineEngine::state() const
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( !stream_ || fadeOutRunning_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
return Engine::Empty;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
switch( xine_get_status( stream_ ) )
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
case XINE_STATUS_PLAY: return xine_get_param( stream_, XINE_PARAM_SPEED ) != XINE_SPEED_PAUSE ? Engine::Playing : Engine::Paused;
|
2009-12-24 20:16:07 +01:00
|
|
|
case XINE_STATUS_IDLE: return Engine::Empty;
|
|
|
|
case XINE_STATUS_STOP:
|
2010-04-15 14:39:34 +02:00
|
|
|
default: return url_.isEmpty() ? Engine::Empty : Engine::Idle;
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
uint
|
|
|
|
XineEngine::position() const
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
if ( state() == Engine::Empty )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int pos;
|
|
|
|
int time = 0;
|
|
|
|
int length;
|
|
|
|
|
|
|
|
// Workaround for problems when you seek too quickly, see BUG 99808
|
|
|
|
int tmp = 0, i = 0;
|
|
|
|
while( ++i < 4 )
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_get_pos_length( stream_, &pos, &time, &length );
|
2010-02-10 16:04:15 +01:00
|
|
|
if( time > tmp ) break;
|
|
|
|
usleep( 100000 );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Here we check for new metadata periodically, because xine does not emit an event
|
|
|
|
// in all cases (e.g. with ogg streams). See BUG 122505
|
|
|
|
if ( state() != Engine::Idle && state() != Engine::Empty )
|
|
|
|
{
|
|
|
|
const Engine::SimpleMetaBundle bundle = fetchMetaData();
|
2010-04-15 14:39:34 +02:00
|
|
|
if( bundle.title != currentBundle_.title || bundle.artist != currentBundle_.artist ) {
|
2010-02-10 16:04:15 +01:00
|
|
|
qDebug() << "Metadata received.";
|
2010-04-15 14:39:34 +02:00
|
|
|
currentBundle_ = bundle;
|
2010-02-10 16:04:15 +01:00
|
|
|
|
|
|
|
XineEngine* p = const_cast<XineEngine*>( this );
|
2010-04-15 14:39:34 +02:00
|
|
|
p->emit MetaData( bundle );
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
return time;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
uint
|
|
|
|
XineEngine::length() const
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( !stream_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
return 0;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
// xine often delivers nonsense values for VBR files and such, so we only
|
|
|
|
// use the length for remote files
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( url_.scheme().toLower() == "file" )
|
2010-02-10 16:04:15 +01:00
|
|
|
return 0;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
else {
|
|
|
|
int pos;
|
|
|
|
int time;
|
|
|
|
int length = 0;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_get_pos_length( stream_, &pos, &time, &length );
|
2010-02-10 16:04:15 +01:00
|
|
|
if( length < 0 )
|
|
|
|
length=0;
|
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-04-15 14:39:34 +02:00
|
|
|
XineEngine::Seek( uint ms )
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
if( !ensureStream() )
|
|
|
|
return;
|
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( xine_get_param( stream_, XINE_PARAM_SPEED ) == XINE_SPEED_PAUSE ) {
|
2010-02-10 16:04:15 +01:00
|
|
|
// FIXME this is a xine API issue really, they need to add a seek function
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_play( stream_, 0, (int)ms );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_SPEED, XINE_SPEED_PAUSE );
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
|
|
|
else
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_play( stream_, 0, (int)ms );
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-04-15 14:39:34 +02:00
|
|
|
XineEngine::SetVolumeSW( uint vol )
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( !stream_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
return;
|
|
|
|
if( !s_fader )
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_set_param( stream_, XINE_PARAM_AUDIO_AMP_LEVEL, static_cast<uint>( vol * preamp_ ) );
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
XineEngine::fadeOut( uint fadeLength, bool* terminate, bool exiting )
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if( fadeOutRunning_ ) //Let us not start another fadeout...
|
2010-02-10 16:04:15 +01:00
|
|
|
return;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
fadeOutRunning_ = !fadeOutRunning_;
|
|
|
|
const bool isPlaying = stream_ && ( xine_get_status( stream_ ) == XINE_STATUS_PLAY );
|
|
|
|
const float originalVol = Engine::Base::MakeVolumeLogarithmic( volume_ ) * preamp_;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
// On shutdown, limit fadeout to 3 secs max, so that we don't risk getting killed
|
|
|
|
const int length = exiting ? qMin( fadeLength, 3000u ) : fadeLength;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
if( length > 0 && isPlaying )
|
|
|
|
{
|
|
|
|
// fader-class doesn't work in this spot as is, so some parts need to be copied here... (ugly)
|
|
|
|
uint stepsCount = length < 1000 ? length / 10 : 100;
|
|
|
|
uint stepSizeUs = (int)( 1000.0 * (float)length / (float)stepsCount );
|
|
|
|
|
|
|
|
::usleep( stepSizeUs );
|
|
|
|
QTime t;
|
|
|
|
t.start();
|
|
|
|
float mix = 0.0;
|
|
|
|
while ( mix < 1.0 )
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
if( *terminate ) break;
|
|
|
|
|
|
|
|
::usleep( stepSizeUs );
|
2010-04-15 14:39:34 +02:00
|
|
|
float vol = Engine::Base::MakeVolumeLogarithmic( volume_ ) * preamp_;
|
2010-02-10 16:04:15 +01:00
|
|
|
float mix = (float)t.elapsed() / (float)length;
|
|
|
|
if ( mix > 1.0 )
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( stream_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
|
|
|
float v = 4.0 * (1.0 - mix) / 3.0;
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_set_param( stream_, XINE_PARAM_AUDIO_AMP_LEVEL, (uint)( v < 1.0 ? vol * v : vol ) );
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2010-04-15 14:39:34 +02:00
|
|
|
if( fadeOutRunning_ && stream_ )
|
|
|
|
xine_set_param( stream_, XINE_PARAM_AUDIO_AMP_LEVEL, (uint) originalVol );
|
|
|
|
fadeOutRunning_ = !fadeOutRunning_;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
XineEngine::setEqualizerEnabled( bool enable )
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( !stream_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
return;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
equalizerEnabled_ = enable;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
if( !enable ) {
|
|
|
|
QList<int> gains;
|
|
|
|
for( uint x = 0; x < 10; x++ )
|
|
|
|
gains << -101; // sets eq gains to zero.
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
setEqualizerParameters( 0, gains );
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-02-10 16:04:15 +01:00
|
|
|
sets the eq params for xine engine - have to rescale eq params to fitting range (adapted from kaffeine and xfmedia)
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
preamp
|
|
|
|
pre: (-100..100)
|
|
|
|
post: (0.1..1.9) - this is not really a preamp but we use the xine preamp parameter for our normal volume. so we make a postamp.
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
gains
|
|
|
|
pre: (-100..100)
|
|
|
|
post: (1..200) - (1 = down, 100 = middle, 200 = up, 0 = off)
|
2009-12-24 20:16:07 +01:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
XineEngine::setEqualizerParameters( int preamp, const QList<int> &gains )
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( !stream_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
return;
|
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
equalizerGains_ = gains;
|
|
|
|
intPreamp_ = preamp;
|
2010-02-10 16:04:15 +01:00
|
|
|
QList<int>::ConstIterator it = gains.begin();
|
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_set_param( stream_, XINE_PARAM_EQ_30HZ, int( (*it )*0.995 + 100 ) );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_EQ_60HZ, int( (*++it)*0.995 + 100 ) );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_EQ_125HZ, int( (*++it)*0.995 + 100 ) );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_EQ_250HZ, int( (*++it)*0.995 + 100 ) );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_EQ_500HZ, int( (*++it)*0.995 + 100 ) );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_EQ_1000HZ, int( (*++it)*0.995 + 100 ) );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_EQ_2000HZ, int( (*++it)*0.995 + 100 ) );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_EQ_4000HZ, int( (*++it)*0.995 + 100 ) );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_EQ_8000HZ, int( (*++it)*0.995 + 100 ) );
|
|
|
|
xine_set_param( stream_, XINE_PARAM_EQ_16000HZ, int( (*++it)*0.995 + 100 ) );
|
|
|
|
|
|
|
|
preamp_ = ( preamp - 0.1 * preamp + 100 ) / 100.0;
|
|
|
|
SetVolume( volume_ );
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-04-15 14:39:34 +02:00
|
|
|
XineEngine::CanDecode( const QUrl &url )
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
static QStringList list;
|
|
|
|
if(list.isEmpty())
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
QMutexLocker l(&const_cast<XineEngine*>(this)->initMutex_);
|
2010-02-10 16:04:15 +01:00
|
|
|
|
|
|
|
if (list.isEmpty()) {
|
2010-04-15 14:39:34 +02:00
|
|
|
char* exts = xine_get_file_extensions( xine_ );
|
2010-02-10 16:04:15 +01:00
|
|
|
list = QString(exts).split(' ');
|
|
|
|
free( exts ); exts = NULL;
|
|
|
|
//images
|
|
|
|
list.removeAll("png");
|
|
|
|
list.removeAll("jpg");
|
|
|
|
list.removeAll("jpeg");
|
|
|
|
list.removeAll("gif");
|
|
|
|
list.removeAll("ilbm");
|
|
|
|
list.removeAll("iff");
|
|
|
|
//subtitles
|
|
|
|
list.removeAll("asc");
|
|
|
|
list.removeAll("txt");
|
|
|
|
list.removeAll("sub");
|
|
|
|
list.removeAll("srt");
|
|
|
|
list.removeAll("smi");
|
|
|
|
list.removeAll("ssa");
|
|
|
|
//HACK we also check for m4a because xine plays them but
|
|
|
|
//for some reason doesn't return the extension
|
|
|
|
if(!list.contains("m4a"))
|
|
|
|
list << "m4a";
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
if (url.scheme() == "cdda")
|
|
|
|
// play audio CDs pls
|
|
|
|
return true;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
QString path = url.path();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
// partial downloads from Konqi and other browsers
|
|
|
|
// tend to have a .part extension
|
|
|
|
if (path.endsWith( ".part" ))
|
|
|
|
path = path.left( path.length() - 5 );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
const QString ext = path.mid( path.lastIndexOf( '.' ) + 1 ).toLower();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
return list.contains( ext );
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const Engine::Scope&
|
|
|
|
XineEngine::scope()
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if( !post_ || !stream_ || xine_get_status( stream_ ) != XINE_STATUS_PLAY )
|
|
|
|
return scope_;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
MyNode* const myList = scope_plugin_list( post_ );
|
|
|
|
metronom_t* const myMetronom = scope_plugin_metronom( post_ );
|
|
|
|
const int myChannels = scope_plugin_channels( post_ );
|
2010-02-10 16:04:15 +01:00
|
|
|
int scopeidx = 0;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
if (myChannels > 2)
|
2010-04-15 14:39:34 +02:00
|
|
|
return scope_;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
for( int n, frame = 0; frame < 512; )
|
|
|
|
{
|
|
|
|
MyNode *best_node = 0;
|
|
|
|
|
|
|
|
for( MyNode *node = myList->next; node != myList; node = node->next, Log::bufferCount++ )
|
2010-04-15 14:39:34 +02:00
|
|
|
if( node->vpts <= currentVpts_ && (!best_node || node->vpts > best_node->vpts) )
|
2010-02-10 16:04:15 +01:00
|
|
|
best_node = node;
|
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( !best_node || best_node->vpts_end < currentVpts_ ) {
|
2010-02-10 16:04:15 +01:00
|
|
|
Log::noSuitableBuffer++; break; }
|
|
|
|
|
|
|
|
int64_t
|
2010-04-15 14:39:34 +02:00
|
|
|
diff = currentVpts_;
|
2010-02-10 16:04:15 +01:00
|
|
|
diff -= best_node->vpts;
|
|
|
|
diff *= 1<<16;
|
|
|
|
diff /= myMetronom->pts_per_smpls;
|
|
|
|
|
|
|
|
const int16_t*
|
|
|
|
data16 = best_node->mem;
|
|
|
|
data16 += diff;
|
|
|
|
|
|
|
|
diff += diff % myChannels; //important correction to ensure we don't overflow the buffer
|
|
|
|
diff /= myChannels; //use units of frames, not samples
|
|
|
|
|
|
|
|
//calculate the number of available samples in this buffer
|
|
|
|
n = best_node->num_frames;
|
|
|
|
n -= diff;
|
|
|
|
n += frame; //clipping for # of frames we need
|
|
|
|
|
|
|
|
if( n > 512 )
|
|
|
|
n = 512; //we don't want more than 512 frames
|
|
|
|
|
|
|
|
for( int a, c; frame < n; ++frame, data16 += myChannels ) {
|
|
|
|
for( a = c = 0; c < myChannels; ++c )
|
|
|
|
{
|
|
|
|
// we now give interleaved pcm to the scope
|
2010-04-15 14:39:34 +02:00
|
|
|
scope_[scopeidx++] = data16[c];
|
2010-02-10 16:04:15 +01:00
|
|
|
if (myChannels == 1) // duplicate mono samples
|
2010-04-15 14:39:34 +02:00
|
|
|
scope_[scopeidx++] = data16[c];
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
currentVpts_ = best_node->vpts_end;
|
|
|
|
currentVpts_++; //FIXME needs to be done for some reason, or you get situations where it uses same buffer again and again
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
Log::scopeCallCount++;
|
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
return scope_;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-03-22 00:11:34 +01:00
|
|
|
XineEngine::PruneScope()
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( !stream_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
return;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
//here we prune the buffer list regularly
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
MyNode *myList = scope_plugin_list( post_ );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
if ( ! myList ) return;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
//we operate on a subset of the list for thread-safety
|
|
|
|
MyNode * const first_node = myList->next;
|
|
|
|
MyNode const * const list_end = myList;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
currentVpts_ = (xine_get_status( stream_ ) == XINE_STATUS_PLAY)
|
|
|
|
? xine_get_current_vpts( stream_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
: LLONG_MAX; //if state is not playing OR paused, empty the list
|
|
|
|
//: std::numeric_limits<int64_t>::max(); //TODO don't support crappy gcc 2.95
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
for( MyNode *prev = first_node, *node = first_node->next; node != list_end; node = node->next )
|
|
|
|
{
|
|
|
|
//we never delete first_node
|
|
|
|
//this maintains thread-safety
|
2010-04-15 14:39:34 +02:00
|
|
|
if( node->vpts_end < currentVpts_ ) {
|
2010-02-10 16:04:15 +01:00
|
|
|
prev->next = node->next;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
free( node->mem );
|
|
|
|
free( node );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
node = prev;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
prev = node;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
XineEngine::event( QEvent* e )
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
#define message static_cast<QString*>(static_cast<XineEvent*>(e)->data())
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
switch( e->type() )
|
|
|
|
{
|
2009-12-24 20:16:07 +01:00
|
|
|
case XineEvent::PlaybackFinished: //XINE_EVENT_UI_PLAYBACK_FINISHED
|
2010-04-15 14:39:34 +02:00
|
|
|
emit TrackEnded();
|
2010-02-10 16:04:15 +01:00
|
|
|
return true;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
case XineEvent::InfoMessage:
|
2010-04-15 14:39:34 +02:00
|
|
|
emit InfoMessage( (*message).arg( url_.toString() ) );
|
2010-02-10 16:04:15 +01:00
|
|
|
delete message;
|
|
|
|
return true;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
case XineEvent::StatusMessage:
|
2010-04-15 14:39:34 +02:00
|
|
|
emit StatusText( *message );
|
2010-02-10 16:04:15 +01:00
|
|
|
delete message;
|
|
|
|
return true;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
case XineEvent::MetaInfoChanged: { //meta info has changed
|
2010-02-26 23:17:52 +01:00
|
|
|
qDebug() << "Metadata received.";
|
|
|
|
const Engine::SimpleMetaBundle bundle = fetchMetaData();
|
2010-04-15 14:39:34 +02:00
|
|
|
if( bundle.title != currentBundle_.title || bundle.artist != currentBundle_.artist ) {
|
|
|
|
currentBundle_ = bundle;
|
2010-02-26 23:17:52 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
emit MetaData( bundle );
|
2010-02-26 23:17:52 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
case XineEvent::Redirecting:
|
2010-04-15 14:39:34 +02:00
|
|
|
emit StatusText( QString("Redirecting to: ").arg( *message ) );
|
|
|
|
Load( QUrl( *message ), Engine::Auto );
|
|
|
|
Play();
|
2010-02-10 16:04:15 +01:00
|
|
|
delete message;
|
|
|
|
return true;
|
2009-12-24 20:16:07 +01:00
|
|
|
case XineEvent::LastFMTrackChanged:
|
2010-04-15 14:39:34 +02:00
|
|
|
emit LastFmTrackChange();
|
2010-02-10 16:04:15 +01:00
|
|
|
return true;
|
2009-12-24 20:16:07 +01:00
|
|
|
default:
|
2010-02-10 16:04:15 +01:00
|
|
|
;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
#undef message
|
|
|
|
return false;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//SLOT
|
|
|
|
void
|
|
|
|
XineEngine::playlistChanged()
|
|
|
|
{
|
|
|
|
// TODO
|
2010-02-10 16:04:15 +01:00
|
|
|
/*#ifdef XINE_PARAM_EARLY_FINISHED_EVENT
|
|
|
|
#ifdef XINE_PARAM_GAPLESS_SWITCH
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( xine_check_version(1,1,1) && !(xfadeLength_ > 0)
|
|
|
|
&& url_.isLocalFile() && Playlist::instance()->isTrackAfter() )
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_set_param(stream_, XINE_PARAM_EARLY_FINISHED_EVENT, 1 );
|
2010-02-10 16:04:15 +01:00
|
|
|
debug() << "XINE_PARAM_EARLY_FINISHED_EVENT enabled" << endl;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//we don't want an early finish event if there is no track after the current one
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_set_param(stream_, XINE_PARAM_EARLY_FINISHED_EVENT, 0 );
|
2010-02-10 16:04:15 +01:00
|
|
|
debug() << "XINE_PARAM_EARLY_FINISHED_EVENT disabled" << endl;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif*/
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static time_t last_error_time = 0; // hysteresis on xine errors
|
|
|
|
static int last_error = XINE_MSG_NO_ERROR;
|
|
|
|
|
|
|
|
void
|
|
|
|
XineEngine::XineEventListener( void *p, const xine_event_t* xineEvent )
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
time_t current;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
if( !p ) return;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
#define xe static_cast<XineEngine*>(p)
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
switch( xineEvent->type )
|
|
|
|
{
|
2009-12-24 20:16:07 +01:00
|
|
|
case XINE_EVENT_UI_SET_TITLE:
|
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
qDebug() << "XINE_EVENT_UI_SET_TITLE";
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
QApplication::postEvent( xe, new XineEvent( XineEvent::MetaInfoChanged ) );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
break;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
case XINE_EVENT_UI_PLAYBACK_FINISHED:
|
2010-02-10 16:04:15 +01:00
|
|
|
qDebug() << "XINE_EVENT_UI_PLAYBACK_FINISHED";
|
|
|
|
|
|
|
|
#ifdef XINE_PARAM_GAPLESS_SWITCH
|
|
|
|
// TODO
|
2010-04-15 14:39:34 +02:00
|
|
|
/*if ( xine_check_version(1,1,1) && xe->url_.isLocalFile() //Remote media break with gapless
|
2010-02-10 16:04:15 +01:00
|
|
|
//don't prepare for a track that isn't coming
|
|
|
|
&& Playlist::instance()
|
|
|
|
&& Playlist::instance()->isTrackAfter()
|
|
|
|
&& !AmarokConfig::crossfade() )
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_set_param( xe->stream_, XINE_PARAM_GAPLESS_SWITCH, 1);*/
|
2010-02-10 16:04:15 +01:00
|
|
|
#endif
|
|
|
|
//emit signal from GUI thread
|
|
|
|
QApplication::postEvent( xe, new XineEvent(XineEvent::PlaybackFinished) );
|
|
|
|
break;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
case XINE_EVENT_PROGRESS: {
|
2010-02-10 16:04:15 +01:00
|
|
|
xine_progress_data_t* pd = (xine_progress_data_t*)xineEvent->data;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
QString
|
2009-12-24 20:16:07 +01:00
|
|
|
msg = "%1 %2%";
|
2010-02-10 16:04:15 +01:00
|
|
|
msg = msg.arg( QString::fromUtf8( pd->description ) )
|
|
|
|
.arg( QString::number(pd->percent) + QLocale::system().percent() );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
XineEvent *e = new XineEvent( XineEvent::StatusMessage );
|
|
|
|
e->setData( new QString( msg ) );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
QApplication::postEvent( xe, e );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
} break;
|
|
|
|
|
2010-02-28 18:28:56 +01:00
|
|
|
case XINE_EVENT_MRL_REFERENCE_EXT: {
|
2010-02-10 16:04:15 +01:00
|
|
|
/// xine has read the stream and found it actually links to something else
|
|
|
|
/// so we need to play that instead
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-28 18:28:56 +01:00
|
|
|
QString message = QString::fromUtf8(
|
|
|
|
static_cast<xine_mrl_reference_data_ext_t*>(xineEvent->data)->mrl);
|
2010-02-10 16:04:15 +01:00
|
|
|
XineEvent *e = new XineEvent( XineEvent::Redirecting );
|
|
|
|
e->setData( new QString( message ) );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
QApplication::postEvent( xe, e );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case XINE_EVENT_UI_MESSAGE:
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
qDebug() << "message received from xine";
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
xine_ui_message_data_t *data = (xine_ui_message_data_t *)xineEvent->data;
|
|
|
|
QString message;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
switch( data->type )
|
|
|
|
{
|
2009-12-24 20:16:07 +01:00
|
|
|
case XINE_MSG_NO_ERROR:
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
2009-12-24 20:16:07 +01:00
|
|
|
//series of \0 separated strings, terminated with a \0\0
|
|
|
|
char str[2000];
|
|
|
|
char *p = str;
|
|
|
|
for( char *msg = data->messages; !(*msg == '\0' && *(msg+1) == '\0'); ++msg, ++p )
|
2010-02-10 16:04:15 +01:00
|
|
|
*p = *msg == '\0' ? '\n' : *msg;
|
2009-12-24 20:16:07 +01:00
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
qDebug() << str;
|
|
|
|
|
|
|
|
break;
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
case XINE_MSG_ENCRYPTED_SOURCE:
|
2010-02-10 16:04:15 +01:00
|
|
|
break;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
case XINE_MSG_UNKNOWN_HOST:
|
2010-02-10 16:04:15 +01:00
|
|
|
message = "The host is unknown for the URL: <i>%1</i>"; goto param;
|
2009-12-24 20:16:07 +01:00
|
|
|
case XINE_MSG_UNKNOWN_DEVICE:
|
2010-02-10 16:04:15 +01:00
|
|
|
message = "The device name you specified seems invalid."; goto param;
|
2009-12-24 20:16:07 +01:00
|
|
|
case XINE_MSG_NETWORK_UNREACHABLE:
|
2010-02-10 16:04:15 +01:00
|
|
|
message = "The network appears unreachable."; goto param;
|
2009-12-24 20:16:07 +01:00
|
|
|
case XINE_MSG_AUDIO_OUT_UNAVAILABLE:
|
2010-02-10 16:04:15 +01:00
|
|
|
message = "Audio output unavailable; the device is busy."; goto param;
|
2009-12-24 20:16:07 +01:00
|
|
|
case XINE_MSG_CONNECTION_REFUSED:
|
2010-02-10 16:04:15 +01:00
|
|
|
message = "The connection was refused for the URL: <i>%1</i>"; goto param;
|
2009-12-24 20:16:07 +01:00
|
|
|
case XINE_MSG_FILE_NOT_FOUND:
|
2010-02-10 16:04:15 +01:00
|
|
|
message = "xine could not find the URL: <i>%1</i>"; goto param;
|
2009-12-24 20:16:07 +01:00
|
|
|
case XINE_MSG_PERMISSION_ERROR:
|
2010-02-10 16:04:15 +01:00
|
|
|
message = "Access was denied for the URL: <i>%1</i>"; goto param;
|
2009-12-24 20:16:07 +01:00
|
|
|
case XINE_MSG_READ_ERROR:
|
2010-02-10 16:04:15 +01:00
|
|
|
message = "The source cannot be read for the URL: <i>%1</i>"; goto param;
|
2009-12-24 20:16:07 +01:00
|
|
|
case XINE_MSG_LIBRARY_LOAD_ERROR:
|
2010-02-10 16:04:15 +01:00
|
|
|
message = "A problem occurred while loading a library or decoder."; goto param;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
case XINE_MSG_GENERAL_WARNING:
|
2010-02-10 16:04:15 +01:00
|
|
|
message = "General Warning"; goto explain;
|
2009-12-24 20:16:07 +01:00
|
|
|
case XINE_MSG_SECURITY:
|
2010-02-10 16:04:15 +01:00
|
|
|
message = "Security Warning"; goto explain;
|
2009-12-24 20:16:07 +01:00
|
|
|
default:
|
2010-02-10 16:04:15 +01:00
|
|
|
message = "Unknown Error"; goto explain;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
|
|
|
|
explain:
|
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
// Don't flood the user with error messages
|
|
|
|
if( (last_error_time + 10) > time( ¤t ) &&
|
|
|
|
data->type == last_error )
|
|
|
|
{
|
2009-12-24 20:16:07 +01:00
|
|
|
last_error_time = current;
|
2010-02-10 16:04:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
last_error_time = current;
|
|
|
|
last_error = data->type;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
if( data->explanation )
|
|
|
|
{
|
|
|
|
message.prepend( "<b>" );
|
|
|
|
message += "</b>:<p>";
|
|
|
|
message += QString::fromUtf8( (char*)data + data->explanation );
|
|
|
|
}
|
|
|
|
else break; //if no explanation then why bother!
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
//FALL THROUGH
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
param:
|
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
// Don't flood the user with error messages
|
|
|
|
if((last_error_time + 10) > time(¤t) &&
|
|
|
|
data->type == last_error)
|
|
|
|
{
|
2009-12-24 20:16:07 +01:00
|
|
|
last_error_time = current;
|
2010-02-10 16:04:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
last_error_time = current;
|
|
|
|
last_error = data->type;
|
|
|
|
|
|
|
|
message.prepend( "<p>" );
|
|
|
|
message += "<p>";
|
|
|
|
|
|
|
|
if(data->explanation)
|
|
|
|
{
|
|
|
|
message += "xine parameters: <i>";
|
|
|
|
message += QString::fromUtf8( (char*)data + data->parameters );
|
|
|
|
message += "</i>";
|
|
|
|
}
|
|
|
|
else message += "Sorry, no additional information is available.";
|
|
|
|
|
|
|
|
QApplication::postEvent( xe, new XineEvent(XineEvent::InfoMessage, new QString(message)) );
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
} //case
|
|
|
|
case XINE_EVENT_UI_CHANNELS_CHANGED: //Flameeyes used this for last.fm track changes
|
|
|
|
QApplication::postEvent( xe, new XineEvent(XineEvent::LastFMTrackChanged) );
|
2010-02-10 16:04:15 +01:00
|
|
|
break;
|
|
|
|
} //switch
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
#undef xe
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Engine::SimpleMetaBundle
|
|
|
|
XineEngine::fetchMetaData() const
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
Engine::SimpleMetaBundle bundle;
|
2010-04-15 14:39:34 +02:00
|
|
|
bundle.title = QString::fromUtf8( xine_get_meta_info( stream_, XINE_META_INFO_TITLE ) );
|
|
|
|
bundle.artist = QString::fromUtf8( xine_get_meta_info( stream_, XINE_META_INFO_ARTIST ) );
|
|
|
|
bundle.album = QString::fromUtf8( xine_get_meta_info( stream_, XINE_META_INFO_ALBUM ) );
|
|
|
|
bundle.comment = QString::fromUtf8( xine_get_meta_info( stream_, XINE_META_INFO_COMMENT ) );
|
|
|
|
bundle.genre = QString::fromUtf8( xine_get_meta_info( stream_, XINE_META_INFO_GENRE ) );
|
|
|
|
bundle.bitrate = QString::number( xine_get_stream_info( stream_, XINE_STREAM_INFO_AUDIO_BITRATE ) / 1000 );
|
|
|
|
bundle.samplerate = QString::number( xine_get_stream_info( stream_, XINE_STREAM_INFO_AUDIO_SAMPLERATE ) );
|
|
|
|
bundle.year = QString::fromUtf8( xine_get_meta_info( stream_, XINE_META_INFO_YEAR ) );
|
|
|
|
bundle.tracknr = QString::fromUtf8( xine_get_meta_info( stream_, XINE_META_INFO_TRACK_NUMBER ) );
|
2010-02-10 16:04:15 +01:00
|
|
|
|
|
|
|
return bundle;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool XineEngine::metaDataForUrl(const QUrl &url, Engine::SimpleMetaBundle &b)
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
bool result = false;
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_stream_t* tmpstream = xine_stream_new(xine_, NULL, NULL);
|
2010-02-10 16:04:15 +01:00
|
|
|
if (xine_open(tmpstream, QFile::encodeName(url.toString()))) {
|
|
|
|
QString audioCodec = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_SYSTEMLAYER));
|
|
|
|
|
|
|
|
if (audioCodec == "CDDA") {
|
|
|
|
QString title = QString::fromUtf8(
|
|
|
|
xine_get_meta_info(tmpstream, XINE_META_INFO_TITLE));
|
|
|
|
if ((!title.isNull()) && (!title.isEmpty())) { //no meta info
|
|
|
|
b.title = title;
|
|
|
|
b.artist =
|
|
|
|
QString::fromUtf8(
|
|
|
|
xine_get_meta_info(tmpstream, XINE_META_INFO_ARTIST));
|
|
|
|
b.album =
|
|
|
|
QString::fromUtf8(
|
|
|
|
xine_get_meta_info(tmpstream, XINE_META_INFO_ALBUM));
|
|
|
|
b.genre =
|
|
|
|
QString::fromUtf8(
|
|
|
|
xine_get_meta_info(tmpstream, XINE_META_INFO_GENRE));
|
|
|
|
b.year =
|
|
|
|
QString::fromUtf8(
|
|
|
|
xine_get_meta_info(tmpstream, XINE_META_INFO_YEAR));
|
|
|
|
b.tracknr =
|
|
|
|
QString::fromUtf8(
|
|
|
|
xine_get_meta_info(tmpstream, XINE_META_INFO_TRACK_NUMBER));
|
|
|
|
if( b.tracknr.isEmpty() )
|
|
|
|
b.tracknr = QFileInfo(url.path()).fileName();
|
|
|
|
} else {
|
|
|
|
b.title = QString("Track %1").arg(QFileInfo(url.path()).fileName());
|
|
|
|
b.album = "AudioCD";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (audioCodec == "CDDA" || audioCodec == "WAV") {
|
|
|
|
result = true;
|
|
|
|
int samplerate = xine_get_stream_info( tmpstream, XINE_STREAM_INFO_AUDIO_SAMPLERATE );
|
|
|
|
|
|
|
|
// xine would provide a XINE_STREAM_INFO_AUDIO_BITRATE, but unfortunately not for CDDA or WAV
|
|
|
|
// so we calculate the bitrate by our own
|
|
|
|
int bitsPerSample = xine_get_stream_info( tmpstream, XINE_STREAM_INFO_AUDIO_BITS );
|
|
|
|
int nbrChannels = xine_get_stream_info( tmpstream, XINE_STREAM_INFO_AUDIO_CHANNELS );
|
|
|
|
int bitrate = (samplerate * bitsPerSample * nbrChannels) / 1000;
|
|
|
|
|
|
|
|
b.bitrate = QString::number(bitrate);
|
|
|
|
b.samplerate = QString::number(samplerate);
|
|
|
|
int pos, time, length = 0;
|
|
|
|
xine_get_pos_length(tmpstream, &pos, &time, &length);
|
|
|
|
b.length = QString::number(length / 1000);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-02-10 16:04:15 +01:00
|
|
|
xine_close(tmpstream);
|
|
|
|
}
|
|
|
|
xine_dispose(tmpstream);
|
|
|
|
return result;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool XineEngine::getAudioCDContents(const QString &device, QList<QUrl> &urls)
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
char **xine_urls = NULL;
|
|
|
|
int num;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
if (!device.isNull()) {
|
|
|
|
qDebug() << "xine-engine setting CD Device to: " << device;
|
|
|
|
xine_cfg_entry_t config;
|
2010-04-15 14:39:34 +02:00
|
|
|
if (!xine_config_lookup_entry(xine_, "input.cdda_device", &config)) {
|
|
|
|
emit StatusText("Failed CD device lookup in xine engine");
|
2010-02-10 16:04:15 +01:00
|
|
|
return false;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-02-10 16:04:15 +01:00
|
|
|
config.str_value = (char *)device.toAscii().constData();
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_config_update_entry(xine_, &config);
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
emit StatusText("Getting AudioCD contents...");
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_urls = xine_get_autoplay_mrls(xine_, "CD", &num);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
if (xine_urls) {
|
|
|
|
while (xine_urls[i]) {
|
|
|
|
urls << QUrl(xine_urls[i]);
|
|
|
|
++i;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
2010-04-15 14:39:34 +02:00
|
|
|
else emit StatusText("Could not read AudioCD");
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
return true;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool XineEngine::flushBuffer()
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
return false;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool XineEngine::lastFmProxyRequired()
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
return !( xine_check_version(1,1,9) );
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// class Fader
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
Fader::Fader( XineEngine *engine, uint fadeMs )
|
2010-02-10 16:04:15 +01:00
|
|
|
: QThread(engine)
|
2010-04-15 14:39:34 +02:00
|
|
|
, engine_( engine )
|
|
|
|
, xine_( engine->xine_ )
|
|
|
|
, decrease_( engine->stream_ )
|
|
|
|
, increase_( 0 )
|
|
|
|
, port_( engine->audioPort_ )
|
|
|
|
, post_( engine->post_ )
|
|
|
|
, fadeLength_( fadeMs )
|
|
|
|
, paused_( false )
|
|
|
|
, terminated_( false )
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
if( engine->makeNewStream() )
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
increase_ = engine->stream_;
|
2010-02-10 16:04:15 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_set_param( increase_, XINE_PARAM_AUDIO_AMP_LEVEL, 0 );
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
s_fader = 0;
|
|
|
|
deleteLater();
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Fader::~Fader()
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
wait();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_close( decrease_ );
|
|
|
|
xine_dispose( decrease_ );
|
|
|
|
xine_close_audio_driver( xine_, port_ );
|
|
|
|
if( post_ ) xine_post_dispose( xine_, post_ );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if( !engine_->stopFader_ )
|
|
|
|
engine_->SetVolume( engine_->volume() );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
engine_->stopFader_ = false;
|
2010-02-10 16:04:15 +01:00
|
|
|
s_fader = 0;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Fader::run()
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
// do a volume change in 100 steps (or every 10ms)
|
2010-04-15 14:39:34 +02:00
|
|
|
uint stepsCount = fadeLength_ < 1000 ? fadeLength_ / 10 : 100;
|
|
|
|
uint stepSizeUs = (int)( 1000.0 * (float)fadeLength_ / (float)stepsCount );
|
2010-02-10 16:04:15 +01:00
|
|
|
|
|
|
|
float mix = 0.0;
|
|
|
|
float elapsedUs = 0.0;
|
|
|
|
while ( mix < 1.0 )
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( terminated_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
break;
|
|
|
|
// sleep a constant amount of time
|
|
|
|
QThread::usleep( stepSizeUs );
|
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( paused_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
elapsedUs += stepSizeUs;
|
|
|
|
|
|
|
|
// get volume (amarok main * equalizer preamp)
|
2010-04-15 14:39:34 +02:00
|
|
|
float vol = Engine::Base::MakeVolumeLogarithmic( engine_->volume_ ) * engine_->preamp_;
|
2010-02-10 16:04:15 +01:00
|
|
|
|
|
|
|
// compute the mix factor as the percentage of time spent since fade begun
|
2010-04-15 14:39:34 +02:00
|
|
|
float mix = (elapsedUs / 1000.0) / (float)fadeLength_;
|
2010-02-10 16:04:15 +01:00
|
|
|
if ( mix > 1.0 )
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( increase_ )
|
|
|
|
xine_set_param( increase_, XINE_PARAM_AUDIO_AMP_LEVEL, (uint)vol );
|
2010-02-10 16:04:15 +01:00
|
|
|
break;
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
// change volume of streams (using dj-like cross-fade profile)
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( decrease_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
//xine_set_param( decrease_, XINE_PARAM_AUDIO_AMP_LEVEL, (uint)(vol * (1.0 - mix)) ); // linear
|
2010-02-10 16:04:15 +01:00
|
|
|
float v = 4.0 * (1.0 - mix) / 3.0;
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_set_param( decrease_, XINE_PARAM_AUDIO_AMP_LEVEL, (uint)( v < 1.0 ? vol * v : vol ) );
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-04-15 14:39:34 +02:00
|
|
|
if ( increase_ )
|
2010-02-10 16:04:15 +01:00
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
//xine_set_param( increase_, XINE_PARAM_AUDIO_AMP_LEVEL, (uint)(vol * mix) ); //linear
|
2010-02-10 16:04:15 +01:00
|
|
|
float v = 4.0 * mix / 3.0;
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_set_param( increase_, XINE_PARAM_AUDIO_AMP_LEVEL, (uint)( v < 1.0 ? vol * v : vol ) );
|
2010-02-10 16:04:15 +01:00
|
|
|
}
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
//stop using cpu!
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_stop( decrease_ );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
deleteLater();
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
void
|
2009-12-24 20:16:07 +01:00
|
|
|
Fader::pause()
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
paused_ = true;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
void
|
2009-12-24 20:16:07 +01:00
|
|
|
Fader::resume()
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
paused_ = false;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
void
|
2009-12-24 20:16:07 +01:00
|
|
|
Fader::finish()
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
terminated_ = true;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// class OutFader
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
OutFader::OutFader( XineEngine *engine, uint fadeLength )
|
2010-02-10 16:04:15 +01:00
|
|
|
: QThread(engine)
|
2010-04-15 14:39:34 +02:00
|
|
|
, engine_( engine )
|
|
|
|
, terminated_( false )
|
|
|
|
, fadeLength_( fadeLength )
|
2009-12-24 20:16:07 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
OutFader::~OutFader()
|
|
|
|
{
|
2010-02-10 16:04:15 +01:00
|
|
|
wait();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
s_outfader = 0;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OutFader::run()
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
engine_->fadeOut( fadeLength_, &terminated_ );
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-15 14:39:34 +02:00
|
|
|
xine_stop( engine_->stream_ );
|
|
|
|
xine_close( engine_->stream_ );
|
|
|
|
xine_set_param( engine_->stream_, XINE_PARAM_AUDIO_CLOSE_DEVICE, 1);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-10 16:04:15 +01:00
|
|
|
deleteLater();
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OutFader::finish()
|
|
|
|
{
|
2010-04-15 14:39:34 +02:00
|
|
|
terminated_ = true;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-03-22 00:11:34 +01:00
|
|
|
|
|
|
|
PruneScopeThread::PruneScopeThread(XineEngine *parent)
|
|
|
|
: engine_(parent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void PruneScopeThread::run() {
|
2010-03-29 16:03:05 +02:00
|
|
|
QTimer timer;
|
|
|
|
connect(&timer, SIGNAL(timeout()), engine_, SLOT(PruneScope()), Qt::DirectConnection);
|
|
|
|
timer.start(1000);
|
2010-03-22 00:11:34 +01:00
|
|
|
|
|
|
|
exec();
|
|
|
|
}
|