mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-04-15 13:32:35 +02:00
* Initial work on phonon-powered AudioEngine.
This commit is contained in:
parent
a4e140972e
commit
41512c4b61
@ -7,6 +7,8 @@ ENDIF()
|
||||
SET( QT_USE_QTSQL TRUE )
|
||||
SET( QT_USE_QTNETWORK TRUE )
|
||||
SET( QT_USE_QTXML TRUE )
|
||||
SET( QT_USE_PHONON TRUE )
|
||||
|
||||
INCLUDE( ${QT_USE_FILE} )
|
||||
|
||||
INCLUDE( ${CMAKE_MODULE_PATH}/AddAppIconMacro.cmake )
|
||||
@ -27,9 +29,6 @@ SET( TOMAHAWK_INC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../include/" )
|
||||
#ENDFOREACH( moddir )
|
||||
|
||||
SET( tomahawkSources ${tomahawkSources}
|
||||
audio/madtranscode.cpp
|
||||
audio/vorbistranscode.cpp
|
||||
audio/flactranscode.cpp
|
||||
audio/audioengine.cpp
|
||||
|
||||
utils/tomahawkutils.cpp
|
||||
@ -113,9 +112,6 @@ SET( tomahawkHeaders ${tomahawkHeaders}
|
||||
"${TOMAHAWK_INC_DIR}/tomahawk/infosystem.h"
|
||||
|
||||
audio/transcodeinterface.h
|
||||
audio/madtranscode.h
|
||||
audio/vorbistranscode.h
|
||||
audio/flactranscode.h
|
||||
audio/audioengine.h
|
||||
|
||||
sip/SipHandler.h
|
||||
@ -215,7 +211,6 @@ INCLUDE_DIRECTORIES(
|
||||
utils
|
||||
libtomahawk
|
||||
|
||||
../rtaudio
|
||||
../alsa-playback
|
||||
../qxt/qxtweb-standalone/qxtweb
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "audioengine.h"
|
||||
|
||||
#include <QUrl>
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include "playlistinterface.h"
|
||||
|
||||
@ -9,47 +8,36 @@
|
||||
#include "database/databasecommand_logplayback.h"
|
||||
#include "network/servent.h"
|
||||
|
||||
#include "madtranscode.h"
|
||||
#ifndef NO_OGG
|
||||
#include "vorbistranscode.h"
|
||||
#endif
|
||||
#ifndef NO_FLAC
|
||||
#include "flactranscode.h"
|
||||
#endif
|
||||
|
||||
|
||||
AudioEngine::AudioEngine()
|
||||
: QThread()
|
||||
: QObject()
|
||||
, m_playlist( 0 )
|
||||
, m_currentTrackPlaylist( 0 )
|
||||
, m_queue( 0 )
|
||||
, m_timeElapsed( 0 )
|
||||
, m_i( 0 )
|
||||
{
|
||||
qDebug() << "Init AudioEngine";
|
||||
|
||||
moveToThread( this );
|
||||
qRegisterMetaType< AudioErrorCode >("AudioErrorCode");
|
||||
|
||||
#ifdef Q_WS_X11
|
||||
m_audio = new AlsaPlayback();
|
||||
#else
|
||||
m_audio = new RTAudioOutput();
|
||||
#endif
|
||||
connect( m_audio, SIGNAL( timeElapsed( unsigned int ) ), SLOT( timerTriggered( unsigned int ) ), Qt::DirectConnection );
|
||||
m_mediaObject = new Phonon::MediaObject( this );
|
||||
m_audioOutput = new Phonon::AudioOutput( Phonon::MusicCategory, this );
|
||||
Phonon::createPath( m_mediaObject, m_audioOutput );
|
||||
|
||||
start();
|
||||
// connect( m_audio, SIGNAL( timeElapsed( unsigned int ) ), SLOT( timerTriggered( unsigned int ) ), Qt::DirectConnection );
|
||||
}
|
||||
|
||||
|
||||
AudioEngine::~AudioEngine()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << "waiting for event loop to finish...";
|
||||
quit();
|
||||
wait( 1000 );
|
||||
|
||||
m_mediaObject->stop();
|
||||
|
||||
delete m_audioOutput;
|
||||
delete m_mediaObject;
|
||||
|
||||
m_input.clear();
|
||||
delete m_audio;
|
||||
}
|
||||
|
||||
|
||||
@ -58,10 +46,9 @@ AudioEngine::play()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
if ( m_audio->isPaused() )
|
||||
if ( isPaused() )
|
||||
{
|
||||
QMutexLocker lock( &m_mutex );
|
||||
m_audio->resume();
|
||||
m_mediaObject->play();
|
||||
emit resumed();
|
||||
}
|
||||
else
|
||||
@ -73,9 +60,8 @@ void
|
||||
AudioEngine::pause()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
QMutexLocker lock( &m_mutex );
|
||||
|
||||
m_audio->pause();
|
||||
m_mediaObject->pause();
|
||||
emit paused();
|
||||
}
|
||||
|
||||
@ -84,7 +70,8 @@ void
|
||||
AudioEngine::stop()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
QMutexLocker lock( &m_mutex );
|
||||
|
||||
m_mediaObject->stop();
|
||||
|
||||
if ( !m_input.isNull() )
|
||||
{
|
||||
@ -92,11 +79,6 @@ AudioEngine::stop()
|
||||
m_input.clear();
|
||||
}
|
||||
|
||||
if ( !m_transcode.isNull() )
|
||||
m_transcode->clearBuffers();
|
||||
|
||||
m_audio->stopPlayback();
|
||||
|
||||
setCurrentTrack( Tomahawk::result_ptr() );
|
||||
emit stopped();
|
||||
}
|
||||
@ -106,7 +88,6 @@ void
|
||||
AudioEngine::previous()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
clearBuffers();
|
||||
loadPreviousTrack();
|
||||
}
|
||||
|
||||
@ -115,7 +96,6 @@ void
|
||||
AudioEngine::next()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
clearBuffers();
|
||||
loadNextTrack();
|
||||
}
|
||||
|
||||
@ -127,7 +107,7 @@ AudioEngine::setVolume( int percentage )
|
||||
|
||||
percentage = qBound( 0, percentage, 100 );
|
||||
|
||||
m_audio->setVolume( percentage );
|
||||
m_audioOutput->setVolume( (qreal)percentage / 100.0 );
|
||||
emit volumeChanged( percentage );
|
||||
}
|
||||
|
||||
@ -149,9 +129,7 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
|
||||
qDebug() << Q_FUNC_INFO << thread() << result;
|
||||
bool err = false;
|
||||
|
||||
// in a separate scope due to the QMutexLocker!
|
||||
{
|
||||
QMutexLocker lock( &m_mutex );
|
||||
QSharedPointer<QIODevice> io;
|
||||
|
||||
if ( result.isNull() )
|
||||
@ -181,44 +159,15 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
|
||||
m_input.clear();
|
||||
}
|
||||
|
||||
if ( m_lastTrack.isNull() || ( m_currentTrack->mimetype() != m_lastTrack->mimetype() ) )
|
||||
{
|
||||
if ( !m_transcode.isNull() )
|
||||
{
|
||||
m_transcode.clear();
|
||||
}
|
||||
m_input = io;
|
||||
|
||||
if ( m_currentTrack->mimetype() == "audio/mpeg" )
|
||||
{
|
||||
m_transcode = QSharedPointer<TranscodeInterface>(new MADTranscode());
|
||||
}
|
||||
#ifndef NO_OGG
|
||||
else if ( m_currentTrack->mimetype() == "application/ogg" )
|
||||
{
|
||||
m_transcode = QSharedPointer<TranscodeInterface>(new VorbisTranscode());
|
||||
}
|
||||
#endif
|
||||
#ifndef NO_FLAC
|
||||
else if ( m_currentTrack->mimetype() == "audio/flac" )
|
||||
{
|
||||
m_transcode = QSharedPointer<TranscodeInterface>(new FLACTranscode());
|
||||
}
|
||||
#endif
|
||||
else
|
||||
qDebug() << "Could NOT find suitable transcoder! Stopping audio.";
|
||||
m_mediaObject->setCurrentSource( io.data() ) ;
|
||||
m_mediaObject->play();
|
||||
|
||||
if ( !m_transcode.isNull() )
|
||||
connect( m_transcode.data(), SIGNAL( streamInitialized( long, int ) ), SLOT( setStreamData( long, int ) ), Qt::DirectConnection );
|
||||
}
|
||||
emit started( m_currentTrack );
|
||||
|
||||
if ( !m_transcode.isNull() )
|
||||
{
|
||||
m_transcode->clearBuffers();
|
||||
m_input = io;
|
||||
|
||||
if ( m_audio->isPaused() )
|
||||
m_audio->resume();
|
||||
}
|
||||
DatabaseCommand_LogPlayback* cmd = new DatabaseCommand_LogPlayback( m_currentTrack, DatabaseCommand_LogPlayback::Started );
|
||||
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,14 +177,7 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
|
||||
return false;
|
||||
}
|
||||
|
||||
// needs to be out of the mutexlocker scope
|
||||
if ( m_transcode.isNull() )
|
||||
{
|
||||
stop();
|
||||
emit error( AudioEngine::DecodeError );
|
||||
}
|
||||
|
||||
return !m_transcode.isNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -287,8 +229,6 @@ AudioEngine::playItem( PlaylistInterface* playlist, const Tomahawk::result_ptr&
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
clearBuffers();
|
||||
|
||||
m_playlist = playlist;
|
||||
m_currentTrackPlaylist = playlist;
|
||||
|
||||
@ -296,33 +236,6 @@ AudioEngine::playItem( PlaylistInterface* playlist, const Tomahawk::result_ptr&
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioEngine::setStreamData( long sampleRate, int channels )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << sampleRate << channels << thread();
|
||||
|
||||
if ( sampleRate < 44100 )
|
||||
sampleRate = 44100;
|
||||
|
||||
m_audio->initAudio( sampleRate, channels );
|
||||
if ( m_audio->startPlayback() )
|
||||
{
|
||||
emit started( m_currentTrack );
|
||||
|
||||
DatabaseCommand_LogPlayback* cmd = new DatabaseCommand_LogPlayback( m_currentTrack, DatabaseCommand_LogPlayback::Started );
|
||||
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Can't open device for audio output!";
|
||||
stop();
|
||||
emit error( AudioEngine::AudioDeviceError );
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << sampleRate << channels << "done";
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioEngine::timerTriggered( unsigned int seconds )
|
||||
{
|
||||
@ -340,14 +253,6 @@ AudioEngine::timerTriggered( unsigned int seconds )
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioEngine::clearBuffers()
|
||||
{
|
||||
QMutexLocker lock( &m_mutex );
|
||||
m_audio->clearBuffers();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioEngine::setCurrentTrack( const Tomahawk::result_ptr& result )
|
||||
{
|
||||
@ -362,88 +267,3 @@ AudioEngine::setCurrentTrack( const Tomahawk::result_ptr& result )
|
||||
|
||||
m_currentTrack = result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioEngine::run()
|
||||
{
|
||||
QTimer::singleShot( 0, this, SLOT( engineLoop() ) );
|
||||
exec();
|
||||
qDebug() << "AudioEngine event loop stopped";
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioEngine::engineLoop()
|
||||
{
|
||||
qDebug() << "AudioEngine thread:" << this->thread();
|
||||
loop();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioEngine::loop()
|
||||
{
|
||||
m_i++;
|
||||
//if( m_i % 500 == 0 ) qDebug() << Q_FUNC_INFO << thread();
|
||||
|
||||
{
|
||||
QMutexLocker lock( &m_mutex );
|
||||
|
||||
/* if ( m_i % 200 == 0 )
|
||||
{
|
||||
if ( !m_input.isNull() )
|
||||
qDebug() << "Outer audio loop" << m_input->bytesAvailable() << m_audio->needData();
|
||||
}*/
|
||||
|
||||
if ( m_i % 10 == 0 && m_audio->isPlaying() )
|
||||
m_audio->triggerTimers();
|
||||
|
||||
if( !m_transcode.isNull() &&
|
||||
!m_input.isNull() &&
|
||||
m_input->bytesAvailable() &&
|
||||
m_audio->needData() &&
|
||||
!m_audio->isPaused() )
|
||||
{
|
||||
//if ( m_i % 50 == 0 )
|
||||
// qDebug() << "Inner audio loop";
|
||||
|
||||
if ( m_transcode->needData() > 0 )
|
||||
{
|
||||
QByteArray encdata = m_input->read( m_transcode->preferredDataSize() );
|
||||
m_transcode->processData( encdata, m_input->atEnd() );
|
||||
}
|
||||
|
||||
if ( m_transcode->haveData() )
|
||||
{
|
||||
QByteArray rawdata = m_transcode->data();
|
||||
m_audio->processData( rawdata );
|
||||
}
|
||||
|
||||
QTimer::singleShot( 0, this, SLOT( loop() ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int nextdelay = 50;
|
||||
// are we cleanly at the end of a track, and ready for the next one?
|
||||
if ( !m_input.isNull() &&
|
||||
m_input->atEnd() &&
|
||||
!m_input->bytesAvailable() &&
|
||||
!m_audio->haveData() &&
|
||||
!m_audio->isPaused() )
|
||||
{
|
||||
qDebug() << "Starting next track then";
|
||||
loadNextTrack();
|
||||
// will need data immediately:
|
||||
nextdelay = 0;
|
||||
}
|
||||
else if ( !m_input.isNull() && !m_input->isOpen() )
|
||||
{
|
||||
qDebug() << "AudioEngine IODev closed. errorString:" << m_input->errorString();
|
||||
loadNextTrack();
|
||||
nextdelay = 0;
|
||||
}
|
||||
|
||||
QTimer::singleShot( nextdelay, this, SLOT( loop() ) );
|
||||
}
|
||||
|
@ -1,22 +1,21 @@
|
||||
#ifndef AUDIOENGINE_H
|
||||
#define AUDIOENGINE_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QBuffer>
|
||||
#include <QObject>
|
||||
|
||||
#include <Phonon/MediaObject>
|
||||
#include <Phonon/AudioOutput>
|
||||
|
||||
#include "result.h"
|
||||
#include "typedefs.h"
|
||||
|
||||
#include "rtaudiooutput.h"
|
||||
#include "alsaplayback.h"
|
||||
#include "transcodeinterface.h"
|
||||
|
||||
#define AUDIO_VOLUME_STEP 5
|
||||
|
||||
class PlaylistInterface;
|
||||
|
||||
class AudioEngine : public QThread
|
||||
class AudioEngine : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@ -26,8 +25,8 @@ public:
|
||||
explicit AudioEngine();
|
||||
~AudioEngine();
|
||||
|
||||
unsigned int volume() const { if ( m_audio ) return m_audio->volume() * 100.0; else return 0; }; // in percent
|
||||
bool isPaused() const { return m_audio->isPaused(); }
|
||||
unsigned int volume() const { return m_audioOutput->volume() * 100.0; } // in percent
|
||||
bool isPaused() const { return m_mediaObject->state() == Phonon::PausedState; }
|
||||
|
||||
/* Returns the PlaylistInterface of the currently playing track. Note: This might be different to the current playlist! */
|
||||
PlaylistInterface* currentTrackPlaylist() const { return m_currentTrackPlaylist; }
|
||||
@ -74,36 +73,23 @@ private slots:
|
||||
void loadPreviousTrack();
|
||||
void loadNextTrack();
|
||||
|
||||
void setStreamData( long sampleRate, int channels );
|
||||
void timerTriggered( unsigned int seconds );
|
||||
|
||||
void engineLoop();
|
||||
void loop();
|
||||
|
||||
void setCurrentTrack( const Tomahawk::result_ptr& result );
|
||||
|
||||
private:
|
||||
void run();
|
||||
void clearBuffers();
|
||||
|
||||
QSharedPointer<QIODevice> m_input;
|
||||
QSharedPointer<TranscodeInterface> m_transcode;
|
||||
|
||||
#ifdef Q_WS_X11
|
||||
AlsaPlayback* m_audio;
|
||||
#else
|
||||
RTAudioOutput* m_audio;
|
||||
#endif
|
||||
|
||||
Tomahawk::result_ptr m_currentTrack;
|
||||
Tomahawk::result_ptr m_lastTrack;
|
||||
PlaylistInterface* m_playlist;
|
||||
PlaylistInterface* m_currentTrackPlaylist;
|
||||
PlaylistInterface* m_queue;
|
||||
QMutex m_mutex;
|
||||
|
||||
Phonon::MediaObject* m_mediaObject;
|
||||
Phonon::AudioOutput* m_audioOutput;
|
||||
|
||||
unsigned int m_timeElapsed;
|
||||
int m_i;
|
||||
};
|
||||
|
||||
#endif // AUDIOENGINE_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user