diff --git a/src/blacksound/soundgenerator.cpp b/src/blacksound/soundgenerator.cpp index 773c2203c..02447bcf5 100644 --- a/src/blacksound/soundgenerator.cpp +++ b/src/blacksound/soundgenerator.cpp @@ -33,8 +33,7 @@ namespace BlackSound CSoundGenerator::CSoundGenerator(const QAudioDeviceInfo &device, const QAudioFormat &format, const QList &tones, CNotificationSounds::PlayMode mode, QObject *parent) : QIODevice(parent), m_tones(tones), m_position(0), m_playMode(mode), m_endReached(false), m_oneCycleDurationMs(calculateDurationMs(tones)), - m_device(device), m_audioFormat(format), m_audioOutput(new QAudioOutput(format)), - m_pushTimer(nullptr), m_pushModeIODevice(nullptr), m_ownThread(nullptr) + m_device(device), m_audioFormat(format), m_audioOutput(new QAudioOutput(format)) { Q_ASSERT(tones.size() > 0); } @@ -43,8 +42,7 @@ namespace BlackSound : QIODevice(parent), m_tones(tones), m_position(0), m_playMode(mode), m_endReached(false), m_oneCycleDurationMs(calculateDurationMs(tones)), m_device(QAudioDeviceInfo::defaultOutputDevice()), m_audioFormat(CSoundGenerator::defaultAudioFormat()), - m_audioOutput(new QAudioOutput(CSoundGenerator::defaultAudioFormat())), - m_pushTimer(nullptr), m_pushModeIODevice(nullptr), m_ownThread(nullptr) + m_audioOutput(new QAudioOutput(CSoundGenerator::defaultAudioFormat())) { Q_ASSERT(tones.size() > 0); } @@ -52,7 +50,7 @@ namespace BlackSound CSoundGenerator::~CSoundGenerator() { this->stop(true); - if (this->m_ownThread) this->m_ownThread->deleteLater(); + if (this->m_ownThread) { this->m_ownThread->deleteLater(); } } void CSoundGenerator::start(int volume, bool pull) @@ -133,9 +131,13 @@ namespace BlackSound // periodSize-> Returns the period size in bytes. const qint64 len = this->read(m_buffer.data(), this->m_audioOutput->periodSize()); if (len >= 0) + { this->m_pushModeIODevice->write(m_buffer.data(), len); + } if (len != this->m_audioOutput->periodSize()) + { break; // not a complete period, so buffer is completely read + } --chunks; } } @@ -190,9 +192,13 @@ namespace BlackSound // avoid overflow Q_ASSERT(amplitude <= 1.0 && amplitude >= -1.0); if (amplitude < -1.0) + { amplitude = -1.0; + } else if (amplitude > 1.0) + { amplitude = 1.0; + } else if (qAbs(amplitude) < double(1.0 / 65535)) { amplitude = 0; @@ -245,17 +251,25 @@ namespace BlackSound { quint16 value = static_cast((1.0 + amplitude) / 2 * 65535); if (this->m_audioFormat.byteOrder() == QAudioFormat::LittleEndian) + { qToLittleEndian(value, bufferPointer); + } else + { qToBigEndian(value, bufferPointer); + } } else if (this->m_audioFormat.sampleSize() == 16 && this->m_audioFormat.sampleType() == QAudioFormat::SignedInt) { qint16 value = static_cast(amplitude * 32767); if (this->m_audioFormat.byteOrder() == QAudioFormat::LittleEndian) + { qToLittleEndian(value, bufferPointer); + } else + { qToBigEndian(value, bufferPointer); + } } } @@ -309,7 +323,7 @@ namespace BlackSound qint64 CSoundGenerator::calculateDurationMs(const QList &tones) { - if (tones.isEmpty()) return 0; + if (tones.isEmpty()) { return 0; } qint64 d = 0; foreach(Tone t, tones) { @@ -320,7 +334,7 @@ namespace BlackSound qint64 CSoundGenerator::readData(char *data, qint64 len) { - if (len < 1) return 0; + if (len < 1) { return 0; } if (this->m_endReached) { this->stop(); // all data read, we can stop output @@ -368,20 +382,17 @@ namespace BlackSound return format; } - /* - * BlackMisc to Qt audio device - */ QAudioDeviceInfo CSoundGenerator::findClosestOutputDevice(const CAudioDeviceInfo &audioDevice) { Q_ASSERT(audioDevice.getType() == CAudioDeviceInfo::OutputDevice); const QString lookFor = audioDevice.getName().toLower(); QAudioDeviceInfo qtDevice = QAudioDeviceInfo::defaultOutputDevice(); - if (lookFor.startsWith("default")) return qtDevice; + if (lookFor.startsWith("default")) { return qtDevice; } int score = 0; foreach(QAudioDeviceInfo qd, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) { const QString cn = qd.deviceName().toLower(); - if (lookFor == cn) return qd; // exact match + if (lookFor == cn) { return qd; } // exact match if (cn.length() < lookFor.length()) { if (lookFor.contains(cn) && cn.length() > score) @@ -402,34 +413,34 @@ namespace BlackSound return qtDevice; } - CSoundGenerator *CSoundGenerator::playSignal(qint32 volume, const QList &tones, QAudioDeviceInfo device) + CSoundGenerator *CSoundGenerator::playSignal(int volume, const QList &tones, QAudioDeviceInfo device) { CSoundGenerator *generator = new CSoundGenerator(device, CSoundGenerator::defaultAudioFormat(), tones, CNotificationSounds::SingleWithAutomaticDeletion); - if (tones.isEmpty()) return generator; // that was easy - if (volume < 1) return generator; - if (generator->singleCyleDurationMs() < 10) return generator; // unable to hear + if (tones.isEmpty()) { return generator; } // that was easy + if (volume < 1) { return generator; } + if (generator->singleCyleDurationMs() < 10) { return generator; } // unable to hear // play, and maybe clean up when done generator->start(volume); return generator; } - CSoundGenerator *CSoundGenerator::playSignalInBackground(qint32 volume, const QList &tones, QAudioDeviceInfo device) + CSoundGenerator *CSoundGenerator::playSignalInBackground(int volume, const QList &tones, QAudioDeviceInfo device) { CSoundGenerator *generator = new CSoundGenerator(device, CSoundGenerator::defaultAudioFormat(), tones, CNotificationSounds::SingleWithAutomaticDeletion); - if (tones.isEmpty()) return generator; // that was easy - if (volume < 1) return generator; - if (generator->singleCyleDurationMs() < 10) return generator; // unable to hear + if (tones.isEmpty()) { return generator; } // that was easy + if (volume < 1) { return generator; } + if (generator->singleCyleDurationMs() < 10) { return generator; } // unable to hear // play, and maybe clean up when done generator->startInOwnThread(volume); return generator; } - void CSoundGenerator::playSignalRecorded(qint32 volume, const QList &tones, QAudioDeviceInfo device) + void CSoundGenerator::playSignalRecorded(int volume, const QList &tones, QAudioDeviceInfo device) { - if (tones.isEmpty()) return; // that was easy - if (volume < 1) return; + if (tones.isEmpty()) { return; } // that was easy + if (volume < 1) { return; } CSoundGenerator *generator = new CSoundGenerator(device, CSoundGenerator::defaultAudioFormat(), tones, CNotificationSounds::SingleWithAutomaticDeletion); if (generator->singleCyleDurationMs() > 10) @@ -461,14 +472,14 @@ namespace BlackSound // CSoundGenerator::playSignalRecorded(volume, tones, device); } - void CSoundGenerator::playSelcal(qint32 volume, const CSelcal &selcal, const CAudioDeviceInfo &audioDevice) + void CSoundGenerator::playSelcal(int volume, const CSelcal &selcal, const CAudioDeviceInfo &audioDevice) { if (CSoundGenerator::s_selcalStarted.msecsTo(QDateTime::currentDateTimeUtc()) < 2500) return; // simple check not to play 2 SELCAL at the same time CSoundGenerator::s_selcalStarted = QDateTime::currentDateTimeUtc(); CSoundGenerator::playSelcal(volume, selcal, CSoundGenerator::findClosestOutputDevice(audioDevice)); } - void CSoundGenerator::playNotificationSound(qint32 volume, CNotificationSounds::Notification notification) + void CSoundGenerator::playNotificationSound(int volume, CNotificationSounds::Notification notification) { QMediaPlayer *mediaPlayer = CSoundGenerator::mediaPlayer(); if (mediaPlayer->state() == QMediaPlayer::PlayingState) return; @@ -496,9 +507,9 @@ namespace BlackSound mediaPlayer->play(); } - void CSoundGenerator::playFile(qint32 volume, const QString &file, bool removeFileAfterPlaying) + void CSoundGenerator::playFile(int volume, const QString &file, bool removeFileAfterPlaying) { - if (!QFile::exists(file)) return; + if (!QFile::exists(file)) { return; } QMediaPlayer *mediaPlayer = CSoundGenerator::mediaPlayer(); QMediaResource mediaResource(QUrl(file), "audio"); QMediaContent media(mediaResource); diff --git a/src/blacksound/soundgenerator.h b/src/blacksound/soundgenerator.h index 461abc598..03a6e218f 100644 --- a/src/blacksound/soundgenerator.h +++ b/src/blacksound/soundgenerator.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2013 +/* Copyright (C) 2015 * swift project Community / Contributors * * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level @@ -27,24 +27,17 @@ namespace BlackSound { - //! Playing simple sounds class BLACKSOUND_EXPORT CSoundGenerator : public QIODevice { Q_OBJECT public: - //! Tone to be played struct Tone { friend class CSoundGenerator; - private: - int m_frequencyHz; /*!< first tone's frequency, use 0 for silence */ - int m_secondaryFrequencyHz; /*!< second tone's frequency, or 0 */ - qint64 m_durationMs; /*!< How long to play (duration) */ - public: //! Play frequency f for t milliseconds Tone(const BlackMisc::PhysicalQuantities::CFrequency &frequency, const BlackMisc::PhysicalQuantities::CTime &duration) : @@ -57,35 +50,34 @@ namespace BlackSound m_frequencyHz(static_cast(frequency.valueRounded(BlackMisc::PhysicalQuantities::CFrequencyUnit::Hz()))), m_secondaryFrequencyHz(static_cast(secondaryFrequency.valueRounded(BlackMisc::PhysicalQuantities::CFrequencyUnit::Hz()))), m_durationMs(static_cast(duration.valueRounded(BlackMisc::PhysicalQuantities::CTimeUnit::ms()))) {} + + private: + int m_frequencyHz; //!< first tone's frequency, use 0 for silence + int m_secondaryFrequencyHz; //!< second tone's frequency, or 0 + qint64 m_durationMs; //!< How long to play (duration) }; - /*! - * \brief Constructor - * \param device device - * \param format audio format - * \param tones list of Tones - * \param mode play once? - * \param parent - * \see PlayMode - */ + //! Constructor + //! \param device device + //! \param format audio format + //! \param tones list of Tones + //! \param mode play once? + //! \param parent + //! \see PlayMode CSoundGenerator(const QAudioDeviceInfo &device, const QAudioFormat &format, const QList &tones, CNotificationSounds::PlayMode mode, QObject *parent = nullptr); - /*! - * \brief Constructor - * \param tones list of Tones - * \param mode play once? - * \param parent - * \see PlayMode - */ + //! Constructor + //! \param tones list of Tones + //! \param mode play once? + //! \param parent + //! \see PlayMode CSoundGenerator(const QList &tones, CNotificationSounds::PlayMode mode, QObject *parent = nullptr); //! Destructor ~CSoundGenerator(); - /*! - * \brief Set volume - * \param volume 0..100 - */ + //! Set volume + //! \param volume 0..100 void setVolume(int volume) { this->m_audioOutput->setVolume(qreal(volume / 100.0)); @@ -100,10 +92,8 @@ namespace BlackSound //! \copydoc QIODevice::readData() virtual qint64 readData(char *data, qint64 maxlen) override; - /*! - * \copydoc QIODevice::writeData() - * \remarks NOT(!) used here - */ + //! \copydoc QIODevice::writeData() + //! \remarks NOT(!) used here virtual qint64 writeData(const char *data, qint64 len) override; //! \copydoc QIODevice::bytesAvailable() @@ -124,56 +114,44 @@ namespace BlackSound //! Default audio format fo play these sounds static QAudioFormat defaultAudioFormat(); - /*! - * \brief Find the closest Qt device to this audio device - * \param audioDevice output audio device - * \return - */ + //! Find the closest Qt device to this audio device + //! \param audioDevice output audio device + //! \return static QAudioDeviceInfo findClosestOutputDevice(const BlackMisc::Audio::CAudioDeviceInfo &audioDevice); - /*! - * \brief Play signal of tones once - * \param volume 0-100 - * \param tones list of tones - * \param device device to be used - * \return generator used, important with SingleWithAutomaticDeletion automatically deleted - */ - static CSoundGenerator *playSignal(qint32 volume, const QList &tones, QAudioDeviceInfo device = QAudioDeviceInfo::defaultOutputDevice()); + //! Play signal of tones once + //! \param volume 0-100 + //! \param tones list of tones + //! \param device device to be used + //! \return generator used, important with SingleWithAutomaticDeletion automatically deleted + static CSoundGenerator *playSignal(int volume, const QList &tones, QAudioDeviceInfo device = QAudioDeviceInfo::defaultOutputDevice()); - /*! - * \brief Play signal of tones once - * \param volume 0-100 - * \param tones list of tones - * \param device device to be used - * \return generator used, important with SingleWithAutomaticDeletion automatically deleted - */ - static CSoundGenerator *playSignalInBackground(qint32 volume, const QList &tones, QAudioDeviceInfo device); + //! Play signal of tones once + //! \param volume 0-100 + //! \param tones list of tones + //! \param device device to be used + //! \return generator used, important with SingleWithAutomaticDeletion automatically deleted + static CSoundGenerator *playSignalInBackground(int volume, const QList &tones, QAudioDeviceInfo device); - /*! - * \brief Record the tones to a wav file, then play the wav file - * \param volume 0-100 - * \param tones list of tones - * \param device device to be used - */ - static void playSignalRecorded(qint32 volume, const QList &tones, QAudioDeviceInfo device); + //! Record the tones to a wav file, then play the wav file + //! \param volume 0-100 + //! \param tones list of tones + //! \param device device to be used + static void playSignalRecorded(int volume, const QList &tones, QAudioDeviceInfo device); - /*! - * \brief Play SELCAL tone - * \param volume 0-100 - * \param selcal - * \param device device to be used - * \see BlackMisc::Aviation::CSelcal - */ - static void playSelcal(qint32 volume, const BlackMisc::Aviation::CSelcal &selcal, QAudioDeviceInfo device = QAudioDeviceInfo::defaultOutputDevice()); + //! Play SELCAL tone + //! \param volume 0-100 + //! \param selcal + //! \param device device to be used + //! \see BlackMisc::Aviation::CSelcal + static void playSelcal(int volume, const BlackMisc::Aviation::CSelcal &selcal, QAudioDeviceInfo device = QAudioDeviceInfo::defaultOutputDevice()); - /*! - * \brief Play SELCAL tone - * \param volume 0-100 - * \param selcal - * \param audioDevice device to be used - * \see BlackMisc::Aviation::CSelcal - */ - static void playSelcal(qint32 volume, const BlackMisc::Aviation::CSelcal &selcal, const BlackMisc::Audio::CAudioDeviceInfo &audioDevice); + //! Play SELCAL tone + //! \param volume 0-100 + //! \param selcal + //! \param audioDevice device to be used + //! \see BlackMisc::Aviation::CSelcal + static void playSelcal(int volume, const BlackMisc::Aviation::CSelcal &selcal, const BlackMisc::Audio::CAudioDeviceInfo &audioDevice); //! One cycle of tones takes t milliseconds @@ -182,44 +160,34 @@ namespace BlackSound return BlackMisc::PhysicalQuantities::CTime(this->m_oneCycleDurationMs, BlackMisc::PhysicalQuantities::CTimeUnit::ms()); } - /*! - * \brief Play given file - * \param volume 0-100 - * \param file - * \param removeFileAfterPlaying delete the file, after it has been played - */ - static void playFile(qint32 volume, const QString &file, bool removeFileAfterPlaying); + //! Play given file + //! \param volume 0-100 + //! \param file + //! \param removeFileAfterPlaying delete the file, after it has been played + static void playFile(int volume, const QString &file, bool removeFileAfterPlaying); - /*! - * \brief Play notification - * \param volume 0-100 - * \param notification - */ - static void playNotificationSound(qint32 volume, CNotificationSounds::Notification notification); + //! Play notification + //! \param volume 0-100 + //! \param notification + static void playNotificationSound(int volume, CNotificationSounds::Notification notification); //! For debugging purposes void static printAllQtSoundDevices(QTextStream &qtout); signals: - /*! - * \brief Device was closed - * \remarks With singleShot the signal indicates that sound sequence has finished - */ + //! Device was closed + //! \remarks With singleShot the signal indicates that sound sequence has finished void stopped(); public slots: - /*! - * \brief Play sound, open device - * \param volume 0..100 - * \param pull pull/push, if false push mode - */ + //! Play sound, open device + //! \param volume 0..100 + //! \param pull pull/push, if false push mode void start(int volume, bool pull = true); - /*! - * \brief Play sound in own thread, open device - * \remarks always push mode - * \param volume 0..100 - */ + //! Play sound in own thread, open device + //! \remarks always push mode + //! \param volume 0..100 void startInOwnThread(int volume); signals: @@ -235,18 +203,18 @@ namespace BlackSound void generateData(); private: - QList m_tones; /*! tones to be played */ - qint64 m_position; /*!< position in buffer */ - CNotificationSounds::PlayMode m_playMode; /*!< end data provisioning after playing all tones, play endless loop */ - bool m_endReached; /*!< indicates end in combination with single play */ - qint64 m_oneCycleDurationMs; /*!< how long is one cycle of tones */ - QByteArray m_buffer; /*!< generated buffer for data */ - QAudioDeviceInfo m_device; /*!< audio device */ - QAudioFormat m_audioFormat; /*!< used format */ + QList m_tones; //!< tones to be played + qint64 m_position; //!< position in buffer + CNotificationSounds::PlayMode m_playMode; //!< end data provisioning after playing all tones, play endless loop + bool m_endReached; //!< indicates end in combination with single play + qint64 m_oneCycleDurationMs; //!< how long is one cycle of tones + QByteArray m_buffer; //!< generated buffer for data + QAudioDeviceInfo m_device; //!< audio device + QAudioFormat m_audioFormat; //!< used format QScopedPointer m_audioOutput; - QTimer *m_pushTimer; /*!< Push mode timer */ - QIODevice *m_pushModeIODevice; /*!< IO device when used in push mode */ - QThread *m_ownThread; + QTimer *m_pushTimer = nullptr; //!< Push mode timer + QIODevice *m_pushModeIODevice = nullptr; //!< IO device when used in push mode + QThread *m_ownThread = nullptr; static QDateTime s_selcalStarted; //! Header for saving .wav files @@ -302,14 +270,11 @@ namespace BlackSound //! save buffer to wav file bool saveToWavFile(const QString &fileName) const; - /*! - * Write amplitude to buffer - * \param amplitude value -1 .. 1 - * \param bufferPointer current buffer pointer - */ + //! Write amplitude to buffer + //! \param amplitude value -1 .. 1 + //! \param bufferPointer current buffer pointer void writeAmplitudeToBuffer(const double amplitude, unsigned char *bufferPointer); }; } //namespace - #endif // guard