diff --git a/cockatrice/cockatrice.qrc b/cockatrice/cockatrice.qrc index 944e239f..3a46bcc3 100644 --- a/cockatrice/cockatrice.qrc +++ b/cockatrice/cockatrice.qrc @@ -9,6 +9,7 @@ resources/icon_config_interface.svg resources/icon_config_messages.svg resources/icon_config_deckeditor.svg + resources/icon_config_sound.svg resources/phases/icon_phase_untap.svg resources/phases/icon_phase_upkeep.svg resources/phases/icon_phase_draw.svg diff --git a/cockatrice/resources/icon_config_sound.svg b/cockatrice/resources/icon_config_sound.svg new file mode 100644 index 00000000..a3904c50 --- /dev/null +++ b/cockatrice/resources/icon_config_sound.svg @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 00fe9b6a..86568b21 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "carddatabase.h" #include "dlg_settings.h" #include "main.h" @@ -449,8 +451,6 @@ void AppearanceSettingsPage::cardBackPicturePathButtonClicked() UserInterfaceSettingsPage::UserInterfaceSettingsPage() { - QIcon deleteIcon(":/resources/icon_delete.svg"); - notificationsEnabledCheckBox.setChecked(settingsCache->getNotificationsEnabled()); connect(¬ificationsEnabledCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setNotificationsEnabled(int))); connect(¬ificationsEnabledCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setSpecNotificationEnabled(int))); @@ -477,28 +477,6 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() tapAnimationCheckBox.setChecked(settingsCache->getTapAnimation()); connect(&tapAnimationCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setTapAnimation(int))); - soundEnabledCheckBox.setChecked(settingsCache->getSoundEnabled()); - connect(&soundEnabledCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setSoundEnabled(int))); - - soundPathEdit = new QLineEdit(settingsCache->getSoundPath()); - soundPathEdit->setReadOnly(true); - QPushButton *soundPathClearButton = new QPushButton(deleteIcon, QString()); - connect(soundPathClearButton, SIGNAL(clicked()), this, SLOT(soundPathClearButtonClicked())); - QPushButton *soundPathButton = new QPushButton("..."); - connect(soundPathButton, SIGNAL(clicked()), this, SLOT(soundPathButtonClicked())); - connect(&soundTestButton, SIGNAL(clicked()), soundEngine, SLOT(cuckoo())); - - QGridLayout *soundGrid = new QGridLayout; - soundGrid->addWidget(&soundEnabledCheckBox, 0, 0, 1, 4); - soundGrid->addWidget(&soundPathLabel, 1, 0); - soundGrid->addWidget(soundPathEdit, 1, 1); - soundGrid->addWidget(soundPathClearButton, 1, 2); - soundGrid->addWidget(soundPathButton, 1, 3); - soundGrid->addWidget(&soundTestButton, 2, 1); - - soundGroupBox = new QGroupBox; - soundGroupBox->setLayout(soundGrid); - QGridLayout *animationGrid = new QGridLayout; animationGrid->addWidget(&tapAnimationCheckBox, 0, 0); @@ -508,7 +486,6 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(generalGroupBox); mainLayout->addWidget(animationGroupBox); - mainLayout->addWidget(soundGroupBox); setLayout(mainLayout); } @@ -526,27 +503,8 @@ void UserInterfaceSettingsPage::retranslateUi() playToStackCheckBox.setText(tr("&Play all nonlands onto the stack (not the battlefield) by default")); animationGroupBox->setTitle(tr("Animation settings")); tapAnimationCheckBox.setText(tr("&Tap/untap animation")); - soundEnabledCheckBox.setText(tr("Enable &sounds")); - soundPathLabel.setText(tr("Path to sounds directory:")); - soundTestButton.setText(tr("Test system sound engine")); - soundGroupBox->setTitle(tr("Sound settings")); } -void UserInterfaceSettingsPage::soundPathClearButtonClicked() -{ - soundPathEdit->setText(QString()); - settingsCache->setSoundPath(QString()); -} - -void UserInterfaceSettingsPage::soundPathButtonClicked() -{ - QString path = QFileDialog::getExistingDirectory(this, tr("Choose path")); - if (path.isEmpty()) - return; - - soundPathEdit->setText(path); - settingsCache->setSoundPath(path); -} DeckEditorSettingsPage::DeckEditorSettingsPage() { @@ -718,6 +676,102 @@ void MessagesSettingsPage::retranslateUi() hexLabel.setText(tr("(Color is hexadecimal)")); } + +SoundSettingsPage::SoundSettingsPage() +{ + QIcon deleteIcon(":/resources/icon_delete.svg"); + + soundEnabledCheckBox.setChecked(settingsCache->getSoundEnabled()); + connect(&soundEnabledCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setSoundEnabled(int))); + + soundPathEdit = new QLineEdit(settingsCache->getSoundPath()); + soundPathEdit->setReadOnly(true); + QPushButton *soundPathClearButton = new QPushButton(deleteIcon, QString()); + connect(soundPathClearButton, SIGNAL(clicked()), this, SLOT(soundPathClearButtonClicked())); + QPushButton *soundPathButton = new QPushButton("..."); + connect(soundPathButton, SIGNAL(clicked()), this, SLOT(soundPathButtonClicked())); + connect(&soundTestButton, SIGNAL(clicked()), soundEngine, SLOT(cuckoo())); + + masterVolumeSlider = new QSlider(Qt::Horizontal); + masterVolumeSlider->setMinimum(0); + masterVolumeSlider->setMaximum(100); + masterVolumeSlider->setValue(settingsCache->getMasterVolume()); + masterVolumeSlider->setToolTip(QString::number(settingsCache->getMasterVolume())); + connect(settingsCache, SIGNAL(masterVolumeChanged(int)), this, SLOT(masterVolumeChanged(int))); + connect(masterVolumeSlider, SIGNAL(sliderReleased()), soundEngine, SLOT(endStep())); + connect(masterVolumeSlider, SIGNAL(valueChanged(int)), settingsCache, SLOT(setMasterVolume(int))); + + + + masterVolumeSpinBox = new QSpinBox(); + masterVolumeSpinBox->setMinimum(0); + masterVolumeSpinBox->setMaximum(100); + masterVolumeSpinBox->setValue(settingsCache->getMasterVolume()); + connect(masterVolumeSlider, SIGNAL(valueChanged(int)), masterVolumeSpinBox, SLOT(setValue(int))); + connect(masterVolumeSpinBox, SIGNAL(valueChanged(int)), masterVolumeSlider, SLOT(setValue(int))); + +#if QT_VERSION < 0x050000 + masterVolumeSlider->setEnabled(false); + masterVolumeSpinBox->setEnabled(false); +#endif + + QGridLayout *soundGrid = new QGridLayout; + soundGrid->addWidget(&soundEnabledCheckBox, 0, 0, 1, 4); + soundGrid->addWidget(&masterVolumeLabel, 1, 0); + soundGrid->addWidget(masterVolumeSlider, 1, 1); + soundGrid->addWidget(masterVolumeSpinBox, 1, 2); + soundGrid->addWidget(&soundPathLabel, 2, 0); + soundGrid->addWidget(soundPathEdit, 2, 1); + soundGrid->addWidget(soundPathClearButton, 2, 2); + soundGrid->addWidget(soundPathButton, 2, 3); + soundGrid->addWidget(&soundTestButton, 3, 1); + + soundGroupBox = new QGroupBox; + soundGroupBox->setLayout(soundGrid); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(soundGroupBox); + + setLayout(mainLayout); +} + +void SoundSettingsPage::masterVolumeChanged(int value) { + masterVolumeSlider->setToolTip(QString::number(value)); + //QToolTip::showText(masterVolumeSlider->mapToGlobal(QPoint(0, 0)), QString::number(value)); + // to do + // need to set the edit bar to be the same + // also need to update the slider to be the same +} + +void SoundSettingsPage::soundPathClearButtonClicked() +{ + soundPathEdit->setText(QString()); + settingsCache->setSoundPath(QString()); +} + +void SoundSettingsPage::soundPathButtonClicked() +{ + QString path = QFileDialog::getExistingDirectory(this, tr("Choose path")); + if (path.isEmpty()) + return; + + soundPathEdit->setText(path); + settingsCache->setSoundPath(path); +} + +void SoundSettingsPage::retranslateUi() { + soundEnabledCheckBox.setText(tr("Enable &sounds")); + soundPathLabel.setText(tr("Path to sounds directory:")); + soundTestButton.setText(tr("Test system sound engine")); + soundGroupBox->setTitle(tr("Sound settings")); + #if QT_VERSION < 0x050000 + masterVolumeLabel.setText(tr("Master volume requires QT5")); +#else + masterVolumeLabel.setText(tr("Master volume")); +#endif + +} + DlgSettings::DlgSettings(QWidget *parent) : QDialog(parent) { @@ -737,6 +791,7 @@ DlgSettings::DlgSettings(QWidget *parent) pagesWidget->addWidget(new UserInterfaceSettingsPage); pagesWidget->addWidget(new DeckEditorSettingsPage); pagesWidget->addWidget(new MessagesSettingsPage); + pagesWidget->addWidget(new SoundSettingsPage); createIcons(); contentsWidget->setCurrentRow(0); @@ -785,6 +840,11 @@ void DlgSettings::createIcons() messagesButton->setTextAlignment(Qt::AlignHCenter); messagesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); messagesButton->setIcon(QIcon(":/resources/icon_config_messages.svg")); + + soundButton = new QListWidgetItem(contentsWidget); + soundButton->setTextAlignment(Qt::AlignHCenter); + soundButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + soundButton->setIcon(QIcon(":/resources/icon_config_sound.svg")); connect(contentsWidget, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this, SLOT(changePage(QListWidgetItem *, QListWidgetItem *))); } @@ -894,6 +954,7 @@ void DlgSettings::retranslateUi() userInterfaceButton->setText(tr("User Interface")); deckEditorButton->setText(tr("Deck Editor")); messagesButton->setText(tr("Chat")); + soundButton->setText(tr("Sound")); for (int i = 0; i < pagesWidget->count(); i++) dynamic_cast(pagesWidget->widget(i))->retranslateUi(); diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index aa6cefa2..c949ed6d 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -23,6 +23,8 @@ class QCloseEvent; class QSpinBox; class QRadioButton; class QSpinBox; +class QSlider; +class QSpinBox; class AbstractSettingsPage : public QWidget { public: @@ -115,24 +117,16 @@ public: class UserInterfaceSettingsPage : public AbstractSettingsPage { Q_OBJECT private slots: - void soundPathClearButtonClicked(); - void soundPathButtonClicked(); void setSpecNotificationEnabled(int); -signals: - void soundPathChanged(); private: QCheckBox notificationsEnabledCheckBox; QCheckBox specNotificationsEnabledCheckBox; QCheckBox doubleClickToPlayCheckBox; QCheckBox playToStackCheckBox; QCheckBox tapAnimationCheckBox; - QCheckBox soundEnabledCheckBox; - QLabel soundPathLabel; - QLineEdit *soundPathEdit; QGroupBox *generalGroupBox; QGroupBox *animationGroupBox; - QGroupBox *soundGroupBox; - QPushButton soundTestButton; + public: UserInterfaceSettingsPage(); void retranslateUi(); @@ -181,6 +175,28 @@ private: void updateMentionPreview(); }; +class SoundSettingsPage : public AbstractSettingsPage { + Q_OBJECT +public: + SoundSettingsPage(); + void retranslateUi(); +private: + QGroupBox *soundGroupBox; + QPushButton soundTestButton; + QCheckBox soundEnabledCheckBox; + QLabel soundPathLabel; + QLineEdit *soundPathEdit; + QLabel masterVolumeLabel; + QSlider *masterVolumeSlider; + QSpinBox *masterVolumeSpinBox; +signals: + void soundPathChanged(); +private slots: + void masterVolumeChanged(int value); + void soundPathClearButtonClicked(); + void soundPathButtonClicked(); +}; + class DlgSettings : public QDialog { Q_OBJECT public: @@ -192,7 +208,7 @@ private slots: private: QListWidget *contentsWidget; QStackedWidget *pagesWidget; - QListWidgetItem *generalButton, *appearanceButton, *userInterfaceButton, *deckEditorButton, *messagesButton; + QListWidgetItem *generalButton, *appearanceButton, *userInterfaceButton, *deckEditorButton, *messagesButton, *soundButton; void createIcons(); void retranslateUi(); protected: diff --git a/cockatrice/src/messagelogwidget.cpp b/cockatrice/src/messagelogwidget.cpp index f42a536f..b92112ba 100644 --- a/cockatrice/src/messagelogwidget.cpp +++ b/cockatrice/src/messagelogwidget.cpp @@ -37,6 +37,7 @@ bool MessageLogWidget::userIsFemale() const void MessageLogWidget::logGameJoined(int gameId) { + soundEngine->playerJoined(); if (userIsFemale()) appendHtmlServerMessage(tr("You have joined game #%1.", "female").arg(""+ QString::number(gameId) + "")); else @@ -53,7 +54,7 @@ void MessageLogWidget::logReplayStarted(int gameId) void MessageLogWidget::logJoin(Player *player) { - soundEngine->cuckoo(); + soundEngine->playerJoined(); if (isFemale(player)) appendHtmlServerMessage(tr("%1 has joined the game.", "female").arg(sanitizeHtml(player->getName()))); else @@ -171,7 +172,6 @@ void MessageLogWidget::logSpectatorSay(QString spectatorName, UserLevelFlags spe void MessageLogWidget::logShuffle(Player *player, CardZone *zone) { - soundEngine->shuffle(); if (currentContext != MessageContext_Mulligan) { appendHtmlServerMessage((isFemale(player) ? tr("%1 shuffles %2.", "female") @@ -194,7 +194,6 @@ void MessageLogWidget::logDrawCards(Player *player, int number) if (currentContext == MessageContext_Mulligan) mulliganPlayer = player; else { - soundEngine->draw(); if (isFemale(player)) appendHtmlServerMessage(tr("%1 draws %2 card(s).", "female").arg(sanitizeHtml(player->getName())).arg("" + QString::number(number) + "")); else @@ -297,7 +296,6 @@ void MessageLogWidget::doMoveCard(LogMoveCard &attributes) QString finalStr; if (targetName == "table") { - soundEngine->playCard(); if (moveCardTapped.value(attributes.card)) finalStr = tr("%1 puts %2 into play tapped%3."); else @@ -320,7 +318,6 @@ void MessageLogWidget::doMoveCard(LogMoveCard &attributes) } else if (targetName == "sb") finalStr = tr("%1 moves %2%3 to sideboard."); else if (targetName == "stack") { - soundEngine->playCard(); finalStr = tr("%1 plays %2%3."); } @@ -570,8 +567,6 @@ void MessageLogWidget::logSetTapped(Player *player, CardItem *card, bool tapped) { if (tapped) soundEngine->tap(); - else - soundEngine->untap(); if (currentContext == MessageContext_MoveCard) moveCardTapped.insert(card, tapped); @@ -792,7 +787,6 @@ void MessageLogWidget::logAlwaysRevealTopCard(Player *player, CardZone *zone, bo void MessageLogWidget::logSetActivePlayer(Player *player) { - soundEngine->notification(); QString str; if (isFemale(player)) @@ -804,7 +798,6 @@ void MessageLogWidget::logSetActivePlayer(Player *player) void MessageLogWidget::logSetActivePhase(int phase) { - soundEngine->notification(); QString phaseName; switch (phase) { case 0: phaseName = tr("untap step"); break; @@ -817,7 +810,7 @@ void MessageLogWidget::logSetActivePhase(int phase) case 7: phaseName = tr("combat damage step"); break; case 8: phaseName = tr("end of combat step"); break; case 9: phaseName = tr("second main phase"); break; - case 10: phaseName = tr("ending phase"); break; + case 10: phaseName = tr("ending phase"); soundEngine->endStep(); break; } appendHtml("" + QDateTime::currentDateTime().toString("[hh:mm:ss] ") + tr("It is now the %1.").arg(phaseName) + ""); } diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index e605619e..c007b010 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -79,6 +79,14 @@ SettingsCache::SettingsCache() showMentionPopups = settings->value("chat/showmentionpopups", true).toBool(); leftJustified = settings->value("interface/leftjustified", false).toBool(); + + masterVolume = settings->value("sound/mastervolume", 100).toInt(); +} + +void SettingsCache::setMasterVolume(int _masterVolume) { + masterVolume = _masterVolume; + settings->setValue("sound/mastervolume", masterVolume); + emit masterVolumeChanged(masterVolume); } void SettingsCache::setLeftJustified(const int _leftJustified) { diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index eb47f24b..0c33414f 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -39,6 +39,7 @@ signals: void ignoreUnregisteredUsersChanged(); void ignoreUnregisteredUserMessagesChanged(); void pixmapCacheSizeChanged(int newSizeInMBs); + void masterVolumeChanged(int value); private: QSettings *settings; @@ -79,6 +80,7 @@ private: bool showMessagePopups; bool showMentionPopups; bool leftJustified; + int masterVolume; public: SettingsCache(); const QByteArray &getMainWindowGeometry() const { return mainWindowGeometry; } @@ -134,6 +136,7 @@ public: bool getShowMessagePopup() const { return showMessagePopups; } bool getShowMentionPopup() const { return showMentionPopups; } bool getLeftJustified() const { return leftJustified; } + int getMasterVolume() const { return masterVolume; } public slots: void setMainWindowGeometry(const QByteArray &_mainWindowGeometry); void setLang(const QString &_lang); @@ -182,6 +185,7 @@ public slots: void setShowMessagePopups(const int _showMessagePopups); void setShowMentionPopups(const int _showMentionPopups); void setLeftJustified( const int _leftJustified); + void setMasterVolume(const int _masterVolume); }; extern SettingsCache *settingsCache; diff --git a/cockatrice/src/soundengine.cpp b/cockatrice/src/soundengine.cpp index d1f5c214..6bcaeded 100644 --- a/cockatrice/src/soundengine.cpp +++ b/cockatrice/src/soundengine.cpp @@ -14,12 +14,15 @@ SoundEngine::SoundEngine(QObject *parent) connect(settingsCache, SIGNAL(soundEnabledChanged()), this, SLOT(soundEnabledChanged())); cacheData(); soundEnabledChanged(); + + lastTapPlayed = QDateTime::currentDateTime(); + lastEndStepPlayed = QDateTime::currentDateTime(); } void SoundEngine::cacheData() { static const QStringList fileNames = QStringList() - << "notification" << "draw" << "playcard" << "shuffle" << "tap" << "untap" << "cuckoo"; + << "end_step" << "tap" << "player_joined"; for (int i = 0; i < fileNames.size(); ++i) { QFile file(settingsCache->getSoundPath() + "/" + fileNames[i] + ".raw"); if(!file.exists()) @@ -64,40 +67,28 @@ void SoundEngine::playSound(const QString &fileName) inputBuffer->close(); inputBuffer->setData(audioData[fileName]); inputBuffer->open(QIODevice::ReadOnly); +#if QT_VERSION >= 0x050000 + audio->setVolume(settingsCache->getMasterVolume() / 100.0); +#endif audio->start(inputBuffer); + } -void SoundEngine::notification() +void SoundEngine::endStep() { - playSound("notification"); -} - -void SoundEngine::draw() -{ - playSound("draw"); -} - -void SoundEngine::playCard() -{ - playSound("playcard"); -} - -void SoundEngine::shuffle() -{ - playSound("shuffle"); + if (lastEndStepPlayed.secsTo(QDateTime::currentDateTime()) >= 1) + playSound("end_step"); + lastEndStepPlayed = QDateTime::currentDateTime(); } void SoundEngine::tap() { - playSound("tap"); + if (lastTapPlayed.secsTo(QDateTime::currentDateTime()) >= 1) + playSound("tap"); + lastTapPlayed = QDateTime::currentDateTime(); } -void SoundEngine::untap() +void SoundEngine::playerJoined() { - playSound("untap"); -} - -void SoundEngine::cuckoo() -{ - playSound("cuckoo"); + playSound("player_joined"); } diff --git a/cockatrice/src/soundengine.h b/cockatrice/src/soundengine.h index a3071fda..f15b3931 100644 --- a/cockatrice/src/soundengine.h +++ b/cockatrice/src/soundengine.h @@ -3,6 +3,7 @@ #include #include +#include class QAudioOutput; class QBuffer; @@ -14,19 +15,17 @@ private: QMap audioData; QBuffer *inputBuffer; QAudioOutput *audio; + QDateTime lastTapPlayed; + QDateTime lastEndStepPlayed; private slots: void cacheData(); void soundEnabledChanged(); public: SoundEngine(QObject *parent = 0); public slots: - void notification(); - void draw(); - void playCard(); - void shuffle(); + void endStep(); void tap(); - void untap(); - void cuckoo(); + void playerJoined(); }; extern SoundEngine *soundEngine; diff --git a/sounds/cuckoo.raw b/sounds/cuckoo.raw deleted file mode 100644 index 52f0cbb6..00000000 Binary files a/sounds/cuckoo.raw and /dev/null differ diff --git a/sounds/cuckoo.wav b/sounds/cuckoo.wav deleted file mode 100644 index 5eba46f9..00000000 Binary files a/sounds/cuckoo.wav and /dev/null differ diff --git a/sounds/draw.raw b/sounds/draw.raw deleted file mode 100644 index 3732125f..00000000 Binary files a/sounds/draw.raw and /dev/null differ diff --git a/sounds/end_step.raw b/sounds/end_step.raw new file mode 100644 index 00000000..e58561de Binary files /dev/null and b/sounds/end_step.raw differ diff --git a/sounds/notification.raw b/sounds/notification.raw deleted file mode 100644 index 19906b48..00000000 Binary files a/sounds/notification.raw and /dev/null differ diff --git a/sounds/playcard.raw b/sounds/playcard.raw deleted file mode 100644 index c9fb802d..00000000 Binary files a/sounds/playcard.raw and /dev/null differ diff --git a/sounds/player_joined.raw b/sounds/player_joined.raw new file mode 100644 index 00000000..fa43ad91 Binary files /dev/null and b/sounds/player_joined.raw differ diff --git a/sounds/shuffle.raw b/sounds/shuffle.raw deleted file mode 100644 index f092e567..00000000 Binary files a/sounds/shuffle.raw and /dev/null differ diff --git a/sounds/tap.raw b/sounds/tap.raw index db5290a8..748a9323 100644 Binary files a/sounds/tap.raw and b/sounds/tap.raw differ diff --git a/sounds/untap.raw b/sounds/untap.raw deleted file mode 100644 index 938efe26..00000000 Binary files a/sounds/untap.raw and /dev/null differ