mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-11-30 17:24:08 +00:00
Use Phonon framework for audio playback.
This is now the default way to play back sound under Windows. The other one, "Use external program", was also made available there. The old method of audio playback under Windows (via PlaySound) was completely removed. Since Phonon uses DirectShow as a backend under Windows, all file types supported by it should now play back. Therefore, the previous limitation to just .wav files is now lifted. Since Phonon is only available starting from Qt 4.6.0 under Windows, no older versions of Qt are supported anymore there.
This commit is contained in:
parent
0e187e1e76
commit
c25e7f268e
|
@ -14,14 +14,38 @@
|
||||||
#include "folding.hh"
|
#include "folding.hh"
|
||||||
#include "wstring_qt.hh"
|
#include "wstring_qt.hh"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#include <QBuffer>
|
||||||
#include <windows.h>
|
|
||||||
#include <mmsystem.h> // For PlaySound
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using std::map;
|
using std::map;
|
||||||
using std::list;
|
using std::list;
|
||||||
|
|
||||||
|
/// A phonon-based audio player, created on demand
|
||||||
|
struct AudioPlayer
|
||||||
|
{
|
||||||
|
Phonon::AudioOutput output;
|
||||||
|
Phonon::MediaObject object;
|
||||||
|
|
||||||
|
static AudioPlayer & instance();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
AudioPlayer();
|
||||||
|
};
|
||||||
|
|
||||||
|
AudioPlayer::AudioPlayer():
|
||||||
|
output( Phonon::AccessibilityCategory )
|
||||||
|
{
|
||||||
|
Phonon::createPath( &object, &output );
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioPlayer & AudioPlayer::instance()
|
||||||
|
{
|
||||||
|
static AudioPlayer a;
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm,
|
ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm,
|
||||||
std::vector< sptr< Dictionary::Class > > const & allDictionaries_,
|
std::vector< sptr< Dictionary::Class > > const & allDictionaries_,
|
||||||
Instances::Groups const & groups_, bool popupView_,
|
Instances::Groups const & groups_, bool popupView_,
|
||||||
|
@ -118,15 +142,6 @@ void ArticleView::setGroupComboBox( GroupComboBox const * g )
|
||||||
ArticleView::~ArticleView()
|
ArticleView::~ArticleView()
|
||||||
{
|
{
|
||||||
cleanupTemp();
|
cleanupTemp();
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
if ( winWavData.size() )
|
|
||||||
{
|
|
||||||
// If we were playing some sound some time ago, make sure it stopped
|
|
||||||
// playing before freeing the waveform memory.
|
|
||||||
PlaySoundA( 0, 0, 0 );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArticleView::showDefinition( QString const & word, unsigned group,
|
void ArticleView::showDefinition( QString const & word, unsigned group,
|
||||||
|
@ -945,42 +960,49 @@ void ArticleView::resourceDownloadFinished()
|
||||||
|
|
||||||
if ( resourceDownloadUrl.scheme() == "gdau" )
|
if ( resourceDownloadUrl.scheme() == "gdau" )
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN32
|
// Audio data
|
||||||
// Windows-only: use system PlaySound function
|
|
||||||
|
|
||||||
if ( winWavData.size() )
|
if ( !cfg.preferences.useExternalPlayer )
|
||||||
PlaySoundA( 0, 0, 0 ); // Stop any currently playing sound to make sure
|
|
||||||
// previous data isn't used anymore
|
|
||||||
//
|
|
||||||
winWavData = data;
|
|
||||||
|
|
||||||
PlaySoundA( &winWavData.front(), 0,
|
|
||||||
SND_ASYNC | SND_MEMORY | SND_NODEFAULT | SND_NOWAIT );
|
|
||||||
#else
|
|
||||||
|
|
||||||
// Use external viewer to play the file
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
ExternalViewer * viewer = new ExternalViewer( this, data, "wav", cfg.preferences.audioPlaybackProgram.trimmed() );
|
// Play via Phonon
|
||||||
|
|
||||||
|
QBuffer * buf = new QBuffer;
|
||||||
|
|
||||||
|
buf->buffer().append( &data.front(), data.size() );
|
||||||
|
|
||||||
|
Phonon::MediaSource source( buf );
|
||||||
|
source.setAutoDelete( true ); // Dispose of our buf when done
|
||||||
|
|
||||||
|
AudioPlayer::instance().object.stop();
|
||||||
|
AudioPlayer::instance().object.clear();
|
||||||
|
AudioPlayer::instance().object.enqueue( source );
|
||||||
|
AudioPlayer::instance().object.play();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
// Use external viewer to play the file
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
viewer->start();
|
ExternalViewer * viewer = new ExternalViewer( this, data, "wav", cfg.preferences.audioPlaybackProgram.trimmed() );
|
||||||
|
|
||||||
// Once started, it will erase itself
|
try
|
||||||
|
{
|
||||||
|
viewer->start();
|
||||||
|
|
||||||
|
// Once started, it will erase itself
|
||||||
|
}
|
||||||
|
catch( ... )
|
||||||
|
{
|
||||||
|
delete viewer;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch( ... )
|
catch( ExternalViewer::Ex & e )
|
||||||
{
|
{
|
||||||
delete viewer;
|
QMessageBox::critical( this, tr( "GoldenDict" ), tr( "Failed to run a player to play sound file: %1" ).arg( e.what() ) );
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch( ExternalViewer::Ex & e )
|
|
||||||
{
|
|
||||||
QMessageBox::critical( this, tr( "GoldenDict" ), tr( "Failed to run a player to play sound file: %1" ).arg( e.what() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "instances.hh"
|
#include "instances.hh"
|
||||||
#include "groupcombobox.hh"
|
#include "groupcombobox.hh"
|
||||||
#include "ui_articleview.h"
|
#include "ui_articleview.h"
|
||||||
|
#include <phonon>
|
||||||
|
|
||||||
/// A widget with the web view tailored to view and handle articles -- it
|
/// A widget with the web view tailored to view and handle articles -- it
|
||||||
/// uses the appropriate netmgr, handles link clicks, rmb clicks etc
|
/// uses the appropriate netmgr, handles link clicks, rmb clicks etc
|
||||||
|
@ -31,11 +32,6 @@ class ArticleView: public QFrame
|
||||||
goBackAction, goForwardAction, openSearchAction;
|
goBackAction, goForwardAction, openSearchAction;
|
||||||
bool searchIsOpened;
|
bool searchIsOpened;
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
// Used in Windows only
|
|
||||||
vector< char > winWavData;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Any resource we've decided to download off the dictionary gets stored here.
|
/// Any resource we've decided to download off the dictionary gets stored here.
|
||||||
/// Full vector capacity is used for search requests, where we have to make
|
/// Full vector capacity is used for search requests, where we have to make
|
||||||
/// a multitude of requests.
|
/// a multitude of requests.
|
||||||
|
|
|
@ -94,6 +94,11 @@ Preferences::Preferences():
|
||||||
scanPopupAltModeSecs( 3 ),
|
scanPopupAltModeSecs( 3 ),
|
||||||
pronounceOnLoadMain( false ),
|
pronounceOnLoadMain( false ),
|
||||||
pronounceOnLoadPopup( false ),
|
pronounceOnLoadPopup( false ),
|
||||||
|
#ifdef Q_WS_WIN
|
||||||
|
useExternalPlayer( false ),
|
||||||
|
#else
|
||||||
|
useExternalPlayer( true ), // Phonon on Linux still feels quite buggy
|
||||||
|
#endif
|
||||||
checkForNewReleases( true ),
|
checkForNewReleases( true ),
|
||||||
disallowContentFromOtherSites( false ),
|
disallowContentFromOtherSites( false ),
|
||||||
zoomFactor( 1 )
|
zoomFactor( 1 )
|
||||||
|
@ -487,6 +492,9 @@ Class load() throw( exError )
|
||||||
|
|
||||||
c.preferences.pronounceOnLoadMain = ( preferences.namedItem( "pronounceOnLoadMain" ).toElement().text() == "1" );
|
c.preferences.pronounceOnLoadMain = ( preferences.namedItem( "pronounceOnLoadMain" ).toElement().text() == "1" );
|
||||||
c.preferences.pronounceOnLoadPopup = ( preferences.namedItem( "pronounceOnLoadPopup" ).toElement().text() == "1" );
|
c.preferences.pronounceOnLoadPopup = ( preferences.namedItem( "pronounceOnLoadPopup" ).toElement().text() == "1" );
|
||||||
|
|
||||||
|
if ( !preferences.namedItem( "useExternalPlayer" ).isNull() )
|
||||||
|
c.preferences.useExternalPlayer = ( preferences.namedItem( "useExternalPlayer" ).toElement().text() == "1" );
|
||||||
|
|
||||||
if ( !preferences.namedItem( "audioPlaybackProgram" ).isNull() )
|
if ( !preferences.namedItem( "audioPlaybackProgram" ).isNull() )
|
||||||
c.preferences.audioPlaybackProgram = preferences.namedItem( "audioPlaybackProgram" ).toElement().text();
|
c.preferences.audioPlaybackProgram = preferences.namedItem( "audioPlaybackProgram" ).toElement().text();
|
||||||
|
@ -892,6 +900,10 @@ void save( Class const & c ) throw( exError )
|
||||||
opt.appendChild( dd.createTextNode( c.preferences.pronounceOnLoadPopup ? "1" : "0" ) );
|
opt.appendChild( dd.createTextNode( c.preferences.pronounceOnLoadPopup ? "1" : "0" ) );
|
||||||
preferences.appendChild( opt );
|
preferences.appendChild( opt );
|
||||||
|
|
||||||
|
opt = dd.createElement( "useExternalPlayer" );
|
||||||
|
opt.appendChild( dd.createTextNode( c.preferences.useExternalPlayer ? "1" : "0" ) );
|
||||||
|
preferences.appendChild( opt );
|
||||||
|
|
||||||
opt = dd.createElement( "audioPlaybackProgram" );
|
opt = dd.createElement( "audioPlaybackProgram" );
|
||||||
opt.appendChild( dd.createTextNode( c.preferences.audioPlaybackProgram ) );
|
opt.appendChild( dd.createTextNode( c.preferences.audioPlaybackProgram ) );
|
||||||
preferences.appendChild( opt );
|
preferences.appendChild( opt );
|
||||||
|
|
|
@ -156,6 +156,7 @@ struct Preferences
|
||||||
// Whether the word should be pronounced on page load, in main window/popup
|
// Whether the word should be pronounced on page load, in main window/popup
|
||||||
bool pronounceOnLoadMain, pronounceOnLoadPopup;
|
bool pronounceOnLoadMain, pronounceOnLoadPopup;
|
||||||
QString audioPlaybackProgram;
|
QString audioPlaybackProgram;
|
||||||
|
bool useExternalPlayer;
|
||||||
|
|
||||||
ProxyServer proxyServer;
|
ProxyServer proxyServer;
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,16 @@ bool isNameOfSound( string const & name )
|
||||||
endsWith( s, ".au" ) ||
|
endsWith( s, ".au" ) ||
|
||||||
endsWith( s, ".voc" ) ||
|
endsWith( s, ".voc" ) ||
|
||||||
endsWith( s, ".ogg" ) ||
|
endsWith( s, ".ogg" ) ||
|
||||||
endsWith( s, ".mp3" );
|
endsWith( s, ".mp3" ) ||
|
||||||
|
endsWith( s, ".mp4" ) ||
|
||||||
|
endsWith( s, ".aac" ) ||
|
||||||
|
endsWith( s, ".flac" ) ||
|
||||||
|
endsWith( s, ".mid" ) ||
|
||||||
|
endsWith( s, ".kar" ) ||
|
||||||
|
endsWith( s, ".mpc" ) ||
|
||||||
|
endsWith( s, ".wma" ) ||
|
||||||
|
endsWith( s, ".wv" ) ||
|
||||||
|
endsWith( s, ".ape" );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isNameOfPicture( string const & name )
|
bool isNameOfPicture( string const & name )
|
||||||
|
|
|
@ -10,6 +10,7 @@ INCLUDEPATH += .
|
||||||
QT += webkit
|
QT += webkit
|
||||||
QT += xml
|
QT += xml
|
||||||
QT += network
|
QT += network
|
||||||
|
QT += phonon
|
||||||
CONFIG += exceptions \
|
CONFIG += exceptions \
|
||||||
rtti \
|
rtti \
|
||||||
stl
|
stl
|
||||||
|
|
|
@ -125,16 +125,18 @@ Preferences::Preferences( QWidget * parent, Config::Preferences const & p ):
|
||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
|
|
||||||
|
#ifdef Q_WS_WIN
|
||||||
|
// Since there's only one Phonon backend under Windows, be more precise
|
||||||
|
ui.playViaPhonon->setText( tr( "Play via DirectShow" ) );
|
||||||
|
#endif
|
||||||
|
|
||||||
ui.pronounceOnLoadPopup->setEnabled( p.enableScanPopup );
|
ui.pronounceOnLoadPopup->setEnabled( p.enableScanPopup );
|
||||||
|
|
||||||
ui.pronounceOnLoadMain->setChecked( p.pronounceOnLoadMain );
|
ui.pronounceOnLoadMain->setChecked( p.pronounceOnLoadMain );
|
||||||
ui.pronounceOnLoadPopup->setChecked( p.pronounceOnLoadPopup );
|
ui.pronounceOnLoadPopup->setChecked( p.pronounceOnLoadPopup );
|
||||||
ui.audioPlaybackProgram->setText( p.audioPlaybackProgram );
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
ui.useExternalPlayer->setChecked( p.useExternalPlayer );
|
||||||
ui.audioPlaybackProgram->hide();
|
ui.audioPlaybackProgram->setText( p.audioPlaybackProgram );
|
||||||
ui.audioPlaybackProgramLabel->hide();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ( !isRECORDBroken() )
|
if ( !isRECORDBroken() )
|
||||||
ui.brokenXRECORD->hide();
|
ui.brokenXRECORD->hide();
|
||||||
|
@ -203,6 +205,7 @@ Config::Preferences Preferences::getPreferences()
|
||||||
|
|
||||||
p.pronounceOnLoadMain = ui.pronounceOnLoadMain->isChecked();
|
p.pronounceOnLoadMain = ui.pronounceOnLoadMain->isChecked();
|
||||||
p.pronounceOnLoadPopup = ui.pronounceOnLoadPopup->isChecked();
|
p.pronounceOnLoadPopup = ui.pronounceOnLoadPopup->isChecked();
|
||||||
|
p.useExternalPlayer = ui.useExternalPlayer->isChecked();
|
||||||
p.audioPlaybackProgram = ui.audioPlaybackProgram->text();
|
p.audioPlaybackProgram = ui.audioPlaybackProgram->text();
|
||||||
|
|
||||||
p.proxyServer.enabled = ui.useProxyServer->isChecked();
|
p.proxyServer.enabled = ui.useProxyServer->isChecked();
|
||||||
|
@ -294,3 +297,8 @@ void Preferences::on_buttonBox_accepted()
|
||||||
tr( "Restart the program to apply the language change." ) );
|
tr( "Restart the program to apply the language change." ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Preferences::on_useExternalPlayer_toggled( bool enabled )
|
||||||
|
{
|
||||||
|
ui.audioPlaybackProgram->setEnabled( enabled );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ private slots:
|
||||||
void on_enableClipboardHotkey_toggled( bool checked );
|
void on_enableClipboardHotkey_toggled( bool checked );
|
||||||
|
|
||||||
void on_buttonBox_accepted();
|
void on_buttonBox_accepted();
|
||||||
|
|
||||||
|
void on_useExternalPlayer_toggled( bool enabled );
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -703,33 +703,65 @@ p, li { white-space: pre-wrap; }
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="pronounceOnLoadMain">
|
<widget class="QGroupBox" name="groupBox_3">
|
||||||
<property name="text">
|
<property name="title">
|
||||||
<string>Auto-pronounce words in main window</string>
|
<string>Pronunciation</string>
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_15">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="pronounceOnLoadMain">
|
||||||
|
<property name="text">
|
||||||
|
<string>Auto-pronounce words in main window</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="pronounceOnLoadPopup">
|
||||||
|
<property name="text">
|
||||||
|
<string>Auto-pronounce words in scan popup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="pronounceOnLoadPopup">
|
<widget class="QGroupBox" name="groupBox_4">
|
||||||
<property name="text">
|
<property name="title">
|
||||||
<string>Auto-pronounce words in scan popup</string>
|
<string>Playback</string>
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_16">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="playViaPhonon">
|
||||||
|
<property name="text">
|
||||||
|
<string>Play via Phonon</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="useExternalPlayer">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use external program:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="audioPlaybackProgram">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="audioPlaybackProgramLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Program to play audio files:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="audioPlaybackProgram"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer_2">
|
<spacer name="verticalSpacer_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -973,9 +1005,6 @@ download page.</string>
|
||||||
<tabstop>winKey</tabstop>
|
<tabstop>winKey</tabstop>
|
||||||
<tabstop>scanPopupAltMode</tabstop>
|
<tabstop>scanPopupAltMode</tabstop>
|
||||||
<tabstop>scanPopupAltModeSecs</tabstop>
|
<tabstop>scanPopupAltModeSecs</tabstop>
|
||||||
<tabstop>pronounceOnLoadMain</tabstop>
|
|
||||||
<tabstop>pronounceOnLoadPopup</tabstop>
|
|
||||||
<tabstop>audioPlaybackProgram</tabstop>
|
|
||||||
<tabstop>useProxyServer</tabstop>
|
<tabstop>useProxyServer</tabstop>
|
||||||
<tabstop>proxyType</tabstop>
|
<tabstop>proxyType</tabstop>
|
||||||
<tabstop>proxyHost</tabstop>
|
<tabstop>proxyHost</tabstop>
|
||||||
|
|
Loading…
Reference in a new issue