2007-02-05 00:49:40 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* Copyright (C) 2007 by Pino Toscano <pino@kde.org> *
|
|
|
|
* *
|
|
|
|
* 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. *
|
|
|
|
***************************************************************************/
|
|
|
|
|
2007-04-19 18:30:20 +00:00
|
|
|
#include "audioplayer.h"
|
|
|
|
|
2007-02-05 00:49:40 +00:00
|
|
|
// qt/kde includes
|
2007-05-01 11:25:32 +00:00
|
|
|
#include <qbuffer.h>
|
2007-02-05 00:49:40 +00:00
|
|
|
#include <qhash.h>
|
|
|
|
#include <qsignalmapper.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <krandom.h>
|
2007-02-06 00:53:24 +00:00
|
|
|
#include <kurl.h>
|
2007-02-05 00:49:40 +00:00
|
|
|
#include <phonon/audiopath.h>
|
|
|
|
#include <phonon/audiooutput.h>
|
2007-05-01 05:28:28 +00:00
|
|
|
#include <phonon/abstractmediastream.h>
|
2007-02-05 00:49:40 +00:00
|
|
|
#include <phonon/mediaobject.h>
|
|
|
|
|
|
|
|
// local includes
|
2007-04-20 11:26:05 +00:00
|
|
|
#include "action.h"
|
2007-02-05 00:49:40 +00:00
|
|
|
#include "sound.h"
|
|
|
|
|
|
|
|
using namespace Okular;
|
|
|
|
|
|
|
|
// helper class used to store info about a sound to be played
|
|
|
|
class SoundInfo
|
|
|
|
{
|
|
|
|
public:
|
2007-04-20 12:37:12 +00:00
|
|
|
explicit SoundInfo( const Sound * s = 0, const ActionSound * ls = 0 )
|
2007-02-05 00:49:40 +00:00
|
|
|
: sound( s ), volume( 0.5 ), synchronous( false ), repeat( false ),
|
|
|
|
mix( false )
|
|
|
|
{
|
|
|
|
if ( ls )
|
|
|
|
{
|
|
|
|
volume = ls->volume();
|
|
|
|
synchronous = ls->synchronous();
|
|
|
|
repeat = ls->repeat();
|
|
|
|
mix = ls->mix();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Sound * sound;
|
|
|
|
double volume;
|
|
|
|
bool synchronous;
|
|
|
|
bool repeat;
|
|
|
|
bool mix;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2007-02-05 12:02:53 +00:00
|
|
|
class PlayData
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
|
|
|
public:
|
2007-02-05 12:02:53 +00:00
|
|
|
PlayData()
|
2007-05-01 11:25:32 +00:00
|
|
|
: m_mediaobject( 0 ), m_path( 0 ), m_output( 0 ), m_buffer( 0 )
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-02-05 15:26:05 +00:00
|
|
|
void play()
|
|
|
|
{
|
2007-05-01 11:25:32 +00:00
|
|
|
if ( m_buffer )
|
2007-02-05 15:26:05 +00:00
|
|
|
{
|
2007-05-01 11:25:32 +00:00
|
|
|
m_buffer->open( QIODevice::ReadOnly );
|
2007-02-05 15:26:05 +00:00
|
|
|
}
|
2007-05-01 11:25:32 +00:00
|
|
|
m_mediaobject->play();
|
2007-02-05 15:26:05 +00:00
|
|
|
}
|
|
|
|
|
2007-02-05 12:02:53 +00:00
|
|
|
~PlayData()
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
2007-05-01 11:25:32 +00:00
|
|
|
m_mediaobject->stop();
|
|
|
|
delete m_mediaobject;
|
2007-02-05 00:49:40 +00:00
|
|
|
delete m_path;
|
|
|
|
delete m_output;
|
2007-05-01 11:25:32 +00:00
|
|
|
delete m_buffer;
|
2007-02-05 00:49:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Phonon::MediaObject * m_mediaobject;
|
|
|
|
Phonon::AudioPath * m_path;
|
|
|
|
Phonon::AudioOutput * m_output;
|
2007-05-01 11:25:32 +00:00
|
|
|
QBuffer * m_buffer;
|
2007-02-05 00:49:40 +00:00
|
|
|
SoundInfo m_info;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class AudioPlayer::Private
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Private( AudioPlayer * qq )
|
|
|
|
: q( qq )
|
|
|
|
{
|
|
|
|
connect( &m_mapper, SIGNAL( mapped( int ) ), q, SLOT( finished( int ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
~Private()
|
|
|
|
{
|
2007-02-05 11:50:07 +00:00
|
|
|
stopPlayings();
|
2007-02-05 00:49:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int newId() const;
|
|
|
|
bool play( const SoundInfo& si );
|
2007-02-05 11:50:07 +00:00
|
|
|
void stopPlayings();
|
2007-02-05 00:49:40 +00:00
|
|
|
|
|
|
|
// private slots
|
|
|
|
void finished( int );
|
|
|
|
|
|
|
|
AudioPlayer * q;
|
|
|
|
|
2007-02-05 12:02:53 +00:00
|
|
|
QHash< int, PlayData * > m_playing;
|
2007-02-05 00:49:40 +00:00
|
|
|
QSignalMapper m_mapper;
|
|
|
|
};
|
|
|
|
|
|
|
|
int AudioPlayer::Private::newId() const
|
|
|
|
{
|
|
|
|
int newid = 0;
|
2007-02-05 12:02:53 +00:00
|
|
|
QHash< int, PlayData * >::const_iterator it;
|
|
|
|
QHash< int, PlayData * >::const_iterator itEnd = m_playing.constEnd();
|
2007-02-05 00:49:40 +00:00
|
|
|
do
|
|
|
|
{
|
|
|
|
newid = KRandom::random();
|
|
|
|
it = m_playing.constFind( newid );
|
|
|
|
} while ( it != itEnd );
|
|
|
|
return newid;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AudioPlayer::Private::play( const SoundInfo& si )
|
|
|
|
{
|
|
|
|
kDebug() << k_funcinfo << endl;
|
2007-02-05 12:02:53 +00:00
|
|
|
PlayData * data = new PlayData();
|
|
|
|
data->m_output = new Phonon::AudioOutput( Phonon::NotificationCategory );
|
|
|
|
data->m_output->setVolume( si.volume );
|
|
|
|
data->m_path = new Phonon::AudioPath();
|
|
|
|
data->m_path->addOutput( data->m_output );
|
2007-05-01 11:25:32 +00:00
|
|
|
data->m_mediaobject = new Phonon::MediaObject();
|
2007-02-05 12:02:53 +00:00
|
|
|
data->m_info = si;
|
2007-02-05 00:49:40 +00:00
|
|
|
bool valid = false;
|
|
|
|
|
|
|
|
switch ( si.sound->soundType() )
|
|
|
|
{
|
|
|
|
case Sound::External:
|
|
|
|
{
|
|
|
|
QString url = si.sound->url();
|
|
|
|
kDebug() << "[AudioPlayer::Playinfo::play()] External, " << url << endl;
|
|
|
|
if ( !url.isEmpty() )
|
|
|
|
{
|
2007-02-05 12:02:53 +00:00
|
|
|
if ( data->m_mediaobject->addAudioPath( data->m_path ) )
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
|
|
|
int newid = newId();
|
2007-02-05 12:02:53 +00:00
|
|
|
m_mapper.setMapping( data->m_mediaobject, newid );
|
2007-05-01 05:28:28 +00:00
|
|
|
data->m_mediaobject->setCurrentSource( url );
|
2007-02-05 12:02:53 +00:00
|
|
|
m_playing.insert( newid, data );
|
2007-02-05 00:49:40 +00:00
|
|
|
valid = true;
|
|
|
|
kDebug() << "[AudioPlayer::Playinfo::play()] PLAY url" << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Sound::Embedded:
|
|
|
|
{
|
|
|
|
#if 0 // disable because of broken bytestream in xine :(
|
2007-02-05 12:02:53 +00:00
|
|
|
QByteArray filedata = si.sound->data();
|
2007-02-05 12:05:20 +00:00
|
|
|
kDebug() << "[AudioPlayer::Playinfo::play()] Embedded, " << filedata.length() << endl;
|
2007-02-05 12:02:53 +00:00
|
|
|
if ( !filedata.isEmpty() )
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
2007-02-05 12:02:53 +00:00
|
|
|
kDebug() << "[AudioPlayer::Playinfo::play()] bytestream: " << data->m_bytestream << endl;
|
|
|
|
if ( data->m_bytestream->addAudioPath( data->m_path ) )
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
|
|
|
int newid = newId();
|
2007-02-05 12:02:53 +00:00
|
|
|
m_mapper.setMapping( data->m_mediaobject, newid );
|
2007-05-01 11:25:32 +00:00
|
|
|
data->m_buffer = new QBuffer();
|
|
|
|
data->m_buffer->setData( filedata );
|
|
|
|
data->m_mediaobject->setCurrentSource( Phonon::MediaSource( data->m_buffer ) );
|
2007-02-05 12:02:53 +00:00
|
|
|
m_playing.insert( newid, data );
|
2007-02-05 00:49:40 +00:00
|
|
|
valid = true;
|
|
|
|
kDebug() << "[AudioPlayer::Playinfo::play()] PLAY data" << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( !valid )
|
|
|
|
{
|
2007-02-05 12:02:53 +00:00
|
|
|
delete data;
|
|
|
|
data = 0;
|
2007-02-05 00:49:40 +00:00
|
|
|
}
|
2007-02-05 15:26:05 +00:00
|
|
|
if ( data )
|
|
|
|
{
|
2007-05-01 11:25:32 +00:00
|
|
|
connect( data->m_mediaobject, SIGNAL( finished() ), &m_mapper, SLOT( map() ) );
|
2007-02-05 15:26:05 +00:00
|
|
|
data->play();
|
|
|
|
}
|
2007-02-05 00:49:40 +00:00
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
2007-02-05 11:50:07 +00:00
|
|
|
void AudioPlayer::Private::stopPlayings()
|
|
|
|
{
|
|
|
|
qDeleteAll( m_playing );
|
|
|
|
m_playing.clear();
|
|
|
|
}
|
2007-02-05 00:49:40 +00:00
|
|
|
|
|
|
|
void AudioPlayer::Private::finished( int id )
|
|
|
|
{
|
2007-02-05 12:02:53 +00:00
|
|
|
QHash< int, PlayData * >::iterator it = m_playing.find( id );
|
2007-02-05 00:49:40 +00:00
|
|
|
if ( it == m_playing.end() )
|
|
|
|
return;
|
|
|
|
|
2007-02-05 15:26:05 +00:00
|
|
|
SoundInfo si = it.value()->m_info;
|
|
|
|
// if the sound must be repeated indefinitely, then start the playback
|
|
|
|
// again, otherwise destroy the PlayData as it's no more useful
|
|
|
|
if ( si.repeat )
|
|
|
|
{
|
|
|
|
it.value()->play();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete it.value();
|
|
|
|
m_playing.erase( it );
|
|
|
|
}
|
2007-02-05 00:49:40 +00:00
|
|
|
kDebug() << k_funcinfo << "finished, " << m_playing.count() << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AudioPlayer::AudioPlayer()
|
|
|
|
: QObject(), d( new Private( this ) )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioPlayer::~AudioPlayer()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioPlayer * AudioPlayer::instance()
|
|
|
|
{
|
|
|
|
static AudioPlayer ap;
|
|
|
|
return ≈
|
|
|
|
}
|
|
|
|
|
2007-04-20 12:37:12 +00:00
|
|
|
void AudioPlayer::playSound( const Sound * sound, const ActionSound * linksound )
|
2007-02-05 00:49:40 +00:00
|
|
|
{
|
|
|
|
// we can't play null pointers ;)
|
|
|
|
if ( !sound )
|
|
|
|
return;
|
|
|
|
|
|
|
|
kDebug() << k_funcinfo << endl;
|
|
|
|
SoundInfo si( sound, linksound );
|
|
|
|
|
2007-02-05 11:50:07 +00:00
|
|
|
// if the mix flag of the new sound is false, then the currently playing
|
|
|
|
// sounds must be stopped.
|
|
|
|
if ( !si.mix )
|
|
|
|
d->stopPlayings();
|
|
|
|
|
2007-02-05 00:49:40 +00:00
|
|
|
d->play( si );
|
|
|
|
}
|
|
|
|
|
2007-02-05 15:26:05 +00:00
|
|
|
void AudioPlayer::stopPlaybacks()
|
|
|
|
{
|
|
|
|
d->stopPlayings();
|
|
|
|
}
|
|
|
|
|
2007-02-05 00:49:40 +00:00
|
|
|
#include "audioplayer.moc"
|