From 1e7cb0b2eb24efe8991c87ded6d3a20fa60f5e10 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Mon, 5 Feb 2007 00:49:40 +0000 Subject: [PATCH] First version of an audio player for okular, able to play the sounds in the documents. At the moment is quite simple and works only for external sounds. Activate the sound playing when processing links and when switching pages in presentation mode only. svn path=/trunk/playground/graphics/okular/; revision=630315 --- CMakeLists.txt | 3 +- core/audioplayer.cpp | 235 ++++++++++++++++++++++++++++++++++++++ core/audioplayer.h | 58 ++++++++++ core/document.cpp | 9 +- ui/presentationwidget.cpp | 7 ++ 5 files changed, 308 insertions(+), 4 deletions(-) create mode 100644 core/audioplayer.cpp create mode 100644 core/audioplayer.h 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 ) ); + } }