diff --git a/CMakeLists.txt b/CMakeLists.txt index a51e6296b..9c615e4a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ include_directories( set(okularcore_SRCS core/annotations.cpp core/area.cpp + core/audioplayer.cpp core/bookmarkmanager.cpp core/chooseenginedialog.cpp core/document.cpp @@ -87,7 +88,7 @@ IF(APPLE) SET(OKULAR_IOKIT "-framework IOKit" CACHE STRING "Apple IOKit framework") ENDIF(APPLE) -target_link_libraries(okularcore ${OKULAR_IOKIT} ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBRARY} ${KDE4_KDEPRINT_LIBS} m ) +target_link_libraries(okularcore ${OKULAR_IOKIT} ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBRARY} ${KDE4_KDEPRINT_LIBS} ${KDE4_PHONONCORE_LIBRARY} m ) install(TARGETS okularcore DESTINATION ${LIB_INSTALL_DIR} ) diff --git a/core/audioplayer.cpp b/core/audioplayer.cpp new file mode 100644 index 000000000..ccf467730 --- /dev/null +++ b/core/audioplayer.cpp @@ -0,0 +1,235 @@ +/*************************************************************************** + * Copyright (C) 2007 by Pino Toscano * + * * + * 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. * + ***************************************************************************/ + +// qt/kde includes +#include +#include +#include +#include +#include +#include +#include +#include + +// local includes +#include "audioplayer.h" +#include "link.h" +#include "sound.h" + +using namespace Okular; + +// helper class used to store info about a sound to be played +class SoundInfo +{ +public: + explicit SoundInfo( const Sound * s = 0, const LinkSound * ls = 0 ) + : 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; +}; + + +class PlayInfo +{ +public: + PlayInfo() + : m_mediaobject( 0 ), m_bytestream( 0 ), m_path( 0 ), m_output( 0 ) + { + } + + ~PlayInfo() + { + if ( m_bytestream ) + { + m_bytestream->stop(); + delete m_bytestream; + } + if ( m_mediaobject ) + { + m_mediaobject->stop(); + delete m_mediaobject; + } + delete m_path; + delete m_output; + } + + Phonon::MediaObject * m_mediaobject; + Phonon::ByteStream * m_bytestream; + Phonon::AudioPath * m_path; + Phonon::AudioOutput * m_output; + SoundInfo m_info; +}; + + +class AudioPlayer::Private +{ +public: + Private( AudioPlayer * qq ) + : q( qq ) + { + connect( &m_mapper, SIGNAL( mapped( int ) ), q, SLOT( finished( int ) ) ); + } + + ~Private() + { + qDeleteAll( m_playing ); + } + + int newId() const; + bool play( const SoundInfo& si ); + + // private slots + void finished( int ); + + AudioPlayer * q; + + QHash< int, PlayInfo * > m_playing; + QSignalMapper m_mapper; +}; + +int AudioPlayer::Private::newId() const +{ + int newid = 0; + QHash< int, PlayInfo * >::const_iterator it; + QHash< int, PlayInfo * >::const_iterator itEnd = m_playing.constEnd(); + 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; + PlayInfo * info = new PlayInfo(); + info->m_output = new Phonon::AudioOutput( Phonon::NotificationCategory ); + info->m_output->setVolume( si.volume ); + info->m_path = new Phonon::AudioPath(); + info->m_path->addOutput( info->m_output ); + info->m_info = si; + 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() ) + { + info->m_mediaobject = new Phonon::MediaObject(); + if ( info->m_mediaobject->addAudioPath( info->m_path ) ) + { + QObject::connect( info->m_mediaobject, SIGNAL( finished() ), &m_mapper, SLOT( map() ) ); + int newid = newId(); + m_mapper.setMapping( info->m_mediaobject, newid ); + info->m_mediaobject->setUrl( url ); + m_playing.insert( newid, info ); + valid = true; + info->m_mediaobject->play(); + kDebug() << "[AudioPlayer::Playinfo::play()] PLAY url" << endl; + } + } + break; + } + case Sound::Embedded: + { +#if 0 // disable because of broken bytestream in xine :( + QByteArray data = si.sound->data(); + kDebug() << "[AudioPlayer::Playinfo::play()] Embedded, " << data.length() << endl; + if ( !data.isEmpty() ) + { + kDebug() << "[AudioPlayer::Playinfo::play()] bytestream: " << info->m_bytestream << endl; + info->m_bytestream = new Phonon::ByteStream(); + kDebug() << "[AudioPlayer::Playinfo::play()] bytestream: " << info->m_bytestream << endl; + if ( info->m_bytestream->addAudioPath( info->m_path ) ) + { + QObject::connect( info->m_bytestream, SIGNAL( finished() ), &m_mapper, SLOT( map() ) ); + int newid = newId(); + m_mapper.setMapping( info->m_mediaobject, newid ); + m_playing.insert( newid, info ); + info->m_bytestream->writeData( data ); + info->m_bytestream->setStreamSize( data.length() ); + info->m_bytestream->setStreamSeekable( true ); + info->m_bytestream->endOfData(); + valid = true; + info->m_bytestream->play(); + kDebug() << "[AudioPlayer::Playinfo::play()] PLAY data" << endl; + } + } +#endif + break; + } + } + if ( !valid ) + { + delete info; + info = 0; + } + return valid; +} + + +void AudioPlayer::Private::finished( int id ) +{ + QHash< int, PlayInfo * >::iterator it = m_playing.find( id ); + if ( it == m_playing.end() ) + return; + + delete it.value(); + m_playing.erase( it ); + 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 ≈ +} + +void AudioPlayer::playSound( const Sound * sound, const LinkSound * linksound ) +{ + // we can't play null pointers ;) + if ( !sound ) + return; + + kDebug() << k_funcinfo << endl; + SoundInfo si( sound, linksound ); + + d->play( si ); +} + +#include "audioplayer.moc" diff --git a/core/audioplayer.h b/core/audioplayer.h new file mode 100644 index 000000000..538d2a5d9 --- /dev/null +++ b/core/audioplayer.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2007 by Pino Toscano * + * * + * 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. * + ***************************************************************************/ + +#ifndef _OKULAR_AUDIOPLAYER_H_ +#define _OKULAR_AUDIOPLAYER_H_ + +#include + +#include + +namespace Okular { + +class LinkSound; +class Sound; + +/** + * @short An audio player. + * + * Singleton utility class to play sounds in documents using the KDE sound + * system. + */ +class OKULAR_EXPORT AudioPlayer : public QObject +{ + Q_OBJECT + + public: + ~AudioPlayer(); + + /** + * Gets the instance of the audio player. + */ + static AudioPlayer * instance(); + + /** + * Enqueue the specified @p sound for playing, optionally taking more + * information about the playing from the @p soundlink . + */ + void playSound( const Sound * sound, const LinkSound * linksound = 0 ); + + private: + AudioPlayer(); + + class Private; + Private * const d; + + Q_DISABLE_COPY( AudioPlayer ) + Q_PRIVATE_SLOT( d, void finished( int ) ) +}; + +} + +#endif diff --git a/core/document.cpp b/core/document.cpp index 5748db0b9..e0a647afd 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -33,6 +33,7 @@ #include // local includes +#include "audioplayer.h" #include "bookmarkmanager.h" #include "chooseenginedialog.h" #include "document.h" @@ -2033,13 +2034,15 @@ void Document::processLink( const Link * link ) } } break; + case Link::Sound: { + const LinkSound * linksound = static_cast< const LinkSound * >( link ); + AudioPlayer::instance()->playSound( linksound->sound(), linksound ); + } break; + case Link::Movie: //const LinkMovie * browse = static_cast< const LinkMovie * >( link ); // TODO this (Movie link) break; - case Link::Sound: - // TODO this (Sound link) - break; } } diff --git a/ui/presentationwidget.cpp b/ui/presentationwidget.cpp index f7dc01ce7..b2ce3bd3f 100644 --- a/ui/presentationwidget.cpp +++ b/ui/presentationwidget.cpp @@ -622,9 +622,16 @@ void PresentationWidget::changePage( int newPage ) // set a new viewport in document if page number differs if ( m_frameIndex != -1 && m_frameIndex != m_document->viewport().pageNumber ) { + if ( m_document->page( m_document->viewport().pageNumber )->pageAction( Okular::Page::Closing ) ) + m_document->processLink( m_document->page( m_document->viewport().pageNumber )->pageAction( Okular::Page::Closing ) ); + // remove the drawing on the old page before switching clearDrawings(); m_document->setViewportPage( m_frameIndex, PRESENTATION_ID ); + + if ( m_document->page( m_frameIndex)->pageAction( Okular::Page::Opening ) ) + m_document->processLink( m_document->page( m_frameIndex )->pageAction( Okular::Page::Opening ) ); + } }