started conversion of VOIP code (from Joss) into a plugin. Unfinished!

git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@4945 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
csoler 2012-02-15 21:17:18 +00:00
parent 8f0f6a9f3e
commit fc15899f0f
23 changed files with 10288 additions and 0 deletions

View File

@ -0,0 +1,274 @@
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
Copyright (C) 2008, Andreas Messer <andi@bupfen.de>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the Mumble Developers nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//#include "AudioInput.h"
//#include "AudioOutput.h"
#include "AudioStats.h"
#include "AudioInputConfig.h"
//#include "Global.h"
//#include "NetworkConfig.h"
#include "rsharesettings.h"
#include "util/audiodevicehelper.h"
#include "AudioWizard.h"
#define iroundf(x) ( static_cast<int>(x) )
/*void AudioInputDialog::hideEvent(QHideEvent *) {
qtTick->stop();
}
void AudioInputDialog::showEvent(QShowEvent *) {
qtTick->start(20);
}*/
/** Constructor */
AudioInputConfig::AudioInputConfig(QWidget * parent, Qt::WFlags flags)
: ConfigPage(parent, flags)
{
/* Invoke the Qt Designer generated object setup routine */
ui.setupUi(this);
loaded = false;
}
AudioInputConfig::~AudioInputConfig()
{
if (inputDevice) {
inputDevice->stop();
}
}
/** Loads the settings for this page */
void AudioInputConfig::load()
{
//connect( ui.allowIpDeterminationCB, SIGNAL( toggled( bool ) ), this, SLOT( toggleIpDetermination(bool) ) );
//connect( ui.allowTunnelConnectionCB, SIGNAL( toggled( bool ) ), this, SLOT( toggleTunnelConnection(bool) ) );
qtTick = new QTimer(this);
connect( qtTick, SIGNAL( timeout ( ) ), this, SLOT( on_Tick_timeout() ) );
qtTick->start(20);
/*if (AudioInputRegistrar::qmNew) {
QList<QString> keys = AudioInputRegistrar::qmNew->keys();
foreach(QString key, keys) {
qcbSystem->addItem(key);
}
}
qcbSystem->setEnabled(qcbSystem->count() > 1);*/
ui.qcbTransmit->addItem(tr("Continuous"), RshareSettings::AudioTransmitContinous);
ui.qcbTransmit->addItem(tr("Voice Activity"), RshareSettings::AudioTransmitVAD);
ui.qcbTransmit->addItem(tr("Push To Talk"), RshareSettings::AudioTransmitPushToTalk);
abSpeech = new AudioBar();
abSpeech->qcBelow = Qt::red;
abSpeech->qcInside = Qt::yellow;
abSpeech->qcAbove = Qt::green;
//abSpeech->setGeometry(9,20,50,10);
ui.qwVadLayout_2->addWidget(abSpeech,0,0,1,0);
//on_qcbPushClick_clicked(g.s.bPushClick);
//ui.on_Tick_timeout();
loadSettings();
inputProcessor = NULL;
inputDevice = NULL;
}
void AudioInputConfig::loadSettings() {
/*QList<QString> keys;
if (AudioInputRegistrar::qmNew)
keys=AudioInputRegistrar::qmNew->keys();
else
keys.clear();
i=keys.indexOf(AudioInputRegistrar::current);
if (i >= 0)
loadComboBox(qcbSystem, i);
loadCheckBox(qcbExclusive, r.bExclusiveInput);*/
//qlePushClickPathOn->setText(r.qsPushClickOn);
//qlePushClickPathOff->setText(r.qsPushClickOff);
/*loadComboBox(qcbTransmit, r.atTransmit);
loadSlider(qsTransmitHold, r.iVoiceHold);
loadSlider(qsTransmitMin, iroundf(r.fVADmin * 32767.0f + 0.5f));
loadSlider(qsTransmitMax, iroundf(r.fVADmax * 32767.0f + 0.5f));
loadSlider(qsFrames, (r.iFramesPerPacket == 1) ? 1 : (r.iFramesPerPacket/2 + 1));
loadSlider(qsDoublePush, iroundf(static_cast<float>(r.uiDoublePush) / 1000.f + 0.5f));*/
ui.qcbTransmit->setCurrentIndex(Settings->getVoipATransmit()-1);
on_qcbTransmit_currentIndexChanged(Settings->getVoipATransmit()-1);
ui.qsTransmitHold->setValue(Settings->getVoipVoiceHold());
on_qsTransmitHold_valueChanged(Settings->getVoipVoiceHold());
ui.qsTransmitMin->setValue(Settings->getVoipfVADmin());
ui.qsTransmitMax->setValue(Settings->getVoipfVADmax());
ui.qcbEchoCancel->setChecked(Settings->getVoipEchoCancel());
//ui.qsDoublePush->setValue(iroundf(static_cast<float>(r.uiDoublePush) / 1000.f + 0.5f));
//loadCheckBox(qcbPushClick, r.bPushClick);
//loadSlider(qsQuality, r.iQuality);
if (Settings->getVoipiNoiseSuppress() != 0)
ui.qsNoise->setValue(-Settings->getVoipiNoiseSuppress());
else
ui.qsNoise->setValue(14);
on_qsNoise_valueChanged(-Settings->getVoipiNoiseSuppress());
ui.qsAmp->setValue(20000 - Settings->getVoipiMinLoudness());
on_qsAmp_valueChanged(20000 - Settings->getVoipiMinLoudness());
//loadSlider(qsIdle, r.iIdleTime);
/*int echo = 0;
if (r.bEcho)
echo = r.bEchoMulti ? 2 : 1;
loadComboBox(qcbEcho, echo);*/
connect( ui.qsTransmitHold, SIGNAL( valueChanged ( int ) ), this, SLOT( on_qsTransmitHold_valueChanged(int) ) );
connect( ui.qsNoise, SIGNAL( valueChanged ( int ) ), this, SLOT( on_qsNoise_valueChanged(int) ) );
connect( ui.qsAmp, SIGNAL( valueChanged ( int ) ), this, SLOT( on_qsAmp_valueChanged(int) ) );
connect( ui.qcbTransmit, SIGNAL( currentIndexChanged ( int ) ), this, SLOT( on_qcbTransmit_currentIndexChanged(int) ) );
loaded = true;
}
bool AudioInputConfig::save(QString &/*errmsg*/) {//mainly useless beacause saving occurs in realtime
//s.iQuality = qsQuality->value();
Settings->setVoipiNoiseSuppress((ui.qsNoise->value() == 14) ? 0 : - ui.qsNoise->value());
Settings->setVoipiMinLoudness(20000 - ui.qsAmp->value());
Settings->setVoipVoiceHold(ui.qsTransmitHold->value());
Settings->setVoipfVADmin(ui.qsTransmitMin->value());
Settings->setVoipfVADmax(ui.qsTransmitMax->value());
/*s.uiDoublePush = qsDoublePush->value() * 1000;*/
Settings->setVoipATransmit(static_cast<RshareSettings::enumAudioTransmit>(ui.qcbTransmit->currentIndex() + 1));
Settings->setVoipEchoCancel(ui.qcbEchoCancel->isChecked());
return true;
}
/*bool AudioInputDialog::expert(bool b) {
qgbInterfaces->setVisible(b);
qgbAudio->setVisible(b);
qliFrames->setVisible(b);
qsFrames->setVisible(b);
qlFrames->setVisible(b);
qswTransmit->setVisible(b);
qliIdle->setVisible(b);
qsIdle->setVisible(b);
qlIdle->setVisible(b);
return true;
}*/
void AudioInputConfig::on_qsTransmitHold_valueChanged(int v) {
float val = static_cast<float>(v * FRAME_SIZE);
val = val / SAMPLING_RATE;
ui.qlTransmitHold->setText(tr("%1 s").arg(val, 0, 'f', 2));
Settings->setVoipVoiceHold(v);
}
void AudioInputConfig::on_qsNoise_valueChanged(int v) {
QPalette pal;
if (v < 15) {
ui.qlNoise->setText(tr("Off"));
pal.setColor(ui.qlNoise->foregroundRole(), Qt::red);
} else {
ui.qlNoise->setText(tr("-%1 dB").arg(v));
}
ui.qlNoise->setPalette(pal);
Settings->setVoipiNoiseSuppress(- ui.qsNoise->value());
}
void AudioInputConfig::on_qsAmp_valueChanged(int v) {
v = 20000 - v;
float d = 20000.0f/static_cast<float>(v);
ui.qlAmp->setText(QString::fromLatin1("%1").arg(d, 0, 'f', 2));
Settings->setVoipiMinLoudness(20000 - ui.qsAmp->value());
}
void AudioInputConfig::on_qcbEchoCancel_clicked() {
Settings->setVoipEchoCancel(ui.qcbEchoCancel->isChecked());
}
void AudioInputConfig::on_qcbTransmit_currentIndexChanged(int v) {
switch (v) {
case 0:
ui.qswTransmit->setCurrentWidget(ui.qwContinuous);
break;
case 1:
ui.qswTransmit->setCurrentWidget(ui.qwVAD);
break;
case 2:
ui.qswTransmit->setCurrentWidget(ui.qwPTT);
break;
}
if (loaded)
Settings->setVoipATransmit(static_cast<RshareSettings::enumAudioTransmit>(ui.qcbTransmit->currentIndex() + 1));
}
void AudioInputConfig::on_Tick_timeout() {
if (!inputProcessor) {
inputProcessor = new QtSpeex::SpeexInputProcessor();
inputProcessor->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
if (!inputDevice) {
inputDevice = AudioDeviceHelper::getPreferedInputDevice();
}
inputDevice->start(inputProcessor);
connect(inputProcessor, SIGNAL(networkPacketReady()), this, SLOT(emptyBuffer()));
}
abSpeech->iBelow = ui.qsTransmitMin->value();
abSpeech->iAbove = ui.qsTransmitMax->value();
if (loaded) {
Settings->setVoipfVADmin(ui.qsTransmitMin->value());
Settings->setVoipfVADmax(ui.qsTransmitMax->value());
}
abSpeech->iValue = iroundf(inputProcessor->dVoiceAcivityLevel * 32767.0f + 0.5f);
abSpeech->update();
}
void AudioInputConfig::emptyBuffer() {
while(inputProcessor->hasPendingPackets()) {
inputProcessor->getNetworkPacket(); //that will purge the buffer
}
}
void AudioInputConfig::on_qpbAudioWizard_clicked() {
AudioWizard *aw = new AudioWizard(this);
aw->exec();
delete aw;
loadSettings();
}

View File

@ -0,0 +1,82 @@
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the Mumble Developers nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _AUDIOINPUTCONFIG_H
#define _AUDIOINPUTCONFIG_H
#include <QAudioInput>
#include <QWidget>
#include "configpage.h"
#include "ui_AudioInputConfig.h"
#include "util/SpeexProcessor.h"
#include "AudioStats.h"
class AudioInputConfig : public ConfigPage {
Q_OBJECT
private:
Ui::AudioInput ui;
QAudioInput* inputDevice;
QtSpeex::SpeexInputProcessor* inputProcessor;
AudioBar* abSpeech;
bool loaded;
protected:
QTimer *qtTick;
/*void hideEvent(QHideEvent *event);
void showEvent(QShowEvent *event);*/
public:
/** Default Constructor */
AudioInputConfig(QWidget * parent = 0, Qt::WFlags flags = 0);
/** Default Destructor */
~AudioInputConfig();
/** Saves the changes on this page */
bool save(QString &errmsg);
/** Loads the settings for this page */
void load();
private slots:
void loadSettings();
void emptyBuffer();
void on_qsTransmitHold_valueChanged(int v);
void on_qsAmp_valueChanged(int v);
void on_qsNoise_valueChanged(int v);
void on_qcbTransmit_currentIndexChanged(int v);
void on_Tick_timeout();
void on_qpbAudioWizard_clicked();
void on_qcbEchoCancel_clicked();
};
#endif

View File

@ -0,0 +1,378 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AudioInput</class>
<widget class="QWidget" name="AudioInput">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>508</width>
<height>378</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="qwVadLayout">
<item>
<widget class="QPushButton" name="qpbAudioWizard">
<property name="text">
<string>Audio Wizard</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="qgbTransmission">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Transmission</string>
</property>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QLabel" name="qliTransmit">
<property name="text">
<string>&amp;Transmit</string>
</property>
<property name="buddy">
<cstring>qcbTransmit</cstring>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="qcbTransmit">
<property name="toolTip">
<string>When to transmit your speech</string>
</property>
<property name="whatsThis">
<string>&lt;b&gt;This sets when speech should be transmitted.&lt;/b&gt;&lt;br /&gt;&lt;i&gt;Continuous&lt;/i&gt; - All the time&lt;br /&gt;&lt;i&gt;Voice Activity&lt;/i&gt; - When you are speaking clearly.&lt;br /&gt;&lt;i&gt;Push To Talk&lt;/i&gt; - When you hold down the hotkey set under &lt;i&gt;Shortcuts&lt;/i&gt;.</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QStackedWidget" name="qswTransmit">
<property name="currentIndex">
<number>2</number>
</property>
<widget class="QWidget" name="qwPTT">
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QLabel" name="qliDoublePush">
<property name="text">
<string>DoublePush Time</string>
</property>
<property name="buddy">
<cstring>qsDoublePush</cstring>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QSlider" name="qsDoublePush">
<property name="toolTip">
<string>If you press the PTT key twice in this time it will get locked.</string>
</property>
<property name="whatsThis">
<string>&lt;b&gt;DoublePush Time&lt;/b&gt;&lt;br /&gt;If you press the push-to-talk key twice during the configured interval of time it will be locked. Mumble will keep transmitting until you hit the key once more to unlock PTT again.</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="pageStep">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="qlDoublePush">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="qwVAD">
<layout class="QGridLayout" name="qwVadLayout_2">
<item row="1" column="0">
<widget class="QLabel" name="qliTransmitHold">
<property name="text">
<string>Voice &amp;Hold</string>
</property>
<property name="buddy">
<cstring>qsTransmitHold</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSlider" name="qsTransmitHold">
<property name="toolTip">
<string>How long to keep transmitting after silence</string>
</property>
<property name="whatsThis">
<string>&lt;b&gt;This selects how long after a perceived stop in speech transmission should continue.&lt;/b&gt;&lt;br /&gt;Set this higher if your voice breaks up when you speak (seen by a rapidly blinking voice icon next to your name).</string>
</property>
<property name="minimum">
<number>20</number>
</property>
<property name="maximum">
<number>250</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="qliTransmitMin">
<property name="text">
<string>Silence Below</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSlider" name="qsTransmitMin">
<property name="toolTip">
<string>Signal values below this count as silence</string>
</property>
<property name="whatsThis">
<string>&lt;b&gt;This sets the trigger values for voice detection.&lt;/b&gt;&lt;br /&gt;Use this together with the Audio Statistics window to manually tune the trigger values for detecting speech. Input values below &quot;Silence Below&quot; always count as silence. Values above &quot;Speech Above&quot; always count as voice. Values in between will count as voice if you're already talking, but will not trigger a new detection.</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>32767</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
<property name="pageStep">
<number>1000</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="qliTransmitMax">
<property name="text">
<string>Speech Above</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSlider" name="qsTransmitMax">
<property name="toolTip">
<string>Signal values above this count as voice</string>
</property>
<property name="whatsThis">
<string>&lt;b&gt;This sets the trigger values for voice detection.&lt;/b&gt;&lt;br /&gt;Use this together with the Audio Statistics window to manually tune the trigger values for detecting speech. Input values below &quot;Silence Below&quot; always count as silence. Values above &quot;Speech Above&quot; always count as voice. Values in between will count as voice if you're already talking, but will not trigger a new detection.</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>32767</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
<property name="pageStep">
<number>1000</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="qlTransmitHold">
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>empty</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="qwContinuous"/>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="qgbAudio">
<property name="title">
<string>Audio Processing</string>
</property>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QLabel" name="qliNoise">
<property name="text">
<string>Noise Suppression</string>
</property>
<property name="buddy">
<cstring>qsNoise</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSlider" name="qsNoise">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Noise suppression</string>
</property>
<property name="whatsThis">
<string>&lt;b&gt;This sets the amount of noise suppression to apply.&lt;/b&gt;&lt;br /&gt;The higher this value, the more aggressively stationary noise will be suppressed.</string>
</property>
<property name="minimum">
<number>14</number>
</property>
<property name="maximum">
<number>60</number>
</property>
<property name="pageStep">
<number>5</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="qlNoise">
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="qliAmp">
<property name="text">
<string>Amplification</string>
</property>
<property name="buddy">
<cstring>qsAmp</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSlider" name="qsAmp">
<property name="toolTip">
<string>Maximum amplification of input sound</string>
</property>
<property name="whatsThis">
<string>&lt;b&gt;Maximum amplification of input.&lt;/b&gt;&lt;br /&gt;Mumble normalizes the input volume before compressing, and this sets how much it's allowed to amplify.&lt;br /&gt;The actual level is continually updated based on your current speech pattern, but it will never go above the level specified here.&lt;br /&gt;If the &lt;i&gt;Microphone loudness&lt;/i&gt; level of the audio statistics hover around 100%, you probably want to set this to 2.0 or so, but if, like most people, you are unable to reach 100%, set this to something much higher.&lt;br /&gt;Ideally, set it so &lt;i&gt;Microphone Loudness * Amplification Factor &gt;= 100&lt;/i&gt;, even when you're speaking really soft.&lt;br /&gt;&lt;br /&gt;Note that there is no harm in setting this to maximum, but Mumble will start picking up other conversations if you leave it to auto-tune to that level.</string>
</property>
<property name="maximum">
<number>19500</number>
</property>
<property name="singleStep">
<number>500</number>
</property>
<property name="pageStep">
<number>2000</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="qlAmp">
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="qcbEchoCancel">
<property name="text">
<string>Echo Cancellation Processing</string>
</property>
<property name="tristate">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>151</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>
<tabstop>qcbTransmit</tabstop>
<tabstop>qsDoublePush</tabstop>
<tabstop>qsTransmitHold</tabstop>
<tabstop>qsTransmitMin</tabstop>
<tabstop>qsTransmitMax</tabstop>
<tabstop>qsNoise</tabstop>
<tabstop>qsAmp</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

411
plugins/VOIP/AudioStats.cpp Normal file
View File

@ -0,0 +1,411 @@
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the Mumble Developers nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define iroundf(x) ( static_cast<int>(x) )
#include "AudioStats.h"
#include "AudioInputConfig.h"
//#include "Global.h"
//#include "smallft.h"
AudioBar::AudioBar(QWidget *p) : QWidget(p) {
highContrast = false;
qcBelow = Qt::yellow;
qcAbove = Qt::red;
qcInside = Qt::green;
iMin = 0;
iMax = 32768;
iBelow = 2000;
iAbove = 22000;
iValue = 1000;
iPeak = -1;
setMinimumSize(100,20);
qlReplacableColors << Qt::yellow << Qt::red << Qt::green << Qt::blue;
qlReplacementBrushes << Qt::BDiagPattern << Qt::DiagCrossPattern << Qt::NoBrush << Qt::FDiagPattern;
}
void AudioBar::paintEvent(QPaintEvent *) {
QPainter p(this);
if (isEnabled()) {
qcBelow.setAlphaF(1.0f);
qcAbove.setAlphaF(1.0f);
qcInside.setAlphaF(1.0f);
} else {
qcBelow.setAlphaF(0.5f);
qcAbove.setAlphaF(0.5f);
qcInside.setAlphaF(0.5f);
}
if (iBelow > iAbove)
iBelow = iAbove;
if (iValue < iMin)
iValue = iMin;
else if (iValue > iMax)
iValue = iMax;
float scale = static_cast<float>(width()) / static_cast<float>(iMax - iMin);
int h = height();
int val = iroundf(static_cast<float>(iValue) * scale + 0.5f);
int below = iroundf(static_cast<float>(iBelow) * scale + 0.5f);
int above = iroundf(static_cast<float>(iAbove) * scale + 0.5f);
int max = iroundf(static_cast<float>(iMax) * scale + 0.5f);
int min = iroundf(static_cast<float>(iMin) * scale + 0.5f);
int peak = iroundf(static_cast<float>(iPeak) * scale + 0.5f);
if (highContrast) {
// Draw monochrome representation
QColor fg = QPalette().foreground().color();
p.fillRect(0, 0, below, h, QBrush(fg, qlReplacementBrushes.value(qlReplacableColors.indexOf(qcBelow), Qt::CrossPattern)));
p.fillRect(below, 0, above - below, h, QBrush(fg, qlReplacementBrushes.value(qlReplacableColors.indexOf(qcInside), Qt::NoBrush)));
p.fillRect(above, 0, max - above, h, QBrush(fg, qlReplacementBrushes.value(qlReplacableColors.indexOf(qcAbove), Qt::CrossPattern)));
p.fillRect(0, 0, val, h, QBrush(fg, Qt::SolidPattern));
p.drawRect(0, 0, max - 1, h - 1);
p.drawLine(below, 0, below, h);
p.drawLine(above, 0, above, h);
} else {
if (val <= below) {
p.fillRect(0, 0, val, h, qcBelow);
p.fillRect(val, 0, below-val, h, qcBelow.darker(300));
p.fillRect(below, 0, above-below, h, qcInside.darker(300));
p.fillRect(above, 0, max-above, h, qcAbove.darker(300));
} else if (val <= above) {
p.fillRect(0, 0, below, h, qcBelow);
p.fillRect(below, 0, val-below, h, qcInside);
p.fillRect(val, 0, above-val, h, qcInside.darker(300));
p.fillRect(above, 0, max-above, h, qcAbove.darker(300));
} else {
p.fillRect(0, 0, below, h, qcBelow);
p.fillRect(below, 0, above-below, h, qcInside);
p.fillRect(above, 0, val-above, h, qcAbove);
p.fillRect(val, 0, max-val, h, qcAbove.darker(300));
}
}
if ((peak >= min) && (peak <= max)) {
if (peak <= below)
p.setPen(qcBelow.lighter(150));
else if (peak <= above)
p.setPen(qcInside.lighter(150));
else
p.setPen(qcAbove.lighter(150));
p.drawLine(peak, 0, peak, h);
}
}
/*
AudioEchoWidget::AudioEchoWidget(QWidget *p) : QWidget(p) {
setMinimumSize(100, 60);
}
static inline const QColor mapEchoToColor(float echo) {
bool neg = (echo < 0.0f);
echo = fabsf(echo);
float a, b, c;
if (echo > 1.0f) {
echo = 1.0f;
c = 0.5f;
} else {
c = 0.0f;
}
if (echo < 0.5f) {
a = echo * 2.0f;
b = 0.0f;
} else {
a = 1.0f;
b = (echo - 0.5f) * 2.0f;
}
if (neg)
return QColor::fromRgbF(a, b, c);
else
return QColor::fromRgbF(c, b, a);
}
#define WGT(x,y) st->W[(y)*N + 2*(x)+1]
void AudioEchoWidget::paintEvent(QPaintEvent *) {
QPainter paint(this);
paint.scale(width(), height());
paint.fillRect(rect(), Qt::black);
AudioInputPtr ai = g.ai;
if (! ai || ! ai->sesEcho)
return;
ai->qmSpeex.lock();
spx_int32_t sz;
speex_echo_ctl(ai->sesEcho, SPEEX_ECHO_GET_IMPULSE_RESPONSE_SIZE, &sz);
STACKVAR(spx_int32_t, w, sz);
STACKVAR(float, W, sz);
speex_echo_ctl(ai->sesEcho, SPEEX_ECHO_GET_IMPULSE_RESPONSE, w);
ai->qmSpeex.unlock();
int N = 160;
int n = 2 * N;
int M = sz / n;
drft_lookup d;
mumble_drft_init(&d, n);
for (int j=0;j<M;j++) {
for (int i=0;i<n;i++)
W[j*n+i] = static_cast<float>(w[j*n+i]) / static_cast<float>(n);
mumble_drft_forward(&d, & W[j*n]);
}
mumble_drft_clear(&d);
float xscale = 1.0f / static_cast<float>(N);
float yscale = 1.0f / static_cast<float>(M);
for (int j = 0; j < M; j++) {
for (int i=1;i < N; i++) {
float xa = static_cast<float>(i) * xscale;
float ya = static_cast<float>(j) * yscale;
float xb = xa + xscale;
float yb = ya + yscale;
const QColor &c = mapEchoToColor(sqrtf(W[j*n+2*i]*W[j*n+2*i]+W[j*n+2*i-1]*W[j*n+2*i-1]) / 65536.f);
paint.fillRect(QRectF(QPointF(xa, ya), QPointF(xb, yb)), c);
}
}
QPolygonF poly;
xscale = 1.0f / (2.0f * static_cast<float>(n));
yscale = 1.0f / (200.0f * 32767.0f);
for (int i = 0; i < 2 * n; i++) {
poly << QPointF(static_cast<float>(i) * xscale, 0.5f + static_cast<float>(w[i]) * yscale);
}
paint.setPen(QColor::fromRgbF(1.0f, 0.0f, 1.0f));
paint.drawPolyline(poly);
}
AudioNoiseWidget::AudioNoiseWidget(QWidget *p) : QWidget(p) {
setMinimumSize(100,60);
}
void AudioNoiseWidget::paintEvent(QPaintEvent *) {
QPainter paint(this);
QPalette pal;
paint.fillRect(rect(), pal.color(QPalette::Background));
AudioInputPtr ai = g.ai;
if (ai.get() == NULL || ! ai->sppPreprocess)
return;
QPolygonF poly;
ai->qmSpeex.lock();
spx_int32_t ps_size = 0;
speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_PSD_SIZE, &ps_size);
STACKVAR(spx_int32_t, noise, ps_size);
STACKVAR(spx_int32_t, ps, ps_size);
speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_PSD, ps);
speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_NOISE_PSD, noise);
ai->qmSpeex.unlock();
qreal sx, sy;
sx = (static_cast<float>(width()) - 1.0f) / static_cast<float>(ps_size);
sy = static_cast<float>(height()) - 1.0f;
poly << QPointF(0.0f, height() - 1);
float fftmul = 1.0 / (32768.0);
for (int i=0; i < ps_size; i++) {
qreal xp, yp;
xp = i * sx;
yp = sqrtf(sqrtf(static_cast<float>(noise[i]))) - 1.0f;
yp = yp * fftmul;
yp = qMin<qreal>(yp * 3000.0f, 1.0f);
yp = (1 - yp) * sy;
poly << QPointF(xp, yp);
}
poly << QPointF(width() - 1, height() - 1);
poly << QPointF(0.0f, height() - 1);
paint.setPen(Qt::blue);
paint.setBrush(Qt::blue);
paint.drawPolygon(poly);
poly.clear();
for (int i=0;i < ps_size; i++) {
qreal xp, yp;
xp = i * sx;
yp = sqrtf(sqrtf(static_cast<float>(ps[i]))) - 1.0f;
yp = yp * fftmul;
yp = qMin(yp * 3000.0, 1.0);
yp = (1 - yp) * sy;
poly << QPointF(xp, yp);
}
paint.setPen(Qt::red);
paint.drawPolyline(poly);
}
AudioStats::AudioStats(QWidget *p) : QDialog(p) {
setAttribute(Qt::WA_DeleteOnClose, true);
qtTick = new QTimer(this);
qtTick->setObjectName(QLatin1String("Tick"));
qtTick->start(50);
setupUi(this);
AudioInputPtr ai = g.ai;
if (ai && ai->sesEcho) {
qgbEcho->setVisible(true);
} else {
qgbEcho->setVisible(false);
}
bTalking = false;
abSpeech->iPeak = -1;
abSpeech->qcBelow = Qt::red;
abSpeech->qcInside = Qt::yellow;
abSpeech->qcAbove = Qt::green;
on_Tick_timeout();
}
AudioStats::~AudioStats() {
}
void AudioStats::on_Tick_timeout() {
AudioInputPtr ai = g.ai;
if (ai.get() == NULL || ! ai->sppPreprocess)
return;
bool nTalking = ai->isTransmitting();
QString txt;
txt.sprintf("%06.2f dB",ai->dPeakMic);
qlMicLevel->setText(txt);
txt.sprintf("%06.2f dB",ai->dPeakSpeaker);
qlSpeakerLevel->setText(txt);
txt.sprintf("%06.2f dB",ai->dPeakSignal);
qlSignalLevel->setText(txt);
spx_int32_t ps_size = 0;
speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_PSD_SIZE, &ps_size);
STACKVAR(spx_int32_t, noise, ps_size);
STACKVAR(spx_int32_t, ps, ps_size);
speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_PSD, ps);
speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_NOISE_PSD, noise);
float s = 0.0f;
float n = 0.0001f;
int start = (ps_size * 300) / SAMPLE_RATE;
int stop = (ps_size * 2000) / SAMPLE_RATE;
for (int i=start;i<stop;i++) {
s += sqrtf(static_cast<float>(ps[i]));
n += sqrtf(static_cast<float>(noise[i]));
}
txt.sprintf("%06.3f",s / n);
qlMicSNR->setText(txt);
spx_int32_t v;
speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_AGC_GAIN, &v);
float fv = powf(10.0f, (static_cast<float>(v) / 20.0f));
txt.sprintf("%03.0f%%",100.0f / fv);
qlMicVolume->setText(txt);
txt.sprintf("%03.0f%%",ai->fSpeechProb * 100.0f);
qlSpeechProb->setText(txt);
txt.sprintf("%04.1f kbit/s",static_cast<float>(ai->iBitrate) / 1000.0f);
qlBitrate->setText(txt);
if (nTalking != bTalking) {
bTalking = nTalking;
QFont f = qlSpeechProb->font();
f.setBold(bTalking);
qlSpeechProb->setFont(f);
}
if (g.uiDoublePush > 1000000)
txt = tr(">1000 ms");
else
txt.sprintf("%04llu ms",g.uiDoublePush / 1000);
qlDoublePush->setText(txt);
abSpeech->iBelow = iroundf(g.s.fVADmin * 32767.0f + 0.5f);
abSpeech->iAbove = iroundf(g.s.fVADmax * 32767.0f + 0.5f);
if (g.s.vsVAD == Settings::Amplitude) {
#ifndef COMPAT_CLIENT
abSpeech->iValue = iroundf((32767.f/96.0f) * (96.0f + ai->dPeakCleanMic) + 0.5f);
#else
abSpeech->iValue = iroundf((32767.f/96.0f) * (96.0f + ai->dPeakMic) + 0.5f);
#endif
} else {
abSpeech->iValue = iroundf(ai->fSpeechProb * 32767.0f + 0.5f);
}
abSpeech->update();
anwNoise->update();
if (aewEcho)
aewEcho->update();
}
*/

96
plugins/VOIP/AudioStats.h Normal file
View File

@ -0,0 +1,96 @@
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the Mumble Developers nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _AUDIOSTATS_H
#define _AUDIOSTATS_H
#include <QtCore/QtCore>
#include <QtGui/QtGui>
//#include "mumble_pch.hpp"
class AudioBar : public QWidget {
private:
Q_OBJECT
Q_DISABLE_COPY(AudioBar)
protected:
void paintEvent(QPaintEvent *event);
public:
AudioBar(QWidget *parent = NULL);
int iMin, iMax;
int iBelow, iAbove;
int iValue, iPeak;
bool highContrast;
QColor qcBelow, qcInside, qcAbove;
QList<QColor> qlReplacableColors;
QList<Qt::BrushStyle> qlReplacementBrushes;
};
/*
class AudioEchoWidget : public QWidget {
private:
Q_OBJECT
Q_DISABLE_COPY(AudioEchoWidget)
public:
AudioEchoWidget(QWidget *parent);
protected slots:
void paintEvent(QPaintEvent *event);
};
class AudioNoiseWidget : public QWidget {
private:
Q_OBJECT
Q_DISABLE_COPY(AudioNoiseWidget)
public:
AudioNoiseWidget(QWidget *parent);
protected slots:
void paintEvent(QPaintEvent *event);
};
#include "ui_AudioStats.h"
class AudioStats : public QDialog, public Ui::AudioStats {
private:
Q_OBJECT
Q_DISABLE_COPY(AudioStats)
protected:
QTimer *qtTick;
bool bTalking;
public:
AudioStats(QWidget *parent);
~AudioStats();
public slots:
void on_Tick_timeout();
};
*/
#else
class AudioStats;
#endif

327
plugins/VOIP/AudioStats.ui Normal file
View File

@ -0,0 +1,327 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AudioStats</class>
<widget class="QDialog" name="AudioStats">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>598</width>
<height>548</height>
</rect>
</property>
<property name="windowTitle">
<string>Audio Statistics</string>
</property>
<layout class="QVBoxLayout">
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QGroupBox" name="qgbInput">
<property name="title">
<string>Input Levels</string>
</property>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QLabel" name="qliMicLevel">
<property name="text">
<string>Peak microphone level</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="qlMicLevel">
<property name="toolTip">
<string>Peak power in last frame</string>
</property>
<property name="whatsThis">
<string>This shows the peak power in the last frame (20 ms), and is the same measurement as you would usually find displayed as &quot;input power&quot;. Please disregard this and look at &lt;b&gt;Microphone power&lt;/b&gt; instead, which is much more steady and disregards outliers.</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="qliSpeakerLevel">
<property name="text">
<string>Peak speaker level</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="qlSpeakerLevel">
<property name="toolTip">
<string>Peak power in last frame</string>
</property>
<property name="whatsThis">
<string>This shows the peak power of the speakers in the last frame (20 ms). Unless you are using a multi-channel sampling method (such as ASIO) with speaker channels configured, this will be 0. If you have such a setup configured, and this still shows 0 while you're playing audio from other programs, your setup is not working.</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="qliSignalLevel">
<property name="text">
<string>Peak clean level</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="qlSignalLevel">
<property name="toolTip">
<string>Peak power in last frame</string>
</property>
<property name="whatsThis">
<string>This shows the peak power in the last frame (20 ms) after all processing. Ideally, this should be -96 dB when you're not talking. In reality, a sound studio should see -60 dB, and you should hopefully see somewhere around -20 dB. When you are talking, this should rise to somewhere between -5 and -10 dB.&lt;br /&gt;If you are using echo cancellation, and this rises to more than -15 dB when you're not talking, your setup is not working, and you'll annoy other users with echoes.</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="qgbSignal">
<property name="title">
<string>Signal Analysis</string>
</property>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QLabel" name="qliMicVolume">
<property name="text">
<string>Microphone power</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="qlMicVolume">
<property name="toolTip">
<string>How close the current input level is to ideal</string>
</property>
<property name="whatsThis">
<string>This shows how close your current input volume is to the ideal. To adjust your microphone level, open whatever program you use to adjust the recording volume, and look at the value here while talking.&lt;br /&gt;&lt;b&gt;Talk loud, as you would when you're upset over getting fragged by a noob.&lt;/b&gt;&lt;br /&gt;Adjust the volume until this value is close to 100%, but make sure it doesn't go above. If it does go above, you are likely to get clipping in parts of your speech, which will degrade sound quality.</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="qliMicSNR">
<property name="text">
<string>Signal-To-Noise ratio</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="qlMicSNR">
<property name="toolTip">
<string>Signal-To-Noise ratio from the microphone</string>
</property>
<property name="whatsThis">
<string>This is the Signal-To-Noise Ratio (SNR) of the microphone in the last frame (20 ms). It shows how much clearer the voice is compared to the noise.&lt;br /&gt;If this value is below 1.0, there's more noise than voice in the signal, and so quality is reduced.&lt;br /&gt;There is no upper limit to this value, but don't expect to see much above 40-50 without a sound studio.</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="qliSpeechProb">
<property name="text">
<string>Speech Probability</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="qlSpeechProb">
<property name="toolTip">
<string>Probability of speech</string>
</property>
<property name="whatsThis">
<string>This is the probability that the last frame (20 ms) was speech and not environment noise.&lt;br /&gt;Voice activity transmission depends on this being right. The trick with this is that the middle of a sentence is always detected as speech; the problem is the pauses between words and the start of speech. It's hard to distinguish a sigh from a word starting with 'h'.&lt;br /&gt;If this is in bold font, it means Mumble is currently transmitting (if you're connected).</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="qgbConfiguration">
<property name="title">
<string>Configuration feedback</string>
</property>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QLabel" name="qliBitrate">
<property name="text">
<string>Current audio bitrate</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="qlBitrate">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Bitrate of last frame</string>
</property>
<property name="whatsThis">
<string>This is the audio bitrate of the last compressed frame (20 ms), and as such will jump up and down as the VBR adjusts the quality. The peak bitrate can be adjusted in the Settings dialog.</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="qliDoublePush">
<property name="text">
<string>DoublePush interval</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="qlDoublePush">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Time between last two Push-To-Talk presses</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="qliSpeech">
<property name="text">
<string>Speech Detection</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="4">
<widget class="AudioBar" name="abSpeech" native="true">
<property name="toolTip">
<string>Current speech detection chance</string>
</property>
<property name="whatsThis">
<string>&lt;b&gt;This shows the current speech detection settings.&lt;/b&gt;&lt;br /&gt;You can change the settings from the Settings dialog or from the Audio Wizard.</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="qgbSpectrum">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Signal and noise power spectrum</string>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="AudioNoiseWidget" name="anwNoise" native="true">
<property name="toolTip">
<string>Power spectrum of input signal and noise estimate</string>
</property>
<property name="whatsThis">
<string>This shows the power spectrum of the current input signal (red line) and the current noise estimate (filled blue).&lt;br /&gt;All amplitudes are multiplied by 30 to show the interesting parts (how much more signal than noise is present in each waveband).&lt;br /&gt;This is probably only of interest if you're trying to fine-tune noise conditions on your microphone. Under good conditions, there should be just a tiny flutter of blue at the bottom. If the blue is more than halfway up on the graph, you have a seriously noisy environment.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="qgbEcho">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Echo Analysis</string>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="AudioEchoWidget" name="aewEcho" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Weights of the echo canceller</string>
</property>
<property name="whatsThis">
<string>This shows the weights of the echo canceller, with time increasing downwards and frequency increasing to the right.&lt;br /&gt;Ideally, this should be black, indicating no echo exists at all. More commonly, you'll have one or more horizontal stripes of bluish color representing time delayed echo. You should be able to see the weights updated in real time.&lt;br /&gt;Please note that as long as you have nothing to echo off, you won't see much useful data here. Play some music and things should stabilize. &lt;br /&gt;You can choose to view the real or imaginary parts of the frequency-domain weights, or alternately the computed modulus and phase. The most useful of these will likely be modulus, which is the amplitude of the echo, and shows you how much of the outgoing signal is being removed at that time step. The other viewing modes are mostly useful to people who want to tune the echo cancellation algorithms.&lt;br /&gt;Please note: If the entire image fluctuates massively while in modulus mode, the echo canceller fails to find any correlation whatsoever between the two input sources (speakers and microphone). Either you have a very long delay on the echo, or one of the input sources is configured wrong.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AudioBar</class>
<extends>QWidget</extends>
<header>AudioStats.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AudioNoiseWidget</class>
<extends>QWidget</extends>
<header>AudioStats.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AudioEchoWidget</class>
<extends>QWidget</extends>
<header>AudioStats.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,299 @@
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the Mumble Developers nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "AudioWizard.h"
//#include "AudioInput.h"
//#include "Global.h"
//#include "Settings.h"
//#include "Log.h"
//#include "MainWindow.h"
#include "gui/settings/rsharesettings.h"
#include "audiodevicehelper.h"
#define iroundf(x) ( static_cast<int>(x) )
AudioWizard::~AudioWizard()
{
if (inputDevice) {
inputDevice->stop();
}
if (outputDevice) {
outputDevice->stop();
}
}
AudioWizard::AudioWizard(QWidget *p) : QWizard(p) {
bInit = true;
bLastActive = false;
//g.bInAudioWizard = true;
ticker = new QTimer(this);
ticker->setObjectName(QLatin1String("Ticker"));
setupUi(this);
inputProcessor = NULL;
inputDevice = NULL;
outputProcessor = NULL;
outputDevice = NULL;
abAmplify = new AudioBar(this);
abAmplify->qcBelow = Qt::green;
abAmplify->qcInside = QColor::fromRgb(255,128,0);
abAmplify->qcAbove = Qt::red;
verticalLayout_3->addWidget(abAmplify);
if (Settings->getVoipATransmit() == RshareSettings::AudioTransmitPushToTalk)
qrPTT->setChecked(true);
else if (Settings->getVoipATransmit() == RshareSettings::AudioTransmitVAD)
qrVAD->setChecked(true);
else
qrContinuous->setChecked(true);
abVAD = new AudioBar(this);
abVAD->qcBelow = Qt::red;
abVAD->qcInside = Qt::yellow;
abVAD->qcAbove = Qt::green;
qsTransmitMin->setValue(Settings->getVoipfVADmin());
qsTransmitMax->setValue(Settings->getVoipfVADmax());
verticalLayout_6->addWidget(abVAD);
// Volume
qsMaxAmp->setValue(Settings->getVoipiMinLoudness());
setOption(QWizard::NoCancelButton, false);
resize(700, 500);
updateTriggerWidgets(qrVAD->isChecked());
bTransmitChanged = false;
iMaxPeak = 0;
iTicks = 0;
qpTalkingOn = QPixmap::fromImage(QImage(QLatin1String("skin:talking_on.svg")).scaled(64,64));
qpTalkingOff = QPixmap::fromImage(QImage(QLatin1String("skin:talking_off.svg")).scaled(64,64));
bInit = false;
connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(showPage(int)));
ticker->setSingleShot(false);
ticker->start(20);
connect( ticker, SIGNAL( timeout ( ) ), this, SLOT( on_Ticker_timeout() ) );
}
/*bool AudioWizard::eventFilter(QObject *obj, QEvent *evt) {
if ((evt->type() == QEvent::MouseButtonPress) ||
(evt->type() == QEvent::MouseMove)) {
QMouseEvent *qme = dynamic_cast<QMouseEvent *>(evt);
if (qme) {
if (qme->buttons() & Qt::LeftButton) {
QPointF qpf = qgvView->mapToScene(qme->pos());
fX = static_cast<float>(qpf.x());
fY = static_cast<float>(qpf.y());
}
}
}
return QWizard::eventFilter(obj, evt);
}*/
void AudioWizard::on_qsMaxAmp_valueChanged(int v) {
Settings->setVoipiMinLoudness(qMin(v, 30000));
}
void AudioWizard::on_Ticker_timeout() {
if (!inputProcessor) {
inputProcessor = new QtSpeex::SpeexInputProcessor();
inputProcessor->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
if (!inputDevice) {
inputDevice = AudioDeviceHelper::getPreferedInputDevice();
}
inputDevice->start(inputProcessor);
connect(inputProcessor, SIGNAL(networkPacketReady()), this, SLOT(loopAudio()));
}
if (!outputProcessor) {
outputProcessor = new QtSpeex::SpeexOutputProcessor();
outputProcessor->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
if (!outputDevice) {
outputDevice = AudioDeviceHelper::getPreferedOutputDevice();
}
outputDevice->start(outputProcessor);
connect(outputProcessor, SIGNAL(playingFrame(QByteArray*)), inputProcessor, SLOT(addEchoFrame(QByteArray*)));
}
abVAD->iBelow = qsTransmitMin->value();
abVAD->iAbove = qsTransmitMax->value();
Settings->setVoipfVADmin(qsTransmitMin->value());
Settings->setVoipfVADmax(qsTransmitMax->value());
abVAD->iValue = iroundf(inputProcessor->dVoiceAcivityLevel * 32767.0f + 0.5f);
abVAD->update();
int iPeak = inputProcessor->dMaxMic;
if (iTicks++ >= 50) {
iMaxPeak = 0;
iTicks = 0;
}
if (iPeak > iMaxPeak)
iMaxPeak = iPeak;
abAmplify->iBelow = qsMaxAmp->value();
abAmplify->iValue = iPeak;
abAmplify->iPeak = iMaxPeak;
abAmplify->update();
bool active = inputProcessor->bPreviousVoice;
if (active != bLastActive) {
bLastActive = active;
qlTalkIcon->setPixmap(active ? qpTalkingOn : qpTalkingOff);
}
}
void AudioWizard::loopAudio() {
while(inputProcessor && inputProcessor->hasPendingPackets()) {
packetQueue.enqueue(inputProcessor->getNetworkPacket());
QTimer* playEcho = new QTimer();
playEcho->setSingleShot(true);
connect( playEcho, SIGNAL( timeout ( ) ), this, SLOT( on_playEcho_timeout() ) );
playEcho->start(1500);
}
}
void AudioWizard::on_playEcho_timeout() {
if(!packetQueue.isEmpty()) {
if (!qcbStopEcho->isChecked()) {
if (outputDevice && outputDevice->error() != QAudio::NoError) {
std::cerr << "Stopping output device. Error " << outputDevice->error() << std::endl;
outputDevice->stop();
//TODO : find a way to restart output device, but there is a pulseaudio locks that prevents it here but it works in ChatWidget.cpp
//outputDevice->start(outputProcessor);
}
outputProcessor->putNetworkPacket("myself_loop",packetQueue.dequeue());
} else {
packetQueue.dequeue();
}
}
}
void AudioWizard::on_qsTransmitMax_valueChanged(int v) {
if (! bInit) {
Settings->setVoipfVADmax(v);
}
}
void AudioWizard::on_qsTransmitMin_valueChanged(int v) {
if (! bInit) {
Settings->setVoipfVADmin(v);
}
}
void AudioWizard::on_qrVAD_clicked(bool on) {
if (on) {
Settings->setVoipATransmit(RshareSettings::AudioTransmitVAD);
updateTriggerWidgets(true);
bTransmitChanged = true;
}
}
void AudioWizard::on_qrPTT_clicked(bool on) {
if (on) {
Settings->setVoipATransmit(RshareSettings::AudioTransmitPushToTalk);
updateTriggerWidgets(false);
bTransmitChanged = true;
}
}
void AudioWizard::on_qrContinuous_clicked(bool on) {
if (on) {
Settings->setVoipATransmit(RshareSettings::AudioTransmitContinous);
updateTriggerWidgets(false);
bTransmitChanged = true;
}
}
/*void AudioWizard::on_skwPTT_keySet(bool valid, bool last) {
if (valid)
qrPTT->setChecked(true);
else if (qrPTT->isChecked())
qrAmplitude->setChecked(true);
updateTriggerWidgets(valid);
bTransmitChanged = true;
if (last) {
const QList<QVariant> &buttons = skwPTT->getShortcut();
QList<Shortcut> ql;
bool found = false;
foreach(Shortcut s, g.s.qlShortcuts) {
if (s.iIndex == g.mw->gsPushTalk->idx) {
if (buttons.isEmpty())
continue;
else if (! found) {
s.qlButtons = buttons;
found = true;
}
}
ql << s;
}
if (! found && ! buttons.isEmpty()) {
Shortcut s;
s.iIndex = g.mw->gsPushTalk->idx;
s.bSuppress = false;
s.qlButtons = buttons;
ql << s;
}
g.s.qlShortcuts = ql;
GlobalShortcutEngine::engine->bNeedRemap = true;
GlobalShortcutEngine::engine->needRemap();
}
}*/
void AudioWizard::updateTriggerWidgets(bool vad_on) {
if (!vad_on)
qwVAD->hide();
else
qwVAD->show();
}
void AudioWizard::on_qcbHighContrast_clicked(bool on) {
abAmplify->highContrast = on;
abVAD->highContrast = on;
}

View File

@ -0,0 +1,96 @@
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the Mumble Developers nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _AUDIOWIZARD_H
#define _AUDIOWIZARD_H
#include <QAudioInput>
#include <QAudioOutput>
#include <QQueue>
#include "AudioStats.h"
#include "SpeexProcessor.h"
#include "ui_AudioWizard.h"
class AudioWizard: public QWizard, public Ui::AudioWizard {
private:
Q_OBJECT
Q_DISABLE_COPY(AudioWizard)
AudioBar* abAmplify;
AudioBar* abVAD;
QAudioInput* inputDevice;
QAudioOutput* outputDevice;
QtSpeex::SpeexInputProcessor* inputProcessor;
QtSpeex::SpeexOutputProcessor* outputProcessor;
QQueue<QByteArray>packetQueue;
protected:
bool bTransmitChanged;
QGraphicsScene *qgsScene;
QGraphicsItem *qgiSource;
//AudioOutputSample *aosSource;
float fAngle;
float fX, fY;
//Settings sOldSettings;
QTimer *ticker;
bool bInit;
bool bDelay;
bool bLastActive;
QPixmap qpTalkingOn, qpTalkingOff;
int iMaxPeak;
int iTicks;
public slots:
void on_playEcho_timeout();
void on_Ticker_timeout();
void on_qsMaxAmp_valueChanged(int);
void on_qrPTT_clicked(bool);
void on_qrVAD_clicked(bool);
void on_qrContinuous_clicked(bool);
void on_qsTransmitMin_valueChanged(int);
void on_qsTransmitMax_valueChanged(int);
void on_qcbHighContrast_clicked(bool);
void updateTriggerWidgets(bool);
public:
AudioWizard(QWidget *parent);
~AudioWizard();
private slots :
void loopAudio();
};
#endif

329
plugins/VOIP/AudioWizard.ui Normal file
View File

@ -0,0 +1,329 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AudioWizard</class>
<widget class="QWizard" name="AudioWizard">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>757</width>
<height>823</height>
</rect>
</property>
<property name="windowTitle">
<string>Audio Tuning Wizard</string>
</property>
<widget class="QWizardPage" name="qwpIntro">
<property name="title">
<string>Introduction</string>
</property>
<property name="subTitle">
<string>Welcome to the Mumble Audio Wizard</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;This is the audio tuning wizard for Mumble. This will help you correctly set the input levels of your sound card, and also set the correct parameters for sound processing in Retroshare. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>291</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWizardPage" name="qwpVolume">
<property name="title">
<string>Volume tuning</string>
</property>
<property name="subTitle">
<string>Tuning microphone hardware volume to optimal settings.</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="qliVolumeTuningText">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Open your sound control panel and go to the recording settings. Make sure the microphone is selected as active input with maximum recording volume. If there's an option to enable a &amp;quot;Microphone boost&amp;quot; make sure it's checked. &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Speak loudly, as when you are annoyed or excited. Decrease the volume in the sound control panel until the bar below stays as high as possible in the green and orange but &lt;span style=&quot; font-weight:600;&quot;&gt;not&lt;/span&gt; the red zone while you speak. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="qliAmpTuningText">
<property name="text">
<string>Talk normally, and adjust the slider below so that the bar moves into green when you talk, and doesn't go into the orange zone.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="qsMaxAmp">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>32767</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
<property name="pageStep">
<number>1000</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>552</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="qcbStopEcho">
<property name="text">
<string>Stop looping echo for this wizard</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="qcbHighContrast">
<property name="toolTip">
<string>Apply some high contrast optimizations for visually impaired users</string>
</property>
<property name="text">
<string>Use high contrast graphics</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWizardPage" name="qwpTrigger">
<property name="title">
<string>Voice Activity Detection</string>
</property>
<property name="subTitle">
<string>Letting Mumble figure out when you're talking and when you're silent.</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="qliVADText">
<property name="text">
<string>This will help Retroshare figure out when you are talking. The first step is selecting which data value to use.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="qrPTT">
<property name="text">
<string>Push To Talk:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>todo shortcut</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1" rowspan="3">
<widget class="QLabel" name="qlTalkIcon">
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="qrVAD">
<property name="text">
<string>Voice Detection</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QWidget" name="qwVAD" native="true">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="qliVadTuningText">
<property name="text">
<string>Next you need to adjust the following slider. The first few utterances you say should end up in the green area (definitive speech). While talking, you should stay inside the yellow (might be speech) and when you're not talking, everything should be in the red (definitively not speech).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="qsTransmitMin">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>32767</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
<property name="pageStep">
<number>1000</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="qsTransmitMax">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>32767</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
<property name="pageStep">
<number>1000</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QRadioButton" name="qrContinuous">
<property name="text">
<string>Continuous transmission</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWizardPage" name="qwpDone">
<property name="title">
<string>Finished</string>
</property>
<property name="subTitle">
<string>Enjoy using Mumble</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="qlDone">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Congratulations. You should now be ready to enjoy a richer sound experience with Retroshare. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>267</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,521 @@
#include "SpeexProcessor.h"
#include <speex/speex.h>
#include <speex/speex_preprocess.h>
#include <sstream>
#include <iostream>
#include <fstream>
#include <fcntl.h>
#include <math.h>
#include <cstdlib>
#include <QDateTime>
#include "gui/settings/rsharesettings.h"
#define iroundf(x) ( static_cast<int>(x) )
using namespace QtSpeex;
SpeexInputProcessor::SpeexInputProcessor(QObject *parent) : QIODevice(parent),
preprocessor(0),
enc_state(0),
enc_bits(),
send_timestamp(0),
echo_state(0),
inputBuffer(),
iMaxBitRate(16800),
bResetProcessor(true),
lastEchoFrame(NULL)
{
enc_bits = new SpeexBits;
speex_bits_init(enc_bits);
speex_bits_reset(enc_bits);
enc_state = speex_encoder_init(&speex_wb_mode);
int iArg = 0;
speex_encoder_ctl(enc_state,SPEEX_SET_VAD, &iArg);
speex_encoder_ctl(enc_state,SPEEX_SET_DTX, &iArg);
float fArg=9.0;
speex_encoder_ctl(enc_state,SPEEX_SET_VBR_QUALITY, &fArg);
iArg = iMaxBitRate;
speex_encoder_ctl(enc_state, SPEEX_SET_VBR_MAX_BITRATE, &iArg);
iArg = 10;
speex_encoder_ctl(enc_state,SPEEX_SET_COMPLEXITY, &iArg);
iArg = 9;
speex_encoder_ctl(enc_state,SPEEX_SET_QUALITY, &iArg);
echo_state = NULL;
//iEchoFreq = iMicFreq = iSampleRate;
iSilentFrames = 0;
iHoldFrames = 0;
bResetProcessor = true;
//bEchoMulti = false;
preprocessor = NULL;
echo_state = NULL;
//srsMic = srsEcho = NULL;
//iJitterSeq = 0;
//iMinBuffered = 1000;
//psMic = new short[iFrameSize];
psClean = new short[SAMPLING_RATE];
//psSpeaker = NULL;
//iEchoChannels = iMicChannels = 0;
//iEchoFilled = iMicFilled = 0;
//eMicFormat = eEchoFormat = SampleFloat;
//iMicSampleSize = iEchoSampleSize = 0;
bPreviousVoice = false;
//pfMicInput = pfEchoInput = pfOutput = NULL;
iRealTimeBitrate = 0;
dPeakSignal = dPeakSpeaker = dPeakMic = dPeakCleanMic = dVoiceAcivityLevel = 0.0;
//if (g.uiSession) {
//TODO : get the maxbitrate from a rs service or a dynamic code
//iMaxBitRate = 10000;
//}
//bRunning = true;
}
SpeexInputProcessor::~SpeexInputProcessor() {
speex_preprocess_state_destroy(preprocessor);
if (echo_state) {
speex_echo_state_destroy(echo_state);
}
speex_encoder_destroy(enc_state);
speex_bits_destroy(enc_bits);
delete enc_bits;
free(psClean);
}
QByteArray SpeexInputProcessor::getNetworkPacket() {
return outputNetworkBuffer.takeFirst();
}
bool SpeexInputProcessor::hasPendingPackets() {
return !outputNetworkBuffer.empty();
}
qint64 SpeexInputProcessor::writeData(const char *data, qint64 maxSize) {
int iArg;
int i;
float sum;
short max;
inputBuffer += QByteArray(data, maxSize);
while(inputBuffer.size() > FRAME_SIZE * sizeof(qint16)) {
QByteArray source_frame = inputBuffer.left(FRAME_SIZE * sizeof(qint16));
short* psMic = (short *)source_frame.data();
//let's do volume detection
sum=1.0f;
for (i=0;i<FRAME_SIZE;i++) {
sum += static_cast<float>(psMic[i] * psMic[i]);
}
dPeakMic = qMax(20.0f*log10f(sqrtf(sum / static_cast<float>(FRAME_SIZE)) / 32768.0f), -96.0f);
max = 1;
for (i=0;i<FRAME_SIZE;i++)
max = static_cast<short>(std::abs(psMic[i]) > max ? std::abs(psMic[i]) : max);
dMaxMic = max;
dPeakSpeaker = 0.0;
QMutexLocker l(&qmSpeex);
if (bResetProcessor) {
if (preprocessor)
speex_preprocess_state_destroy(preprocessor);
preprocessor = speex_preprocess_state_init(FRAME_SIZE, SAMPLING_RATE);
iArg = 1;
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_VAD, &iArg);
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_AGC, &iArg);
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_DENOISE, &iArg);
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_DEREVERB, &iArg);
iArg = 30000;
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_AGC_TARGET, &iArg);
iArg = -60;
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_AGC_DECREMENT, &iArg);
iArg = Settings->getVoipiNoiseSuppress();
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &iArg);
if (echo_state) {
iArg = SAMPLING_RATE;
speex_echo_ctl(echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &iArg);
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_ECHO_STATE, echo_state);
}
bResetProcessor = false;
}
float v = 30000.0f / static_cast<float>(Settings->getVoipiMinLoudness());
iArg = iroundf(floorf(20.0f * log10f(v)));
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &iArg);
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_GET_AGC_GAIN, &iArg);
float gainValue = static_cast<float>(iArg);
iArg = Settings->getVoipiNoiseSuppress() - iArg;
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &iArg);
short * psSource = psMic;
if (echo_state && Settings->getVoipEchoCancel()) {
speex_echo_playback(echo_state, (short*)lastEchoFrame->data());
speex_echo_capture(echo_state,psMic,psClean);
psSource = psClean;
}
speex_preprocess_run(preprocessor, psSource);
//we will now analize the processed signal
sum=1.0f;
for (i=0;i<FRAME_SIZE;i++)
sum += static_cast<float>(psSource[i] * psSource[i]);
float micLevel = sqrtf(sum / static_cast<float>(FRAME_SIZE));
dPeakSignal = qMax(20.0f*log10f(micLevel / 32768.0f), -96.0f);
spx_int32_t prob = 0;
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_GET_PROB, &prob);//speech probability
fSpeechProb = static_cast<float>(prob) / 100.0f;
// clean microphone level: peak of filtered signal attenuated by AGC gain
dPeakCleanMic = qMax(dPeakSignal - gainValue, -96.0f);
dVoiceAcivityLevel = 0.4f * fSpeechProb + 0.6f * (1.0f + dPeakCleanMic / 96.0f);//ponderation for speech detection and audio amplitude
bool bIsSpeech = false;
if (dVoiceAcivityLevel > (static_cast<float>(Settings->getVoipfVADmax()) / 32767))
bIsSpeech = true;
else if (dVoiceAcivityLevel > (static_cast<float>(Settings->getVoipfVADmin()) / 32767) && bPreviousVoice)
bIsSpeech = true;
if (! bIsSpeech) {
iHoldFrames++;
if (iHoldFrames < Settings->getVoipVoiceHold())
bIsSpeech = true;
} else {
iHoldFrames = 0;
}
if (Settings->getVoipATransmit() == RshareSettings::AudioTransmitContinous) {
bIsSpeech = true;
}
else if (Settings->getVoipATransmit() == RshareSettings::AudioTransmitPushToTalk)
bIsSpeech = false;//g.s.uiDoublePush && ((g.uiDoublePush < g.s.uiDoublePush) || (g.tDoublePush.elapsed() < g.s.uiDoublePush));
//bIsSpeech = bIsSpeech || (g.iPushToTalk > 0);
/*if (g.s.bMute || ((g.s.lmLoopMode != Settings::Local) && p && (p->bMute || p->bSuppress)) || g.bPushToMute || (g.iTarget < 0)) {
bIsSpeech = false;
}*/
if (bIsSpeech) {
iSilentFrames = 0;
} else {
iSilentFrames++;
}
/*if (p) {
if (! bIsSpeech)
p->setTalking(Settings::Passive);
else if (g.iTarget == 0)
p->setTalking(Settings::Talking);
else
p->setTalking(Settings::Shouting);
}*/
if (! bIsSpeech && ! bPreviousVoice) {
iRealTimeBitrate = 0;
/*if (g.s.iIdleTime && ! g.s.bDeaf && ((tIdle.elapsed() / 1000000ULL) > g.s.iIdleTime)) {
emit doDeaf();
tIdle.restart();
}*/
spx_int32_t increment = 0;
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_AGC_INCREMENT, &increment);
} else {
spx_int32_t increment = 12;
speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_AGC_INCREMENT, &increment);
}
int vbr_on=0;
//just use fixed bitrate for now
//encryption of VBR-encoded speech may not ensure complete privacy, as phrases can still be identified, at least in a controlled setting with a small dictionary of phrases, by analysing the pattern of variation of the bit rate.
if (Settings->getVoipATransmit() == RshareSettings::AudioTransmitVAD) {//maybe we can do fixer bitrate when voice detection is active
vbr_on = 1;//test it on for all modes
} else {//maybe we can do vbr for ppt and continuous
vbr_on = 1;
}
speex_encoder_ctl(enc_state,SPEEX_SET_VBR, &vbr_on);
int br = 0;
speex_encoder_ctl(enc_state, SPEEX_GET_VBR_MAX_BITRATE, &br);
if (br != iMaxBitRate) {
br = iMaxBitRate;
speex_encoder_ctl(enc_state, SPEEX_SET_VBR_MAX_BITRATE, &br);
}
speex_encoder_ctl(enc_state, SPEEX_GET_BITRATE, &br);
if (br != iMaxBitRate) {
br = iMaxBitRate;
speex_encoder_ctl(enc_state, SPEEX_SET_BITRATE, &br);
}
if (! bPreviousVoice)
speex_encoder_ctl(enc_state, SPEEX_RESET_STATE, NULL);
if (bIsSpeech) {
speex_bits_reset(enc_bits);
speex_encode_int(enc_state, psSource, enc_bits);
QByteArray networkFrame;
networkFrame.resize(speex_bits_nbytes(enc_bits)+4);//add 4 for the frame timestamp for the jitter buffer
int packetSize = speex_bits_write(enc_bits, networkFrame.data()+4, networkFrame.size()-4);
((int*)networkFrame.data())[0] = send_timestamp;
outputNetworkBuffer.append(networkFrame);
emit networkPacketReady();
iRealTimeBitrate = packetSize * SAMPLING_RATE / FRAME_SIZE * 8;
} else {
iRealTimeBitrate = 0;
}
bPreviousVoice = bIsSpeech;
//std::cerr << "iRealTimeBitrate : " << iRealTimeBitrate << std::endl;
send_timestamp += FRAME_SIZE;
if (send_timestamp >= INT_MAX)
send_timestamp = 0;
inputBuffer = inputBuffer.right(inputBuffer.size() - FRAME_SIZE * sizeof(qint16));
}
return maxSize;
}
SpeexOutputProcessor::SpeexOutputProcessor(QObject *parent) : QIODevice(parent),
outputBuffer()
{
}
SpeexOutputProcessor::~SpeexOutputProcessor() {
QHashIterator<QString, SpeexJitter*> i(userJitterHash);
while (i.hasNext()) {
i.next();
speex_jitter_destroy(*(i.value()));
free (i.value());
}
}
void SpeexOutputProcessor::putNetworkPacket(QString name, QByteArray packet) {
//buffer:
// timestamp | encodedBuf
// —————–———–——————–———–——————–———–——————–
// 4 | totalSize 4
//the size part (first 4 byets) is not actually used in the logic
if (packet.size() > 4)
{
SpeexJitter* userJitter;
if (userJitterHash.contains(name)) {
userJitter = userJitterHash.value(name);
} else {
userJitter = (SpeexJitter*)malloc(sizeof(SpeexJitter));
speex_jitter_init(userJitter, speex_decoder_init(&speex_wb_mode), SAMPLING_RATE);
int on = 1;
speex_decoder_ctl(userJitter->dec, SPEEX_SET_ENH, &on);
userJitterHash.insert(name, userJitter);
}
int recv_timestamp = ((int*)packet.data())[0];
userJitter->mostUpdatedTSatPut = recv_timestamp;
if (userJitter->firsttimecalling_get)
return;
speex_jitter_put(*userJitter, (char *)packet.data()+4, packet.size()-4, recv_timestamp);
}
}
bool SpeexInputProcessor::isSequential() const {
return true;
}
void SpeexInputProcessor::addEchoFrame(QByteArray* echo_frame) {
if (Settings->getVoipEchoCancel() && echo_frame) {
QMutexLocker l(&qmSpeex);
lastEchoFrame = echo_frame;
if (!echo_state) {//init echo_state
echo_state = speex_echo_state_init(FRAME_SIZE, ECHOTAILSIZE*FRAME_SIZE);
int tmp = SAMPLING_RATE;
speex_echo_ctl(echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &tmp);
bResetProcessor = true;
}
lastEchoFrame = echo_frame;
}
}
qint64 SpeexOutputProcessor::readData(char *data, qint64 maxSize) {
int ts = 0; //time stamp for the jitter call
while(outputBuffer.size() < maxSize) {
QByteArray* result_frame = new QByteArray();
result_frame->resize(FRAME_SIZE * sizeof(qint16));
result_frame->fill(0,FRAME_SIZE * sizeof(qint16));
QHashIterator<QString, SpeexJitter*> i(userJitterHash);
while (i.hasNext()) {
i.next();
SpeexJitter* jitter = i.value();
QByteArray intermediate_frame;
intermediate_frame.resize(FRAME_SIZE * sizeof(qint16));
if (jitter->firsttimecalling_get)
{
int ts = jitter->mostUpdatedTSatPut;
jitter->firsttimecalling_get = false;
}
speex_jitter_get(*jitter, (spx_int16_t*)intermediate_frame.data(), &ts);
for (int j = 0; j< FRAME_SIZE; j++) {
short sample1 = ((short*)result_frame->data())[j];
short sample2 = ((short*)intermediate_frame.data())[j];
float samplef1 = sample1 / 32768.0f;
float samplef2 = sample2 / 32768.0f;
float mixed = samplef1 + 0.8f * samplef2;
// hard clipping
if (mixed > 1.0f) mixed = 1.0f;
if (mixed < -1.0f) mixed = -1.0f;
((spx_int16_t*)result_frame->data())[j] = (short)(mixed * 32768.0f);
}
}
outputBuffer += *result_frame;
emit playingFrame(result_frame);
}
QByteArray resultBuffer = outputBuffer.left(maxSize);
memcpy(data, resultBuffer.data(), resultBuffer.size());
outputBuffer = outputBuffer.right(outputBuffer.size() - resultBuffer.size());
return resultBuffer.size();
}
bool SpeexOutputProcessor::isSequential() const {
return true;
}
void SpeexOutputProcessor::speex_jitter_init(SpeexJitter *jit, void *decoder, int sampling_rate)
{
jit->dec = decoder;
speex_decoder_ctl(decoder, SPEEX_GET_FRAME_SIZE, &jit->frame_size);
jit->packets = jitter_buffer_init(jit->frame_size);
jit->current_packet = new SpeexBits;
speex_bits_init(jit->current_packet);
jit->valid_bits = 0;
jit->firsttimecalling_get = true;
jit->mostUpdatedTSatPut = 0;
}
void SpeexOutputProcessor::speex_jitter_destroy(SpeexJitter jitter)
{
if (jitter.dec) {
speex_decoder_destroy(jitter.dec);
}
jitter_buffer_destroy(jitter.packets);
speex_bits_destroy(jitter.current_packet);
}
void SpeexOutputProcessor::speex_jitter_put(SpeexJitter jitter, char *packet, int len, int timestamp)
{
JitterBufferPacket p;
p.data = packet;
p.len = len;
p.timestamp = timestamp;
p.span = jitter.frame_size;
jitter_buffer_put(jitter.packets, &p);
}
void SpeexOutputProcessor::speex_jitter_get(SpeexJitter jitter, spx_int16_t *out, int *current_timestamp)
{
int i;
int ret;
spx_int32_t activity;
int bufferCount = 0;
JitterBufferPacket packet;
char data[FRAME_SIZE * ECHOTAILSIZE * 10];
packet.data = data;
packet.len = FRAME_SIZE * ECHOTAILSIZE * 10;
if (jitter.valid_bits)
{
/* Try decoding last received packet */
ret = speex_decode_int(jitter.dec, jitter.current_packet, out);
if (ret == 0)
{
jitter_buffer_tick(jitter.packets);
return;
} else {
jitter.valid_bits = 0;
}
}
if (current_timestamp)
ret = jitter_buffer_get(jitter.packets, &packet, jitter.frame_size, current_timestamp);
else
ret = jitter_buffer_get(jitter.packets, &packet, jitter.frame_size, NULL);
if (ret != JITTER_BUFFER_OK)
{
/* No packet found */
speex_decode_int(jitter.dec, NULL, out);
} else {
speex_bits_read_from(jitter.current_packet, packet.data, packet.len);
/* Decode packet */
ret = speex_decode_int(jitter.dec, jitter.current_packet, out);
if (ret == 0)
{
jitter.valid_bits = 1;
} else {
/* Error while decoding */
for (i=0;i<jitter.frame_size;i++)
out[i]=0;
}
}
speex_decoder_ctl(jitter.dec, SPEEX_GET_ACTIVITY, &activity);
if (activity < 30)
{
jitter_buffer_update_delay(jitter.packets, &packet, NULL);
}
jitter_buffer_tick(jitter.packets);
//ret = jitter_buffer_ctl(jitter.packets, JITTER_BUFFER_GET_AVALIABLE_COUNT, &bufferCount);
//sprintf(msg, “ get %d bufferCount=%d\n”, speex_jitter_get_pointer_timestamp(jitter), bufferCount);
//debugPrint(msg);
}
int SpeexOutputProcessor::speex_jitter_get_pointer_timestamp(SpeexJitter jitter)
{
return jitter_buffer_get_pointer_timestamp(jitter.packets);
}

View File

@ -0,0 +1,121 @@
/*
* QtSpeex - Speex bindings for Qt
* Copyright (C) 2010 Peter Zotov
*/
#ifndef SPEEXPROCESSOR_H
#define SPEEXPROCESSOR_H
#include <iostream>
#include <QIODevice>
#include <QByteArray>
#include <QList>
#include <QMutex>
#include <QHash>
#include <QMap>
#include <QStack>
#include <speex/speex_preprocess.h>
#include <speex/speex_echo.h>
#include <speex/speex_jitter.h>
#define SAMPLING_RATE 16000 //must be the same as the speex setted mode (speex_wb_mode)
#define FRAME_SIZE 320 //must be the same as the speex setted mode (speex_wb_mode)
#define ECHOTAILSIZE 25
class SpeexBits;
/** Speex jitter-buffer state. Never use it directly! */
typedef struct SpeexJitter {
SpeexBits *current_packet; /**< Current Speex packet */
int valid_bits; /**< True if Speex bits are valid */
JitterBuffer *packets; /**< Generic jitter buffer state */
void *dec; /**< Pointer to Speex decoder */
spx_int32_t frame_size; /**< Frame size of Speex decoder */
int mostUpdatedTSatPut; /**< timestamp of the last packet put */
bool firsttimecalling_get;
} SpeexJitter;
namespace QtSpeex {
class SpeexInputProcessor : public QIODevice {
Q_OBJECT
public:
float dPeakSpeaker, dPeakSignal, dMaxMic, dPeakMic, dPeakCleanMic, dVoiceAcivityLevel;
float fSpeechProb;
SpeexInputProcessor(QObject* parent = 0);
virtual ~SpeexInputProcessor();
bool hasPendingPackets();
QByteArray getNetworkPacket();
int iMaxBitRate;
int iRealTimeBitrate;
bool bPreviousVoice;
public slots:
void addEchoFrame(QByteArray*);
signals:
void networkPacketReady();
protected:
virtual qint64 readData(char *data, qint64 maxSize) {return false;} //not used for input processor
virtual qint64 writeData(const char *data, qint64 maxSize);
virtual bool isSequential() const;
private:
QByteArray* lastEchoFrame;
int iSilentFrames;
int iHoldFrames;
QMutex qmSpeex;
void* enc_state;
SpeexBits* enc_bits;
int send_timestamp; //set at the encode time for the jitter buffer of the reciever
bool bResetProcessor;
SpeexPreprocessState* preprocessor;
SpeexEchoState *echo_state;
short * psClean; //temp buffer for audio sampling after echo cleaning (if enabled)
QByteArray inputBuffer;
QList<QByteArray> outputNetworkBuffer;
};
class SpeexOutputProcessor : public QIODevice {
Q_OBJECT
public:
SpeexOutputProcessor(QObject* parent = 0);
virtual ~SpeexOutputProcessor();
void putNetworkPacket(QString name, QByteArray packet);
protected:
virtual qint64 readData(char *data, qint64 maxSize);
virtual qint64 writeData(const char *data, qint64 maxSize) {return 0;} //not used for output processor
virtual bool isSequential() const;
signals:
void playingFrame(QByteArray*);
private:
QByteArray outputBuffer;
QList<QByteArray> inputNetworkBuffer;
QHash<QString, SpeexJitter*> userJitterHash;
//SpeexJitter jitter;
void speex_jitter_init(SpeexJitter *jit, void *decoder, int sampling_rate);
void speex_jitter_destroy(SpeexJitter jitter);
void speex_jitter_put(SpeexJitter jitter, char *packet, int len, int timestamp);
void speex_jitter_get(SpeexJitter jitter, spx_int16_t *out, int *current_timestamp);
int speex_jitter_get_pointer_timestamp(SpeexJitter jitter);
};
}
#endif // SPEEXPROCESSOR_H

16
plugins/VOIP/VOIP.pro Normal file
View File

@ -0,0 +1,16 @@
!include("../Common/retroshare_plugin.pri"): error("Could not include file ../Common/retroshare_plugin.pri")
CONFIG += qt uic qrc resources
CONFIG += mobility
QT += multimedia
MOBILITY = multimedia
SOURCES = AudioInputConfig.cpp AudioStats.cpp AudioWizard.cpp SpeexProcessor.cpp audiodevicehelper.cpp
HEADERS = AudioInputConfig.h AudioStats.h AudioWizard.h SpeexProcessor.h audiodevicehelper.h
FORMS = AudioInputConfig.ui AudioStats.ui AudioWizard.ui
TARGET = VOIP
RESOURCES = VOIP_images.qrc
LIBS += -lspeex -lspeexdsp

36
plugins/VOIP/VOIPPlugin.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <retroshare/rsplugin.h>
#include <gui/mainpage.h>
#include "p3Voip.h"
class VOIPPlugin: public RsPlugin
{
public:
VOIPPlugin() ;
virtual ~VOIPPlugin() {}
virtual RsPQIService *rs_pqi_service() const ;
virtual MainPage *qt_page() const ;
virtual QIcon *qt_icon() const ;
virtual uint16_t rs_service_id() const { return RS_SERVICE_TYPE_VOIP ; }
virtual QTranslator *qt_translator(QApplication *app, const QString& languageCode) const;
virtual void getPluginVersion(int& major,int& minor,int& svn_rev) const ;
virtual void setPlugInHandler(RsPluginHandler *pgHandler);
virtual std::string configurationFileName() const { return "voip.cfg" ; }
virtual std::string getShortPluginDescription() const ;
virtual std::string getPluginName() const;
virtual void setInterfaces(RsPlugInInterfaces& interfaces);
private:
mutable p3Voip *mVoip ;
mutable RsPluginHandler *mPlugInHandler;
mutable RsPeers* mPeers;
mutable MainPage* mainpage ;
mutable QIcon* mIcon ;
};

View File

@ -0,0 +1,10 @@
<RCC>
<qresource prefix="/">
<file>images/deafened_self.svg</file>
<file>images/muted_self.svg</file>
<file>images/self_undeafened.svg</file>
<file>images/talking_on.svg</file>
<file>images/talking_off.svg</file>
</qresource>
</RCC>

View File

@ -0,0 +1,76 @@
#include "audiodevicehelper.h"
#include <iostream>
AudioDeviceHelper::AudioDeviceHelper()
{
}
QAudioInput* AudioDeviceHelper::getDefaultInputDevice() {
QAudioFormat fmt;
fmt.setFrequency(16000);
fmt.setChannels(1);
fmt.setSampleSize(16);
fmt.setSampleType(QAudioFormat::SignedInt);
fmt.setByteOrder(QAudioFormat::LittleEndian);
fmt.setCodec("audio/pcm");
QAudioDeviceInfo it, dev;
dev = QAudioDeviceInfo::defaultInputDevice();
if (dev.deviceName() != "pulse") {
foreach(it, QAudioDeviceInfo::availableDevices(QAudio::AudioInput)) {
if(it.deviceName() == "pulse") {
dev = it;
qDebug("Ok.");
break;
}
}
}
if (dev.deviceName() == "null") {
foreach(it, QAudioDeviceInfo::availableDevices(QAudio::AudioInput)) {
if(it.deviceName() != "null") {
dev = it;
break;
}
}
}
std::cerr << "input device : " << dev.deviceName().toStdString() << std::endl;
return new QAudioInput(dev, fmt);
}
QAudioInput* AudioDeviceHelper::getPreferedInputDevice() {
return AudioDeviceHelper::getDefaultInputDevice();
}
QAudioOutput* AudioDeviceHelper::getDefaultOutputDevice() {
QAudioFormat fmt;
fmt.setFrequency(16000);
fmt.setChannels(1);
fmt.setSampleSize(16);
fmt.setSampleType(QAudioFormat::SignedInt);
fmt.setByteOrder(QAudioFormat::LittleEndian);
fmt.setCodec("audio/pcm");
QAudioDeviceInfo it, dev;
dev = QAudioDeviceInfo::defaultOutputDevice();
if (dev.deviceName() != "pulse") {
foreach(it, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) {
if(it.deviceName() == "pulse") {
dev = it;
break;
}
}
}
if (dev.deviceName() == "null") {
foreach(it, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) {
if(it.deviceName() != "null") {
dev = it;
break;
}
}
}
std::cerr << "output device : " << dev.deviceName().toStdString() << std::endl;
return new QAudioOutput(dev, fmt);
}
QAudioOutput* AudioDeviceHelper::getPreferedOutputDevice() {
return AudioDeviceHelper::getDefaultOutputDevice();
}

View File

@ -0,0 +1,19 @@
#ifndef AUDIODEVICEHELPER_H
#define AUDIODEVICEHELPER_H
#include <QtMultimediaKit/QAudioInput>
#include <QtMultimediaKit/QAudioOutput>
class AudioDeviceHelper
{
public:
AudioDeviceHelper();
static QAudioInput* getDefaultInputDevice();
static QAudioInput* getPreferedInputDevice();
//static list getInputDeviceList();
static QAudioOutput* getDefaultOutputDevice();
static QAudioOutput* getPreferedOutputDevice();
//static list getOutputDeviceList();
};
#endif // AUDIODEVICEHELPER_H

View File

@ -0,0 +1,573 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="300"
height="300"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.47pre1"
version="1.0"
sodipodi:docname="deafened_self.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<title
id="title686">deafened_self</title>
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient905">
<stop
style="stop-color:#f9f9f9;stop-opacity:1;"
offset="0"
id="stop907" />
<stop
style="stop-color:#f9f9f9;stop-opacity:0;"
offset="1"
id="stop909" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient1078">
<stop
style="stop-color:#808080;stop-opacity:1;"
offset="0"
id="stop1080" />
<stop
style="stop-color:#808080;stop-opacity:0;"
offset="1"
id="stop1082" />
</linearGradient>
<linearGradient
id="linearGradient1054">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop1056" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop1058" />
</linearGradient>
<linearGradient
id="linearGradient855">
<stop
style="stop-color:#e6e6e6;stop-opacity:0.90983605;"
offset="0"
id="stop857" />
<stop
id="stop863"
offset="0.58318341"
style="stop-color:#eeeeee;stop-opacity:0.49803922;" />
<stop
style="stop-color:#dddddd;stop-opacity:0.24705882;"
offset="1"
id="stop865" />
<stop
style="stop-color:#cccccc;stop-opacity:0;"
offset="1"
id="stop859" />
</linearGradient>
<linearGradient
id="linearGradient847">
<stop
style="stop-color:#808080;stop-opacity:1;"
offset="0"
id="stop849" />
<stop
style="stop-color:#000000;stop-opacity:1"
offset="1"
id="stop851" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient999">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop1001" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop1003" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient969">
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="0"
id="stop971" />
<stop
style="stop-color:#999999;stop-opacity:0;"
offset="1"
id="stop973" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient939">
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="0"
id="stop941" />
<stop
style="stop-color:#999999;stop-opacity:0;"
offset="1"
id="stop943" />
</linearGradient>
<inkscape:path-effect
effect="spiro"
id="path-effect893"
is_visible="true" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective20"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective30"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective52"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective74"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective96"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective907"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient939"
id="radialGradient945"
cx="38.503746"
cy="137.89552"
fx="38.503746"
fy="137.89552"
r="31.535269"
gradientTransform="matrix(1,0,0,1.631579,0,-90.150692)"
gradientUnits="userSpaceOnUse" />
<inkscape:perspective
id="perspective955"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient969"
id="radialGradient975"
cx="76.675011"
cy="117.3753"
fx="76.675011"
fy="117.3753"
r="31.535269"
gradientTransform="matrix(1,0,0,1.631579,0,-90.150692)"
gradientUnits="userSpaceOnUse" />
<inkscape:perspective
id="perspective985"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient999"
id="radialGradient1005"
cx="48.547718"
cy="142.73859"
fx="48.547718"
fy="142.73859"
r="31.535269"
gradientTransform="matrix(1,0,0,1.631579,0,-90.150692)"
gradientUnits="userSpaceOnUse" />
<inkscape:perspective
id="perspective58"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient855"
id="linearGradient861"
x1="20.084265"
y1="45.905323"
x2="-10.16969"
y2="128.47794"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(59.057439,15.625251)" />
<inkscape:perspective
id="perspective875"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient939-9"
id="radialGradient945-7"
cx="38.503746"
cy="137.89552"
fx="38.503746"
fy="137.89552"
r="31.535269"
gradientTransform="matrix(1,0,0,1.631579,0,-90.150692)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient939-9">
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="0"
id="stop941-5" />
<stop
style="stop-color:#999999;stop-opacity:0;"
offset="1"
id="stop943-4" />
</linearGradient>
<inkscape:perspective
id="perspective913"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective976"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective1016"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1000-3"
id="linearGradient1006-3"
x1="150.35954"
y1="91.538269"
x2="231.07716"
y2="91.538269"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient1000-3">
<stop
style="stop-color:#1a1a1a;stop-opacity:1;"
offset="0"
id="stop1002-4" />
<stop
style="stop-color:#1a1a1a;stop-opacity:0;"
offset="1"
id="stop1004-1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1078"
id="linearGradient1084"
x1="234.39488"
y1="126.12481"
x2="197.27017"
y2="54.755512"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(1.1736212,2.3472424)" />
<inkscape:perspective
id="perspective1096"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective97"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient855-7"
id="linearGradient861-1"
x1="20.084265"
y1="45.905323"
x2="-10.16969"
y2="128.47794"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(57.883818,13.278009)" />
<linearGradient
id="linearGradient855-7">
<stop
style="stop-color:#e6e6e6;stop-opacity:0.90983605;"
offset="0"
id="stop857-4" />
<stop
id="stop863-0"
offset="0.58318341"
style="stop-color:#eeeeee;stop-opacity:0.49803922;" />
<stop
style="stop-color:#dddddd;stop-opacity:0.24705882;"
offset="1"
id="stop865-9" />
<stop
style="stop-color:#cccccc;stop-opacity:0;"
offset="1"
id="stop859-4" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient905"
id="radialGradient911"
cx="120.74688"
cy="52.178066"
fx="120.74688"
fy="52.178066"
r="61.126489"
gradientTransform="matrix(1,0,0,0.95011099,2.0034967,7.1291352)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1078"
id="linearGradient918"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(1.5885589,754.70942)"
x1="257.83887"
y1="161.80946"
x2="197.27017"
y2="54.755512" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.41"
inkscape:cx="150"
inkscape:cy="150"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1680"
inkscape:window-height="989"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:object-paths="false"
inkscape:snap-nodes="false"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>deafened_self</dc:title>
<cc:license
rdf:resource="" />
<dc:date>2009.08.17</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Martin Skilnand</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title>Martin Skilnand</dc:title>
</cc:Agent>
</dc:rights>
<dc:publisher>
<cc:Agent>
<dc:title>Mumble team</dc:title>
</cc:Agent>
</dc:publisher>
<dc:subject>
<rdf:Bag>
<rdf:li>mumble deafened self</rdf:li>
</rdf:Bag>
</dc:subject>
<dc:description>Icon for voice chat program mumble</dc:description>
<dc:identifier>deafened_self.svg</dc:identifier>
<dc:source>git://mumble.git.sourceforge.net/gitroot/mumble</dc:source>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Headset"
style="display:inline"
sodipodi:insensitive="true">
<path
sodipodi:type="arc"
style="fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-1"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(1.6336033,-0.28276325,0.241907,1.3975653,-11.803269,33.04371)" />
<path
style="fill:#000000;fill-opacity:1;stroke:none;display:inline"
d="m 71.510583,156.37238 c -1.426624,-9.07028 -5.336641,-31.4877 -1.359055,-53.72424 3.160918,-17.670985 12.276043,-35.227747 24.618385,-45.881137 27.873497,-24.059234 81.566677,-14.96367 81.566677,-14.96367 0,0 20.05923,6.022873 35.62273,22.511204 8.63901,9.152369 16.09582,22.950326 17.48363,37.343473 3.88801,40.32295 0.92227,37.90174 0.92227,37.90174 l -23.52762,-4.57806 -0.57367,-27.16217 c 0,0 -2.09252,-17.378879 -10.23023,-32.29583 -7.51719,-13.7795 -24.63987,-23.171724 -33.78057,-24.918198 -17.44946,-3.487023 -29.80263,2.171011 -43.12577,12.914967 -4.56785,4.280704 -8.81851,9.601287 -11.44762,15.545347 -3.63874,8.226723 -7.13997,23.252624 -8.055322,37.528514 -1.247707,19.4593 5.269652,39.19422 5.269652,39.19422 l -33.383487,0.58384 z"
id="path897"
sodipodi:nodetypes="csscsscccsccsscc" />
<path
style="opacity:0.41999996;fill:url(#linearGradient861);fill-opacity:1;stroke:none;display:inline"
d="m 71.447638,156.17269 c -1.42663,-9.07028 -5.33664,-31.4877 -1.35906,-53.72425 3.16092,-17.670979 12.276049,-35.22775 24.61839,-45.881139 15.191572,-13.112728 40.156802,-16.530455 59.163072,-16.548442 15.86644,-0.01502 27.39149,2.464988 27.39149,2.464988 -3.1203,0.794025 12.70148,5.2491 8.68807,6.639008 -3.6937,-0.05933 -9.48361,0.168416 -17.20427,-0.489547 -6.81102,-0.580442 -16.86144,0.211186 -19.48579,0.618019 -17.5142,2.7151 -12.62458,-0.147864 -34.19512,14.069432 -4.56785,4.2807 -8.81851,9.60129 -11.44762,15.54535 -3.63874,8.22672 -7.13997,23.252621 -8.055332,37.528521 -1.2477,19.4593 5.269662,39.19422 5.269662,39.19422 l -33.383492,0.58384 z"
id="path897-1"
sodipodi:nodetypes="csssccsscsscc" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(1.6378998,-0.25654091,0.21946999,1.4012634,-17.139184,33.66704)" />
<path
style="opacity:0.41999996;fill:url(#radialGradient911);fill-opacity:1;stroke:none;display:inline"
d="m 72.187089,156.27481 c -1.42663,-9.07028 -5.33664,-31.4877 -1.35906,-53.72425 3.16092,-17.670978 12.276049,-35.227744 24.61839,-45.881136 15.191571,-13.112728 40.156801,-16.530455 59.163071,-16.548442 15.86644,-0.01502 27.39149,2.464988 27.39149,2.464988 -3.1203,0.794025 12.70148,5.2491 8.68807,6.639008 -3.6937,-0.05933 -9.48361,0.168416 -17.20427,-0.489547 -6.81102,-0.580442 -16.86144,0.211186 -19.48579,0.618019 -17.5142,2.7151 -12.62458,-0.147864 -34.19512,14.069436 -4.56785,4.2807 -8.81851,9.60129 -11.44762,15.545346 -3.63874,8.22672 -7.13997,23.252618 -8.05533,37.528518 -1.247701,19.4593 5.26966,39.19422 5.26966,39.19422 l -33.383491,0.58384 z"
id="path897-1-8"
sodipodi:nodetypes="csssccsscsscc" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-1-7-0"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(1.3477557,0.10829879,-0.12233339,1.2822744,162.12601,8.66535)" />
<path
sodipodi:type="arc"
style="fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-1-7"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(1.2042743,0.10720955,-0.10930983,1.2693777,155.61448,12.22926)" />
<path
sodipodi:type="arc"
style="fill:url(#radialGradient975);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-1-7-0-7"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(1.3477557,0.10829879,-0.12233339,1.2822744,162.08163,9.11961)" />
<path
style="fill:#1a1a1a;fill-opacity:1;stroke:none;display:inline"
d="m 172.79474,48.649099 c -6.81102,-0.580442 -16.8444,0.218167 -19.46875,0.625 -0.98569,0.152804 -1.67877,0.253492 -2.53125,0.375 3.61024,-0.135926 7.43857,0.150887 11.53125,0.96875 9.1407,1.746474 26.26406,11.158001 33.78125,24.937501 8.13771,14.916946 10.21875,32.28125 10.21875,32.28125 l 0.35503,25.28903 11.2404,0.90903 10.21616,6.79675 c 0,0 5.26392,1.17189 1.37591,-39.15106 -1.38781,-14.393151 -8.86099,-28.191381 -17.5,-37.34375 C 195.02432,46.540395 193.1914,50.034689 172.79474,48.649099 z"
id="path897-2-1"
sodipodi:nodetypes="csccsccccscc" />
<path
sodipodi:type="arc"
style="opacity:0.7260002;fill:#1a1a1a;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-1-7-4"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(0.60431602,0.11003354,-0.10691687,0.84398493,183.98459,72.89808)" />
<path
sodipodi:type="arc"
style="fill:url(#radialGradient945);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-9"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(1.6379261,-0.25654091,0.21947351,1.4012634,-17.080156,34.05526)" />
<path
transform="translate(0,-752.36218)"
style="fill:url(#linearGradient918);fill-opacity:1;stroke:none;display:inline"
d="m 172.9212,801.01402 c -6.81102,-0.58044 -16.8444,0.21817 -19.46875,0.625 -0.98569,0.1528 -1.67877,0.25349 -2.53125,0.375 3.61024,-0.13593 7.43857,0.15089 11.53125,0.96875 9.1407,1.74647 26.26406,11.158 33.78125,24.9375 8.13771,14.91695 10.21875,32.28125 10.21875,32.28125 l 0.35503,24.97783 10.85921,1.4723 10.80482,6.44095 c 0,0 5.05645,1.27562 1.16844,-39.04734 -1.38781,-14.39314 -8.86099,-28.19137 -17.5,-37.34374 -16.98917,-17.7962 -18.82209,-14.30191 -39.21875,-15.6875 z"
id="path897-2"
sodipodi:nodetypes="csccsccccscc" />
<path
sodipodi:type="arc"
style="opacity:0.7260002;fill:url(#radialGradient1005);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-1-7-4-1"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(0.60431602,0.11820366,-0.10691687,0.90665171,181.85398,65.58585)" />
</g>
<g
inkscape:label="Cross"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-752.36218)"
style="display:inline"
sodipodi:insensitive="true">
<path
style="opacity:0.6;fill:#ff0505;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
d="m 36.348621,756.00008 c -0.625949,0 -1.259428,0.24152 -1.739063,0.72187 L 7.0470587,784.31726 c -0.9592695,0.96072 -0.9592695,2.51742 0,3.47813 L 120.31581,901.2282 L 6.5220587,1015.186 c -0.9592695,0.9607 -0.9592695,2.5174 0,3.4781 l 27.5624993,27.5953 c 0.95927,0.9607 2.518856,0.9607 3.478125,0 L 151.32362,932.30164 l 112.71094,112.87496 c 0.95927,0.9607 2.48604,0.9607 3.44531,0 l 27.59531,-27.5953 c 0.95927,-0.9607 0.95927,-2.5174 0,-3.4781 L 182.36424,901.2282 l 112.18594,-112.35 c 0.95927,-0.96071 0.95927,-2.4846 0,-3.44531 l -27.5625,-27.62813 c -0.95927,-0.96071 -2.51885,-0.96071 -3.47812,0 l -112.18594,112.35 L 38.054871,756.72195 c -0.479635,-0.48035 -1.080301,-0.72187 -1.70625,-0.72187 z"
id="rect1086-7" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -0,0 +1,561 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="300"
height="300"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.47 r22583"
version="1.0"
sodipodi:docname="deafened_self.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<title
id="title686">deafened_self</title>
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient905">
<stop
style="stop-color:#f9f9f9;stop-opacity:1;"
offset="0"
id="stop907" />
<stop
style="stop-color:#f9f9f9;stop-opacity:0;"
offset="1"
id="stop909" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient1078">
<stop
style="stop-color:#808080;stop-opacity:1;"
offset="0"
id="stop1080" />
<stop
style="stop-color:#808080;stop-opacity:0;"
offset="1"
id="stop1082" />
</linearGradient>
<linearGradient
id="linearGradient1054">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop1056" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop1058" />
</linearGradient>
<linearGradient
id="linearGradient855">
<stop
style="stop-color:#e6e6e6;stop-opacity:0.90983605;"
offset="0"
id="stop857" />
<stop
id="stop863"
offset="0.58318341"
style="stop-color:#eeeeee;stop-opacity:0.49803922;" />
<stop
style="stop-color:#dddddd;stop-opacity:0.24705882;"
offset="1"
id="stop865" />
<stop
style="stop-color:#cccccc;stop-opacity:0;"
offset="1"
id="stop859" />
</linearGradient>
<linearGradient
id="linearGradient847">
<stop
style="stop-color:#808080;stop-opacity:1;"
offset="0"
id="stop849" />
<stop
style="stop-color:#000000;stop-opacity:1"
offset="1"
id="stop851" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient999">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop1001" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop1003" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient969">
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="0"
id="stop971" />
<stop
style="stop-color:#999999;stop-opacity:0;"
offset="1"
id="stop973" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient939">
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="0"
id="stop941" />
<stop
style="stop-color:#999999;stop-opacity:0;"
offset="1"
id="stop943" />
</linearGradient>
<inkscape:path-effect
effect="spiro"
id="path-effect893"
is_visible="true" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective20"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective30"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective52"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective74"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective96"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective907"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient939"
id="radialGradient945"
cx="38.503746"
cy="137.89552"
fx="38.503746"
fy="137.89552"
r="31.535269"
gradientTransform="matrix(1,0,0,1.631579,0,-90.150692)"
gradientUnits="userSpaceOnUse" />
<inkscape:perspective
id="perspective955"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient969"
id="radialGradient975"
cx="76.675011"
cy="117.3753"
fx="76.675011"
fy="117.3753"
r="31.535269"
gradientTransform="matrix(1,0,0,1.631579,0,-90.150692)"
gradientUnits="userSpaceOnUse" />
<inkscape:perspective
id="perspective985"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient999"
id="radialGradient1005"
cx="48.547718"
cy="142.73859"
fx="48.547718"
fy="142.73859"
r="31.535269"
gradientTransform="matrix(1,0,0,1.631579,0,-90.150692)"
gradientUnits="userSpaceOnUse" />
<inkscape:perspective
id="perspective58"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient855"
id="linearGradient861"
x1="20.084265"
y1="45.905323"
x2="-10.16969"
y2="128.47794"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(59.057439,15.625251)" />
<inkscape:perspective
id="perspective875"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient939-9"
id="radialGradient945-7"
cx="38.503746"
cy="137.89552"
fx="38.503746"
fy="137.89552"
r="31.535269"
gradientTransform="matrix(1,0,0,1.631579,0,-90.150692)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient939-9">
<stop
style="stop-color:#999999;stop-opacity:1;"
offset="0"
id="stop941-5" />
<stop
style="stop-color:#999999;stop-opacity:0;"
offset="1"
id="stop943-4" />
</linearGradient>
<inkscape:perspective
id="perspective913"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective976"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective1016"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1000-3"
id="linearGradient1006-3"
x1="150.35954"
y1="91.538269"
x2="231.07716"
y2="91.538269"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient1000-3">
<stop
style="stop-color:#1a1a1a;stop-opacity:1;"
offset="0"
id="stop1002-4" />
<stop
style="stop-color:#1a1a1a;stop-opacity:0;"
offset="1"
id="stop1004-1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1078"
id="linearGradient1084"
x1="234.39488"
y1="126.12481"
x2="197.27017"
y2="54.755512"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(1.1736212,2.3472424)" />
<inkscape:perspective
id="perspective1096"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective97"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient855-7"
id="linearGradient861-1"
x1="20.084265"
y1="45.905323"
x2="-10.16969"
y2="128.47794"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(57.883818,13.278009)" />
<linearGradient
id="linearGradient855-7">
<stop
style="stop-color:#e6e6e6;stop-opacity:0.90983605;"
offset="0"
id="stop857-4" />
<stop
id="stop863-0"
offset="0.58318341"
style="stop-color:#eeeeee;stop-opacity:0.49803922;" />
<stop
style="stop-color:#dddddd;stop-opacity:0.24705882;"
offset="1"
id="stop865-9" />
<stop
style="stop-color:#cccccc;stop-opacity:0;"
offset="1"
id="stop859-4" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient905"
id="radialGradient911"
cx="120.74688"
cy="52.178066"
fx="120.74688"
fy="52.178066"
r="61.126488"
gradientTransform="matrix(1,0,0,0.95011099,2.0034967,7.1291352)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1078"
id="linearGradient918"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(1.5885589,754.70942)"
x1="257.83887"
y1="161.80946"
x2="197.27017"
y2="54.755512" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.41"
inkscape:cx="150"
inkscape:cy="116.80498"
inkscape:document-units="px"
inkscape:current-layer="layer2"
showgrid="false"
inkscape:window-width="1360"
inkscape:window-height="716"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:object-paths="false"
inkscape:snap-nodes="false"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>deafened_self</dc:title>
<cc:license
rdf:resource="" />
<dc:date>2009.08.17</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Martin Skilnand</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title>Martin Skilnand</dc:title>
</cc:Agent>
</dc:rights>
<dc:publisher>
<cc:Agent>
<dc:title>Mumble team</dc:title>
</cc:Agent>
</dc:publisher>
<dc:subject>
<rdf:Bag>
<rdf:li>mumble deafened self</rdf:li>
</rdf:Bag>
</dc:subject>
<dc:description>Icon for voice chat program mumble</dc:description>
<dc:identifier>deafened_self.svg</dc:identifier>
<dc:source>git://mumble.git.sourceforge.net/gitroot/mumble</dc:source>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Headset"
style="display:inline"
sodipodi:insensitive="true">
<path
sodipodi:type="arc"
style="fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-1"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(1.6336033,-0.28276325,0.241907,1.3975653,-11.803269,33.04371)" />
<path
style="fill:#000000;fill-opacity:1;stroke:none;display:inline"
d="m 71.510583,156.37238 c -1.426624,-9.07028 -5.336641,-31.4877 -1.359055,-53.72424 3.160918,-17.670985 12.276043,-35.227747 24.618385,-45.881137 27.873497,-24.059234 81.566677,-14.96367 81.566677,-14.96367 0,0 20.05923,6.022873 35.62273,22.511204 8.63901,9.152369 16.09582,22.950326 17.48363,37.343473 3.88801,40.32295 0.92227,37.90174 0.92227,37.90174 l -23.52762,-4.57806 -0.57367,-27.16217 c 0,0 -2.09252,-17.378879 -10.23023,-32.29583 -7.51719,-13.7795 -24.63987,-23.171724 -33.78057,-24.918198 -17.44946,-3.487023 -29.80263,2.171011 -43.12577,12.914967 -4.56785,4.280704 -8.81851,9.601287 -11.44762,15.545347 -3.63874,8.226723 -7.13997,23.252624 -8.055322,37.528514 -1.247707,19.4593 5.269652,39.19422 5.269652,39.19422 l -33.383487,0.58384 z"
id="path897"
sodipodi:nodetypes="csscsscccsccsscc" />
<path
style="opacity:0.41999996;fill:url(#linearGradient861);fill-opacity:1;stroke:none;display:inline"
d="m 71.447638,156.17269 c -1.42663,-9.07028 -5.33664,-31.4877 -1.35906,-53.72425 3.16092,-17.670979 12.276049,-35.22775 24.61839,-45.881139 15.191572,-13.112728 40.156802,-16.530455 59.163072,-16.548442 15.86644,-0.01502 27.39149,2.464988 27.39149,2.464988 -3.1203,0.794025 12.70148,5.2491 8.68807,6.639008 -3.6937,-0.05933 -9.48361,0.168416 -17.20427,-0.489547 -6.81102,-0.580442 -16.86144,0.211186 -19.48579,0.618019 -17.5142,2.7151 -12.62458,-0.147864 -34.19512,14.069432 -4.56785,4.2807 -8.81851,9.60129 -11.44762,15.54535 -3.63874,8.22672 -7.13997,23.252621 -8.055332,37.528521 -1.2477,19.4593 5.269662,39.19422 5.269662,39.19422 l -33.383492,0.58384 z"
id="path897-1"
sodipodi:nodetypes="csssccsscsscc" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(1.6378998,-0.25654091,0.21946999,1.4012634,-17.139184,33.66704)" />
<path
style="opacity:0.41999996;fill:url(#radialGradient911);fill-opacity:1;stroke:none;display:inline"
d="m 72.187089,156.27481 c -1.42663,-9.07028 -5.33664,-31.4877 -1.35906,-53.72425 3.16092,-17.670978 12.276049,-35.227744 24.61839,-45.881136 15.191571,-13.112728 40.156801,-16.530455 59.163071,-16.548442 15.86644,-0.01502 27.39149,2.464988 27.39149,2.464988 -3.1203,0.794025 12.70148,5.2491 8.68807,6.639008 -3.6937,-0.05933 -9.48361,0.168416 -17.20427,-0.489547 -6.81102,-0.580442 -16.86144,0.211186 -19.48579,0.618019 -17.5142,2.7151 -12.62458,-0.147864 -34.19512,14.069436 -4.56785,4.2807 -8.81851,9.60129 -11.44762,15.545346 -3.63874,8.22672 -7.13997,23.252618 -8.05533,37.528518 -1.247701,19.4593 5.26966,39.19422 5.26966,39.19422 l -33.383491,0.58384 z"
id="path897-1-8"
sodipodi:nodetypes="csssccsscsscc" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-1-7-0"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(1.3477557,0.10829879,-0.12233339,1.2822744,162.12601,8.66535)" />
<path
sodipodi:type="arc"
style="fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-1-7"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(1.2042743,0.10720955,-0.10930983,1.2693777,155.61448,12.22926)" />
<path
sodipodi:type="arc"
style="fill:url(#radialGradient975);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-1-7-0-7"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(1.3477557,0.10829879,-0.12233339,1.2822744,162.08163,9.11961)" />
<path
style="fill:#1a1a1a;fill-opacity:1;stroke:none;display:inline"
d="m 172.79474,48.649099 c -6.81102,-0.580442 -16.8444,0.218167 -19.46875,0.625 -0.98569,0.152804 -1.67877,0.253492 -2.53125,0.375 3.61024,-0.135926 7.43857,0.150887 11.53125,0.96875 9.1407,1.746474 26.26406,11.158001 33.78125,24.937501 8.13771,14.916946 10.21875,32.28125 10.21875,32.28125 l 0.35503,25.28903 11.2404,0.90903 10.21616,6.79675 c 0,0 5.26392,1.17189 1.37591,-39.15106 -1.38781,-14.393151 -8.86099,-28.191381 -17.5,-37.34375 C 195.02432,46.540395 193.1914,50.034689 172.79474,48.649099 z"
id="path897-2-1"
sodipodi:nodetypes="csccsccccscc" />
<path
sodipodi:type="arc"
style="opacity:0.7260002;fill:#1a1a1a;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-1-7-4"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(0.60431602,0.11003354,-0.10691687,0.84398493,183.98459,72.89808)" />
<path
sodipodi:type="arc"
style="fill:url(#radialGradient945);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-9"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(1.6379261,-0.25654091,0.21947351,1.4012634,-17.080156,34.05526)" />
<path
transform="translate(0,-752.36218)"
style="fill:url(#linearGradient918);fill-opacity:1;stroke:none;display:inline"
d="m 172.9212,801.01402 c -6.81102,-0.58044 -16.8444,0.21817 -19.46875,0.625 -0.98569,0.1528 -1.67877,0.25349 -2.53125,0.375 3.61024,-0.13593 7.43857,0.15089 11.53125,0.96875 9.1407,1.74647 26.26406,11.158 33.78125,24.9375 8.13771,14.91695 10.21875,32.28125 10.21875,32.28125 l 0.35503,24.97783 10.85921,1.4723 10.80482,6.44095 c 0,0 5.05645,1.27562 1.16844,-39.04734 -1.38781,-14.39314 -8.86099,-28.19137 -17.5,-37.34374 -16.98917,-17.7962 -18.82209,-14.30191 -39.21875,-15.6875 z"
id="path897-2"
sodipodi:nodetypes="csccsccccscc" />
<path
sodipodi:type="arc"
style="opacity:0.7260002;fill:url(#radialGradient1005);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
id="path20-1-7-4-1"
sodipodi:cx="48.547718"
sodipodi:cy="142.73859"
sodipodi:rx="31.535269"
sodipodi:ry="51.452282"
d="m 80.082987,142.73859 a 31.535269,51.452282 0 1 1 -63.070538,0 31.535269,51.452282 0 1 1 63.070538,0 z"
transform="matrix(0.60431602,0.11820366,-0.10691687,0.90665171,181.85398,65.58585)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 66 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 66 KiB

76
plugins/VOIP/p3Voip.h Normal file
View File

@ -0,0 +1,76 @@
/*
* Services for RetroShare.
*
* Copyright 2011-2012 by Cyril Soler
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License Version 2 as published by the Free Software Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* Please report all bugs and problems to "retroshare@lunamutt.com".
*
*/
#pragma once
#include <list>
#include <string>
#include <vector>
#include "serialiser/rsmsgitems.h"
#include "services/p3service.h"
#include "retroshare/rsmsgs.h"
//!The basic VOIP service.
class p3VoipService: public RsPQIService, public RsVoip
{
public:
p3VoipService() : RsPQIService(RS_SERVICE_TYPE_VOIP) {}
/***** overloaded from p3Service *****/
/*!
* This retrieves all chat msg items and also (important!)
* processes chat-status items that are in service item queue. chat msg item requests are also processed and not returned
* (important! also) notifications sent to notify base on receipt avatar, immediate status and custom status
* : notifyCustomState, notifyChatStatus, notifyPeerHasNewAvatar
* @see NotifyBase
*/
virtual int tick();
virtual int status();
/*************** pqiMonitor callback ***********************/
virtual void statusChange(const std::list<pqipeer> &plist);
/*!
* public chat sent to all peers
*/
int sendVoipData(const void *data,uint32_t size);
protected:
/************* from p3Config *******************/
virtual RsSerialiser *setupSerialiser() ;
/*!
* chat msg items and custom status are saved
*/
virtual bool saveList(bool& cleanup, std::list<RsItem*>&) ;
virtual bool loadList(std::list<RsItem*>& load) ;
private:
//RsMutex mChatMtx;
void receiveVoipQueue();
};

View File

@ -0,0 +1,18 @@
#pragma once
const uint8_t RS_VOIP_SUBTYPE_RINGING = 0x01 ;
const uint8_t RS_VOIP_SUBTYPE_ACKNOWL = 0x02 ;
const uint8_t RS_VOIP_SUBTYPE_DATA = 0x03 ;
class RsVoipItem: public RsItem
{
public:
RsVoipItem(uint8_t turtle_subtype) : RsItem(RS_PKT_VERSION_SERVICE,RS_SERVICE_TYPE_VOIP,voip_subtype) {}
virtual bool serialize(void *data,uint32_t& size) = 0 ; // Isn't it better that items can serialize themselves ?
virtual uint32_t serial_size() = 0 ; // deserialise is handled using a constructor
virtual void clear() {}
};
// to derive: hanshake items, data items etc.