mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Merge pull request #2415 from antis81:ngf/mic
This commit is contained in:
commit
33e54b841a
6
qtox.pro
6
qtox.pro
|
@ -494,12 +494,13 @@ SOURCES += \
|
|||
src/widget/friendlistlayout.cpp \
|
||||
src/widget/genericchatitemlayout.cpp \
|
||||
src/widget/categorywidget.cpp \
|
||||
src/widget/tool/removefrienddialog.cpp \
|
||||
src/widget/contentlayout.cpp \
|
||||
src/widget/contentdialog.cpp \
|
||||
src/video/genericnetcamview.cpp \
|
||||
src/widget/tool/activatedialog.cpp \
|
||||
src/widget/tool/movablewidget.cpp \
|
||||
src/video/genericnetcamview.cpp \
|
||||
src/widget/tool/micfeedbackwidget.cpp \
|
||||
src/widget/tool/removefrienddialog.cpp \
|
||||
src/video/groupnetcamview.cpp
|
||||
|
||||
HEADERS += \
|
||||
|
@ -547,6 +548,7 @@ HEADERS += \
|
|||
src/widget/contentlayout.h \
|
||||
src/widget/contentdialog.h \
|
||||
src/widget/tool/activatedialog.h \
|
||||
src/widget/tool/micfeedbackwidget.h \
|
||||
src/widget/tool/removefrienddialog.h \
|
||||
src/widget/tool/movablewidget.h \
|
||||
src/video/genericnetcamview.h \
|
||||
|
|
|
@ -28,58 +28,85 @@
|
|||
|
||||
#include "audio.h"
|
||||
#include "src/core/core.h"
|
||||
#include "src/persistence/settings.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <QThread>
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
std::atomic<int> Audio::userCount{0};
|
||||
Audio* Audio::instance{nullptr};
|
||||
QThread* Audio::audioThread{nullptr};
|
||||
QMutex* Audio::audioInLock{nullptr};
|
||||
QMutex* Audio::audioOutLock{nullptr};
|
||||
ALCdevice* Audio::alInDev{nullptr};
|
||||
ALCdevice* Audio::alOutDev{nullptr};
|
||||
ALCcontext* Audio::alContext{nullptr};
|
||||
ALuint Audio::alMainSource{0};
|
||||
float Audio::outputVolume{1.0};
|
||||
|
||||
/**
|
||||
Returns the singleton's instance. Will construct on first call.
|
||||
*/
|
||||
Audio& Audio::getInstance()
|
||||
{
|
||||
if (!instance)
|
||||
{
|
||||
instance = new Audio();
|
||||
audioThread = new QThread(instance);
|
||||
audioThread->setObjectName("qTox Audio");
|
||||
audioThread->start();
|
||||
audioInLock = new QMutex(QMutex::Recursive);
|
||||
audioOutLock = new QMutex(QMutex::Recursive);
|
||||
instance->moveToThread(audioThread);
|
||||
instance->startAudioThread();
|
||||
}
|
||||
return *instance;
|
||||
}
|
||||
|
||||
Audio::Audio()
|
||||
: audioThread(new QThread())
|
||||
, audioInLock(QMutex::Recursive)
|
||||
, audioOutLock(QMutex::Recursive)
|
||||
, inputSubscriptions(0)
|
||||
, alOutDev(nullptr)
|
||||
, alInDev(nullptr)
|
||||
, outputVolume(1.0)
|
||||
, inputVolume(1.0)
|
||||
, alMainSource(0)
|
||||
, alContext(nullptr)
|
||||
, timer(new QTimer(this))
|
||||
{
|
||||
timer->setSingleShot(true);
|
||||
connect(timer, &QTimer::timeout, this, &Audio::closeOutput);
|
||||
|
||||
audioThread->setObjectName("qTox Audio");
|
||||
connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
|
||||
}
|
||||
|
||||
Audio::~Audio()
|
||||
{
|
||||
audioThread->exit(0);
|
||||
audioThread->exit();
|
||||
audioThread->wait();
|
||||
if (audioThread->isRunning())
|
||||
audioThread->terminate();
|
||||
|
||||
delete audioThread;
|
||||
delete audioInLock;
|
||||
delete audioOutLock;
|
||||
}
|
||||
|
||||
float Audio::getOutputVolume()
|
||||
/**
|
||||
Start the audio thread for capture and playback.
|
||||
*/
|
||||
void Audio::startAudioThread()
|
||||
{
|
||||
if (!audioThread->isRunning())
|
||||
audioThread->start();
|
||||
else
|
||||
qWarning("Audio thread already started -> ignored.");
|
||||
|
||||
moveToThread(audioThread);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the current output volume, between 0 and 1
|
||||
*/
|
||||
qreal Audio::getOutputVolume()
|
||||
{
|
||||
QMutexLocker locker(&audioOutLock);
|
||||
return outputVolume;
|
||||
}
|
||||
|
||||
void Audio::setOutputVolume(float volume)
|
||||
/**
|
||||
The volume must be between 0 and 1
|
||||
*/
|
||||
void Audio::setOutputVolume(qreal volume)
|
||||
{
|
||||
QMutexLocker locker(&audioOutLock);
|
||||
outputVolume = volume;
|
||||
alSourcef(alMainSource, AL_GAIN, outputVolume);
|
||||
|
||||
|
@ -99,87 +126,114 @@ void Audio::setOutputVolume(float volume)
|
|||
}
|
||||
}
|
||||
|
||||
void Audio::suscribeInput()
|
||||
/**
|
||||
The volume must be between 0 and 2
|
||||
*/
|
||||
void Audio::setInputVolume(qreal volume)
|
||||
{
|
||||
if (!alInDev)
|
||||
{
|
||||
qWarning()<<"input device is closed";
|
||||
return;
|
||||
}
|
||||
QMutexLocker locker(&audioInLock);
|
||||
inputVolume = volume;
|
||||
}
|
||||
|
||||
qDebug() << "suscribing input";
|
||||
QMutexLocker lock(audioInLock);
|
||||
if (!userCount++ && alInDev)
|
||||
/**
|
||||
@brief Subscribe to capture sound from the opened input device.
|
||||
|
||||
If the input device is not open, it will be opened before capturing.
|
||||
*/
|
||||
void Audio::subscribeInput()
|
||||
{
|
||||
qDebug() << "subscribing input" << inputSubscriptions;
|
||||
if (!inputSubscriptions++)
|
||||
{
|
||||
openInput(Settings::getInstance().getInDev());
|
||||
openOutput(Settings::getInstance().getOutDev());
|
||||
|
||||
#if (!FIX_SND_PCM_PREPARE_BUG)
|
||||
qDebug() << "starting capture";
|
||||
alcCaptureStart(alInDev);
|
||||
if (alInDev)
|
||||
{
|
||||
qDebug() << "starting capture";
|
||||
alcCaptureStart(alInDev);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::unsuscribeInput()
|
||||
{
|
||||
if (!alInDev)
|
||||
{
|
||||
qWarning()<<"input device is closed";
|
||||
return;
|
||||
}
|
||||
/**
|
||||
@brief Unsubscribe from capturing from an opened input device.
|
||||
|
||||
qDebug() << "unsuscribing input";
|
||||
QMutexLocker lock(audioInLock);
|
||||
if (!--userCount && alInDev)
|
||||
{
|
||||
If the input device has no more subscriptions, it will be closed.
|
||||
*/
|
||||
void Audio::unsubscribeInput()
|
||||
{
|
||||
qDebug() << "unsubscribing input" << inputSubscriptions;
|
||||
if (inputSubscriptions > 0)
|
||||
inputSubscriptions--;
|
||||
else if(inputSubscriptions < 0)
|
||||
inputSubscriptions = 0;
|
||||
|
||||
if (!inputSubscriptions) {
|
||||
closeOutput();
|
||||
closeInput();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Open an input device, use before suscribing
|
||||
*/
|
||||
void Audio::openInput(const QString& inDevDescr)
|
||||
{
|
||||
QMutexLocker lock(&audioInLock);
|
||||
|
||||
if (alInDev) {
|
||||
#if (!FIX_SND_PCM_PREPARE_BUG)
|
||||
qDebug() << "stopping capture";
|
||||
alcCaptureStop(alInDev);
|
||||
#endif
|
||||
alcCaptureCloseDevice(alInDev);
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::openInput(const QString& inDevDescr)
|
||||
{
|
||||
QMutexLocker lock(audioInLock);
|
||||
auto* tmp = alInDev;
|
||||
alInDev = nullptr;
|
||||
if (tmp)
|
||||
alcCaptureCloseDevice(tmp);
|
||||
|
||||
int stereoFlag = av_DefaultSettings.audio_channels==1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
||||
const uint32_t sampleRate = av_DefaultSettings.audio_sample_rate;
|
||||
const uint16_t frameDuration = av_DefaultSettings.audio_frame_duration;
|
||||
const uint32_t chnls = av_DefaultSettings.audio_channels;
|
||||
const ALCsizei bufSize = (frameDuration * sampleRate * 4) / 1000 * chnls;
|
||||
if (inDevDescr.isEmpty())
|
||||
alInDev = alcCaptureOpenDevice(nullptr,av_DefaultSettings.audio_sample_rate, stereoFlag,
|
||||
(av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4)
|
||||
/ 1000 * av_DefaultSettings.audio_channels);
|
||||
else
|
||||
alInDev = alcCaptureOpenDevice(inDevDescr.toStdString().c_str(),av_DefaultSettings.audio_sample_rate, stereoFlag,
|
||||
(av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4)
|
||||
/ 1000 * av_DefaultSettings.audio_channels);
|
||||
if (!alInDev)
|
||||
qWarning() << "Cannot open input audio device " + inDevDescr;
|
||||
alInDev = alcCaptureOpenDevice(nullptr, sampleRate, stereoFlag, bufSize);
|
||||
else
|
||||
alInDev = alcCaptureOpenDevice(inDevDescr.toStdString().c_str(),
|
||||
sampleRate, stereoFlag, bufSize);
|
||||
|
||||
if (alInDev)
|
||||
qDebug() << "Opening audio input "<<inDevDescr;
|
||||
else
|
||||
qWarning() << "Cannot open input audio device " + inDevDescr;
|
||||
|
||||
Core* core = Core::getInstance();
|
||||
if (core)
|
||||
core->resetCallSources(); // Force to regen each group call's sources
|
||||
|
||||
// Restart the capture if necessary
|
||||
if (userCount.load() != 0 && alInDev)
|
||||
if (alInDev)
|
||||
{
|
||||
alcCaptureStart(alInDev);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if (FIX_SND_PCM_PREPARE_BUG)
|
||||
alcCaptureStart(alInDev);
|
||||
alcCaptureStart(alInDev);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Audio::openOutput(const QString& outDevDescr)
|
||||
/**
|
||||
Open an output device
|
||||
*/
|
||||
bool Audio::openOutput(const QString &outDevDescr)
|
||||
{
|
||||
QMutexLocker lock(audioOutLock);
|
||||
qDebug() << "Opening audio output " + outDevDescr;
|
||||
QMutexLocker lock(&audioOutLock);
|
||||
|
||||
auto* tmp = alOutDev;
|
||||
alOutDev = nullptr;
|
||||
if (outDevDescr.isEmpty())
|
||||
|
@ -187,11 +241,7 @@ void Audio::openOutput(const QString& outDevDescr)
|
|||
else
|
||||
alOutDev = alcOpenDevice(outDevDescr.toStdString().c_str());
|
||||
|
||||
if (!alOutDev)
|
||||
{
|
||||
qWarning() << "Cannot open output audio device " + outDevDescr;
|
||||
}
|
||||
else
|
||||
if (alOutDev)
|
||||
{
|
||||
if (alContext && alcMakeContextCurrent(nullptr) == ALC_TRUE)
|
||||
alcDestroyContext(alContext);
|
||||
|
@ -199,36 +249,49 @@ void Audio::openOutput(const QString& outDevDescr)
|
|||
if (tmp)
|
||||
alcCloseDevice(tmp);
|
||||
|
||||
alContext=alcCreateContext(alOutDev,nullptr);
|
||||
if (!alcMakeContextCurrent(alContext))
|
||||
{
|
||||
qWarning() << "Cannot create output audio context";
|
||||
alcCloseDevice(alOutDev);
|
||||
}
|
||||
else
|
||||
alContext = alcCreateContext(alOutDev, nullptr);
|
||||
if (alcMakeContextCurrent(alContext))
|
||||
{
|
||||
alGenSources(1, &alMainSource);
|
||||
}
|
||||
|
||||
|
||||
qDebug() << "Opening audio output " + outDevDescr;
|
||||
else
|
||||
{
|
||||
qWarning() << "Cannot create output audio context";
|
||||
alcCloseDevice(alOutDev);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Cannot open output audio device " + outDevDescr;
|
||||
return false;
|
||||
}
|
||||
|
||||
Core* core = Core::getInstance();
|
||||
if (core)
|
||||
core->resetCallSources(); // Force to regen each group call's sources
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Close an input device, please don't use unless everyone's unsuscribed
|
||||
*/
|
||||
void Audio::closeInput()
|
||||
{
|
||||
qDebug() << "Closing input";
|
||||
QMutexLocker lock(audioInLock);
|
||||
QMutexLocker locker(&audioInLock);
|
||||
if (alInDev)
|
||||
{
|
||||
#if (!FIX_SND_PCM_PREPARE_BUG)
|
||||
qDebug() << "stopping capture";
|
||||
alcCaptureStop(alInDev);
|
||||
#endif
|
||||
|
||||
if (alcCaptureCloseDevice(alInDev) == ALC_TRUE)
|
||||
{
|
||||
alInDev = nullptr;
|
||||
userCount = 0;
|
||||
inputSubscriptions = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -237,12 +300,22 @@ void Audio::closeInput()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Close an output device
|
||||
*/
|
||||
void Audio::closeOutput()
|
||||
{
|
||||
qDebug() << "Closing output";
|
||||
QMutexLocker lock(audioOutLock);
|
||||
QMutexLocker locker(&audioOutLock);
|
||||
|
||||
if (inputSubscriptions > 0)
|
||||
return;
|
||||
|
||||
if (alContext && alcMakeContextCurrent(nullptr) == ALC_TRUE)
|
||||
{
|
||||
alcDestroyContext(alContext);
|
||||
alContext = nullptr;
|
||||
}
|
||||
|
||||
if (alOutDev)
|
||||
{
|
||||
|
@ -253,11 +326,18 @@ void Audio::closeOutput()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Play a 44100Hz mono 16bit PCM sound
|
||||
*/
|
||||
void Audio::playMono16Sound(const QByteArray& data)
|
||||
{
|
||||
QMutexLocker lock(audioOutLock);
|
||||
QMutexLocker lock(&audioOutLock);
|
||||
|
||||
if (!alOutDev)
|
||||
return;
|
||||
{
|
||||
if (!openOutput(Settings::getInstance().getOutDev()))
|
||||
return;
|
||||
}
|
||||
|
||||
ALuint buffer;
|
||||
alGenBuffers(1, &buffer);
|
||||
|
@ -265,9 +345,32 @@ void Audio::playMono16Sound(const QByteArray& data)
|
|||
alSourcef(alMainSource, AL_GAIN, outputVolume);
|
||||
alSourcei(alMainSource, AL_BUFFER, buffer);
|
||||
alSourcePlay(alMainSource);
|
||||
|
||||
ALint sizeInBytes;
|
||||
ALint channels;
|
||||
ALint bits;
|
||||
|
||||
alGetBufferi(buffer, AL_SIZE, &sizeInBytes);
|
||||
alGetBufferi(buffer, AL_CHANNELS, &channels);
|
||||
alGetBufferi(buffer, AL_BITS, &bits);
|
||||
int lengthInSamples = sizeInBytes * 8 / (channels * bits);
|
||||
|
||||
ALint frequency;
|
||||
alGetBufferi(buffer, AL_FREQUENCY, &frequency);
|
||||
qreal duration = (lengthInSamples / static_cast<qreal>(frequency)) * 1000;
|
||||
int remaining = timer->interval();
|
||||
|
||||
if (duration > remaining)
|
||||
timer->start(duration);
|
||||
|
||||
alDeleteBuffers(1, &buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief May be called from any thread, will always queue a call to playGroupAudio.
|
||||
|
||||
The first and last argument are ignored, but allow direct compatibility with toxcore.
|
||||
*/
|
||||
void Audio::playGroupAudioQueued(Tox*,int group, int peer, const int16_t* data,
|
||||
unsigned samples, uint8_t channels, unsigned sample_rate, void* core)
|
||||
{
|
||||
|
@ -277,12 +380,16 @@ void Audio::playGroupAudioQueued(Tox*,int group, int peer, const int16_t* data,
|
|||
emit static_cast<Core*>(core)->groupPeerAudioPlaying(group, peer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Must be called from the audio thread, plays a group call's received audio
|
||||
*/
|
||||
void Audio::playGroupAudio(int group, int peer, const int16_t* data,
|
||||
unsigned samples, uint8_t channels, unsigned sample_rate)
|
||||
{
|
||||
assert(QThread::currentThread() == audioThread);
|
||||
|
||||
QMutexLocker lock(audioOutLock);
|
||||
QMutexLocker lock(&audioOutLock);
|
||||
|
||||
ToxGroupCall& call = Core::groupCalls[group];
|
||||
|
||||
|
@ -298,7 +405,7 @@ void Audio::playGroupAudio(int group, int peer, const int16_t* data,
|
|||
qreal volume = 0.;
|
||||
int bufsize = samples * 2 * channels;
|
||||
for (int i = 0; i < bufsize; ++i)
|
||||
volume += abs(data[i]);//std::max(volume, data[i]);
|
||||
volume += abs(data[i]);
|
||||
|
||||
emit groupAudioPlayed(group, peer, volume / bufsize);
|
||||
|
||||
|
@ -309,7 +416,7 @@ void Audio::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, u
|
|||
{
|
||||
assert(channels == 1 || channels == 2);
|
||||
|
||||
QMutexLocker lock(audioOutLock);
|
||||
QMutexLocker lock(&audioOutLock);
|
||||
|
||||
ALuint bufid;
|
||||
ALint processed = 0, queued = 16;
|
||||
|
@ -345,19 +452,30 @@ void Audio::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, u
|
|||
alSourcePlay(alSource);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns true if the input device is open and suscribed to
|
||||
*/
|
||||
bool Audio::isInputReady()
|
||||
{
|
||||
return (alInDev && userCount);
|
||||
QMutexLocker locker(&audioInLock);
|
||||
return alInDev && inputSubscriptions;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns true if the output device is open
|
||||
*/
|
||||
bool Audio::isOutputClosed()
|
||||
{
|
||||
return (alOutDev);
|
||||
QMutexLocker locker(&audioOutLock);
|
||||
return alOutDev;
|
||||
}
|
||||
|
||||
/**
|
||||
Does nothing and return false on failure
|
||||
*/
|
||||
bool Audio::tryCaptureSamples(uint8_t* buf, int framesize)
|
||||
{
|
||||
QMutexLocker lock(audioInLock);
|
||||
QMutexLocker lock(&audioInLock);
|
||||
|
||||
ALint samples=0;
|
||||
alcGetIntegerv(Audio::alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples);
|
||||
|
@ -366,6 +484,23 @@ bool Audio::tryCaptureSamples(uint8_t* buf, int framesize)
|
|||
|
||||
memset(buf, 0, framesize * 2 * av_DefaultSettings.audio_channels); // Avoid uninitialized values (Valgrind)
|
||||
alcCaptureSamples(Audio::alInDev, buf, framesize);
|
||||
|
||||
if (inputVolume != 1)
|
||||
{
|
||||
int16_t* bufReal = reinterpret_cast<int16_t*>(buf);
|
||||
for (int i = 0; i < framesize; ++i)
|
||||
{
|
||||
int sample = bufReal[i] * pow(inputVolume, 2);
|
||||
|
||||
if (sample < std::numeric_limits<int16_t>::min())
|
||||
sample = std::numeric_limits<int16_t>::min();
|
||||
else if (sample > std::numeric_limits<int16_t>::max())
|
||||
sample = std::numeric_limits<int16_t>::max();
|
||||
|
||||
bufReal[i] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#define AUDIO_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include <QMutexLocker>
|
||||
#include <atomic>
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
|
@ -33,11 +33,7 @@
|
|||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
class QString;
|
||||
class QByteArray;
|
||||
class QTimer;
|
||||
class QThread;
|
||||
class QMutex;
|
||||
struct Tox;
|
||||
class AudioFilterer;
|
||||
|
||||
|
@ -46,28 +42,27 @@ class Audio : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static Audio& getInstance(); ///< Returns the singleton's instance. Will construct on first call.
|
||||
static Audio& getInstance();
|
||||
|
||||
static float getOutputVolume(); ///< Returns the current output volume, between 0 and 1
|
||||
static void setOutputVolume(float volume); ///< The volume must be between 0 and 1
|
||||
public:
|
||||
void startAudioThread();
|
||||
|
||||
static void suscribeInput(); ///< Call when you need to capture sound from the open input device.
|
||||
static void unsuscribeInput(); ///< Call once you don't need to capture on the open input device anymore.
|
||||
qreal getOutputVolume();
|
||||
void setOutputVolume(qreal volume);
|
||||
|
||||
static void openInput(const QString& inDevDescr); ///< Open an input device, use before suscribing
|
||||
static void openOutput(const QString& outDevDescr); ///< Open an output device
|
||||
void setInputVolume(qreal volume);
|
||||
|
||||
static void closeInput(); ///< Close an input device, please don't use unless everyone's unsuscribed
|
||||
static void closeOutput(); ///< Close an output device
|
||||
void subscribeInput();
|
||||
void unsubscribeInput();
|
||||
void openInput(const QString& inDevDescr);
|
||||
bool openOutput(const QString& outDevDescr);
|
||||
|
||||
static bool isInputReady(); ///< Returns true if the input device is open and suscribed to
|
||||
static bool isOutputClosed(); ///< Returns true if the output device is open
|
||||
bool isInputReady();
|
||||
bool isOutputClosed();
|
||||
|
||||
static void playMono16Sound(const QByteArray& data); ///< Play a 44100Hz mono 16bit PCM sound
|
||||
static bool tryCaptureSamples(uint8_t* buf, int framesize); ///< Does nothing and return false on failure
|
||||
void playMono16Sound(const QByteArray& data);
|
||||
bool tryCaptureSamples(uint8_t* buf, int framesize);
|
||||
|
||||
/// May be called from any thread, will always queue a call to playGroupAudio
|
||||
/// The first and last argument are ignored, but allow direct compatibility with toxcore
|
||||
static void playGroupAudioQueued(Tox*, int group, int peer, const int16_t* data,
|
||||
unsigned samples, uint8_t channels, unsigned sample_rate, void*);
|
||||
|
||||
|
@ -77,7 +72,8 @@ public:
|
|||
#endif
|
||||
|
||||
public slots:
|
||||
/// Must be called from the audio thread, plays a group call's received audio
|
||||
void closeInput();
|
||||
void closeOutput();
|
||||
void playGroupAudio(int group, int peer, const int16_t* data,
|
||||
unsigned samples, uint8_t channels, unsigned sample_rate);
|
||||
|
||||
|
@ -85,19 +81,26 @@ signals:
|
|||
void groupAudioPlayed(int group, int peer, unsigned short volume);
|
||||
|
||||
private:
|
||||
explicit Audio()=default;
|
||||
Audio();
|
||||
~Audio();
|
||||
static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate);
|
||||
|
||||
void playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate);
|
||||
|
||||
private:
|
||||
static Audio* instance;
|
||||
static std::atomic<int> userCount;
|
||||
static ALCdevice* alOutDev, *alInDev;
|
||||
static QMutex* audioInLock, *audioOutLock;
|
||||
static float outputVolume;
|
||||
static ALuint alMainSource;
|
||||
static QThread* audioThread;
|
||||
static ALCcontext* alContext;
|
||||
|
||||
private:
|
||||
QThread* audioThread;
|
||||
QMutex audioInLock;
|
||||
QMutex audioOutLock;
|
||||
std::atomic<int> inputSubscriptions;
|
||||
ALCdevice* alOutDev;
|
||||
ALCdevice* alInDev;
|
||||
qreal outputVolume;
|
||||
qreal inputVolume;
|
||||
ALuint alMainSource;
|
||||
ALCcontext* alContext;
|
||||
QTimer* timer;
|
||||
};
|
||||
|
||||
#endif // AUDIO_H
|
||||
|
|
|
@ -80,12 +80,6 @@ Core::Core(QThread *CoreThread, Profile& profile) :
|
|||
calls[i].sendAudioTimer = new QTimer();
|
||||
calls[i].sendAudioTimer->moveToThread(coreThread);
|
||||
}
|
||||
|
||||
// OpenAL init
|
||||
QString outDevDescr = Settings::getInstance().getOutDev();
|
||||
Audio::openOutput(outDevDescr);
|
||||
QString inDevDescr = Settings::getInstance().getInDev();
|
||||
Audio::openInput(inDevDescr);
|
||||
}
|
||||
|
||||
void Core::deadifyTox()
|
||||
|
@ -131,8 +125,9 @@ Core::~Core()
|
|||
delete[] videobuf;
|
||||
videobuf=nullptr;
|
||||
|
||||
Audio::closeInput();
|
||||
Audio::closeOutput();
|
||||
Audio& audio = Audio::getInstance();
|
||||
audio.closeInput();
|
||||
audio.closeOutput();
|
||||
}
|
||||
|
||||
Core* Core::getInstance()
|
||||
|
|
|
@ -70,7 +70,7 @@ void Core::prepareCall(uint32_t friendId, int32_t callId, ToxAv* toxav, bool vid
|
|||
qWarning() << QString("Error starting call %1: toxav_prepare_transmission failed with %2").arg(callId).arg(r);
|
||||
|
||||
// Audio
|
||||
Audio::suscribeInput();
|
||||
Audio::getInstance().subscribeInput();
|
||||
|
||||
// Go
|
||||
calls[callId].active = true;
|
||||
|
@ -175,7 +175,6 @@ void Core::answerCall(int32_t callId)
|
|||
void Core::hangupCall(int32_t callId)
|
||||
{
|
||||
qDebug() << QString("hanging up call %1").arg(callId);
|
||||
calls[callId].active = false;
|
||||
toxav_hangup(toxav, callId);
|
||||
}
|
||||
|
||||
|
@ -249,7 +248,7 @@ void Core::cleanupCall(int32_t callId)
|
|||
}
|
||||
}
|
||||
|
||||
Audio::unsuscribeInput();
|
||||
Audio::getInstance().unsubscribeInput();
|
||||
toxav_kill_transmission(Core::getInstance()->toxav, callId);
|
||||
|
||||
if (!anyActiveCalls())
|
||||
|
@ -279,7 +278,7 @@ void Core::sendCallAudio(int32_t callId, ToxAv* toxav)
|
|||
if (!calls[callId].active)
|
||||
return;
|
||||
|
||||
if (calls[callId].muteMic || !Audio::isInputReady())
|
||||
if (calls[callId].muteMic || !Audio::getInstance().isInputReady())
|
||||
{
|
||||
calls[callId].sendAudioTimer->start();
|
||||
return;
|
||||
|
@ -289,7 +288,7 @@ void Core::sendCallAudio(int32_t callId, ToxAv* toxav)
|
|||
const int bufsize = framesize * 2 * av_DefaultSettings.audio_channels;
|
||||
uint8_t buf[bufsize];
|
||||
|
||||
if (Audio::tryCaptureSamples(buf, framesize))
|
||||
if (Audio::getInstance().tryCaptureSamples(buf, framesize))
|
||||
{
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
if (Settings::getInstance().getFilterAudio())
|
||||
|
@ -603,7 +602,7 @@ void Core::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, un
|
|||
|
||||
ALint state;
|
||||
alGetSourcei(alSource, AL_SOURCE_STATE, &state);
|
||||
alSourcef(alSource, AL_GAIN, Audio::getOutputVolume());
|
||||
alSourcef(alSource, AL_GAIN, Audio::getInstance().getOutputVolume());
|
||||
if (state != AL_PLAYING)
|
||||
{
|
||||
alSourcePlay(alSource);
|
||||
|
@ -629,7 +628,7 @@ void Core::joinGroupCall(int groupId)
|
|||
groupCalls[groupId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
|
||||
|
||||
// Audio
|
||||
Audio::suscribeInput();
|
||||
Audio::getInstance().subscribeInput();
|
||||
|
||||
// Go
|
||||
Core* core = Core::getInstance();
|
||||
|
@ -652,7 +651,7 @@ void Core::leaveGroupCall(int groupId)
|
|||
for (ALuint source : groupCalls[groupId].alSources)
|
||||
alDeleteSources(1, &source);
|
||||
groupCalls[groupId].alSources.clear();
|
||||
Audio::unsuscribeInput();
|
||||
Audio::getInstance().unsubscribeInput();
|
||||
delete groupCalls[groupId].sendAudioTimer;
|
||||
}
|
||||
|
||||
|
@ -661,7 +660,7 @@ void Core::sendGroupCallAudio(int groupId, ToxAv* toxav)
|
|||
if (!groupCalls[groupId].active)
|
||||
return;
|
||||
|
||||
if (groupCalls[groupId].muteMic || !Audio::isInputReady())
|
||||
if (groupCalls[groupId].muteMic || !Audio::getInstance().isInputReady())
|
||||
{
|
||||
groupCalls[groupId].sendAudioTimer->start();
|
||||
return;
|
||||
|
@ -671,7 +670,7 @@ void Core::sendGroupCallAudio(int groupId, ToxAv* toxav)
|
|||
const int bufsize = framesize * 2 * av_DefaultSettings.audio_channels;
|
||||
uint8_t buf[bufsize];
|
||||
|
||||
if (Audio::tryCaptureSamples(buf, framesize))
|
||||
if (Audio::getInstance().tryCaptureSamples(buf, framesize))
|
||||
{
|
||||
if (toxav_group_send_audio(toxav_get_tox(toxav), groupId, (int16_t*)buf,
|
||||
framesize, av_DefaultSettings.audio_channels, av_DefaultSettings.audio_sample_rate) < 0)
|
||||
|
|
|
@ -227,6 +227,8 @@ void Settings::loadGlobal()
|
|||
s.beginGroup("Audio");
|
||||
inDev = s.value("inDev", "").toString();
|
||||
outDev = s.value("outDev", "").toString();
|
||||
inVolume = s.value("inVolume", 100).toInt();
|
||||
outVolume = s.value("outVolume", 100).toInt();
|
||||
filterAudio = s.value("filterAudio", false).toBool();
|
||||
s.endGroup();
|
||||
|
||||
|
@ -427,6 +429,8 @@ void Settings::saveGlobal()
|
|||
s.beginGroup("Audio");
|
||||
s.setValue("inDev", inDev);
|
||||
s.setValue("outDev", outDev);
|
||||
s.setValue("inVolume", inVolume);
|
||||
s.setValue("outVolume", outVolume);
|
||||
s.setValue("filterAudio", filterAudio);
|
||||
s.endGroup();
|
||||
|
||||
|
@ -1138,6 +1142,18 @@ void Settings::setInDev(const QString& deviceSpecifier)
|
|||
inDev = deviceSpecifier;
|
||||
}
|
||||
|
||||
int Settings::getInVolume() const
|
||||
{
|
||||
QMutexLocker locker{&bigLock};
|
||||
return inVolume;
|
||||
}
|
||||
|
||||
void Settings::setInVolume(int volume)
|
||||
{
|
||||
QMutexLocker locker{&bigLock};
|
||||
inVolume = volume;
|
||||
}
|
||||
|
||||
QString Settings::getVideoDev() const
|
||||
{
|
||||
QMutexLocker locker{&bigLock};
|
||||
|
@ -1162,6 +1178,18 @@ void Settings::setOutDev(const QString& deviceSpecifier)
|
|||
outDev = deviceSpecifier;
|
||||
}
|
||||
|
||||
int Settings::getOutVolume() const
|
||||
{
|
||||
QMutexLocker locker{&bigLock};
|
||||
return outVolume;
|
||||
}
|
||||
|
||||
void Settings::setOutVolume(int volume)
|
||||
{
|
||||
QMutexLocker locker{&bigLock};
|
||||
outVolume = volume;
|
||||
}
|
||||
|
||||
bool Settings::getFilterAudio() const
|
||||
{
|
||||
QMutexLocker locker{&bigLock};
|
||||
|
|
|
@ -154,6 +154,12 @@ public:
|
|||
QString getOutDev() const;
|
||||
void setOutDev(const QString& deviceSpecifier);
|
||||
|
||||
int getInVolume() const;
|
||||
void setInVolume(int volume);
|
||||
|
||||
int getOutVolume() const;
|
||||
void setOutVolume(int volume);
|
||||
|
||||
bool getFilterAudio() const;
|
||||
void setFilterAudio(bool newValue);
|
||||
|
||||
|
@ -374,6 +380,8 @@ private:
|
|||
// Audio
|
||||
QString inDev;
|
||||
QString outDev;
|
||||
int inVolume;
|
||||
int outVolume;
|
||||
bool filterAudio;
|
||||
|
||||
// Video
|
||||
|
|
|
@ -62,8 +62,10 @@ AVForm::AVForm() :
|
|||
|
||||
connect(bodyUI->filterAudio, &QCheckBox::toggled, this, &AVForm::onFilterAudioToggled);
|
||||
connect(bodyUI->rescanButton, &QPushButton::clicked, this, [=](){getAudioInDevices(); getAudioOutDevices();});
|
||||
bodyUI->playbackSlider->setValue(100);
|
||||
bodyUI->microphoneSlider->setValue(100);
|
||||
connect(bodyUI->playbackSlider, &QSlider::sliderReleased, this, &AVForm::onPlaybackSliderReleased);
|
||||
connect(bodyUI->microphoneSlider, &QSlider::sliderReleased, this, &AVForm::onMicrophoneSliderReleased);
|
||||
bodyUI->playbackSlider->setValue(Settings::getInstance().getOutVolume());
|
||||
bodyUI->microphoneSlider->setValue(Settings::getInstance().getInVolume());
|
||||
|
||||
for (QComboBox* cb : findChildren<QComboBox*>())
|
||||
{
|
||||
|
@ -87,6 +89,7 @@ void AVForm::showEvent(QShowEvent*)
|
|||
getAudioInDevices();
|
||||
createVideoSurface();
|
||||
getVideoDevices();
|
||||
Audio::getInstance().subscribeInput();
|
||||
}
|
||||
|
||||
void AVForm::onVideoModesIndexChanged(int index)
|
||||
|
@ -217,6 +220,7 @@ void AVForm::hideEvent(QHideEvent *)
|
|||
killVideoSurface();
|
||||
}
|
||||
videoDeviceList.clear();
|
||||
Audio::getInstance().unsubscribeInput();
|
||||
}
|
||||
|
||||
void AVForm::getVideoDevices()
|
||||
|
@ -310,13 +314,19 @@ void AVForm::getAudioOutDevices()
|
|||
void AVForm::onInDevChanged(const QString &deviceDescriptor)
|
||||
{
|
||||
Settings::getInstance().setInDev(deviceDescriptor);
|
||||
Audio::openInput(deviceDescriptor);
|
||||
|
||||
Audio& audio = Audio::getInstance();
|
||||
audio.unsubscribeInput();
|
||||
audio.subscribeInput();
|
||||
}
|
||||
|
||||
void AVForm::onOutDevChanged(const QString& deviceDescriptor)
|
||||
{
|
||||
Settings::getInstance().setOutDev(deviceDescriptor);
|
||||
Audio::openOutput(deviceDescriptor);
|
||||
|
||||
Audio& audio = Audio::getInstance();
|
||||
audio.unsubscribeInput();
|
||||
audio.subscribeInput();
|
||||
}
|
||||
|
||||
void AVForm::onFilterAudioToggled(bool filterAudio)
|
||||
|
@ -326,16 +336,26 @@ void AVForm::onFilterAudioToggled(bool filterAudio)
|
|||
|
||||
void AVForm::on_playbackSlider_valueChanged(int value)
|
||||
{
|
||||
Audio::setOutputVolume(value / 100.0);
|
||||
Audio::getInstance().setOutputVolume(value / 100.0);
|
||||
bodyUI->playbackMax->setText(QString::number(value));
|
||||
}
|
||||
|
||||
void AVForm::on_microphoneSlider_valueChanged(int value)
|
||||
{
|
||||
Audio::setOutputVolume(value / 100.0);
|
||||
Audio::getInstance().setInputVolume(value / 100.0);
|
||||
bodyUI->microphoneMax->setText(QString::number(value));
|
||||
}
|
||||
|
||||
void AVForm::onPlaybackSliderReleased()
|
||||
{
|
||||
Settings::getInstance().setOutVolume(bodyUI->playbackSlider->value());
|
||||
}
|
||||
|
||||
void AVForm::onMicrophoneSliderReleased()
|
||||
{
|
||||
Settings::getInstance().setInVolume(bodyUI->microphoneSlider->value());
|
||||
}
|
||||
|
||||
bool AVForm::eventFilter(QObject *o, QEvent *e)
|
||||
{
|
||||
if ((e->type() == QEvent::Wheel) &&
|
||||
|
|
|
@ -59,6 +59,8 @@ private slots:
|
|||
void onFilterAudioToggled(bool filterAudio);
|
||||
void on_playbackSlider_valueChanged(int value);
|
||||
void on_microphoneSlider_valueChanged(int value);
|
||||
void onPlaybackSliderReleased();
|
||||
void onMicrophoneSliderReleased();
|
||||
|
||||
// camera
|
||||
void onVideoDevChanged(int index);
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="filterAudio">
|
||||
<property name="toolTip">
|
||||
<string>Filter sound from your microphone, so that people hearing you would get better sound.</string>
|
||||
|
@ -74,15 +74,15 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="microphoneLabel">
|
||||
<property name="text">
|
||||
<string>Microphone</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QLabel" name="playbackMax">
|
||||
<item row="5" column="3">
|
||||
<widget class="QLabel" name="microphoneMax">
|
||||
<property name="text">
|
||||
<string>100</string>
|
||||
</property>
|
||||
|
@ -95,27 +95,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="3">
|
||||
<widget class="QLabel" name="microphoneMax">
|
||||
<item row="2" column="3">
|
||||
<widget class="QLabel" name="playbackMax">
|
||||
<property name="text">
|
||||
<string>100</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QSlider" name="microphoneSlider">
|
||||
<property name="toolTip">
|
||||
<string>Use slider to set volume of your microphone.
|
||||
WARNING: slider is not supposed to work yet.</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="inDevLabel">
|
||||
<property name="text">
|
||||
|
@ -123,13 +109,26 @@ WARNING: slider is not supposed to work yet.</string>
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="microphoneMin">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QSlider" name="microphoneSlider">
|
||||
<property name="toolTip">
|
||||
<string>Use slider to set volume of your microphone.</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>400</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="playbackLabel">
|
||||
<property name="text">
|
||||
|
@ -147,6 +146,9 @@ WARNING: slider is not supposed to work yet.</string>
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="2">
|
||||
<widget class="MicFeedbackWidget" name="microphoneFeedback" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -234,6 +236,12 @@ which may lead to problems with video calls.</string>
|
|||
<header>src/widget/form/settings/verticalonlyscroller.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>MicFeedbackWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>src/widget/tool/micfeedbackwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
|
104
src/widget/tool/micfeedbackwidget.cpp
Normal file
104
src/widget/tool/micfeedbackwidget.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
Copyright © 2015 by The qTox Project
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "micfeedbackwidget.h"
|
||||
#include "src/audio/audio.h"
|
||||
#include <QPainter>
|
||||
#include <QLinearGradient>
|
||||
|
||||
MicFeedbackWidget::MicFeedbackWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, timerId(0)
|
||||
{
|
||||
setFixedHeight(20);
|
||||
}
|
||||
|
||||
void MicFeedbackWidget::paintEvent(QPaintEvent*)
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.setPen(QPen(Qt::black));
|
||||
painter.drawRect(QRect(0, 0, width() - 1, height() - 1));
|
||||
|
||||
int gradientWidth = round(width() * current) - 4;
|
||||
|
||||
if (gradientWidth < 0)
|
||||
gradientWidth = 0;
|
||||
|
||||
QRect gradientRect(2, 2, gradientWidth, height() - 4);
|
||||
|
||||
QLinearGradient gradient(0, 0, width(), 0);
|
||||
gradient.setColorAt(0, Qt::green);
|
||||
gradient.setColorAt(0.5, Qt::yellow);
|
||||
gradient.setColorAt(1, Qt::red);
|
||||
painter.fillRect(gradientRect, gradient);
|
||||
|
||||
float slice = width() / 5;
|
||||
int padding = slice / 2;
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
float pos = slice * i + padding;
|
||||
painter.drawLine(pos, 2, pos, height() - 4);
|
||||
}
|
||||
}
|
||||
|
||||
void MicFeedbackWidget::timerEvent(QTimerEvent*)
|
||||
{
|
||||
const int framesize = (20 * 48000) / 1000;
|
||||
const int bufsize = framesize * 2;
|
||||
uint8_t buff[bufsize];
|
||||
memset(buff, 0, bufsize);
|
||||
|
||||
if (Audio::getInstance().tryCaptureSamples(buff, framesize))
|
||||
{
|
||||
double max = 0;
|
||||
int16_t* buffReal = reinterpret_cast<int16_t*>(&buff[0]);
|
||||
|
||||
for (int i = 0; i < bufsize / 2; ++i)
|
||||
max = std::max(max, fabs(buffReal[i] / 32767.0));
|
||||
|
||||
if (max > current)
|
||||
current = max;
|
||||
else
|
||||
current -= 0.05;
|
||||
|
||||
update();
|
||||
}
|
||||
else if (current > 0)
|
||||
{
|
||||
current -= 0.01;
|
||||
}
|
||||
}
|
||||
|
||||
void MicFeedbackWidget::showEvent(QShowEvent*)
|
||||
{
|
||||
Audio::getInstance().subscribeInput();
|
||||
timerId = startTimer(60);
|
||||
}
|
||||
|
||||
void MicFeedbackWidget::hideEvent(QHideEvent*)
|
||||
{
|
||||
Audio::getInstance().unsubscribeInput();
|
||||
|
||||
if (timerId != 0)
|
||||
{
|
||||
killTimer(timerId);
|
||||
timerId = 0;
|
||||
}
|
||||
}
|
42
src/widget/tool/micfeedbackwidget.h
Normal file
42
src/widget/tool/micfeedbackwidget.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Copyright © 2015 by The qTox Project
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MICFEEDBACKWIDGET_H
|
||||
#define MICFEEDBACKWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class MicFeedbackWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MicFeedbackWidget(QWidget *parent = 0);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void timerEvent(QTimerEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
|
||||
private:
|
||||
double current;
|
||||
int timerId;
|
||||
};
|
||||
|
||||
#endif // MICFEEDBACKWIDGET_H
|
|
@ -1249,7 +1249,7 @@ bool Widget::newMessageAlert(QWidget* currentWindow, bool isActive, bool notify)
|
|||
sndFile.close();
|
||||
}
|
||||
|
||||
Audio::playMono16Sound(sndData);
|
||||
Audio::getInstance().playMono16Sound(sndData);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1271,7 +1271,7 @@ void Widget::playRingtone()
|
|||
sndFile1.close();
|
||||
}
|
||||
|
||||
Audio::playMono16Sound(sndData1);
|
||||
Audio::getInstance().playMono16Sound(sndData1);
|
||||
}
|
||||
|
||||
void Widget::onFriendRequestReceived(const QString& userId, const QString& message)
|
||||
|
|
Loading…
Reference in New Issue
Block a user