diff options
author | Thorvald Natvig <slicer@users.sourceforge.net> | 2007-05-24 15:21:11 +0400 |
---|---|---|
committer | Thorvald Natvig <slicer@users.sourceforge.net> | 2007-05-24 15:21:11 +0400 |
commit | 4151f4b46e33ed324cef44a4c48ba11172080250 (patch) | |
tree | 463b89003e7155966ef4a9f45958466aae965abf /src/mumble | |
parent | e81c5229030c450077832b63252e35ee47e8a838 (diff) |
Refactoring
git-svn-id: https://mumble.svn.sourceforge.net/svnroot/mumble/trunk@445 05730e5d-ab1b-0410-a4ac-84af385074fa
Diffstat (limited to 'src/mumble')
76 files changed, 25461 insertions, 0 deletions
diff --git a/src/mumble/ACLEditor.cpp b/src/mumble/ACLEditor.cpp new file mode 100644 index 000000000..fff9b3f2e --- /dev/null +++ b/src/mumble/ACLEditor.cpp @@ -0,0 +1,1035 @@ +/* copyright (C) 2005-2006, 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 "ACLEditor.h" +#include "ACL.h" +#include "ServerHandler.h" +#include "Channel.h" +#include "Global.h" + +ACLEditor::ACLEditor(const MessageEditACL *mea, QWidget *p) : QDialog(p) { + QLabel *l; + MessageEditACL::ACLStruct as; + MessageEditACL::GroupStruct gs; + MessageEditACL::ACLStruct *asp; + MessageEditACL::GroupStruct *gsp; + + iId = mea->iId; + setWindowTitle(tr("Mumble - Edit ACL for %1").arg(Channel::get(iId)->qsName)); + + QTabWidget *qtwTab = new QTabWidget(this); + QWidget *groupEditor=new QWidget(); + QWidget *aclEditor=new QWidget(); + + QGroupBox *qgbACLs = new QGroupBox(tr("Active ACLs")); + QGroupBox *qgbACLapply = new QGroupBox(tr("Context")); + QGroupBox *qgbACLugroup = new QGroupBox(tr("User/Group")); + QGroupBox *qgbACLpermissions = new QGroupBox(tr("Permissions")); + + QGroupBox *qgbGroups = new QGroupBox(tr("Group")); + QGroupBox *qgbGroupMembers = new QGroupBox(tr("Members")); + + qlwACLs = new QListWidget(); + qlwACLs->setObjectName("ACLList"); + qpbACLAdd=new QPushButton(tr("&Add")); + qpbACLAdd->setObjectName("ACLAdd"); + qpbACLRemove=new QPushButton(tr("&Remove")); + qpbACLRemove->setObjectName("ACLRemove"); + qpbACLUp=new QPushButton(tr("&Up")); + qpbACLUp->setObjectName("ACLUp"); + qpbACLDown=new QPushButton(tr("&Down")); + qpbACLDown->setObjectName("ACLDown"); + qcbACLInherit=new QCheckBox(tr("Inherit ACLs")); + qcbACLInherit->setObjectName("ACLInherit"); + + QHBoxLayout *qhblAclList = new QHBoxLayout; + qhblAclList->addWidget(qcbACLInherit); + qhblAclList->addStretch(1); + qhblAclList->addWidget(qpbACLUp); + qhblAclList->addWidget(qpbACLDown); + qhblAclList->addWidget(qpbACLAdd); + qhblAclList->addWidget(qpbACLRemove); + + QVBoxLayout *qvblAclList = new QVBoxLayout; + qvblAclList->addWidget(qlwACLs); + qvblAclList->addLayout(qhblAclList); + + qgbACLs->setLayout(qvblAclList); + + QGridLayout *grid = new QGridLayout(); + + qcbACLApplyHere=new QCheckBox(tr("Applies to this channel")); + qcbACLApplyHere->setObjectName("ACLApplyHere"); + qcbACLApplySubs=new QCheckBox(tr("Applies to sub-channels")); + qcbACLApplySubs->setObjectName("ACLApplySubs"); + grid->addWidget(qcbACLApplyHere,0,0); + grid->addWidget(qcbACLApplySubs,1,0); + + qgbACLapply->setLayout(grid); + + grid = new QGridLayout(); + + qcbACLGroup=new QComboBox(); + qcbACLGroup->setEditable(true); + qcbACLGroup->setObjectName("ACLGroup"); + l=new QLabel(tr("Group")); + l->setBuddy(qcbACLGroup); + grid->addWidget(qcbACLGroup,0,0); + grid->addWidget(l,0,1); + + qleACLUser=new QLineEdit(); + qleACLUser->setObjectName("ACLUser"); + l=new QLabel(tr("User ID")); + l->setBuddy(qleACLUser); + grid->addWidget(qleACLUser,1,0); + grid->addWidget(l,1,1); + + qgbACLugroup->setLayout(grid); + + + grid = new QGridLayout(); + + l=new QLabel(tr("Deny")); + grid->addWidget(l,0,1); + l=new QLabel(tr("Allow")); + grid->addWidget(l,0,2); + + int perm=1; + int idx=1; + QString name; + while (! (name = ChanACL::permName(static_cast<ChanACL::Perm>(perm))).isEmpty()) { + QCheckBox *qcb; + l = new QLabel(name); + grid->addWidget(l,idx,0); + qcb=new QCheckBox(); + connect(qcb, SIGNAL(clicked(bool)), this, SLOT(ACLPermissions_clicked())); + grid->addWidget(qcb,idx,1); + qlACLDeny << qcb; + qcb=new QCheckBox(); + connect(qcb, SIGNAL(clicked(bool)), this, SLOT(ACLPermissions_clicked())); + grid->addWidget(qcb,idx,2); + qlACLAllow << qcb; + + idx++; + perm = perm * 2; + } + + qgbACLpermissions->setLayout(grid); + + grid = new QGridLayout(); + grid->addWidget(qgbACLs, 0, 0, 1, 2); + grid->addWidget(qgbACLapply, 1, 0); + grid->addWidget(qgbACLugroup, 2, 0); + grid->addWidget(qgbACLpermissions,1, 1, 2, 1); + aclEditor->setLayout(grid); + + + grid = new QGridLayout(); + + qcbGroupList=new QComboBox(); + qcbGroupList->setObjectName("GroupList"); + qcbGroupList->setEditable(true); + grid->addWidget(qcbGroupList,0,0); + qpbGroupRemove=new QPushButton(tr("Remove")); + qpbGroupRemove->setObjectName("GroupRemove"); + grid->addWidget(qpbGroupRemove,0,1); + qcbGroupInherit=new QCheckBox(tr("Inherit")); + qcbGroupInherit->setObjectName("GroupInherit"); + grid->addWidget(qcbGroupInherit,0,2); + qcbGroupInheritable=new QCheckBox(tr("Inheritable")); + qcbGroupInheritable->setObjectName("GroupInheritable"); + grid->addWidget(qcbGroupInheritable,0,3); + qcbGroupInherited=new QCheckBox(tr("Inherited")); + qcbGroupInherited->setObjectName("GroupInherited"); + qcbGroupInherited->setEnabled(false); + grid->addWidget(qcbGroupInherited,0,4); + + qgbGroups->setLayout(grid); + + grid = new QGridLayout(); + + qlwGroupAdd = new QListWidget(); + qlwGroupAdd->setObjectName("ListGroupAdd"); + qlwGroupRemove = new QListWidget(); + qlwGroupRemove->setObjectName("ListGroupRemove"); + qlwGroupInherit = new QListWidget(); + qlwGroupInherit->setObjectName("ListGroupInherit"); + + l = new QLabel(tr("Add")); + grid->addWidget(l, 0, 0, 1, 2); + l = new QLabel(tr("Remove")); + grid->addWidget(l, 0, 2, 1, 2); + l = new QLabel(tr("Inherit")); + grid->addWidget(l, 0, 4, 1, 2); + + grid->addWidget(qlwGroupAdd, 1, 0, 1, 2); + grid->addWidget(qlwGroupRemove, 1, 2, 1, 2); + grid->addWidget(qlwGroupInherit, 1, 4, 2, 2); + + qleGroupAdd=new QLineEdit(); + qleGroupAdd->setObjectName("GroupAddName"); + qleGroupRemove=new QLineEdit(); + qleGroupRemove->setObjectName("GroupRemoveName"); + + qpbGroupAddAdd=new QPushButton(tr("Add")); + qpbGroupAddAdd->setObjectName("GroupAddAdd"); + qpbGroupAddRemove=new QPushButton(tr("Remove")); + qpbGroupAddRemove->setObjectName("GroupAddRemove"); + + qpbGroupRemoveAdd=new QPushButton(tr("Add")); + qpbGroupRemoveAdd->setObjectName("GroupRemoveAdd"); + qpbGroupRemoveRemove=new QPushButton(tr("Remove")); + qpbGroupRemoveRemove->setObjectName("GroupRemoveRemove"); + + qpbGroupInheritRemove=new QPushButton(tr("Add to Remove")); + qpbGroupInheritRemove->setObjectName("GroupInheritRemove"); + + grid->addWidget(qleGroupAdd, 2, 0); + grid->addWidget(qpbGroupAddAdd, 2, 1); + grid->addWidget(qpbGroupAddRemove, 3, 0, 1, 2); + + grid->addWidget(qleGroupRemove, 2, 2); + grid->addWidget(qpbGroupRemoveAdd, 2, 3); + grid->addWidget(qpbGroupRemoveRemove, 3, 2, 1, 2); + + grid->addWidget(qpbGroupInheritRemove, 3, 4, 1, 2); + + qgbGroupMembers->setLayout(grid); + + grid = new QGridLayout(); + grid->addWidget(qgbGroups, 0, 0); + grid->addWidget(qgbGroupMembers, 1, 0); + + groupEditor->setLayout(grid); + + qtwTab->addTab(groupEditor, tr("&Groups")); + qtwTab->addTab(aclEditor, tr("&ACL")); + + QPushButton *okButton = new QPushButton(tr("&OK")); + connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); + okButton->setToolTip(tr("Accept changes")); + okButton->setWhatsThis(tr("This button will accept current groups/ACLs and send them to " + "the server. Note that if you mistakenly remove write permission " + "from yourself, the server will add it.")); + QPushButton *cancelButton = new QPushButton(tr("&Cancel")); + connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); + cancelButton->setToolTip(tr("Reject changes")); + cancelButton->setWhatsThis(tr("This button will cancel all changes and closes the dialog without " + "updating the ACLs or groups on the server.")); + + QHBoxLayout *buttons = new QHBoxLayout; + buttons->addStretch(1); + buttons->addWidget(okButton); + buttons->addWidget(cancelButton); + + QVBoxLayout *ml = new QVBoxLayout; + ml->addWidget(qtwTab); + ml->addStretch(1); + ml->addSpacing(12); + ml->addLayout(buttons); + setLayout(ml); + + foreach (as, mea->acls) { + asp = new MessageEditACL::ACLStruct(as); + acls << asp; + } + foreach (gs, mea->groups) { + gsp = new MessageEditACL::GroupStruct(gs); + groups << gsp; + } + + numInheritACL = 0; + + bInheritACL = mea->bInheritACL; + qcbACLInherit->setChecked(bInheritACL); + + foreach(asp, acls) { + if (asp->bInherited) + numInheritACL++; + if (asp->iPlayerId != -1) + addQuery(ACLList, asp->iPlayerId); + } + foreach(gsp, groups) { + int id; + foreach(id, gsp->qsAdd) + addQuery(GroupAdd, id); + foreach(id, gsp->qsRemove) + addQuery(GroupRemove, id); + foreach(id, gsp->qsInheritedMembers) + addQuery(GroupInherit, id); + } + + refill(GroupAdd); + refill(GroupRemove); + refill(GroupInherit); + refill(ACLList); + + doneQuery(); + + QMetaObject::connectSlotsByName(this); + + refillGroupNames(); + + resize(minimumSize()); + + ACLEnableCheck(); + groupEnableCheck(); + + addToolTipsWhatsThis(); +} + +ACLEditor::~ACLEditor() { + MessageEditACL::ACLStruct *asp; + MessageEditACL::GroupStruct *gsp; + + foreach(asp, acls) { + delete asp; + } + foreach(gsp, groups) { + delete gsp; + } +} + +void ACLEditor::addToolTipsWhatsThis() { + qlwACLs->setToolTip(tr("List of entries")); + qlwACLs->setWhatsThis(tr("This shows all the entries active on this channel. Entries inherited from parent channels will be " + "show in italics.")); + qcbACLInherit->setToolTip(tr("Inherit ACL of parent?")); + qcbACLInherit->setWhatsThis(tr("This sets whether or not the ACL up the chain of parent channels are applied to this object. " + "Only those entries that are marked in the parent as \"Apply to sub-channels\" will be inherited.")); + qpbACLAdd->setToolTip(tr("Add new entry")); + qpbACLAdd->setWhatsThis(tr("This adds a new entry, initially set with no permissions and applying to all.")); + qpbACLRemove->setToolTip(tr("Remove entry")); + qpbACLRemove->setWhatsThis(tr("This removes the currently selected entry.")); + qpbACLUp->setToolTip(tr("Move entry up")); + qpbACLUp->setWhatsThis(tr("This moves the entry up in the list. As entries are evaluated in order, this may change " + "the effective permissions of users. You cannot move an entry above an inherited entry, if you " + "really need that you'll have to duplicate the inherited entry.")); + qpbACLDown->setToolTip(tr("Move entry down")); + qpbACLDown->setWhatsThis(tr("This moves the entry down in the list. As entries are evaluated in order, this may change " + "the effective permissions of users.")); + qcbACLApplyHere->setToolTip(tr("Entry should apply to this channel")); + qcbACLApplyHere->setWhatsThis(tr("This makes the entry apply to this channel.")); + qcbACLApplySubs->setToolTip(tr("Entry should apply to sub-channels.")); + qcbACLApplySubs->setWhatsThis(tr("This makes the entry apply to sub-channels of this channel.")); + qcbACLGroup->setToolTip(tr("Group this entry applies to")); + qcbACLGroup->setWhatsThis(tr("This controls which group of users this entry applies to.<br />Note that the group is evaluated " + "in the context of the channel the entry is used in. For example, the default ACL on the Root " + "channel gives <i>Write</i> permission to the <i>admin</i> group. This entry, if inherited by a " + "channel, will give a user write privileges if he belongs to the <i>admin</i> group in that channel, " + "even if he doesn't belong to the <i>admin</i> group in the channel where the ACL originated.<br />" + "If a group name starts with a !, its membership is negated, and if it starts with a ~, it is evaluated in the channel the ACL was defined in, rather than " + "the channel the ACL is active in. Order is important; <i>!~in</i> is valid, but <i>~!in</i> is not.<br />" + "A few special predefined groups are:<br />" + "<b>all</b> - Everyone will match.<br />" + "<b>auth</b> - All authenticated users will match.<br />" + "<b>sub,a,b,c</b> - User currently in a sub-channel minimum <i>a</i> common parents, and between <i>b</i> and <i>c</i> channels down the chain. " + "See the website for more extensive documentation on this one.<br />" + "<b>in</b> - Users currently in the channel will match (convenience for <i>sub,0,0,0</i>).<br />" + "<b>out</b> - Users outside the channel will match (convenience for <i>!sub,0,0,0</i>).<br />" + "Note that an entry applies to either a user or a group, not both.")); + qleACLUser->setToolTip(tr("User this entry applies to")); + qleACLUser->setWhatsThis(tr("This controls which user this entry applies to. Just type in the user name and hit enter " + "to query the server for a match.")); + + int idx; + int p = 0x1; + for(idx=0;idx<qlACLAllow.count();idx++) { + ChanACL::Perm prm=static_cast<ChanACL::Perm>(p); + QString perm = ChanACL::permName(prm); + qlACLAllow[idx]->setToolTip(tr("Allow %1").arg(perm)); + qlACLDeny[idx]->setToolTip(tr("Deny %1").arg(perm)); + qlACLAllow[idx]->setWhatsThis(tr("This grants the %1 privilege. If a privilege is both allowed and denied, it is denied.<br />%2").arg(perm).arg(ChanACL::whatsThis(prm))); + qlACLDeny[idx]->setWhatsThis(tr("This revokes the %1 privilege. If a privilege is both allowed and denied, it is denied.<br />%2").arg(perm).arg(ChanACL::whatsThis(prm))); + p = p * 2; + } + + qcbGroupList->setToolTip(tr("List of groups")); + qcbGroupList->setWhatsThis(tr("This is all the groups currently defined for the channel. To create a new group, just type in the " + "name and press enter.")); + qpbGroupRemove->setToolTip(tr("Remove selected group")); + qpbGroupRemove->setWhatsThis(tr("This removes the currently selected group. If the group was inherited, it will not be removed " + "from the list, but all local information about the group will be cleared.")); + qcbGroupInherit->setToolTip(tr("Inherit group members from parent")); + qcbGroupInherit->setWhatsThis(tr("This inherits all the members in the group from the parent, if the group is marked as " + "<i>Inheritable</i> in the parent channel.")); + qcbGroupInheritable->setToolTip(tr("Make group inheritable to sub-channels")); + qcbGroupInheritable->setWhatsThis(tr("This makes this group inheritable to sub-channels. If the group is non-inheritable, " + "sub-channels are still free to create a new group with the same name.")); + qcbGroupInherited->setToolTip(tr("Group was inherited from parent channel")); + qcbGroupInherited->setWhatsThis(tr("This indicates that the group was inherited from the parent channel. You cannot edit " + "this flag, it's just for information.")); + qleGroupAdd->setToolTip(tr("Add member to group")); + qleGroupAdd->setWhatsThis(tr("Type in the name of a player you wish to add to the group and press enter.")); + qleGroupRemove->setToolTip(tr("Remove member from group")); + qleGroupRemove->setWhatsThis(tr("Type in the name of a player you wish to remove from the group and press enter.")); +} + + +void ACLEditor::accept() { + MessageEditACL::ACLStruct as; + MessageEditACL::GroupStruct gs; + MessageEditACL::ACLStruct *asp; + MessageEditACL::GroupStruct *gsp; + MessageEditACL mea; + + mea.iId = iId; + mea.bQuery = false; + mea.bInheritACL = bInheritACL; + + foreach(asp, acls) { + as = *asp; + if (as.bInherited) + continue; + mea.acls << as; + } + + foreach(gsp, groups) { + gs = *gsp; + if (gs.bInherited && gs.bInherit && gs.bInheritable && (gs.qsAdd.count() == 0) && (gs.qsRemove.count() == 0)) + continue; + gs.qsInheritedMembers.clear(); + mea.groups << gs; + } + + g.sh->sendMessage(&mea); + QDialog::accept(); +} + +void ACLEditor::addQuery(WaitID me, int id) { + qhIDWait[id].insert(me); +} + +void ACLEditor::addQuery(WaitID me, QString name) { + qhNameWait[name].insert(me); +} + +void ACLEditor::doneQuery() { + MessageQueryUsers mqu; + + cleanQuery(); + + foreach(int id, qhIDWait.keys()) { + mqu.qlIds << id; + mqu.qlNames << QString(); + } + foreach(QString name, qhNameWait.keys()) { + mqu.qlIds << -1; + mqu.qlNames << name; + } + if (mqu.qlIds.count() > 0) + g.sh->sendMessage(&mqu); +} + +void ACLEditor::cleanQuery() { + QSet<WaitID> notify; + + foreach(int id, qhIDWait.keys()) { + if (qhNameCache.contains(id)) { + notify = notify.unite(qhIDWait.value(id)); + qhIDWait.remove(id); + } + } + foreach(QString name, qhNameWait.keys()) { + if (qhIDCache.contains(name)) { + notify = notify.unite(qhNameWait.value(name)); + qhNameWait.remove(name); + } + } + foreach(WaitID wid, notify) { + refill(wid); + } +} + +void ACLEditor::returnQuery(const MessageQueryUsers *mqu) { + int i; + for(i=0;i<mqu->qlIds.count();i++) { + int id = mqu->qlIds[i]; + QString name = mqu->qlNames[i]; + qhIDCache[name] = id; + qhNameCache[id] = name; + } + cleanQuery(); +} + +void ACLEditor::refill(WaitID wid) { + switch (wid) { + case ACLList: + refillACL(); + break; + case GroupInherit: + refillGroupInherit(); + break; + case GroupRemove: + refillGroupRemove(); + break; + case GroupAdd: + refillGroupAdd(); + break; + } +} + +QString ACLEditor::userName(int id) { + if (qhNameCache.contains(id)) + return qhNameCache.value(id); + else + return QString("#%1").arg(id); +} + +void ACLEditor::refillACL() { + MessageEditACL::ACLStruct *as; + + foreach(as, qhACLNameWait.keys()) { + if (acls.indexOf(as) >= 0) { + QString name = qhACLNameWait.value(as); + if (qhIDCache.contains(name)) { + int id = qhIDCache.value(name); + if (id != -1) { + as->iPlayerId = id; + as->qsGroup = QString(); + } + qhACLNameWait.remove(as); + } + } + } + + int idx = qlwACLs->currentRow(); + bool previnh = bInheritACL; + bInheritACL = qcbACLInherit->isChecked(); + + + qlwACLs->clear(); + foreach(as, acls) { + if (! bInheritACL && as->bInherited) + continue; + QString text; + if (as->iPlayerId == -1) + text=QString("@%1").arg(as->qsGroup); + else + text=userName(as->iPlayerId); + QListWidgetItem *item=new QListWidgetItem(text, qlwACLs); + if (as->bInherited) { + QFont f = item->font(); + f.setItalic(true); + item->setFont(f); + } + } + if (bInheritACL && ! previnh) + idx += numInheritACL; + if (! bInheritACL && previnh) + idx -= numInheritACL; + + qlwACLs->setCurrentRow(idx); +} + +void ACLEditor::refillGroupNames() { + MessageEditACL::GroupStruct *gsp; + + QString text = qcbGroupList->currentText().toLower(); + QStringList qsl; + + foreach(gsp, groups) { + qsl << gsp->qsName; + } + qsl.sort(); + + qcbGroupList->clear(); + foreach(QString name, qsl) + qcbGroupList->addItem(name); + qcbGroupList->setCurrentIndex(qcbGroupList->findText(text, Qt::MatchExactly)); +} + +MessageEditACL::GroupStruct *ACLEditor::currentGroup() { + QString group = qcbGroupList->currentText().toLower(); + MessageEditACL::GroupStruct *gs; + + foreach(gs, groups) { + if (gs->qsName == group) { + return gs; + } + } + + return NULL; +} + +MessageEditACL::ACLStruct *ACLEditor::currentACL() { + int idx = qlwACLs->currentRow(); + if (idx == -1) + return NULL; + + if (! bInheritACL) + idx += numInheritACL; + return acls[idx]; +} + +void ACLEditor::refillGroupAdd() { + MessageEditACL::GroupStruct *gs; + + foreach(gs, qhAddNameWait.keys()) { + if (groups.indexOf(gs) >= 0) { + QString name = qhAddNameWait.value(gs); + if (qhIDCache.contains(name)) { + int id = qhIDCache.value(name); + if (id != -1) { + gs->qsAdd.insert(id); + } + qhAddNameWait.remove(gs); + } + } + } + + gs = currentGroup(); + + if (! gs) + return; + + + QStringList qsl; + foreach(int id, gs->qsAdd) { + qsl << userName(id); + } + qsl.sort(); + qlwGroupAdd->clear(); + foreach(QString name, qsl) { + qlwGroupAdd->addItem(name); + } +} + +void ACLEditor::refillGroupRemove() { + MessageEditACL::GroupStruct *gs; + + foreach(gs, qhRemoveNameWait.keys()) { + if (groups.indexOf(gs) >= 0) { + QString name = qhRemoveNameWait.value(gs); + if (qhIDCache.contains(name)) { + int id = qhIDCache.value(name); + if (id != -1) { + gs->qsRemove.insert(id); + } + qhRemoveNameWait.remove(gs); + } + } + } + + gs = currentGroup(); + if (! gs) + return; + + QStringList qsl; + foreach(int id, gs->qsRemove) { + qsl << userName(id); + } + qsl.sort(); + qlwGroupRemove->clear(); + foreach(QString name, qsl) { + qlwGroupRemove->addItem(name); + } +} + +void ACLEditor::refillGroupInherit() { + MessageEditACL::GroupStruct *gs = currentGroup(); + + if (! gs) + return; + + QStringList qsl; + foreach(int id, gs->qsInheritedMembers) { + qsl << userName(id); + } + qsl.sort(); + qlwGroupInherit->clear(); + foreach(QString name, qsl) { + qlwGroupInherit->addItem(name); + } +} + +void ACLEditor::groupEnableCheck() { + MessageEditACL::GroupStruct *gs = currentGroup(); + + bool ena = true; + + if (! gs) + ena = false; + else + ena = gs->bInherit; + + qlwGroupRemove->setEnabled(ena); + qlwGroupInherit->setEnabled(ena); + qleGroupRemove->setEnabled(ena); + qpbGroupRemoveAdd->setEnabled(ena); + qpbGroupRemoveRemove->setEnabled(ena); + qpbGroupInheritRemove->setEnabled(ena); + + ena = (gs != NULL); + qlwGroupAdd->setEnabled(ena); + qpbGroupAddAdd->setEnabled(ena); + qpbGroupAddRemove->setEnabled(ena); + qcbGroupInherit->setEnabled(ena); + qcbGroupInheritable->setEnabled(ena); + + if (gs) { + qcbGroupInherit->setChecked(gs->bInherit); + qcbGroupInheritable->setChecked(gs->bInheritable); + qcbGroupInherited->setChecked(gs->bInherited); + } +} + +void ACLEditor::ACLEnableCheck() { + MessageEditACL::ACLStruct *as = currentACL(); + MessageEditACL::GroupStruct *gs;; + + bool ena = true; + if (! as) + ena = false; + else + ena = ! as->bInherited; + + qpbACLRemove->setEnabled(ena); + qpbACLUp->setEnabled(ena); + qpbACLDown->setEnabled(ena); + qcbACLApplyHere->setEnabled(ena); + qcbACLApplySubs->setEnabled(ena); + qcbACLGroup->setEnabled(ena); + qleACLUser->setEnabled(ena); + + int idx; + for(idx=0;idx<qlACLAllow.count();idx++) { + qlACLAllow[idx]->setEnabled(ena); + qlACLDeny[idx]->setEnabled(ena); + } + + if (as) { + qcbACLApplyHere->setChecked(as->bApplyHere); + qcbACLApplySubs->setChecked(as->bApplySubs); + int p = 0x1; + for(idx=0;idx<qlACLAllow.count();idx++) { + qlACLAllow[idx]->setChecked(static_cast<int>(as->pAllow) & p); + qlACLDeny[idx]->setChecked(static_cast<int>(as->pDeny) & p); + p = p * 2; + } + qcbACLGroup->clear(); + qcbACLGroup->addItem(QString()); + qcbACLGroup->addItem("all"); + qcbACLGroup->addItem("auth"); + qcbACLGroup->addItem("in"); + qcbACLGroup->addItem("sub"); + qcbACLGroup->addItem("out"); + qcbACLGroup->addItem("~in"); + qcbACLGroup->addItem("~sub"); + qcbACLGroup->addItem("~out"); + foreach(gs, groups) + qcbACLGroup->addItem(gs->qsName); + if (as->iPlayerId == -1) { + qleACLUser->setText(QString()); + qcbACLGroup->addItem(as->qsGroup); + qcbACLGroup->setCurrentIndex(qcbACLGroup->findText(as->qsGroup, Qt::MatchExactly)); + } else { + qleACLUser->setText(userName(as->iPlayerId)); + } + } +} + +void ACLEditor::on_ACLList_currentRowChanged() { + ACLEnableCheck(); +} + +void ACLEditor::on_ACLAdd_clicked() { + MessageEditACL::ACLStruct *as = new MessageEditACL::ACLStruct; + as->bApplyHere = true; + as->bApplySubs = true; + as->bInherited = false; + as->qsGroup = "all"; + as->iPlayerId = -1; + as->pAllow = ChanACL::None; + as->pDeny = ChanACL::None; + acls << as; + refillACL(); + qlwACLs->setCurrentRow(qlwACLs->count() - 1); +} + +void ACLEditor::on_ACLRemove_clicked() { + MessageEditACL::ACLStruct *as = currentACL(); + if (! as || as->bInherited) + return; + acls.removeAll(as); + delete as; + refillACL(); +} + +void ACLEditor::on_ACLUp_clicked() { + MessageEditACL::ACLStruct *as = currentACL(); + if (! as || as->bInherited) + return; + + int idx = acls.indexOf(as); + if (idx <= numInheritACL) + return; + acls.swap(idx - 1, idx); + qlwACLs->setCurrentRow(qlwACLs->currentRow() - 1); + refillACL(); +} + +void ACLEditor::on_ACLDown_clicked() { + MessageEditACL::ACLStruct *as = currentACL(); + if (! as || as->bInherited) + return; + + int idx = acls.indexOf(as) + 1; + if (idx >= acls.count()) + return; + acls.swap(idx - 1, idx); + qlwACLs->setCurrentRow(qlwACLs->currentRow() + 1); + refillACL(); +} + +void ACLEditor::on_ACLInherit_clicked(bool checked) { + refillACL(); +} + +void ACLEditor::on_ACLApplyHere_clicked(bool checked) { + MessageEditACL::ACLStruct *as = currentACL(); + if (! as || as->bInherited) + return; + + as->bApplyHere = checked; +} + +void ACLEditor::on_ACLApplySubs_clicked(bool checked) { + MessageEditACL::ACLStruct *as = currentACL(); + if (! as || as->bInherited) + return; + + as->bApplySubs = checked; +} + +void ACLEditor::on_ACLGroup_activated(const QString &text) { + MessageEditACL::ACLStruct *as = currentACL(); + if (! as || as->bInherited) + return; + + as->iPlayerId = -1; + + if (text.isEmpty()) { + qcbACLGroup->setCurrentIndex(1); + as->qsGroup="all"; + } else { + qleACLUser->setText(QString()); + as->qsGroup=text; + } + refillACL(); +} + +void ACLEditor::on_ACLUser_editingFinished() { + QString text = qleACLUser->text(); + + MessageEditACL::ACLStruct *as = currentACL(); + if (! as || as->bInherited) + return; + + if (text.isEmpty()) { + as->iPlayerId = -1; + if (qcbACLGroup->currentIndex() == 0) { + qcbACLGroup->setCurrentIndex(1); + as->qsGroup="all"; + } + refillACL(); + } else { + qcbACLGroup->setCurrentIndex(0); + qhACLNameWait[as] = text; + addQuery(ACLList, text); + doneQuery(); + } +} + +void ACLEditor::ACLPermissions_clicked() { + QCheckBox *source = qobject_cast<QCheckBox *>(sender()); + + MessageEditACL::ACLStruct *as = currentACL(); + if (! as || as->bInherited) + return; + + int a, d, p, idx; + a = 0; + d = 0; + + p = 0x1; + for(idx=0;idx<qlACLAllow.count();idx++) { + if (qlACLAllow[idx]->isChecked() && qlACLDeny[idx]->isChecked()) { + if (source == qlACLAllow[idx]) + qlACLDeny[idx]->setChecked(false); + else + qlACLAllow[idx]->setChecked(false); + } + if (qlACLAllow[idx]->isChecked()) + a |= p; + if (qlACLDeny[idx]->isChecked()) + d |= p; + p = p * 2; + } + + as->pAllow=static_cast<ChanACL::Permissions>(a); + as->pDeny=static_cast<ChanACL::Permissions>(d); +} + +void ACLEditor::on_GroupList_activated(const QString &text) { + MessageEditACL::GroupStruct *gs = currentGroup(); + if (text.isEmpty()) + return; + if (! gs) { + QString name = text.toLower(); + gs = new MessageEditACL::GroupStruct; + gs->bInherited = false; + gs->bInherit = true; + gs->bInheritable = true; + gs->qsName = name; + groups << gs; + } + + refillGroupNames(); + refillGroupAdd(); + refillGroupRemove(); + refillGroupInherit(); + groupEnableCheck(); +} + +void ACLEditor::on_GroupRemove_clicked() { + MessageEditACL::GroupStruct *gs = currentGroup(); + if (! gs) + return; + if (gs->bInherited) { + gs->bInheritable = true; + gs->bInherit = true; + gs->qsAdd.clear(); + gs->qsRemove.clear(); + } else { + groups.removeAll(gs); + delete gs; + } + refillGroupNames(); + refillGroupAdd(); + refillGroupRemove(); + refillGroupInherit(); + groupEnableCheck(); +} + +void ACLEditor::on_GroupInherit_clicked(bool checked) { + MessageEditACL::GroupStruct *gs = currentGroup(); + if (! gs) + return; + gs->bInherit = checked; + groupEnableCheck(); +} + +void ACLEditor::on_GroupInheritable_clicked(bool checked) { + MessageEditACL::GroupStruct *gs = currentGroup(); + if (! gs) + return; + gs->bInheritable = checked; +} + +void ACLEditor::on_GroupAddName_editingFinished() { + QString text = qleGroupAdd->text(); + + MessageEditACL::GroupStruct *gs = currentGroup(); + if (! gs) + return; + + if (text.isEmpty()) + return; + + qhAddNameWait[gs] = text; + addQuery(GroupAdd, text); + doneQuery(); +} + +void ACLEditor::on_GroupAddAdd_clicked() { + on_GroupAddName_editingFinished(); +} + +void ACLEditor::on_GroupAddRemove_clicked() { + MessageEditACL::GroupStruct *gs = currentGroup(); + if (! gs) + return; + + QListWidgetItem *item= qlwGroupAdd->currentItem(); + if (! item) + return; + + int id = qhIDCache.value(item->text()); + gs->qsAdd.remove(id); + refillGroupAdd(); +} + +void ACLEditor::on_GroupRemoveName_editingFinished() { + QString text = qleGroupRemove->text(); + + MessageEditACL::GroupStruct *gs = currentGroup(); + if (! gs) + return; + + if (text.isEmpty()) + return; + + qhRemoveNameWait[gs] = text; + addQuery(GroupRemove, text); + doneQuery(); +} + +void ACLEditor::on_GroupRemoveAdd_clicked() { + on_GroupRemoveName_editingFinished(); +} + +void ACLEditor::on_GroupRemoveRemove_clicked() { + MessageEditACL::GroupStruct *gs = currentGroup(); + if (! gs) + return; + + QListWidgetItem *item= qlwGroupRemove->currentItem(); + if (! item) + return; + + int id = qhIDCache.value(item->text()); + gs->qsRemove.remove(id); + refillGroupRemove(); +} + +void ACLEditor::on_GroupInheritRemove_clicked() { + MessageEditACL::GroupStruct *gs = currentGroup(); + if (! gs) + return; + + QListWidgetItem *item= qlwGroupInherit->currentItem(); + if (! item) + return; + + int id = qhIDCache.value(item->text()); + gs->qsRemove.insert(id); + refillGroupRemove(); +} diff --git a/src/mumble/ACLEditor.h b/src/mumble/ACLEditor.h new file mode 100644 index 000000000..7381d1561 --- /dev/null +++ b/src/mumble/ACLEditor.h @@ -0,0 +1,141 @@ +/* Copyright (C) 2005-2006, 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 _ACLEDITOR_H +#define _ACLEDITOR_H + +#include "Message.h" + +class ACLEditor : public QDialog { + Q_OBJECT; + protected: + enum WaitID { + GroupAdd, GroupRemove, GroupInherit, ACLList + }; + int iId; + QHash<int, QString> qhNameCache; + QHash<QString, int> qhIDCache; + + QHash<int, QSet<WaitID> > qhIDWait; + QHash<QString, QSet<WaitID> > qhNameWait; + + void addQuery(WaitID me, int id); + void addQuery(WaitID me, QString name); + void doneQuery(); + void cleanQuery(); + void refill(WaitID what); + + QHash<MessageEditACL::ACLStruct *, QString> qhACLNameWait; + QHash<MessageEditACL::GroupStruct *, QString> qhAddNameWait; + QHash<MessageEditACL::GroupStruct *, QString> qhRemoveNameWait; + + MessageEditACL::GroupStruct *currentGroup(); + MessageEditACL::ACLStruct *currentACL(); + + bool bInheritACL; + QList<MessageEditACL::ACLStruct *> acls; + QList<MessageEditACL::GroupStruct *> groups; + int numInheritACL; + + QString userName(int id); + + QCheckBox *qcbACLInherit; + QPushButton *qpbACLAdd; + QPushButton *qpbACLRemove; + QPushButton *qpbACLUp; + QPushButton *qpbACLDown; + QListWidget *qlwACLs; + QCheckBox *qcbACLApplyHere; + QCheckBox *qcbACLApplySubs; + QComboBox *qcbACLGroup; + QLineEdit *qleACLUser; + QList<QCheckBox *> qlACLAllow; + QList<QCheckBox *> qlACLDeny; + + QComboBox *qcbGroupList; + QPushButton *qpbGroupRemove; + QCheckBox *qcbGroupInherit; + QCheckBox *qcbGroupInheritable; + QCheckBox *qcbGroupInherited; + QListWidget *qlwGroupAdd; + QListWidget *qlwGroupRemove; + QListWidget *qlwGroupInherit; + QLineEdit *qleGroupAdd; + QLineEdit *qleGroupRemove; + QPushButton *qpbGroupAddAdd; + QPushButton *qpbGroupAddRemove; + QPushButton *qpbGroupRemoveAdd; + QPushButton *qpbGroupRemoveRemove; + QPushButton *qpbGroupInheritRemove; + + void addToolTipsWhatsThis(); + public: + ACLEditor(const MessageEditACL *mea, QWidget *p = NULL); + ~ACLEditor(); + void returnQuery(const MessageQueryUsers *mqu); + public slots: + void accept(); + public slots: + void refillACL(); + void refillGroupNames(); + void refillGroupAdd(); + void refillGroupRemove(); + void refillGroupInherit(); + void groupEnableCheck(); + void ACLEnableCheck(); + + void on_ACLList_currentRowChanged(); + void on_ACLAdd_clicked(); + void on_ACLRemove_clicked(); + void on_ACLUp_clicked(); + void on_ACLDown_clicked(); + void on_ACLInherit_clicked(bool checked); + void on_ACLApplyHere_clicked(bool checked); + void on_ACLApplySubs_clicked(bool checked); + void on_ACLGroup_activated(const QString &text); + void on_ACLUser_editingFinished(); + void ACLPermissions_clicked(); + + void on_GroupList_activated(const QString &text); + void on_GroupRemove_clicked(); + void on_GroupInherit_clicked(bool checked); + void on_GroupInheritable_clicked(bool checked); + void on_GroupAddName_editingFinished(); + void on_GroupAddAdd_clicked(); + void on_GroupAddRemove_clicked(); + void on_GroupRemoveName_editingFinished(); + void on_GroupRemoveAdd_clicked(); + void on_GroupRemoveRemove_clicked(); + void on_GroupInheritRemove_clicked(); +}; + +#else +class ACLEditor; +#endif diff --git a/src/mumble/ALSAAudio.cpp b/src/mumble/ALSAAudio.cpp new file mode 100644 index 000000000..a5154a4d4 --- /dev/null +++ b/src/mumble/ALSAAudio.cpp @@ -0,0 +1,328 @@ +/* Copyright (C) 2005-2006, 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 "ALSAAudio.h" +#include "Player.h" +#include "Global.h" +#include <sys/poll.h> + +#define NBLOCKS 8 + +#define MAX(a,b) ( (a) > (b) ? (a) : (b) ) +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) + +static AudioOutput *ALSAAudioOutputNew() +{ + return new ALSAAudioOutput(); +} +static AudioInput *ALSAAudioInputNew() +{ + return new ALSAAudioInput(); +} + +static AudioOutputRegistrar aorALSA("ALSA", ALSAAudioOutputNew); +static AudioInputRegistrar airALSA("ALSA", ALSAAudioInputNew); + +ALSAAudioInput::ALSAAudioInput() +{ +} + +ALSAAudioInput::~ALSAAudioInput() +{ + // Signal input thread to end + bRunning = false; + wait(); +} + +void ALSAAudioInput::run() +{ + int readblapp; + + + const char *device_name = "default"; + snd_pcm_hw_params_t *hw_params; + + unsigned int rrate = SAMPLE_RATE; + + bRunning = true; + int err = 0; + + qWarning("ALSAAudioInput: Initing audiocapture."); + err = snd_pcm_open(&capture_handle, device_name, SND_PCM_STREAM_CAPTURE, 0); + if (err < 0) qWarning("ALSAAudioInput: %s", snd_strerror(err)); + + /* Setup parameters using hw_params structure */ + err = snd_pcm_hw_params_malloc(&hw_params); + if (err < 0) qWarning("ALSAAudioInput: Allocating Memory: %s", snd_strerror(err)); + err = snd_pcm_hw_params_any(capture_handle, hw_params); + if (err < 0) qWarning("ALSAAudioInput: Any Settings: %s", snd_strerror(err)); + err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) qWarning("ALSAAudioInput: Access: %s", snd_strerror(err)); + err = snd_pcm_hw_params_set_format(capture_handle, hw_params, SND_PCM_FORMAT_S16_LE); + if (err < 0) qWarning("ALSAAudioInput: Format: %s", snd_strerror(err)); + err = snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &rrate, NULL); + if (err < 0) qWarning("ALSAAudioInput: Rate: %s", snd_strerror(err)); + err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, 1); + if (err < 0) qWarning("ALSAAudioInput: Channels: %s", snd_strerror(err)); + + err = snd_pcm_hw_params_set_buffer_size_near(capture_handle, hw_params, (snd_pcm_uframes_t *)&iByteSize); + if (err < 0) qWarning("ALSAAudioInput: Buffer Size: %s", snd_strerror(err)); + err = snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params, (snd_pcm_uframes_t *)&iFrameSize, NULL); + if (err < 0) qWarning("ALSAAudioInput: Period Size: %s", snd_strerror(err)); + err = snd_pcm_hw_params(capture_handle, hw_params); + if (err < 0) qWarning("ALSAAudioInput: hw params: %s", snd_strerror(err)); + + snd_pcm_hw_params_free(hw_params); + +/* + * Add this information to either very very very vers verbose output + * or debug output + snd_output_t *log; + snd_pcm_status_t *status; + snd_output_stdio_attach(&log, stderr,0 ); + snd_pcm_dump(capture_handle, log); + */ + + /* Prepare device */ + err = snd_pcm_prepare(capture_handle); + if (err < 0) qWarning("ALSAAudioInput: Prepare: %s", snd_strerror(err)); + + /* Start device - not necessary */ + err = snd_pcm_start(capture_handle); + if (err < 0) qWarning("ALSAAudioInput: Start: %s", snd_strerror(err)); + + while (bRunning) { +/* + * Add this information to either very very very very verbose output + * or debug output. + snd_pcm_status_malloc(&status); + snd_pcm_status(capture_handle, status); + snd_pcm_status_dump(status, log); + snd_pcm_status_free(status); + */ + readblapp = snd_pcm_readi(capture_handle, psMic, iFrameSize); + if (readblapp == -ESTRPIPE) { + // suspend event - what to do? + qWarning("ALSAAudioInput: %s", snd_strerror(readblapp)); + } else if (readblapp == -EPIPE) { + err = snd_pcm_prepare(capture_handle); + qWarning("ALSAAudioInput: %s: %s", snd_strerror(readblapp), snd_strerror(err)); + } else if (readblapp < 0) { + err = snd_pcm_prepare(capture_handle); + qWarning("ALSAAudioInput: %s: %s", snd_strerror(readblapp), snd_strerror(err)); + } else if (iFrameSize == readblapp) { + encodeAudioFrame(); + } + } + + snd_pcm_drain(capture_handle); + snd_pcm_close(capture_handle); + + qWarning("ALSAAudioInput: Releasing ALSA Mic."); +} + +ALSAOutputPlayer::ALSAOutputPlayer(ALSAAudioOutput * ao, Player * player):AudioOutputPlayer(ao, player) +{ + aao = static_cast < ALSAAudioOutput * >(aoOutput); + pcm_handle = NULL; + iAliveHold = 0; +} + +ALSAOutputPlayer::~ALSAOutputPlayer() +{ + if (pcm_handle) { + snd_pcm_close(pcm_handle); + } + // If playing, stop + qWarning("ALSAOutputPlayer: %s: Removed", qPrintable(p->qsName)); +} + +bool ALSAOutputPlayer::playFrames() +{ + int avail; + bool alive; + + avail = snd_pcm_avail_update(pcm_handle); + if (avail < 0) + return false; + while (avail >= iFrameSize) { + alive = decodeNextFrame(); + snd_pcm_writei(pcm_handle, psBuffer, iFrameSize); + avail = snd_pcm_avail_update(pcm_handle); + if (avail < 0) + return false; + if (alive) + iAliveHold = 0; + else { + iAliveHold++; + if (iAliveHold > 50) { + return false; + } + } + } + return true; +} + +void ALSAOutputPlayer::initialize() +{ + if (pcm_handle) + return; + + unsigned int rate = SAMPLE_RATE; + snd_pcm_uframes_t period_size = iFrameSize; + snd_pcm_uframes_t buffer_size = iFrameSize * 5; + + snd_pcm_hw_params_t *hw_params; + snd_pcm_sw_params_t *sw_params; + + /* The device name */ + const char *device_name = "default"; + + /* Open the device */ + snd_pcm_open(&pcm_handle, device_name, SND_PCM_STREAM_PLAYBACK, 0); + + /* Error check */ + + snd_pcm_hw_params_malloc(&hw_params); + snd_pcm_hw_params_any(pcm_handle, hw_params); + + snd_pcm_hw_params_set_access(pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); + snd_pcm_hw_params_set_format(pcm_handle, hw_params, SND_PCM_FORMAT_S16_LE); + snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, &rate, NULL); + snd_pcm_hw_params_set_channels(pcm_handle, hw_params, 1); + + snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hw_params, &buffer_size); + snd_pcm_hw_params_set_period_size_near(pcm_handle, hw_params, &period_size, NULL); + + /* Apply parameters */ + snd_pcm_hw_params(pcm_handle, hw_params); + + /* Free HW structure */ + snd_pcm_hw_params_free(hw_params); + + /* Software paramters */ + snd_pcm_sw_params_malloc(&sw_params); + snd_pcm_sw_params_current(pcm_handle, sw_params); + + snd_pcm_sw_params_set_start_threshold(pcm_handle, sw_params, 0); + snd_pcm_sw_params_set_avail_min(pcm_handle, sw_params, period_size); + + snd_pcm_sw_params(pcm_handle, sw_params); + snd_pcm_sw_params_free(sw_params); + + /* Prepare device */ + snd_pcm_prepare(pcm_handle); + + // Fill one frame + for (int i = 0; i < 5; i++) + snd_pcm_writei(pcm_handle, psBuffer, iFrameSize); + + snd_pcm_start(pcm_handle); +} + +ALSAAudioOutput::ALSAAudioOutput() +{ + qWarning("ALSAAudioOutput: Initialized"); +} + +ALSAAudioOutput::~ALSAAudioOutput() +{ + bRunning = false; + // Call destructor of all children + wipe(); + // Wait for terminate + wait(); + qWarning("ALSAAudioOutput: Destroyed"); +} + +AudioOutputPlayer *ALSAAudioOutput::getPlayer(Player * player) +{ + return new ALSAOutputPlayer(this, player); +} + +#define MAX_FDS 256 + +void ALSAAudioOutput::run() +{ + struct pollfd fds[MAX_FDS]; + int count, idx; + QSet <ALSAOutputPlayer *> del; + QMap <ALSAOutputPlayer *, int> map; + QMap <ALSAOutputPlayer *, int> cmap; + + + bRunning = true; + + ALSAOutputPlayer *aaop; + AudioOutputPlayer *aop; + + while (bRunning) { + del.clear(); + map.clear(); + cmap.clear(); + + qrwlOutputs.lockForRead(); + idx = 0; + foreach(aop, qmOutputs) { + aaop = static_cast < ALSAOutputPlayer * >(aop); + aaop->initialize(); + count = snd_pcm_poll_descriptors_count(aaop->pcm_handle); + snd_pcm_poll_descriptors(aaop->pcm_handle, &fds[idx], count); + map[aaop] = idx; + cmap[aaop] = count; + idx += count; + } + qrwlOutputs.unlock(); + + poll(fds, idx, 20); + + qrwlOutputs.lockForRead(); + foreach(aop, qmOutputs) { + unsigned short revents; + aaop = static_cast < ALSAOutputPlayer * >(aop); + idx = map[aaop]; + count = cmap[aaop]; + if (count) { + snd_pcm_poll_descriptors_revents(aaop->pcm_handle, &fds[idx], count, &revents); + if (revents & POLLERR) { + del.insert(aaop); + } else if (revents & POLLOUT) { + if (!aaop->playFrames()) + del.insert(aaop); + } + } + } + qrwlOutputs.unlock(); + + foreach(aaop, del) { + removeBuffer(aaop->p); + } + } +} diff --git a/src/mumble/ALSAAudio.h b/src/mumble/ALSAAudio.h new file mode 100644 index 000000000..c93d51cc3 --- /dev/null +++ b/src/mumble/ALSAAudio.h @@ -0,0 +1,81 @@ +/* Copyright (C) 2005-2006, 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 _ALSAAUDIO_H +#define _ALSAAUDIO_H + +#include "AudioInput.h" +#include "AudioOutput.h" +#include <alsa/asoundlib.h> + +class ALSAAudioOutput; +class ALSAAudioInput; + +class ALSAAudioInput : public AudioInput { + snd_pcm_t *capture_handle; + Q_OBJECT + protected: + void release(); + public: + ALSAAudioInput(); + ~ALSAAudioInput(); + void run(); +}; + +class ALSAAudioOutput; +class ALSAOutputPlayer : public AudioOutputPlayer { + friend class ALSAAudioOutput; + Q_OBJECT + protected: + ALSAAudioOutput *aao; + int iAliveHold; + bool playFrames(); + public: + void initialize(); + ALSAOutputPlayer(ALSAAudioOutput *, Player *); + ~ALSAOutputPlayer(); +}; + + +class ALSAAudioOutput : public AudioOutput { + friend class ALSAOutputPlayer; + Q_OBJECT + protected: + virtual AudioOutputPlayer *getPlayer(Player *); + public: + ALSAAudioOutput(); + ~ALSAAudioOutput(); + void run(); +}; + +#else +class ALSAAudioInput; +class ALSAAudioOutput; +#endif diff --git a/src/mumble/ASIOInput.cpp b/src/mumble/ASIOInput.cpp new file mode 100644 index 000000000..eee3c6161 --- /dev/null +++ b/src/mumble/ASIOInput.cpp @@ -0,0 +1,788 @@ +/* Copyright (C) 2005-2006, 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 "ASIOInput.h" +#include "MainWindow.h" +#include "Global.h" + +/* + * This file blatanly assumes the sampling frequency is 48000Hz. + * Anything else is an error, and to fix it all the constants + * of 960 need to be replaced. + * (960 == 20 ms of samples at 48000) + */ + +static AudioInput *ASIOAudioInputNew() { + return new ASIOInput(); +} + +static AudioInputRegistrar airASIO("ASIO", ASIOAudioInputNew); + + +static ConfigWidget *ASIOConfigDialogNew() { + return new ASIOConfig(); +} + +static ConfigRegistrar registrar(22, ASIOConfigDialogNew); + + +ASIOInput *ASIOInput::aiSelf; + +ASIOConfig::ASIOConfig(QWidget *p) : ConfigWidget(p) { + HKEY hkDevs; + HKEY hk; + DWORD idx = 0; + WCHAR keyname[255]; + DWORD keynamelen = 255; + FILETIME ft; + HRESULT hr; + + // List of devices known to misbehave or be totally useless + QStringList blacklist; + blacklist << "{a91eaba1-cf4c-11d3-b96a-00a0c9c7b61a}"; // ASIO DirectX + blacklist << "{e3186861-3a74-11d1-aef8-0080ad153287}"; // ASIO Multimedia + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\ASIO", 0, KEY_READ, &hkDevs) == ERROR_SUCCESS) { + while (RegEnumKeyEx(hkDevs, idx++, keyname, &keynamelen, NULL, NULL, NULL, &ft) == ERROR_SUCCESS) { + QString name=QString::fromUtf16(reinterpret_cast<ushort *>(keyname),keynamelen); + if (RegOpenKeyEx(hkDevs, keyname, 0, KEY_READ, &hk) == ERROR_SUCCESS) { + DWORD dtype = REG_SZ; + WCHAR wclsid[255]; + DWORD datasize = 255; + CLSID clsid; + if (RegQueryValueEx(hk, L"CLSID", 0, &dtype, reinterpret_cast<BYTE *>(wclsid), &datasize) == ERROR_SUCCESS) { + QString qsCls = QString::fromUtf16(reinterpret_cast<ushort *>(wclsid), datasize / 2); + if (! blacklist.contains(qsCls.toLatin1()) && ! FAILED(hr =CLSIDFromString(wclsid, &clsid))) { + ASIODev ad(name, qsCls); + qlDevs << ad; + } + } + RegCloseKey(hk); + } + keynamelen = 255; + } + RegCloseKey(hkDevs); + } + + bOk = false; + + QGroupBox *qgbDevices, *qgbCapab, *qgbChannels; + QGridLayout *grid; + QVBoxLayout *v; + QHBoxLayout *h; + ASIODev ad; + QLabel *l; + + qcbDevice = new QComboBox(); + qcbDevice->setObjectName("Device"); + + foreach(ad, qlDevs) { + qcbDevice->addItem(ad.first, QVariant(ad.second)); + if (ad.second == g.s.qsASIOclass) { + qcbDevice->setCurrentIndex(qcbDevice->count() - 1); + } + } + + qgbDevices=new QGroupBox(tr("Device selection")); + qgbCapab = new QGroupBox(tr("Capabilities")); + qgbChannels = new QGroupBox(tr("Channels")); + qgbChannels->setToolTip(tr("Configure input channels")); + qgbChannels->setWhatsThis(tr("This will configure the input channels for ASIO. Make sure you select at least one " + "channel as microphone and speaker. <i>Microphone</i> should be where your microphone is attached, " + "and <i>Speaker</i> should be a channel that samples \"What you hear\".<br />" + "For example, on the Audigy 2 ZS, a good selection for Microphone would be \"Mic L\" while " + "Speaker should be \"Mix L\" and \"Mix R\"")); + + grid=new QGridLayout(); + + qcbDevice->setToolTip(tr("Device to use for microphone")); + qcbDevice->setWhatsThis(tr("This chooses what device to query. You still need to actually query the device and " + "select which channels to use.")); + l = new QLabel(tr("Device")); + + QPushButton *queryButton=new QPushButton(tr("&Query")); + queryButton->setObjectName("Query"); + queryButton->setToolTip(tr("Query selected device")); + queryButton->setWhatsThis(tr("This queries the selected device for channels. Be aware that many ASIO drivers are " + "buggy to the extreme, and querying them might cause a crash of either the application " + "or the system.")); + + QPushButton *configButton=new QPushButton(tr("&Configure")); + configButton->setObjectName("Config"); + configButton->setToolTip(tr("Configure selected device")); + configButton->setWhatsThis(tr("This configures the selected device. Be aware that many ASIO drivers are " + "buggy to the extreme, and querying them might cause a crash of either the application " + "or the system.")); + + grid->addWidget(l, 0, 0); + grid->addWidget(qcbDevice, 0, 1); + grid->addWidget(queryButton, 0, 2); + grid->addWidget(configButton, 0, 3); + + qgbDevices->setLayout(grid); + + + grid = new QGridLayout(); + + qlName=new QLabel(); + qlBuffers=new QLabel(); + + l = new QLabel(tr("Driver name")); + l->setBuddy(qlName); + grid->addWidget(l, 0, 0); + grid->addWidget(qlName, 0, 1); + + l = new QLabel(tr("Buffersize")); + l->setBuddy(qlBuffers); + grid->addWidget(l, 1, 0); + grid->addWidget(qlBuffers, 1, 1); + + grid->setColumnStretch(1, 1); + + qgbCapab->setLayout(grid); + + + h = new QHBoxLayout; + + v = new QVBoxLayout; + qlwMic = new QListWidget(); + qlwMic->setMinimumWidth(100); + qlwMic->setMaximumWidth(100); + l = new QLabel(tr("Microphone")); + v->addWidget(l); + v->addWidget(qlwMic); + h->addLayout(v); + + QPushButton *addMicButton=new QPushButton(tr("<-")); + addMicButton->setObjectName("AddMic"); + addMicButton->setMaximumWidth(20); + QPushButton *remMicButton=new QPushButton(tr("->")); + remMicButton->setObjectName("RemMic"); + remMicButton->setMaximumWidth(20); + v = new QVBoxLayout; + v->addStretch(1); + v->addWidget(addMicButton); + v->addWidget(remMicButton); + v->addStretch(1); + h->addLayout(v); + + v = new QVBoxLayout; + qlwUnused = new QListWidget(); + qlwUnused->setMinimumWidth(100); + qlwUnused->setMaximumWidth(100); + l = new QLabel(tr("Unused")); + v->addWidget(l); + v->addWidget(qlwUnused); + h->addLayout(v); + + QPushButton *addSpeakerButton=new QPushButton(tr("->")); + addSpeakerButton->setObjectName("AddSpeaker"); + addSpeakerButton->setMaximumWidth(20); + QPushButton *remSpeakerButton=new QPushButton(tr("<-")); + remSpeakerButton->setObjectName("RemSpeaker"); + remSpeakerButton->setMaximumWidth(20); + v = new QVBoxLayout; + v->addStretch(1); + v->addWidget(remSpeakerButton); + v->addWidget(addSpeakerButton); + v->addStretch(1); + h->addLayout(v); + + v = new QVBoxLayout; + qlwSpeaker = new QListWidget(); + qlwSpeaker->setMinimumWidth(100); + qlwSpeaker->setMaximumWidth(100); + l = new QLabel(tr("Speakers")); + v->addWidget(l); + v->addWidget(qlwSpeaker); + h->addLayout(v); + + + qgbChannels->setLayout(h); + + if (qlDevs.count() == 0) { + queryButton->setEnabled(false); + configButton->setEnabled(false); + } + + v = new QVBoxLayout(); + v->addWidget(qgbDevices); + v->addWidget(qgbCapab); + v->addWidget(qgbChannels); + v->addStretch(1); + + setLayout(v); + + QMetaObject::connectSlotsByName(this); +} + +#include "iasiothiscallresolver.h" + +void ASIOConfig::on_Query_clicked() { + QString qsCls = qcbDevice->itemData(qcbDevice->currentIndex()).toString(); + CLSID clsid; + IASIO *iorigasio; + + bool ok = false; + + clearQuery(); + + CLSIDFromString(const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(qsCls.utf16())), &clsid); + if (CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, clsid, reinterpret_cast<void **>(&iorigasio)) == S_OK) { + SleepEx(10, false); + IASIOThiscallResolver iasio(iorigasio); + if (iasio.init(winId())) { + SleepEx(10, false); + char buff[512]; + memset(buff, 0, 512); + + iasio.getDriverName(buff); + SleepEx(10, false); + + long ver = iasio.getDriverVersion(); + SleepEx(10, false); + + ASIOSampleRate srate; + iasio.getSampleRate(&srate); + SleepEx(10, false); + if (fabs(srate-48000.0) > 1.0) { + iasio.setSampleRate(48000.0); + SleepEx(10, false); + iasio.getSampleRate(&srate); + SleepEx(10, false); + } + + long minSize, maxSize, prefSize, granSize; + iasio.getBufferSize(&minSize, &maxSize, &prefSize, &granSize); + SleepEx(10, false); + + // We need sample sizes in ms for 2 bytes at 48khz + + bOk = false; + + if ((fabs(srate-48000.0) < 1.0) && (minSize <= 1920) && (maxSize >= 1920)) { + if (granSize > 0) { + long remain; + remain = 1920 - minSize; + if ((remain % granSize) == 0) + bOk = true; + } else if (granSize == -1) { + long v = minSize; + for(int i=0;i<100;i++) { + if (v == 1920) { + bOk = true; + break; + } + v = v * 2; + } + } + } + + long divider = lround(srate * 2.0 / 1000.0); + if (divider < 1) + divider = 1; + + if (granSize > 0) + granSize /= divider; + minSize /= divider; + maxSize /= divider; + prefSize /= divider; + + QString s = tr("%1 (ver %2)").arg(buff).arg(ver); + qlName->setText(s); + + if (bOk) + s = tr("%1 ms -> %2 ms (%3 ms resolution) %4Hz").arg(minSize).arg(maxSize).arg(granSize).arg(srate,0,'f',0); + else + s = tr("%1 ms -> %2 ms (%3 ms resolution) %4Hz -- Unusable").arg(minSize).arg(maxSize).arg(granSize).arg(srate,0,'f',0); + + qlBuffers->setText(s); + + if (bOk) { + long ichannels, ochannels; + iasio.getChannels(&ichannels, &ochannels); + SleepEx(10, false); + long cnum; + + bool match = (g.s.qsASIOclass == qsCls); + for(cnum=0;cnum<ichannels;cnum++) { + ASIOChannelInfo aci; + aci.channel = cnum; + aci.isInput = true; + iasio.getChannelInfo(&aci); + SleepEx(10, false); + switch(aci.type) { + case ASIOSTInt32LSB: + case ASIOSTInt16LSB: + { + QListWidget *widget = qlwUnused; + QVariant v = static_cast<int>(cnum); + if (match && g.s.qlASIOmic.contains(v)) + widget = qlwMic; + else if (match && g.s.qlASIOspeaker.contains(v)) + widget = qlwSpeaker; + QListWidgetItem *item = new QListWidgetItem(aci.name, widget); + item->setData(Qt::UserRole, static_cast<int>(cnum)); + } + break; + default: + qWarning("ASIOInput: Channel %d %s (Unusable format %d)", cnum, aci.name,aci.type); + } + } + } + + ok = true; + } + if (! ok) { + SleepEx(10, false); + char err[255]; + iasio.getErrorMessage(err); + SleepEx(10, false); + QMessageBox::critical(this, tr("Mumble"), tr("ASIO Initialization failed: %1").arg(err), QMessageBox::Ok, QMessageBox::NoButton); + } + iasio.Release(); + } else { + QMessageBox::critical(this, tr("Mumble"), tr("Failed to instantiate ASIO driver"), QMessageBox::Ok, QMessageBox::NoButton); + } +} + +void ASIOConfig::on_Config_clicked() { + QString qsCls = qcbDevice->itemData(qcbDevice->currentIndex()).toString(); + CLSID clsid; + IASIO *iorigasio; + + bool ok = false; + + CLSIDFromString(const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(qsCls.utf16())), &clsid); + if (CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, clsid, reinterpret_cast<void **>(&iorigasio)) == S_OK) { + SleepEx(10, false); + IASIOThiscallResolver iasio(iorigasio); + if (iasio.init(winId())) { + SleepEx(10, false); + ok = (iasio.controlPanel() == ASE_OK); + SleepEx(10, false); + } else { + SleepEx(10, false); + char err[255]; + iasio.getErrorMessage(err); + SleepEx(10, false); + QMessageBox::critical(this, tr("Mumble"), tr("ASIO Initialization failed: %1").arg(err), QMessageBox::Ok, QMessageBox::NoButton); + } + iasio.Release(); + } else { + QMessageBox::critical(this, tr("Mumble"), tr("Failed to instantiate ASIO driver"), QMessageBox::Ok, QMessageBox::NoButton); + } +} + +void ASIOConfig::on_Device_activated(int index) { + clearQuery(); +} + +void ASIOConfig::on_AddMic_clicked() { + int row = qlwUnused->currentRow(); + if (row < 0) + return; + qlwMic->addItem(qlwUnused->takeItem(row)); +} + +void ASIOConfig::on_RemMic_clicked() { + int row = qlwMic->currentRow(); + if (row < 0) + return; + qlwUnused->addItem(qlwMic->takeItem(row)); +} + +void ASIOConfig::on_AddSpeaker_clicked() { + int row = qlwUnused->currentRow(); + if (row < 0) + return; + qlwSpeaker->addItem(qlwUnused->takeItem(row)); +} + +void ASIOConfig::on_RemSpeaker_clicked() { + int row = qlwSpeaker->currentRow(); + if (row < 0) + return; + qlwUnused->addItem(qlwSpeaker->takeItem(row)); +} + +QString ASIOConfig::title() const { + return tr("ASIO"); +} + +QIcon ASIOConfig::icon() const { + return QIcon(":/icons/config_asio.png"); +} + +void ASIOConfig::accept() { + if (! bOk) + return; + + g.s.qsASIOclass = qcbDevice->itemData(qcbDevice->currentIndex()).toString(); + + QList<QVariant> list; + + for(int i=0;i<qlwMic->count();i++) { + QListWidgetItem *item = qlwMic->item(i); + list << item->data(Qt::UserRole); + } + + g.s.qlASIOmic = list; + + list.clear(); + + for(int i=0;i<qlwSpeaker->count();i++) { + QListWidgetItem *item = qlwSpeaker->item(i); + list << item->data(Qt::UserRole); + } + + g.s.qlASIOspeaker = list; +} + +void ASIOConfig::clearQuery() { + bOk = false; + qlName->setText(""); + qlBuffers->setText(""); + qlwMic->clear(); + qlwUnused->clear(); + qlwSpeaker->clear(); +} + + + +#include "firmumble.inc" + +ASIOInput::ASIOInput() { + QString qsCls = g.s.qsASIOclass; + CLSID clsid; + IASIO *iorigasio; + + iorigasio = false; + iasio = NULL; + abiInfo = NULL; + aciInfo = NULL; + + int i, idx; + + bHasSpeaker = true; + + // Allocate buffers + pdInputBuffer = new double[960]; + pdOutputBuffer = new double[iFrameSize]; + pdMicDelayLine = new double[MUMBLE_SFP_LENGTH]; + pdSpeakerDelayLine = new double[MUMBLE_SFP_LENGTH]; + + for(i=0;i<MUMBLE_SFP_LENGTH;i++) + pdMicDelayLine[i]=pdSpeakerDelayLine[i] = 0.0; + + // Sanity check things first. + + iNumMic=g.s.qlASIOmic.count(); + iNumSpeaker=g.s.qlASIOspeaker.count(); + + if ((iNumMic == 0) || (iNumSpeaker == 0)) { + QMessageBox::warning(NULL, tr("Mumble"), tr("You need to select at least one microphone and one speaker source to use ASIO. " + "If you just need microphone sampling, use DirectSound."), QMessageBox::Ok, QMessageBox::NoButton); + return; + } + + CLSIDFromString(const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(qsCls.utf16())), &clsid); + if (CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, clsid, reinterpret_cast<void **>(&iaOriginal)) == S_OK) { + iasio = new IASIOThiscallResolver(iaOriginal); + if (iasio->init(NULL)) { + ASIOSampleRate srate = 0.0; + iasio->getSampleRate(&srate); + if ((fabs(srate - 48000.0) < 1.0) || (iasio->setSampleRate(48000.0) == ASE_OK)) { + + abiInfo = new ASIOBufferInfo[iNumMic + iNumSpeaker]; + aciInfo = new ASIOChannelInfo[iNumMic + iNumSpeaker]; + idx = 0; + for(i=0;i<iNumMic;i++) { + abiInfo[idx].isInput = true; + abiInfo[idx].channelNum = g.s.qlASIOmic[i].toInt(); + + aciInfo[idx].channel = abiInfo[idx].channelNum; + aciInfo[idx].isInput = true; + iasio->getChannelInfo(&aciInfo[idx]); + SleepEx(10, false); + + idx++; + } + for(i=0;i<iNumSpeaker;i++) { + abiInfo[idx].isInput = true; + abiInfo[idx].channelNum = g.s.qlASIOspeaker[i].toInt(); + + aciInfo[idx].channel = abiInfo[idx].channelNum; + aciInfo[idx].isInput = true; + iasio->getChannelInfo(&aciInfo[idx]); + SleepEx(10, false); + + idx++; + } + + ASIOCallbacks asioCallbacks; + ZeroMemory(&asioCallbacks, sizeof(asioCallbacks)); + asioCallbacks.bufferSwitch = &bufferSwitch; + asioCallbacks.sampleRateDidChange = &sampleRateChanged; + asioCallbacks.asioMessage = &asioMessages; + asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo; + + if (iasio->createBuffers(abiInfo, idx, 960, &asioCallbacks) == ASE_OK) + return; + } + } + } + if (abiInfo) { + delete [] abiInfo; + abiInfo = NULL; + } + + if (aciInfo) { + delete [] aciInfo; + aciInfo = NULL; + } + + if (iasio) { + iaOriginal->Release(); + delete iasio; + + iasio = NULL; + iaOriginal = NULL; + } + + QMessageBox::critical(NULL, tr("Mumble"), tr("Opening selected ASIO device failed. No input will be done."), + QMessageBox::Ok, QMessageBox::NoButton); + +} + +ASIOInput::~ASIOInput() { + qwDone.wakeAll(); + wait(); + if (iasio) { + iasio->stop(); + iasio->disposeBuffers(); + iaOriginal->Release(); + delete iasio; + } + if (abiInfo) { + delete [] abiInfo; + abiInfo = NULL; + } + + delete [] pdInputBuffer; + delete [] pdOutputBuffer; + delete [] pdMicDelayLine; + delete [] pdSpeakerDelayLine; +} + +void ASIOInput::run() { + QMutex m; + m.lock(); + bRunning = true; + if (iasio) { + aiSelf = this; + iasio->start(); + qwDone.wait(&m); + } +} + +ASIOTime *ASIOInput::bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow) { + aiSelf->bufferReady(index); + return 0L; +} + +void ASIOInput::addBuffer(ASIOSampleType sampType, void *src, double *dst) { + switch (sampType) { + case ASIOSTInt16LSB: + { + short *buf=static_cast<short *>(src); + for(int i=0;i<960;i++) + dst[i]+=buf[i]; + } + break; + case ASIOSTInt32LSB: + { + int *buf=static_cast<int *>(src); + for(int i=0;i<960;i++) + dst[i]+=(buf[i] >> 16); + } + break; + } +} + +void ASIOInput::bufferReady(long buffindex) { + int c, i; + + // Microphone inputs + ZeroMemory(pdInputBuffer, sizeof(double) * 960); + for(c=0;c<iNumMic;c++) + addBuffer(aciInfo[c].type, abiInfo[c].buffers[buffindex], pdInputBuffer); + + double mul = 1.0 / (32768.0 * iNumMic); + + for(i=0;i<960;i++) { + pdInputBuffer[i] *= mul; + } + + decim(pdInputBuffer, pdOutputBuffer, pdMicDelayLine); + + for(i=0;i<320;i++) + psMic[i] = static_cast<short>(pdOutputBuffer[i] * 32768.0); + + + // Speaker inputs + ZeroMemory(pdInputBuffer, sizeof(double) * 960); + for(c=0;c<iNumMic;c++) + addBuffer(aciInfo[iNumMic+c].type, abiInfo[iNumMic+c].buffers[buffindex], pdInputBuffer); + + mul = 1.0 / (32768.0 * iNumSpeaker); + + for(i=0;i<960;i++) { + pdInputBuffer[i] *= mul; + } + + decim(pdInputBuffer, pdOutputBuffer, pdSpeakerDelayLine); + + for(i=0;i<320;i++) + psSpeaker[i] = static_cast<short>(pdOutputBuffer[i] * 32768.0); + + encodeAudioFrame(); +} + +void ASIOInput::bufferSwitch(long index, ASIOBool processNow) +{ + ASIOTime timeInfo; + memset (&timeInfo, 0, sizeof (timeInfo)); + + if(aiSelf->iasio->getSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK) + timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid; + + bufferSwitchTimeInfo (&timeInfo, index, processNow); +} + +void ASIOInput::sampleRateChanged(ASIOSampleRate sRate) +{ + qFatal("ASIOInput: sampleRateChanged"); +} + +long ASIOInput::asioMessages(long selector, long value, void* message, double* opt) +{ + long ret = 0; + switch(selector) + { + case kAsioSelectorSupported: + if(value == kAsioResetRequest + || value == kAsioEngineVersion + || value == kAsioResyncRequest + || value == kAsioLatenciesChanged + || value == kAsioSupportsTimeInfo + || value == kAsioSupportsTimeCode + || value == kAsioSupportsInputMonitor) + ret = 1L; + break; + case kAsioResetRequest: + qFatal("ASIOInput: kAsioResetRequest"); + ret = 1L; + break; + case kAsioResyncRequest: + ret = 1L; + break; + case kAsioLatenciesChanged: + ret = 1L; + break; + case kAsioEngineVersion: + ret = 2L; + break; + case kAsioSupportsTimeInfo: + ret = 1; + break; + case kAsioSupportsTimeCode: + ret = 0; + break; + } + return ret; +} + +/**************************************************************************** +* +* The following subroutine (ASIOInput::decim()) was adopted from decim.c +* found at dspguru. +* Original copyright notice as follows: +* +* Name: decim.c +* +* Synopsis: Decimates a real or complex signal. +* +* Description: See decim.h. +* +* by Grant R. Griffin +* Provided by Iowegian's "dspGuru" service (http://www.dspguru.com). +* Copyright 2001, Iowegian International Corporation (http://www.iowegian.com) +* +* The Wide Open License (WOL) +* +* Permission to use, copy, modify, distribute and sell this software and its +* documentation for any purpose is hereby granted without fee, provided that +* the above copyright notice and this license appear in all source copies. +* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF +* ANY KIND. See http://www.dspguru.com/wol.htm for more information. +* +*****************************************************************************/ + + +#define factor_M 3 +#define H_size MUMBLE_SFP_LENGTH +#define p_H mumble_sfp + +void ASIOInput::decim(const double *p_inp, double *p_out, double *p_Z) { + int tap; + double sum; + int num_inp = 960; + + while (num_inp >= factor_M) { + /* shift Z delay line up to make room for next samples */ + for (tap = H_size - 1; tap >= factor_M; tap--) { + p_Z[tap] = p_Z[tap - factor_M]; + } + + /* copy next samples from input buffer to bottom of Z delay line */ + for (tap = factor_M - 1; tap >= 0; tap--) { + p_Z[tap] = *p_inp++; + } + num_inp -= factor_M; + + /* calculate FIR sum */ + sum = 0.0; + for (tap = 0; tap < H_size; tap++) { + sum += p_H[tap] * p_Z[tap]; + } + *p_out++ = sum; /* store sum and point to next output */ + } +} diff --git a/src/mumble/ASIOInput.h b/src/mumble/ASIOInput.h new file mode 100644 index 000000000..e0984ed27 --- /dev/null +++ b/src/mumble/ASIOInput.h @@ -0,0 +1,108 @@ +/* Copyright (C) 2005-2006, 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 _ASIOINPUT_H +#define _ASIOINPUT_H + +#include <windows.h> +#include <asiodrvr.h> +#include "AudioInput.h" +#include "ConfigDialog.h" + +typedef QPair<QString, QString> ASIODev; + +class ASIOConfig : public ConfigWidget { + Q_OBJECT + protected: + QList<ASIODev> qlDevs; + QComboBox *qcbDevice; + QLabel *qlName, *qlBuffers; + QListWidget *qlwMic, *qlwSpeaker, *qlwUnused; + bool bOk; + public: + ASIOConfig(QWidget *p = NULL); + virtual QString title() const; + virtual QIcon icon() const; + public slots: + void accept(); + void clearQuery(); + void on_Device_activated(int index); + void on_Query_clicked(); + void on_Config_clicked(); + void on_AddMic_clicked(); + void on_RemMic_clicked(); + void on_AddSpeaker_clicked(); + void on_RemSpeaker_clicked(); +}; + +#define IEEE754_64FLOAT 1 +#include "asio.h" + +class IASIOThiscallResolver; + +class ASIOInput : public AudioInput { + Q_OBJECT + protected: + IASIO *iaOriginal; + IASIOThiscallResolver *iasio; + + int iNumMic, iNumSpeaker; + ASIOBufferInfo *abiInfo; + ASIOChannelInfo *aciInfo; + + // ASIO Callbacks + static ASIOInput *aiSelf; + + static void sampleRateChanged(ASIOSampleRate sRate); + static long asioMessages(long selector, long value, void* message, double* opt); + static void bufferSwitch(long index, ASIOBool processNow); + static void addBuffer(long sampType, void *src, double *dst); + static ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow); + + void bufferReady(long index); + bool initializeDriver(); + + double *pdMicDelayLine; + double *pdSpeakerDelayLine; + double *pdInputBuffer; + double *pdOutputBuffer; + + QWaitCondition qwDone; + + void decim(const double *output, double *input, double *delayline); + public: + ASIOInput(); + ~ASIOInput(); + void run(); +}; + +#else +class DXAudioInput; +#endif diff --git a/src/mumble/About.cpp b/src/mumble/About.cpp new file mode 100644 index 000000000..f7c458afa --- /dev/null +++ b/src/mumble/About.cpp @@ -0,0 +1,96 @@ +/* Copyright (C) 2005-2006, 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 "About.h" +#include "Global.h" +#include "licenses.h" + +AboutDialog::AboutDialog(QWidget *p) : QDialog(p) { + QTabWidget *qtwTab = new QTabWidget(this); + QVBoxLayout *vblMain = new QVBoxLayout(this); + + QTextEdit *qteLicense=new QTextEdit(); + qteLicense->setReadOnly(true); + qteLicense->setPlainText(licenseMumble); + + QWidget *about=new QWidget(); + + QLabel *icon=new QLabel(about); + icon->setPixmap(qApp->windowIcon().pixmap(qApp->windowIcon().actualSize(QSize(64,64)))); + + QLabel *text=new QLabel(about); + text->setText(tr( + "<h3>Mumble v%1</h3>" + "<p>Copyright 2005-2006 Thorvald Natvig<br />slicer@users.sourceforge.net</p>" + "<p><b>A voice-chat utility for gamers</b></p>" + "<p><tt>http://mumble.sourceforge.net/</tt></p>" + ).arg(MUMBLE_RELEASE)); + QHBoxLayout *qhbl=new QHBoxLayout(); + qhbl->addWidget(icon); + qhbl->addWidget(text); + about->setLayout(qhbl); + + qtwTab->addTab(about, tr("&About Mumble")); + qtwTab->addTab(qteLicense, tr("&License")); + + QPushButton *okButton = new QPushButton(tr("OK")); + connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); + + vblMain->addWidget(qtwTab); + vblMain->addWidget(okButton); + + setLayout(vblMain); +} + +AboutSpeexDialog::AboutSpeexDialog(QWidget *p) : QDialog(p) { + char *verptr; + speex_lib_ctl(SPEEX_LIB_GET_VERSION_STRING, &verptr); + + QLabel *text=new QLabel(); + text->setText(tr( + "<h3>About Speex</h3>" + "<p><tt>http://www.speex.org/</tt></p>" + "<p>This program uses Speex version %1</p>" + "<p>Speex is used for echo cancellation, noise<br />" + "filtering, voice activity detection and speech<br />" + "compression.</p>" + ).arg(verptr)); + + QPushButton *okButton = new QPushButton(tr("OK")); + connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); + + QVBoxLayout *vblMain = new QVBoxLayout(); + QHBoxLayout *qhbl=new QHBoxLayout(); + qhbl->addWidget(text); + + vblMain->addLayout(qhbl); + vblMain->addWidget(okButton); + setLayout(vblMain); +} diff --git a/src/mumble/About.h b/src/mumble/About.h new file mode 100644 index 000000000..921bcd691 --- /dev/null +++ b/src/mumble/About.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2005-2006, 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 _ABOUT_H +#define _ABOUT_H + +class AboutDialog : public QDialog { + Q_OBJECT; + public: + AboutDialog(QWidget *parent); +}; + +class AboutSpeexDialog : public QDialog { + Q_OBJECT; + public: + AboutSpeexDialog(QWidget *parent); +}; + +#endif diff --git a/src/mumble/Audio.cpp b/src/mumble/Audio.cpp new file mode 100644 index 000000000..cdf902fdd --- /dev/null +++ b/src/mumble/Audio.cpp @@ -0,0 +1,30 @@ +/* Copyright (C) 2005-2006, 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. +*/ + diff --git a/src/mumble/Audio.h b/src/mumble/Audio.h new file mode 100644 index 000000000..1f5ce5ea8 --- /dev/null +++ b/src/mumble/Audio.h @@ -0,0 +1,37 @@ +/* Copyright (C) 2005-2006, 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 _AUDIO_H +#define _AUDIO_H + +#include "ConfigDialog.h" +#define SAMPLE_RATE 16000 + +#endif diff --git a/src/mumble/AudioConfigDialog.cpp b/src/mumble/AudioConfigDialog.cpp new file mode 100644 index 000000000..4667c0871 --- /dev/null +++ b/src/mumble/AudioConfigDialog.cpp @@ -0,0 +1,420 @@ +/* Copyright (C) 2005-2006, 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 "AudioInput.h" +#include "AudioOutput.h" +#include "AudioConfigDialog.h" +#include "Global.h" + +static ConfigWidget *AudioConfigDialogNew() { + return new AudioConfigDialog(); +} + +static ConfigRegistrar registrar(10, AudioConfigDialogNew); + +AudioConfigDialog::AudioConfigDialog(QWidget *p) : ConfigWidget(p) { + QGroupBox *qgbInterface, *qgbTransmit, *qgbCompress, *qgbJitter; + QLabel *l; + QVBoxLayout *v; + QGridLayout *grid; + QList<QString> keys; + QString key; + int i; + + qgbInterface=new QGroupBox(tr("Interfaces")); + qgbTransmit=new QGroupBox(tr("Transmission")); + qgbCompress=new QGroupBox(tr("Compression")); + qgbJitter=new QGroupBox(tr("Jitter Buffer")); + + grid = new QGridLayout(); + + qcbInput = new QComboBox(); + keys=AudioInputRegistrar::qmNew->keys(); + foreach(key, keys) { + qcbInput->addItem(key); + } + i=keys.indexOf(AudioInputRegistrar::current); + if (i >= 0) + qcbInput->setCurrentIndex(i); + + l = new QLabel(tr("Input")); + l->setBuddy(qcbInput); + + qcbInput->setToolTip(tr("Input method for audio")); + qcbInput->setWhatsThis(tr("<b>This is the input method to use for audio.</b><br />Most likely you want to use DirectSound.")); + + grid->addWidget(l, 0, 0); + grid->addWidget(qcbInput, 0, 1); + + qcbOutput = new QComboBox(); + keys=AudioOutputRegistrar::qmNew->keys(); + foreach(key, keys) { + qcbOutput->addItem(key); + } + i=keys.indexOf(AudioOutputRegistrar::current); + if (i >= 0) + qcbOutput->setCurrentIndex(i); + + l = new QLabel(tr("Output")); + l->setBuddy(qcbOutput); + + qcbOutput->setToolTip(tr("Output method for audio")); + qcbOutput->setWhatsThis(tr("<b>This is the output method to use for audio.</b><br />Most likely you want to use DirectSound.")); + + grid->addWidget(l, 1, 0); + grid->addWidget(qcbOutput, 1, 1); + + qgbInterface->setLayout(grid); + + grid = new QGridLayout(); + + qcbTransmit = new QComboBox(); + qcbTransmit->addItem(tr("Continuous"), Settings::Continous); + qcbTransmit->addItem(tr("Voice Activity"), Settings::VAD); + qcbTransmit->addItem(tr("Push To Talk"), Settings::PushToTalk); + qcbTransmit->setCurrentIndex(g.s.atTransmit); + l = new QLabel(tr("Transmit")); + l->setBuddy(qcbTransmit); + + qcbTransmit->setToolTip(tr("When to transmit your speech")); + qcbTransmit->setWhatsThis(tr("<b>This sets when speech should be transmitted.</b><br />" + "<i>Continuous</i> - All the time<br />" + "<i>Voice Activity</i> - When you are speaking clearly.<br />" + "<i>Push To Talk</i> - When you hold down the hotkey set under <i>Shortcuts</i>.")); + grid->addWidget(l, 0, 0); + grid->addWidget(qcbTransmit, 0, 1, 1, 2); + + qsTransmitHold = new QSlider(Qt::Horizontal); + qsTransmitHold->setRange(20, 250); + qsTransmitHold->setSingleStep(1); + qsTransmitHold->setPageStep(5); + qsTransmitHold->setValue(g.s.iVoiceHold); + qsTransmitHold->setObjectName("TransmitHold"); + + l = new QLabel(tr("Voice Hold")); + l->setBuddy(qsTransmitHold); + + qlTransmitHold=new QLabel(); + qlTransmitHold->setMinimumWidth(40); + on_TransmitHold_valueChanged(qsTransmitHold->value()); + + qsTransmitHold->setToolTip(tr("How long to keep transmitting after silence")); + qsTransmitHold->setWhatsThis(tr("<b>This selects how long after a perceived stop in speech transmission should continue.</b><br />" + "Set this higher if your voice breaks up when you speak (seen by a rapidly blinking voice icon next to your name).<br />" + "Only makes sense when used with Voice Activity transmission.")); + + grid->addWidget(l, 1, 0); + grid->addWidget(qsTransmitHold, 1, 1); + grid->addWidget(qlTransmitHold, 1, 2); + + qsFrames = new QSlider(Qt::Horizontal); + qsFrames->setRange(1, 4); + qsFrames->setSingleStep(1); + qsFrames->setPageStep(1); + qsFrames->setValue(g.s.iFramesPerPacket); + qsFrames->setObjectName("Frames"); + + l = new QLabel(tr("Audio per packet")); + l->setBuddy(qsFrames); + + qlFrames=new QLabel(); + qlFrames->setMinimumWidth(40); + qsQuality = NULL; + qlBitrate = NULL; + on_Frames_valueChanged(qsFrames->value()); + + qsFrames->setToolTip(tr("How many audio frames to send per packet")); + qsFrames->setWhatsThis(tr("<b>This selects how many audio frames should be put in one packet.</b><br />" + "Increasing this will increase the " + "latency of your voice, but will also reduce bandwidth requirements.")); + grid->addWidget(l, 2, 0); + grid->addWidget(qsFrames, 2, 1); + grid->addWidget(qlFrames, 2, 2); + + qcbTCP = new QCheckBox(tr("Use TCP mode")); + qcbTCP->setChecked(g.s.bTCPCompat); + qcbTCP->setToolTip(tr("Use TCP Compatibility mode")); + qcbTCP->setWhatsThis(tr("<b>Enable TCP Compatibility mode</b>.<br />" + "This will make Mumble use only TCP when communicating with the server. " + "This will increase overhead and cause lost packets to produce noticeable " + "pauses in communication, so this should only be used if you are unable to " + "use the default (which uses UDP for voice and TCP for control).")); + + qcbReconnect = new QCheckBox(tr("Automatic Reconnect")); + qcbReconnect->setChecked(g.s.bReconnect); + qcbReconnect->setToolTip(tr("Reconnect when disconnected")); + qcbReconnect->setWhatsThis(tr("<b>Reconnect when disconnected</b>.<br />" + "This will make Mumble try to automatically reconnect after 10 " + "seconds if your server " + "connection fails.")); + + QHBoxLayout *qhbl=new QHBoxLayout(); + qhbl->addWidget(qcbTCP); + qhbl->addWidget(qcbReconnect); + grid->addLayout(qhbl,3,1,1,2); + + qlBitrate = new QLabel(); + qlBitrate->setToolTip(tr("Maximum bandwidth used for sent audio")); + qlBitrate->setWhatsThis(tr("<b>This shows peak outgoing bandwidth used.</b><br />" + "This shows the peak amount of bandwidth sent out from your machine. Audio bitrate " + "is the maximum bitrate (as we use VBR) for the audio data alone. Position " + "is the bitrate used for positional information. Overhead is our framing and the " + "IP packet headers (IP and UDP is 90% of this overhead).")); + + l = new QLabel(tr("Outgoing Bitrate")); + l->setBuddy(qlBitrate); + + grid->addWidget(l, 4, 0); + grid->addWidget(qlBitrate, 4, 1, 1, 2); + + qgbTransmit->setLayout(grid); + + + grid = new QGridLayout(); + + qsQuality = new QSlider(Qt::Horizontal); + qsQuality->setRange(2, 10); + qsQuality->setSingleStep(1); + qsQuality->setPageStep(2); + qsQuality->setValue(g.s.iQuality); + qsQuality->setObjectName("Quality"); + + l = new QLabel(tr("Quality")); + l->setBuddy(qsQuality); + + qlQuality=new QLabel(); + qlQuality->setMinimumWidth(30); + on_Quality_valueChanged(qsQuality->value()); + + qsQuality->setToolTip(tr("Quality of compression (peak bandwidth)")); + qsQuality->setWhatsThis(tr("<b>This sets the quality of compression.</b><br />" + "This determines how much bandwidth Mumble is allowed to use for outgoing audio.")); + + grid->addWidget(l, 0, 0); + grid->addWidget(qsQuality, 0, 1); + grid->addWidget(qlQuality, 0, 2); + + qsComplexity = new QSlider(Qt::Horizontal); + qsComplexity->setRange(4, 10); + qsComplexity->setSingleStep(1); + qsComplexity->setPageStep(2); + qsComplexity->setValue(g.s.iComplexity); + qsComplexity->setObjectName("Complexity"); + + l = new QLabel(tr("Complexity")); + l->setBuddy(qsComplexity); + + qlComplexity=new QLabel(); + on_Complexity_valueChanged(qsComplexity->value()); + + qsComplexity->setToolTip(tr("Complexity of compression (CPU)")); + qsComplexity->setWhatsThis(tr("<b>This sets the complexity of compression.</b><br />" + "This determines how much CPU Mumble is allowed to use to increase transmitted " + "voice quality. Settings above 5 give only marginal gain.")); + + grid->addWidget(l, 1, 0); + grid->addWidget(qsComplexity, 1, 1); + grid->addWidget(qlComplexity, 1, 2); + + qsAmp = new QSlider(Qt::Horizontal); + qsAmp->setRange(0, 18000); + qsAmp->setSingleStep(500); + qsAmp->setPageStep(2000); + qsAmp->setValue(20000 - g.s.iMinLoudness); + qsAmp->setObjectName("Amp"); + + l = new QLabel(tr("Amp")); + l->setBuddy(qsAmp); + + qlAmp=new QLabel(); + on_Amp_valueChanged(qsAmp->value()); + + qsAmp->setToolTip(tr("Maximum amplification of input sound")); + qsAmp->setWhatsThis(tr( + "<b>Maximum amplification of input.</b><br />" + "Mumble normalizes the input volume before compressing, and this sets how much it's allowed to amplify.<br />" + "The actual level is continually updated based on your current speech pattern, but it will never go " + "above the level specified here.<br />" + "If the <i>Microphone loudness</i> 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.<br />" + "Ideally, set it so <i>Microphone Loudness * Amplification Factor >= 100</i>, even when you're speaking really soft.<br /><br />" + "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." + )); + + grid->addWidget(l, 2, 0); + grid->addWidget(qsAmp, 2, 1); + grid->addWidget(qlAmp, 2, 2); + + qgbCompress->setLayout(grid); + + + grid = new QGridLayout(); + + qsJitter = new QSlider(Qt::Horizontal); + qsJitter->setRange(2, 10); + qsJitter->setSingleStep(1); + qsJitter->setPageStep(5); + qsJitter->setValue(g.s.iJitterBufferSize); + qsJitter->setObjectName("Jitter"); + + l = new QLabel(tr("Default Jitter Buffer")); + l->setBuddy(qsJitter); + + qlJitter=new QLabel(); + qlJitter->setMinimumWidth(40); + on_Jitter_valueChanged(qsJitter->value()); + + qsJitter->setToolTip(tr("How long to prebuffer on first packet")); + qsJitter->setWhatsThis(tr("<b>This sets the default buffer size for the jitter buffer</b>.<br />" + "All incoming audio is buffered, and the jitter buffer continually tries to " + "push the buffer to the minimum sustainable by your network, so latency can " + "be as low as possible. This sets the default buffer size to use on the first " + "packet to arrive from a new speaker, or when a speaker using Voice Activity or " + "Push-To-Talk just started talking again. If the start of sentences you hear is " + "very jittery, increase this value.")); + grid->addWidget(l, 0, 0); + grid->addWidget(qsJitter, 0, 1); + grid->addWidget(qlJitter, 0, 2); + + qgbJitter->setLayout(grid); + + v = new QVBoxLayout; + v->addWidget(qgbInterface); + v->addWidget(qgbTransmit); + v->addWidget(qgbCompress); + v->addWidget(qgbJitter); + v->addStretch(1); + setLayout(v); + + QMetaObject::connectSlotsByName(this); +} + +QString AudioConfigDialog::title() const { + return tr("Basic Audio"); +} + +QIcon AudioConfigDialog::icon() const { + return QIcon(":/icons/config_basic.png"); +} + +void AudioConfigDialog::accept() { + g.s.iQuality = qsQuality->value(); + g.s.iComplexity = qsComplexity->value(); + g.s.iMinLoudness = 18000 - qsAmp->value() + 2000; + g.s.iVoiceHold = qsTransmitHold->value(); + g.s.iFramesPerPacket = qsFrames->value(); + g.s.bTCPCompat = qcbTCP->isChecked(); + g.s.bReconnect = qcbReconnect->isChecked(); + g.s.iJitterBufferSize = qsJitter->value(); + g.s.atTransmit = static_cast<Settings::AudioTransmit>(qcbTransmit->currentIndex()); + g.s.qsAudioInput = qcbInput->currentText(); + g.s.qsAudioOutput = qcbOutput->currentText(); +} + +void AudioConfigDialog::on_Frames_valueChanged(int v) { + qlFrames->setText(tr("%1 ms").arg(v*20)); + updateBitrate(); +} + +void AudioConfigDialog::on_TransmitHold_valueChanged(int v) { + double val = v * 20; + val = val / 1000.0; + qlTransmitHold->setText(tr("%1s").arg(val, 0, 'f', 2)); +} + +void AudioConfigDialog::on_Quality_valueChanged(int v) { + qlQuality->setText(QString::number(v)); + updateBitrate(); +} + +void AudioConfigDialog::on_Complexity_valueChanged(int v) { + qlComplexity->setText(QString::number(v)); +} + +void AudioConfigDialog::on_Amp_valueChanged(int v) { + v = 18000 - v + 2000; + double d = 20000.0/v; + qlAmp->setText(QString("%1").arg(d, 0, 'f', 2)); +} + +void AudioConfigDialog::on_Jitter_valueChanged(int v) { + qlJitter->setText(tr("%1 ms").arg(v*20)); +} + +void AudioConfigDialog::updateBitrate() { + if (! qsQuality || ! qsFrames || ! qlBitrate) + return; + int q = qsQuality->value(); + int p = qsFrames->value(); + + int audiorate, overhead, posrate; + float f = q; + void *es; + + es = speex_encoder_init(&speex_wb_mode); + speex_encoder_ctl(es,SPEEX_SET_VBR_QUALITY, &f); + speex_encoder_ctl(es,SPEEX_GET_BITRATE,&audiorate); + speex_encoder_destroy(es); + + // 50 packets, in bits, IP + UDP + type/id (Message header) + flags + seq + overhead = 50 * 8 * (20 + 8 + 3 + 1 + 2); + + // TCP is 12 more bytes than UDP + if (qcbTCP->isChecked()) + overhead += 50 * 8 * 12; + + // Individual packet sizes + if (p > 1) + overhead += p * 8 * 50; + + if (g.s.bTransmitPosition) + posrate = 12; + else + posrate = 0; + + posrate = posrate * 50 * 8; + + overhead = overhead / p; + posrate = posrate / p; + + int total = audiorate + overhead + posrate; + + QPalette pal; + + if ((total / 8 > g.iMaxBandwidth) && g.sId) { + pal.setColor(qlBitrate->foregroundRole(), Qt::red); + } + + qlBitrate->setPalette(pal); + + QString v = tr("%1kbit/s (Audio %2, Position %4, Overhead %3)").arg(total / 1000.0, 0, 'f', 1).arg(audiorate / 1000.0, 0, 'f', 1).arg(overhead / 1000.0, 0, 'f', 1).arg(posrate / 1000.0, 0, 'f', 1); + qlBitrate->setText(v); +} diff --git a/src/mumble/AudioConfigDialog.h b/src/mumble/AudioConfigDialog.h new file mode 100644 index 000000000..64a79c391 --- /dev/null +++ b/src/mumble/AudioConfigDialog.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2005-2006, 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 _AUDIOCONFIGDIALOG_H +#define _AUDIOCONFIGDIALOG_H + +#include "ConfigDialog.h" + +class AudioConfigDialog : public ConfigWidget { + Q_OBJECT + protected: + QComboBox *qcbInput, *qcbOutput; + QComboBox *qcbTransmit; + QSlider *qsTransmitHold, *qsFrames; + QLabel *qlTransmitHold, *qlFrames; + QSlider *qsQuality, *qsComplexity, *qsAmp; + QLabel *qlQuality, *qlComplexity, *qlAmp; + QSlider *qsJitter; + QCheckBox *qcbTCP, *qcbReconnect; + QLabel *qlJitter; + QLabel *qlBitrate; + public: + AudioConfigDialog(QWidget *p = NULL); + virtual QString title() const; + virtual QIcon icon() const; + public slots: + void accept(); + void updateBitrate(); + void on_TransmitHold_valueChanged(int v); + void on_Frames_valueChanged(int v); + void on_Quality_valueChanged(int v); + void on_Complexity_valueChanged(int v); + void on_Amp_valueChanged(int v); + void on_Jitter_valueChanged(int v); +}; + +#else +class AudioConfigDialog; +#endif diff --git a/src/mumble/AudioInput.cpp b/src/mumble/AudioInput.cpp new file mode 100644 index 000000000..ceff494fc --- /dev/null +++ b/src/mumble/AudioInput.cpp @@ -0,0 +1,392 @@ +/* Copyright (C) 2005-2006, 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 "AudioInput.h" +#include "ServerHandler.h" +#include "MainWindow.h" +#include "Player.h" +#include "Plugins.h" +#include "Message.h" +#include "Global.h" + +// Remember that we cannot use static member classes that are not pointers, as the constructor +// for AudioInputRegistrar() might be called before they are initialized, as the constructor +// is called from global initialization. +// Hence, we allocate upon first call. + +QMap<QString, AudioInputRegistrarNew> *AudioInputRegistrar::qmNew; +QString AudioInputRegistrar::current = QString(); + +AudioInputRegistrar::AudioInputRegistrar(QString name, AudioInputRegistrarNew n) { + if (! qmNew) + qmNew = new QMap<QString, AudioInputRegistrarNew>(); + qmNew->insert(name,n); +} + +AudioInputPtr AudioInputRegistrar::newFromChoice(QString choice) { + QSettings qs; + if (!choice.isEmpty() && qmNew->contains(choice)) { + qs.setValue("AudioInputDevice", choice); + current = choice; + return AudioInputPtr(qmNew->value(current)()); + } + choice = qs.value("AudioInputDevice").toString(); + if (qmNew->contains(choice)) { + current = choice; + return AudioInputPtr(qmNew->value(choice)()); + } + + // Try a sensible default. For example, ASIO is NOT a sensible default, but it's + // pretty early in the sorted map. + + if (qmNew->contains("DirectSound")) { + current = "DirectSound"; + return AudioInputPtr(qmNew->value(current)()); + } + + QMapIterator<QString, AudioInputRegistrarNew> i(*qmNew); + if (i.hasNext()) { + i.next(); + current = i.key(); + return AudioInputPtr(i.value()()); + } + return AudioInputPtr(); +} + + +AudioInput::AudioInput() +{ + speex_bits_init(&sbBits); + esEncState=speex_encoder_init(&speex_wb_mode); + speex_encoder_ctl(esEncState,SPEEX_GET_FRAME_SIZE,&iFrameSize); + + iByteSize=iFrameSize * 2; + + iFrameCounter = 0; + iSilentFrames = 0; + iHoldFrames = 0; + + int iarg=1; + speex_encoder_ctl(esEncState,SPEEX_SET_VBR, &iarg); + + iarg = 0; + + speex_encoder_ctl(esEncState,SPEEX_SET_VAD, &iarg); + speex_encoder_ctl(esEncState,SPEEX_SET_DTX, &iarg); + + bResetProcessor = true; + + sppPreprocess = NULL; + sesEcho = NULL; + + psMic = new short[iFrameSize]; + psSpeaker = new short[iFrameSize]; + psClean = new short[iFrameSize]; + pfY = new int[iFrameSize+1]; + + bHasSpeaker = false; + + iBitrate = 0; + dPeakMic = dPeakSignal = dPeakSpeaker = 0.0; + + bRunning = false; +} + +AudioInput::~AudioInput() +{ + bRunning = false; + wait(); + speex_bits_destroy(&sbBits); + speex_encoder_destroy(esEncState); + + if (sppPreprocess) + speex_preprocess_state_destroy(sppPreprocess); + if (sesEcho) + speex_echo_state_destroy(sesEcho); + + delete [] psMic; + delete [] psSpeaker; + delete [] psClean; + delete [] pfY; +} + +int AudioInput::getMaxBandwidth() { + int audiorate; + + void *es; + float f = g.s.iQuality; + es = speex_encoder_init(&speex_wb_mode); + speex_encoder_ctl(es,SPEEX_SET_VBR_QUALITY, &f); + speex_encoder_ctl(es,SPEEX_GET_BITRATE,&audiorate); + speex_encoder_destroy(es); + + audiorate /= 400/g.s.iFramesPerPacket; + + // Overhead + audiorate += 20 + 8 + 3 + 2; + + // Subframe lengths + if (g.s.iFramesPerPacket > 1) + audiorate += g.s.iFramesPerPacket; + + if (g.s.bTransmitPosition) + audiorate += 12; + + audiorate = (audiorate * 50) / g.s.iFramesPerPacket; + + return audiorate; +} + +void AudioInput::encodeAudioFrame() { + int iArg; + float fArg; + int iLen; + Player *p=Player::get(g.sId); + short max; + int i; + + short *psSource; + + iFrameCounter++; + + if (! bRunning) { + return; + } + +// #define ECHOTEST +#ifdef ECHOTEST + + static double framhist[10]; + + for(int i=0;i<9;i++) + framhist[i]=framhist[i+1]; + framhist[9]=1+(int) (310.0*rand()/(RAND_MAX+1.0)); + + // Sine wave test + for(i=0;i<iFrameSize;i++) { + psMic[i] += (sin((framhist[0] * M_PI * i) / (iFrameSize * 1.0)) * 4096.0); + psSpeaker[i] = (sin((framhist[4] * M_PI * i) / (iFrameSize * 1.0)) * 8192.0); +// psSpeaker[i] = psMic[i] * 2; +// qWarning("%d %d", i, psMic[i]); + } +#endif + + + max=1; + for(i=0;i<iFrameSize;i++) + if (abs(psMic[i]) > max) + max=abs(psMic[i]); + dPeakMic=20.0*log10((max * 1.0L) / 32768.0L); + + if (bHasSpeaker) { + max=1; + for(i=0;i<iFrameSize;i++) + if (abs(psSpeaker[i]) > max) + max=abs(psSpeaker[i]); + dPeakSpeaker=20.0*log10((max * 1.0L) / 32768.0L); + } else { + dPeakSpeaker = 0.0; + } + + if (bResetProcessor) { + if (sppPreprocess) + speex_preprocess_state_destroy(sppPreprocess); + + sppPreprocess = speex_preprocess_state_init(iFrameSize, SAMPLE_RATE); + + iArg = 1; + speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_VAD, &iArg); + speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_DENOISE, &iArg); + speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_AGC, &iArg); + speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_DEREVERB, &iArg); + + fArg = 20000; + speex_preprocess_ctl(sppPreprocess, SPEEX_PREPROCESS_SET_AGC_LEVEL, &fArg); + + if (bHasSpeaker) { + if (sesEcho) + speex_echo_state_destroy(sesEcho); + sesEcho = speex_echo_state_init(iFrameSize, iFrameSize*20); + iArg = SAMPLE_RATE; + speex_echo_ctl(sesEcho, SPEEX_SET_SAMPLING_RATE, &iArg); + qWarning("AudioInput: ECHO CANCELLER ACTIVE"); + } + + bResetProcessor = false; + +/* + SpeexEchoState *st = sesEcho; + if (st && ! st->adapted) { + + for(i=0;i<st->window_size*st->M;i++) + st->W[i] = 0.0; + + int frame = st->M - 4 - 1; + for(i=0;i<st->window_size/2;i++) { + st->W[frame * st->window_size + 2 * i + 1] = 0.5; + } + + if (st->adapted) + for(i=0;i<st->window_size;i++) + printf("%3d %f\n", i, st->W[frame * st->window_size + i]); + } +*/ + + } + + + fArg = g.s.iQuality; + speex_encoder_ctl(esEncState,SPEEX_SET_VBR_QUALITY, &fArg); + + iArg = g.s.iComplexity; + speex_encoder_ctl(esEncState,SPEEX_SET_COMPLEXITY, &iArg); + + int iIsSpeech; + + if (bHasSpeaker) { + speex_echo_cancel(sesEcho, psMic, psSpeaker, psClean, pfY); + iIsSpeech=speex_preprocess(sppPreprocess, psClean, pfY); + psSource = psClean; + } else { + iIsSpeech=speex_preprocess(sppPreprocess, psMic, NULL); + psSource = psMic; + } + + max=1; + for(i=0;i<iFrameSize;i++) + if (abs(psSource[i]) > max) + max=abs(psSource[i]); + dPeakSignal=20.0*log10((max * 1.0L) / 32768.0L); + + + // The default is a bit short, increase it + if (! iIsSpeech) { + iHoldFrames++; + if (iHoldFrames < g.s.iVoiceHold) + iIsSpeech=1; + } else { + iHoldFrames = 0; + } +/* + CloneSpeexPreprocessState *pps=reinterpret_cast<CloneSpeexPreprocessState *>(sppPreprocess); + if (pps->loudness2 < g.s.iMinLoudness) + pps->loudness2 = g.s.iMinLoudness; + if (isnan(pps->loudness2)) { + qWarning("AudioInput: loudness is nan"); + bResetProcessor = true; + } +*/ + if (g.s.atTransmit == Settings::Continous) + iIsSpeech = 1; + else if (g.s.atTransmit == Settings::PushToTalk) + iIsSpeech = 0; + + iIsSpeech = iIsSpeech || (g.iPushToTalk > 0) || g.bAltSpeak; + + if (g.s.bMute || (p && p->bMute) || g.bPushToMute) { + iIsSpeech = 0; + } + + if (iIsSpeech) + iSilentFrames = 0; + else { + iSilentFrames++; + if (iSilentFrames > 200) + iFrameCounter = 0; + } + + if (p) + p->setTalking(iIsSpeech, g.bAltSpeak); + + if (! iIsSpeech && ! bPreviousVoice) { + iBitrate = 0; + return; + } + + bPreviousVoice = iIsSpeech; + + if (! iIsSpeech) { + memset(psMic, 0, iByteSize); + } + + speex_bits_reset(&sbBits); + speex_encode_int(esEncState, psSource, &sbBits); + speex_encoder_ctl(esEncState, SPEEX_GET_BITRATE, &iBitrate); + speex_bits_pack(&sbBits, (iIsSpeech) ? 1 : 0, 1); + speex_bits_pack(&sbBits, (g.bAltSpeak)? 1 : 0, 1); + + if (g.s.bTransmitPosition && g.p && ! g.bCenterPosition && (qlFrames.count() == 0)) { + g.p->fetch(); + if (g.p->bValid) { + QByteArray q; + QDataStream ds(&q, QIODevice::WriteOnly); + ds << g.p->fPosition[0]; + ds << g.p->fPosition[1]; + ds << g.p->fPosition[2]; + const unsigned char *d=reinterpret_cast<const unsigned char*>(q.data()); + for(i=0;i<q.size();i++) { + speex_bits_pack(&sbBits, d[i], 8); + } + } + } + + + iLen=speex_bits_nbytes(&sbBits); + QByteArray qbaPacket(iLen, 0); + speex_bits_write(&sbBits, qbaPacket.data(), iLen); + + qlFrames << qbaPacket; + + flushCheck(); +} + +void AudioInput::flushCheck() { + if (qlFrames.count() == 0) + return; + if ((qlFrames.count() < g.s.iFramesPerPacket) && bPreviousVoice) + return; + + if (qlFrames.count() == 1) { + MessageSpeex msPacket; + msPacket.qbaSpeexPacket = qlFrames[0]; + msPacket.iSeq = iFrameCounter; + msPacket.ucFlags = g.bAltSpeak ? 1 : 0; + if (g.sh) + g.sh->sendMessage(&msPacket); + } else { + MessageMultiSpeex mmsPacket; + mmsPacket.qlFrames = qlFrames; + mmsPacket.iSeq = iFrameCounter - qlFrames.count() + 1; + mmsPacket.ucFlags = g.bAltSpeak ? 1 : 0; + if (g.sh) + g.sh->sendMessage(&mmsPacket); + } + qlFrames.clear(); +} diff --git a/src/mumble/AudioInput.h b/src/mumble/AudioInput.h new file mode 100644 index 000000000..677a3bfc3 --- /dev/null +++ b/src/mumble/AudioInput.h @@ -0,0 +1,99 @@ +/* Copyright (C) 2005-2006, 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 _AUDIOINPUT_H +#define _AUDIOINPUT_H + +#include "Audio.h" + +class AudioInput; +typedef boost::shared_ptr<AudioInput> AudioInputPtr; + +typedef AudioInput *(*AudioInputRegistrarNew)(); + +class AudioInputRegistrar { + public: + static QMap<QString, AudioInputRegistrarNew> *qmNew; + static QString current; + AudioInputRegistrar(QString name, AudioInputRegistrarNew n); + static AudioInputPtr newFromChoice(QString choice = QString()); +}; + +class AudioInput : public QThread { + friend class AudioNoiseWidget; + friend class AudioEchoWidget; + friend class AudioStats; + friend class FMODSystem; + Q_OBJECT + protected: + int iFrameSize; + int iByteSize; + + SpeexBits sbBits; + SpeexPreprocessState *sppPreprocess; + SpeexEchoState *sesEcho; + void *esEncState; + + short *psMic; + short *psSpeaker; + short *psClean; + int *pfY; + + QList<QByteArray> qlFrames; + + void encodeAudioFrame(); + + bool bRunning; + bool bPreviousVoice; + bool bHasSpeaker; + + int iFrameCounter; + int iSilentFrames; + int iHoldFrames; + + void flushCheck(); + public: + bool bResetProcessor; + + int iBitrate; + double dPeakMic, dPeakSpeaker, dPeakSignal; + + bool isTransmitting() { return bPreviousVoice; }; + + int getMaxBandwidth(); + + AudioInput(); + ~AudioInput(); + void run() = 0; +}; + +#else +class AudioInput; +#endif diff --git a/src/mumble/AudioOutput.cpp b/src/mumble/AudioOutput.cpp new file mode 100644 index 000000000..b84da29f6 --- /dev/null +++ b/src/mumble/AudioOutput.cpp @@ -0,0 +1,226 @@ +/* Copyright (C) 2005-2006, 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 "AudioOutput.h" +#include "Player.h" +#include "Global.h" + +// Remember that we cannot use static member classes that are not pointers, as the constructor +// for AudioOutputRegistrar() might be called before they are initialized, as the constructor +// is called from global initialization. +// Hence, we allocate upon first call. + +QMap<QString, AudioOutputRegistrarNew> *AudioOutputRegistrar::qmNew; +QString AudioOutputRegistrar::current = QString(); + +AudioOutputRegistrar::AudioOutputRegistrar(QString name, AudioOutputRegistrarNew n) { + if (! qmNew) + qmNew = new QMap<QString, AudioOutputRegistrarNew>(); + qmNew->insert(name,n); +} + +AudioOutputPtr AudioOutputRegistrar::newFromChoice(QString choice) { + QSettings qs; + if (!choice.isEmpty() && qmNew->contains(choice)) { + qs.setValue("AudioOutputDevice", choice); + current = choice; + return AudioOutputPtr(qmNew->value(choice)()); + } + choice = qs.value("AudioOutputDevice").toString(); + if (qmNew->contains(choice)) { + current = choice; + return AudioOutputPtr(qmNew->value(choice)()); + } + + // Try a sensible default. For example, ASIO is NOT a sensible default, but it's + // pretty early in the sorted map. + + if (qmNew->contains("DirectSound")) { + current = "DirectSound"; + return AudioOutputPtr(qmNew->value(current)()); + } + + + QMapIterator<QString, AudioOutputRegistrarNew> i(*qmNew); + if (i.hasNext()) { + i.next(); + current = i.key(); + return AudioOutputPtr(i.value()()); + } + return AudioOutputPtr(); +} + +AudioOutputPlayer::AudioOutputPlayer(AudioOutput *ao, Player *player) { + aoOutput = ao; + p = player; + + speex_bits_init(&sbBits); + dsDecState=speex_decoder_init(&speex_wb_mode); + + int iArg=1; + + speex_decoder_ctl(dsDecState, SPEEX_SET_ENH, &iArg); + speex_decoder_ctl(dsDecState, SPEEX_GET_FRAME_SIZE, &iFrameSize); + + iByteSize = iFrameSize * 2; + + iFrameCounter = 0; + + psBuffer = new short[iFrameSize]; + bSpeech = false; + + for(int i=0;i<3;i++) + fPos[i]=0.0; + for(int i=0;i<iFrameSize;i++) + psBuffer[i]=0; + iMissCount = 0; + + speex_jitter_init(&sjJitter, dsDecState, SAMPLE_RATE); +} + +AudioOutputPlayer::~AudioOutputPlayer() { + speex_decoder_destroy(dsDecState); + speex_jitter_destroy(&sjJitter); + delete [] psBuffer; +} + +void AudioOutputPlayer::addFrameToBuffer(QByteArray &qbaPacket, int iSeq) { + QMutexLocker lock(&qmJitter); + +/* if (! bSpeech) + sjJitter.buffer_size=g.s.iJitterBufferSize; +*/ + speex_jitter_put(&sjJitter, qbaPacket.data(), qbaPacket.size(), iSeq * iFrameSize); +} + +static QTime tt; + +bool AudioOutputPlayer::decodeNextFrame() { + int iTimestamp; + int iSpeech = 0; + int iAltSpeak = 0; + int left; + int i; + unsigned int v; + bool alive = true; + +// qWarning("Asking for packet at %08ld", tt.elapsed() / 20); + + { + QMutexLocker lock(&qmJitter); + speex_jitter_get(&sjJitter, psBuffer, &iTimestamp); + if (sjJitter.valid_bits) { + iSpeech = speex_bits_unpack_unsigned(&sjJitter.current_packet, 1); + iAltSpeak = speex_bits_unpack_unsigned(&sjJitter.current_packet, 1); + if (! iSpeech) { +#ifdef SPEEX_ANCIENT + sjJitter.reset_state = 1; +#else + jitter_buffer_reset(sjJitter.packets); +#endif + sjJitter.valid_bits = 0; + speex_decoder_ctl(dsDecState, SPEEX_RESET_STATE, NULL); + } + left = speex_bits_remaining(&sjJitter.current_packet) / 8; + if (left >= 12) { + QByteArray qba(left, 0); + unsigned char *ptr = reinterpret_cast<unsigned char *>(qba.data()); + for(i=0;i<left;i++) { + v = speex_bits_unpack_unsigned(&sjJitter.current_packet, 8); + ptr[i]=v; + } + QDataStream ds(qba); + ds >> fPos[0]; + ds >> fPos[1]; + ds >> fPos[2]; + iMissCount = 0; + } else { + iMissCount++; + if (iMissCount >= 4) + fPos[0] = fPos[1] = fPos[2] = 0.0; + } + } + if (! sjJitter.valid_bits) { + alive = false; + } + } + + bSpeech = iSpeech; + + p->setTalking(bSpeech, (iAltSpeak ? true : false)); + + if (g.s.bDeaf || ! bSpeech) + memset(psBuffer, 0, iByteSize); + + return alive; +} + +AudioOutput::AudioOutput() { + bRunning = false; +} + +AudioOutput::~AudioOutput() { + bRunning = false; + wait(); + wipe(); +} + +void AudioOutput::wipe() { + QWriteLocker locker(&qrwlOutputs); + + foreach(AudioOutputPlayer *aop, qmOutputs) { + delete aop; + } + qmOutputs.clear(); +} + +void AudioOutput::addFrameToBuffer(Player *player, QByteArray &qbaPacket, int iSeq) { + qrwlOutputs.lockForRead(); + AudioOutputPlayer *aop = qmOutputs.value(player); + if (! aop) { + qrwlOutputs.unlock(); + qrwlOutputs.lockForWrite(); + aop = getPlayer(player); + qmOutputs[player]=aop; + } + + aop->addFrameToBuffer(qbaPacket, iSeq); + + if (! isRunning()) + start(QThread::HighPriority); + qrwlOutputs.unlock(); +} + +void AudioOutput::removeBuffer(Player *player) { + QWriteLocker locker(&qrwlOutputs); + AudioOutputPlayer *aopOutput = qmOutputs.take(player); + if (aopOutput) + delete aopOutput; +} diff --git a/src/mumble/AudioOutput.h b/src/mumble/AudioOutput.h new file mode 100644 index 000000000..5d7e2009b --- /dev/null +++ b/src/mumble/AudioOutput.h @@ -0,0 +1,104 @@ +/* Copyright (C) 2005-2006, 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 _AUDIOOUTPUT_H +#define _AUDIOOUTPUT_H + +// AudioOutput depends on Player being valid. This means it's important +// to removeBuffer from here BEFORE MainWindow gets any PlayerLeft +// messages. Any decendant player should feel free to remove unused +// AudioOutputPlayer objects; it's better to recreate them than +// having them use resources while unused. + +#include "Audio.h" + +class AudioOutput; +class Player; + +typedef AudioOutput *(*AudioOutputRegistrarNew)(); +typedef boost::shared_ptr<AudioOutput> AudioOutputPtr; + +class AudioOutputRegistrar { + public: + static QMap<QString, AudioOutputRegistrarNew> *qmNew; + static QString current; + AudioOutputRegistrar(QString name, AudioOutputRegistrarNew n); + static AudioOutputPtr newFromChoice(QString choice = QString()); +}; + +class AudioOutputPlayer : public QObject { + friend class AudioOutput; + Q_OBJECT + protected: + Player *p; + + SpeexBits sbBits; + unsigned int iFrameSize; + unsigned int iByteSize; + int iFrameCounter; + QMutex qmJitter; + SpeexJitter sjJitter; + void *dsDecState; + AudioOutput *aoOutput; + + short *psBuffer; + bool bSpeech; + + int iMissCount; + float fPos[3]; + + bool decodeNextFrame(); + public: + void addFrameToBuffer(QByteArray &, int iSeq); + AudioOutputPlayer(AudioOutput *, Player *); + ~AudioOutputPlayer(); +}; + +class AudioOutput : public QThread { + friend class FMODSystem; + Q_OBJECT + protected: + bool bRunning; + QReadWriteLock qrwlOutputs; + QHash<Player *, AudioOutputPlayer *> qmOutputs; + virtual AudioOutputPlayer *getPlayer(Player *) = 0; + public: + void wipe(); + + AudioOutput(); + ~AudioOutput(); + void addFrameToBuffer(Player *, QByteArray &, int iSeq); + void removeBuffer(Player *); + void run() = 0; +}; + +#else +class AudioInput; +#endif diff --git a/src/mumble/AudioStats.cpp b/src/mumble/AudioStats.cpp new file mode 100644 index 000000000..17d54964e --- /dev/null +++ b/src/mumble/AudioStats.cpp @@ -0,0 +1,547 @@ +/* Copyright (C) 2005-2006, 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 "AudioStats.h" +#include "AudioInput.h" +#include "Global.h" + +/** Speex echo cancellation state; sync from mdf.c in Speex distribution. + */ + +typedef float spx_word16_t; +typedef float spx_word32_t; +typedef float spx_float_t; +typedef spx_word32_t spx_mem_t; +/** Speex echo cancellation state. */ +struct CloneSpeexEchoState { + int frame_size; /**< Number of samples processed each time */ + int window_size; + int M; + int cancel_count; + int adapted; + int saturated; + int screwed_up; + spx_int32_t sampling_rate; + spx_word16_t spec_average; + spx_word16_t beta0; + spx_word16_t beta_max; + spx_word32_t sum_adapt; + spx_word16_t leak_estimate; + + spx_word16_t *e; /* scratch */ + spx_word16_t *x; /* Far-end input buffer (2N) */ + spx_word16_t *X; /* Far-end buffer (M+1 frames) in frequency domain */ + spx_word16_t *input; /* scratch */ + spx_word16_t *y; /* scratch */ + spx_word16_t *last_y; + spx_word16_t *Y; /* scratch */ + spx_word16_t *E; + spx_word32_t *PHI; /* scratch */ + spx_word32_t *W; /* (Background) filter weights */ + spx_word16_t *foreground; /* Foreground filter weights */ + spx_word32_t Davg1; /* 1st recursive average of the residual power difference */ + spx_word32_t Davg2; /* 2nd recursive average of the residual power difference */ + spx_float_t Dvar1; /* Estimated variance of 1st estimator */ + spx_float_t Dvar2; /* Estimated variance of 2nd estimator */ + spx_word32_t *power; /* Power of the far-end signal */ + spx_float_t *power_1;/* Inverse power of far-end */ + spx_word16_t *wtmp; /* scratch */ + spx_word32_t *Rf; /* scratch */ + spx_word32_t *Yf; /* scratch */ + spx_word32_t *Xf; /* scratch */ + spx_word32_t *Eh; + spx_word32_t *Yh; + spx_float_t Pey; + spx_float_t Pyy; + spx_word16_t *window; + spx_word16_t *prop; + void *fft_table; + spx_word16_t memX, memD, memE; + spx_word16_t preemph; + spx_word16_t notch_radius; + spx_mem_t notch_mem[2]; +}; + +/** Speex pre-processor state. */ +struct CloneSpeexPreprocessState { + /* Basic info */ + int frame_size; /**< Number of samples processed each time */ + int ps_size; /**< Number of points in the power spectrum */ + int sampling_rate; /**< Sampling rate of the input/output */ + int nbands; + void *bank; + int denoise_enabled; + int vad_enabled; + int dereverb_enabled; + spx_word16_t reverb_decay; + spx_word16_t reverb_level; + spx_word16_t speech_prob_start; + spx_word16_t speech_prob_continue; + int noise_suppress; + int echo_suppress; + int echo_suppress_active; + CloneSpeexEchoState *echo_state; + spx_word16_t *frame; /**< Processing frame (2*ps_size) */ + spx_word16_t *ft; /**< Processing frame in freq domain (2*ps_size) */ + spx_word32_t *ps; /**< Current power spectrum */ + spx_word16_t *gain2; /**< Adjusted gains */ + spx_word16_t *gain_floor; /**< Minimum gain allowed */ + spx_word16_t *window; /**< Analysis/Synthesis window */ + spx_word32_t *noise; /**< Noise estimate */ + spx_word32_t *reverb_estimate; /**< Estimate of reverb energy */ + spx_word32_t *old_ps; /**< Power spectrum for last frame */ + spx_word16_t *gain; /**< Ephraim Malah gain */ + spx_word16_t *prior; /**< A-priori SNR */ + spx_word16_t *post; /**< A-posteriori SNR */ + + spx_word32_t *S; /**< Smoothed power spectrum */ + spx_word32_t *Smin; /**< See Cohen paper */ + spx_word32_t *Stmp; /**< See Cohen paper */ + int *update_prob; /**< Propability of speech presence for noise update */ + + spx_word16_t *zeta; /**< Smoothed a priori SNR */ + spx_word32_t *echo_noise; + spx_word32_t *residual_echo; + + spx_word16_t *inbuf; /**< Input buffer (overlapped analysis) */ + spx_word16_t *outbuf; /**< Output buffer (for overlap and add) */ + + int agc_enabled; + float agc_level; + float loudness_accum; + float *loudness_weight; /**< Perceptual loudness curve */ + float loudness; /**< Loudness estimate */ + float agc_gain; /**< Current AGC gain */ + int nb_loudness_adapt; /**< Number of frames used for loudness adaptation so far */ + float max_gain; /**< Maximum gain allowed */ + float max_increase_step; /**< Maximum increase in gain from one frame to another */ + float max_decrease_step; /**< Maximum decrease in gain from one frame to another */ + float prev_loudness; /**< Loudness of previous frame */ + float init_max; /**< Current gain limit during initialisation */ + int nb_adapt; /**< Number of frames used for adaptation so far */ + int was_speech; + int min_count; /**< Number of frames processed so far */ + void *fft_lookup; /**< Lookup table for the FFT */ +}; + +AudioEchoWidget::AudioEchoWidget(QWidget *p) : QGLWidget(p) { + setMinimumSize(100, 60); + mode = MODULUS; +} + +void AudioEchoWidget::initializeGL() { + glDisable(GL_LIGHTING); + + glClearColor(0,0,0,0); + glShadeModel(GL_SMOOTH); + + glEnable(GL_COLOR_MATERIAL); + glDisable(GL_CULL_FACE); +} + +void AudioEchoWidget::resizeGL(int w, int h) { + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +static inline void mapEchoToColor(float echo) { + bool neg = (echo < 0); + echo = fabs(echo); + + float a, b, c; + + if (echo > 1.0) { + echo = 1.0; + c = 0.5; + } else { + c = 0.0; + } + + if (echo < 0.5) { + a = echo * 2; + b = 0; + } else { + a = 1; + b = (echo - 0.5) * 2; + } + + if (neg) + glColor3f(a, b, c); + else + glColor3f(c, b, a); +} + +#define WGT(x,y) st->W[(y)*N + 2*(x)+1] + +void AudioEchoWidget::paintGL() { + AudioInputPtr ai = g.ai; + if (! ai || ! ai->sesEcho) + return; + + CloneSpeexEchoState *st = reinterpret_cast<CloneSpeexEchoState *>(ai->sesEcho); + + int N = st->window_size; + int n = N / 2; + int M = st->M; + + double xscale = 1.0 / n; + double yscale = 1.0 / M; + + + glBegin(GL_QUADS); + + for(int j = 0; j < M; j++) { + for(int i=0;i < n; i++) { + double xa = i * xscale; + double ya = j * yscale; + + double xb = xa + xscale; + double yb = ya + yscale; + + float real = 1.0; + float imag = 0.0; + if (i == 0) + real = st->W[j*N]; + else if (i == n-1) + real = st->W[j*N + 2*i]; + else { + real = st->W[j*N + 2*i - 1]; + imag = st->W[j*N + 2*i]; + } + + float v; + + switch (mode) { + case REAL: + v = real; + break; + case IMAGINARY: + v = imag; + break; + case MODULUS: + v = sqrt(real*real+imag*imag); + break; + case PHASE: + v = atan2(imag,real)/M_PI; + break; + } + + mapEchoToColor(v); + glVertex2f(xa, ya); + glVertex2f(xb, ya); + glVertex2f(xb, yb); + glVertex2f(xa, yb); + } + } + + glEnd(); +} + +AudioNoiseWidget::AudioNoiseWidget(QWidget *p) : QWidget(p) { + setMinimumSize(100,60); +} + +void AudioNoiseWidget::paintEvent(QPaintEvent *evt) { + QPainter paint(this); + QPalette pal; + + paint.fillRect(rect(), pal.color(QPalette::Background)); + + AudioInputPtr ai = g.ai; + if (ai.get() == NULL || ! ai->isRunning() || ! ai->sppPreprocess) + return; + + QPolygonF poly; + + CloneSpeexPreprocessState *st=reinterpret_cast<CloneSpeexPreprocessState *>(ai->sppPreprocess); + + qreal sx, sy; + + sx = (width() - 1.0f) / (st->ps_size * 1.0f); + sy = height() - 1; + + poly << QPointF(0.0f, height() - 1); +#ifdef SPEEX_ANCIENT_PP + float fftmul = 1.0 / (st->ps_size * 32768.0); +#else + float fftmul = 1.0 / (32768.0); +#endif + for(int i=0; i < st->ps_size; i++) { + qreal xp, yp; + xp = i * sx; + yp = sqrt(st->noise[i]) - 1; + yp = yp * fftmul; + yp = fmin(yp * 30.0, 1.0); + 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 < st->ps_size; i++) { + qreal xp, yp; + xp = i * sx; + yp = sqrt(st->ps[i]) - 1; + yp = yp * fftmul; + yp = fmin(yp * 30.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); + + setWindowTitle("Mumble"); + + QLabel *lab; + QGridLayout *l=new QGridLayout; + setLayout(l); + + lab = new QLabel(tr("Peak microphone level"), this); + l->addWidget(lab, 0, 0); + qlMicLevel = new QLabel(this); + l->addWidget(qlMicLevel, 0, 1); + + lab = new QLabel(tr("Peak speaker level"), this); + l->addWidget(lab, 1, 0); + qlSpeakerLevel = new QLabel(this); + l->addWidget(qlSpeakerLevel, 1, 1); + + lab = new QLabel(tr("Peak clean level"), this); + l->addWidget(lab, 2, 0); + qlSignalLevel = new QLabel(this); + l->addWidget(qlSignalLevel, 2, 1); + + lab = new QLabel(tr("Microphone loudness"), this); + l->addWidget(lab, 3, 0); + qlMicVolume = new QLabel(this); + l->addWidget(qlMicVolume, 3, 1); + + lab = new QLabel(tr("Mic Signal-To-Noise"), this); + l->addWidget(lab, 4, 0); + qlMicSNR = new QLabel(this); + l->addWidget(qlMicSNR, 4, 1); + + lab = new QLabel(tr("Speech Probability"), this); + l->addWidget(lab, 5, 0); + qlSpeechProb = new QLabel(this); + l->addWidget(qlSpeechProb, 5, 1); + + lab = new QLabel(tr("Audio bitrate"), this); + l->addWidget(lab, 6, 0); + qlBitrate = new QLabel(this); + l->addWidget(qlBitrate, 6, 1); + + anwNoise = new AudioNoiseWidget(this); + l->addWidget(anwNoise,7,0,1,2); + l->setRowStretch(7, 1); + + AudioInputPtr ai = g.ai; + if (ai && ai->sesEcho) { + + QHBoxLayout *hbox = new QHBoxLayout; + + QRadioButton *b; + + b = new QRadioButton("Real"); + qmEchoMode[b] = AudioEchoWidget::REAL; + connect(b, SIGNAL(clicked(bool)), this, SLOT(onEchoMode(bool))); + hbox->addWidget(b); + + b = new QRadioButton("Imaginary"); + qmEchoMode[b] = AudioEchoWidget::IMAGINARY; + connect(b, SIGNAL(clicked(bool)), this, SLOT(onEchoMode(bool))); + hbox->addWidget(b); + + b = new QRadioButton("Modulus"); + b->setChecked(true); + qmEchoMode[b] = AudioEchoWidget::MODULUS; + connect(b, SIGNAL(clicked(bool)), this, SLOT(onEchoMode(bool))); + hbox->addWidget(b); + + b = new QRadioButton("Phase"); + qmEchoMode[b] = AudioEchoWidget::PHASE; + connect(b, SIGNAL(clicked(bool)), this, SLOT(onEchoMode(bool))); + hbox->addWidget(b); + + l->addLayout(hbox, 8, 0, 1, 2); + + aewEcho = new AudioEchoWidget(this); + l->addWidget(aewEcho,9,0,1,2); + l->setRowStretch(9, 1); + } else { + aewEcho = NULL; + } + + qtTick = new QTimer(this); + qtTick->setObjectName("Tick"); + qtTick->start(50); + + qlMicLevel->setToolTip(tr("Peak power in last frame")); + qlMicLevel->setWhatsThis(tr("This shows the peak power in the last frame (20 ms), and is the same measurement " + "as you would usually find displayed as \"input power\". Please disregard this and " + "look at <b>Loudness</b> instead, which is much more steady and disregards outliers." + )); + qlSpeakerLevel->setToolTip(tr("Peak power in last frame")); + qlSpeakerLevel->setWhatsThis(tr("This shows the peak power in the last frame (20 ms) of the speakers. 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.")); + qlSpeakerLevel->setToolTip(tr("Peak power in last frame")); + qlSpeakerLevel->setWhatsThis(tr("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.<br />" + "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 players with echoes.")); + qlMicVolume->setToolTip(tr("How close the current input level is to ideal")); + qlMicVolume->setWhatsThis(tr("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.<br />" + "<b>Talk loud, as you would when you're upset over getting fragged by a noob.</b><br />" + "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")); + qlMicSNR->setToolTip(tr("Signal-To-Noise ratio from the microphone")); + qlMicSNR->setWhatsThis(tr("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.<br />If this value is below 1.0, there's more noise " + "than voice in the signal, and so quality is reduced.<br />There is no upper limit to this " + "value, but don't expect to see much above 40-50 without a sound studio.")); + qlSpeechProb->setToolTip(tr("Probability of speech")); + qlSpeechProb->setWhatsThis(tr("This is the probability that the last frame (20 ms) was speech and not environment noise.<br />" + "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'.<br />" + "If this is in bold font, it means Mumble is currently transmitting (if you're connected).")); + qlBitrate->setToolTip(tr("Bitrate of last frame")); + qlBitrate->setWhatsThis(tr("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. To adjust the peak bitrate, adjust <b>Compression Complexity</b> " + "in the Settings dialog.")); + + anwNoise->setToolTip(tr("Power spectrum of input signal and noise estimate")); + anwNoise->setWhatsThis(tr("This shows the power spectrum of the current input signal (red line) and the current noise estimate " + "(filled blue).<br />" + "All amplitudes are multiplied by 30 to show the interesting parts (how much more signal than noise " + "is present in each waveband).<br />" + "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.")); + + if (aewEcho) { + aewEcho->setToolTip(tr("Weights of the echo canceller")); + aewEcho->setWhatsThis(tr("This shows the weights of the echo canceller, with time increasing downwards and frequency increasing to the right.<br />" + "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.<br />" + "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. <br />" + "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.<br />" + "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." + )); + } + + QMetaObject::connectSlotsByName(this); + + bTalking = false; + + on_Tick_timeout(); +} + +void AudioStats::on_Tick_timeout() { + AudioInputPtr ai = g.ai; + + if (ai.get() == NULL || ! ai->isRunning() || ! ai->sppPreprocess) + return; + + CloneSpeexPreprocessState *st=reinterpret_cast<CloneSpeexPreprocessState *>(ai->sppPreprocess); + 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); + + float Zframe = 0; + int freq_start = static_cast<int>(300.0f*2.f*st->ps_size/st->sampling_rate); + int freq_end = static_cast<int>(2000.0f*2.f*st->ps_size/st->sampling_rate); + for (int i=freq_start;i<freq_end;i++) + Zframe += st->zeta[i]; + Zframe /= (freq_end-freq_start); + txt.sprintf("%06.3f",Zframe); + qlMicSNR->setText(txt); + + txt.sprintf("%03.0f%%",nTalking ? 100.0 : 0.0); + qlSpeechProb->setText(txt); + + txt.sprintf("%04.1f kbit/s",ai->iBitrate / 1000.0); + qlBitrate->setText(txt); + + if (nTalking != bTalking) { + bTalking = nTalking; + QFont f = qlSpeechProb->font(); + f.setBold(bTalking); + qlSpeechProb->setFont(f); + } + + anwNoise->update(); + if (aewEcho) + aewEcho->updateGL(); +} + +void AudioStats::onEchoMode(bool) { + if (aewEcho) + aewEcho->mode = qmEchoMode[sender()]; +} diff --git a/src/mumble/AudioStats.h b/src/mumble/AudioStats.h new file mode 100644 index 000000000..b1bde3c33 --- /dev/null +++ b/src/mumble/AudioStats.h @@ -0,0 +1,72 @@ +/* Copyright (C) 2005-2006, 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 + +class AudioEchoWidget : public QGLWidget { + Q_OBJECT + public: + enum Mode { REAL, IMAGINARY, MODULUS, PHASE }; + Mode mode; + AudioEchoWidget(QWidget *parent); + protected: + void initializeGL(); + void resizeGL(int w, int h); + void paintGL(); +}; + +class AudioNoiseWidget : public QWidget { + Q_OBJECT + public: + AudioNoiseWidget(QWidget *parent); + protected slots: + void paintEvent (QPaintEvent *event); +}; + +class AudioStats : public QDialog { + Q_OBJECT; + protected: + QLabel *qlMicLevel, *qlSpeakerLevel, *qlSignalLevel, *qlMicVolume, *qlMicSNR, *qlSpeechProb, *qlBitrate; + QMap<QObject *, AudioEchoWidget::Mode> qmEchoMode; + AudioNoiseWidget *anwNoise; + AudioEchoWidget *aewEcho; + QTimer *qtTick; + bool bTalking; + public: + AudioStats(QWidget *parent); + public slots: + void on_Tick_timeout(); + void onEchoMode(bool); +}; + +#else +class AudioStats; +#endif diff --git a/src/mumble/BanEditor.cpp b/src/mumble/BanEditor.cpp new file mode 100644 index 000000000..96cc5ddcd --- /dev/null +++ b/src/mumble/BanEditor.cpp @@ -0,0 +1,167 @@ +/* copyright (C) 2005-2006, 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 "BanEditor.h" +#include "ServerHandler.h" +#include "Channel.h" +#include "Global.h" + +BanEditor::BanEditor(const MessageServerBanList *msbl, QWidget *p) : QDialog(p) { + setWindowTitle(tr("Mumble - Edit Bans")); + + qlwBans = new QListWidget(); + qleIP = new QLineEdit("0.0.0.0"); + qsbMask = new QSpinBox(); + qpbAdd = new QPushButton(tr("&Add")); + qpbUpdate = new QPushButton(tr("&Update")); + qpbRemove = new QPushButton(tr("&Remove")); + + qlwBans->setObjectName("Bans"); + + qleIP->setObjectName("IP"); + QRegExp rx("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"); + QValidator *validator = new QRegExpValidator(rx, this); + qleIP->setValidator(validator); + + qsbMask->setObjectName("Mask"); + qsbMask->setRange(8,32); + qsbMask->setValue(24); + qpbAdd->setObjectName("Add"); + qpbUpdate->setObjectName("Update"); + qpbRemove->setObjectName("Remove"); + + QGridLayout *grid = new QGridLayout; + + grid->addWidget(qlwBans,0,0,2,1); + grid->addWidget(qleIP,0,1,1,2); + grid->addWidget(qsbMask,0,3); + grid->addWidget(qpbAdd,1,1); + grid->addWidget(qpbUpdate,1,2); + grid->addWidget(qpbRemove,1,3); + + QPushButton *okButton = new QPushButton(tr("&OK")); + connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); + okButton->setToolTip(tr("Accept changes")); + okButton->setWhatsThis(tr("This button will accept current groups/ACLs and send them to " + "the server. Note that if you mistakenly remove write permission " + "from yourself, the server will add it.")); + QPushButton *cancelButton = new QPushButton(tr("&Cancel")); + connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); + cancelButton->setToolTip(tr("Reject changes")); + cancelButton->setWhatsThis(tr("This button will cancels all changes and closes the dialog without " + "updating the ACLs or groups on the server.")); + + QHBoxLayout *buttons = new QHBoxLayout; + buttons->addStretch(1); + buttons->addWidget(okButton); + buttons->addWidget(cancelButton); + + QVBoxLayout *ml = new QVBoxLayout; + ml->addLayout(grid); + ml->addSpacing(12); + ml->addLayout(buttons); + setLayout(ml); + + qlBans = msbl->qlBans; + refreshBanList(); + + QMetaObject::connectSlotsByName(this); + addToolTipsWhatsThis(); +} + +void BanEditor::addToolTipsWhatsThis() { +} + +void BanEditor::accept() { + MessageServerBanList msbl; + + msbl.bQuery = false; + msbl.qlBans = qlBans; + + g.sh->sendMessage(&msbl); + QDialog::accept(); +} + +void BanEditor::on_Bans_currentRowChanged() { + QPair<quint32, int> ban; + int idx = qlwBans->currentRow(); + if (idx < 0) + return; + ban = qlBans[idx]; + + QHostAddress addr(ban.first); + qleIP->setText(addr.toString()); + qsbMask->setValue(ban.second); +} + +void BanEditor::on_Add_clicked() { + QPair<quint32, int> ban; + QHostAddress addr; + + bool ok; + + ok = addr.setAddress(qleIP->text()); + if (ok) { + quint32 base = addr.toIPv4Address(); + quint32 mask = qsbMask->value(); + quint32 bitmask = (1<<(32-mask))-1; + ban.first = base & ~bitmask; + ban.second = mask; + qlBans << ban; + refreshBanList(); + qlwBans->setCurrentRow(qlBans.indexOf(ban)); + } +} + +void BanEditor::on_Update_clicked() { + on_Remove_clicked(); + on_Add_clicked(); +} + +void BanEditor::on_Remove_clicked() { + int idx = qlwBans->currentRow(); + if (idx >= 0) + qlBans.removeAt(idx); + refreshBanList(); +} + +void BanEditor::refreshBanList() { + QPair<quint32, int> ban; + + qlwBans->clear(); + + qSort(qlBans); + + foreach(ban,qlBans) { + QHostAddress addr(ban.first); + QString qs = QString("%1/%2").arg(addr.toString()).arg(ban.second); + qlwBans->addItem(qs); + } +} diff --git a/src/mumble/BanEditor.h b/src/mumble/BanEditor.h new file mode 100644 index 000000000..63066321a --- /dev/null +++ b/src/mumble/BanEditor.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2005-2006, 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 _BANEDITOR_H +#define _BANEDITOR_H + +#include "Message.h" + +class BanEditor : public QDialog { + Q_OBJECT; + protected: + QList<QPair<quint32, int> > qlBans; + + QListWidget *qlwBans; + + QLineEdit *qleIP; + QSpinBox *qsbMask; + + QPushButton *qpbAdd; + QPushButton *qpbUpdate; + QPushButton *qpbRemove; + + void addToolTipsWhatsThis(); + public: + BanEditor(const MessageServerBanList *msbl, QWidget *p = NULL); + public slots: + void accept(); + public slots: + void on_Bans_currentRowChanged(); + void on_Add_clicked(); + void on_Update_clicked(); + void on_Remove_clicked(); + void refreshBanList(); +}; + +#endif diff --git a/src/mumble/ConfigDialog.cpp b/src/mumble/ConfigDialog.cpp new file mode 100644 index 000000000..cfe364ae7 --- /dev/null +++ b/src/mumble/ConfigDialog.cpp @@ -0,0 +1,156 @@ +/* Copyright (C) 2005-2006, 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 "Audio.h" + +#include "AudioInput.h" +#include "AudioOutput.h" +#include "Global.h" + +QMap<int, ConfigWidgetNew> *ConfigRegistrar::c_qmNew; + +ConfigRegistrar::ConfigRegistrar(int priority, ConfigWidgetNew n) { + if (! c_qmNew) + c_qmNew = new QMap<int, ConfigWidgetNew>(); + c_qmNew->insert(priority,n); +} + +ConfigWidget::ConfigWidget(QWidget *p) : QWidget(p) { +} + +QString ConfigWidget::title() const { + return QString("Missing Title"); +} + +QIcon ConfigWidget::icon() const { + return qApp->windowIcon(); +} + +ConfigDialog::ConfigDialog(QWidget *p) : QDialog(p) { + setWindowTitle(tr("Mumble Configuration")); + + qlwIcons = new QListWidget(); + qswPages = new QStackedWidget(); + + qlwIcons->setViewMode(QListView::IconMode); + qlwIcons->setIconSize(QSize(96, 84)); + qlwIcons->setMovement(QListView::Static); + qlwIcons->setMaximumWidth(128); + qlwIcons->setSpacing(12); + qlwIcons->setObjectName("Icons"); + + QPushButton *okButton = new QPushButton(tr("&OK")); + okButton->setDefault(true); + connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); + okButton->setToolTip(tr("Accept changes")); + okButton->setWhatsThis(tr("This button will accept current settings and return to the application.<br />" + "The settings will be stored to disk when you leave the application.")); + QPushButton *cancelButton = new QPushButton(tr("&Cancel")); + connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); + cancelButton->setToolTip(tr("Reject changes")); + cancelButton->setWhatsThis(tr("This button will reject all changes and return to the application.<br />" + "The settings will be reset to the previous positions.")); + QPushButton *applyButton = new QPushButton(tr("&Apply")); + connect(applyButton, SIGNAL(clicked()), this, SLOT(apply())); + applyButton->setToolTip(tr("Apply changes")); + applyButton->setWhatsThis(tr("This button will immediately apply all changes.")); + + ConfigWidgetNew cwn; + foreach(cwn, *ConfigRegistrar::c_qmNew) { + addPage(cwn()); + } + + QHBoxLayout *buttons = new QHBoxLayout; + buttons->addStretch(1); + buttons->addWidget(applyButton); + buttons->addWidget(okButton); + buttons->addWidget(cancelButton); + + QVBoxLayout *l = new QVBoxLayout; + l->addWidget(qswPages, 1); + l->addSpacing(12); + l->addLayout(buttons); + + + QHBoxLayout *top = new QHBoxLayout; + top->addWidget(qlwIcons); + top->addLayout(l); + + setLayout(top); + + qlwIcons->scrollTo(qlwIcons->currentIndex(), QAbstractItemView::PositionAtTop); + + QMetaObject::connectSlotsByName(this); + + qlwIcons->setCurrentRow(0); +} + +void ConfigDialog::addPage(ConfigWidget *cw) { + qswPages->addWidget(cw); + QListWidgetItem *i = new QListWidgetItem(qlwIcons); + i->setIcon(cw->icon()); + i->setText(cw->title()); + i->setTextAlignment(Qt::AlignHCenter); + i->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + + widgets << cw; +} + +void ConfigDialog::on_Icons_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +{ + if (!current) + current = previous; + + qswPages->setCurrentIndex(qlwIcons->row(current)); +} + +void ConfigDialog::apply() { + foreach(ConfigWidget *cw, widgets) + cw->accept(); + + boost::weak_ptr<AudioInput> wai(g.ai); + boost::weak_ptr<AudioOutput> wao(g.ao); + + g.ai.reset(); + g.ao.reset(); + + while(! wai.expired() || ! wao.expired()) { + // Where is QThread::yield() ? + } + + g.ai = AudioInputRegistrar::newFromChoice(g.s.qsAudioInput); + g.ai->start(QThread::HighestPriority); + g.ao = AudioOutputRegistrar::newFromChoice(g.s.qsAudioOutput); +} + +void ConfigDialog::accept() { + apply(); + QDialog::accept(); +} diff --git a/src/mumble/ConfigDialog.h b/src/mumble/ConfigDialog.h new file mode 100644 index 000000000..810cec6ad --- /dev/null +++ b/src/mumble/ConfigDialog.h @@ -0,0 +1,72 @@ +/* Copyright (C) 2005-2006, 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 _CONFIGDIALOG_H +#define _CONFIGDIALOG_H + +class ConfigWidget : public QWidget { + Q_OBJECT + public: + ConfigWidget(QWidget *p = NULL); + virtual QString title() const; + virtual QIcon icon() const; + public slots: + virtual void accept() = 0; +}; + +typedef ConfigWidget *(*ConfigWidgetNew)(); + +class ConfigRegistrar { + friend class ConfigDialog; + protected: + static QMap<int, ConfigWidgetNew> *c_qmNew; + public: + ConfigRegistrar(int priority, ConfigWidgetNew n); +}; + +class ConfigDialog : public QDialog { + Q_OBJECT; + protected: + QList<ConfigWidget *> widgets; + QListWidget *qlwIcons; + QStackedWidget *qswPages; + void addPage(ConfigWidget *aw); + public: + ConfigDialog(QWidget *p = NULL); + public slots: + void on_Icons_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + void apply(); + void accept(); +}; + +#else +class ConfigWidget; +class ConfigDialog; +#endif diff --git a/src/mumble/ConnectDialog.cpp b/src/mumble/ConnectDialog.cpp new file mode 100644 index 000000000..0cc53babd --- /dev/null +++ b/src/mumble/ConnectDialog.cpp @@ -0,0 +1,199 @@ +/* Copyright (C) 2005-2006, 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 "ConnectDialog.h" +#include "Global.h" + +ConnectDialog::ConnectDialog(QWidget *p) : QDialog(p) { + QGridLayout *l=new QGridLayout; + QVBoxLayout *vbl = new QVBoxLayout; + QHBoxLayout *vbh = new QHBoxLayout(); + vbl->addLayout(l); + vbl->addLayout(vbh); + + setLayout(vbl); + QLabel *lab; + + bDirty = false; + + qstmServers = new QSqlTableModel(this); + qstmServers->setTable("servers"); + qstmServers->setSort(1, Qt::AscendingOrder); + if (! qstmServers->select()) { + qWarning("ConnectDialog: Failed to reselect table"); + } + qstmServers->setEditStrategy(QSqlTableModel::OnManualSubmit); + + qlwServers=new QListView(); + l->addWidget(qlwServers,0,0,5,1); + qlwServers->setModel(qstmServers); + qlwServers->setModelColumn(1); + qlwServers->setEditTriggers(QAbstractItemView::NoEditTriggers); + qlwServers->setObjectName("List"); + + QItemSelectionModel *selectionModel = qlwServers->selectionModel(); + connect(selectionModel, SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(onSelection_Changed(const QModelIndex &, const QModelIndex &))); + + qleName=new QLineEdit(); + lab=new QLabel(tr("&Name")); + lab->setBuddy(qleName); + + l->addWidget(lab, 0, 1); + l->addWidget(qleName, 0, 2); + + qleServer=new QLineEdit(); + lab=new QLabel(tr("A&ddress")); + lab->setBuddy(qleServer); + + l->addWidget(lab, 1, 1); + l->addWidget(qleServer, 1, 2); + + qlePort=new QLineEdit("64738"); + qlePort->setValidator(new QIntValidator(1, 65535, qlePort)); + lab=new QLabel(tr("&Port")); + lab->setBuddy(qlePort); + + l->addWidget(lab, 2, 1); + l->addWidget(qlePort, 2, 2); + + qleUsername=new QLineEdit(); + lab=new QLabel(tr("&Username")); + lab->setBuddy(qleUsername); + + l->addWidget(lab, 3, 1); + l->addWidget(qleUsername, 3, 2); + + qlePassword=new QLineEdit(); + qlePassword->setEchoMode(QLineEdit::Password); + lab=new QLabel(tr("&Password")); + lab->setBuddy(qlePassword); + + l->addWidget(lab, 4, 1); + l->addWidget(qlePassword, 4,2); + + QPushButton *okButton = new QPushButton(tr("&Connect")); + okButton->setDefault(true); + connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); + + QPushButton *cancelButton = new QPushButton(tr("Cancel")); + connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); + + QPushButton *addButton = new QPushButton(tr("&Add")); + addButton->setObjectName("Add"); + + QPushButton *removeButton = new QPushButton(tr("&Remove")); + removeButton->setObjectName("Remove"); + + vbh->addWidget(okButton); + vbh->addWidget(cancelButton); + vbh->addWidget(addButton); + vbh->addWidget(removeButton); + + connect(qleName, SIGNAL(textEdited(const QString &)), this, SLOT(onDirty(const QString &))); + connect(qleServer, SIGNAL(textEdited(const QString &)), this, SLOT(onDirty(const QString &))); + connect(qleUsername, SIGNAL(textEdited(const QString &)), this, SLOT(onDirty(const QString &))); + connect(qlePassword, SIGNAL(textEdited(const QString &)), this, SLOT(onDirty(const QString &))); + connect(qlePort, SIGNAL(textEdited(const QString &)), this, SLOT(onDirty(const QString &))); + + connect(qlwServers, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(accept())); + + QMetaObject::connectSlotsByName(this); + + QModelIndex idx = qstmServers->index(g.qs->value("ServerRow",-1).toInt(),0); + if (idx.isValid()) + qlwServers->setCurrentIndex(idx); +} + +void ConnectDialog::accept() { + if (bDirty && qlwServers->currentIndex().isValid()) { + QSqlRecord r; + r = toRecord(); + qstmServers->setRecord(qlwServers->currentIndex().row(), r); + qstmServers->submitAll(); + } + + qsServer = qleServer->text(); + qsUsername = qleUsername->text(); + qsPassword = qlePassword->text(); + iPort = qlePort->text().toInt(); + + g.qs->setValue("ServerRow", qlwServers->currentIndex().row()); + QDialog::accept(); +} + +QSqlRecord ConnectDialog::toRecord() const +{ + QSqlRecord r = qstmServers->record(); + r.setValue("name", qleName->text()); + r.setValue("hostname", qleServer->text()); + r.setValue("username", qleUsername->text()); + r.setValue("password", qlePassword->text()); + r.setValue("port", qlePort->text().toInt()); + return r; +} + +void ConnectDialog::onSelection_Changed(const QModelIndex &index, const QModelIndex &previndex) +{ + QSqlRecord r; + + if (bDirty) { + bDirty = false; + r = toRecord(); + qstmServers->setRecord(previndex.row(), r); + qstmServers->submitAll(); + } + if (index.isValid()) { + r = qstmServers->record(index.row()); + qleName->setText(r.value("name").toString()); + qleServer->setText(r.value("hostname").toString()); + qleUsername->setText(r.value("username").toString()); + qlePassword->setText(r.value("password").toString()); + qlePort->setText(r.value("port").toString()); + bDirty = false; + } +} + +void ConnectDialog::on_Add_clicked() +{ + bDirty = false; + QSqlRecord r = toRecord(); + qstmServers->insertRecord(-1, r); + qstmServers->submitAll(); +} + +void ConnectDialog::on_Remove_clicked() +{ + qstmServers->removeRows(qlwServers->currentIndex().row(), 1, QModelIndex()); + qstmServers->submitAll(); +} + +void ConnectDialog::onDirty(const QString &) { + bDirty = true; +} diff --git a/src/mumble/ConnectDialog.h b/src/mumble/ConnectDialog.h new file mode 100644 index 000000000..277406328 --- /dev/null +++ b/src/mumble/ConnectDialog.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2005-2006, 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 _CONNECTDIALOG_H +#define _CONNECTDIALOG_H + +class ConnectDialog : public QDialog { + Q_OBJECT; + protected: + QSqlTableModel *qstmServers; + QListView *qlwServers; + QLineEdit *qleName, *qleServer, *qlePort, *qleUsername, *qlePassword; + QCheckBox *qcbUdp; + QModelIndex qmiDirty; + QSqlRecord toRecord() const; + bool bDirty; + public slots: + void accept(); + void on_Add_clicked(); + void on_Remove_clicked(); + void onSelection_Changed(const QModelIndex &n, const QModelIndex &p); + void onDirty(const QString &qs = QString()); + public: + QString qsServer, qsUsername, qsPassword; + int iPort; + ConnectDialog(QWidget *parent); +}; + +#else +class ConnectDialog; +#endif diff --git a/src/mumble/DXAudioInput.cpp b/src/mumble/DXAudioInput.cpp new file mode 100644 index 000000000..0cb5516c3 --- /dev/null +++ b/src/mumble/DXAudioInput.cpp @@ -0,0 +1,178 @@ +/* Copyright (C) 2005-2006, 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 "DXAudioInput.h" +#include "Global.h" + +#undef FAILED +#define FAILED(Status) (static_cast<HRESULT>(Status)<0) + +static AudioInput *DXAudioInputNew() { + return new DXAudioInput(); +} + +static AudioInputRegistrar airDX("DirectSound", DXAudioInputNew); + +#define NBUFFBLOCKS 50 + +DXAudioInput::DXAudioInput() { + HRESULT hr; + WAVEFORMATEX wfx; + DSCBUFFERDESC dscbd; + DSBPOSITIONNOTIFY aPosNotify[ NBUFFBLOCKS ]; + + hNotificationEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + + pDSCapture = NULL; + pDSCaptureBuffer = NULL; + pDSNotify = NULL; + + bOk = false; + + bool failed = false; + + ZeroMemory( &wfx, sizeof(wfx) ); + wfx.wFormatTag = WAVE_FORMAT_PCM; + + ZeroMemory( &dscbd, sizeof(dscbd) ); + dscbd.dwSize = sizeof(dscbd); + + dscbd.dwBufferBytes = dwBufferSize = iByteSize * NBUFFBLOCKS; + dscbd.lpwfxFormat = &wfx; + + wfx.nChannels = 1; + wfx.nSamplesPerSec = SAMPLE_RATE; + wfx.nBlockAlign = 2; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + wfx.wBitsPerSample = 16; + + for( int i = 0; i < NBUFFBLOCKS; i++ ) + { + aPosNotify[i].dwOffset = (iByteSize * (i+1)) -1; + aPosNotify[i].hEventNotify = hNotificationEvent; + } + + // Create IDirectSoundCapture using the preferred capture device + if (! g.s.qbaDXInput.isEmpty()) { + LPGUID lpguid = reinterpret_cast<LPGUID>(g.s.qbaDXInput.data()); + if( FAILED( hr = DirectSoundCaptureCreate8(lpguid, &pDSCapture, NULL))) { + failed = true; + } + } + + if (! pDSCapture && FAILED( hr = DirectSoundCaptureCreate8(&DSDEVID_DefaultVoiceCapture, &pDSCapture, NULL ) ) ) + qWarning("DXAudioInput: DirectSoundCaptureCreate"); + else if( FAILED( hr = pDSCapture->CreateCaptureBuffer( &dscbd, &pDSCaptureBuffer, NULL))) + qWarning("DXAudioInput: CreateCaptureBuffer"); + else if( FAILED( hr = pDSCaptureBuffer->QueryInterface( IID_IDirectSoundNotify, reinterpret_cast<VOID**>(&pDSNotify) ) ) ) + qWarning("DXAudioInput: QueryInterface (Notify)"); + else if( FAILED( hr = pDSNotify->SetNotificationPositions( NBUFFBLOCKS, aPosNotify ) ) ) + qWarning("DXAudioInput: SetNotificationPositions"); + else + bOk = true; + + + if (! bOk) { + QMessageBox::warning(NULL, tr("Mumble"), tr("Opening chosen DirectSound Input failed. No microphone capture will be done."), QMessageBox::Ok, QMessageBox::NoButton); + return; + } + + if (failed) + QMessageBox::warning(NULL, tr("Mumble"), tr("Opening chosen DirectSound Input failed. Default device will be used."), QMessageBox::Ok, QMessageBox::NoButton); + + qWarning("DXAudioInput: Initialized"); +} + +DXAudioInput::~DXAudioInput() { + bRunning = false; + wait(); + + if (pDSNotify) + pDSNotify->Release(); + if (pDSCaptureBuffer) + pDSCaptureBuffer->Release(); + if (pDSCapture) + pDSCapture->Release(); + + CloseHandle(hNotificationEvent); +} + +void DXAudioInput::run() { + HRESULT hr; + DWORD dwReadyBytes; + DWORD dwLastReadPos = 0; + DWORD dwReadPosition; + DWORD dwCapturePosition; + + LPVOID aptr1, aptr2; + DWORD nbytes1, nbytes2; + + if (! bOk) + return; + + if( FAILED( hr = pDSCaptureBuffer->Start( DSCBSTART_LOOPING ) ) ) + qFatal("DXAudioInput: Start"); + + bRunning = true; + + while (bRunning) { + + do { + if( FAILED( hr = pDSCaptureBuffer->GetCurrentPosition(&dwCapturePosition, &dwReadPosition ) ) ) + qFatal("DXAudioInput: GetCurrentPosition"); + if (dwReadPosition < dwLastReadPos) + dwReadyBytes = (dwBufferSize - dwLastReadPos) + dwReadPosition; + else + dwReadyBytes = dwReadPosition - dwLastReadPos; + + if (static_cast<int>(dwReadyBytes) < iByteSize) { + WaitForSingleObject(hNotificationEvent, INFINITE); + } + } while (static_cast<int>(dwReadyBytes) < iByteSize); + + if (FAILED( hr = pDSCaptureBuffer->Lock(dwLastReadPos, iByteSize, &aptr1, &nbytes1, &aptr2, &nbytes2, 0))) + qFatal("DXAudioInput: Lock from %ld (%d bytes)",dwLastReadPos, iByteSize); + + if (aptr1 && nbytes1) + CopyMemory(psMic, aptr1, nbytes1); + + if (aptr2 && nbytes2) + CopyMemory(psMic+nbytes1/2, aptr2, nbytes2); + + if (FAILED( hr = pDSCaptureBuffer->Unlock(aptr1, nbytes1, aptr2, nbytes2))) + qFatal("DXAudioInput: Unlock"); + + dwLastReadPos = (dwLastReadPos + iByteSize) % dwBufferSize; + + encodeAudioFrame(); + } + + pDSCaptureBuffer->Stop(); +} diff --git a/src/mumble/DXAudioInput.h b/src/mumble/DXAudioInput.h new file mode 100644 index 000000000..d99b4cb7c --- /dev/null +++ b/src/mumble/DXAudioInput.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2005-2006, 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 _DXAUDIOINPUT_H +#define _DXAUDIOINPUT_H + +#include "AudioInput.h" +#include <dsound.h> + +class DXAudioInput : public AudioInput { + Q_OBJECT + protected: + LPDIRECTSOUNDCAPTURE8 pDSCapture; + LPDIRECTSOUNDCAPTUREBUFFER pDSCaptureBuffer; + LPDIRECTSOUNDNOTIFY8 pDSNotify; + + HANDLE hNotificationEvent; + DWORD dwBufferSize; + bool bOk; + public: + DXAudioInput(); + ~DXAudioInput(); + void run(); +}; + +#else +class DXAudioInput; +#endif diff --git a/src/mumble/DXAudioOutput.cpp b/src/mumble/DXAudioOutput.cpp new file mode 100644 index 000000000..8ee6b9a99 --- /dev/null +++ b/src/mumble/DXAudioOutput.cpp @@ -0,0 +1,458 @@ +/* Copyright (C) 2005-2006, 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 "DXAudioOutput.h" +#include "MainWindow.h" +#include "Plugins.h" +#include "Player.h" +#include "Global.h" + +#undef FAILED +#define FAILED(Status) (static_cast<HRESULT>(Status)<0) + +// #define MY_DEFERRED DS3D_DEFERRED +#define MY_DEFERRED DS3D_IMMEDIATE + + +#define NBLOCKS 50 +#define MAX(a,b) ( (a) > (b) ? (a) : (b) ) +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) + +static AudioOutput *DXAudioOutputNew() { + return new DXAudioOutput(); +} + +static AudioOutputRegistrar aorDX("DirectSound", DXAudioOutputNew); + +DXAudioOutputPlayer::DXAudioOutputPlayer(DXAudioOutput *ao, Player *player) : AudioOutputPlayer(ao, player) { + bPlaying = false; + dxAudio = static_cast<DXAudioOutput *>(aoOutput); + + pDSBOutput = NULL; + pDSNotify = NULL; + pDS3dBuffer = NULL; + + hNotificationEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); +} + +void DXAudioOutputPlayer::setupAudioDevice() { + DSBUFFERDESC dsbd; + WAVEFORMATEX wfx; + HRESULT hr; + + ZeroMemory( &wfx, sizeof(wfx) ); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 1; + wfx.nBlockAlign = 2; + wfx.nSamplesPerSec = SAMPLE_RATE; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + wfx.wBitsPerSample = 16; + + ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); + dsbd.dwSize = sizeof(DSBUFFERDESC); + dsbd.dwFlags = DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2; + dsbd.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY; + if (dxAudio->p3DListener) + dsbd.dwFlags |= DSBCAPS_CTRL3D; + dsbd.dwBufferBytes = iFrameSize * 2 * NBLOCKS; + dsbd.lpwfxFormat = &wfx; + if (dxAudio->p3DListener) { + switch (g.s.a3dModel) { + case Settings::None: + case Settings::Panning: + dsbd.guid3DAlgorithm = DS3DALG_NO_VIRTUALIZATION; + break; + case Settings::Light: + dsbd.guid3DAlgorithm = DS3DALG_HRTF_LIGHT; + break; + case Settings::Full: + dsbd.guid3DAlgorithm = DS3DALG_HRTF_FULL; + break; + } + } + + // Create the DirectSound buffer + if( FAILED( hr = dxAudio->pDS->CreateSoundBuffer( &dsbd, &pDSBOutput, NULL ) ) ) + qFatal("DXAudioOutputPlayer: CreateSoundBuffer (Secondary): 0x%08lx", hr); + + DSBPOSITIONNOTIFY aPosNotify[NBLOCKS]; + + for(int i=0;i<NBLOCKS;i++) { + aPosNotify[i].dwOffset = iFrameSize * 2 * i; + aPosNotify[i].hEventNotify = hNotificationEvent; + } + + if( FAILED( hr = pDSBOutput->QueryInterface( IID_IDirectSoundNotify, reinterpret_cast<VOID**>(&pDSNotify) ) ) ) + qFatal("DXAudioOutputPlayer: QueryInterface (Notify)"); + + if( FAILED( hr = pDSNotify->SetNotificationPositions( NBLOCKS, aPosNotify ) ) ) + qFatal("DXAudioOutputPlayer: SetNotificationPositions"); + + if (dxAudio->p3DListener) { + if (FAILED(pDSBOutput->QueryInterface(IID_IDirectSound3DBuffer8, reinterpret_cast<void **>(&pDS3dBuffer)))) + qFatal("DXAudioOutputPlayer: QueryInterface (DirectSound3DBuffer)"); + + pDS3dBuffer->SetMinDistance(g.s.fDXMinDistance, MY_DEFERRED); + pDS3dBuffer->SetMaxDistance(g.s.fDXMaxDistance, MY_DEFERRED); + } + + qWarning("DXAudioOutputPlayer: %s: New %dHz output buffer of %ld bytes", qPrintable(p->qsName), SAMPLE_RATE, dsbd.dwBufferBytes); +} + +DXAudioOutputPlayer::~DXAudioOutputPlayer() { + qWarning("DXAudioOutputPlayer: %s: Removed", qPrintable(p->qsName)); + if (pDS3dBuffer) + pDS3dBuffer->Release(); + if (pDSNotify) + pDSNotify->Release(); + if (pDSBOutput) { + pDSBOutput->Stop(); + pDSBOutput->Release(); + } + CloseHandle(hNotificationEvent); +} + +bool DXAudioOutputPlayer::playFrames() { + int playblock; + int nowriteblock; + DWORD dwPlayPosition, dwWritePosition; + HRESULT hr; + + LPVOID aptr1, aptr2; + DWORD nbytes1, nbytes2; + + bool alive = true; + + DWORD dwApply = MY_DEFERRED; + + if (! pDSBOutput) { + setupAudioDevice(); + + if (FAILED( hr = pDSBOutput->Lock(0, 0, &aptr1, &nbytes1, &aptr2, &nbytes2, DSBLOCK_ENTIREBUFFER))) + qFatal("DXAudioOutputPlayer: Initial Lock"); + + dwBufferSize = nbytes1 + nbytes2; + if (aptr1) + ZeroMemory(aptr1, nbytes1); + if (aptr2) + ZeroMemory(aptr2, nbytes2); + + if (FAILED( hr = pDSBOutput->Unlock(aptr1, nbytes1, aptr2, nbytes2))) + qFatal("DXAudioOutputPlayer: Initial Unlock"); + + dwApply = DS3D_IMMEDIATE; + + dwLastWritePos = 0; + dwLastPlayPos = 0; + dwTotalPlayPos = 0; + + iLastwriteblock = (NBLOCKS - 1 + g.s.iDXOutputDelay) % NBLOCKS; + } + + if( FAILED( hr = pDSBOutput->GetCurrentPosition(&dwPlayPosition, &dwWritePosition ) ) ) + qFatal("DXAudioOutputPlayer: GetCurrentPosition"); + + playblock = dwWritePosition / iByteSize; + nowriteblock = (playblock + g.s.iDXOutputDelay + 1) % NBLOCKS; + + for(int block=(iLastwriteblock + 1) % NBLOCKS;alive && (block!=nowriteblock);block=(block + 1) % NBLOCKS) { + + // Apparantly, even high end cards can sometimes move the play cursor BACKWARDS in 3D mode. + // If that happens, let's just say we're in synch. + + bool broken = false; + for(int i=0;i<10;i++) + if ((nowriteblock + i)%NBLOCKS == iLastwriteblock) + broken = true; + + if (broken) { + qWarning("DXAudioOutputPlayer: Playbackwards"); + iLastwriteblock = (nowriteblock + NBLOCKS - 1) % NBLOCKS; + break; + } + + iLastwriteblock = block; + + alive = decodeNextFrame(); +// qWarning("Block %02d/%02d nowrite %02d, last %02d (Pos %08d / %08d, Del %d)", block, NBLOCKS, nowriteblock, iLastwriteblock, dwPlayPosition, dwWritePosition,g.s.iDXOutputDelay); + if (! alive) { + iMissingFrames++; + // Give 5 seconds grace before killing off buffer, as it seems continously creating and destroying them + // taxes cheap soundcards more then it should. + if (iMissingFrames > 250) { + pDSBOutput->Stop(); + bPlaying = false; + return false; + } + } else { + iMissingFrames = 0; + } + + if (pDS3dBuffer) { + bool center = g.bCenterPosition; + DWORD mode; + + pDS3dBuffer->GetMode(&mode); + if (! center) { + if ((fabs(fPos[0]) < 0.1) && (fabs(fPos[1]) < 0.1) && (fabs(fPos[2]) < 0.1)) + center = true; + else if (! g.p->bValid) + center = true; + } + if (center) { + if (mode != DS3DMODE_DISABLE) + pDS3dBuffer->SetMode(DS3DMODE_DISABLE, dwApply); + } else { + if (mode != DS3DMODE_NORMAL) + pDS3dBuffer->SetMode(DS3DMODE_NORMAL, dwApply); + pDS3dBuffer->SetPosition(fPos[0], fPos[1], fPos[2], dwApply); + } + } + + if (FAILED( hr = pDSBOutput->Lock(block * iByteSize, iByteSize, &aptr1, &nbytes1, &aptr2, &nbytes2, 0))) + qFatal("DXAudioOutput: Lock block %d (%d bytes)",block, iByteSize); + if (aptr1 && nbytes1) + CopyMemory(aptr1, psBuffer, MIN(iByteSize, nbytes1)); + if (aptr2 && nbytes2) + CopyMemory(aptr2, psBuffer+(nbytes1/2), MIN(iByteSize-nbytes1, nbytes2)); + if (FAILED( hr = pDSBOutput->Unlock(aptr1, nbytes1, aptr2, nbytes2))) + qFatal("DXAudioOutput: Unlock"); + + // If we get another while we're working, we're already taking care of it. + ResetEvent(hNotificationEvent); + + if( FAILED( hr = pDSBOutput->GetCurrentPosition(&dwPlayPosition, &dwWritePosition ) ) ) + qFatal("DXAudioOutputPlayer: GetCurrentPosition"); + + playblock = dwWritePosition / iByteSize; + nowriteblock = (playblock + g.s.iDXOutputDelay + 1) % NBLOCKS; + } + + + if (! bPlaying) { + if (FAILED( hr = pDSBOutput->Play(0, 0, DSBPLAY_LOOPING))) + qFatal("DXAUdioOutputPlayer: Play"); + bPlaying = true; + } + + return true; +} + + +DXAudioOutput::DXAudioOutput() { + HRESULT hr; + DSBUFFERDESC dsbdesc; + WAVEFORMATEX wfx; + WAVEFORMATEX wfxSet; + + pDS = NULL; + + bool failed = false; + + bOk = false; + + ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) ); + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER; + if (g.s.a3dModel != Settings::None) + dsbdesc.dwFlags |= DSBCAPS_CTRL3D; + + ZeroMemory( &wfxSet, sizeof(wfxSet) ); + wfxSet.wFormatTag = WAVE_FORMAT_PCM; + + ZeroMemory( &wfx, sizeof(wfx) ); + wfx.wFormatTag = WAVE_FORMAT_PCM; + + wfx.nChannels = 1; + wfx.nSamplesPerSec = SAMPLE_RATE; + wfx.nBlockAlign = 2; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + wfx.wBitsPerSample = 16; + + pDS = NULL; + pDSBPrimary = NULL; + p3DListener = NULL; + + hNotificationEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + + if (! g.s.qbaDXOutput.isEmpty()) { + LPGUID lpguid = reinterpret_cast<LPGUID>(g.s.qbaDXOutput.data()); + if( FAILED( hr = DirectSoundCreate8(lpguid, &pDS, NULL))) { + failed = true; + } + } + + if (! pDS && FAILED( hr = DirectSoundCreate8( &DSDEVID_DefaultVoicePlayback, &pDS, NULL ) ) ) + qWarning("DXAudioOutput: DirectSoundCreate"); + else if( FAILED( hr = pDS->SetCooperativeLevel( g.mw->winId(), DSSCL_PRIORITY ) ) ) + qWarning("DXAudioOutput: SetCooperativeLevel"); + else if( FAILED( hr = pDS->CreateSoundBuffer( &dsbdesc, &pDSBPrimary, NULL ) ) ) + qWarning("DXAudioOutput: CreateSoundBuffer (Primary) : 0x%08lx", hr); + else if( FAILED( hr = pDSBPrimary->SetFormat( &wfx ) ) ) + qFatal("DXAudioOutput: SetFormat"); + else if( FAILED( hr = pDSBPrimary->GetFormat( &wfxSet, sizeof(wfxSet), NULL ) ) ) + qFatal("DXAudioOutput: GetFormat"); + else if (g.s.a3dModel != Settings::None) { + if (FAILED(hr = pDSBPrimary->QueryInterface(IID_IDirectSound3DListener8, reinterpret_cast<void **>(&p3DListener)))) { + qWarning("DXAudioOutput: QueryInterface (DirectSound3DListener8): 0x%08lx",hr); + } else { + p3DListener->SetRolloffFactor(g.s.fDXRollOff, MY_DEFERRED); + p3DListener->SetDopplerFactor(DS3D_MINDOPPLERFACTOR, MY_DEFERRED); + p3DListener->CommitDeferredSettings(); + bOk = true; + } + } else { + bOk = true; + } + + if (! bOk) { + QMessageBox::warning(NULL, tr("Mumble"), tr("Opening chosen DirectSound Output failed. No audio will be heard."), QMessageBox::Ok, QMessageBox::NoButton); + return; + } + + if (failed) + QMessageBox::warning(NULL, tr("Mumble"), tr("Opening chosen DirectSound Output failed. Default device will be used."), QMessageBox::Ok, QMessageBox::NoButton); + + qWarning("DXAudioOutput: Primary buffer of %ld Hz, %d channels, %d bits",wfxSet.nSamplesPerSec,wfxSet.nChannels,wfxSet.wBitsPerSample); + if (p3DListener) + qWarning("DXAudioOutput: 3D mode active"); + + bRunning = true; +} + +DXAudioOutput::~DXAudioOutput() { + bRunning = false; + wipe(); + SetEvent(hNotificationEvent); + wait(); + + if (p3DListener) + p3DListener->Release(); + if (pDSBPrimary) + pDSBPrimary->Release(); + if (pDS) + pDS->Release(); + CloseHandle(hNotificationEvent); +} + +AudioOutputPlayer *DXAudioOutput::getPlayer(Player *player) { + DXAudioOutputPlayer *daopPlayer = new DXAudioOutputPlayer(this, player); + SetEvent(daopPlayer->hNotificationEvent); + return daopPlayer; +} + +void DXAudioOutput::updateListener() { + if (! p3DListener) + return; + + HRESULT hr; + DS3DLISTENER li; + Plugins *p = g.p; + li.dwSize=sizeof(li); + if (p->bValid && ! g.bCenterPosition && p3DListener) { + // Only set this if we need to. If centerposition is on, or we don't have valid data, + // the 3d mode for the buffers will be disabled, so don't bother with updates. + p3DListener->SetPosition(p->fPosition[0], p->fPosition[1], p->fPosition[2], MY_DEFERRED); + p3DListener->SetOrientation(p->fFront[0], p->fFront[1], p->fFront[2], + p->fTop[0], p->fTop[1], p->fTop[2], MY_DEFERRED); + } + if (FAILED(hr =p3DListener->CommitDeferredSettings())) + qWarning("DXAudioOutputPlayer: CommitDeferrredSettings failed 0x%08lx", hr); + +/* + float a[3], b[3]; + p3DListener->GetOrientation((D3DVECTOR *) a, (D3DVECTOR *) b); + qWarning("%f %f %f -- %f %f %f", a[0], a[1], a[2], b[0], b[1], b[2]); +*/ +} + + +void DXAudioOutput::run() { + DXAudioOutputPlayer *dxaop; + AudioOutputPlayer *aop; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + DWORD count = 0; + DWORD hit; + + LARGE_INTEGER ticksPerSecond; + LARGE_INTEGER ticksPerFrame; + LARGE_INTEGER ticksNow; + LARGE_INTEGER ticksNext = {QuadPart: 0L}; + + bool found; + bool alive; + + QueryPerformanceFrequency(&ticksPerSecond); + ticksPerFrame.QuadPart = ticksPerSecond.QuadPart / 50; + + while (bRunning) { + // Ok, so I thought about it.. + // We might optimize this by having a QHash<hEvent, AOP *>, but.. + // .. most of the time, there will be one or maybe two AOPs active. + // The overhead of updating the cache is likely to outstrip the + // benefit. + + count = 0; + + qrwlOutputs.lockForRead(); + foreach(aop, qmOutputs) { + dxaop=static_cast<DXAudioOutputPlayer *>(aop); + handles[count++] = dxaop->hNotificationEvent; + } + handles[count++] = hNotificationEvent; + qrwlOutputs.unlock(); + + hit = WaitForMultipleObjects(count, handles, false, 20); + + found = false; + + QueryPerformanceCounter(&ticksNow); + if (ticksNow.QuadPart > ticksNext.QuadPart) { + ticksNext.QuadPart = ticksNow.QuadPart + ticksPerFrame.QuadPart; + updateListener(); + } + + qrwlOutputs.lockForRead(); + if (hit >= WAIT_OBJECT_0 && hit < WAIT_OBJECT_0 + count - 1) { + foreach(aop, qmOutputs) { + dxaop=static_cast<DXAudioOutputPlayer *>(aop); + if (handles[hit - WAIT_OBJECT_0] == dxaop->hNotificationEvent) { + found = true; + alive = dxaop->playFrames(); + break; + } + } + } + qrwlOutputs.unlock(); + + if (found && ! alive) + removeBuffer(dxaop->p); + } +} diff --git a/src/mumble/DXAudioOutput.h b/src/mumble/DXAudioOutput.h new file mode 100644 index 000000000..3a18eb39a --- /dev/null +++ b/src/mumble/DXAudioOutput.h @@ -0,0 +1,89 @@ +/* Copyright (C) 2005-2006, 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 _DXAUDIOOUTPUT_H +#define _DXAUDIOOUTPUT_H + +#include "AudioOutput.h" +#include <dsound.h> + +class DXAudioOutput; + +class DXAudioOutputPlayer : public AudioOutputPlayer { + friend class DXAudioOutput; + Q_OBJECT + protected: + LPDIRECTSOUNDBUFFER pDSBOutput; + LPDIRECTSOUNDNOTIFY8 pDSNotify; + HANDLE hNotificationEvent; + LPDIRECTSOUND3DBUFFER8 pDS3dBuffer; + + DWORD dwBufferSize; + DWORD dwLastWritePos; + DWORD dwLastPlayPos; + DWORD dwTotalPlayPos; + int iLastwriteblock; + + DXAudioOutput *dxAudio; + + bool bPlaying; + + int iMissingFrames; + void setupAudioDevice(); + public: + DXAudioOutputPlayer(DXAudioOutput *, Player *); + ~DXAudioOutputPlayer(); + bool playFrames(); +}; + + +class DXAudioOutput : public AudioOutput { + friend class DXAudioOutputPlayer; + Q_OBJECT + protected: + LPDIRECTSOUND8 pDS; + LPDIRECTSOUNDBUFFER pDSBPrimary; + LPDIRECTSOUND3DLISTENER8 p3DListener; + HANDLE hNotificationEvent; + + virtual AudioOutputPlayer *getPlayer(Player *); + void updateListener(); + + bool bRebuild; + bool bOk; + public: + DXAudioOutput(); + ~DXAudioOutput(); + void run(); +}; + +#else +class DXAudioOutput; +#endif diff --git a/src/mumble/DXConfigDialog.cpp b/src/mumble/DXConfigDialog.cpp new file mode 100644 index 000000000..3fcee385a --- /dev/null +++ b/src/mumble/DXConfigDialog.cpp @@ -0,0 +1,274 @@ +/* Copyright (C) 2005-2006, 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 "DXConfigDialog.h" +#include "Global.h" + +static ConfigWidget *DXConfigDialogNew() { + return new DXConfigDialog(); +} + +static ConfigRegistrar registrar(20, DXConfigDialogNew); + + +BOOL CALLBACK DSEnumProc(LPGUID lpGUID, const WCHAR* lpszDesc, + const WCHAR* lpszDrvName, void *ctx) +{ + if ( lpGUID ) + { + QList<dsDevice> *l =reinterpret_cast<QList<dsDevice> *>(ctx); + *l << dsDevice(QString::fromUtf16(reinterpret_cast<const ushort*>(lpszDesc)), *lpGUID); + } + + return(true); +} + +DXConfigDialog::DXConfigDialog(QWidget *p) : ConfigWidget(p) { + QGroupBox *qgbDevices, *qgbOutput, *qgb3D; + QGridLayout *grid; + QVBoxLayout *v; + dsDevice dev; + QLabel *l; + QByteArray a; + + qlInput << dsDevice(tr("Default DirectSound Voice Input"), DSDEVID_DefaultVoiceCapture); + qlOutput << dsDevice(tr("Default DirectSound Voice Output"),DSDEVID_DefaultVoicePlayback); + + DirectSoundCaptureEnumerate(DSEnumProc, reinterpret_cast<void *>(&qlInput)); + DirectSoundEnumerate(DSEnumProc, reinterpret_cast<void *>(&qlOutput)); + + qcbInputDevice = new QComboBox(); + qcbOutputDevice = new QComboBox(); + + foreach(dev, qlInput) { + a = QByteArray(reinterpret_cast<const char *>(&dev.second), sizeof(GUID)); + qcbInputDevice->addItem(dev.first, a); + if (a == g.s.qbaDXInput) + qcbInputDevice->setCurrentIndex(qcbInputDevice->count() - 1); + } + + foreach(dev, qlOutput) { + a = QByteArray(reinterpret_cast<const char *>(&dev.second), sizeof(GUID)); + qcbOutputDevice->addItem(dev.first, a); + if (a == g.s.qbaDXOutput) + qcbOutputDevice->setCurrentIndex(qcbOutputDevice->count() - 1); + } + + qgbDevices=new QGroupBox(tr("Device selection")); + grid=new QGridLayout(); + + qcbInputDevice->setToolTip(tr("Device to use for microphone")); + qcbInputDevice->setWhatsThis(tr("This sets the input device to use, which is where you have connected the " + "microphone.")); + l = new QLabel(tr("Input")); + l->setBuddy(qcbInputDevice); + grid->addWidget(l, 0, 0); + grid->addWidget(qcbInputDevice, 0, 1); + + qcbOutputDevice->setToolTip(tr("Device to use for speakers/headphones")); + qcbOutputDevice->setWhatsThis(tr("This sets the output device to use, which is where you have connected your " + "speakers or your headset.")); + l = new QLabel(tr("Output")); + l->setBuddy(qcbOutputDevice); + grid->addWidget(l, 1, 0); + grid->addWidget(qcbOutputDevice, 1, 1); + + qgbDevices->setLayout(grid); + + qgbOutput=new QGroupBox(tr("Output Options")); + grid=new QGridLayout(); + + qsOutputDelay = new QSlider(Qt::Horizontal); + qsOutputDelay->setRange(1, 6); + qsOutputDelay->setSingleStep(1); + qsOutputDelay->setPageStep(2); + qsOutputDelay->setValue(g.s.iDXOutputDelay); + qsOutputDelay->setObjectName("OutputDelay"); + l = new QLabel(tr("Output Delay")); + l->setBuddy(qsOutputDelay); + qlOutputDelay=new QLabel(); + qlOutputDelay->setMinimumWidth(30); + on_OutputDelay_valueChanged(qsOutputDelay->value()); + qsOutputDelay->setToolTip(tr("Amount of data to buffer for DirectSound")); + qsOutputDelay->setWhatsThis(tr("This sets the amount of data to prebuffer in the DirectSound buffer. " + "Experiment with different values and set it to the lowest which doesn't " + "cause rapid jitter in the sound.")); + grid->addWidget(l, 0, 0); + grid->addWidget(qsOutputDelay, 0, 1); + grid->addWidget(qlOutputDelay, 0, 2); + + qgbOutput->setLayout(grid); + + + qgb3D=new QGroupBox(tr("Positional Audio")); + grid=new QGridLayout(); + + qcbMethod=new QComboBox(); + qcbMethod->addItem(tr("None"), Settings::None); + qcbMethod->addItem(tr("Panning"), Settings::Panning); + qcbMethod->addItem(tr("Light HRTF"), Settings::Light); + qcbMethod->addItem(tr("Full HRTF"), Settings::Full); + qcbMethod->setCurrentIndex(static_cast<int>(g.s.a3dModel)); + + qcbMethod->setToolTip(tr("3D Sound Algorithm")); + qcbMethod->setWhatsThis(tr("This sets what 3D Sound algorithm to use.<br />" + "<b>None</b> - Disable 3D Sound (least CPU).<br />" + "<b>Panning</b> - Just use stereo panning (some CPU).<br />" + "<b>Light/Full HRTF</b> - Head-Related Transfer Functions enabled. " + "This may use a small amount of CPU.<br />" + "Note that if you have a soundcard with <i>hardware</i> 3D processing, HRTF " + "processing will be done on the soundcard and will use practically no processing " + "power.")); + l = new QLabel(tr("Method")); + l->setBuddy(qcbMethod); + grid->addWidget(l, 0, 0); + grid->addWidget(qcbMethod, 0, 1, 1, 2); + + qsMinDistance = new QSlider(Qt::Horizontal); + qsMaxDistance = new QSlider(Qt::Horizontal); + qsRollOff = new QSlider(Qt::Horizontal); + qlIntensity = new QLabel(); + + qsMinDistance->setRange(10, 200); + qsMinDistance->setSingleStep(1); + qsMinDistance->setPageStep(10); + qsMinDistance->setValue(lround(g.s.fDXMinDistance * 10)); + qsMinDistance->setObjectName("MinDistance"); + l = new QLabel(tr("MinDistance")); + l->setBuddy(qsMinDistance); + qlMinDistance=new QLabel(); + qlMinDistance->setMinimumWidth(40); + on_MinDistance_valueChanged(qsMinDistance->value()); + qsMinDistance->setToolTip(tr("Minimum distance to player before sound decreases")); + qsMinDistance->setWhatsThis(tr("This sets the minimum distance for sound calculations. The volume of other players' " + "speech will not decrease until they are at least this far away from you.")); + grid->addWidget(l, 1, 0); + grid->addWidget(qsMinDistance, 1, 1); + grid->addWidget(qlMinDistance, 1, 2); + + qsMaxDistance->setRange(10, 1000); + qsMaxDistance->setSingleStep(1); + qsMaxDistance->setPageStep(10); + qsMaxDistance->setValue(lround(g.s.fDXMaxDistance * 10)); + qsMaxDistance->setObjectName("MaxDistance"); + l = new QLabel(tr("MaxDistance")); + l->setBuddy(qsMaxDistance); + qlMaxDistance=new QLabel(); + qlMaxDistance->setMinimumWidth(40); + on_MaxDistance_valueChanged(qsMaxDistance->value()); + qsMaxDistance->setToolTip(tr("Maximum distance, beyond which sound won't decrease")); + qsMaxDistance->setWhatsThis(tr("This sets the maximum distance for sound calculations. When farther away than this, " + "other players' sound volume will not decrease any more.")); + grid->addWidget(l, 2, 0); + grid->addWidget(qsMaxDistance, 2, 1); + grid->addWidget(qlMaxDistance, 2, 2); + + qsRollOff->setRange(0, 200); + qsRollOff->setSingleStep(1); + qsRollOff->setPageStep(10); + qsRollOff->setValue(lround(g.s.fDXRollOff * 100)); + qsRollOff->setObjectName("RollOff"); + l = new QLabel(tr("RollOff")); + l->setBuddy(qsRollOff); + qlRollOff=new QLabel(); + qlRollOff->setMinimumWidth(40); + on_RollOff_valueChanged(qsRollOff->value()); + qsRollOff->setToolTip(tr("Factor for sound volume decrease")); + qsRollOff->setWhatsThis(tr("How fast should sound volume drop when passing beyond the minimum distance. The normal (1.0) is that " + "sound volume halves each time the distance doubles. Increasing this value means sound volume " + "drops faster, while decreasing it means it drops slower.")); + grid->addWidget(l, 3, 0); + grid->addWidget(qsRollOff, 3, 1); + grid->addWidget(qlRollOff, 3, 2); + + grid->addWidget(qlIntensity, 4, 0, 1, 3); + + qgb3D->setLayout(grid); + + v = new QVBoxLayout(); + v->addWidget(qgbDevices); + v->addWidget(qgbOutput); + v->addWidget(qgb3D); + v->addStretch(1); + + setLayout(v); + QMetaObject::connectSlotsByName(this); +} + +QString DXConfigDialog::title() const { + return tr("DirectSound"); +} + +QIcon DXConfigDialog::icon() const { + return QIcon(":/icons/config_dsound.png"); +} + +void DXConfigDialog::accept() { + g.s.iDXOutputDelay = qsOutputDelay->value(); + g.s.qbaDXInput = qcbInputDevice->itemData(qcbInputDevice->currentIndex()).toByteArray(); + g.s.qbaDXOutput = qcbOutputDevice->itemData(qcbOutputDevice->currentIndex()).toByteArray(); + + g.s.a3dModel = static_cast<Settings::Audio3D>(qcbMethod->currentIndex()); + g.s.fDXMinDistance = qsMinDistance->value() / 10.0; + g.s.fDXMaxDistance = qsMaxDistance->value() / 10.0; + g.s.fDXRollOff = qsRollOff->value() / 100.0; +} + +void DXConfigDialog::on_OutputDelay_valueChanged(int v) { + qlOutputDelay->setText(tr("%1ms").arg(v*20)); +} + +void DXConfigDialog::on_MinDistance_valueChanged(int v) { + qlMinDistance->setText(tr("%1m").arg(v/10.0, 0, 'f', 1)); + qsMaxDistance->setMinimum(v); + updateIntensity(); +} + +void DXConfigDialog::on_MaxDistance_valueChanged(int v) { + qlMaxDistance->setText(tr("%1m").arg(v/10.0, 0, 'f', 1)); + if (v > 200) + v = 200; + qsMinDistance->setMaximum(v); + updateIntensity(); +} + +void DXConfigDialog::on_RollOff_valueChanged(int v) { + qlRollOff->setText(tr("%1").arg(v/100.0, 0, 'f', 2)); + updateIntensity(); +} + +void DXConfigDialog::updateIntensity() { + float min = qsMinDistance->value() / 10.0; + float max = qsMaxDistance->value() / 10.0; + float roll = qsRollOff->value() / 100.0; + + float intensity = min / (min + (max-min)*roll); + qlIntensity->setText(tr("Players more than %1 meters away have %2% intensity").arg(max,0,'f',1).arg(intensity * 100.0, 0, 'f', 1)); +} diff --git a/src/mumble/DXConfigDialog.h b/src/mumble/DXConfigDialog.h new file mode 100644 index 000000000..0555c103e --- /dev/null +++ b/src/mumble/DXConfigDialog.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2005-2006, 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 _DXCONFIGDIALOG_H +#define _DXCONFIGDIALOG_H + +#include <dsound.h> + +#include "ConfigDialog.h" + +typedef QPair<QString, GUID> dsDevice; + +class DXConfigDialog : public ConfigWidget { + Q_OBJECT + protected: + QList<dsDevice> qlInput; + QList<dsDevice> qlOutput; + + QComboBox *qcbInputDevice; + QComboBox *qcbOutputDevice; + QComboBox *qcbMethod; + + QSlider *qsOutputDelay; + QLabel *qlOutputDelay; + QSlider *qsMinDistance, *qsMaxDistance, *qsRollOff; + QLabel *qlMinDistance, *qlMaxDistance, *qlRollOff; + QLabel *qlIntensity; + public: + DXConfigDialog(QWidget *p = NULL); + virtual QString title() const; + virtual QIcon icon() const; + public slots: + void accept(); + void on_OutputDelay_valueChanged(int v); + void on_MinDistance_valueChanged(int v); + void on_MaxDistance_valueChanged(int v); + void on_RollOff_valueChanged(int v); + void updateIntensity(); +}; + +#else +class DXConfigDialog; +#endif diff --git a/src/mumble/Database.cpp b/src/mumble/Database.cpp new file mode 100644 index 000000000..c397f53b6 --- /dev/null +++ b/src/mumble/Database.cpp @@ -0,0 +1,75 @@ +/* Copyright (C) 2005-2006, 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 "Database.h" +#include "Global.h" + +Database::Database() { + QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); + QSettings qs; + QStringList datapaths; + int i; + + datapaths << qs.value("InstPath").toString(); + datapaths << QDir::homePath(); + datapaths << qApp->applicationDirPath(); + datapaths << QDir::currentPath(); + bool found = false; + + for(i = 0; (i < datapaths.size()) && ! found; i++) { + if (!datapaths[i].isEmpty()) { + QFile f(datapaths[i] + "/mumble.sqlite"); + if (f.exists()) { + db.setDatabaseName(f.fileName()); + found = db.open(); + } + } + } + + if (! found) { + for(i = 0; (i < datapaths.size()) && ! found; i++) { + if (!datapaths[i].isEmpty()) { + QFile f(datapaths[i] + "/mumble.sqlite"); + db.setDatabaseName(f.fileName()); + found = db.open(); + } + } + } + + if (! found) { + QMessageBox::critical(NULL, tr("Mumble"), tr("Mumble failed to initialize a database in any\nof the possible locations."), QMessageBox::Ok | QMessageBox::Default, QMessageBox::NoButton); + qFatal("Database: Failed initialization"); + } + + QSqlQuery query; + // query.exec("DROP TABLE servers"); + query.exec("CREATE TABLE servers (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, hostname TEXT, port INTEGER DEFAULT 64738, username TEXT, password TEXT)"); + query.exec("ALTER TABLE servers ADD COLUMN udp INTEGER DEFAULT 1"); +} diff --git a/src/mumble/Database.h b/src/mumble/Database.h new file mode 100644 index 000000000..84cc0eb15 --- /dev/null +++ b/src/mumble/Database.h @@ -0,0 +1,42 @@ +/* Copyright (C) 2005-2006, 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 _DATABASE_H +#define _DATABASE_H + +class Database : public QObject{ + Q_OBJECT + public: + Database(); +}; + +#else +class Database; +#endif diff --git a/src/mumble/FMODAudio.cpp b/src/mumble/FMODAudio.cpp new file mode 100644 index 000000000..991b438af --- /dev/null +++ b/src/mumble/FMODAudio.cpp @@ -0,0 +1,337 @@ +/* Copyright (C) 2005-2006, 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 "FMODAudio.h" +#include "Player.h" +#include "Global.h" + +#include <fmod_errors.h> + +#define NBLOCKS 8 + +#define MAX(a,b) ( (a) > (b) ? (a) : (b) ) +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) + +static AudioOutput *FMODAudioOutputNew() { + return new FMODAudioOutput(); +} +static AudioInput *FMODAudioInputNew() { + return new FMODAudioInput(); +} + +static AudioOutputRegistrar aorFMOD("FMOD", FMODAudioOutputNew); +static AudioInputRegistrar airFMOD("FMOD", FMODAudioInputNew); + +WeakFMODSystemPtr FMODSystem::fsSystem; +QMutex FMODSystem::qmSystem; + +FMODSystem::FMODSystem() { + aiInput = NULL; + aoOutput = NULL; + bRunning = true; +} + +FMODSystem::~FMODSystem() { + bRunning = false; + wait(); +} + +FMODSystemPtr FMODSystem::getSystem() { + qmSystem.lock(); + + FMODSystemPtr ptr = fsSystem.lock(); + if (! ptr) { + ptr = FMODSystemPtr(new FMODSystem()); + fsSystem = ptr; + ptr->start(QThread::HighPriority); + } + + qmSystem.unlock(); + return ptr; +} + +void FMODSystem::run() { + FMOD_RESULT result; + + result = FMOD_System_Create(&system); + if (result != FMOD_OK) + qWarning("FMODSystem: FMOD_System_Create %d", result); + + FMOD_System_SetOutput(system, FMOD_OUTPUTTYPE_ALSA); + if (result != FMOD_OK) + qWarning("FMODSystem: FMOD_System_SetOutput %d", result); + + result = FMOD_System_Init(system, 4, FMOD_INIT_NORMAL, NULL); + if (result != FMOD_OK) + qWarning("FMODSystem: FMOD_System_Init %d: %s", result,FMOD_ErrorString(result)); + + qWarning("FMODSystem: System initialized"); + + while (bRunning) { + if (aoOutput) { + if (! aoOutput->bRunning) { + aoOutput->wipe(); + qwWait.wakeAll(); + } else + aoOutput->frame(system); + } + if (aiInput) { + if (! aiInput->bRunning) { + aiInput->release(); + qwWait.wakeAll(); + } else + aiInput->frame(system); + } + FMOD_System_Update(system); + msleep(10); + }; + + result = FMOD_System_Close(system); + result = FMOD_System_Release(system); + + qWarning("FMODSystem: Exiting"); +} + +FMODAudioInput::FMODAudioInput() { + sound = NULL; +} + +FMODAudioInput::~FMODAudioInput() { + bRunning = false; + wait(); +} + +void FMODAudioInput::release() { + if (sound) { + qWarning("Releasing FMOD AI"); + FMOD_Sound_Release(sound); + sound = NULL; + } +} + +void FMODAudioInput::run() { + FMODSystemPtr sys = FMODSystem::getSystem(); + QMutex m; + + bRunning = true; + + sys->aiInput = this; + while (bRunning) { + m.lock(); + sys->qwWait.wait(&m); + m.unlock(); + } + sys->aiInput = NULL; +} + +void FMODAudioInput::frame(FMOD_SYSTEM *system) { + if (! sound) { + FMOD_CREATESOUNDEXINFO exinfo; + FMOD_RESULT result; + + memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); + + uiBufferSize = iFrameSize * sizeof(short) * 32; + + exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); + exinfo.numchannels = 1; + exinfo.format = FMOD_SOUND_FORMAT_PCM16; + exinfo.defaultfrequency = SAMPLE_RATE; + exinfo.length = uiBufferSize; + + result = FMOD_System_CreateSound(system, 0, FMOD_2D | FMOD_SOFTWARE | FMOD_OPENUSER, &exinfo, &sound); + result = FMOD_System_RecordStart(system, sound, true); + + uiLastRead = 0; + } + + unsigned int recordpos; + + unsigned int ready; + + do { + FMOD_System_GetRecordPosition(system, &recordpos); + recordpos *= 2; + if (recordpos <= uiLastRead) + ready = (uiBufferSize - uiLastRead) + recordpos; + else + ready = recordpos - uiLastRead; + + + if (ready > iByteSize) { + void *ptr1, *ptr2; + unsigned int len1, len2; + + FMOD_Sound_Lock(sound, uiLastRead, iByteSize, &ptr1, &ptr2, &len1, &len2); + + if (ptr1 && len1) + CopyMemory(psMic, ptr1, len1); + + if (ptr2 && len2) + CopyMemory(psMic+len1/2, ptr2, len2); + + FMOD_Sound_Unlock(sound, ptr1, ptr2, len1, len2); + + encodeAudioFrame(); + + uiLastRead = (uiLastRead + iByteSize) % uiBufferSize; + } + + } while(ready >= iByteSize * 2); +} + +FMODOutputPlayer::FMODOutputPlayer(FMODAudioOutput *ao, Player *player) : AudioOutputPlayer(ao, player) { + fao = static_cast<FMODAudioOutput *>(aoOutput); + + sound = NULL; + channel = NULL; +} + +void FMODOutputPlayer::setupSound(FMOD_SYSTEM *system) { + FMOD_CREATESOUNDEXINFO createsoundexinfo; + FMOD_RESULT result; + FMOD_MODE mode = FMOD_2D | FMOD_OPENUSER | FMOD_LOOP_NORMAL | FMOD_SOFTWARE | FMOD_CREATESTREAM; + memset(&createsoundexinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); + createsoundexinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); /* required. */ + createsoundexinfo.decodebuffersize = iFrameSize * 5; /* Chunk size of stream update in samples. This will be the amount of data passed to the user callback. */ + createsoundexinfo.length = iFrameSize * sizeof(signed short) * 10; /* Length of PCM data in bytes of whole song (for Sound::getLength) */ + createsoundexinfo.numchannels = 1; /* Number of channels in the sound. */ + createsoundexinfo.defaultfrequency = SAMPLE_RATE; /* Default playback rate of sound. */ + createsoundexinfo.format = FMOD_SOUND_FORMAT_PCM16; /* Data format of sound. */ + createsoundexinfo.pcmreadcallback = PCMReadCallback; /* User callback for reading. */ + createsoundexinfo.userdata = this; + + bSetup = true; + + result = FMOD_System_CreateSound(system, 0, mode, &createsoundexinfo, &sound); + if (result != FMOD_OK) + qWarning("FMODOutputPlayer: FMOD_System_CreateSound %d", result); + result = FMOD_System_PlaySound(system, FMOD_CHANNEL_FREE, sound, 0, &channel); + if (result != FMOD_OK) + qWarning("FMODOutputPlayer: FMOD_System_PlaySound %d", result); + + bSetup = false; + + qWarning("FMODOutputPlayer: %s initialized", qPrintable(p->qsName)); + + bAlive = true; + iAliveHold = 0; +} + +FMODOutputPlayer::~FMODOutputPlayer() { + if (sound) { + FMOD_Sound_Release(sound); + } + + qWarning("FMODOutputPlayer: %s: Removed", qPrintable(p->qsName)); +} + +FMOD_RESULT F_CALLBACK FMODOutputPlayer::PCMReadCallback(FMOD_SOUND *sound, void *data, unsigned int datalen) { + void *udata; + FMOD_Sound_GetUserData(sound, &udata); + FMODOutputPlayer *aop = static_cast<FMODOutputPlayer *>(udata); + aop->ReadNextFrames(data,datalen); + return FMOD_OK; +} + +void FMODOutputPlayer::ReadNextFrames(void *data, unsigned int datalen) { + if (bSetup) { + ZeroMemory(data, datalen); + return; + } + + short *buffer_orig = psBuffer; + psBuffer = static_cast<short *>(data); + + bool alive; + + while(datalen) { + alive = decodeNextFrame(); + psBuffer += iFrameSize; + datalen -= iByteSize; + + if (alive) + iAliveHold = 0; + else { + iAliveHold++; + if (iAliveHold > 50) { + bAlive = false; + } + } + + } + + psBuffer = buffer_orig; +} + + +FMODAudioOutput::FMODAudioOutput() { + qWarning("FMODAudioOutput: Initialized"); + bRunning = true; +} + +FMODAudioOutput::~FMODAudioOutput() { + bRunning = false; + wait(); +} + +AudioOutputPlayer *FMODAudioOutput::getPlayer(Player *player) { + return new FMODOutputPlayer(this, player); +} + +void FMODAudioOutput::frame(FMOD_SYSTEM *system) { + qrwlOutputs.lockForRead(); + foreach(AudioOutputPlayer *aop, qmOutputs) { + FMODOutputPlayer *fop=static_cast<FMODOutputPlayer *>(aop); + if (! fop->sound) + fop->setupSound(system); + else if (! fop->bAlive) { + qrwlOutputs.unlock(); + removeBuffer(fop->p); + qrwlOutputs.lockForRead(); + break; + } + } + qrwlOutputs.unlock(); +} + +void FMODAudioOutput::run() { + FMODSystemPtr sys = FMODSystem::getSystem(); + QMutex m; + + sys->aoOutput = this; + + while (bRunning) { + m.lock(); + sys->qwWait.wait(&m); + m.unlock(); + } + sys->aoOutput = NULL; +} diff --git a/src/mumble/FMODAudio.h b/src/mumble/FMODAudio.h new file mode 100644 index 000000000..adc73ebf0 --- /dev/null +++ b/src/mumble/FMODAudio.h @@ -0,0 +1,118 @@ +/* Copyright (C) 2005-2006, 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 _FMODAUDIO_H +#define _FMODAUDIO_H + +#include "AudioInput.h" +#include "AudioOutput.h" +#include <fmod.h> +#include <fmod_errors.h> + +class FMODSystem; +class FMODAudioOutput; +class FMODAudioInput; + +typedef boost::shared_ptr<FMODSystem> FMODSystemPtr; +typedef boost::weak_ptr<FMODSystem> WeakFMODSystemPtr; + +class FMODSystem : public QThread { + friend class FMODAudioOutput; + friend class FMODAudioInput; + Q_OBJECT + protected: + static QMutex qmSystem; + static WeakFMODSystemPtr fsSystem; + + FMOD_SYSTEM *system; + FMODAudioOutput *aoOutput; + FMODAudioInput *aiInput; + bool bRunning; + public: + QWaitCondition qwWait; + + FMODSystem(); + ~FMODSystem(); + + static FMODSystemPtr getSystem(); + void run(); +}; + +class FMODAudioInput : public AudioInput { + friend class FMODSystem; + Q_OBJECT + protected: + FMOD_SOUND *sound; + unsigned int uiLastRead; + unsigned int uiBufferSize; + void frame(FMOD_SYSTEM *system); + void release(); + public: + FMODAudioInput(); + ~FMODAudioInput(); + void run(); +}; + +class FMODAudioOutput; +class FMODOutputPlayer : public AudioOutputPlayer { + friend class FMODAudioOutput; + Q_OBJECT + protected: + FMOD_SOUND *sound; + FMOD_CHANNEL *channel; + FMODAudioOutput *fao; + bool bAlive; + bool bSetup; + int iAliveHold; + void setupSound(FMOD_SYSTEM *system); + void ReadNextFrames(void *data, unsigned int datalen); + static FMOD_RESULT F_CALLBACK PCMReadCallback(FMOD_SOUND *sound, void *data, unsigned int datalen); + public: + FMODOutputPlayer(FMODAudioOutput *, Player *); + ~FMODOutputPlayer(); +}; + + +class FMODAudioOutput : public AudioOutput { + friend class FMODSystem; + friend class FMODOutputPlayer; + Q_OBJECT + protected: + virtual AudioOutputPlayer *getPlayer(Player *); + void frame(FMOD_SYSTEM *system); + public: + FMODAudioOutput(); + ~FMODAudioOutput(); + void run(); +}; + +#else +class DXAudioOutput; +#endif diff --git a/src/mumble/Global.cpp b/src/mumble/Global.cpp new file mode 100644 index 000000000..e44d92325 --- /dev/null +++ b/src/mumble/Global.cpp @@ -0,0 +1,45 @@ +/* Copyright (C) 2005-2006, 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 "Global.h" + +Global g; + +Global::Global() { + mw = 0; + sh = 0; + db = 0; + p = 0; + sId = 0; + iPushToTalk = 0; + bPushToMute = false; + bCenterPosition = false; + bAltSpeak = false; +} diff --git a/src/mumble/Global.h b/src/mumble/Global.h new file mode 100644 index 000000000..0531616ca --- /dev/null +++ b/src/mumble/Global.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2005-2006, 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 _GLOBAL_H +#define _GLOBAL_H + +#include "Settings.h" + +#ifndef MUMBLE_VERSION +#define MUMBLE_RELEASE "Compiled " __DATE__ " " __TIME__ +#else +#define MUMXTEXT(X) #X +#define MUMTEXT(X) MUMXTEXT(X) +#define MUMBLE_RELEASE MUMTEXT(MUMBLE_VERSION) +#endif + +// Global helper class to spread variables around across threads. + +class MainWindow; +class ServerHandler; +class AudioInput; +class AudioOutput; +class Database; +class Log; +class Plugins; +class QSettings; +class Overlay; + +struct Global { + MainWindow *mw; + Settings s; + ServerHandler *sh; + boost::shared_ptr<AudioInput> ai; + boost::shared_ptr<AudioOutput> ao; + Database *db; + Log *l; + Plugins *p; + QSettings *qs; + Overlay *o; + int iPushToTalk; + bool bPushToMute; + bool bCenterPosition; + bool bAltSpeak; + short sId; + int iMaxBandwidth; + Global(); +}; + +// -Wshadow is bugged. If an inline function of a class uses a variable or +// parameter named 'g', that will generate a warning even if the class header +// is included long before this definition. + +#define g g_global_struct +extern Global g_global_struct; + +#else +class Settings; +#endif diff --git a/src/mumble/GlobalShortcut.h b/src/mumble/GlobalShortcut.h new file mode 100644 index 000000000..00de49f71 --- /dev/null +++ b/src/mumble/GlobalShortcut.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2005-2006, 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 _GLOBALSHORTCUT_H +#define _GLOBALSHORTCUT_H + +#include <QObject> +#include <QString> +#include <QVariant> + +class GlobalShortcut : public QObject { + friend class GlobalShortcutWin; + friend class GlobalShortcutWinConfig; + friend class GlobalShortcutX; + friend class GlobalShortcutXConfig; + Q_OBJECT + Q_PROPERTY(QVariant data READ data WRITE setData) + Q_PROPERTY(bool active READ active) + protected: + QString name; + QVariant dv; + int idx; + bool act; + signals: + void down(); + void up(); + void triggered(bool); + public: + GlobalShortcut(QObject *parent, int index, QString qsName); + ~GlobalShortcut(); + QVariant data() const { return dv; }; + void setData(QVariant d) { dv = d; }; + bool active() const { return act; }; + private: + Q_DISABLE_COPY(GlobalShortcut) +}; + +#else +class GlobalShortcut; +#endif diff --git a/src/mumble/GlobalShortcut_unix.cpp b/src/mumble/GlobalShortcut_unix.cpp new file mode 100644 index 000000000..24333d56f --- /dev/null +++ b/src/mumble/GlobalShortcut_unix.cpp @@ -0,0 +1,359 @@ +/* Copyright (C) 2005-2006, 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 "GlobalShortcut_unix.h" +#include "Global.h" +#include <QX11Info> +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/extensions/Xevie.h> + +static GlobalShortcutX *gsx = NULL; + +static ConfigWidget *GlobalShortcutXConfigDialogNew() { + return new GlobalShortcutXConfig(); +} + +static ConfigRegistrar registrar(55, GlobalShortcutXConfigDialogNew); + +XInputKeyWidget::XInputKeyWidget(QWidget *p) : QLineEdit(p) { + setReadOnly(true); + clearFocus(); + bModified = false; + displayKeys(); +} + +void XInputKeyWidget::setShortcut(GlobalShortcut *gs) { + if (gsx->qhGlobalToX.contains(gs)) { + Shortcut *s = gsx->qhGlobalToX[gs]; + qlButtons = s->qlButtons; + displayKeys(); + } +} + +void XInputKeyWidget::focusInEvent(QFocusEvent *e) { + if (e->reason() == Qt::MouseFocusReason) { + setText(tr("Press Shortcut")); + + QPalette pal=parentWidget()->palette(); + pal.setColor(QPalette::Base, pal.color(QPalette::Base).dark(120)); + setPalette(pal); + + setForegroundRole(QPalette::Button); + gsx->resetMap(); + connect(gsx, SIGNAL(buttonPressed(bool)), this, SLOT(setButton(bool))); + } +} + +void XInputKeyWidget::focusOutEvent(QFocusEvent *) { + setPalette(parentWidget()->palette()); + clearFocus(); + disconnect(gsx, SIGNAL(buttonPressed(bool)), this, SLOT(setButton(bool))); + displayKeys(); +} + +void XInputKeyWidget::mouseDoubleClickEvent (QMouseEvent *) { + bModified = true; + qlButtons.clear(); + clearFocus(); + displayKeys(); +} + +void XInputKeyWidget::setButton(bool last) { + qlButtons = gsx->getCurrentButtons(); + bModified = true; + if (last) + clearFocus(); + else + displayKeys(); +} + +void XInputKeyWidget::displayKeys() { + QStringList sl; + foreach(int key, qlButtons) { + KeySym ks=XKeycodeToKeysym(QX11Info::display(), key, 0); + if (ks == NoSymbol) { + sl << QString("0x")+QString::number(key,16); + } else { + const char *str = XKeysymToString(ks); + if (strlen(str) == 0) { + sl << QString("KS0x")+QString::number(ks,16); + } else { + sl << QString(str); + } + } + } + setText(sl.join(" ")); +} + +GlobalShortcutXConfig::GlobalShortcutXConfig(QWidget *p) : ConfigWidget(p) { + QGroupBox *qgbShortcuts = new QGroupBox(tr("Shortcuts")); + QLabel *lab; + + QGridLayout *l=new QGridLayout(); + + lab=new QLabel(tr("Function")); + l->addWidget(lab, 0, 0); + lab=new QLabel(tr("Shortcut")); + l->addWidget(lab, 0, 1); + + int i=0; + + foreach(GlobalShortcut *gs, gsx->qmShortcuts) { + XInputKeyWidget *dikw=new XInputKeyWidget(); + dikw->setShortcut(gs); + + lab=new QLabel(gs->name); + l->addWidget(lab, i+1, 0); + l->addWidget(dikw, i+1, 1); + + dikw->setToolTip(tr("Shortcut bound to %1.").arg(gs->name)); + dikw->setWhatsThis(tr("<b>This is the global shortcut bound to %1</b><br />" + "Click this field and then the desired key combo " + "to rebind. Double-click to clear.").arg(gs->name)); + qhKeys[gs]=dikw; + i++; + } + + qgbShortcuts->setLayout(l); + + QVBoxLayout *v = new QVBoxLayout; + v->addWidget(qgbShortcuts); + v->addStretch(1); + setLayout(v); + + QMetaObject::connectSlotsByName(this); +} + +QString GlobalShortcutXConfig::title() const { + return tr("Shortcuts"); +} + +QIcon GlobalShortcutXConfig::icon() const { + return QIcon(":/icons/config_shortcuts.png"); +} + +void GlobalShortcutXConfig::accept() { + foreach(GlobalShortcut *gs, gsx->qmShortcuts) { + XInputKeyWidget *dikw = qhKeys[gs]; + if (dikw->bModified) { + QString base=QString("GS%1_").arg(gs->idx); + g.qs->setValue(base + QString("num"), dikw->qlButtons.count()); + int i=0; + foreach(int bt, dikw->qlButtons) { + g.qs->setValue(base + QString("%1_Key").arg(i), bt); + i++; + } + } + } + gsx->bNeedRemap = true; +} + +GlobalShortcutX::GlobalShortcutX() { + gsx = this; + ref = 0; + bGrabbing = false; + bNeedRemap = true; + int min, maj; + bRunning=false; + display = XOpenDisplay(NULL); + + if (! display) { + qWarning("GlobalShortcutX: Unable to open dedicated display connection."); + return; + } + + maj = 1; + min = 1; + + if (! XevieQueryVersion(display, &maj, &min)) { + qWarning("GlobalShortcutX: XEVIE extension not found. Enable it in xorg.conf"); + return; + } + + qWarning("GlobalShortcutX: XEVIE %d.%d", maj, min); + + if (! XevieStart(display)) { + qWarning("GlobalShortcutX: Another client is already using XEVIE"); + return; + } + + XevieSelectInput(display, KeyPressMask | KeyReleaseMask); + + bRunning=true; + start(QThread::TimeCriticalPriority); +} + +void GlobalShortcutX::add(GlobalShortcut *gs) { + qmShortcuts[gs->idx] = gs; + bNeedRemap=true; +} + +void GlobalShortcutX::remove(GlobalShortcut *gs) { + qmShortcuts.remove(gs->idx); + bNeedRemap=true; +} + +void GlobalShortcutX::grab() { + bGrabbing = true; +} + +void GlobalShortcutX::release() { + bGrabbing = false; +} + +GlobalShortcutX::~GlobalShortcutX() { + bRunning = false; + wait(); +} + +void GlobalShortcutX::remap() { + bNeedRemap = false; + + foreach(Shortcut *s, qhGlobalToX) { + delete s; + } + qhGlobalToX.clear(); + qmhKeyToShortcut.clear(); + + foreach(GlobalShortcut *gs, qmShortcuts) { + + QString base=QString("GS%1_").arg(gs->idx); + QList<int> buttons; + int nbuttons = g.qs->value(base + QString("num"), 0).toInt(); + for(int i=0;i<nbuttons;i++) { + int key = g.qs->value(base + QString("%1_Key").arg(i), 0xffffffff).toUInt(); + if (key != 0xffffffff) { + buttons << key; + } + } + if (buttons.count() > 0) { + Shortcut *s = new Shortcut(); + s->gs = gs; + s->bActive = false; + s->iNumDown = 0; + for(int i=0;i<buttons.count();i++) { + s->qlButtons << buttons[i]; + qmhKeyToShortcut.insert(buttons[i], s); + } + qhGlobalToX[gs] = s; + } + } +} + +void GlobalShortcutX::resetMap() { + for(int i=0;i<256;i++) + touchMap[i]=false; +} + +QList<int> GlobalShortcutX::getCurrentButtons() { + QList<int> keys; + for(int i=0;i<256;i++) + if (touchMap[i]) + keys << i; + return keys; +} + +void GlobalShortcutX::run() { + fd_set in_fds; + XEvent evt; + struct timeval tv; + while(bRunning) { + if (bNeedRemap) + remap(); + FD_ZERO(&in_fds); + FD_SET(ConnectionNumber(display), &in_fds); + tv.tv_sec = 0; + tv.tv_usec = 100000; + if(select(ConnectionNumber(display)+1, &in_fds, NULL, NULL, &tv)) { + while (XPending(display)) { + XNextEvent(display, &evt); + XevieSendEvent(display, &evt, XEVIE_UNMODIFIED); + switch(evt.type) { + case KeyPress: + case KeyRelease: + { + bool down = (evt.type == KeyPress); + + if (down == activeMap[evt.xkey.keycode]) + break; + activeMap[evt.xkey.keycode] = down; + if (down) + touchMap[evt.xkey.keycode] = true; + emit buttonPressed(!down); + foreach(Shortcut *s, qmhKeyToShortcut.values(evt.xkey.keycode)) { + if (down) { + s->iNumDown++; + if (s->iNumDown == s->qlButtons.count()) { + s->bActive = true; + emit s->gs->triggered(down); + emit s->gs->down(); + } + } else { + s->iNumDown--; + if (s->bActive) { + s->bActive = false; + emit s->gs->triggered(down); + emit s->gs->up(); + } + } + } + } + + break; + default: + qWarning("GlobalShortcutX: EVT %x", evt.type); + } + } + } + } + XevieEnd(display); + XCloseDisplay(display); +} + +GlobalShortcut::GlobalShortcut(QObject *p, int index, QString qsName) : QObject(p) { + if (! gsx) + gsx = new GlobalShortcutX(); + gsx->ref++; + idx = index; + name=qsName; + act = false; + gsx->add(this); +} + +GlobalShortcut::~GlobalShortcut() +{ + gsx->remove(this); + gsx->ref--; + if (gsx->ref == 0) { + delete gsx; + gsx = NULL; + } +} diff --git a/src/mumble/GlobalShortcut_unix.h b/src/mumble/GlobalShortcut_unix.h new file mode 100644 index 000000000..2e32c5bb3 --- /dev/null +++ b/src/mumble/GlobalShortcut_unix.h @@ -0,0 +1,98 @@ +/* Copyright (C) 2005-2006, 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 "GlobalShortcut.h" +#include "ConfigDialog.h" + +typedef QPair<int, unsigned int> qpButton; + +struct Shortcut { + GlobalShortcut *gs; + int iNumDown; + bool bActive; + QList<int> qlButtons; +}; + +class XInputKeyWidget : public QLineEdit { + Q_OBJECT + protected: + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + public: + QList<int> qlButtons; + bool bModified; + XInputKeyWidget(QWidget *p = NULL); + void setShortcut(GlobalShortcut *gs); + public slots: + void setButton(bool); + void displayKeys(); +}; + +class GlobalShortcutXConfig : public ConfigWidget { + Q_OBJECT + protected: + QHash<GlobalShortcut *,XInputKeyWidget *> qhKeys; + public: + GlobalShortcutXConfig(QWidget *p = NULL); + virtual QString title() const; + virtual QIcon icon() const; + public slots: + void accept(); +}; + +class GlobalShortcutX : public QThread { + Q_OBJECT + public: + Display *display; + int ref; + bool bRunning; + bool activeMap[256]; + bool touchMap[256]; + QHash<int, GlobalShortcut *> qmShortcuts; + QHash<GlobalShortcut *, Shortcut *> qhGlobalToX; + bool globalEvent(XEvent *); + bool bGrabbing; + bool bNeedRemap; + void grab(); + void release(); + QMultiHash<int, Shortcut *> qmhKeyToShortcut; + public: + GlobalShortcutX(); + ~GlobalShortcutX(); + void run(); + void remap(); + void resetMap(); + void add(GlobalShortcut *); + void remove(GlobalShortcut *); + QList<int> getCurrentButtons(); + signals: + void buttonPressed(bool); +}; diff --git a/src/mumble/GlobalShortcut_win.cpp b/src/mumble/GlobalShortcut_win.cpp new file mode 100644 index 000000000..8f36f6670 --- /dev/null +++ b/src/mumble/GlobalShortcut_win.cpp @@ -0,0 +1,491 @@ +/* Copyright (C) 2005-2006, 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 "GlobalShortcut_win.h" +#include "MainWindow.h" +#include "Global.h" + +#undef FAILED +#define FAILED(Status) (static_cast<HRESULT>(Status)<0) + +#define DX_SAMPLE_BUFFER_SIZE 512 + +static GlobalShortcutWin *gsw = NULL; + +static ConfigWidget *GlobalShortcutWinConfigDialogNew() { + return new GlobalShortcutWinConfig(); +} + +static ConfigRegistrar registrar(55, GlobalShortcutWinConfigDialogNew); + +uint qHash(const GUID &a) { + uint val = a.Data1 ^ a.Data2 ^ a.Data3; + for(int i=0;i<8;i++) + val += a.Data4[i]; + return val; +} + +DirectInputKeyWidget::DirectInputKeyWidget(QWidget *p) : QLineEdit(p) { + setReadOnly(true); + clearFocus(); + bModified = false; + displayKeys(); +} + +void DirectInputKeyWidget::setShortcut(GlobalShortcut *gs) { + if (gsw->qhGlobalToWin.contains(gs)) { + Shortcut *s = gsw->qhGlobalToWin[gs]; + qlButtons = s->qlButtons; + displayKeys(); + } +} + +void DirectInputKeyWidget::focusInEvent(QFocusEvent *e) { + if (e->reason() == Qt::MouseFocusReason) { + setText(tr("Press Shortcut")); + + QPalette pal=parentWidget()->palette(); + pal.setColor(QPalette::Base, pal.color(QPalette::Base).dark(120)); + setPalette(pal); + + setForegroundRole(QPalette::Button); + gsw->resetMap(); + connect(gsw, SIGNAL(buttonPressed(bool)), this, SLOT(updateKeys(bool))); + } +} + +void DirectInputKeyWidget::focusOutEvent(QFocusEvent *) { + setPalette(parentWidget()->palette()); + clearFocus(); + disconnect(gsw, SIGNAL(buttonPressed(bool)), this, SLOT(updateKeys(bool))); + displayKeys(); +} + +void DirectInputKeyWidget::mouseDoubleClickEvent (QMouseEvent *) { + bModified = true; + qlButtons.clear(); + clearFocus(); + displayKeys(); +} + + +void DirectInputKeyWidget::updateKeys(bool last) { + qlButtons = gsw->getCurrentButtons(); + bModified = true; + if (last) + clearFocus(); + else + displayKeys(); +} + +void DirectInputKeyWidget::displayKeys() { + QStringList keys; + + foreach(qpButton button, qlButtons) { + QString device=QString::number(button.first.Data1,16) + ":"; + QString name=QString("Unknown"); + InputDevice *id = gsw->qhInputDevices[button.first]; + if (button.first == GUID_SysMouse) + device=QString("M:"); + else if ((button.first == GUID_SysKeyboard) || (button.first.Data1 == 0)) + device=QString("K:"); + else if (id) + device=id->name+":"; + if (id) { + name=id->qhNames[button.second]; + } + keys << device+name; + } + setText(keys.join(QString(" + "))); +} + +GlobalShortcutWinConfig::GlobalShortcutWinConfig(QWidget *p) : ConfigWidget(p) { + QGroupBox *qgbShortcuts = new QGroupBox(tr("Shortcuts")); + QLabel *lab; + + QGridLayout *l=new QGridLayout(); + + lab=new QLabel(tr("Function")); + l->addWidget(lab, 0, 0); + lab=new QLabel(tr("Shortcut")); + l->addWidget(lab, 0, 1); + + int i=0; + + foreach(GlobalShortcut *gs, gsw->qmShortcuts) { + DirectInputKeyWidget *dikw=new DirectInputKeyWidget(); + dikw->setShortcut(gs); + + lab=new QLabel(gs->name); + l->addWidget(lab, i+1, 0); + l->addWidget(dikw, i+1, 1); + + dikw->setToolTip(tr("Shortcut bound to %1.").arg(gs->name)); + dikw->setWhatsThis(tr("<b>This is the global shortcut bound to %1</b><br />" + "Click this field and then the desired key/button combo " + "to rebind. Double-click to clear.").arg(gs->name)); + qhKeys[gs]=dikw; + i++; + } + + qgbShortcuts->setLayout(l); + + QVBoxLayout *v = new QVBoxLayout; + v->addWidget(qgbShortcuts); + v->addStretch(1); + setLayout(v); + + QMetaObject::connectSlotsByName(this); +} + +QString GlobalShortcutWinConfig::title() const { + return tr("Shortcuts"); +} + +QIcon GlobalShortcutWinConfig::icon() const { + return QIcon(":/icons/config_shortcuts.png"); +} + +void GlobalShortcutWinConfig::accept() { + foreach(GlobalShortcut *gs, gsw->qmShortcuts) { + DirectInputKeyWidget *dikw = qhKeys[gs]; + if (dikw->bModified) { + QString base=QString("GS%1_").arg(gs->idx); + g.qs->setValue(base + QString("num"), dikw->qlButtons.count()); + int i=0; + foreach(qpButton button, dikw->qlButtons) { + g.qs->setValue(base + QString("%1_GUID").arg(i), QUuid(dikw->qlButtons[i].first).toString()); + g.qs->setValue(base + QString("%1_Type").arg(i), static_cast<int>(dikw->qlButtons[i].second)); + i++; + } + } + } + + gsw->remap(); +} + +GlobalShortcutWin::GlobalShortcutWin() { + gsw = this; + ref = 0; + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(timeTicked())); + + HRESULT hr; + + pDI = NULL; + + if(FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast<VOID**>(&pDI), NULL))) + { + qFatal("GlobalShortcutWin: Failed to create d8input"); + return; + } + + remap(); + + timer->start(20); +} + +BOOL CALLBACK GlobalShortcutWin::EnumDeviceObjectsCallback( + LPCDIDEVICEOBJECTINSTANCE lpddoi, + LPVOID pvRef) +{ + InputDevice *id=static_cast<InputDevice *>(pvRef); + QString name = QString::fromUtf16(reinterpret_cast<const ushort *>(lpddoi->tszName)); +// qWarning("%08lx %08lx %s",lpddoi->dwOfs,lpddoi->dwType, qPrintable(name)); + id->qhNames[lpddoi->dwType] = name; + + return DIENUM_CONTINUE; +} + +BOOL GlobalShortcutWin::EnumDevicesCB(LPCDIDEVICEINSTANCE pdidi, LPVOID pContext) +{ + GlobalShortcutWin *cbgsw=static_cast<GlobalShortcutWin *>(pContext); + HRESULT hr; + + QString name = QString::fromUtf16(reinterpret_cast<const ushort *>(pdidi->tszProductName)); + QString sname = QString::fromUtf16(reinterpret_cast<const ushort *>(pdidi->tszInstanceName)); + + InputDevice *id = new InputDevice; + id->name = name; + id->guid = pdidi->guidInstance; + + if (FAILED(hr = cbgsw->pDI->CreateDevice(pdidi->guidInstance, &id->pDID, NULL))) + qFatal("GlobalShortcutWin: CreateDevice: %lx", hr); + + if (FAILED(hr = id->pDID->EnumObjects(EnumDeviceObjectsCallback, static_cast<void *>(id), DIDFT_BUTTON))) + qFatal("GlobalShortcutWin: EnumObjects: %lx", hr); + + if (id->qhNames.count() > 0) { + QList<DWORD> types = id->qhNames.keys(); + qSort(types); + + int nbuttons = types.count(); + DIOBJECTDATAFORMAT *rgodf = new DIOBJECTDATAFORMAT[nbuttons]; + DIDATAFORMAT df; + ZeroMemory(&df, sizeof(df)); + df.dwSize = sizeof(df); + df.dwObjSize = sizeof(DIOBJECTDATAFORMAT); + df.dwFlags=DIDF_ABSAXIS; + df.dwDataSize = (nbuttons + 3) & (~0x3); + df.dwNumObjs = nbuttons; + df.rgodf = rgodf; + for(int i=0;i<nbuttons;i++) { + ZeroMemory(& rgodf[i], sizeof(DIOBJECTDATAFORMAT)); + DWORD dwType = types[i]; + DWORD dwOfs = i; + rgodf[i].dwOfs = dwOfs; + rgodf[i].dwType = dwType; + id->qhOfsToType[dwOfs] = dwType; + id->qhTypeToOfs[dwType] = dwOfs; +// qWarning("%4x %4x %s", dwType, dwOfs, qPrintable(id->qhNames[dwType])); + } + + if (FAILED(hr = id->pDID->SetCooperativeLevel( g.mw->winId(), DISCL_NONEXCLUSIVE|DISCL_BACKGROUND ))) + qFatal("GlobalShortcutWin: SetCooperativeLevel: %lx", hr); + + if (FAILED(hr = id->pDID->SetDataFormat(&df))) + qFatal("GlobalShortcutWin: SetDataFormat: %lx", hr); + + DIPROPDWORD dipdw; + + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = DX_SAMPLE_BUFFER_SIZE; + + if(FAILED(hr = id->pDID->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph))) + qFatal("GlobalShortcutWin::SetProperty"); + + qWarning("Adding device %s %s %s:%d", qPrintable(QUuid(id->guid).toString()),qPrintable(name),qPrintable(sname),id->qhNames.count()); + + cbgsw->qhInputDevices[id->guid] = id; + } else { + id->pDID->Release(); + delete(id); + } + + return DIENUM_CONTINUE; +} + +void GlobalShortcutWin::add(GlobalShortcut *gs) { + qmShortcuts[gs->idx] = gs; + bNeedRemap = true; +} + +void GlobalShortcutWin::remove(GlobalShortcut *gs) { + qmShortcuts.remove(gs->idx); + bNeedRemap = true; +} + +void GlobalShortcutWin::remap() { + HRESULT hr; + + bNeedRemap = false; + + unacquire(); + + if (qmShortcuts.isEmpty()) + return; + + foreach(InputDevice *id, qhInputDevices) { + if (id->pDID) { + id->pDID->Release(); + } + delete(id); + } + qhInputDevices.clear(); + + pDI->EnumDevices(DI8DEVCLASS_ALL, EnumDevicesCB, static_cast<void *>(this), DIEDFL_ALLDEVICES); + + foreach(Shortcut *s, qhGlobalToWin) { + delete(s); + } + qhGlobalToWin.clear(); + + foreach(GlobalShortcut *gs, qmShortcuts) { + QString base=QString("GS%1_").arg(gs->idx); + QList<QUuid> guids; + QList<DWORD> types; + int nbuttons = g.qs->value(base + QString("num"), 0).toInt(); + for(int i=0;i<nbuttons;i++) { + QUuid guid(g.qs->value(base + QString("%1_GUID").arg(i), QUuid(GUID_SysKeyboard).toString()).toString()); + DWORD type = g.qs->value(base + QString("%1_Type").arg(i), 0xffffffff).toUInt(); + if (! guid.isNull() && (type != 0xffffffff)) { + guids << guid; + types << type; + } + } + if (guids.count() > 0) { + Shortcut *s = new Shortcut(); + s->gs = gs; + s->bActive = false; + s->iNumDown = 0; + for(int i=0;i<guids.count();i++) { + s->qlButtons << qpButton(guids[i], types[i]); + s->qlActive << false; + if (qhInputDevices.contains(guids[i])) { + InputDevice *id = qhInputDevices[guids[i]]; + id->qmhOfsToShortcut.insert(id->qhTypeToOfs[types[i]], s); + } + } + qhGlobalToWin[gs] = s; + } + } +} + +GlobalShortcutWin::~GlobalShortcutWin() { + unacquire(); + + foreach(InputDevice *id, qhInputDevices) { + if (id->pDID) { + id->pDID->Release(); + } + } + pDI->Release(); +} + +void GlobalShortcutWin::unacquire() { + foreach(InputDevice *id, qhInputDevices) { + if (id->pDID) { + id->pDID->Unacquire(); + } + } +} + +void GlobalShortcutWin::timeTicked() { + bool pressed = false; + + if (bNeedRemap) + remap(); + + + foreach(InputDevice *id, qhInputDevices) { + DIDEVICEOBJECTDATA rgdod[DX_SAMPLE_BUFFER_SIZE]; + DWORD dwItems = DX_SAMPLE_BUFFER_SIZE; + HRESULT hr; + + id->pDID->Acquire(); + id->pDID->Poll(); + + hr = id->pDID->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), rgdod, &dwItems, 0 ); + if( FAILED(hr) ) + continue; + + for( DWORD j=0; j<dwItems; j++ ) + { +// qWarning("%4x %4x", rgdod[j].dwOfs, rgdod[j].dwData); + + if (!bIgnoreActive) { + if (rgdod[j].dwData & 0x80) { + gsw->iButtonsDown++; + id->activeMap.insert(rgdod[j].dwOfs); + pressed = true; + } else if (id->activeMap.contains(rgdod[j].dwOfs)) { + gsw->iButtonsDown--; + if (gsw->iButtonsDown == 0) + pressed = true; + } + } + + foreach(Shortcut *s, id->qmhOfsToShortcut.values(rgdod[j].dwOfs)) { + qpButton button(id->guid, id->qhOfsToType[rgdod[j].dwOfs]); + int idx; + for(idx=0;idx<s->qlButtons.count();idx++) { + if(s->qlButtons[idx] == button) { + bool a = (rgdod[j].dwData & 0x80); + if (a != s->qlActive[idx]) { + s->qlActive[idx] = a; + if (!a && s->bActive) { + s->iNumDown--; + s->bActive = false; + emit s->gs->triggered(a); + s->gs->act = false; + emit s->gs->up(); + } else if (!a) { + s->iNumDown--; + } else if (a) { + s->iNumDown++; + if (s->iNumDown == s->qlActive.count()) { + s->bActive = true; + emit s->gs->triggered(a); + s->gs->act = true; + emit s->gs->down(); + } + } + } + } + } + } + } + } + + if (pressed) + emit buttonPressed(gsw->iButtonsDown == 0); + bIgnoreActive = false; +} + +void GlobalShortcutWin::resetMap() { + foreach(InputDevice *id, qhInputDevices) { + id->activeMap.clear(); + } + bIgnoreActive = true; + iButtonsDown = 0; +} + +QList<qpButton> GlobalShortcutWin::getCurrentButtons() { + QList<qpButton> buttons; + foreach(InputDevice *id, qhInputDevices) { + foreach(DWORD ofs, id->activeMap) { + buttons << qpButton(id->guid, id->qhOfsToType[ofs]); + } + } + return buttons; +} + +GlobalShortcut::GlobalShortcut(QObject *p, int index, QString qsName) : QObject(p) { + if (! gsw) + gsw = new GlobalShortcutWin(); + gsw->ref++; + idx = index; + name=qsName; + act = false; + gsw->add(this); +} + +GlobalShortcut::~GlobalShortcut() +{ + gsw->remove(this); + gsw->ref--; + if (gsw->ref == 0) { + delete gsw; + gsw = 0; + } +} diff --git a/src/mumble/GlobalShortcut_win.h b/src/mumble/GlobalShortcut_win.h new file mode 100644 index 000000000..cb3eb6e03 --- /dev/null +++ b/src/mumble/GlobalShortcut_win.h @@ -0,0 +1,127 @@ +/* Copyright (C) 2005-2006, 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 "GlobalShortcut.h" +#include "ConfigDialog.h" + +#define DIRECTINPUT_VERSION 0x0800 +#include <dinput.h> + +typedef QPair<GUID, DWORD> qpButton; + +struct Shortcut { + GlobalShortcut *gs; + bool bActive; + int iNumDown; + QList<qpButton> qlButtons; + QList<bool> qlActive; +}; + +struct InputDevice { + LPDIRECTINPUTDEVICE8 pDID; + GUID guid; + QString name; + + // dwType to name + QHash<DWORD, QString> qhNames; + + // Map dwType to dwOfs in our structure + QHash<DWORD, DWORD> qhTypeToOfs; + + // Map dwOfs in our structure to dwType + QHash<DWORD, DWORD> qhOfsToType; + + // Map dwOfs for this device to possible matching shortcut + QMultiHash<DWORD, Shortcut *> qmhOfsToShortcut; + + // Buttons active since last reset + QSet<DWORD> activeMap; +}; + +class DirectInputKeyWidget : public QLineEdit { + Q_OBJECT + protected: + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + public: + QList<qpButton> qlButtons; + bool bModified; + DirectInputKeyWidget(QWidget *p = NULL); + void setShortcut(GlobalShortcut *gs); + public slots: + void updateKeys(bool last); + void displayKeys(); +}; + +class GlobalShortcutWinConfig : public ConfigWidget { + Q_OBJECT + protected: + QHash<GlobalShortcut *,DirectInputKeyWidget *> qhKeys; + public: + GlobalShortcutWinConfig(QWidget *p = NULL); + virtual QString title() const; + virtual QIcon icon() const; + public slots: + void accept(); +}; + +class GlobalShortcutWin : public QObject { + Q_OBJECT + public: + QTimer *timer; + int ref; + + LPDIRECTINPUT8 pDI; + QHash<int, GlobalShortcut *> qmShortcuts; + QHash<GlobalShortcut *, Shortcut *> qhGlobalToWin; + QHash<GUID, InputDevice *> qhInputDevices; + static BOOL CALLBACK EnumSuitableDevicesCB(LPCDIDEVICEINSTANCE, LPDIRECTINPUTDEVICE8, DWORD, DWORD, LPVOID); + static BOOL CALLBACK EnumDevicesCB(LPCDIDEVICEINSTANCE, LPVOID); + static BOOL CALLBACK EnumDeviceObjectsCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef); + bool bNeedRemap; + bool bIgnoreActive; + int iButtonsDown; + public slots: + void timeTicked(); + signals: + void buttonPressed(bool last); + public: + GlobalShortcutWin(); + ~GlobalShortcutWin(); + void unacquire(); + void remap(); + void add(GlobalShortcut *); + void remove(GlobalShortcut *); + void resetMap(); + QList<qpButton> getCurrentButtons(); +}; + +uint qHash(const GUID &); diff --git a/src/mumble/Log.cpp b/src/mumble/Log.cpp new file mode 100644 index 000000000..e3efd42d3 --- /dev/null +++ b/src/mumble/Log.cpp @@ -0,0 +1,224 @@ +/* Copyright (C) 2005-2006, 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 <QVBoxLayout> +#include <QHBoxLayout> +#include <QGridLayout> +#include <QLabel> +#include <QString> +#include <QList> +#include <QTime> +#include <QSettings> +#include "TextToSpeech.h" +#include "MainWindow.h" +#include "Log.h" +#include "Global.h" + +static ConfigWidget *LogConfigDialogNew() { + return new LogConfig(); +} + +static ConfigRegistrar registrar(40, LogConfigDialogNew); + +LogConfig::LogConfig(QWidget *p) : ConfigWidget(p) { + QGroupBox *qgbMessages = new QGroupBox(tr("Messages")); + QLabel *lab; + + QGridLayout *l=new QGridLayout(); + + lab=new QLabel(tr("Console")); + l->addWidget(lab, 0, 1); + lab=new QLabel(tr("TTS")); + l->addWidget(lab, 0, 2); + + for(int i=Log::firstMsgType;i<=Log::lastMsgType;++i) { + QCheckBox *qcb; + Log::MsgType t=static_cast<Log::MsgType>(i); + MsgSettings *ms=g.l->qhSettings[t]; + QString name = g.l->msgName(t); + + lab=new QLabel(name); + l->addWidget(lab, i+1, 0); + + qcb=new QCheckBox(); + qcb->setCheckState(ms->bConsole ? Qt::Checked : Qt::Unchecked); + qcb->setToolTip(tr("Enable console for %1").arg(name)); + qlConsole << qcb; + l->addWidget(qcb, i+1, 1); + + qcb=new QCheckBox(); + qcb->setCheckState(ms->bTTS ? Qt::Checked : Qt::Unchecked); + qcb->setToolTip(tr("Enable Text-To-Speech for %1").arg(name)); + qlTTS << qcb; + l->addWidget(qcb, i+1, 2); + } + + qgbMessages->setLayout(l); + + QGroupBox *qgbTTS = new QGroupBox(tr("Text To Speech")); + l = new QGridLayout(); + + lab=new QLabel(tr("Volume")); + l->addWidget(lab,0,0); + qsVolume = new QSlider(Qt::Horizontal); + qsVolume->setRange(0, 100); + qsVolume->setSingleStep(5); + qsVolume->setPageStep(20); + qsVolume->setValue(g.s.iTTSVolume); + qsVolume->setObjectName("Volume"); + qsVolume->setToolTip(tr("Volume of Text-To-Speech Engine")); + qsVolume->setWhatsThis(tr("<b>This is the volume used for the speech synthesis.</b>")); + l->addWidget(qsVolume,0,1); + + qgbTTS->setLayout(l); + + + QVBoxLayout *v = new QVBoxLayout; + v->addWidget(qgbMessages); + v->addWidget(qgbTTS); + v->addStretch(1); + setLayout(v); + + QMetaObject::connectSlotsByName(this); +} + +QString LogConfig::title() const { + return tr("Messages"); +} + +QIcon LogConfig::icon() const { + return QIcon(":/icons/config_msgs.png"); +} + +void LogConfig::accept() { + for(int i=Log::firstMsgType;i<=Log::lastMsgType;++i) { + Log::MsgType t=static_cast<Log::MsgType>(i); + MsgSettings *ms=g.l->qhSettings[t]; + ms->bConsole = (qlConsole[i]->checkState() == Qt::Checked); + ms->bTTS = (qlTTS[i]->checkState() == Qt::Checked); + } + g.s.iTTSVolume=qsVolume->value(); + g.l->tts->setVolume(g.s.iTTSVolume); +} + +MsgSettings::MsgSettings() { + bConsole = true; + bTTS = true; + iIgnore = 0; +} + +Log::Log(QObject *p) : QObject(p) { + for(int i=firstMsgType;i<=lastMsgType;++i) + qhSettings[static_cast<MsgType>(i)]=new MsgSettings(); + tts=new TextToSpeech(this); + loadSettings(); + tts->setVolume(g.s.iTTSVolume); +} + +const char *Log::msgNames[] = { + QT_TR_NOOP("Debug"), + QT_TR_NOOP("Critical"), + QT_TR_NOOP("Warning"), + QT_TR_NOOP("Information"), + QT_TR_NOOP("Server Connected"), + QT_TR_NOOP("Server Disconnected"), + QT_TR_NOOP("Player Joined Server"), + QT_TR_NOOP("Player Left Server"), + QT_TR_NOOP("Player kicked (you or by you)"), + QT_TR_NOOP("Player kicked"), + QT_TR_NOOP("You self-muted/deafened"), + QT_TR_NOOP("Other self-muted/deafened"), + QT_TR_NOOP("Player muted (you)"), + QT_TR_NOOP("Player muted (by you)"), + QT_TR_NOOP("Player muted (other)"), + QT_TR_NOOP("Player Joined Channel"), + QT_TR_NOOP("Player Left Channel"), + QT_TR_NOOP("Permission Denied") +}; + +QString Log::msgName(MsgType t) const { + return tr(msgNames[t]); +} + +void Log::setIgnore(MsgType t, int ignore) { + MsgSettings *ms=qhSettings.value(t); + Q_ASSERT(ms); + ms->iIgnore=ignore; +} + +void Log::clearIgnore() { + foreach(MsgSettings *ms, qhSettings) + ms->iIgnore=0; +} + +void Log::loadSettings() { + for(int i=firstMsgType;i<=lastMsgType;++i) { + MsgType t=static_cast<MsgType>(i); + MsgSettings *ms=qhSettings[t]; + QString key=QString("msg%1").arg(msgNames[t]); + key = key.remove(QRegExp("[ /\\(\\)-]")); + int v = g.qs->value(key,0xff).toInt(); + ms->bConsole=(v & 0x01); + ms->bTTS=(v & 0x02); + } +} + +void Log::saveSettings() const { + for(int i=firstMsgType;i<=lastMsgType;++i) { + MsgType t=static_cast<MsgType>(i); + MsgSettings *ms=qhSettings[t]; + QString key=QString("msg%1").arg(msgNames[t]); + key = key.remove(QRegExp("[ /\\(\\)-]")); + int v = 0; + if (ms->bConsole) + v |= 0x01; + if (ms->bTTS) + v |= 0x02; + g.qs->setValue(key, v); + } +} + +void Log::log(MsgType t, QString console, QString phonetic) { + QTime now = QTime::currentTime(); + MsgSettings *ms=qhSettings.value(t); + Q_ASSERT(ms); + + if (ms->iIgnore) { + ms->iIgnore--; + return; + } + + if (ms->bConsole) + g.mw->appendLog(tr("[%2] %1").arg(console).arg(now.toString(Qt::LocalDate))); + if (! g.s.bTTS || ! ms->bTTS || (! phonetic.isNull() && phonetic.isEmpty())) + return; + tts->say(phonetic.isNull() ? console : phonetic); +} + diff --git a/src/mumble/Log.h b/src/mumble/Log.h new file mode 100644 index 000000000..4441dbc1c --- /dev/null +++ b/src/mumble/Log.h @@ -0,0 +1,91 @@ +/* Copyright (C) 2005-2006, 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 _LOG_H +#define _LOG_H + +#include <QThread> +#include <QGroupBox> +#include <QComboBox> +#include <QSlider> +#include <QLabel> +#include <QHash> +#include <QCheckBox> + +#include "ConfigDialog.h" + +class TextToSpeech; + +class LogConfig : public ConfigWidget { + Q_OBJECT + protected: + QList<QCheckBox *> qlConsole; + QList<QCheckBox *> qlTTS; + QSlider *qsVolume; + public: + LogConfig(QWidget *p = NULL); + virtual QString title() const; + virtual QIcon icon() const; + public slots: + void accept(); +}; + +struct MsgSettings { + bool bConsole; + bool bTTS; + int iIgnore; + MsgSettings(); +}; + +class Log : public QObject { + friend class LogConfig; + Q_OBJECT + public: + enum MsgType { DebugInfo, CriticalError, Warning, Information, ServerConnected, ServerDisconnected, PlayerJoin, PlayerLeave, YouKicked, PlayerKicked, SelfMute, OtherSelfMute, YouMuted, YouMutedOther, OtherMutedOther, ChannelJoin, ChannelLeave, PermissionDenied }; + static const MsgType firstMsgType = DebugInfo; + static const MsgType lastMsgType = PermissionDenied; + protected: + QHash<MsgType, MsgSettings *> qhSettings; + static const char *msgNames[]; + TextToSpeech *tts; + public: + Log(QObject *p = NULL); + QString msgName(MsgType t) const; + void setIgnore(MsgType t, int ignore = 1 << 30); + void clearIgnore(); + void loadSettings(); + void saveSettings() const; + public slots: + void log(MsgType t, QString console, QString phonetic=QString()); +}; + +#else +class Log; +#endif diff --git a/src/mumble/LookConfig.cpp b/src/mumble/LookConfig.cpp new file mode 100644 index 000000000..030b9a841 --- /dev/null +++ b/src/mumble/LookConfig.cpp @@ -0,0 +1,175 @@ +/* Copyright (C) 2005-2006, 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 "AudioInput.h"
+#include "AudioOutput.h"
+#include "LookConfig.h"
+#include "Global.h"
+#include "MainWindow.h"
+
+static ConfigWidget *LookConfigNew() {
+ return new LookConfig();
+}
+
+static ConfigRegistrar registrar(11, LookConfigNew);
+
+LookConfig::LookConfig(QWidget *p) : ConfigWidget(p) {
+ QGroupBox *qgbLanguage, *qgbLook;
+ QLabel *l;
+ QVBoxLayout *v;
+ QGridLayout *grid;
+ int i;
+
+ qgbLanguage=new QGroupBox(tr("Language"));
+ qgbLook=new QGroupBox(tr("Look and Feel"));
+
+ grid = new QGridLayout();
+
+ qcbLanguage = new QComboBox();
+ qcbLanguage->addItem(tr("System default"));
+ QDir d(":","*.qm",QDir::Name,QDir::Files);
+ QStringList langs;
+ foreach(QString key, d.entryList()) {
+ QString cc = key.mid(7,2);
+ langs << cc;
+ qcbLanguage->addItem(cc);
+ }
+ i=langs.indexOf(g.qs->value("Language").toString());
+ if (i >= 0)
+ qcbLanguage->setCurrentIndex(i+1);
+ l = new QLabel(tr("Language"));
+ l->setBuddy(qcbLanguage);
+ qcbLanguage->setToolTip(tr("Language to use (requires restart)"));
+ qcbLanguage->setWhatsThis(tr("<b>This sets which language Mumble should use.</b><br />You have to restart Mumble to use the new language."));
+
+ grid->addWidget(l,0,0);
+ grid->addWidget(qcbLanguage, 0, 1);
+ qgbLanguage->setLayout(grid);
+
+
+ grid = new QGridLayout();
+
+ qcbStyle = new QComboBox();
+ QStringList styles = QStyleFactory::keys();
+ styles.sort();
+ qcbStyle->addItem(tr("System default"));
+ foreach(QString key, styles) {
+ qcbStyle->addItem(key);
+ }
+ i=styles.indexOf(g.qs->value("Style").toString());
+ if (i >= 0)
+ qcbStyle->setCurrentIndex(i+1);
+ l = new QLabel(tr("Style"));
+ l->setBuddy(qcbStyle);
+ qcbStyle->setToolTip(tr("Basic widget style"));
+ qcbStyle->setWhatsThis(tr("<b>This sets the basic look and feel to use.</b>"));
+ grid->addWidget(l,0,0);
+ grid->addWidget(qcbStyle, 0, 1, 1, 2);
+
+ qleCSS = new QLineEdit(g.qs->value("Skin").toString());
+ l = new QLabel(tr("Skin"));
+ l->setBuddy(qcbLanguage);
+ qpbCSS = new QPushButton(tr("..."));
+ qpbCSS->setObjectName("SkinFile");
+ qleCSS->setToolTip(tr("Skin file to use"));
+ qleCSS->setWhatsThis(tr("<b>This sets which skin Mumble should use.</b><br />The skin is a style file applied on top of the basic widget style."));
+ qpbCSS->setToolTip(qleCSS->toolTip());
+ qpbCSS->setWhatsThis(qleCSS->whatsThis());
+ grid->addWidget(l,1,0);
+ grid->addWidget(qleCSS, 1, 1);
+ grid->addWidget(qpbCSS, 1, 2);
+
+ qcbHorizontal = new QCheckBox(tr("Use Horizontal Splitter"));
+ qcbHorizontal->setChecked(g.qs->value("Horizontal", true).toBool());
+ qcbHorizontal->setToolTip(tr("Use horizontal or vertical splitter for the main window"));
+ qcbHorizontal->setWhatsThis(tr("<b>This sets whether the split in the main window is horizontal or vertical.</b>"));
+ grid->addWidget(qcbHorizontal, 2, 1, 1, 2);
+
+ qgbLook->setLayout(grid);
+
+ v = new QVBoxLayout;
+ v->addWidget(qgbLanguage);
+ v->addWidget(qgbLook);
+ v->addStretch(1);
+ setLayout(v);
+
+ QMetaObject::connectSlotsByName(this);
+}
+
+QString LookConfig::title() const {
+ return tr("User Interface");
+}
+
+QIcon LookConfig::icon() const {
+ return QIcon(":/icons/config_ui.png");
+}
+
+void LookConfig::accept() {
+ if (qcbLanguage->currentIndex() == 0)
+ g.qs->remove("Language");
+ else
+ g.qs->setValue("Language", qcbLanguage->currentText());
+
+ if (qcbStyle->currentIndex() == 0)
+ g.qs->remove("Style");
+ else {
+ g.qs->setValue("Style", qcbStyle->currentText());
+ qApp->setStyle(qcbStyle->currentText());
+ }
+
+ if (qleCSS->text().isEmpty()) {
+ qApp->setStyleSheet("");
+ g.qs->remove("Skin");
+ } else {
+ QFile file(qleCSS->text());
+ file.open(QFile::ReadOnly);
+ QString sheet = file.readAll();
+ if (! sheet.isEmpty())
+ qApp->setStyleSheet(sheet);
+ g.qs->setValue("Skin", qleCSS->text());
+ }
+ g.qs->setValue("Horizontal", qcbHorizontal->isChecked());
+ if (qcbHorizontal->isChecked()) {
+ g.mw->qsSplit->setOrientation(Qt::Horizontal);
+ g.mw->qsSplit->addWidget(g.mw->qteLog);
+ g.mw->qsSplit->addWidget(g.mw->qtvPlayers);
+ } else {
+ g.mw->qsSplit->setOrientation(Qt::Vertical);
+ g.mw->qsSplit->addWidget(g.mw->qtvPlayers);
+ g.mw->qsSplit->addWidget(g.mw->qteLog);
+ }
+}
+
+void LookConfig::on_SkinFile_clicked(bool) {
+ QString file = QFileDialog::getOpenFileName(this, tr("Choose skin file"), QString(), "*.qss");
+ if (! file.isEmpty()) {
+ qleCSS->setText(file);
+ }
+}
diff --git a/src/mumble/LookConfig.h b/src/mumble/LookConfig.h new file mode 100644 index 000000000..71085e3d1 --- /dev/null +++ b/src/mumble/LookConfig.h @@ -0,0 +1,54 @@ +/* Copyright (C) 2005-2006, 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 _LOOKCONFIG_H
+#define _LOOKCONFIG_H
+
+#include "ConfigDialog.h"
+
+class LookConfig : public ConfigWidget {
+ Q_OBJECT
+ protected:
+ QComboBox *qcbLanguage, *qcbStyle;
+ QLineEdit *qleCSS;
+ QPushButton *qpbCSS;
+ QCheckBox *qcbHorizontal;
+ public:
+ LookConfig(QWidget *p = NULL);
+ virtual QString title() const;
+ virtual QIcon icon() const;
+ public slots:
+ void accept();
+ void on_SkinFile_clicked(bool);
+};
+
+#else
+class LookConfig;
+#endif
diff --git a/src/mumble/MainWindow.cpp b/src/mumble/MainWindow.cpp new file mode 100644 index 000000000..a2fd623d3 --- /dev/null +++ b/src/mumble/MainWindow.cpp @@ -0,0 +1,1145 @@ +/* Copyright (C) 2005-2006, 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 "MainWindow.h" +#include "AudioInput.h" +#include "ConnectDialog.h" +#include "Player.h" +#include "Channel.h" +#include "ACLEditor.h" +#include "BanEditor.h" +#include "Connection.h" +#include "ServerHandler.h" +#include "About.h" +#include "GlobalShortcut.h" +#include "VersionCheck.h" +#include "PlayerModel.h" +#include "AudioStats.h" +#include "Plugins.h" +#include "Log.h" +#ifdef Q_OS_WIN +#include "Overlay.h" +#endif +#include "Global.h" + +MainWindow::MainWindow(QWidget *p) : QMainWindow(p) { + Channel::add(0, tr("Root"), NULL); + + aclEdit = NULL; + banEdit = NULL; + + qtReconnect = new QTimer(this); + qtReconnect->setInterval(10000); + qtReconnect->setSingleShot(true); + qtReconnect->setObjectName("Reconnect"); + + createActions(); + setupGui(); + + connect(g.sh, SIGNAL(connected()), this, SLOT(serverConnected())); + connect(g.sh, SIGNAL(disconnected(QString)), this, SLOT(serverDisconnected(QString))); + +// ti = new TrayIcon(this); +} + +void MainWindow::createActions() { + qaServerConnect=new QAction(tr("&Connect"), this); + qaServerConnect->setToolTip(tr("Open the server connection dialog")); + qaServerConnect->setWhatsThis(tr("Shows a dialog of registered servers, and also allows quick connect.")); + qaServerConnect->setObjectName("ServerConnect"); + qaServerDisconnect=new QAction(tr("&Disconnect"), this); + qaServerDisconnect->setToolTip(tr("Disconnect from server")); + qaServerDisconnect->setWhatsThis(tr("Disconnects you from the server.")); + qaServerDisconnect->setObjectName("ServerDisconnect"); + qaServerDisconnect->setEnabled(false); + qaServerBanList=new QAction(tr("&Ban lists"), this); + qaServerBanList->setToolTip(tr("Edit ban lists on server")); + qaServerBanList->setWhatsThis(tr("This lets you edit the server-side IP ban lists.")); + qaServerBanList->setObjectName("ServerBanList"); + qaServerBanList->setEnabled(false); + + qaPlayerKick=new QAction(tr("&Kick"), this); + qaPlayerKick->setObjectName("PlayerKick"); + qaPlayerKick->setToolTip(tr("Kick player (with reason)")); + qaPlayerKick->setWhatsThis(tr("Kick selected player off server. You'll be asked to specify a reason.")); + qaPlayerBan=new QAction(tr("&Ban"), this); + qaPlayerBan->setObjectName("PlayerBan"); + qaPlayerBan->setToolTip(tr("Kick and ban player (with reason)")); + qaPlayerBan->setWhatsThis(tr("Kick and ban selected player from server. You'll be asked to specify a reason.")); + qaPlayerMute=new QAction(tr("&Mute"), this); + qaPlayerMute->setObjectName("PlayerMute"); + qaPlayerMute->setCheckable(true); + qaPlayerMute->setToolTip(tr("Mute player")); + qaPlayerMute->setWhatsThis(tr("Mute or unmute player on server. Unmuting a deafened player will also undeafen them.")); + qaPlayerDeaf=new QAction(tr("&Deafen"), this); + qaPlayerDeaf->setObjectName("PlayerDeaf"); + qaPlayerDeaf->setCheckable(true); + qaPlayerDeaf->setToolTip(tr("Deafen player")); + qaPlayerDeaf->setWhatsThis(tr("Deafen or undeafen player on server. Deafening a player will also mute them.")); + qaPlayerLocalMute=new QAction(tr("&Local Mute"), this); + qaPlayerLocalMute->setObjectName("PlayerLocalMute"); + qaPlayerLocalMute->setCheckable(true); + qaPlayerLocalMute->setToolTip(tr("Mute player")); + qaPlayerLocalMute->setWhatsThis(tr("Mute or unmute player locally.")); + + qaChannelAdd=new QAction(tr("&Add"), this); + qaChannelAdd->setObjectName("ChannelAdd"); + qaChannelAdd->setToolTip(tr("Add new channel")); + qaChannelAdd->setWhatsThis(tr("This adds a new sub-channel to the currently selected channel.")); + qaChannelRemove=new QAction(tr("&Remove"), this); + qaChannelRemove->setObjectName("ChannelRemove"); + qaChannelRemove->setToolTip(tr("Remove channel")); + qaChannelRemove->setWhatsThis(tr("This removes a channel and all sub-channels.")); + qaChannelACL=new QAction(tr("&Edit ACL"), this); + qaChannelACL->setObjectName("ChannelACL"); + qaChannelACL->setToolTip(tr("Edit Groups and ACL for channel")); + qaChannelACL->setWhatsThis(tr("This opens the Group and ACL dialog for the channel, to control permissions.")); + qaChannelLink=new QAction(tr("&Link"), this); + qaChannelLink->setObjectName("ChannelLink"); + qaChannelLink->setToolTip(tr("Link your channel to another channel")); + qaChannelLink->setWhatsThis(tr("This links your current channel to the selected channel. If they have permission to speak in " + "the other channel, players can now hear each other. This is a permanent link, and will last until " + "manually unlinked or the server is restarted. Please see the shortcuts for push-to-link.")); + qaChannelUnlink=new QAction(tr("&Unlink"), this); + qaChannelUnlink->setObjectName("ChannelUnlink"); + qaChannelUnlink->setToolTip(tr("Unlink your channel from another channel")); + qaChannelUnlink->setWhatsThis(tr("This unlinks your current channel from the selected channel.")); + qaChannelUnlinkAll=new QAction(tr("Unlink &All"), this); + qaChannelUnlinkAll->setObjectName("ChannelUnlinkAll"); + qaChannelUnlinkAll->setToolTip(tr("Unlinks your channel from all linked channels.")); + qaChannelUnlinkAll->setWhatsThis(tr("This unlinks your current channel (not the selected one) from all linked channels.")); + + qaAudioReset=new QAction(tr("&Reset"), this); + qaAudioReset->setObjectName("AudioReset"); + qaAudioReset->setToolTip(tr("Reset audio preprocessor")); + qaAudioReset->setWhatsThis(tr("This will reset the audio preprocessor, including noise cancellation, automatic gain and voice activity detection. " + "If something suddenly worsens the audio environment (like dropping the microphone) and it was temporary, " + "use this to avoid having to wait for the preprocessor to readjust.")); + qaAudioMute=new QAction(tr("&Mute Self"), this); + qaAudioMute->setObjectName("AudioMute"); + qaAudioMute->setCheckable(true); + qaAudioMute->setChecked(g.s.bMute); + qaAudioMute->setToolTip(tr("Mute yourself")); + qaAudioMute->setWhatsThis(tr("Mute or unmute yourself. When muted, you will not send any data to the server. Unmuting while deafened will also undeafen.")); + qaAudioDeaf=new QAction(tr("&Deafen Self"), this); + qaAudioDeaf->setObjectName("AudioDeaf"); + qaAudioDeaf->setCheckable(true); + qaAudioDeaf->setChecked(g.s.bDeaf); + qaAudioDeaf->setToolTip(tr("Deafen yourself")); + qaAudioDeaf->setWhatsThis(tr("Deafen or undeafen yourself. When deafened, you will not hear anything. Deafening yourself will also mute.")); + qaAudioTTS=new QAction(tr("&Text-To-Speech"), this); + qaAudioTTS->setObjectName("AudioTextToSpeech"); + qaAudioTTS->setCheckable(true); + qaAudioTTS->setChecked(g.qs->value("TextToSpeech", true).toBool()); + qaAudioTTS->setToolTip(tr("Toggle Text-To-Speech")); + qaAudioTTS->setWhatsThis(tr("Enable or disable the text-to-speech engine. Only messages enabled for TTS in the Configuration dialog will actually be spoken.")); + qaAudioStats=new QAction(tr("S&tatistics"), this); + qaAudioStats->setObjectName("AudioStats"); + qaAudioStats->setToolTip(tr("Display audio statistics")); + qaAudioStats->setWhatsThis(tr("Pops up a small dialog with information about your current audio input.")); + qaAudioUnlink=new QAction(tr("&Unlink"), this); + qaAudioUnlink->setObjectName("AudioUnlink"); + qaAudioUnlink->setToolTip(tr("Forcibly unlink plugin")); + qaAudioUnlink->setWhatsThis(tr("This forces the current plugin to unlink, which is handy if it is reading " + "completely wrong data.")); + + qaConfigDialog=new QAction(tr("&Settings"), this); + qaConfigDialog->setObjectName("ConfigDialog"); + qaConfigDialog->setToolTip(tr("Configure Mumble")); + qaConfigDialog->setWhatsThis(tr("Allows you to change most settings for Mumble.")); + + qaHelpWhatsThis = new QAction(tr("&What's This?"), this); + qaHelpWhatsThis->setObjectName("HelpWhatsThis"); + qaHelpWhatsThis->setToolTip(tr("Enter What's This? mode")); + qaHelpWhatsThis->setWhatsThis(tr("Click this to enter \"What's This?\" mode. Your cursor will turn into a question mark. Click " + "on any button, menu choice or area to show a description of what it is.")); + qaHelpAbout=new QAction(tr("&About"), this); + qaHelpAbout->setObjectName("HelpAbout"); + qaHelpAbout->setToolTip(tr("Information about Mumble")); + qaHelpAbout->setWhatsThis(tr("Shows a small dialog with information and license for Mumble.")); + qaHelpAboutSpeex=new QAction(tr("About &Speex"), this); + qaHelpAboutSpeex->setObjectName("HelpAboutSpeex"); + qaHelpAboutSpeex->setToolTip(tr("Information about Speex")); + qaHelpAboutSpeex->setWhatsThis(tr("Shows a small dialog with information about Speex.")); + qaHelpAboutQt=new QAction(tr("&About Qt"), this); + qaHelpAboutQt->setObjectName("HelpAboutQt"); + qaHelpAboutQt->setToolTip(tr("Information about Qt")); + qaHelpAboutQt->setWhatsThis(tr("Shows a small dialog with information about Qt.")); + qaHelpVersionCheck=new QAction(tr("Check for &Updates"), this); + qaHelpVersionCheck->setObjectName("HelpVersionCheck"); + qaHelpVersionCheck->setToolTip(tr("Check for new version of Mumble")); + qaHelpVersionCheck->setWhatsThis(tr("Connects to the Mumble webpage to check if a new version is available, and notifies " + "you with an appropriate download URL if this is the case.")); +} + +void MainWindow::setupGui() { + setWindowTitle(tr("Mumble -- %1").arg(QString(MUMBLE_RELEASE))); + + pmModel = new PlayerModel(this); + + QTreeView *view = new QTreeView(this); + qtvPlayers = view; + qtvPlayers->setObjectName("Players"); + qtvPlayers->setContextMenuPolicy(Qt::CustomContextMenu); + qtvPlayers->setModel(pmModel); + qtvPlayers->setItemDelegate(new PlayerDelegate(view)); + qtvPlayers->setDragEnabled(true); + qtvPlayers->setAcceptDrops(true); + qtvPlayers->setDropIndicatorShown(true); + qtvPlayers->setIndentation(10); + + qteLog = new QTextEdit(this); + qteLog->setReadOnly(true); + qteLog->setToolTip(tr("Log of messages")); + qteLog->setWhatsThis(tr("This shows all recent activity. Connecting to servers, errors and information messages all show up here.<br />" + "To configure exactly which messages show up here, use the <b>Settings</b> command from the menu.")); + + qmServer = new QMenu(tr("&Server"), this); + qmPlayer = new QMenu(tr("&Player"), this); + qmChannel = new QMenu(tr("&Channel"), this); + qmAudio = new QMenu(tr("&Audio"), this); + qmConfig = new QMenu(tr("C&onfigure"), this); + qmHelp = new QMenu(tr("&Help"), this); + + qmServer->setObjectName("ServerMenu"); + qmPlayer->setObjectName("PlayerMenu"); + qmChannel->setObjectName("ChannelMenu"); + qmAudio->setObjectName("AudioMenu"); + qmConfig->setObjectName("ConfigMenu"); + qmHelp->setObjectName("HelpMenu"); + + qmServer->addAction(qaServerConnect); + qmServer->addAction(qaServerDisconnect); + qmServer->addAction(qaServerBanList); + + qmPlayer->addAction(qaPlayerKick); + qmPlayer->addAction(qaPlayerBan); + qmPlayer->addAction(qaPlayerMute); + qmPlayer->addAction(qaPlayerDeaf); + qmPlayer->addAction(qaPlayerLocalMute); + + qmChannel->addAction(qaChannelAdd); + qmChannel->addAction(qaChannelRemove); + qmChannel->addAction(qaChannelACL); + qmChannel->addAction(qaChannelLink); + qmChannel->addAction(qaChannelUnlink); + qmChannel->addAction(qaChannelUnlinkAll); + + qmAudio->addAction(qaAudioMute); + qmAudio->addAction(qaAudioDeaf); + qmAudio->addSeparator(); + qmAudio->addAction(qaAudioReset); + qmAudio->addAction(qaAudioUnlink); + qmAudio->addSeparator(); + qmAudio->addAction(qaAudioTTS); + qmAudio->addSeparator(); + qmAudio->addAction(qaAudioStats); + + qmConfig->addAction(qaConfigDialog); + + qmHelp->addAction(qaHelpWhatsThis); + qmHelp->addSeparator(); + qmHelp->addAction(qaHelpAbout); + qmHelp->addAction(qaHelpAboutSpeex); + qmHelp->addAction(qaHelpAboutQt); + qmHelp->addSeparator(); + qmHelp->addAction(qaHelpVersionCheck); + + menuBar()->addMenu(qmServer); + menuBar()->addMenu(qmPlayer); + menuBar()->addMenu(qmChannel); + menuBar()->addMenu(qmAudio); + menuBar()->addMenu(qmConfig); + menuBar()->addMenu(qmHelp); + + int idx = 1; + + gsPushTalk=new GlobalShortcut(this, idx++, tr("Push-to-Talk", "Global Shortcut")); + gsPushTalk->setObjectName("PushToTalk"); + + gsResetAudio=new GlobalShortcut(this, idx++, tr("Reset Audio Processor", "Global Shortcut")); + gsResetAudio->setObjectName("ResetAudio"); + connect(gsResetAudio, SIGNAL(down()), qaAudioReset, SLOT(trigger())); + + gsMuteSelf=new GlobalShortcut(this, idx++, tr("Toggle Mute Self", "Global Shortcut")); + gsMuteSelf->setObjectName("MuteSelf"); + connect(gsMuteSelf, SIGNAL(down()), qaAudioMute, SLOT(trigger())); + + gsDeafSelf=new GlobalShortcut(this, idx++, tr("Toggle Deafen Self", "Global Shortcut")); + gsDeafSelf->setObjectName("DeafSelf"); + connect(gsDeafSelf, SIGNAL(down()), qaAudioDeaf, SLOT(trigger())); + + gsUnlink=new GlobalShortcut(this, idx++, tr("Unlink Plugin", "Global Shortcut")); + gsUnlink->setObjectName("UnlinkPlugin"); + connect(gsUnlink, SIGNAL(down()), qaAudioUnlink, SLOT(trigger())); + + gsCenterPos=new GlobalShortcut(this, idx++, tr("Force Center Position", "Global Shortcut")); + gsCenterPos->setObjectName("CenterPos"); + + GlobalShortcut *gs; + + gs = new GlobalShortcut(this, idx++, tr("Chan Parent", "Global Shortcut")); + gs->setData(0); + connect(gs, SIGNAL(triggered(bool)), this, SLOT(pushLink(bool))); + + for(int i = 1; i< 10;i++) { + gs = new GlobalShortcut(this, idx++, tr("Chan Sub#%1", "Global Shortcut").arg(i)); + gs->setData(i); + connect(gs, SIGNAL(triggered(bool)), this, SLOT(pushLink(bool))); + } + + gs = new GlobalShortcut(this, idx++, tr("Chan All Subs", "Global Shortcut")); + gs->setData(10); + connect(gs, SIGNAL(triggered(bool)), this, SLOT(pushLink(bool))); + + gsPushMute=new GlobalShortcut(this, idx++, tr("Push-to-Mute", "Global Shortcut")); + gsPushMute->setObjectName("PushToMute"); + + gsMetaChannel=new GlobalShortcut(this, idx++, tr("Join Channel", "Global Shortcut")); + gsMetaChannel->setObjectName("MetaChannel"); + +#ifdef Q_OS_WIN + gsToggleOverlay=new GlobalShortcut(this, idx++, tr("Toggle Overlay", "Global Shortcut")); + gsToggleOverlay->setObjectName("ToggleOverlay"); + connect(gsToggleOverlay, SIGNAL(down()), g.o, SLOT(toggleShow())); +#endif + + gsAltTalk=new GlobalShortcut(this, idx++, tr("Alt Push-to-Talk", "Global Shortcut")); + gsAltTalk->setObjectName("AltPushToTalk"); + + if (g.qs->value("Horizontal", true).toBool()) { + qsSplit = new QSplitter(Qt::Horizontal, this); + qsSplit->addWidget(qteLog); + qsSplit->addWidget(qtvPlayers); + } else { + qsSplit = new QSplitter(Qt::Vertical, this); + qsSplit->addWidget(qtvPlayers); + qsSplit->addWidget(qteLog); + } + setCentralWidget(qsSplit); + + restoreGeometry(g.qs->value("mwgeom").toByteArray()); + restoreState(g.qs->value("mw").toByteArray()); + qsSplit->restoreState(g.qs->value("mwSplitter").toByteArray()); + + qstiIcon = new QSystemTrayIcon(qApp->windowIcon(), this); + qstiIcon->setObjectName("Icon"); + qstiIcon->show(); + + QMetaObject::connectSlotsByName(this); +} + +void MainWindow::closeEvent(QCloseEvent *e) { + g.sId = 0; + g.qs->setValue("mwgeom", saveGeometry()); + g.qs->setValue("mw", saveState()); + g.qs->setValue("mwSplitter", qsSplit->saveState()); + QMainWindow::closeEvent(e); +} + +void MainWindow::hideEvent(QHideEvent *e) { + if (qstiIcon->isSystemTrayAvailable()) + qApp->postEvent(this, new QEvent(static_cast<QEvent::Type>(TI_QEVENT))); +} + +void MainWindow::appendLog(QString entry) +{ + qteLog->append(entry); + QTextCursor p=qteLog->textCursor(); + p.movePosition(QTextCursor::End); + qteLog->setTextCursor(p); + qteLog->ensureCursorVisible(); +} + +void MainWindow::on_Players_customContextMenuRequested(const QPoint &mpos) { + QModelIndex idx = qtvPlayers->indexAt(mpos); + if (! idx.isValid()) + idx = qtvPlayers->currentIndex(); + Player *p = pmModel->getPlayer(idx); + + if (p) { + qmPlayer->popup(qtvPlayers->mapToGlobal(mpos), qaPlayerMute); + } else { + qmChannel->popup(qtvPlayers->mapToGlobal(mpos), qaChannelACL); + } +} + +void MainWindow::on_Players_doubleClicked(const QModelIndex &idx) { + Channel *c = pmModel->getChannel(idx); + if (!c) + return; + MessagePlayerMove mpm; + mpm.sVictim = g.sId; + mpm.iChannelId = c->iId; + g.sh->sendMessage(&mpm); +} + +void MainWindow::on_ServerConnect_triggered() +{ + ConnectDialog *cd = new ConnectDialog(this); + int res = cd->exec(); + + if (g.sh && g.sh->isRunning() && res == QDialog::Accepted) + { + on_ServerDisconnect_triggered(); + g.sh->wait(); + } + + if (res == QDialog::Accepted) { + qaServerDisconnect->setEnabled(true); + g.sh->setConnectionInfo(cd->qsServer, cd->iPort, cd->qsUsername, cd->qsPassword); + g.sh->start(QThread::TimeCriticalPriority); + } + delete cd; +} + +void MainWindow::on_ServerDisconnect_triggered() +{ + qtReconnect->stop(); + g.sh->disconnect(); +} + +void MainWindow::on_ServerBanList_triggered() +{ + MessageServerBanList msbl; + msbl.bQuery = true; + g.sh->sendMessage(&msbl); + + if (banEdit) { + banEdit->reject(); + delete banEdit; + banEdit = NULL; + } +} + +void MainWindow::on_PlayerMenu_aboutToShow() +{ + Player *p = pmModel->getPlayer(qtvPlayers->currentIndex()); + if (! p) { + qaPlayerKick->setEnabled(false); + qaPlayerBan->setEnabled(false); + qaPlayerMute->setEnabled(false); + qaPlayerLocalMute->setEnabled(false); + qaPlayerDeaf->setEnabled(false); + } else { + qaPlayerKick->setEnabled(true); + qaPlayerBan->setEnabled(true); + qaPlayerMute->setEnabled(true); + qaPlayerLocalMute->setEnabled(true); + qaPlayerDeaf->setEnabled(true); + qaPlayerMute->setChecked(p->bMute); + qaPlayerLocalMute->setChecked(p->bLocalMute); + qaPlayerDeaf->setChecked(p->bDeaf); + } +} + +void MainWindow::on_PlayerMute_triggered() +{ + Player *p = pmModel->getPlayer(qtvPlayers->currentIndex()); + if (!p) + return; + + MessagePlayerMute mpmMsg; + mpmMsg.sVictim = p->sId; + mpmMsg.bMute = ! p->bMute; + g.sh->sendMessage(&mpmMsg); +} + +void MainWindow::on_PlayerLocalMute_triggered() +{ + Player *p = pmModel->getPlayer(qtvPlayers->currentIndex()); + if (!p) + return; + + p->setLocalMute(qaPlayerLocalMute->isChecked()); +} + +void MainWindow::on_PlayerDeaf_triggered() +{ + Player *p = pmModel->getPlayer(qtvPlayers->currentIndex()); + if (!p) + return; + + MessagePlayerDeaf mpdMsg; + mpdMsg.sVictim = p->sId; + mpdMsg.bDeaf = ! p->bDeaf; + g.sh->sendMessage(&mpdMsg); +} + +void MainWindow::on_PlayerKick_triggered() +{ + Player *p = pmModel->getPlayer(qtvPlayers->currentIndex()); + if (!p) + return; + + bool ok; + QString reason = QInputDialog::getText(this, tr("Kicking player %1").arg(p->qsName), tr("Enter reason"), QLineEdit::Normal, "", &ok); + if (ok) { + MessagePlayerKick mpkMsg; + mpkMsg.sVictim=p->sId; + mpkMsg.qsReason = reason; + g.sh->sendMessage(&mpkMsg); + } +} + +void MainWindow::on_PlayerBan_triggered() +{ + Player *p = pmModel->getPlayer(qtvPlayers->currentIndex()); + if (!p) + return; + + bool ok; + QString reason = QInputDialog::getText(this, tr("Banning player %1").arg(p->qsName), tr("Enter reason"), QLineEdit::Normal, "", &ok); + if (ok) { + MessagePlayerBan mpbMsg; + mpbMsg.sVictim=p->sId; + mpbMsg.qsReason = reason; + g.sh->sendMessage(&mpbMsg); + } +} + +void MainWindow::on_ChannelMenu_aboutToShow() +{ + QModelIndex idx = qtvPlayers->currentIndex(); + + bool add, remove, acl, link, unlink, unlinkall; + + add = remove = acl = link = unlink = unlinkall = false; + + if (g.sId != 0) { + add = true; + acl = true; + + Channel *c = pmModel->getChannel(idx); + Channel *home = Player::get(g.sId)->cChannel; + + if (c && c->iId != 0) + remove = true; + if (! c) + c = Channel::get(0); + + unlinkall = (home->qhLinks.count() > 0); + + if (home != c) { + if (c->allLinks().contains(home)) + unlink = true; + else + link = true; + } + } + + qaChannelAdd->setEnabled(add); + qaChannelRemove->setEnabled(remove); + qaChannelACL->setEnabled(acl); + qaChannelLink->setEnabled(link); + qaChannelUnlink->setEnabled(unlink); + qaChannelUnlinkAll->setEnabled(unlinkall); +} + +void MainWindow::on_ChannelAdd_triggered() +{ + bool ok; + Channel *c = pmModel->getChannel(qtvPlayers->currentIndex()); + int iParent = c ? c->iId : 0; + QString name = QInputDialog::getText(this, tr("Mumble"), tr("Channel Name"), QLineEdit::Normal, "", &ok); + if (ok) { + MessageChannelAdd mca; + mca.qsName = name; + mca.iParent = iParent; + g.sh->sendMessage(&mca); + } +} + +void MainWindow::on_ChannelRemove_triggered() +{ + int ret; + Channel *c = pmModel->getChannel(qtvPlayers->currentIndex()); + if (! c) + return; + + ret=QMessageBox::question(this, tr("Mumble"), tr("Are you sure you want to delete %1 and all its sub-channels?").arg(c->qsName), QMessageBox::Yes, QMessageBox::No); + + if (ret == QMessageBox::Yes ) { + MessageChannelRemove mcr; + mcr.iId = c->iId; + g.sh->sendMessage(&mcr); + } +} + +void MainWindow::on_ChannelACL_triggered() +{ + Channel *c = pmModel->getChannel(qtvPlayers->currentIndex()); + int id = c ? c->iId : 0; + + MessageEditACL mea; + mea.iId = id; + mea.bQuery = true; + g.sh->sendMessage(&mea); + + if (aclEdit) { + aclEdit->reject(); + delete aclEdit; + aclEdit = NULL; + } +} + +void MainWindow::on_ChannelLink_triggered() +{ + Channel *c = Player::get(g.sId)->cChannel; + Channel *l = pmModel->getChannel(qtvPlayers->currentIndex()); + if (! l) + l = Channel::get(0); + + MessageChannelLink mcl; + mcl.iId = c->iId; + mcl.qlTargets << l->iId; + mcl.ltType = MessageChannelLink::Link; + g.sh->sendMessage(&mcl); +} + +void MainWindow::on_ChannelUnlink_triggered() +{ + Channel *c = Player::get(g.sId)->cChannel; + Channel *l = pmModel->getChannel(qtvPlayers->currentIndex()); + if (! l) + l = Channel::get(0); + + MessageChannelLink mcl; + mcl.iId = c->iId; + mcl.qlTargets << l->iId; + mcl.ltType = MessageChannelLink::Unlink; + g.sh->sendMessage(&mcl); +} + +void MainWindow::on_ChannelUnlinkAll_triggered() +{ + Channel *c = Player::get(g.sId)->cChannel; + + MessageChannelLink mcl; + mcl.iId = c->iId; + mcl.ltType = MessageChannelLink::UnlinkAll; + g.sh->sendMessage(&mcl); +} + +void MainWindow::on_AudioReset_triggered() +{ + AudioInputPtr ai = g.ai; + if (ai) + ai->bResetProcessor = true; +} + +void MainWindow::on_AudioMute_triggered() +{ + g.s.bMute = qaAudioMute->isChecked(); + if (! g.s.bMute && g.s.bDeaf) { + g.s.bDeaf = false; + qaAudioDeaf->setChecked(false); + g.l->log(Log::SelfMute, tr("Unmuted and undeafened.")); + } else if (! g.s.bMute) { + g.l->log(Log::SelfMute, tr("Unmuted.")); + } else { + g.l->log(Log::SelfMute, tr("Muted.")); + } + + MessagePlayerSelfMuteDeaf mpsmd; + mpsmd.bMute = g.s.bMute; + mpsmd.bDeaf = g.s.bDeaf; + g.sh->sendMessage(&mpsmd); +} + +void MainWindow::on_AudioDeaf_triggered() +{ + g.s.bDeaf = qaAudioDeaf->isChecked(); + if (g.s.bDeaf && ! g.s.bMute) { + g.s.bMute = true; + qaAudioMute->setChecked(true); + g.l->log(Log::SelfMute, tr("Muted and deafened.")); + } else if (g.s.bDeaf) { + g.l->log(Log::SelfMute, tr("Deafened.")); + } else { + g.l->log(Log::SelfMute, tr("Undeafened.")); + } + + MessagePlayerSelfMuteDeaf mpsmd; + mpsmd.bMute = g.s.bMute; + mpsmd.bDeaf = g.s.bDeaf; + g.sh->sendMessage(&mpsmd); +} + +void MainWindow::on_AudioTextToSpeech_triggered() +{ + g.s.bTTS = qaAudioTTS->isChecked(); +} + +void MainWindow::on_AudioStats_triggered() +{ + AudioStats *as=new AudioStats(this); + as->show(); +} + +void MainWindow::on_AudioUnlink_triggered() +{ + g.p->bUnlink = true; +} + +void MainWindow::on_ConfigDialog_triggered() +{ + ConfigDialog dlg; + dlg.exec(); +} + +void MainWindow::on_HelpWhatsThis_triggered() +{ + QWhatsThis::enterWhatsThisMode(); +} + +void MainWindow::on_HelpAbout_triggered() +{ + AboutDialog adAbout(this); + adAbout.exec(); +} + +void MainWindow::on_HelpAboutSpeex_triggered() +{ + AboutSpeexDialog adAbout(this); + adAbout.exec(); +} + +void MainWindow::on_HelpAboutQt_triggered() +{ + QMessageBox::aboutQt(this, tr("About Qt")); +} + +void MainWindow::on_HelpVersionCheck_triggered() +{ + new VersionCheck(this); +} + +void MainWindow::on_PushToTalk_triggered(bool down) +{ + if (down) + g.iPushToTalk++; + else + g.iPushToTalk--; +} + +void MainWindow::on_PushToMute_triggered(bool down) +{ + g.bPushToMute = down; +} + +void MainWindow::on_AltPushToTalk_triggered(bool down) +{ + if (down) + g.iPushToTalk++; + else + g.iPushToTalk--; + + g.bAltSpeak = down; +} + +void MainWindow::on_CenterPos_triggered(bool down) +{ + g.bCenterPosition = down; + + if (down) + g.iPushToTalk++; + else + g.iPushToTalk--; +} + +void MainWindow::pushLink(bool down) +{ + if (down) + g.iPushToTalk++; + else + g.iPushToTalk--; + + if (g.sId == 0) + return; + + GlobalShortcut *gs = qobject_cast<GlobalShortcut *>(sender()); + int idx = gs->data().toInt(); + Channel *home = Player::get(g.sId)->cChannel; + + Channel *target = NULL; + switch(idx) { + case 0: + target = home->cParent; + break; + case 10: + break; + default: + target = pmModel->getSubChannel(home, idx-1); + break; + } + + + if (gsMetaChannel->active()) { + if (! target || ! down) + return; + + MessagePlayerMove mpm; + mpm.sVictim = g.sId; + mpm.iChannelId = target->iId; + g.sh->sendMessage(&mpm); + g.l->log(Log::Information, tr("Joining %1.").arg(target->qsName)); + } else { + MessageChannelLink mcl; + mcl.iId = home->iId; + if (down) + mcl.ltType = MessageChannelLink::PushLink; + else + mcl.ltType = MessageChannelLink::PushUnlink; + if (idx == 10) { + foreach(Channel *l, home->qlChannels) + mcl.qlTargets << l->iId; + } else if (target) { + mcl.qlTargets << target->iId; + } + if (mcl.qlTargets.count() == 0) + return; + g.sh->sendMessage(&mcl); + } +} + +void MainWindow::serverConnected() +{ + g.sId = 0; + g.l->clearIgnore(); + g.l->setIgnore(Log::PlayerJoin); + g.l->setIgnore(Log::OtherSelfMute); + g.l->log(Log::ServerConnected, tr("Connected to server.")); + qaServerDisconnect->setEnabled(true); + qaServerBanList->setEnabled(true); + + if (g.s.bMute || g.s.bDeaf) { + MessagePlayerSelfMuteDeaf mpsmd; + mpsmd.bMute = g.s.bMute; + mpsmd.bDeaf = g.s.bDeaf; + g.sh->sendMessage(&mpsmd); + } +} + +void MainWindow::serverDisconnected(QString reason) +{ + g.sId = 0; + qaServerDisconnect->setEnabled(false); + qaServerBanList->setEnabled(false); + + if (aclEdit) { + aclEdit->reject(); + delete aclEdit; + aclEdit = NULL; + } + + if (banEdit) { + banEdit->reject(); + delete banEdit; + banEdit = NULL; + } + + pmModel->removeAll(); + + if (! reason.isEmpty()) { + g.l->log(Log::ServerDisconnected, tr("Server connection failed: %1.").arg(reason)); + if (g.s.bReconnect) { + qaServerDisconnect->setEnabled(true); + qtReconnect->start(); + } + } else { + g.l->log(Log::ServerDisconnected, tr("Disconnected from server.")); + } +} + +void MainWindow::on_Reconnect_timeout() +{ + g.l->log(Log::ServerDisconnected, tr("Reconnecting.")); + g.sh->start(QThread::TimeCriticalPriority); +} + +void MainWindow::on_Icon_activated(QSystemTrayIcon::ActivationReason) { + if (! isVisible()) { + show(); + showNormal(); + } + activateWindow(); +} + + +void MainWindow::customEvent(QEvent *evt) { + if (evt->type() == TI_QEVENT) { + hide(); + return; + } + if (evt->type() != SERVERSEND_EVENT) + return; + + ServerHandlerMessageEvent *shme=static_cast<ServerHandlerMessageEvent *>(evt); + + Message *mMsg = Message::networkToMessage(shme->qbaMsg); + if (mMsg) { + mMsg->process(NULL); + delete mMsg; + } +} + +void MessageServerJoin::process(Connection *) { + Player *p = g.mw->pmModel->addPlayer(sPlayerId, qsPlayerName); + p->iId = iId; + g.l->log(Log::PlayerJoin, MainWindow::tr("Joined server: %1.").arg(p->qsName)); +} + +#define MSG_INIT \ + Player *pSrc=Player::get(sPlayerId); \ + Q_UNUSED(pSrc); + +#define VICTIM_INIT \ + Player *pDst=Player::get(sVictim); \ + if (! pDst) \ + qFatal("MainWindow: Message for nonexistant victim %d.", sVictim); + +void MessageServerLeave::process(Connection *) { + MSG_INIT; + + g.l->log(Log::PlayerLeave, MainWindow::tr("Left server: %1.").arg(pSrc->qsName)); + g.mw->pmModel->removePlayer(pSrc); +} + +void MessageServerBanList::process(Connection *) { + MSG_INIT; + + if (g.mw->banEdit) { + g.mw->banEdit->reject(); + delete g.mw->banEdit; + g.mw->banEdit = NULL; + } + g.mw->banEdit = new BanEditor(this, g.mw); + g.mw->banEdit->show(); + +} + +void MessageSpeex::process(Connection *) { +} + +void MessageMultiSpeex::process(Connection *) { +} + +void MessagePlayerSelfMuteDeaf::process(Connection *) { + MSG_INIT; + + pSrc->setSelfMuteDeaf(bMute, bDeaf); + + if (sPlayerId == g.sId || ! g.sId) + return; + if (pSrc->cChannel != Player::get(g.sId)->cChannel) + return; + + QString name = pSrc->qsName; + if (bMute && bDeaf) + g.l->log(Log::OtherSelfMute, MainWindow::tr("%1 is now muted and deafened.").arg(name)); + else if (bMute) + g.l->log(Log::OtherSelfMute, MainWindow::tr("%1 is now muted.").arg(name)); + else + g.l->log(Log::OtherSelfMute, MainWindow::tr("%1 is now unmuted.").arg(name)); +} + +void MessagePlayerMute::process(Connection *) { + MSG_INIT; + VICTIM_INIT; + + pDst->setMute(bMute); + + if (!g.sId || pDst->cChannel != Player::get(g.sId)->cChannel) + return; + + QString vic = pDst->qsName; + QString admin = pSrc ? pSrc->qsName : MainWindow::tr("server"); + + if (sVictim == g.sId) + g.l->log(Log::YouMuted, bMute ? MainWindow::tr("You were muted by %1.").arg(admin) : MainWindow::tr("You were unmuted by %1.").arg(admin)); + else + g.l->log((sPlayerId == g.sId) ? Log::YouMutedOther : Log::OtherMutedOther, bMute ? MainWindow::tr("%1 muted by %2.").arg(vic).arg(admin) : MainWindow::tr("%1 unmuted by %2.").arg(vic).arg(admin), QString()); +} + +void MessagePlayerDeaf::process(Connection *) { + MSG_INIT; + VICTIM_INIT; + + pDst->setDeaf(bDeaf); + + if (!g.sId || pDst->cChannel != Player::get(g.sId)->cChannel) + return; + + QString vic = pDst->qsName; + QString admin = pSrc ? pSrc->qsName : MainWindow::tr("server"); + + if (sVictim == g.sId) + g.l->log(Log::YouMuted, bDeaf ? MainWindow::tr("You were deafened by %1.").arg(admin) : MainWindow::tr("You were undeafened by %1.").arg(admin)); + else + g.l->log((sPlayerId == g.sId) ? Log::YouMutedOther : Log::OtherMutedOther, bDeaf ? MainWindow::tr("%1 deafened by %2.").arg(vic).arg(admin) : MainWindow::tr("%1 undeafened by %2.").arg(vic).arg(admin), QString()); +} + +void MessagePlayerKick::process(Connection *) { + MSG_INIT; + VICTIM_INIT; + if (sVictim == g.sId) { + g.l->log(Log::YouKicked, MainWindow::tr("You were kicked from the server by %1: %2.").arg(pSrc->qsName).arg(qsReason)); + g.l->setIgnore(Log::ServerDisconnected, 1); + } else { + g.l->setIgnore(Log::PlayerLeave, 1); + g.l->log((sPlayerId == g.sId) ? Log::YouKicked : Log::PlayerKicked, MainWindow::tr("%3 was kicked from the server by %1: %2.").arg(pSrc->qsName).arg(qsReason).arg(pDst->qsName)); + } +} + +void MessagePlayerBan::process(Connection *) { + MSG_INIT; + VICTIM_INIT; + if (sVictim == g.sId) { + g.l->log(Log::YouKicked, MainWindow::tr("You were kicked and banned from the server by %1: %2.").arg(pSrc->qsName).arg(qsReason)); + g.l->setIgnore(Log::ServerDisconnected, 1); + } else { + g.l->setIgnore(Log::PlayerLeave, 1); + g.l->log((sPlayerId == g.sId) ? Log::YouKicked : Log::PlayerKicked, MainWindow::tr("%3 was kicked and banned from the server by %1: %2.").arg(pSrc->qsName).arg(qsReason).arg(pDst->qsName)); + } +} + +void MessagePlayerMove::process(Connection *) { + MSG_INIT; + VICTIM_INIT; + + bool log = true; + if ((sVictim == g.sId) && (sPlayerId == sVictim)) + log = false; + if (g.sId == 0) + log = false; + + QString pname = pDst->qsName; + QString admin = pSrc ? pSrc->qsName : "server"; + + if (log && (pDst->cChannel == Player::get(g.sId)->cChannel)) { + if (pDst == pSrc || (!pSrc)) + g.l->log(Log::ChannelJoin, MainWindow::tr("%1 left channel.").arg(pname)); + else + g.l->log(Log::ChannelJoin, MainWindow::tr("%1 moved out by %2.").arg(pname).arg(admin)); + } + + g.mw->pmModel->movePlayer(pDst, iChannelId); + + if (log && (pDst->cChannel == Player::get(g.sId)->cChannel)) { + if (pDst == pSrc || (!pSrc)) + g.l->log(Log::ChannelLeave, MainWindow::tr("%1 entered channel.").arg(pname)); + else + g.l->log(Log::ChannelLeave, MainWindow::tr("%1 moved in by %2.").arg(pname).arg(admin)); + } +} + +void MessagePlayerRename::process(Connection *) { + MSG_INIT; + g.mw->pmModel->renamePlayer(pSrc, qsName); +} + +void MessageChannelAdd::process(Connection *) { + Channel *p = Channel::get(iParent); + if (p) + g.mw->pmModel->addChannel(iId, p, qsName); +} + +void MessageChannelRemove::process(Connection *) { + Channel *c = Channel::get(iId); + if (c) + g.mw->pmModel->removeChannel(c); +} + +void MessageChannelMove::process(Connection *) { + g.mw->pmModel->moveChannel(Channel::get(iId), iParent); +} + +void MessageChannelLink::process(Connection *) { + Channel *c = Channel::get(iId); + QList<Channel *> qlChans; + foreach(int id, qlTargets) { + Channel *l = Channel::get(id); + qlChans << l; + } + + switch (ltType) { + case Link: + g.mw->pmModel->linkChannels(c, qlChans); + break; + case Unlink: + g.mw->pmModel->unlinkChannels(c, qlChans); + break; + case UnlinkAll: + g.mw->pmModel->unlinkAll(c); + break; + default: + qFatal("Unknown link message"); + } +} + +void MessageServerAuthenticate::process(Connection *) { +} + +void MessageServerReject::process(Connection *) { + g.l->log(Log::ServerDisconnected, MainWindow::tr("Server connection rejected: %1.").arg(qsReason)); + g.l->setIgnore(Log::ServerDisconnected, 1); +} + +void MessagePermissionDenied::process(Connection *) { + g.l->log(Log::PermissionDenied, MainWindow::tr("Denied: %1.").arg(qsReason)); +} + +void MessageServerSync::process(Connection *) { + MSG_INIT; + g.iMaxBandwidth = iMaxBandwidth; + g.sId = sPlayerId; + g.l->clearIgnore(); + g.l->log(Log::Information, qsWelcomeText, QString("")); + g.mw->pmModel->ensureSelfVisible(); +} + +void MessageEditACL::process(Connection *) { + if (g.mw->aclEdit) { + g.mw->aclEdit->reject(); + delete g.mw->aclEdit; + g.mw->aclEdit = NULL; + } + g.mw->aclEdit = new ACLEditor(this, g.mw); + g.mw->aclEdit->show(); +} + +void MessageQueryUsers::process(Connection *) { + if (g.mw->aclEdit) + g.mw->aclEdit->returnQuery(this); +} + +void MessagePing::process(Connection *) { +} diff --git a/src/mumble/MainWindow.h b/src/mumble/MainWindow.h new file mode 100644 index 000000000..304b0e0ed --- /dev/null +++ b/src/mumble/MainWindow.h @@ -0,0 +1,122 @@ +/* Copyright (C) 2005-2006, 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 _MAINWINDOW_H +#define _MAINWINDOW_H + +#define TI_QEVENT (QEvent::User + 938) + +class ACLEditor; +class BanEditor; +class ServerHandler; +class GlobalShortcut; +class TextToSpeech; +class PlayerModel; + +class MainWindow : public QMainWindow { + friend class PlayerModel; + Q_OBJECT + public: + PlayerModel *pmModel; + QMenu *qmServer, *qmPlayer, *qmChannel, *qmAudio, *qmConfig, *qmHelp; + QAction *qaServerConnect, *qaServerDisconnect, *qaServerBanList; + QAction *qaPlayerKick, *qaPlayerBan, *qaPlayerMute, *qaPlayerDeaf, *qaPlayerLocalMute; + QAction *qaAudioReset, *qaAudioMute, *qaAudioDeaf, *qaAudioTTS, *qaAudioStats, *qaAudioUnlink; + QAction *qaConfigDialog; + QAction *qaHelpWhatsThis, *qaHelpAbout, *qaHelpAboutSpeex, *qaHelpAboutQt, *qaHelpVersionCheck; + QAction *qaChannelAdd, *qaChannelRemove, *qaChannelACL, *qaChannelLink, *qaChannelUnlink, *qaChannelUnlinkAll; + QSplitter *qsSplit; + QSystemTrayIcon *qstiIcon; + QTextEdit *qteLog; + QTreeView *qtvPlayers; + + GlobalShortcut *gsPushTalk, *gsResetAudio, *gsMuteSelf, *gsDeafSelf; + GlobalShortcut *gsUnlink, *gsCenterPos, *gsPushMute, *gsMetaChannel, *gsToggleOverlay; + GlobalShortcut *gsAltTalk; + + ACLEditor *aclEdit; + BanEditor *banEdit; + + void recheckTTS(); + void appendLog(QString entry); + protected: + QTimer *qtReconnect; + void createActions(); + void setupGui(); + void customEvent(QEvent *evt); + virtual void closeEvent(QCloseEvent *e); + virtual void hideEvent (QHideEvent *e); + public slots: + void on_Players_customContextMenuRequested(const QPoint &pos); + void on_Players_doubleClicked(const QModelIndex &idx); + void on_ServerConnect_triggered(); + void on_ServerDisconnect_triggered(); + void on_ServerBanList_triggered(); + void on_PlayerMenu_aboutToShow(); + void on_PlayerKick_triggered(); + void on_PlayerBan_triggered(); + void on_PlayerMute_triggered(); + void on_PlayerDeaf_triggered(); + void on_PlayerLocalMute_triggered(); + void on_ChannelMenu_aboutToShow(); + void on_ChannelAdd_triggered(); + void on_ChannelRemove_triggered(); + void on_ChannelACL_triggered(); + void on_ChannelLink_triggered(); + void on_ChannelUnlink_triggered(); + void on_ChannelUnlinkAll_triggered(); + void on_AudioReset_triggered(); + void on_AudioMute_triggered(); + void on_AudioDeaf_triggered(); + void on_AudioTextToSpeech_triggered(); + void on_AudioUnlink_triggered(); + void on_AudioStats_triggered(); + void on_ConfigDialog_triggered(); + void on_HelpWhatsThis_triggered(); + void on_HelpAbout_triggered(); + void on_HelpAboutSpeex_triggered(); + void on_HelpAboutQt_triggered(); + void on_HelpVersionCheck_triggered(); + void on_PushToTalk_triggered(bool); + void on_PushToMute_triggered(bool); + void on_AltPushToTalk_triggered(bool); + void on_CenterPos_triggered(bool); + void on_Reconnect_timeout(); + void on_Icon_activated(QSystemTrayIcon::ActivationReason); + void serverConnected(); + void serverDisconnected(QString reason); + void pushLink(bool down); + public: + MainWindow(QWidget *parent); +}; + +#else +class MainWindow; +#endif diff --git a/src/mumble/Overlay.h b/src/mumble/Overlay.h new file mode 100644 index 000000000..4d218644e --- /dev/null +++ b/src/mumble/Overlay.h @@ -0,0 +1,97 @@ +/* Copyright (C) 2005-2006, 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 _OVERLAY_H +#define _OVERLAY_H + +#include "../../overlay/overlay.h" +#include "ConfigDialog.h" + +class Player; + +class OverlayConfig : public ConfigWidget { + Q_OBJECT + protected: + QCheckBox *qcbEnable, *qcbAlwaysSelf; + QComboBox *qcbShow; + + QSlider *qsX, *qsY; + QCheckBox *qcbLeft, *qcbRight, *qcbTop, *qcbBottom; + QLabel *qlCurrentFont; + + QFont qfFont; + QPushButton *qpbSetFont; + QSlider *qsMaxWidth; + QLabel *qlMaxWidth; + + QColor qcPlayer, qcAltTalking, qcTalking, qcChannel, qcChannelTalking; + + QLabel *qlPlayer, *qlTalking, *qlAltTalking, *qlChannel, *qlChannelTalking; + QPushButton *qpbPlayer, *qpbTalking, *qpbAltTalking, *qpbChannel, *qpbChannelTalking; + + static void setColorLabel(QLabel *label, QColor col); + public: + OverlayConfig(QWidget *p = NULL); + virtual QString title() const; + virtual QIcon icon() const; + public slots: + void on_MaxWidth_valueChanged(int v); + void on_SetFont_clicked(); + void on_Player_clicked(); + void on_Talking_clicked(); + void on_AltTalking_clicked(); + void on_Channel_clicked(); + void on_ChannelTalking_clicked(); + void accept(); +}; + +class Overlay : public QObject { + friend class OverlayConfig; + Q_OBJECT + protected: + typedef void (__cdecl *HooksProc)(); + QLibrary *qlOverlay; + QTimer *qtTimer; + HANDLE hMutex; + HooksProc hpInstall, hpRemove; + SharedMem *sm; + public: + Overlay(); + ~Overlay(); + bool isActive() const; + public slots: + void on_Timer_timeout(); + void updateOverlay(); + void setActive(bool act); + void toggleShow(); + void forceSettings(); +}; + +#endif diff --git a/src/mumble/Overlay_win.cpp b/src/mumble/Overlay_win.cpp new file mode 100644 index 000000000..868e6085f --- /dev/null +++ b/src/mumble/Overlay_win.cpp @@ -0,0 +1,565 @@ +/* Copyright (C) 2005-2006, 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 "Player.h" +#include "Channel.h" +#include "Overlay.h" +#include "Global.h" + +static ConfigWidget *OverlayConfigDialogNew() { + return new OverlayConfig(); +} + +static ConfigRegistrar registrar(60, OverlayConfigDialogNew); + +OverlayConfig::OverlayConfig(QWidget *p) : ConfigWidget(p) { + QGroupBox *qgbOptions = new QGroupBox(tr("Options")); + QGroupBox *qgbPosition = new QGroupBox(tr("Position")); + QGroupBox *qgbColors = new QGroupBox(tr("Font")); + QLabel *lab; + QVBoxLayout *v; + QGridLayout *grid; + + grid=new QGridLayout(); + + qcbEnable = new QCheckBox(tr("Enable Overlay")); + qcbEnable->setObjectName("Enable"); + qcbEnable->setToolTip(tr("Enable overlay.")); + qcbEnable->setWhatsThis(tr("This sets whether the overlay is enabled or not. This setting is " + "only checked with D3D9 applications are started, so make sure Mumble " + "is running and this option is on before you start the application.<br />" + "Please note that if you start the application after starting Mumble, " + "or if you disable the overlay while running, there is no safe way " + "to restart the overlay without also restarting the application.")); + qcbEnable->setChecked(g.s.bOverlayEnable); + grid->addWidget(qcbEnable, 0, 0, 1, 2); + + qcbShow = new QComboBox(); + qcbShow->addItem(tr("No one"), Settings::Nothing); + qcbShow->addItem(tr("Only talking"), Settings::Talking); + qcbShow->addItem(tr("Everyone"), Settings::All); + qcbShow->setCurrentIndex(g.s.osOverlay); + lab = new QLabel(tr("Show")); + lab->setBuddy(qcbShow); + qcbShow->setToolTip(tr("Who to show on the overlay")); + qcbShow->setWhatsThis(tr("<b>This sets who to show in the in-game overlay.</b><br />" + "If many people are connected to the same channel, the overlay list might be " + "very long. Use this to shorten it.<br />" + "<i>No one</i> - Don't show anyone (but leave overlay running).<br />" + "<i>Only talking</i> - Only show talking people.<br />" + "<i>Everyone</i> - Show everyone.")); + grid->addWidget(lab, 1, 0); + grid->addWidget(qcbShow, 1, 1, 1, 2); + + qcbAlwaysSelf = new QCheckBox(tr("Always Show Self")); + qcbAlwaysSelf->setObjectName("AlwaysSelf"); + qcbAlwaysSelf->setToolTip(tr("Always show yourself on overlay.")); + qcbAlwaysSelf->setWhatsThis(tr("This sets whether to always show yourself or not. " + "This setting is useful if you aren't showing everyone in the overlay, " + "as then you would only see your own status if you were talking, which " + "wouldn't let you see that you were deafened or muted.")); + qcbAlwaysSelf->setChecked(g.s.bOverlayAlwaysSelf); + grid->addWidget(qcbAlwaysSelf, 2, 0, 1, 2); + + qgbOptions->setLayout(grid); + + grid=new QGridLayout(); + + qcbLeft = new QCheckBox(tr("Grow Left")); + qcbLeft->setObjectName("Left"); + qcbLeft->setToolTip(tr("Let overlay grow to the left")); + qcbLeft->setWhatsThis(tr("The overlay tries to stay as small as possible and at the position " + "you have selected. This allows the overlay to grow to the left if " + "needed.")); + qcbLeft->setChecked(g.s.bOverlayLeft); + qcbRight = new QCheckBox(tr("Grow Right")); + qcbRight->setObjectName("Right"); + qcbRight->setToolTip(tr("Let overlay grow to the Right")); + qcbRight->setWhatsThis(tr("The overlay tries to stay as small as possible and at the position " + "you have selected. This allows the overlay to grow to the Right if " + "needed.")); + qcbRight->setChecked(g.s.bOverlayRight); + qcbTop = new QCheckBox(tr("Grow Up")); + qcbTop->setObjectName("Top"); + qcbTop->setToolTip(tr("Let overlay grow upwards")); + qcbTop->setWhatsThis(tr("The overlay tries to stay as small as possible and at the position " + "you have selected. This allows the overlay to grow to the top if " + "needed.")); + qcbTop->setChecked(g.s.bOverlayTop); + qcbBottom = new QCheckBox(tr("Grow Down")); + qcbBottom->setObjectName("Bottom"); + qcbBottom->setToolTip(tr("Let overlay grow downwards")); + qcbBottom->setWhatsThis(tr("The overlay tries to stay as small as possible and at the position " + "you have selected. This allows the overlay to grow towards the bottom if " + "needed.")); + qcbBottom->setChecked(g.s.bOverlayBottom); + + qsX = new QSlider(Qt::Horizontal); + qsX->setObjectName("X"); + qsX->setToolTip(tr("X-Position of Overlay")); + qsX->setWhatsThis(tr("This sets the relative X position of the overlay.")); + qsX->setRange(0, 100); + qsX->setSingleStep(1); + qsX->setPageStep(10); + qsX->setValue(qRound64(g.s.fOverlayX * 100)); + + qsY = new QSlider(Qt::Vertical); + qsY->setObjectName("Y"); + qsY->setToolTip(tr("Y-Position of Overlay")); + qsY->setWhatsThis(tr("This sets the relative Y position of the overlay.")); + qsY->setRange(0, 100); + qsY->setSingleStep(1); + qsY->setPageStep(10); + qsY->setValue(100 - qRound64(g.s.fOverlayY * 100)); + + grid->addWidget(qcbLeft, 1, 0); + grid->addWidget(qcbTop, 0, 1); + grid->addWidget(qcbBottom, 2, 1); + grid->addWidget(qcbRight, 1, 2); + + grid->addWidget(qsX, 3, 0, 1, 3); + grid->addWidget(qsY, 0, 3, 3, 1); + + qgbPosition->setLayout(grid); + + grid = new QGridLayout(); + + qfFont = g.s.qfOverlayFont; + qcPlayer = g.s.qcOverlayPlayer; + qcTalking= g.s.qcOverlayTalking; + qcAltTalking= g.s.qcOverlayAltTalking; + qcChannel = g.s.qcOverlayChannel; + qcChannelTalking = g.s.qcOverlayChannelTalking; + + lab=new QLabel(tr("Current Font")); + qlCurrentFont = new QLabel(); + qlCurrentFont->setText(qfFont.family()); + + qpbSetFont = new QPushButton(tr("Set Font")); + qpbSetFont->setObjectName("SetFont"); + + grid->addWidget(lab, 0, 0); + grid->addWidget(qlCurrentFont, 0, 1); + grid->addWidget(qpbSetFont, 0, 2); + + qsMaxWidth = new QSlider(Qt::Horizontal); + qsMaxWidth->setObjectName("MaxWidth"); + qsMaxWidth->setToolTip(tr("Maximum width of names.")); + qsMaxWidth->setWhatsThis(tr("This sets the maximum width of names shown, relative to the height. If you set this to 100%, no line " + "of text will be wider than it is high. At 500%, no line will be more than 5 times wider than its height, " + "and so on.")); + qsMaxWidth->setRange(100, 5000); + qsMaxWidth->setSingleStep(10); + qsMaxWidth->setPageStep(50); + qsMaxWidth->setValue(qRound64(g.s.fOverlayWidth * 100)); + + qlMaxWidth=new QLabel(); + qlMaxWidth->setMinimumWidth(40); + on_MaxWidth_valueChanged(qsMaxWidth->value()); + + lab = new QLabel(tr("Maximum width")); + lab->setBuddy(qsMaxWidth); + + grid->addWidget(lab, 1, 0); + grid->addWidget(qsMaxWidth, 1, 1); + grid->addWidget(qlMaxWidth, 1, 2); + + qlPlayer = new QLabel(); + qpbPlayer = new QPushButton(tr("Change")); + qpbPlayer->setObjectName("Player"); + lab = new QLabel(tr("Color for players")); + setColorLabel(qlPlayer, qcPlayer); + + grid->addWidget(lab, 2, 0); + grid->addWidget(qlPlayer, 2, 1); + grid->addWidget(qpbPlayer, 2, 2); + + qlTalking = new QLabel(); + qpbTalking = new QPushButton(tr("Change")); + qpbTalking->setObjectName("Talking"); + lab = new QLabel(tr("Color for talking players")); + setColorLabel(qlTalking, qcTalking); + + grid->addWidget(lab, 3, 0); + grid->addWidget(qlTalking, 3, 1); + grid->addWidget(qpbTalking, 3, 2); + + qlAltTalking = new QLabel(); + qpbAltTalking = new QPushButton(tr("Change")); + qpbAltTalking->setObjectName("AltTalking"); + lab = new QLabel(tr("Color for alt-talking players")); + setColorLabel(qlAltTalking, qcAltTalking); + + grid->addWidget(lab, 4, 0); + grid->addWidget(qlAltTalking, 4, 1); + grid->addWidget(qpbAltTalking, 4, 2); + + qlChannel = new QLabel(); + qpbChannel = new QPushButton(tr("Change")); + qpbChannel->setObjectName("Channel"); + lab = new QLabel(tr("Color for Channels")); + setColorLabel(qlChannel, qcChannel); + + grid->addWidget(lab, 5, 0); + grid->addWidget(qlChannel, 5, 1); + grid->addWidget(qpbChannel, 5, 2); + + qlChannelTalking = new QLabel(); + qpbChannelTalking = new QPushButton(tr("Change")); + qpbChannelTalking->setObjectName("ChannelTalking"); + lab = new QLabel(tr("Color for active Channels")); + setColorLabel(qlChannelTalking, qcChannelTalking); + + grid->addWidget(lab, 6, 0); + grid->addWidget(qlChannelTalking, 6, 1); + grid->addWidget(qpbChannelTalking, 6, 2); + + qgbColors->setLayout(grid); + + v = new QVBoxLayout; + v->addWidget(qgbOptions); + v->addWidget(qgbPosition); + v->addWidget(qgbColors); + v->addStretch(1); + setLayout(v); + + QMetaObject::connectSlotsByName(this); +} + +void OverlayConfig::setColorLabel(QLabel *label, QColor col) { + label->setText(col.name()); + + QPalette palette; + palette.setColor(label->foregroundRole(), col); + label->setPalette(palette); +} + +void OverlayConfig::on_SetFont_clicked() { + bool ok; + QFont qf = QFontDialog::getFont(&ok, qfFont, this); + if (ok) { + qfFont = qf; + qlCurrentFont->setText(qfFont.family()); + } +} + +void OverlayConfig::on_Player_clicked() { + bool ok; + QRgb rgb=QColorDialog::getRgba(qcPlayer.rgba(), &ok, this); + if (ok) { + qcPlayer = QColor::fromRgba(rgb); + setColorLabel(qlPlayer, qcPlayer); + } +} + +void OverlayConfig::on_Talking_clicked() { + bool ok; + QRgb rgb=QColorDialog::getRgba(qcTalking.rgba(), &ok, this); + if (ok) { + qcTalking = QColor::fromRgba(rgb); + setColorLabel(qlTalking, qcTalking); + } +} + +void OverlayConfig::on_AltTalking_clicked() { + bool ok; + QRgb rgb=QColorDialog::getRgba(qcAltTalking.rgba(), &ok, this); + if (ok) { + qcAltTalking = QColor::fromRgba(rgb); + setColorLabel(qlAltTalking, qcAltTalking); + } +} + +void OverlayConfig::on_Channel_clicked() { + bool ok; + QRgb rgb=QColorDialog::getRgba(qcChannel.rgba(), &ok, this); + if (ok) { + qcChannel = QColor::fromRgba(rgb); + setColorLabel(qlChannel, qcChannel); + } +} + +void OverlayConfig::on_ChannelTalking_clicked() { + bool ok; + QRgb rgb=QColorDialog::getRgba(qcChannelTalking.rgba(), &ok, this); + if (ok) { + qcChannelTalking = QColor::fromRgba(rgb); + setColorLabel(qlChannelTalking, qcChannelTalking); + } +} + +void OverlayConfig::on_MaxWidth_valueChanged(int v) { + qlMaxWidth->setText(QString("%1%").arg(v)); +} + +QString OverlayConfig::title() const { + return tr("Overlay"); +} + +QIcon OverlayConfig::icon() const { + return QIcon(":/icons/config_osd.png"); +} + +void OverlayConfig::accept() { + g.s.bOverlayEnable = qcbEnable->isChecked(); + g.s.osOverlay = static_cast<Settings::OverlayShow>(qcbShow->currentIndex()); + g.s.bOverlayAlwaysSelf = qcbAlwaysSelf->isChecked(); + g.s.fOverlayX = qsX->value() / 100.0; + g.s.fOverlayY = 1.0 - qsY->value() / 100.0; + g.s.bOverlayLeft = qcbLeft->isChecked(); + g.s.bOverlayRight = qcbRight->isChecked(); + g.s.bOverlayTop = qcbTop->isChecked(); + g.s.bOverlayBottom = qcbBottom->isChecked(); + g.s.qfOverlayFont = qfFont; + g.s.fOverlayWidth = qsMaxWidth->value() / 100.0; + g.s.qcOverlayPlayer = qcPlayer; + g.s.qcOverlayTalking = qcTalking; + g.s.qcOverlayAltTalking = qcAltTalking; + g.s.qcOverlayChannel = qcChannel; + g.s.qcOverlayChannelTalking = qcChannelTalking; + + g.o->forceSettings(); + g.o->setActive(g.s.bOverlayEnable); +} + +Overlay::Overlay() : QObject() { + qlOverlay = new QLibrary(this); + +#ifndef QT_NO_DEBUG + QString path="../overlay/mumble_ol.dll"; +#else + QString path=QString("%1/mumble_ol.dll").arg(qApp->applicationDirPath()); +#endif + + qlOverlay->setFileName(path); + if (! qlOverlay->load()) { + QMessageBox::critical(NULL, tr("Mumble"), tr("Failed to load overlay library. This means either that:\n" + "- the library (mumble_ol.dll) wasn't found in the directory you ran Mumble from\n" + "- you're on an OS earlier than WinXP SP2\n" + "- you do not have the August 2006 updated version of DX9.0c"), QMessageBox::Ok, QMessageBox::NoButton); + qWarning("Overlay failure"); + } + + sm=reinterpret_cast<SharedMem *>(qlOverlay->resolve("sm")); +#ifndef QT_NO_DEBUG + if (sm) + sm->bDebug = true; +#endif + + hpInstall = (HooksProc)qlOverlay->resolve("InstallHooks"); + hpRemove = (HooksProc)qlOverlay->resolve("RemoveHooks"); + + qtTimer=new QTimer(this); + qtTimer->setObjectName("Timer"); + qtTimer->start(1000); + + hMutex = CreateMutex(NULL, false, L"MumbleSharedMutex"); + + forceSettings(); + + QMetaObject::connectSlotsByName(this); +} + +Overlay::~Overlay() { + setActive(false); + qlOverlay->unload(); +} + +bool Overlay::isActive() const { + if (! sm) + return false; + + return sm->bHooked; +} + +void Overlay::setActive(bool act) { + if (! sm) + return; + + if (act) + hpInstall(); + else + hpRemove(); +} + +void Overlay::on_Timer_timeout() { + if (sm) + sm->lastAppAlive = GetTickCount(); +} + +void Overlay::toggleShow() { + Settings::OverlayShow ns; + + switch(g.s.osOverlay) { + case Settings::Nothing: + ns = Settings::All; + break; + case Settings::All: + ns = Settings::Talking; + break; + default: + ns = Settings::All; + break; + } + g.s.osOverlay = ns; + + DWORD dwWaitResult = WaitForSingleObject(hMutex, 500L); + if (dwWaitResult == WAIT_OBJECT_0) { + sm->bShow = (g.s.osOverlay != Settings::Nothing); + ReleaseMutex(hMutex); + } + updateOverlay(); +} + +void Overlay::forceSettings() { + QString str; + const wchar_t *wstr; + + if (! sm) + return; + + DWORD dwWaitResult = WaitForSingleObject(hMutex, 500L); + if (dwWaitResult == WAIT_OBJECT_0) { + str = g.s.qfOverlayFont.family(); + wstr = reinterpret_cast<const wchar_t *>(str.utf16()); + wcscpy(sm->strFontname, wstr); + sm->iFontSize = g.s.qfOverlayFont.pointSize(); + sm->bFontBold = g.s.qfOverlayFont.bold(); + sm->bFontItalic = g.s.qfOverlayFont.italic(); + sm->fWidthFactor = g.s.fOverlayWidth; + sm->fX = g.s.fOverlayX; + sm->fY = g.s.fOverlayY; + sm->bTop = g.s.bOverlayTop; + sm->bBottom = g.s.bOverlayBottom; + sm->bLeft = g.s.bOverlayLeft; + sm->bRight = g.s.bOverlayRight; + sm->bReset = true; + sm->bShow = (g.s.osOverlay != Settings::Nothing); + ReleaseMutex(hMutex); + } + updateOverlay(); +} + +#define SAFE_INC_IDX(x) x=(x < NUM_TEXTS) ? (x+1) : (NUM_TEXTS-1) + +typedef QPair<QString, DWORD> qpChanCol; + +void Overlay::updateOverlay() { + DWORD colPlayer = g.s.qcOverlayPlayer.rgba(); + DWORD colTalking = g.s.qcOverlayTalking.rgba(); + DWORD colAltTalking = g.s.qcOverlayAltTalking.rgba(); + DWORD colChannel = g.s.qcOverlayChannel.rgba(); + DWORD colChannelTalking = g.s.qcOverlayChannelTalking.rgba(); + QString str; + QList<qpChanCol> linkchans; + const wchar_t *wstr; + + if (! sm) + return; + + if (g.sId) { + Channel *home = Player::get(g.sId)->cChannel; + foreach(Channel *c, home->allLinks()) { + if (home == c) + continue; + + bool act = false; + foreach(Player *p, c->qlPlayers) { + act = act || p->bTalking; + if (p->bTalking) + linkchans << qpChanCol(p->qsName + QString("[") + c->qsName + QString("]"), colChannelTalking); + } + if (! act) + linkchans << qpChanCol(c->qsName, colChannel); + } + qSort(linkchans); + } + + DWORD dwWaitResult = WaitForSingleObject(hMutex, 500L); + if (dwWaitResult == WAIT_OBJECT_0) { + int idx = 0; + + if (g.sId) { + + if (g.s.bOverlayTop) { + foreach(qpChanCol cc, linkchans) { + if ((g.s.osOverlay == Settings::All) || (cc.second == colChannelTalking)) { + sm->texts[idx].color = cc.second; + str = cc.first.left(127); + wstr = reinterpret_cast<const wchar_t *>(str.utf16()); + wcscpy(sm->texts[idx].text, wstr); + SAFE_INC_IDX(idx); + } + } + if (linkchans.count() > 0) { + sm->texts[idx].text[0] = L' '; + sm->texts[idx].text[1] = 0; + SAFE_INC_IDX(idx); + } + } + + foreach(Player *p, Player::get(g.sId)->cChannel->qlPlayers) { + if ((g.s.osOverlay == Settings::All) || p->bTalking || ((p == Player::get(g.sId)) && g.s.bOverlayAlwaysSelf)) { + QString name = p->qsName; + if (p->bDeaf || p->bSelfDeaf) + name = name + QString("(D)"); + else if (p->bMute || p->bSelfMute || p->bLocalMute) + name = name + QString("(M)"); + sm->texts[idx].color = p->bTalking ? (p->bAltSpeak ? colAltTalking : colTalking) : colPlayer; + str = name.left(127); + wstr = reinterpret_cast<const wchar_t *>(str.utf16()); + wcscpy(sm->texts[idx].text, wstr); + SAFE_INC_IDX(idx); + } + } + + if (! g.s.bOverlayTop) { + if (linkchans.count() > 0) { + sm->texts[idx].text[0] = L' '; + sm->texts[idx].text[1] = 0; + SAFE_INC_IDX(idx); + } + foreach(qpChanCol cc, linkchans) { + if ((g.s.osOverlay == Settings::All) || (cc.second == colChannelTalking)) { + sm->texts[idx].color = cc.second; + str = cc.first.left(127); + wstr = reinterpret_cast<const wchar_t *>(str.utf16()); + wcscpy(sm->texts[idx].text, wstr); + SAFE_INC_IDX(idx); + } + } + } + } + + for(; idx<NUM_TEXTS;idx++) + sm->texts[idx].text[0] = 0; + ReleaseMutex(hMutex); + } +} diff --git a/src/mumble/PlayerModel.cpp b/src/mumble/PlayerModel.cpp new file mode 100644 index 000000000..25b3a9cf1 --- /dev/null +++ b/src/mumble/PlayerModel.cpp @@ -0,0 +1,817 @@ +/* Copyright (C) 2005-2006, 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 "PlayerModel.h" +#include "MainWindow.h" +#include "Message.h" +#include "ServerHandler.h" +#include "Channel.h" +#include "Player.h" +#include "Global.h" +#ifdef Q_OS_WIN +#include "Overlay.h" +#endif + +QHash <Channel *, ModelItem *> ModelItem::c_qhChannels; +QHash <Player *, ModelItem *> ModelItem::c_qhPlayers; + +ModelItem::ModelItem(Channel *c) { + this->cChan = c; + this->pPlayer = NULL; + c_qhChannels[c] = this; +} + +ModelItem::ModelItem(Player *p) { + this->cChan = NULL; + this->pPlayer = p; + c_qhPlayers[p] = this; +} + +ModelItem::~ModelItem() { + Q_ASSERT(qlPlayers.count() == 0); + Q_ASSERT(qlChannels.count() == 0); + + if (cChan) + c_qhChannels.remove(cChan); + if (pPlayer) + c_qhPlayers.remove(pPlayer); +} + +ModelItem *ModelItem::parent() const { + Channel *p; + + if (cChan) + p = cChan->cParent; + else + p = pPlayer->cChannel; + + return c_qhChannels.value(p); +} + +ModelItem *ModelItem::child(int idx) const { + if (! validRow(idx)) + return NULL; + + if (idx < qlChannels.count()) + return c_qhChannels.value(channelAt(idx)); + else + return c_qhPlayers.value(playerAt(idx)); +} + +bool ModelItem::validRow(int idx) const { + return ((idx >= 0) && (idx < (qlPlayers.count() + qlChannels.count()))); +} + +Player *ModelItem::playerAt(int idx) const { + idx -= qlChannels.count(); + if ((idx>= 0) && (idx < qlPlayers.count())) + return qlPlayers.at(idx); + return NULL; +} + +Channel *ModelItem::channelAt(int idx) const { + if ((idx>= 0) && (idx < qlChannels.count())) + return qlChannels.at(idx); + return NULL; +} + +int ModelItem::rowOf(Channel *c) const { + return qlChannels.lastIndexOf(c); +} + +int ModelItem::rowOf(Player *p) const { + int v = qlPlayers.lastIndexOf(p); + if (v != -1) + v += qlChannels.count(); + return v; +} + +int ModelItem::rowOfSelf() const { + ModelItem *p = parent(); + + Q_ASSERT(p); + + if (pPlayer) + return p->rowOf(pPlayer); + else + return p->rowOf(cChan); +} + +int ModelItem::rows() const { + return qlPlayers.count() + qlChannels.count(); +} + +int ModelItem::insertIndex(Channel *c) const { + QList<QString> qls; + Channel *cp; + + foreach(cp, qlChannels) + qls << cp->qsName; + qls << c->qsName; + qSort(qls); + + return qls.lastIndexOf(c->qsName); +} + +int ModelItem::insertIndex(Player *p) const { + QList<QString> qls; + Player *pp; + + foreach(pp, qlPlayers) + qls << pp->qsName; + qls << p->qsName; + qSort(qls); + + return qls.lastIndexOf(p->qsName) + qlChannels.count(); +} + +void ModelItem::insertChannel(Channel *c) { + int idx = insertIndex(c); + qlChannels.insert(idx, c); +} + +void ModelItem::insertPlayer(Player *p) { + int idx = insertIndex(p) - qlChannels.count(); + qlPlayers.insert(idx, p); +} + +PlayerModel::PlayerModel(QObject *p) : QAbstractItemModel(p) { + qiTalkingOn=QIcon(":/icons/talking_on.png"); + qiTalkingAlt=QIcon(":/icons/talking_alt.png"); + qiTalkingOff=QIcon(":/icons/talking_off.png"); + qiMutedSelf=QIcon(":/icons/muted_self.png"); + qiMutedServer=QIcon(":/icons/muted_server.png"); + qiMutedLocal=QIcon(":/icons/muted_local.png"); + qiDeafenedSelf=QIcon(":/icons/deafened_self.png"); + qiDeafenedServer=QIcon(":/icons/deafened_server.png"); + qiAuthenticated=QIcon(":/icons/authenticated.png"); + qiChannel=QIcon(":/icons/channel.png"); + qiLinkedChannel=QIcon(":/icons/channel_linked.png"); + + miRoot = new ModelItem(Channel::get(0)); +} + +PlayerModel::~PlayerModel() { + removeAll(); + Q_ASSERT(ModelItem::c_qhPlayers.count() == 0); + Q_ASSERT(ModelItem::c_qhChannels.count() == 1); +} + + +int PlayerModel::columnCount(const QModelIndex &) const +{ + return 2; +} + +QModelIndex PlayerModel::index(int row, int column, const QModelIndex &p) const +{ + ModelItem *item; + QModelIndex idx = QModelIndex(); + + if (row == -1) { + return QModelIndex(); + } + + if ( ! p.isValid()) { + item = miRoot; + } else { + item = static_cast<ModelItem *>(p.internalPointer()); + } + + if (! item->validRow(row)) + return idx; + + idx = createIndex(row, column, item->child(row)); + + return idx; +} + +QModelIndex PlayerModel::index(Player *p, int column) const +{ + ModelItem *item = ModelItem::c_qhPlayers.value(p); + Q_ASSERT(p); + Q_ASSERT(item); + QModelIndex idx=createIndex(item->rowOfSelf(), column, item); + return idx; +} + +QModelIndex PlayerModel::index(Channel *c) const +{ + ModelItem *item = ModelItem::c_qhChannels.value(c); + Q_ASSERT(c); + Q_ASSERT(item); + if (!c || ! c->parent()) + return QModelIndex(); + QModelIndex idx=createIndex(item->rowOfSelf(), 0, item); + return idx; +} + +QModelIndex PlayerModel::parent(const QModelIndex &idx) const +{ + if (! idx.isValid()) + return QModelIndex(); + + ModelItem *item = static_cast<ModelItem *>(idx.internalPointer()); + + ModelItem *pitem = item->parent(); + ModelItem *gpitem = (pitem) ? pitem->parent() : NULL; + + if (! pitem || ! gpitem) + return QModelIndex(); + + QModelIndex pidx = createIndex(pitem->rowOfSelf(), 0, pitem); + + + return pidx; +} + +int PlayerModel::rowCount(const QModelIndex &p) const +{ + ModelItem *item; + + int val = 0; + + if (!p.isValid()) + item = miRoot; + else + item = static_cast<ModelItem *>(p.internalPointer()); + + val = item->rows(); + + return val; +} + +QString PlayerModel::stringIndex(const QModelIndex &idx) const +{ + ModelItem *item = static_cast<ModelItem *>(idx.internalPointer()); + if (!idx.isValid()) + return QString("invIdx"); + if (!item) + return QString("invPtr"); + if (item->pPlayer) + return QString("P:%1 [%2,%3]").arg(item->pPlayer->qsName).arg(idx.row()).arg(idx.column()); + else + return QString("C:%1 [%2,%3]").arg(item->cChan->qsName).arg(idx.row()).arg(idx.column()); +} + +QVariant PlayerModel::data(const QModelIndex &idx, int role) const +{ + if (!idx.isValid()) + return QVariant(); + + ModelItem *item = static_cast<ModelItem *>(idx.internalPointer()); + + Channel *c = item->cChan; + Player *p = item->pPlayer; + + if (!c && !p) + return QVariant(); + + QVariant v = otherRoles(idx.column(), role, (p != NULL)); + if (v.isValid()) + return v; + + QList<QVariant> l; + + if (p) { + switch (role) { + case Qt::DecorationRole: + if (idx.column() == 0) + return (p->bTalking) ? (p->bAltSpeak ? qiTalkingAlt : qiTalkingOn) : qiTalkingOff; + break; + case Qt::FontRole: + if ((idx.column() == 0) && (p->sId == g.sId)) { + QFont f = g.mw->font(); + f.setBold(true); + return f; + } + break; + case Qt::DisplayRole: + if (idx.column() == 0) + return p->qsName; + if (p->iId >= 0) + l << qiAuthenticated; + if (p->bMute) + l << qiMutedServer; + if (p->bDeaf) + l << qiDeafenedServer; + if (p->bSelfMute) + l << qiMutedSelf; + if (p->bSelfDeaf) + l << qiDeafenedSelf; + if (p->bLocalMute) + l << qiMutedLocal; + return l; + default: + break; + } + } else { + switch (role) { + case Qt::DecorationRole: + if (idx.column() == 0) { + return qsLinked.contains(c) ? qiLinkedChannel : qiChannel; + } + case Qt::DisplayRole: + if (idx.column() == 0) + return c->qsName; + default: + break; + } + } + return QVariant(); +} + +Qt::ItemFlags PlayerModel::flags(const QModelIndex &idx) const +{ + if (!idx.isValid()) + return Qt::ItemIsDropEnabled; + + if (idx.column() != 0) + return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled; + + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; +} + +QVariant PlayerModel::otherRoles(int section, int role, bool isPlayer) const +{ + switch(role) { + case Qt::ToolTipRole: + switch(section) { + case 0: + return isPlayer ? tr("Name of player") : tr("Name of channel"); + case 1: + return isPlayer ? tr("Player flags") : QVariant(); + } + break; + case Qt::WhatsThisRole: + switch(section) { + case 0: + if (isPlayer) + return tr("This is a player connected to the server. The icon to the left of the player indicates " + "whether or not they are talking:<br />" + "<img src=\":/icons/talking_on.png\" /> Talking<br />" + "<img src=\":/icons/talking_off.png\" /> Not talking" + ); + else + return tr("This is a channel on the server. Only players in the same channel can hear each other."); + case 1: + return tr("This shows the flags the player has on the server, if any:<br />" + "<img src=\":/icons/authenticated.png\" />Authenticated user<br />" + "<img src=\":/icons/muted_self.png\" />Muted (by self)<br />" + "<img src=\":/icons/muted_server.png\" />Muted (by admin)<br />" + "<img src=\":/icons/deafened_self.png\" />Deafened (by self)<br />" + "<img src=\":/icons/deafened_server.png\" />Deafened (by admin)<br />" + "A player muted by himself is probably just away, talking on the phone or something like that.<br />" + "A player muted by an admin is probably also just away, and the noise the player is making was annoying " + "enough that an admin muted him."); + } + break; + } + return QVariant(); +} + +QVariant PlayerModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (orientation != Qt::Horizontal) + return QVariant(); + + switch(role) { + case Qt::DisplayRole: + switch(section) { + case 0: + return tr("Name"); + case 1: + return tr("Flags"); + } + } + + return QVariant(); +} + +void PlayerModel::unbugHide(const QModelIndex &idx) { + QAbstractItemView *v=g.mw->qtvPlayers; + QItemSelectionModel *sel=v->selectionModel(); + + // If we hide the current item and re-show it as a child + // an item that is nonexpanded, and then expand + // that item, we get "interresting results". + // + // A permanent fix for this would be to let the private + // pointer of the index be a struct with channel/player, + // and recalculate the correct (sorted) position for each and + // every use. + + if (sel->isSelected(idx) || (idx == v->currentIndex())) { + v->clearSelection(); + v->setCurrentIndex(QModelIndex()); + } +} + +void PlayerModel::hidePlayer(Player *p) { + Channel *c = p->cChannel; + ModelItem *item = ModelItem::c_qhChannels.value(c); + + int row = item->rowOf(p); + + unbugHide(index(p)); + + beginRemoveRows(index(c), row, row); + c->removePlayer(p); + item->qlPlayers.removeAll(p); + endRemoveRows(); + + if (g.sId && (p->cChannel == Player::get(g.sId)->cChannel)) + updateOverlay(); + + p->cChannel = NULL; +} + +void PlayerModel::showPlayer(Player *p, Channel *c) { + ModelItem *item = ModelItem::c_qhChannels.value(c); + + Q_ASSERT(p); + Q_ASSERT(c); + Q_ASSERT(item); + + int row = item->insertIndex(p); + + beginInsertRows(index(c), row, row); + c->addPlayer(p); + item->insertPlayer(p); + endInsertRows(); + + if (g.sId && (p->cChannel == Player::get(g.sId)->cChannel)) + updateOverlay(); + + ensureSelfVisible(); +} + +void PlayerModel::ensureSelfVisible() { + QStack<Channel *> chans; + + if (! g.sId) + return; + Channel *c = Player::get(g.sId)->cChannel; + while (c) { + chans.push(c); + c = c->cParent; + } + while (! chans.isEmpty()) { + c = chans.pop(); + g.mw->qtvPlayers->setExpanded(index(c), true); + } + recheckLinks(); +} + +void PlayerModel::recheckLinks() { + if (! g.sId) + return; + + bool bChanged = false; + + Channel *home = Player::get(g.sId)->cChannel; + + QSet<Channel *> all = home->allLinks(); + + if (all == qsLinked) + return; + + QSet<Channel *> changed = (all - qsLinked); + changed += (qsLinked - all); + + qsLinked = all; + + foreach(Channel *c, changed) { + QModelIndex idx = index(c); + emit dataChanged(idx, idx); + bChanged = true; + } + if (bChanged) + updateOverlay(); +} + +Player *PlayerModel::addPlayer(short id, QString name) { + Player *p = Player::add(id, this); + p->qsName = name; + + new ModelItem(p); + + connect(p, SIGNAL(talkingChanged(bool)), this, SLOT(playerTalkingChanged(bool))); + connect(p, SIGNAL(muteDeafChanged()), this, SLOT(playerMuteDeafChanged())); + + showPlayer(p, Channel::get(0)); + + return p; +} + +void PlayerModel::removePlayer(Player *p) { + ModelItem *item = ModelItem::c_qhPlayers.value(p); + + hidePlayer(p); + + Player::remove(p); + delete p; + delete item; +} + +void PlayerModel::movePlayer(Player *p, int id) { + Channel *np = Channel::get(id); + hidePlayer(p); + showPlayer(p, np); +} + +void PlayerModel::renamePlayer(Player *p, QString name) { + Channel *c = p->cChannel; + hidePlayer(p); + p->qsName = name; + showPlayer(p, c); +} + +void PlayerModel::showChannel(Channel *c, Channel *p) { + ModelItem *item = ModelItem::c_qhChannels.value(p); + + Q_ASSERT(p); + Q_ASSERT(c); + Q_ASSERT(item); + + int row = item->insertIndex(c); + + beginInsertRows(index(p), row, row); + p->addChannel(c); + item->insertChannel(c); + endInsertRows(); + + item = ModelItem::c_qhChannels.value(c); + + ensureSelfVisible(); +} + +void PlayerModel::hideChannel(Channel *c) { + Channel *p = c->cParent; + ModelItem *item = ModelItem::c_qhChannels.value(p); + + int row = item->rowOf(c); + + unbugHide(index(c)); + + beginRemoveRows(index(p), row, row); + p->removeChannel(c); + item->qlChannels.removeAll(c); + qsLinked.remove(c); + endRemoveRows(); + + item = ModelItem::c_qhChannels.value(c); +} + +Channel *PlayerModel::addChannel(int id, Channel *p, QString name) { + Channel *c = Channel::add(id, name, NULL); + + new ModelItem(c); + + showChannel(c, p); + return c; +} + +void PlayerModel::removeChannel(Channel *c) { + ModelItem *item; + Player *pl; + Channel *subc; + + item=ModelItem::c_qhChannels.value(c); + + foreach(subc, item->qlChannels) + removeChannel(subc); + + foreach(pl, item->qlPlayers) + removePlayer(pl); + + hideChannel(c); + Channel::remove(c); + + delete item; + delete c; +} + +void PlayerModel::moveChannel(Channel *c, int id) { + Channel *np = Channel::get(id); + hideChannel(c); + showChannel(c, np); +} + +void PlayerModel::linkChannels(Channel *c, QList<Channel *> links) { + foreach(Channel *l, links) + c->link(l); + recheckLinks(); +} + +void PlayerModel::unlinkChannels(Channel *c, QList<Channel *> links) { + foreach(Channel *l, links) + c->unlink(l); + recheckLinks(); +} + +void PlayerModel::unlinkAll(Channel *c) { + c->unlink(NULL); + recheckLinks(); +} + +void PlayerModel::removeAll() { + ModelItem *item = miRoot; + + while (item->qlChannels.count() > 0) + removeChannel(item->qlChannels[0]); + + while (item->qlPlayers.count() > 0) + removePlayer(item->qlPlayers[0]); + + qsLinked.clear(); +} + +Player *PlayerModel::getPlayer(const QModelIndex &idx) const +{ + if (! idx.isValid()) + return NULL; + + ModelItem *item; + item = static_cast<ModelItem *>(idx.internalPointer()); + + return item->pPlayer; +} + +Channel *PlayerModel::getChannel(const QModelIndex &idx) const +{ + if (! idx.isValid()) + return NULL; + + ModelItem *item; + item = static_cast<ModelItem *>(idx.internalPointer()); + + if (item->pPlayer) + return item->pPlayer->cChannel; + else + return item->cChan; +} + +Channel *PlayerModel::getSubChannel(Channel *p, int idx) const +{ + ModelItem *item=ModelItem::c_qhChannels.value(p); + if (idx < 0 || idx >= item->qlChannels.count()) + return NULL; + return item->qlChannels.at(idx); +} + + +void PlayerModel::playerTalkingChanged(bool bTalking) +{ + Player *p=static_cast<Player *>(sender()); + QModelIndex idx = index(p); + emit dataChanged(idx, idx); + updateOverlay(); +} + +void PlayerModel::playerMuteDeafChanged() +{ + Player *p=static_cast<Player *>(sender()); + QModelIndex idx = index(p, 1); + emit dataChanged(idx, idx); + if (g.sId && (p->cChannel == Player::get(g.sId)->cChannel)) + updateOverlay(); +} + +Qt::DropActions PlayerModel::supportedDropActions() const { + return Qt::CopyAction; +} + +QStringList PlayerModel::mimeTypes() const { + QStringList sl; + sl << "mumble/dragentry"; + return sl; +} + +QMimeData *PlayerModel::mimeData(const QModelIndexList &idxs) const { + QModelIndex idx; + QByteArray qba; + QDataStream ds(&qba, QIODevice::WriteOnly); + + foreach(idx, idxs) { + Player *p = getPlayer(idx); + Channel *c = getChannel(idx); + if (p) { + ds << false; + ds << p->sId; + } else { + ds << true; + ds << c->iId; + } + } + QMimeData *md = new QMimeData(); + md->setData("mumble/dragentry", qba); + return md; +} + +bool PlayerModel::dropMimeData (const QMimeData *md, Qt::DropAction action, int row, int column, const QModelIndex &p) { + if (! md->hasFormat(mimeTypes().at(0))) + return false; + + QByteArray qba = md->data(mimeTypes().at(0)); + QDataStream ds(qba); + + bool isChannel; + int iId = -1; + short sId = -1; + ds >> isChannel; + + if (isChannel) + ds >> iId; + else + ds >> sId; + + Channel *c; + if ( ! p.isValid()) { + c = Channel::get(0); + } else { + c = getChannel(p); + } + + if (! isChannel) { + MessagePlayerMove mpm; + mpm.sVictim = sId; + mpm.iChannelId = c->iId; + g.sh->sendMessage(&mpm); + } else { + MessageChannelMove mcm; + mcm.iId = iId; + mcm.iParent = c->iId; + g.sh->sendMessage(&mcm); + } + + return true; +} + +void PlayerModel::updateOverlay() const { +#ifdef Q_OS_WIN + g.o->updateOverlay(); +#endif +} + +PlayerDelegate::PlayerDelegate(QObject *p) : QItemDelegate(p) { +} + +QSize PlayerDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index) const { + if (index.column() == 1) { + const QAbstractItemModel *m = index.model(); + QVariant data = m->data(index); + QList<QVariant> ql = data.toList(); + return QSize(18 * ql.count(), 18); + } else { + return QItemDelegate::sizeHint(option, index); + } +} + +void PlayerDelegate::paint(QPainter * painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { + if (index.column() == 1) { + const QAbstractItemModel *m = index.model(); + QVariant data = m->data(index); + QList<QVariant> ql = data.toList(); + + painter->save(); + for(int i=0;i<ql.size();i++) { + QRect r = option.rect; + r.setSize(QSize(16,16)); + r.translate(i*18+1,1); + drawDecoration(painter, option, r, qvariant_cast<QIcon>(ql[i]).pixmap(QSize(16,16))); + } + painter->restore(); + return; + } else { + QItemDelegate::paint(painter,option,index); + } +} diff --git a/src/mumble/PlayerModel.h b/src/mumble/PlayerModel.h new file mode 100644 index 000000000..c81209b59 --- /dev/null +++ b/src/mumble/PlayerModel.h @@ -0,0 +1,151 @@ +/* Copyright (C) 2005-2006, 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 _PLAYERMODEL_H +#define _PLAYERMODEL_H + +class Player; +class Channel; + +class PlayerDelegate : public QItemDelegate { + Q_OBJECT + public: + PlayerDelegate(QObject *parent = NULL); + QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index) const; + void paint(QPainter * painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +struct ModelItem { + friend class PlayerModel; + + Channel *cChan; + Player *pPlayer; + QList<Channel *> qlChannels; + QList<Player *> qlPlayers; + + static QHash <Channel *, ModelItem *> c_qhChannels; + static QHash <Player *, ModelItem *> c_qhPlayers; + + ModelItem(Channel *c); + ModelItem(Player *p); + ~ModelItem(); + + ModelItem *parent() const; + ModelItem *child(int idx) const; + + bool validRow(int idx) const; + Player *playerAt(int idx) const; + Channel *channelAt(int idx) const; + int rowOf(Channel *c) const; + int rowOf(Player *p) const; + int rowOfSelf() const; + int rows() const; + int insertIndex(Channel *c) const; + int insertIndex(Player *p) const; + void insertChannel(Channel *c); + void insertPlayer(Player *p); +}; + +class ChannelItem; + +class PlayerModel : public QAbstractItemModel { + friend class ChannelItem; + Q_OBJECT +protected: + QIcon qiTalkingOn, qiTalkingAlt, qiTalkingOff; + QIcon qiMutedSelf, qiMutedServer, qiMutedLocal; + QIcon qiDeafenedSelf, qiDeafenedServer; + QIcon qiAuthenticated, qiChannel, qiLinkedChannel; + ModelItem *miRoot; + QSet<Channel *> qsLinked; + + QModelIndex index(Player *, int column = 0) const; + QModelIndex index(Channel *) const; + QModelIndex index(ChannelItem *) const; + + void hidePlayer(Player *p); + void showPlayer(Player *p, Channel *c); + + void hideChannel(Channel *c); + void showChannel(Channel *c, Channel *p); + + QString stringIndex(const QModelIndex &index) const; + + void unbugHide(const QModelIndex &index); +public: + PlayerModel(QObject *parent = 0); + ~PlayerModel(); + + QVariant data(const QModelIndex &index, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::DropActions supportedDropActions() const; + QStringList mimeTypes() const; + QMimeData *mimeData(const QModelIndexList &idx) const; + bool dropMimeData ( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex & parent); + + Player *addPlayer(short id, QString name); + Player *getPlayer(const QModelIndex &idx) const; + + Channel *addChannel(int id, Channel *p, QString name); + Channel *getChannel(const QModelIndex &idx) const; + + Channel *getSubChannel(Channel *p, int idx) const; + + void renamePlayer(Player *p, QString name); + + void movePlayer(Player *p, int id); + void moveChannel(Channel *p, int id); + + void removePlayer(Player *p); + void removeChannel(Channel *c); + + void linkChannels(Channel *c, QList<Channel *> links); + void unlinkChannels(Channel *c, QList<Channel *> links); + void unlinkAll(Channel *c); + + void removeAll(); + + QVariant otherRoles(int column, int role, bool isPlayer) const; +public slots: + void playerTalkingChanged(bool talking); + void playerMuteDeafChanged(); + void ensureSelfVisible(); + void recheckLinks(); + void updateOverlay() const; +}; + +#else +class PlayerModel; +#endif diff --git a/src/mumble/Plugins.cpp b/src/mumble/Plugins.cpp new file mode 100644 index 000000000..e88d929c4 --- /dev/null +++ b/src/mumble/Plugins.cpp @@ -0,0 +1,298 @@ +/* Copyright (C) 2005-2006, 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 "Plugins.h" +#include "Log.h" +#include "../../plugins/mumble_plugin.h" +#include "Global.h" + +static ConfigWidget *PluginConfigDialogNew() { + return new PluginConfig(); +} + +static ConfigRegistrar registrar(50, PluginConfigDialogNew); + + +struct PluginInfo { + bool locked; + QLibrary lib; + QString filename; + QString description; + QString shortname; + MumblePlugin *p; + PluginInfo(); +}; + +PluginInfo::PluginInfo() { + locked = false; + p = NULL; +} + +PluginConfig::PluginConfig(QWidget *p) : ConfigWidget(p) { + QGroupBox *qgbOptions = new QGroupBox(tr("Options")); + QGroupBox *qgbPlugins = new QGroupBox(tr("Plugins")); + QVBoxLayout *v; + QHBoxLayout *h; + QGridLayout *grid; + + grid=new QGridLayout(); + qcbTransmit = new QCheckBox(tr("Link to Game and Transmit Position")); + qcbTransmit->setChecked(g.s.bTransmitPosition); + qcbTransmit->setToolTip(tr("Enable plugins and transmit positional information")); + qcbTransmit->setWhatsThis(tr("This enables plugins for supported games to fetch your in-game position " + "and transmit that with each voice packet. This enables other players to " + "hear your voice in-game from the direction your character is in relation " + "to their own.")); + grid->addWidget(qcbTransmit, 0, 0); + qgbOptions->setLayout(grid); + + qlwPlugins = new QListWidget(); + refillPluginList(); + + QPushButton *reloadButton=new QPushButton(tr("&Reload plugins")); + reloadButton->setObjectName("Reload"); + reloadButton->setToolTip(tr("Reloads all plugins")); + reloadButton->setWhatsThis(tr("This rescans and reloads plugins. Use this if you just added or changed " + "a plugin to the plugins directory.")); + QPushButton *aboutButton=new QPushButton(tr("&About")); + aboutButton->setObjectName("About"); + aboutButton->setToolTip(tr("Information about plugin")); + aboutButton->setWhatsThis(tr("This shows a small information message about the plugin.")); + QPushButton *configButton=new QPushButton(tr("&Configure")); + configButton->setObjectName("Config"); + configButton->setToolTip(tr("Show configuration page of plugin")); + configButton->setWhatsThis(tr("This shows the configuration page of the plugin, if any.")); + + h = new QHBoxLayout(); + h->addWidget(reloadButton); + h->addStretch(1); + h->addWidget(aboutButton); + h->addWidget(configButton); + + v = new QVBoxLayout; + v->addWidget(qlwPlugins); + v->addLayout(h); + qgbPlugins->setLayout(v); + + v = new QVBoxLayout; + v->addWidget(qgbOptions); + v->addWidget(qgbPlugins); + v->addStretch(1); + setLayout(v); + + QMetaObject::connectSlotsByName(this); +} + +QString PluginConfig::title() const { + return tr("Plugins"); +} + +QIcon PluginConfig::icon() const { + return QIcon(":/icons/config_plugin.png"); +} + +void PluginConfig::accept() { + g.s.bTransmitPosition = qcbTransmit->isChecked(); +} + +void PluginConfig::on_Config_clicked() { + QListWidgetItem *i = qlwPlugins->currentItem(); + if (!i) + return; + PluginInfo *pi=qhInfos[i]; + + if (pi->p->config) + pi->p->config(winId()); + else + QMessageBox::information(this, tr("Mumble"), tr("Plugin has no configure function."), QMessageBox::Ok, QMessageBox::NoButton); +} + +void PluginConfig::on_About_clicked() { + QListWidgetItem *i = qlwPlugins->currentItem(); + if (!i) + return; + PluginInfo *pi=qhInfos[i]; + + if (pi->p->about) + pi->p->about(winId()); + else + QMessageBox::information(this, tr("Mumble"), tr("Plugin has no about function."), QMessageBox::Ok, QMessageBox::NoButton); +} + +void PluginConfig::on_Reload_clicked() { + g.p->rescanPlugins(); + refillPluginList(); +} + +void PluginConfig::refillPluginList() { + QMutexLocker lock(&g.p->qmPlugins); + qlwPlugins->clear(); + qhInfos.clear(); + PluginInfo *pi; + foreach(pi, g.p->qlPlugins) { + QListWidgetItem *i = new QListWidgetItem(pi->description, qlwPlugins); + i->setData(Qt::ToolTipRole, pi->lib.fileName()); + qhInfos[i]=pi; + } +} + +Plugins::Plugins(QObject *p) : QObject(p) { + QTimer *timer=new QTimer(this); + timer->setObjectName("Timer"); + timer->start(1000); + locked = prevlocked = NULL; + bValid = false; + for(int i=0;i<3;i++) + fPosition[i]=fFront[i]=fTop[i]= 0.0; + QMetaObject::connectSlotsByName(this); +} + +void Plugins::rescanPlugins() { + QMutexLocker lock(&qmPlugins); + PluginInfo *pi; + foreach(pi, qlPlugins) { + if (pi->locked) + pi->p->unlock(); + pi->lib.unload(); + delete pi; + } + qlPlugins.clear(); + prevlocked = locked = NULL; + bValid = false; + +#ifdef QT_NO_DEBUG + QString path=QString("%1/plugins").arg(qApp->applicationDirPath()); +#else + QString path=QString("%1/../plugins").arg(qApp->applicationDirPath()); +#endif + + QDir qd(path,"*.dll", QDir::Name, QDir::Files | QDir::Readable); + QStringList libs = qd.entryList(); + foreach(QString libname, libs) { + pi = new PluginInfo(); + pi->filename = QString("%1/%2").arg(path).arg(libname); + pi->lib.setFileName(pi->filename); + if (pi->lib.load()) { + mumblePluginFunc mpf = (mumblePluginFunc)(pi->lib.resolve("getMumblePlugin")); + if (mpf) { + pi->p = mpf(); + if (pi->p) { + pi->description=QString::fromUtf16(reinterpret_cast<const ushort *>(pi->p->description)); + pi->shortname=QString::fromUtf16(reinterpret_cast<const ushort *>(pi->p->shortname)); + qlPlugins << pi; + continue; + } + } + pi->lib.unload(); + } + delete pi; + } +} + +void Plugins::fetch() { + QMutexLocker lock(&qmPlugins); + + if (! locked) { + bValid = false; + return; + } + bool ok=locked->p->fetch(fPosition, fFront, fTop); + if (! ok || bUnlink) { + locked->p->unlock(); + locked->locked = false; + prevlocked = locked; + locked = NULL; + for(int i=0;i<3;i++) + fPosition[i]=fFront[i]=fTop[i]= 0.0; + } + bValid = ok; +} + +void Plugins::on_Timer_timeout() { + QMutexLocker lock(&qmPlugins); + + if (prevlocked) { + g.l->log(Log::Information, + tr("Plugin %1 lost link.").arg(prevlocked->description), + tr("%1 lost link.").arg(prevlocked->shortname)); + prevlocked = NULL; + } + + if (locked) + return; + + if (! g.s.bTransmitPosition) + return; + +#ifdef Q_OS_WIN + // According to MS KB Q131065, we need this to OpenProcess() + + HANDLE hToken = NULL; + + if(!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken)) + { + if (GetLastError() == ERROR_NO_TOKEN) + { + ImpersonateSelf(SecurityImpersonation); + OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken); + } + } + + TOKEN_PRIVILEGES tp; + LUID luid; + TOKEN_PRIVILEGES tpPrevious; + DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES); + + LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &luid); + + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &tpPrevious, &cbPrevious); +#endif + + foreach(PluginInfo *pi, qlPlugins) { + if (pi->p->trylock()) { + g.l->log(Log::Information, + tr("Plugin %1 linked.").arg(pi->description), + tr("%1 linked.").arg(pi->shortname)); + pi->locked = true; + bUnlink = false; + locked = pi; + break; + } + } + +#ifdef Q_OS_WIN + AdjustTokenPrivileges(hToken, FALSE, &tpPrevious, cbPrevious, NULL, NULL); + CloseHandle(hToken); +#endif +} diff --git a/src/mumble/Plugins.h b/src/mumble/Plugins.h new file mode 100644 index 000000000..c4ad07fe1 --- /dev/null +++ b/src/mumble/Plugins.h @@ -0,0 +1,78 @@ +/* Copyright (C) 2005-2006, 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 _PLUGINS_H +#define _PLUGINS_H + +#include "ConfigDialog.h" + +struct PluginInfo; + +class PluginConfig : public ConfigWidget { + Q_OBJECT + protected: + QCheckBox *qcbTransmit; + QListWidget *qlwPlugins; + void refillPluginList(); + QHash<QListWidgetItem *, PluginInfo *> qhInfos; + public: + PluginConfig(QWidget *p = NULL); + virtual QString title() const; + virtual QIcon icon() const; + public slots: + void accept(); + void on_Config_clicked(); + void on_About_clicked(); + void on_Reload_clicked(); +}; + +class Plugins : public QObject { + friend class PluginConfig; + Q_OBJECT + protected: + QMutex qmPlugins; + QList<PluginInfo *> qlPlugins; + PluginInfo *locked; + PluginInfo *prevlocked; + public: + bool bValid; + bool bUnlink; + float fPosition[3], fFront[3], fTop[3]; + + Plugins(QObject *p = NULL); + public slots: + void on_Timer_timeout(); + void rescanPlugins(); + void fetch(); +}; + +#else +class Log; +#endif diff --git a/src/mumble/ServerHandler.cpp b/src/mumble/ServerHandler.cpp new file mode 100644 index 000000000..b479681d3 --- /dev/null +++ b/src/mumble/ServerHandler.cpp @@ -0,0 +1,243 @@ +/* Copyright (C) 2005-2006, 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 "ServerHandler.h" +#include "MainWindow.h" +#include "AudioInput.h" +#include "AudioOutput.h" +#include "Message.h" +#include "Player.h" +#include "Connection.h" +#include "Global.h" + +ServerHandlerMessageEvent::ServerHandlerMessageEvent(QByteArray &msg, bool udp) : QEvent(static_cast<QEvent::Type>(SERVERSEND_EVENT)) { + qbaMsg = msg; + bUdp = udp; +} + +ServerHandler::ServerHandler() +{ + cConnection = NULL; + qusUdp = NULL; +} + +ServerHandler::~ServerHandler() +{ + wait(); +} + +void ServerHandler::customEvent(QEvent *evt) { + if (evt->type() != SERVERSEND_EVENT) + return; + + ServerHandlerMessageEvent *shme=static_cast<ServerHandlerMessageEvent *>(evt); + + if (cConnection) { + if (shme->qbaMsg.size() > 0) { + if (shme->bUdp && ! g.s.bTCPCompat) { + if (! qusUdp) { + if (cConnection->peerAddress().isNull()) + return; + qusUdp = new QUdpSocket(this); + qusUdp->bind(); + connect(qusUdp, SIGNAL(readyRead()), this, SLOT(udpReady())); +#ifdef Q_OS_WIN + int tos = 0xb8; + if (setsockopt(qusUdp->socketDescriptor(), IPPROTO_IP, 3, reinterpret_cast<char *>(&tos), sizeof(tos)) != 0) { + tos = 0x98; + setsockopt(qusUdp->socketDescriptor(), IPPROTO_IP, 3, reinterpret_cast<char *>(&tos), sizeof(tos)); + } +#endif + qhaRemote = cConnection->peerAddress(); + } + qusUdp->writeDatagram(shme->qbaMsg, qhaRemote, iPort); + } else { + cConnection->sendMessage(shme->qbaMsg); + if (shme->bUdp) + cConnection->forceFlush(); + } + } else + cConnection->disconnect(); + } +} + +void ServerHandler::udpReady() { + while (qusUdp->hasPendingDatagrams()) { + QByteArray qba; + qba.resize(qusUdp->pendingDatagramSize()); + QHostAddress senderAddr; + quint16 senderPort; + qusUdp->readDatagram(qba.data(), qba.size(), &senderAddr, &senderPort); + + if (!(senderAddr == qhaRemote) || (senderPort != iPort)) + continue; + + Message *msg = Message::networkToMessage(qba); + + if (! msg) + continue; + if ((msg->messageType() != Message::Speex) && (msg->messageType() != Message::MultiSpeex)) { + delete msg; + continue; + } + message(qba); + delete msg; + } +} + +void ServerHandler::sendMessage(Message *mMsg, bool forceTCP) +{ + QByteArray qbaBuffer; + mMsg->sPlayerId = g.sId; + mMsg->messageToNetwork(qbaBuffer); + + bool mayUdp = !forceTCP && g.sId && ((mMsg->messageType() == Message::Speex) || (mMsg->messageType() == Message::MultiSpeex) || (mMsg->messageType() == Message::Ping)); + + ServerHandlerMessageEvent *shme=new ServerHandlerMessageEvent(qbaBuffer, mayUdp); + QApplication::postEvent(this, shme); +} + +void ServerHandler::run() +{ + QTcpSocket *qtsSock = new QTcpSocket(this); + cConnection = new Connection(this, qtsSock); + qusUdp = NULL; + + connect(qtsSock, SIGNAL(connected()), this, SLOT(serverConnectionConnected())); + connect(cConnection, SIGNAL(connectionClosed(QString)), this, SLOT(serverConnectionClosed(QString))); + connect(cConnection, SIGNAL(message(QByteArray &)), this, SLOT(message(QByteArray &))); + qtsSock->connectToHost(qsHostName, iPort); + + QTimer *ticker = new QTimer(this); + connect(ticker, SIGNAL(timeout()), this, SLOT(sendPing())); + ticker->start(10000); + + exec(); + + ticker->stop(); + cConnection->disconnect(); + delete cConnection; + cConnection = NULL; + if (qusUdp) { + delete qusUdp; + qusUdp = NULL; + } +} + +void ServerHandler::sendPing() { + MessagePing mp; + sendMessage(&mp, true); + sendMessage(&mp, false); +} + +void ServerHandler::message(QByteArray &qbaMsg) { + Message *mMsg = Message::networkToMessage(qbaMsg); + if (! mMsg) + return; + + Player *p = Player::get(mMsg->sPlayerId); + + AudioOutputPtr ao = g.ao; + + if (mMsg->messageType() == Message::Speex) { + if (ao) { + if (p) { + MessageSpeex *msMsg=static_cast<MessageSpeex *>(mMsg); + if (! p->bLocalMute) + ao->addFrameToBuffer(p, msMsg->qbaSpeexPacket, msMsg->iSeq); + } else { + // Eek, we just got a late packet for a player already removed. Remove + // the buffer and pretend this never happened. + // If ~AudioOutputPlayer or decendants uses the Player object now, + // Bad Things happen. + ao->removeBuffer(p); + } + } + } else if (mMsg->messageType() == Message::MultiSpeex) { + if (ao) { + if (p) { + MessageMultiSpeex *mmsMsg=static_cast<MessageMultiSpeex *>(mMsg); + int idx = 0; + foreach(QByteArray qba, mmsMsg->qlFrames) { + if (! p->bLocalMute) + ao->addFrameToBuffer(p, qba, mmsMsg->iSeq + idx); + idx++; + } + } else { + ao->removeBuffer(p); + } + } + } else { + if(mMsg->messageType() == Message::ServerLeave) { + if (ao) + ao->removeBuffer(p); + } + ServerHandlerMessageEvent *shme=new ServerHandlerMessageEvent(qbaMsg, false); + QApplication::postEvent(g.mw, shme); + } + + delete mMsg; +} + +void ServerHandler::disconnect() { + // Actual TCP object is in a different thread, so signal it + QByteArray qbaBuffer; + ServerHandlerMessageEvent *shme=new ServerHandlerMessageEvent(qbaBuffer, false); + QApplication::postEvent(this, shme); +} + +void ServerHandler::serverConnectionClosed(QString reason) { + AudioOutputPtr ao = g.ao; + if (ao) + ao->wipe(); + + emit disconnected(reason); + exit(0); +} + +void ServerHandler::serverConnectionConnected() { + AudioInputPtr ai = g.ai; + MessageServerAuthenticate msaMsg; + msaMsg.qsUsername = qsUserName; + msaMsg.qsPassword = qsPassword; + if (ai) + msaMsg.iMaxBandwidth = ai->getMaxBandwidth(); + else + msaMsg.iMaxBandwidth = 0; + cConnection->sendMessage(&msaMsg); + emit connected(); +} + +void ServerHandler::setConnectionInfo(QString host, int port, QString username, QString pw) { + qsHostName = host; + iPort = port; + qsUserName = username; + qsPassword = pw; +} diff --git a/src/mumble/ServerHandler.h b/src/mumble/ServerHandler.h new file mode 100644 index 000000000..2b3f89643 --- /dev/null +++ b/src/mumble/ServerHandler.h @@ -0,0 +1,80 @@ +/* Copyright (C) 2005-2006, 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 _SERVERHANDLER_H +#define _SERVERHANDLER_H + +#define SERVERSEND_EVENT 3501 + +class Connection; +class Message; + +class ServerHandlerMessageEvent : public QEvent +{ + public: + QByteArray qbaMsg; + bool bUdp; + ServerHandlerMessageEvent(QByteArray &msg, bool udp); +}; + +class ServerHandler : public QThread +{ + Q_OBJECT + protected: + QString qsHostName; + QString qsUserName; + QString qsPassword; + int iPort; + Connection *cConnection; + + QHostAddress qhaRemote; + QUdpSocket *qusUdp; + public: + ServerHandler(); + ~ServerHandler(); + void setConnectionInfo(QString host, int port, QString username, QString pw); + void customEvent(QEvent *evt); + void sendMessage(Message *m, bool forceTCP=false); + void disconnect(); + void run(); + signals: + void disconnected(QString reason); + void connected(); + protected slots: + void message(QByteArray &); + void serverConnectionConnected(); + void serverConnectionClosed(QString); + void udpReady(); + void sendPing(); +}; + +#else +class ServerHandler; +#endif diff --git a/src/mumble/Settings.cpp b/src/mumble/Settings.cpp new file mode 100644 index 000000000..5dc13e6a9 --- /dev/null +++ b/src/mumble/Settings.cpp @@ -0,0 +1,158 @@ +/* Copyright (C) 2005-2006, 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 "Settings.h" +#include "Global.h" + +Settings::Settings() { + atTransmit = VAD; + a3dModel = None; + bMute = bDeaf = false; + bTTS = true; + iTTSVolume = 75; + iQuality = 6; + iComplexity = 4; + iMinLoudness = 4000; + iVoiceHold = 200; + iJitterBufferSize = 8; + iFramesPerPacket = 2; + bTCPCompat = false; + bReconnect = true; + iDXOutputDelay = 5; + fDXMinDistance = 10.0; + fDXMaxDistance = 50.0; + fDXRollOff = 0.15; + + bOverlayEnable = true; + osOverlay = All; + bOverlayAlwaysSelf = true; + fOverlayX = 1.0; + fOverlayY = 0.0; + bOverlayTop = false; + bOverlayBottom = true; + bOverlayLeft = true; + bOverlayRight = false; + bTransmitPosition = false; + qfOverlayFont = QFont("Arial", 20); + fOverlayWidth = 20.0; + qcOverlayPlayer = QColor(255,255,255,128); + qcOverlayTalking = QColor(255,255,196,255); + qcOverlayAltTalking = QColor(255,128,128,255); + qcOverlayChannel = QColor(192,192,255,192); + qcOverlayChannelTalking = QColor(224,224,255,255); +} + +void Settings::load() { + bMute = g.qs->value("AudioMute", false). toBool(); + bDeaf = g.qs->value("AudioDeaf", false). toBool(); + bTTS = g.qs->value("TextToSpeech", bTTS). toBool(); + iTTSVolume = g.qs->value("TTSVolume", iTTSVolume).toInt(); + atTransmit = static_cast<Settings::AudioTransmit>(g.qs->value("AudioTransmit", atTransmit).toInt()); + iQuality = g.qs->value("AudioQuality", iQuality).toInt(); + iComplexity = g.qs->value("AudioComplexity", iComplexity).toInt(); + iMinLoudness = g.qs->value("AudioMinLoudness", iMinLoudness).toInt(); + iVoiceHold = g.qs->value("AudioVoiceHold", iVoiceHold).toInt(); + iJitterBufferSize = g.qs->value("JitterBufferSize", iJitterBufferSize).toInt(); + iFramesPerPacket = g.qs->value("FramesPerPacket", iFramesPerPacket).toInt(); + bTCPCompat = g.qs->value("TCPCompat", bTCPCompat).toBool(); + bReconnect = g.qs->value("Reconnect", bReconnect).toBool(); +#ifdef Q_OS_WIN + a3dModel = static_cast<Settings::Audio3D>(g.qs->value("Audio3D", a3dModel).toInt()); + iDXOutputDelay = g.qs->value("DXOutputDelay", iDXOutputDelay).toInt(); + qbaDXInput = g.qs->value("DXInput").toByteArray(); + qbaDXOutput = g.qs->value("DXOutput").toByteArray(); + fDXMinDistance = g.qs->value("DXMinDistance", fDXMinDistance).toDouble(); + fDXMaxDistance = g.qs->value("DXMaxDistance", fDXMaxDistance).toDouble(); + fDXRollOff = g.qs->value("DXRollOff", fDXRollOff).toDouble(); + qsASIOclass = g.qs->value("ASIOclass").toString(); + qlASIOmic = g.qs->value("ASIOmic").toList(); + qlASIOspeaker = g.qs->value("ASIOspeaker").toList(); + bOverlayEnable = g.qs->value("OverlayEnable",bOverlayEnable).toBool(); + osOverlay = static_cast<Settings::OverlayShow>(g.qs->value("OverlayShow", osOverlay).toInt()); + bOverlayAlwaysSelf = g.qs->value("OverlayAlwaysSelf",bOverlayAlwaysSelf).toBool(); + fOverlayX = g.qs->value("OverlayX",fOverlayX).toDouble(); + fOverlayY = g.qs->value("OverlayY",fOverlayY).toDouble(); + bTransmitPosition = g.qs->value("PosTransmit",bTransmitPosition).toBool(); + bOverlayTop = g.qs->value("OverlayTop",bOverlayTop).toBool(); + bOverlayBottom = g.qs->value("OverlayBottom",bOverlayBottom).toBool(); + bOverlayLeft = g.qs->value("OverlayLeft",bOverlayLeft).toBool(); + bOverlayRight = g.qs->value("OverlayRight",bOverlayRight).toBool(); + qfOverlayFont = qvariant_cast<QFont>(g.qs->value("OverlayFont",qfOverlayFont)); + fOverlayWidth = g.qs->value("OverlayWidth", fOverlayWidth).toDouble(); + qcOverlayPlayer = qvariant_cast<QColor>(g.qs->value("OverlayPlayer",qcOverlayPlayer)); + qcOverlayTalking = qvariant_cast<QColor>(g.qs->value("OverlayTalking",qcOverlayTalking)); + qcOverlayChannel = qvariant_cast<QColor>(g.qs->value("OverlayChannel",qcOverlayChannel)); + qcOverlayChannelTalking = qvariant_cast<QColor>(g.qs->value("OverlayChannelTalking",qcOverlayChannelTalking)); +#endif +} + +void Settings::save() { + g.qs->setValue("AudioMute", g.s.bMute); + g.qs->setValue("AudioDeaf", g.s.bDeaf); + g.qs->setValue("TextToSpeech", g.s.bTTS); + g.qs->setValue("TTSVolume", g.s.iTTSVolume); + g.qs->setValue("AudioTransmit", g.s.atTransmit); + g.qs->setValue("AudioQuality", iQuality); + g.qs->setValue("AudioComplexity", iComplexity); + g.qs->setValue("AudioMinLoudness", iMinLoudness); + g.qs->setValue("AudioVoiceHold", iVoiceHold); + g.qs->setValue("JitterBufferSize", iJitterBufferSize); + g.qs->setValue("FramesPerPacket", iFramesPerPacket); + g.qs->setValue("TCPCompat", bTCPCompat); + g.qs->setValue("Reconnect", bReconnect); +#ifdef Q_OS_WIN + g.qs->setValue("PosTransmit", g.s.bTransmitPosition); + g.qs->setValue("Audio3D", g.s.a3dModel); + g.qs->setValue("DXOutputDelay", iDXOutputDelay); + g.qs->setValue("DXInput", qbaDXInput); + g.qs->setValue("DXOutput", qbaDXOutput); + g.qs->setValue("DXMinDistance", fDXMinDistance); + g.qs->setValue("DXMaxDistance", fDXMaxDistance); + g.qs->setValue("DXRollOff", fDXRollOff); + g.qs->setValue("ASIOclass", qsASIOclass); + g.qs->setValue("ASIOmic", qlASIOmic); + g.qs->setValue("ASIOspeaker", qlASIOspeaker); + g.qs->setValue("OverlayEnable", bOverlayEnable); + g.qs->setValue("OverlayShow", osOverlay); + g.qs->setValue("OverlayAlwaysSelf", bOverlayAlwaysSelf); + g.qs->setValue("OverlayX", fOverlayX); + g.qs->setValue("OverlayY", fOverlayY); + g.qs->setValue("OverlayTop", bOverlayTop); + g.qs->setValue("OverlayBottom", bOverlayBottom); + g.qs->setValue("OverlayLeft", bOverlayLeft); + g.qs->setValue("OverlayRight", bOverlayRight); + g.qs->setValue("OverlayFont", qfOverlayFont); + g.qs->setValue("OverlayWidth", fOverlayWidth); + g.qs->setValue("OverlayPlayer", qcOverlayPlayer); + g.qs->setValue("OverlayTalking", qcOverlayTalking); + g.qs->setValue("OverlayChannel", qcOverlayChannel); + g.qs->setValue("OverlayChannelTalking", qcOverlayChannelTalking); +#endif +} diff --git a/src/mumble/Settings.h b/src/mumble/Settings.h new file mode 100644 index 000000000..d8c384979 --- /dev/null +++ b/src/mumble/Settings.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2005-2006, 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 _SETTINGS_H +#define _SETTINGS_H + +// Global helper class to spread variables around across threads +// especially helpfull to initialize things like the stored +// preference for audio transmission, since the GUI elements +// will be created long before the AudioInput object, and the +// latter lives in a separate thread and so cannot touch the +// GUI. + +struct Settings { + enum AudioTransmit { Continous, VAD, PushToTalk }; + enum Audio3D { None, Panning, Light, Full }; + enum OverlayShow { Nothing, Talking, All }; + AudioTransmit atTransmit; + bool bTransmitPosition; + Audio3D a3dModel; + bool bMute, bDeaf; + bool bTTS; + int iTTSVolume; + int iQuality, iComplexity, iMinLoudness, iVoiceHold, iJitterBufferSize; + int iFramesPerPacket; + bool bTCPCompat, bReconnect; + QString qsAudioInput, qsAudioOutput; + + int iDXOutputDelay; + QByteArray qbaDXInput, qbaDXOutput; + float fDXMinDistance, fDXMaxDistance, fDXRollOff; + + QString qsASIOclass; + QList<QVariant> qlASIOmic; + QList<QVariant> qlASIOspeaker; + + bool bOverlayEnable; + OverlayShow osOverlay; + bool bOverlayAlwaysSelf; + float fOverlayX; + float fOverlayY; + bool bOverlayLeft, bOverlayRight, bOverlayTop, bOverlayBottom; + QFont qfOverlayFont; + float fOverlayWidth; + QColor qcOverlayPlayer; + QColor qcOverlayTalking; + QColor qcOverlayAltTalking; + QColor qcOverlayChannel; + QColor qcOverlayChannelTalking; + + Settings(); + void load(); + void save(); +}; + +#else +class Settings; +#endif diff --git a/src/mumble/TextToSpeech.h b/src/mumble/TextToSpeech.h new file mode 100644 index 000000000..f06df1627 --- /dev/null +++ b/src/mumble/TextToSpeech.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2005-2006, 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 _TEXTTOSPEECH_H +#define _TEXTTOSPEECH_H + +class TextToSpeechPrivate; + +class TextToSpeech : public QObject { + friend class TextToSpeechPrivate; + Q_OBJECT + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) + protected: + bool enabled; + public: + TextToSpeech(QObject *parent = NULL); + ~TextToSpeech(); + bool isEnabled() const; + public slots: + void say(QString text); + void setEnabled(bool ena); + void setVolume(int volume); + private: + TextToSpeechPrivate *d; +}; + +#else +class TextToSpeech; +#endif diff --git a/src/mumble/TextToSpeech_unix.cpp b/src/mumble/TextToSpeech_unix.cpp new file mode 100644 index 000000000..5ee19b2cb --- /dev/null +++ b/src/mumble/TextToSpeech_unix.cpp @@ -0,0 +1,52 @@ +/* Copyright (C) 2005-2006, 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 "TextToSpeech.h" + +TextToSpeech::TextToSpeech(QObject *p) : QObject(p) { +} + +TextToSpeech::~TextToSpeech() { +} + +void TextToSpeech::say(QString text) { +} + +void TextToSpeech::setEnabled(bool e) { + enabled = e; +} + +void TextToSpeech::setVolume(int) { +} + + +bool TextToSpeech::isEnabled() const { + return enabled; +} diff --git a/src/mumble/TextToSpeech_win.cpp b/src/mumble/TextToSpeech_win.cpp new file mode 100644 index 000000000..eaf7ea898 --- /dev/null +++ b/src/mumble/TextToSpeech_win.cpp @@ -0,0 +1,95 @@ +/* Copyright (C) 2005-2006, 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 <servprov.h> +#include <sapi.h> + +#include "TextToSpeech.h" + +#undef FAILED +#define FAILED(Status) (static_cast<HRESULT>(Status)<0) + +class TextToSpeechPrivate { + public: + ISpVoice * pVoice; + TextToSpeechPrivate(); + ~TextToSpeechPrivate(); + void say(QString text); + void setVolume(int v); +}; + +TextToSpeechPrivate::TextToSpeechPrivate() { + pVoice = NULL; + + HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice); + if( FAILED(hr)) + qWarning("TextToSpeechPrivate: Failed to allocate TTS Voice"); +} + +TextToSpeechPrivate::~TextToSpeechPrivate() { + if (pVoice) + pVoice->Release(); +} + +void TextToSpeechPrivate::say(QString text) { + if (pVoice) + pVoice->Speak((const wchar_t *) text.utf16(), SPF_ASYNC, NULL); +} + +void TextToSpeechPrivate::setVolume(int volume) { + if (pVoice) + pVoice->SetVolume(volume); +} + +TextToSpeech::TextToSpeech(QObject *p) : QObject(p) { + enabled = true; + d = new TextToSpeechPrivate(); +} + +TextToSpeech::~TextToSpeech() { + delete d; +} + +void TextToSpeech::say(QString text) { + if (enabled) + d->say(text); +} + +void TextToSpeech::setEnabled(bool e) { + enabled = e; +} + +void TextToSpeech::setVolume(int volume) { + d->setVolume(volume); +} + +bool TextToSpeech::isEnabled() const { + return enabled; +} diff --git a/src/mumble/VersionCheck.cpp b/src/mumble/VersionCheck.cpp new file mode 100644 index 000000000..98b9eb78a --- /dev/null +++ b/src/mumble/VersionCheck.cpp @@ -0,0 +1,78 @@ +/* Copyright (C) 2005-2006, 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 "VersionCheck.h" +#include "Global.h" + +VersionCheck::VersionCheck(QObject *p) : QObject(p) { + qhAgent = new QHttp(this); + qhAgent->setObjectName("Agent"); + quUrl.setScheme("http"); + quUrl.setHost("mumble.sourceforge.net"); + quUrl.setPath("/ver.php"); + quUrl.addQueryItem("ver", QUrl::toPercentEncoding(MUMBLE_RELEASE)); + quUrl.addQueryItem("date", QUrl::toPercentEncoding(__DATE__)); + quUrl.addQueryItem("time", QUrl::toPercentEncoding(__TIME__)); + + QMetaObject::connectSlotsByName(this); + + QFile f(qApp->applicationFilePath()); + if (! f.open(QIODevice::ReadOnly)) { + qWarning("VersionCheck: Failed to open binary"); + } else { + QByteArray a = f.readAll(); + if (a.size() < 1) { + qWarning("VersionCehck: suspiciously small binary"); + } else { + quUrl.addQueryItem("crc", QString::number(qChecksum(a.data(), a.size()),16)); + } + } + + QHttpRequestHeader req("GET", quUrl.toString(QUrl::RemoveScheme|QUrl::RemoveAuthority)); + req.setValue("Host", quUrl.host()); + + qhAgent->setHost(quUrl.host()); + iReqId=qhAgent->request(req); +} + +void VersionCheck::on_Agent_requestFinished(int id, bool error) { + if (id != iReqId) + return; + + QHttpResponseHeader head = qhAgent->lastResponse(); + if ((head.statusCode() == 200) && !error) { + QByteArray a=qhAgent->readAll(); + if (a.size() > 0) + QMessageBox::information(static_cast<QWidget *>(parent()), tr("Mumble"), a, QMessageBox::Ok| QMessageBox::Default| QMessageBox::Escape, QMessageBox::NoButton); + } else { + QMessageBox::information(static_cast<QWidget *>(parent()), tr("Mumble"), tr("Mumble failed to retrieve version information from the SourceForge server."), QMessageBox::Ok| QMessageBox::Default| QMessageBox::Escape, QMessageBox::NoButton); + } + deleteLater(); +} diff --git a/src/mumble/VersionCheck.h b/src/mumble/VersionCheck.h new file mode 100644 index 000000000..a034b8257 --- /dev/null +++ b/src/mumble/VersionCheck.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2005-2006, 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 _VERSIONCHECK_H +#define _VERSIONCHECK_H + +class VersionCheck : public QObject { + Q_OBJECT + protected: + QUrl quUrl; + QHttp *qhAgent; + int iReqId; + public slots: + void on_Agent_requestFinished(int, bool); + public: + VersionCheck(QObject *parent = NULL); +}; + +#else +class VersionCheck; +#endif diff --git a/src/mumble/firmumble.inc b/src/mumble/firmumble.inc new file mode 100644 index 000000000..f797d56f7 --- /dev/null +++ b/src/mumble/firmumble.inc @@ -0,0 +1,156 @@ +#define MUMBLE_SFP_LENGTH 152 + +static const double mumble_sfp[MUMBLE_SFP_LENGTH] = { + 0.000028555872, + -0.000017432033, + -0.000210902904, + -0.000668460343, + -0.001432483528, + -0.002397960699, + -0.003283225074, + -0.003700710485, + -0.003327691332, + -0.002107233529, + -0.000363248885, + 0.001269737246, + 0.002113511643, + 0.001798566071, + 0.000509057439, + -0.001048952206, + -0.001987685218, + -0.001730674443, + -0.000379911205, + 0.001287121367, + 0.002237410623, + 0.001807220164, + 0.000159239042, + -0.001741151216, + -0.002671289373, + -0.001923351829, + 0.000176695340, + 0.002374267237, + 0.003217569107, + 0.002001241228, + -0.000682043917, + -0.003195030344, + -0.003836446973, + -0.001974036343, + 0.001413284332, + 0.004215313813, + 0.004484738505, + 0.001765527010, + -0.002435694148, + -0.005448812266, + -0.005112555918, + -0.001288680170, + 0.003821628815, + 0.006905791647, + 0.005654692491, + 0.000434827786, + -0.005660805447, + -0.008598612288, + -0.006027192891, + 0.000939464829, + 0.008077381744, + 0.010551245127, + 0.006117929062, + -0.003043209881, + -0.011268952139, + -0.012819086397, + -0.005759031420, + 0.006233297885, + 0.015612097556, + 0.015548909270, + 0.004665918500, + -0.011222932105, + -0.021941102664, + -0.019132993068, + -0.002222273132, + 0.019796013163, + 0.032559760914, + 0.024828601914, + -0.003381047886, + -0.038088021851, + -0.056550178833, + -0.038502377027, + 0.022164639547, + 0.111718940290, + 0.200592954775, + 0.255783647894, + 0.255783647894, + 0.200592954775, + 0.111718940290, + 0.022164639547, + -0.038502377027, + -0.056550178833, + -0.038088021851, + -0.003381047886, + 0.024828601914, + 0.032559760914, + 0.019796013163, + -0.002222273132, + -0.019132993068, + -0.021941102664, + -0.011222932105, + 0.004665918500, + 0.015548909270, + 0.015612097556, + 0.006233297885, + -0.005759031420, + -0.012819086397, + -0.011268952139, + -0.003043209881, + 0.006117929062, + 0.010551245127, + 0.008077381744, + 0.000939464829, + -0.006027192891, + -0.008598612288, + -0.005660805447, + 0.000434827786, + 0.005654692491, + 0.006905791647, + 0.003821628815, + -0.001288680170, + -0.005112555918, + -0.005448812266, + -0.002435694148, + 0.001765527010, + 0.004484738505, + 0.004215313813, + 0.001413284332, + -0.001974036343, + -0.003836446973, + -0.003195030344, + -0.000682043917, + 0.002001241228, + 0.003217569107, + 0.002374267237, + 0.000176695340, + -0.001923351829, + -0.002671289373, + -0.001741151216, + 0.000159239042, + 0.001807220164, + 0.002237410623, + 0.001287121367, + -0.000379911205, + -0.001730674443, + -0.001987685218, + -0.001048952206, + 0.000509057439, + 0.001798566071, + 0.002113511643, + 0.001269737246, + -0.000363248885, + -0.002107233529, + -0.003327691332, + -0.003700710485, + -0.003283225074, + -0.002397960699, + -0.001432483528, + -0.000668460343, + -0.000210902904, + -0.000017432033, + 0.000028555872 +}; diff --git a/src/mumble/iasiothiscallresolver.h b/src/mumble/iasiothiscallresolver.h new file mode 100644 index 000000000..792404288 --- /dev/null +++ b/src/mumble/iasiothiscallresolver.h @@ -0,0 +1,222 @@ +/************************************************ + * This file is NOT covered by the Mumble License + * It is an adapted version of IASIOThiscallResolver.h/.cpp, which + * can be found at http://www.audiomulch.com/~rossb/code/calliasio + * + * Note that this file depends on the ASIO SDK, which is covered + * by it's own license, and which you need to download separately. + * + * Original Authors: Fraser Adams + * Ross Bencina + * Rene G. Ceballos + * Martin Fay + * Antti Silvast + * Andrew Baldwin + * + * This version is intended to use just as a wrapper for IASIO, it will + * not fix up the theAsioDriver in the SDK. In addition, it attempts + * to forward the IUnknown interface functions to the original IASIO, as + * we need to support Release(). + * + * It should also be copiable. + */ + +#ifndef _IASIOTHISCALLRESOLVER_H +#define _IASIOTHISCALLRESOLVER_H + +#include <windows.h> +#include <asiodrvr.h> /* From ASIO SDK */ + +#define CALL_THISCALL_0( resultName, thisPtr, funcOffset ) \ + __asm__ __volatile__ ("movl (%1), %%edx\n\t" \ + "call *"#funcOffset"(%%edx)\n\t" \ + :"=a"(resultName) /* Output Operands */ \ + :"c"(thisPtr) /* Input Operands */ \ + ); \ + + +#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 ) \ + __asm__ __volatile__ ("pushl %0\n\t" \ + "movl (%1), %%edx\n\t" \ + "call *"#funcOffset"(%%edx)\n\t" \ + : /* Output Operands */ \ + :"r"(param1), /* Input Operands */ \ + "c"(thisPtr) \ + ); \ + + +#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 ) \ + __asm__ __volatile__ ("pushl %1\n\t" \ + "movl (%2), %%edx\n\t" \ + "call *"#funcOffset"(%%edx)\n\t" \ + :"=a"(resultName) /* Output Operands */ \ + :"r"(param1), /* Input Operands */ \ + "c"(thisPtr) \ + ); \ + + +#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 ) \ + __asm__ __volatile__ ("pushl 4(%1)\n\t" \ + "pushl (%1)\n\t" \ + "movl (%2), %%edx\n\t" \ + "call *"#funcOffset"(%%edx);\n\t" \ + :"=a"(resultName) /* Output Operands */ \ + :"a"(¶m1), /* Input Operands */ \ + /* Note: Using "r" above instead of "a" fails */ \ + /* when using GCC 3.3.3, and maybe later versions*/\ + "c"(thisPtr) \ + ); \ + + +#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 ) \ + __asm__ __volatile__ ("pushl %1\n\t" \ + "pushl %2\n\t" \ + "movl (%3), %%edx\n\t" \ + "call *"#funcOffset"(%%edx)\n\t" \ + :"=a"(resultName) /* Output Operands */ \ + :"r"(param2), /* Input Operands */ \ + "r"(param1), \ + "c"(thisPtr) \ + ); \ + + +#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\ + __asm__ __volatile__ ("pushl %1\n\t" \ + "pushl %2\n\t" \ + "pushl %3\n\t" \ + "pushl %4\n\t" \ + "movl (%5), %%edx\n\t" \ + "call *"#funcOffset"(%%edx)\n\t" \ + :"=a"(resultName) /* Output Operands */ \ + :"r"(param4), /* Input Operands */ \ + "r"(param3), \ + "r"(param2), \ + "r"(param1), \ + "c"(thisPtr) \ + ); \ + +class IASIOThiscallResolver : public IASIO { +private: + IASIO *that_; +public: + IASIOThiscallResolver(IASIO *that) { + that_ = that; + }; + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) { + return that_->QueryInterface(riid, ppv); + }; + virtual ULONG STDMETHODCALLTYPE AddRef() { + return that_->AddRef(); + }; + virtual ULONG STDMETHODCALLTYPE Release() { + return that_->Release(); + }; + + virtual ASIOBool init(void *sysHandle) { + ASIOBool result; + CALL_THISCALL_1( result, that_, 12, sysHandle ); + return result; + }; + virtual void getDriverName(char *name) { + CALL_VOID_THISCALL_1( that_, 16, name ); + }; + virtual long getDriverVersion() { + ASIOBool result; + CALL_THISCALL_0( result, that_, 20 ); + return result; + }; + virtual void getErrorMessage(char *string) { + CALL_VOID_THISCALL_1( that_, 24, string ); + }; + virtual ASIOError start() { + ASIOBool result; + CALL_THISCALL_0( result, that_, 28 ); + return result; + }; + virtual ASIOError stop() { + ASIOBool result; + CALL_THISCALL_0( result, that_, 32 ); + return result; + }; + virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) { + ASIOBool result; + CALL_THISCALL_2( result, that_, 36, numInputChannels, numOutputChannels ); + return result; + }; + virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) { + ASIOBool result; + CALL_THISCALL_2( result, that_, 40, inputLatency, outputLatency ); + return result; + }; + virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity) { + ASIOBool result; + CALL_THISCALL_4( result, that_, 44, minSize, maxSize, preferredSize, granularity ); + return result; + }; + virtual ASIOError canSampleRate(ASIOSampleRate sampleRate) { + ASIOBool result; + CALL_THISCALL_1_DOUBLE( result, that_, 48, sampleRate ); + return result; + }; + virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate) { + ASIOBool result; + CALL_THISCALL_1( result, that_, 52, sampleRate ); + return result; + }; + virtual ASIOError setSampleRate(ASIOSampleRate sampleRate) { + ASIOBool result; + CALL_THISCALL_1_DOUBLE( result, that_, 56, sampleRate ); + return result; + }; + virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) { + ASIOBool result; + CALL_THISCALL_2( result, that_, 60, clocks, numSources ); + return result; + }; + virtual ASIOError setClockSource(long reference) { + ASIOBool result; + CALL_THISCALL_1( result, that_, 64, reference ); + return result; + }; + virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) { + ASIOBool result; + CALL_THISCALL_2( result, that_, 68, sPos, tStamp ); + return result; + }; + virtual ASIOError getChannelInfo(ASIOChannelInfo *info) { + ASIOBool result; + CALL_THISCALL_1( result, that_, 72, info ); + return result; + }; + virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks) { + ASIOBool result; + CALL_THISCALL_4( result, that_, 76, bufferInfos, numChannels, bufferSize, callbacks ); + return result; + }; + virtual ASIOError disposeBuffers() { + ASIOBool result; + CALL_THISCALL_0( result, that_, 80 ); + return result; + }; + virtual ASIOError controlPanel() { + ASIOBool result; + CALL_THISCALL_0( result, that_, 84 ); + return result; + }; + virtual ASIOError future(long selector,void *opt) { + ASIOBool result; + CALL_THISCALL_2( result, that_, 88, selector, opt ); + return result; + }; + virtual ASIOError outputReady() { + ASIOBool result; + CALL_THISCALL_0( result, that_, 92 ); + return result; + }; +}; + +#endif + + + diff --git a/src/mumble/licenses.h b/src/mumble/licenses.h new file mode 100644 index 000000000..5d184f85f --- /dev/null +++ b/src/mumble/licenses.h @@ -0,0 +1,3 @@ +static const char *licenseMumble = "Copyright (C) 2005-2006, Thorvald Natvig <thorvald@natvig.com>\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n- Redistributions of source code must retain the above copyright notice,\n this list of conditions and the following disclaimer.\n- Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n- Neither the name of the Mumble Developers nor the names of its\n contributors may be used to endorse or promote products derived from this\n software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n`AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR\nCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"; + + diff --git a/src/mumble/main.cpp b/src/mumble/main.cpp new file mode 100644 index 000000000..65083e0c5 --- /dev/null +++ b/src/mumble/main.cpp @@ -0,0 +1,166 @@ +/* Copyright (C) 2005-2006, 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. +*/ + +#ifdef Q_OS_WIN +#include "Overlay.h" +#endif + +#include "MainWindow.h" +#include "ServerHandler.h" +#include "AudioInput.h" +#include "AudioOutput.h" +#include "Database.h" +#include "Log.h" +#include "Plugins.h" +#include "Global.h" + +#ifdef BOOST_NO_EXCEPTIONS +namespace boost { + void throw_exception(std::exception const & e) { + qFatal("Boost exception caught!"); + } +} +#endif + +extern void os_init(); + +int main(int argc, char **argv) +{ + // Check for SSE and MMX, but only in the windows binaries + + int res; +#ifdef Q_OS_WIN + os_init(); +#endif + + QT_REQUIRE_VERSION(argc, argv, "4.1.0"); + + // Initialize application object. + QApplication a(argc, argv); + a.setApplicationName("Mumble"); + a.setOrganizationName("Mumble"); + a.setOrganizationDomain("mumble.sourceforge.net"); + a.setQuitOnLastWindowClosed(false); + + QFile inifile(QString("%1/mumble.ini").arg(a.applicationDirPath())); + if (inifile.exists() && inifile.permissions().testFlag(QFile::WriteUser)) + g.qs = new QSettings(inifile.fileName(), QSettings::IniFormat); + else + g.qs = new QSettings(); + + QString style=g.qs->value("Style").toString(); + if (! style.isEmpty()) { + a.setStyle(style); + } + + QString skin=g.qs->value("Skin").toString(); + if (! skin.isEmpty()) { + QFile file(skin); + file.open(QFile::ReadOnly); + QString styleSheet = file.readAll(); + if (! styleSheet.isEmpty()) + a.setStyleSheet(styleSheet); + } + + QString locale=g.qs->value("Language", QLocale::system().name()).toString(); + QTranslator translator; + if (! translator.load(QString("mumble_") + locale)) + translator.load(QString(":/mumble_") + locale); + a.installTranslator(&translator); + + + // Set application icon + QIcon icon; + icon.addFile(":/mumble.png.2"); + icon.addFile(":/mumble.png.1"); + icon.addFile(":/mumble.png.0"); + a.setWindowIcon(icon); + + // Load preferences + g.s.load(); + + // Initialize logger + g.l = new Log(); + g.l->loadSettings(); + + // Initialize database + g.db = new Database(); + + // Initialize the serverhandler + g.sh = new ServerHandler(); + g.sh->moveToThread(g.sh); + +#ifdef Q_OS_WIN + g.o = new Overlay(); + g.o->setActive(g.s.bOverlayEnable); +#endif + + // Main Window + g.mw=new MainWindow(NULL); + g.mw->show(); + + g.l->log(Log::Information, MainWindow::tr("Welcome to Mumble.")); + + // Plugins + g.p = new Plugins(NULL); + g.p->rescanPlugins(); + + // And the start the last chosen audio system. + g.ai = AudioInputRegistrar::newFromChoice(); + g.ai->start(QThread::HighestPriority); + g.ao = AudioOutputRegistrar::newFromChoice(); + + a.setQuitOnLastWindowClosed(true); + res=a.exec(); + + g.s.save(); + g.l->saveSettings(); + + g.ao.reset(); + g.ai.reset(); + + g.sh->disconnect(); + + // This causes QT to complain. Fatally. It's either a bug in + // my code or the QT code. + // delete g.sh; + delete g.mw; + + delete g.db; + delete g.l; + +#ifdef Q_OS_WIN + delete g.o; +#endif + + delete g.qs; + + return res; +} diff --git a/src/mumble/mumble.pro b/src/mumble/mumble.pro new file mode 100644 index 000000000..86f7b5dae --- /dev/null +++ b/src/mumble/mumble.pro @@ -0,0 +1,57 @@ +include(../mumble.pri) + +TEMPLATE = app +QT += network sql opengl +TARGET = mumble +RC_FILE = mumble.rc +HEADERS += +SOURCES += +HEADERS = BanEditor.h ACLEditor.h Log.h AudioConfigDialog.h AudioStats.h AudioInput.h AudioOutput.h MainWindow.h ServerHandler.h About.h ConnectDialog.h GlobalShortcut.h TextToSpeech.h Settings.h Database.h VersionCheck.h Global.h PlayerModel.h Audio.h ConfigDialog.h Plugins.h LookConfig.h +SOURCES = BanEditor.cpp ACLEditor.cpp Log.cpp AudioConfigDialog.cpp AudioStats.cpp AudioInput.cpp AudioOutput.cpp main.cpp MainWindow.cpp ServerHandler.cpp About.cpp ConnectDialog.cpp Settings.cpp Database.cpp VersionCheck.cpp Global.cpp PlayerModel.cpp Audio.cpp ConfigDialog.cpp Plugins.cpp LookConfig.cpp +HEADERS += ../ACL.h ../Group.h ../Channel.h ../Connection.h ../Player.h +SOURCES += ../ACL.cpp ../Group.cpp ../Channel.cpp ../Message.cpp ../Connection.cpp ../Player.cpp +DIST += licenses.h mumble.ico firmumble.inc plugins/mumble_plugin.h overlay/overlay.h +INCLUDEPATH += ../../speex/include +LIBS += -Llib -L. +RESOURCES += mumble.qrc + +TRANSLATIONS = mumble_en.ts mumble_es.ts mumble_de.ts mumble_tr.ts mumble_id.ts mumble_fr.ts mumble_ru.ts mumble_it.ts mumble_pt.ts + +PRECOMPILED_HEADER = mumble_pch.h + +LIBS += -lspeex + +SUBDIRS = speex + +win32 { + HEADERS += DXAudioInput.h DXAudioOutput.h DXConfigDialog.h GlobalShortcut_win.h Overlay.h + SOURCES += DXAudioInput.cpp DXAudioOutput.cpp DXConfigDialog.cpp GlobalShortcut_win.cpp TextToSpeech_win.cpp Overlay_win.cpp os_win.cpp + INCLUDEPATH += /dev/dxsdk/Include /dev/SpeechSDK/include /dev/Boost/include/boost-1_33_1 + LIBS += -L/dev/SpeechSDK/Lib/i386 -ldsound -ldxguid -ldinput8 -lsapi -lole32 -lws2_32 + CONFIG += asio + DEFINES += WIN32 +} + +unix { + HEADERS += ALSAAudio.h GlobalShortcut_unix.h + SOURCES += ALSAAudio.cpp GlobalShortcut_unix.cpp TextToSpeech_unix.cpp + LIBS += -lasound -lXevie +} + +CONFIG(asio) { + INCLUDEPATH += ../../asio/common ../../asio/host ../../asio/host/pc + HEADERS += ASIOInput.h + SOURCES += ASIOInput.cpp +} + +lrel.target = mumble_en.qm +lrel.commands = lrelease mumble.pro +lrel.depends = mumble_en.ts + +lupd.target = mumble_en.ts +lupd.commands = lupdate mumble.pro + +QMAKE_EXTRA_TARGETS += lrel lupd + +PRE_TARGETDEPS = mumble_en.qm + diff --git a/src/mumble/mumble.qrc b/src/mumble/mumble.qrc new file mode 100644 index 000000000..03010274a --- /dev/null +++ b/src/mumble/mumble.qrc @@ -0,0 +1,31 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>../../icons/mumble.png.0</file> + <file>../../icons/mumble.png.1</file> + <file>../../icons/mumble.png.2</file> + <file>../../icons/talking_on.png</file> + <file>../../icons/talking_alt.png</file> + <file>../../icons/talking_off.png</file> + <file>../../icons/deafened_self.png</file> + <file>../../icons/deafened_server.png</file> + <file>../../icons/muted_self.png</file> + <file>../../icons/muted_server.png</file> + <file>../../icons/muted_local.png</file> + <file>../../icons/authenticated.png</file> + <file>../../icons/channel.png</file> + <file>../../icons/channel_linked.png</file> + <file>../../icons/config_basic.png</file> + <file>../../icons/config_dsound.png</file> + <file>../../icons/config_msgs.png</file> + <file>../../icons/config_plugin.png</file> + <file>../../icons/config_asio.png</file> + <file>../../icons/config_osd.png</file> + <file>../../icons/config_shortcuts.png</file> + <file>../../icons/config_ui.png</file> + <file>mumble_en.qm</file> + <file>mumble_es.qm</file> + <file>mumble_de.qm</file> + <file>mumble_tr.qm</file> + <file>mumble_ru.qm</file> +</qresource> +</RCC> diff --git a/src/mumble/mumble.rc b/src/mumble/mumble.rc new file mode 100644 index 000000000..065e2e3b1 --- /dev/null +++ b/src/mumble/mumble.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "..\\..\\icons\\mumble.ico" diff --git a/src/mumble/mumble_de.ts b/src/mumble/mumble_de.ts new file mode 100644 index 000000000..80a5d6f55 --- /dev/null +++ b/src/mumble/mumble_de.ts @@ -0,0 +1,2916 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1" language="de">
+<context>
+ <name>ACLEditor</name>
+ <message>
+ <location filename="ACLEditor.cpp" line="51"/>
+ <source>Active ACLs</source>
+ <translation>Aktive ACLs</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="52"/>
+ <source>Context</source>
+ <translation>Kontext</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="53"/>
+ <source>User/Group</source>
+ <translation>Benutzer/Gruppe</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="54"/>
+ <source>Permissions</source>
+ <translation>Rechte</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="102"/>
+ <source>Group</source>
+ <translation>Gruppe</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="57"/>
+ <source>Members</source>
+ <translation>Mitglieder</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="61"/>
+ <source>&Add</source>
+ <translation>&Hinzufügen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="63"/>
+ <source>&Remove</source>
+ <translation>&Entfernen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="65"/>
+ <source>&Up</source>
+ <translation>&Hoch</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="67"/>
+ <source>&Down</source>
+ <translation>&Runter</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="69"/>
+ <source>Inherit ACLs</source>
+ <translation>ACLs erben</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="88"/>
+ <source>Applies to this channel</source>
+ <translation>Betrifft diesen Kanal</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="90"/>
+ <source>Applies to sub-channels</source>
+ <translation>Betrifft Unterkanäle</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="109"/>
+ <source>User ID</source>
+ <translation>Benutzer ID</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="119"/>
+ <source>Deny</source>
+ <translation>Ablehnen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="121"/>
+ <source>Allow</source>
+ <translation>Erlauben</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="208"/>
+ <source>Remove</source>
+ <translation>Entfernen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="189"/>
+ <source>Inherit</source>
+ <translation>Erben</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="166"/>
+ <source>Inheritable</source>
+ <translation>Vererbbar</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="169"/>
+ <source>Inherited</source>
+ <translation>Vererbt</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="206"/>
+ <source>Add</source>
+ <translation>Hinzufügen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="211"/>
+ <source>Add to Remove</source>
+ <translation>Entfernen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="232"/>
+ <source>&Groups</source>
+ <translation>&Gruppen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="233"/>
+ <source>&ACL</source>
+ <translation>&ACL</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="235"/>
+ <source>&OK</source>
+ <translation>&OK</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="237"/>
+ <source>Accept changes</source>
+ <translation>Änderungen übernehmen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="240"/>
+ <source>This button will accept current groups/ACLs and send them to the server. Note that if you mistakenly remove write permission from yourself, the server will add it.</source>
+ <translation>Dieser Button will aktuelle Gruppen/ACLs akzeptieren und sie zum Server schicken. Falls man sich versehentlich Schreibrechte entzogen hat, wird der Server sie hinzufügen.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="241"/>
+ <source>&Cancel</source>
+ <translation>&Abbrechen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="243"/>
+ <source>Reject changes</source>
+ <translation>Änderungen verwerfen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="245"/>
+ <source>This button will cancel all changes and closes the dialog without updating the ACLs or groups on the server.</source>
+ <translation>Dieser Button macht alle Änderungen rückgängig und schließt den Dialog ohne ACLs oder Gruppen auf dem Server zu aktualisieren.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="321"/>
+ <source>List of entries</source>
+ <translation>Liste mit Einträgen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="323"/>
+ <source>This shows all the entries active on this channel. Entries inherited from parent channels will be show in italics.</source>
+ <translation>Dies zeigt alle aktive Einträge in diesem Kanal an. Von Elternkanälen geerbte Einträge werden kursiv angezeigt.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="324"/>
+ <source>Inherit ACL of parent?</source>
+ <translation>ACL von Eltenr erben?</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="326"/>
+ <source>This sets whether or not the ACL up the chain of parent channels are applied to this object. Only those entries that are marked in the parent as "Apply to sub-channels" will be inherited.</source>
+ <translation>Dies legt fest ob ACL am Anfang der Kette von Elternkanälen dieses Objekt betrifft. Nur solche Einträge die in den Eltern als "Betrifft Unterkanäle" markiert sind, werden beerbt.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="327"/>
+ <source>Add new entry</source>
+ <translation>Neuen Eintrag hinzufügen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="328"/>
+ <source>This adds a new entry, initially set with no permissions and applying to all.</source>
+ <translation>Dies fügt einen neuen Eintrag ohne Rechte hinzu.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="329"/>
+ <source>Remove entry</source>
+ <translation>Eintrag entfernen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="330"/>
+ <source>This removes the currently selected entry.</source>
+ <translation>Dies entfernt den aktuell ausgewählten Eintrag.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="331"/>
+ <source>Move entry up</source>
+ <translation>Eintrag nach oben</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="334"/>
+ <source>This moves the entry up in the list. As entries are evaluated in order, this may change the effective permissions of users. You cannot move an entry above an inherited entry, if you really need that you'll have to duplicate the inherited entry.</source>
+ <translation>Dies schiebt den Eintrag in der Liste nach oben. Da Einträge nach der Reihenfolge ausgewertet werden, könnte dies die Rechte der Nutzer betreffen. Man kann keinen Eintrag über einen geerbten Eintrag verschieben, sollte dies gewünscht sein, muss der Eintrag dupliziert werden.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="335"/>
+ <source>Move entry down</source>
+ <translation>Eintrag nach unten</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="337"/>
+ <source>This moves the entry down in the list. As entries are evaluated in order, this may change the effective permissions of users.</source>
+ <translation>Dies schiebt den Eintrag in der Liste nach unten. Da Einträge der Reihe nach ausgewertet werden, könnte dies die Rechte der Nutzer beeinträchtigen.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="338"/>
+ <source>Entry should apply to this channel</source>
+ <translation>Eintrag sollte auf diesen Kanal angewandt sein</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="339"/>
+ <source>This makes the entry apply to this channel.</source>
+ <translation>Dies übernimmt den Eintrag auf den Kanal.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="340"/>
+ <source>Entry should apply to sub-channels.</source>
+ <translation>Eintrag sollte Unterkanäle betreffen.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="341"/>
+ <source>This makes the entry apply to sub-channels of this channel.</source>
+ <translation>Dies lässt den Eintrag Unterkanäle des Kanals betreffen.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="342"/>
+ <source>Group this entry applies to</source>
+ <translation>Gruppe auf den dieser Eintrag angewandt wird</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="357"/>
+ <source>This controls which group of users this entry applies to.<br />Note that the group is evaluated in the context of the channel the entry is used in. For example, the default ACL on the Root channel gives <i>Write</i> permission to the <i>admin</i> group. This entry, if inherited by a channel, will give a user write privileges if he belongs to the <i>admin</i> group in that channel, even if he doesn't belong to the <i>admin</i> group in the channel where the ACL originated.<br />If a group name starts with a !, its membership is negated, and if it starts with a ~, it is evaluated in the channel the ACL was defined in, rather than the channel the ACL is active in. Order is important; <i>!~in</i> is valid, but <i>~!in</i> is not.<br />A few special predefined groups are:<br /><b>all</b> - Everyone will match.<br /><b>auth</b> - All authenticated users will match.<br /><b>sub,a,b,c</b> - User currently in a sub-channel minimum <i>a</i> common parents, and between <i>b</i> and <i>c</i> channels down the chain. See the website for more extensive documentation on this one.<br /><b>in</b> - Users currently in the channel will match (convenience for <i>sub,0,0,0</i>).<br /><b>out</b> - Users outside the channel will match (convenience for <i>!sub,0,0,0</i>).<br />Note that an entry applies to either a user or a group, not both.</source>
+ <translation>Dies kontrolliert welche Gruppe von Nutzern dieser Eintrag betrifft.<br />Die Gruppen werden im Kontext des Kanals ausgewertet in dem sich der Eintrag befindet. Zum Beispiel gibt der Standard ACL in den Wurzelkanälen <i>Schreib</i>-Rechte der <i>Admin</i>-Gruppe. Dieser Eintrag gibt den Nutzern - wenn von einem Kanal geerbt - Schreibrecht, wenn er zur Gruppe der <i>Admin</i>s in diesem Kanal gehört, selbst wenn er nicht zu der <i>Admin</i>-Gruppe gehört woher der ACL eigentlich stammt.<br />Beginnt ein Gruppenname mit einem "!", wird die Mitgliedschaft negiert und wenn es mit einem "~" beginnt, wird es in dem Kanal ausgewertet in dem die ACL definiert wurde. Die Reihenfolge ist wichtig; <i>!~in</i> ist gültig, <i>~!in</i> jedoch nicht. Einige wenige spezielle vordefinierte Gruppen sind: <br /><b>all</b> - Jeder trifft zu.<br /><b>auth</b> - Alle authentifizierten Nutzer treffen zu.<br /><b>in</b> - Nutzer im Channel treffen zu.<br /><b>out</b> - Nutzer außerhalb des Kanals treffen zu.<br />Ein Eintrag trifft dabei entweder auf einen Nutzer oder eine Gruppe zu, nicht auf beide.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="358"/>
+ <source>User this entry applies to</source>
+ <translation>Benutzer auf den der Eintrag angewandt wird</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="360"/>
+ <source>This controls which user this entry applies to. Just type in the user name and hit enter to query the server for a match.</source>
+ <translation>Dies kontrolliert welchen User dieser Eintrag betrifft. Namen eingeben und Enter drücken um den Server abzufragen.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="367"/>
+ <source>Allow %1</source>
+ <translation>%1 erlauben</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="368"/>
+ <source>Deny %1</source>
+ <translation>%1 ablehnen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="369"/>
+ <source>This grants the %1 privilege. If a privilege is both allowed and denied, it is denied.<br />%2</source>
+ <translation>Dies gibt %1 Privileg. Wenn ein Privileg erlaubt und ablehnt, wird es abgelehnt.<br />%2</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="370"/>
+ <source>This revokes the %1 privilege. If a privilege is both allowed and denied, it is denied.<br />%2</source>
+ <translation>Dies hebt das %1-Privileg auf. Wenn ein Privileg erlaubt und ablehnt, so wird es abgelehnt.<br />%2</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="374"/>
+ <source>List of groups</source>
+ <translation>Liste von Gruppen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="376"/>
+ <source>This is all the groups currently defined for the channel. To create a new group, just type in the name and press enter.</source>
+ <translation>Dies sind alle aktuell zum Kanal festgelegten Gruppen. Um eine Gruppe zu erstellen, den Namen eingeben und Enter drücken.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="377"/>
+ <source>Remove selected group</source>
+ <translation>Entferne ausgewählte Gruppe</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="379"/>
+ <source>This removes the currently selected group. If the group was inherited, it will not be removed from the list, but all local information about the group will be cleared.</source>
+ <translation>Dies entfernt die gewählte Gruppe. Wenn eine Gruppe geerbt wurde, wird sie nicht von der Liste entfernt aber alle lokalen Informationen über die Gruppe werden gelöscht.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="380"/>
+ <source>Inherit group members from parent</source>
+ <translation>Erbe Gruppenmitglieder von Eltern</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="382"/>
+ <source>This inherits all the members in the group from the parent, if the group is marked as <i>Inheritable</i> in the parent channel.</source>
+ <translation>Dies erbt alle Mitglieder in der Gruppe von den Eltern, wenn die Gruppe als <i>Vererbbar</i> im Elternkanal markiert ist.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="383"/>
+ <source>Make group inheritable to sub-channels</source>
+ <translation>Gruppe zu Unterkanal vererbbar machen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="385"/>
+ <source>This makes this group inheritable to sub-channels. If the group is non-inheritable, sub-channels are still free to create a new group with the same name.</source>
+ <translation>Dies macht die Gruppe zu Unterkanälen vererbbar. Ist die Gruppe nicht-vererbbar, können Unterkanäle Gruppen mit demselben Namen erstellen.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="386"/>
+ <source>Group was inherited from parent channel</source>
+ <translation>Gruppe wurde vom Elternkanal geerbt</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="388"/>
+ <source>This indicates that the group was inherited from the parent channel. You cannot edit this flag, it's just for information.</source>
+ <translation>Dies zeigt an, dass die Gruppe vom Elternkanal geerbt wurde. Die Attribute dienen nur zu Informationszwecken und können nicht bearbeitet werden.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="389"/>
+ <source>Add member to group</source>
+ <translation>Mitglied zu Gruppe hinzufügen</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="390"/>
+ <source>Type in the name of a player you wish to add to the group and press enter.</source>
+ <translation>Namen des Spielers eingeben, der zur Gruppe hinzugefügt werden soll.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="391"/>
+ <source>Remove member from group</source>
+ <translation>Entferne Mitglied aus der Gruppe</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="392"/>
+ <source>Type in the name of a player you wish to remove from the group and press enter.</source>
+ <translation>Namen des Players eingeben, der aus der Gruppe entfernt werden soll.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="45"/>
+ <source>Mumble - Edit ACL for %1</source>
+ <translation>Mumble - ACL von %1 editieren</translation>
+ </message>
+</context>
+<context>
+ <name>ASIOConfig</name>
+ <message>
+ <location filename="ASIOInput.cpp" line="113"/>
+ <source>Device selection</source>
+ <translation>Geräteauswahl</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="114"/>
+ <source>Capabilities</source>
+ <translation>Möglichkeiten</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="115"/>
+ <source>Channels</source>
+ <translation>Kanäle</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="116"/>
+ <source>Configure input channels</source>
+ <translation>Eingabekanäle konfigurieren</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="121"/>
+ <source>This will configure the input channels for ASIO. Make sure you select at least one channel as microphone and speaker. <i>Microphone</i> should be where your microphone is attached, and <i>Speaker</i> should be a channel that samples "What you hear".<br />For example, on the Audigy 2 ZS, a good selection for Microphone would be "Mic L" while Speaker should be "Mix L" and "Mix R"</source>
+ <translation>Dies konfiguriert die Eingangskanäle für ASIO. Es muss sichergestellt werden, dass wenigstens ein Kanal als Mikrofon und Lautsprecher ausgewählt wurde. <br />Eine gute Auswahl für eine Audigy 2 ZS wäre für Mikrofon "Mic L", während es für die Lautsprecher "Mix L" und "Mix R" sein sollte</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="125"/>
+ <source>Device to use for microphone</source>
+ <translation>Mikrofongerät</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="127"/>
+ <source>This chooses what device to query. You still need to actually query the device and select which channels to use.</source>
+ <translation>Dies wählt das abzufragende Gerät aus. Man muss trotzdem das Gerät abfragen und die zu nutzenden Kanäle auswählen.</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="128"/>
+ <source>Device</source>
+ <translation>Gerät</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="132"/>
+ <source>Query selected device</source>
+ <translation>Gewähltes Gerät abfragen</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="135"/>
+ <source>This queries the selected device for channels. Be aware that many ASIO drivers are buggy to the extreme, and querying them might cause a crash of either the application or the system.</source>
+ <translation>Dies fragt das ausgewählte Gerät nach Kanälen ab. Achtung: Viele ASIO-Treiber sind extrem buggy und sie abzufragen könnte die Anwendung oder das System crashen.</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="139"/>
+ <source>Configure selected device</source>
+ <translation>Ausgewähltes Gerät konfigurieren</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="142"/>
+ <source>This configures the selected device. Be aware that many ASIO drivers are buggy to the extreme, and querying them might cause a crash of either the application or the system.</source>
+ <translation>Dies konfiguriert das ausgewählte Gerät. Achtung: Viele ASIO-Treiber sind extrem buggy und sie abzufragen könnte die Anwendung oder das System crashen.</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="157"/>
+ <source>Driver name</source>
+ <translation>Treibername</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="162"/>
+ <source>Buffersize</source>
+ <translation>Puffergröße</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="178"/>
+ <source>Microphone</source>
+ <translation>Mikrofon</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="208"/>
+ <source><-</source>
+ <translation><-</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="205"/>
+ <source>-></source>
+ <translation>-></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="200"/>
+ <source>Unused</source>
+ <translation>Ungenutzt</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="222"/>
+ <source>Speakers</source>
+ <translation>Lautsprecher</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="318"/>
+ <source>%1 (ver %2)</source>
+ <translation>%1 (ver %2)</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="322"/>
+ <source>%1 ms -> %2 ms (%3 ms resolution) %4Hz</source>
+ <translation>%1 ms -> %2 ms (%3 ms Auflösung) %4Hz</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="324"/>
+ <source>%1 ms -> %2 ms (%3 ms resolution) %4Hz -- Unusable</source>
+ <translation>%1 ms -> %2 ms (%3 ms Auflösung) %4Hz -- Unbenutzbar</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="400"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="396"/>
+ <source>ASIO Initialization failed: %1</source>
+ <translation>ASIO-Initialisierung fehlgeschlagen: %1</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="400"/>
+ <source>Failed to instantiate ASIO driver</source>
+ <translation type="unfinished">Konnte ASIO-Treiber nicht instanziieren</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="437"/>
+ <source>ASIO</source>
+ <translation>ASIO</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="130"/>
+ <source>&Query</source>
+ <translation>&Abfragen</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="137"/>
+ <source>&Configure</source>
+ <translation>&Konfigurieren</translation>
+ </message>
+</context>
+<context>
+ <name>ASIOInput</name>
+ <message>
+ <location filename="ASIOInput.cpp" line="580"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="512"/>
+ <source>You need to select at least one microphone and one speaker source to use ASIO. If you just need microphone sampling, use DirectSound.</source>
+ <translation>Es muss mindestens ein Mikrofon und eine Lautsprecherquelle ausgewählt sein um ASIO zu nutzen. Um lediglich Mikrofonsampling zu nutzen, DirectSound auswählen.</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="580"/>
+ <source>Opening selected ASIO device failed. No input will be done.</source>
+ <translation>Öffnen des ausgewählten ASIO-Gerätes fehlgeschlagen. Es wird kein Eingang benutzt.</translation>
+ </message>
+</context>
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="About.cpp" line="53"/>
+ <source><h3>Mumble v0.9.5</h3><p>Copyright 2005-2006 Thorvald Natvig<br />slicer@users.sourceforge.net</p><p><b>A voice-chat utility for gamers</b></p><p><tt>http://mumble.sourceforge.net/</tt></p></source>
+ <translation type="obsolete"><h3>Mumble v0.9.4</h3><p>Copyright 2005-2006 Thorvald Natvig<br />slicer@users.sourceforge.net</p><p>Deutsche Übersetzung von Matthias Vogelgesang<br /> m0ta@users.sourceforge.net</p><p><b>Ein Voicechat-Programm für Spieler</b></p><p><tt>http://mumble.sourceforge.net/</tt></p></translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="60"/>
+ <source>&About Mumble</source>
+ <translation>Ü&ber Mumble</translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="61"/>
+ <source>&License</source>
+ <translation>&Lizenz</translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="63"/>
+ <source>OK</source>
+ <translation>OK</translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="54"/>
+ <source><h3>Mumble v%1</h3><p>Copyright 2005-2006 Thorvald Natvig<br />slicer@users.sourceforge.net</p><p><b>A voice-chat utility for gamers</b></p><p><tt>http://mumble.sourceforge.net/</tt></p></source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AboutSpeexDialog</name>
+ <message>
+ <location filename="About.cpp" line="84"/>
+ <source><h3>About Speex</h3><p><tt>http://www.speex.org/</tt></p><p>This program uses Speex version %1</p><p>Speex is used for echo cancellation, noise<br />filtering, voice activity detection and speech<br />compression.</p></source>
+ <translation><h3>Über Speex</h3><p><tt>http://www.speex.org/</tt></p><p>Dieses Programm nutzt Speex Version %1</p><p>Speex wird zur Echoreduktion, Geräuschfilterung<br />, Stimmaktivierungserkennung und<br />Sprachkompression genutzt.</p></translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="86"/>
+ <source>OK</source>
+ <translation>OK</translation>
+ </message>
+</context>
+<context>
+ <name>AudioConfigDialog</name>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="51"/>
+ <source>Interfaces</source>
+ <translation>Schnittstellen</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="52"/>
+ <source>Transmission</source>
+ <translation>Übertragung</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="53"/>
+ <source>Compression</source>
+ <translation>Kompression</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="54"/>
+ <source>Jitter Buffer</source>
+ <translation>Jitterpuffer</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="67"/>
+ <source>Input</source>
+ <translation>Eingang</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="70"/>
+ <source>Input method for audio</source>
+ <translation>Inputmethode für Audio</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="71"/>
+ <source><b>This is the input method to use for audio.</b><br />Most likely you want to use DirectSound.</source>
+ <translation><b>Dies ist die Eingabemethode für Audo.</b><br />Am wahrscheinlichsten sollte DirectSound ausgewählt werden.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="85"/>
+ <source>Output</source>
+ <translation>Ausgabe</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="88"/>
+ <source>Output method for audio</source>
+ <translation>Ausgabemethode für Audio</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="89"/>
+ <source><b>This is the output method to use for audio.</b><br />Most likely you want to use DirectSound.</source>
+ <translation><b>Dies ist die Ausgabemethode für Audio.</b><br />Am wahrscheinlichsten sollte DirectSound ausgewählt werden.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="99"/>
+ <source>Continuous</source>
+ <translation>Kontinuierlich</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="100"/>
+ <source>Voice Activity</source>
+ <translation>Stimmaktivierung</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="101"/>
+ <source>Push To Talk</source>
+ <translation>Push-To-Talk</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="103"/>
+ <source>Transmit</source>
+ <translation>Übertragen</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="106"/>
+ <source>When to transmit your speech</source>
+ <translation>Art der Sprachübertragung</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="110"/>
+ <source><b>This sets when speech should be transmitted.</b><br /><i>Continuous</i> - All the time<br /><i>Voice Activity</i> - When you are speaking clearly.<br /><i>Push To Talk</i> - When you hold down the hotkey set under <i>Shortcuts</i>.</source>
+ <translation><b>Dies legt fest, wann Sprache übertragen werden soll.</b><br /><i>Kontinuierlich</i> - Die ganze Zeit<br /><i>Stimmaktivierung</i> - Sobald man deutlich spricht.</br><i>Push-To-Talk</i> - Wenn ein Hotkey gedrückt wird (siehe <i>Shortcuts</i>).</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="121"/>
+ <source>Voice Hold</source>
+ <translation>Stimme halten</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="128"/>
+ <source>How long to keep transmitting after silence</source>
+ <translation>Wie lange nach Einsetzen der Stille übertragen werden soll</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="131"/>
+ <source><b>This selects how long after a perceived stop in speech transmission should continue.</b><br />Set this higher if your voice breaks up when you speak (seen by a rapidly blinking voice icon next to your name).<br />Only makes sense when used with Voice Activity transmission.</source>
+ <translation><b>Dies wählt aus, wie lange nach Beenden des Gesprächs noch übertragen werden soll.</b><br />Höhere Werte nutzen, wenn die Stimme plötzlich abbricht (erkennbar an einem flackerndem Voice-Icon neben dem Namen).<br />Dies ist nur bei Stimmaktivierungs-Übertragung sinnvoll.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="144"/>
+ <source>Audio per packet</source>
+ <translation>Audio pro Paket</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="153"/>
+ <source>How many audio frames to send per packet</source>
+ <translation>Wieviele Audioframes pro Paket zu senden sind</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="156"/>
+ <source><b>This selects how many audio frames should be put in one packet.</b><br />Increasing this will increase the latency of your voice, but will also reduce bandwidth requirements.</source>
+ <translation><b>Dies legt fest, wieviele Audioframes in ein Paket gepackt werden sollen.</b><br />Dies zu erhöhen, steigert auch die Latenz der Sprache, senkt aber ebenso Bandbreitenbedarf.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="161"/>
+ <source>Use TCP mode</source>
+ <translation>Benutze TCP-Modus</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="163"/>
+ <source>Use TCP Compatibility mode</source>
+ <translation>Benutze TCP-Kompatibilitätsmodus</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="168"/>
+ <source><b>Enable TCP Compatibility mode</b>.<br />This will make Mumble use only TCP when communicating with the server. This will increase overhead and cause lost packets to produce noticeable pauses in communication, so this should only be used if you are unable to use the default (which uses UDP for voice and TCP for control).</source>
+ <translation><b>TCP-Kompatibilitätsmodus aktivieren</b>. <br />Dies lässt Mumble nur TCP zu Kommunikation mit dem Server nutzen. Es wird den Overhead erhöhen und bei verlorenen Paketen hörbare Pausen produzieren. Es wird nur empfohlen wenn die Standardeinstellungen nicht funktioniert.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="170"/>
+ <source>Automatic Reconnect</source>
+ <translation>Automatisches Neuverbinden</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="172"/>
+ <source>Reconnect when disconnected</source>
+ <translation>Bei Unterbrechung erneut verbinden</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="176"/>
+ <source><b>Reconnect when disconnected</b>.<br />This will make Mumble try to automatically reconnect after 10 seconds if your server connection fails.</source>
+ <translation><b>Neuverbinden nach Verbindungsabbruch</b>.<br />Dies führt eine automatische Neuverbindung nach 10 Sekunden durch, wenn die Serververbindung abbricht.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="184"/>
+ <source>Maximum bandwidth used for sent audio</source>
+ <translation>Maximale Bandbreite für Audiodaten</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="189"/>
+ <source><b>This shows peak outgoing bandwidth used.</b><br />This shows the peak amount of bandwidth sent out from your machine. Audio bitrate is the maximum bitrate (as we use VBR) for the audio data alone. Position is the bitrate used for positional information. Overhead is our framing and the IP packet headers (IP and UDP is 90% of this overhead).</source>
+ <translation><b>Dies zeigt die Spitzenbandbreite für Ausgang an.</b><br />Dies zeigt die maximale Menge an Bandbreite zum Senden an. Audio Bitrate ist die maximale Bitrate (da VBR genutzt wird) nur für die Audio Daten. Position ist die Bitrate für Positionsinformationen. Overhead ist das Framing und die IP Paketheader (TCP/IP und UPD machen 80% des Overhead aus).</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="191"/>
+ <source>Outgoing Bitrate</source>
+ <translation>Ausgehende Bitrate</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="209"/>
+ <source>Quality</source>
+ <translation>Qualität</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="216"/>
+ <source>Quality of compression (peak bandwidth)</source>
+ <translation>Qualität der Kompression (Spitzenbandbreite)</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="218"/>
+ <source><b>This sets the quality of compression.</b><br />This determines how much bandwidth Mumble is allowed to use for outgoing audio.</source>
+ <translation><b>Dies setzt die Qualität der Kompression.</b><br />Dies bestimmt wieviel Bandbreite Mumble für ausgehendes Audio nutzen darf.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="231"/>
+ <source>Complexity</source>
+ <translation>Komplexität</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="237"/>
+ <source>Complexity of compression (CPU)</source>
+ <translation>Komplexität der Kompression (CPU)</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="240"/>
+ <source><b>This sets the complexity of compression.</b><br />This determines how much CPU Mumble is allowed to use to increase transmitted voice quality. Settings above 5 give only marginal gain.</source>
+ <translation><b>Dies setzt die Komplexität der Kompression.</b><br />Es bestimmt wieviel CPU-Zeit Mumble nutzen darf um die übertragene Sprachqualität zu steigern. Einstellungen über 5 bringen nur minimale Verbesserungen.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="253"/>
+ <source>Amp</source>
+ <translation>Verstärkung</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="259"/>
+ <source>Maximum amplification of input sound</source>
+ <translation>Maximale Verstärkung des Eingangssounds</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="270"/>
+ <source><b>Maximum amplification of input.</b><br />Mumble normalizes the input volume before compressing, and this sets how much it's allowed to amplify.<br />The actual level is continually updated based on your current speech pattern, but it will never go above the level specified here.<br />If the <i>Microphone loudness</i> 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.<br />Ideally, set it so <i>Microphone Loudness * Amplification Factor >= 100</i>, even when you're speaking really soft.<br /><br />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.</source>
+ <translation><b>Maximale Verstärkung des Eingangs.</b><br />Mumble normalisiert die Eingangslautstärke vor der Kompression, wobei dies einstellt wie sehr verstärkt werden darf.<br />Der tatsächliche Level wird kontinuierlich, vom Sprachmuster abhängig, aktualisiert; allerdings nie höher als hier festgelegt.<br />Wenn die Mikrofonlautstärke in den Audiostatistiken um 100% liegt, sollte man dies auf 2.0 setzen. Für Leute die dies kaum erreichen, muss es deutlich höher angesetzt werden.<br />Idealerweise sollte es folgendermaßen gesetzt werden: <i>Mikrofon Lautstärke * Verstärkungsfaktor >= 100</i>, selbst wenn man wirklich leise spricht.<br />Es ist nicht schädlich dies auf das Maximum zu setzen, aber Mumble wird auch Umgebungsgeräusche aufnehmen.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="288"/>
+ <source>Default Jitter Buffer</source>
+ <translation>Standard Jitter-Puffer</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="295"/>
+ <source>How long to prebuffer on first packet</source>
+ <translation>Wie lange beim ersten Paket vorzupuffern</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="302"/>
+ <source><b>This sets the default buffer size for the jitter buffer</b>.<br />All incoming audio is buffered, and the jitter buffer continually tries to push the buffer to the minimum sustainable by your network, so latency can be as low as possible. This sets the default buffer size to use on the first packet to arrive from a new speaker, or when a speaker using Voice Activity or Push-To-Talk just started talking again. If the start of sentences you hear is very jittery, increase this value.</source>
+ <translation><b>Dies legt die Standard-Puffergröße für den Jitterpuffer fest</b>.<br />Ankommende Audiodaten werden gepuffert und der Jitterpuffer versucht ständig diesen Puffer so schnell wie möglich ans Netzwerk zu übertragen, um die Latenz zu verringern.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="321"/>
+ <source>Basic Audio</source>
+ <translation>Grundeinstellungen</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="369"/>
+ <source>%1 ms</source>
+ <translation>%1 ms</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="350"/>
+ <source>%1s</source>
+ <translation>%1s</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="418"/>
+ <source>%1kbit/s (Audio %2, Position %4, Overhead %3)</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AudioStats</name>
+ <message>
+ <location filename="AudioStats.cpp" line="342"/>
+ <source>Peak microphone level</source>
+ <translation>Höchster Mikrofonlevel</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="347"/>
+ <source>Peak speaker level</source>
+ <translation>Höchster Lautsprecherlevel</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="352"/>
+ <source>Peak clean level</source>
+ <translation>Höchster sauberer Level</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="357"/>
+ <source>Microphone loudness</source>
+ <translation>Mikrofonlautstärke</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="362"/>
+ <source>Mic Signal-To-Noise</source>
+ <translation>Mic Signal-Geräusch</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="367"/>
+ <source>Speech Probability</source>
+ <translation>Sprachwahrscheinlichkeit</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="372"/>
+ <source>Audio bitrate</source>
+ <translation>Audio Bitrate</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="432"/>
+ <source>Peak power in last frame</source>
+ <translation>Spitzenpower im letzten Frame</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="426"/>
+ <source>This shows the peak power in the last frame (20 ms), and is the same measurement as you would usually find displayed as "input power". Please disregard this and look at <b>Loudness</b> instead, which is much more steady and disregards outliers.</source>
+ <translation>Dies zeigt die Spitzenleistung im letzten Frame (20ms) an und ist als Maß äquivalent zu "Input Power". Stattdessen bitte <b>Lautstärke</b> nutzen.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="431"/>
+ <source>This shows the peak power in the last frame (20 ms) of the speakers. 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.</source>
+ <translation>Dies zeigt die Spitzenpower im letzten Frame (20ms) des Lautsprechers an. Solange keine Multikanal Sampling Methode (wie ASIO) genutzt wird, wird dieser Wert 0 sein. Hat man solch ein Setup konfiguriert und der Wert ist trotzdem 0 während man Klänge von anderen Programmen abspielt, arbeit das Setup nicht korrekt.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="438"/>
+ <source>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.<br />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 players with echoes.</source>
+ <translation>Dies zeigt die Spitzenpower im letzten Frame (20ms) nach dem Processing an. Im Idealfall sollte dies -96 dB betragen, wann man nicht spricht. Die Realität zeigt, dass man in einem Tonstudio etwa -60 dB und zuhause -20 dB erwarten kann. When man spricht, sollte dies auf -5 bis -10 dB ansteigen.<br />Nutzt man Echoauslöschung und der Wert steigt auf mehr als -15 dB wenn man nicht spricht, sind die Audioeinstellungen nicht korrekt und man stört andere Spieler mit Echos.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="439"/>
+ <source>How close the current input level is to ideal</source>
+ <translation>Wie nah das aktuelle Eingangssignal zum Idealzustand ist</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="446"/>
+ <source>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.<br /><b>Talk loud, as you would when you're upset over getting fragged by a noob.</b><br />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</source>
+ <translation>Dies zeigt an wie nah die Eingangssignallautstärke am Ideal ist. Um die Mikrofonlautstärke anzupassen, sollte das Programm gestartet werden mit dem die Aufnahmelautstärke angepasst wird.<br /><b>Nun sollte man, so laut wie es im Spiel vorkommt, sprechen</b><br />Die Lautstärke muss nun so nah wie möglich an die 100% angepasst werden, aber nicht darüber hinaus. Wenn dem so ist, wird es hörbares Clipping geben, was die Soundqualität mindert</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="447"/>
+ <source>Signal-To-Noise ratio from the microphone</source>
+ <translation>Signal-Geräusch Rate des Mikrofons</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="452"/>
+ <source>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.<br />If this value is below 1.0, there's more noise than voice in the signal, and so quality is reduced.<br />There is no upper limit to this value, but don't expect to see much above 40-50 without a sound studio.</source>
+ <translation>Dies ist die Signal-Geräusch-Rate (SNR) des Mikrofons im letzten Frame (20ms). Es zeigt an, um wieviel klarer die Stimme im Vergleich zum Geräusch ist.<br />Ist der Wert unter 1.0, gibt es mehr Geräusche als Stimme im Signal, weshalb die Qualität reduziert ist.<br />Es gibt keine Obergrenze, aber ein Wert über 40-50 kann ohne Tonstudio nicht erwartet werden.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="453"/>
+ <source>Probability of speech</source>
+ <translation>Sprachwahrscheinlichkeit</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="458"/>
+ <source>This is the probability that the last frame (20 ms) was speech and not environment noise.<br />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'.<br />If this is in bold font, it means Mumble is currently transmitting (if you're connected).</source>
+ <translation>Dies ist die Wahrscheinlichkeit, dass der letzte Frame (20ms) Sprache und kein Umgebungsgeräusch war.<br />Die Stimmaktivierungserkennung hängt davon ab, wie es eingestellt ist. Der Trick ist, dass die Mitte eines Satzes immer als Sprache erkannt wird; problematisch sind Pausen zwischen Wörtern und der Anfang des Satzes.<br />Ist dies fett geschrieben, bedeutet dies, dass Mumble gerade übertragt (wenn man verbunden ist).</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="459"/>
+ <source>Bitrate of last frame</source>
+ <translation>Bitrate des letzten Frame</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="462"/>
+ <source>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. To adjust the peak bitrate, adjust <b>Compression Complexity</b> in the Settings dialog.</source>
+ <translation>Dies ist die Audiobitrate des letzten komprimierten Frame (20ms) und als solche wird es hoch und runter springen während das VBR die Qualität anpasst. Um die Spitzenbitrate einzustellen muss <b>Kompressionsqualität</b> im Einstellungsdialog angepasst werden.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="464"/>
+ <source>Power spectrum of input signal and noise estimate</source>
+ <translation>Spektrum des Eingangssignals und Geräuschabschätzung</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="471"/>
+ <source>This shows the power spectrum of the current input signal (red line) and the current noise estimate (filled blue).<br />All amplitudes are multiplied by 30 to show the interesting parts (how much more signal than noise is present in each waveband).<br />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.</source>
+ <translation>Dies zeigt das Powerspektrum des Eingangssignals (rote Linie) und die Geräuschabschätzung (blau gefüllt) an.<br />Alle Amplituden werden mit 30 multipliziert um anzuzeigen wieviel mehr Signal als Geräusch im Wellenband vorhanden ist.<br />Dies ist vermutlich nur von Interesse, wenn man die Geräuschbedingungen des Mikrofones präzise einstellt. Unter guten Voraussetzungen sollte nur eine winzige blaue Stelle unten zu sehen sein. Ist mehr als die Hälfte des Graphen blau, hat man eine ungünstige Geräuschkulisse.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="474"/>
+ <source>Weights of the echo canceller</source>
+ <translation>Gewichte des Echoauslöschers</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="487"/>
+ <source>This shows the weights of the echo canceller, with time increasing downwards and frequency increasing to the right.<br />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.<br />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. <br />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.<br />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.</source>
+ <translation>Dies zeigt die Gewichte des Echoauslöschers, mit der Zeit auf der Y- und der Frequenz auf der X-Achse.<br />Idealerweise sollte dies schwarz sein, was kein Echo bedeutet. Normalerweise hat man einen oder mehrere horizontale Streifen von blauer Farbe, die das zeitverzögerte Echo darstellen. Man sollte sehen, wie die Gewichte in Echtzeit aktualisiert werden.<br />So lange man keine Echoquelle hat, wird man keine nützlichen Daten hier sehen, weshalb man etwas Musik abspielen sollte.<br />Man kann zwischen Real- und Imaginäranteilen des Frequenzdomänen-Gewischte oder alternativ den berechneten Modulus und Phase auswählen. Am nützlichsten ist hierbei Modulus, was die Amplitude des Echos angibt und anzeigt wieviel vom ausgehenden Signal bei diesem Zeitschritt entfernt wird. Die anderen Ansichten sind nützlich für diejenigen die den Echoauslöschungs-Algorithmus finetunen wollen.<br />Achtung: Wenn das Gesamtbild im Modulus-Modus massiv fluktuiert, kann der Echoauslöscher keine Korrelation zwischen den beiden Eingangsquellen (Lautsprecher und Mikrofon) herstellen. Entweder hat man eine lange Verzögerung des Echos oder eine der Eingangsquellen sind falsch konfiguriert.</translation>
+ </message>
+</context>
+<context>
+ <name>BanEditor</name>
+ <message>
+ <location filename="BanEditor.cpp" line="42"/>
+ <source>&Add</source>
+ <translation>&Hinzufügen</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="43"/>
+ <source>&Update</source>
+ <translation>&Updaten</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="44"/>
+ <source>&Remove</source>
+ <translation>&Entfernen</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="69"/>
+ <source>&OK</source>
+ <translation>&OK</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="71"/>
+ <source>Accept changes</source>
+ <translation>Änderungen übernehmen</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="74"/>
+ <source>This button will accept current groups/ACLs and send them to the server. Note that if you mistakenly remove write permission from yourself, the server will add it.</source>
+ <translation>Dieser Button wird aktuelle Gruppen/ACLs akzeptieren und sie an den Server senden. Wenn versehentlich Schreibrechte entfernt wurden, wird der Server sie hinzufügen.</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="75"/>
+ <source>&Cancel</source>
+ <translation>&Abbrechen</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="77"/>
+ <source>Reject changes</source>
+ <translation>Änderungen nicht übernehmen</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="79"/>
+ <source>This button will cancels all changes and closes the dialog without updating the ACLs or groups on the server.</source>
+ <translation>Dieser Button macht alle Änderungen rückgängig und schließt den Dialog ohne ACLs oder Gruppen auf dem Server zu aktualisieren. </translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="37"/>
+ <source>Mumble - Edit Bans</source>
+ <translation>Mumble - Bans editieren</translation>
+ </message>
+</context>
+<context>
+ <name>ChanACL</name>
+ <message>
+ <location filename="../ACL.cpp" line="129"/>
+ <source>W</source>
+ <translation>W</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="131"/>
+ <source>T</source>
+ <translation>T</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="133"/>
+ <source>E</source>
+ <translation>E</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="135"/>
+ <source>S</source>
+ <translation>S</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="137"/>
+ <source>A</source>
+ <translation>A</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="139"/>
+ <source>M</source>
+ <translation>M</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="141"/>
+ <source>K</source>
+ <translation>K</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="143"/>
+ <source>C</source>
+ <translation>C</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="145"/>
+ <source>L</source>
+ <translation>L</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="155"/>
+ <source>None</source>
+ <translation>Kein</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="157"/>
+ <source>Write</source>
+ <translation>Schreiben</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="159"/>
+ <source>Traverse</source>
+ <translation>Traverse</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="161"/>
+ <source>Enter</source>
+ <translation>Enter</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="163"/>
+ <source>Speak</source>
+ <translation>Sprechen</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="165"/>
+ <source>AltSpeak</source>
+ <translation>AltSpeak</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="167"/>
+ <source>Mute/Deafen</source>
+ <translation>Stumm-/Taubstellen</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="169"/>
+ <source>Move/Kick</source>
+ <translation>Verschieben/Kick</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="171"/>
+ <source>Make channel</source>
+ <translation>Channel erstellen</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="173"/>
+ <source>Link channel</source>
+ <translation>Channel verbinden</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="183"/>
+ <source>This represents no privileges.</source>
+ <translation>Dies repräsentiert keine Privilegien.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="186"/>
+ <source>This represents total access to the channel, including the ability to change group and ACL information. This privilege implies all other privileges.</source>
+ <translation>Dies repräsentiert vollen Zugriff zum Channel, einschließlich Fähigkeiten Gruppen und ACL-Informationen zu ändern. Dieses Privileg impliziert alle anderen Privilegien.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="190"/>
+ <source>This represents the permission to traverse the channel. If a user is denied this privilege, he will be unable to access this channel and any sub-channels in any way, regardless of other permissions in the sub-channels.</source>
+ <translation>Dies repräsentiert Rechte um den Channel zu durchlaufen. Wenn ein Benutzer dieses Privileg nicht hat, kann er auf keinen Kanal oder Unterkanal zugreifen, egal welche Rechte er in den Unterkanälen hat.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="193"/>
+ <source>This represents the permission to join the channel. If you have a hierarchical channel structure, you might want to give everyone Traverse, but restrict Enter in the root of your hierarchy.</source>
+ <translation>Dies repräsentiert Rechte um den Channel zu betreten. Hat man eine hierarchische Kanalstruktur, sollte man jedem Traverse geben aber Enter in der Wurzel der Hierarchie einschränken.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="197"/>
+ <source>This represents the permission to speak in a channel. Users without this privilege will be suppressed by the server (seen as muted), and will be unable to speak until they are unmuted by someone with the appropriate privileges.</source>
+ <translation>Dies repräsentiert das Recht in einem Kanal zu sprechen. Benutzer ohne dieses Privileg werden vom Server unterdrückt (gekennzeichnet als stumm) and können solange nicht sprechen bis es jemand mit entsprechenden Rechten aufhebt.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="201"/>
+ <source>This represents the permission to speak in a channel with flagged speech. This works exactly like the <i>speak</i> privilege, but applies to packets spoken with AltPushToTalk held down. This may be used to broadcast to a hierarchy of channels without linking.</source>
+ <translation>Dies repräsentiert das Recht in einem Kanal mit belegter Sprache zu sprechen. Dies funktioniert genauso wie das <i>Sprechen</i>-Privileg, betrifft aber Pakete gesprochen mit Alt-Push-To-Talk. Es wird genutzt um Sprache in einer Hierarchie von Kanälen zu verbreiten ohne sie zu verbinden.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="204"/>
+ <source>This represents the permission to mute and deafen other players. Once muted, a player will stay muted until he is unmuted by another privileged player or reconnects to the server.</source>
+ <translation>Dies repräsentiert das Recht andere Spieler stumm- oder taubzustellen. Einmal stummgestellt bleibt ein Spieler stumm bis ein anderer Spieler mit entsprechenden Privilegien dies ändert oder sich neu zum Server verbunden wird.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="209"/>
+ <source>This represents the permission to move a player to another channel or kick him from the server. To actually move the player, either the moving player must have Move/Kick privileges in the destination channel, or the player must normally be allowed to enter the channel. Players with this privilege can move players into channels the target player normally wouldn't have permission to enter.</source>
+ <translation>Dies repräsentiert das Recht einen Spieler in einen anderen Kanal zu verschieben oder ihn vom Server zu kicken. Um einen Spieler zu verschieben, muss der verschiebende Spieler entweder Verschieben/Kick-Rechte im Zielkanal haben, oder der Spieler muss erlaubt sein, den Kanal zu betreten. Spieler mit diesem Privileg können Spieler in Kanäle verschieben, die normalerweise nicht das Recht haben, diese zu betreten.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="212"/>
+ <source>This represents the permission to make sub-channels. The player making the sub-channel will be added to the admin group of the sub-channel.</source>
+ <translation>Dies repräsentiert das Recht Unterkanäle zu erstellen. Der Spieler der den Unterkanal erstellt, gehört automatisch zur Admingruppe des Kanals.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="216"/>
+ <source>This represents the permission to link channels. Players in linked channels hear each other, as long as the speaking player has the <i>speak</i> privilege in the channel of the listener. You need the link privilege in both channels to create a link, but just in either channel to remove it.</source>
+ <translation>Dies repräsentiert das Recht Kanäle zu verbinden. Spieler in verbundenen Kanälen können sich gegenseitig hören, solange der sprechende Spieler das <i>Sprechen</i>-Privileg im Kanal des Hörers hat. Man benötigt Verbindungsprivilegien in beiden Kanälen um eine Verbindung aufzubauen, aber nur einen in beiden um sie aufzuheben.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigDialog</name>
+ <message>
+ <location filename="ConfigDialog.cpp" line="57"/>
+ <source>Mumble Configuration</source>
+ <translation>Mumble-Konfiguration</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="69"/>
+ <source>&OK</source>
+ <translation>&OK</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="72"/>
+ <source>Accept changes</source>
+ <translation>Änderungen übernehmen</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="74"/>
+ <source>This button will accept current settings and return to the application.<br />The settings will be stored to disk when you leave the application.</source>
+ <translation>Dieser Button übernimmt die aktuellen Einstellungen und kehrt zur Anwendung zurück.<br />Die Einstellungen werden bei Beenden der Anwendung gespeichert.</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="75"/>
+ <source>&Cancel</source>
+ <translation>&Abbrechen</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="77"/>
+ <source>Reject changes</source>
+ <translation>Änderungen verwerfen</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="79"/>
+ <source>This button will reject all changes and return to the application.<br />The settings will be reset to the previous positions.</source>
+ <translation>Der Button wird alle Änderungen verwerfen und zur Anwendung zurückkehren.<br />Die Einstellungen werden auf die vorherigen Position zurückgesetzt.</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="80"/>
+ <source>&Apply</source>
+ <translation>Ü&bernehmen</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="82"/>
+ <source>Apply changes</source>
+ <translation>Änderungen übernehmen</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="83"/>
+ <source>This button will immediately apply all changes.</source>
+ <translation>Dieser Button wird alle Änderungen sofort übernehmen.</translation>
+ </message>
+</context>
+<context>
+ <name>ConnectDialog</name>
+ <message>
+ <location filename="ConnectDialog.cpp" line="65"/>
+ <source>&Name</source>
+ <translation>&Name</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="72"/>
+ <source>A&ddress</source>
+ <translation>A&dresse</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="80"/>
+ <source>&Port</source>
+ <translation>&Port</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="87"/>
+ <source>&Username</source>
+ <translation>N&utzername</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="95"/>
+ <source>&Password</source>
+ <translation>&Passwort</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="101"/>
+ <source>&Connect</source>
+ <translation>&Verbinden</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="105"/>
+ <source>Cancel</source>
+ <translation>Abbrechen</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="108"/>
+ <source>&Add</source>
+ <translation>&Hinzufügen</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="111"/>
+ <source>&Remove</source>
+ <translation>&Entfernen</translation>
+ </message>
+</context>
+<context>
+ <name>DXAudioInput</name>
+ <message>
+ <location filename="DXAudioInput.cpp" line="108"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="DXAudioInput.cpp" line="103"/>
+ <source>Opening chosen DirectSound Input failed. No microphone capture will be done.</source>
+ <translation>Öffnen des DirectSound Eingangsgerätes gescheitert. Es wird kein Mikrofonsound aufgenommen.</translation>
+ </message>
+ <message>
+ <location filename="DXAudioInput.cpp" line="108"/>
+ <source>Opening chosen DirectSound Input failed. Using defaults.</source>
+ <translation type="obsolete">Öffnen des DirectSound Eingangsgerätes gescheitert. Standardeinstellungen werden benutzt.</translation>
+ </message>
+ <message>
+ <location filename="DXAudioInput.cpp" line="108"/>
+ <source>Opening chosen DirectSound Input failed. Default device will be used.</source>
+ <translation>Öffnen des DirectSound Eingangsgerätes gescheitert. Standardgerät wird benutzt.</translation>
+ </message>
+</context>
+<context>
+ <name>DXAudioOutput</name>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="341"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="336"/>
+ <source>Opening chosen DirectSound Output failed. No audio will be heard.</source>
+ <translation>Öffnen des DirectSound Ausgabegerätes gescheitert. Es kann nichts gehört werden.</translation>
+ </message>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="341"/>
+ <source>Opening chosen DirectSound Output failed. Using defaults.</source>
+ <translation type="obsolete">Öffnen des DirectSound Ausgabegerätes gescheitert. Standardeinstellungen werden benutzt.</translation>
+ </message>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="341"/>
+ <source>Opening chosen DirectSound Output failed. Default device will be used.</source>
+ <translation>Öffnen des DirectSound Ausgabegerätes gescheitert. Standardgerät wird benutzt.</translation>
+ </message>
+</context>
+<context>
+ <name>DXConfigDialog</name>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="61"/>
+ <source>Default DirectSound Voice Input</source>
+ <translation>Standard DirectSound Spracheingabe</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="62"/>
+ <source>Default DirectSound Voice Output</source>
+ <translation>Standard DirectSound Sprachausgabe</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="84"/>
+ <source>Device selection</source>
+ <translation>Geräteauswahl</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="87"/>
+ <source>Device to use for microphone</source>
+ <translation>Mikrofongerät</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="89"/>
+ <source>This sets the input device to use, which is where you have connected the microphone.</source>
+ <translation>Dies legt das Eingabegerät fest, welches mit dem Mikrofon verbunden ist.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="90"/>
+ <source>Input</source>
+ <translation>Eingang</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="95"/>
+ <source>Device to use for speakers/headphones</source>
+ <translation>Gerät für Lautsprecher/Kopfhörer</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="97"/>
+ <source>This sets the output device to use, which is where you have connected your speakers or your headset.</source>
+ <translation>Dies legt das Ausgabegerät fest, welches mit Lautsprechern oder Headset verbunden ist.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="98"/>
+ <source>Output</source>
+ <translation>Ausgabe</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="105"/>
+ <source>Output Options</source>
+ <translation>Ausgabeoptionen</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="114"/>
+ <source>Output Delay</source>
+ <translation>Ausgabeverzögerung</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="119"/>
+ <source>Amount of data to buffer for DirectSound</source>
+ <translation>Menge der zu puffernden Daten für DirectSound</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="122"/>
+ <source>This sets the amount of data to prebuffer in the DirectSound buffer. Experiment with different values and set it to the lowest which doesn't cause rapid jitter in the sound.</source>
+ <translation>Dies legt die Menge an Daten fest, die für den DirectSound-Puffer vorgepuffert werden sollen. Es sollte der niedrigste Wert gewählt werden, der noch kein Knacken im Sound produziert.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="130"/>
+ <source>Positional Audio</source>
+ <translation>Positionales Audio</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="134"/>
+ <source>None</source>
+ <translation>Keiner</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="135"/>
+ <source>Panning</source>
+ <translation>Panning</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="136"/>
+ <source>Light HRTF</source>
+ <translation>Leichtes HRTF</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="137"/>
+ <source>Full HRTF</source>
+ <translation>Volles HRTF</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="140"/>
+ <source>3D Sound Algorithm</source>
+ <translation>3D Soundalgorithmus</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="148"/>
+ <source>This sets what 3D Sound algorithm to use.<br /><b>None</b> - Disable 3D Sound (least CPU).<br /><b>Panning</b> - Just use stereo panning (some CPU).<br /><b>Light/Full HRTF</b> - Head-Related Transfer Functions enabled. This may use a small amount of CPU.<br />Note that if you have a soundcard with <i>hardware</i> 3D processing, HRTF processing will be done on the soundcard and will use practically no processing power.</source>
+ <translation>Dies legt den 3D-Soundalgorithmus fest.<br /><b>Keiner</b> - 3D-Sound deaktivieren.<br /><b>Panning</b> - Nur Stereo-Panorama (etwas CPU).<br /><b>Leichtes/Volles HRTF</b> - HRTF Funktionen eingeschalten. Dies verwendet etwas CPU-Zeit.<br />Falls die Soundkarte <i>Hardware</i>-3D-Processing unterstützt, wird HRTF praktisch keine CPU-Zeit beanspruchen.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="149"/>
+ <source>Method</source>
+ <translation>Methode</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="164"/>
+ <source>MinDistance</source>
+ <translation>MinDistanz</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="169"/>
+ <source>Minimum distance to player before sound decreases</source>
+ <translation>Minimale Distanz zum Spieler bevor Lautstärke sinkt</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="171"/>
+ <source>This sets the minimum distance for sound calculations. The volume of other players' speech will not decrease until they are at least this far away from you.</source>
+ <translation>Dies legt die minimale Distanz für Klangberechnungnen fest. Die Lautstärke anderer Spieler wird nicht sinken, solange sie nicht wenigstens soweit wie eingestellt entfernt sind.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="181"/>
+ <source>MaxDistance</source>
+ <translation>MaxDistanz</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="186"/>
+ <source>Maximum distance, beyond which sound won't decrease</source>
+ <translation>Maximale Distanz worüber hinaus die Lautstärke nicht sinkt</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="188"/>
+ <source>This sets the maximum distance for sound calculations. When farther away than this, other players' sound volume will not decrease any more.</source>
+ <translation>Dies legt die maximale Distanz für Klangberechnungnen fest. Die Lautstärke anderer Spieler wird nicht mehr sinken, wenn sie weiter als eingestellt entfernt sind.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="198"/>
+ <source>RollOff</source>
+ <translation>Rolloff</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="203"/>
+ <source>Factor for sound volume decrease</source>
+ <translation>Faktor zur Lautstärkensenkung</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="206"/>
+ <source>How fast should sound volume drop when passing beyond the minimum distance. The normal (1.0) is that sound volume halves each time the distance doubles. Increasing this value means sound volume drops faster, while decreasing it means it drops slower.</source>
+ <translation>Wie schnell soll die Lautstärke sinken, wenn die Minimaldistanz überschritten ist. Mit der Standardeinstellung (1.0) wird die Lautstärke bei doppelter Entfernung halbiert. Den Wert erhöhen lässt die Lautstärke schneller fallen, während ihn zu senken die Lautstärker langsamer fallen lässt.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="226"/>
+ <source>DirectSound</source>
+ <translation>DirectSound</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="245"/>
+ <source>%1 ms</source>
+ <translation type="obsolete">%1 ms</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="255"/>
+ <source>%1m</source>
+ <translation>%1m</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="263"/>
+ <source>%1</source>
+ <translation>%1</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="273"/>
+ <source>Players more than %1 meters away have %2% intensity</source>
+ <translation>Spieler mehr als %1 Meter entfernt haben %2% Intensität</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="245"/>
+ <source>%1ms</source>
+ <translation>%1ms</translation>
+ </message>
+</context>
+<context>
+ <name>Database</name>
+ <message>
+ <location filename="Database.cpp" line="67"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="Database.cpp" line="67"/>
+ <source>Mumble failed to initialize a database in any
+of the possible locations.</source>
+ <translation>Mumble konnte die Datenbank in keiner der
+der möglichen Orte initialisieren.</translation>
+ </message>
+</context>
+<context>
+ <name>DirectInputKeyWidget</name>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="72"/>
+ <source>Press Shortcut</source>
+ <translation>Shortcut drücken</translation>
+ </message>
+</context>
+<context>
+ <name>GlobalShortcutWinConfig</name>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="169"/>
+ <source>Shortcuts</source>
+ <translation>Shortcuts</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="135"/>
+ <source>Function</source>
+ <translation>Funktion</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="137"/>
+ <source>Shortcut</source>
+ <translation>Shortcut</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="150"/>
+ <source>Shortcut bound to %1.</source>
+ <translation>Shortcut an %1 gebunden.</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="153"/>
+ <source><b>This is the global shortcut bound to %1</b><br />Click this field and then the desired key/button combo to rebind. Double-click to clear.</source>
+ <translation><b>Der globale Shortcut ist an %1 gebunden</b><br />Dieses Feld anklicken und dann die gewünschte Taste/Button drücken zum neubinden. Doppelklicken zum löschen.</translation>
+ </message>
+</context>
+<context>
+ <name>GlobalShortcutXConfig</name>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="156"/>
+ <source>Shortcuts</source>
+ <translation>Abkürzungen</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="122"/>
+ <source>Function</source>
+ <translation>Funktion</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="124"/>
+ <source>Shortcut</source>
+ <translation>Abkürzung</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="137"/>
+ <source>Shortcut bound to %1.</source>
+ <translation>Abkürzung an %1 gebunden.</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="140"/>
+ <source><b>This is the global shortcut bound to %1</b><br />Click this field and then the desired key combo to rebind. Double-click to clear.</source>
+ <translation><b>Die globale Abkürzung ist an %1 gebunden</b><br />Dieses Feld anklicken und dann die gewünschte Taste/Button drücken zum neubinden. Doppelklicken zum löschen.</translation>
+ </message>
+</context>
+<context>
+ <name>Log</name>
+ <message>
+ <location filename="Log.cpp" line="219"/>
+ <source>[%2] %1</source>
+ <translation>[%2] %1</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="146"/>
+ <source>Debug</source>
+ <translation>Debug</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="147"/>
+ <source>Critical</source>
+ <translation>Kritisch</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="148"/>
+ <source>Warning</source>
+ <translation>Warnung</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="149"/>
+ <source>Information</source>
+ <translation>Information</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="150"/>
+ <source>Server Connected</source>
+ <translation>Server verbunden</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="151"/>
+ <source>Server Disconnected</source>
+ <translation>Serververbindung abgebrochen</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="152"/>
+ <source>Player Joined Server</source>
+ <translation>Spieler betrat Server</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="153"/>
+ <source>Player Left Server</source>
+ <translation>Spieler verließ Server</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="154"/>
+ <source>Player kicked (you or by you)</source>
+ <translation>Spieler gekickt (du oder von dir)</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="155"/>
+ <source>Player kicked</source>
+ <translation>Spieler gekickt</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="156"/>
+ <source>You self-muted/deafened</source>
+ <translation>Selbst stumm-/taubgestellt</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="157"/>
+ <source>Other self-muted/deafened</source>
+ <translation>Anderer stumm-/taubgestellt</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="158"/>
+ <source>Player muted (you)</source>
+ <translation>Spieler stummgestellt (dich)</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="159"/>
+ <source>Player muted (by you)</source>
+ <translation>Spieler stummgestellt (durch dich)</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="160"/>
+ <source>Player muted (other)</source>
+ <translation>Spieler stummgestellt (jemand anderes)</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="161"/>
+ <source>Player Joined Channel</source>
+ <translation>Spieler betrat Kanal</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="162"/>
+ <source>Player Left Channel</source>
+ <translation>Spieler verließ Kanal</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="164"/>
+ <source>Permission Denied</source>
+ <translation>Erlaubnis verweigert</translation>
+ </message>
+</context>
+<context>
+ <name>LogConfig</name>
+ <message>
+ <location filename="Log.cpp" line="113"/>
+ <source>Messages</source>
+ <translation>Nachrichten</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="56"/>
+ <source>Console</source>
+ <translation>Konsole</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="58"/>
+ <source>TTS</source>
+ <translation>TTS</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="72"/>
+ <source>Enable console for %1</source>
+ <translation>Konsole für %1 einschalten</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="78"/>
+ <source>Enable Text-To-Speech for %1</source>
+ <translation>Text-To-Speech für %1 einschalten</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="85"/>
+ <source>Text To Speech</source>
+ <translation>Text-To-Speech</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="88"/>
+ <source>Volume</source>
+ <translation>Lautstärke</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="96"/>
+ <source>Volume of Text-To-Speech Engine</source>
+ <translation>Lautstärke der Text-To-Speech-Engine</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="97"/>
+ <source><b>This is the volume used for the speech synthesis.</b></source>
+ <translation><b>Dies ist die Lautstärke für die Sprachsynthese.</b></translation>
+ </message>
+</context>
+<context>
+ <name>LookConfig</name>
+ <message>
+ <location filename="LookConfig.cpp" line="67"/>
+ <source>Language</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="51"/>
+ <source>Look and Feel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="82"/>
+ <source>System default</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="69"/>
+ <source>Language to use (requires restart)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="70"/>
+ <source><b>This sets which language Mumble should use.</b><br />You have to restart Mumble to use the new language.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="89"/>
+ <source>Style</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="91"/>
+ <source>Basic widget style</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="92"/>
+ <source><b>This sets the basic look and feel to use.</b></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="97"/>
+ <source>Skin</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="101"/>
+ <source>Skin file to use</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="102"/>
+ <source><b>This sets which skin Mumble should use.</b><br />The skin is a style file applied on top of the basic widget style.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="109"/>
+ <source>Use Horizontal Splitter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="111"/>
+ <source>Use horizontal or vertical splitter for the main window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="112"/>
+ <source><b>This sets whether the split in the main window is horizontal or vertical.</b></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="127"/>
+ <source>User Interface</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="99"/>
+ <source>...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="171"/>
+ <source>Choose skin file</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="MainWindow.cpp" line="53"/>
+ <source>Root</source>
+ <translation>Wurzel</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="73"/>
+ <source>&Connect</source>
+ <translation>&Verbinden</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="74"/>
+ <source>Open the server connection dialog</source>
+ <translation>Öffne den Serververbindungs-Dialog</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="75"/>
+ <source>Shows a dialog of registered servers, and also allows quick connect.</source>
+ <translation>Zeigt einen Dialog mit registrierten Servern und erlaubt schnelles Verbinden.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="77"/>
+ <source>&Disconnect</source>
+ <translation>&Trennen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="78"/>
+ <source>Disconnect from server</source>
+ <translation>Vom Server trennen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="79"/>
+ <source>Disconnects you from the server.</source>
+ <translation>Vom Server trennen.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="82"/>
+ <source>&Ban lists</source>
+ <translation>&Banliste</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="83"/>
+ <source>Edit ban lists on server</source>
+ <translation>Banlisten auf Server editieren</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="84"/>
+ <source>This lets you edit the server-side IP ban lists.</source>
+ <translation>Hierdurch lässt sich die serverseitige IP Banliste editieren.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="88"/>
+ <source>&Kick</source>
+ <translation>&Kick</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="90"/>
+ <source>Kick player (with reason)</source>
+ <translation>Kick Spieler (mit Grund)</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="91"/>
+ <source>Kick selected player off server. You'll be asked to specify a reason.</source>
+ <translation>Den ausgewählten Spieler vom Server kicken. Man wird nach einem Grund gefragt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="92"/>
+ <source>&Ban</source>
+ <translation>&Ban</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="94"/>
+ <source>Kick and ban player (with reason)</source>
+ <translation>Kick und ban Spieler (mit Grund)</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="95"/>
+ <source>Kick and ban selected player from server. You'll be asked to specify a reason.</source>
+ <translation>Den ausgewählten Spieler vom Server kicken und bannen. Man wird nach einem Grund gefragt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="96"/>
+ <source>&Mute</source>
+ <translation>&Stumm</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="109"/>
+ <source>Mute player</source>
+ <translation>Spieler stummstellen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="100"/>
+ <source>Mute or unmute player on server. Unmuting a deafened player will also undeafen them.</source>
+ <translation>Stummstellen der Spieler auf dem Server an- oder abstellen. Einem taubgestellten Spieler Stummstellen abschalten wird ebenso das Taubstellen abschalten.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="101"/>
+ <source>&Deafen</source>
+ <translation>&Taubstellen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="104"/>
+ <source>Deafen player</source>
+ <translation>Spieler taubstellen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="105"/>
+ <source>Deafen or undeafen player on server. Deafening a player will also mute them.</source>
+ <translation>Taubstellen des Spielers auf dem Server an- oder abstellen. Einen Spieler taubstellen wird ihn ebenso stummstellen.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="106"/>
+ <source>&Local Mute</source>
+ <translation>&Lokal Stumm</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="110"/>
+ <source>Mute or unmute player locally.</source>
+ <translation>Spieler lokal stumm oder nicht stummstellen.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="112"/>
+ <source>&Add</source>
+ <translation>&Hinzufügen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="114"/>
+ <source>Add new channel</source>
+ <translation>Neuen Kanal hinzufügen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="115"/>
+ <source>This adds a new sub-channel to the currently selected channel.</source>
+ <translation>Dies fügt dem aktuell gewählten Kanal einen Unterkanal hinzu.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="116"/>
+ <source>&Remove</source>
+ <translation>&Entfernen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="118"/>
+ <source>Remove channel</source>
+ <translation>Kanal entfernen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="119"/>
+ <source>This removes a channel and all sub-channels.</source>
+ <translation>Dies entfernt einen Kanal und all seine Unterkanäle.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="120"/>
+ <source>&Edit ACL</source>
+ <translation>ACL &editieren</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="122"/>
+ <source>Edit Groups and ACL for channel</source>
+ <translation>Gruppen und ACL des Channels editieren</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="123"/>
+ <source>This opens the Group and ACL dialog for the channel, to control permissions.</source>
+ <translation>Dies öffnet den Gruppen- und ACL-Dialog des Kanals um die Rechte zu kontrollieren.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="124"/>
+ <source>&Link</source>
+ <translation>&Link</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="126"/>
+ <source>Link your channel to another channel</source>
+ <translation>Den Kanal mit einem anderen verbinden</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="129"/>
+ <source>This links your current channel to the selected channel. If they have permission to speak in the other channel, players can now hear each other. This is a permanent link, and will last until manually unlinked or the server is restarted. Please see the shortcuts for push-to-link.</source>
+ <translation>Dies verbindet den aktuellen Kanal mit dem gewählten. Wenn Spieler Spracherlaubnis im anderen Kanal haben, so können sie sich jetzt gegenseitig hören. Dies ist eine permanente Verbindung die solange bestehen bleibt bis die Verbindung manuell aufgehoben oder der Server neugestart wird.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="167"/>
+ <source>&Unlink</source>
+ <translation>&Unlink</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="132"/>
+ <source>Unlink your channel from another channel</source>
+ <translation>Verbindung des Kanals von anderem Kanal trennen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="133"/>
+ <source>This unlinks your current channel from the selected channel.</source>
+ <translation>Dies trennt die Verbindung des aktuellen Kanals vom ausgewählten.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="134"/>
+ <source>Unlink &All</source>
+ <translation>&Alle Verbindungen trennen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="136"/>
+ <source>Unlinks your channel from all linked channels.</source>
+ <translation>Trennt alle verbundenen Kanäle.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="137"/>
+ <source>This unlinks your current channel (not the selected one) from all linked channels.</source>
+ <translation>Dies trennt die Verbindung des Kanals von allen verbundenen Kanälen.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="139"/>
+ <source>&Reset</source>
+ <translation>&Zurücksetzen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="141"/>
+ <source>Reset audio preprocessor</source>
+ <translation>Setzt Audio-Präprozessor zurück</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="144"/>
+ <source>This will reset the audio preprocessor, including noise cancellation, automatic gain and voice activity detection. If something suddenly worsens the audio environment (like dropping the microphone) and it was temporary, use this to avoid having to wait for the preprocessor to readjust.</source>
+ <translation>Setzt den Audio-Präprozessor zurück: unter anderem Geräuschunterdrückung, automatisches Gain und Stimmaktivierungserkennung. Wenn etwas plötzlich die Audioumgebung verschlechtert (z.B. das Mikrofon fallen lassen), kann dies benutzt werden um den Präprozessor sofort neu auszurichten.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="145"/>
+ <source>&Mute Self</source>
+ <translation>Selbst &Stummstellen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="149"/>
+ <source>Mute yourself</source>
+ <translation>Sich selber stummstellen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="150"/>
+ <source>Mute or unmute yourself. When muted, you will not send any data to the server. Unmuting while deafened will also undeafen.</source>
+ <translation>Stummstellen an/aus.. When stummgestellt, werden keine Daten an den Server gesandt. Stummstellen abschalten führt während des Taubstellens auch zu dessen Deaktivierung.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="151"/>
+ <source>&Deafen Self</source>
+ <translation>Selbst &Taubstellen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="155"/>
+ <source>Deafen yourself</source>
+ <translation>Sich selbst taubstellen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="156"/>
+ <source>Deafen or undeafen yourself. When deafened, you will not hear anything. Deafening yourself will also mute.</source>
+ <translation>Taubstellen an/aus. When taub, kann man nichts hören. Taubstellen aktiviert auch Stummstellen.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="157"/>
+ <source>&Text-To-Speech</source>
+ <translation>&Text-To-Speech</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="161"/>
+ <source>Toggle Text-To-Speech</source>
+ <translation>Text-To-Speech ein-/ausschalten</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="162"/>
+ <source>Enable or disable the text-to-speech engine. Only messages enabled for TTS in the Configuration dialog will actually be spoken.</source>
+ <translation>Text-To-Speech-Engine ein oder ausschalten. Nur Nachrichten die im Konfigurationsdialog entsprechend aktiviert sind werden tatsächlich gesprochen.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="163"/>
+ <source>S&tatistics</source>
+ <translation>St&atistiken</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="165"/>
+ <source>Display audio statistics</source>
+ <translation>Zeigt Audiostatistiken an</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="166"/>
+ <source>Pops up a small dialog with information about your current audio input.</source>
+ <translation>Zeigt einen kleinen Dialog mit Informationen über den aktuellen Audio Input an.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="169"/>
+ <source>Forcibly unlink plugin</source>
+ <translation>Plugin trennen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="171"/>
+ <source>This forces the current plugin to unlink, which is handy if it is reading completely wrong data.</source>
+ <translation>Dies zwingt das aktuelle Plugin zu trennen, was hilfreich ist, wenn es völlig falsche Daten liest.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="173"/>
+ <source>&Settings</source>
+ <translation>&Einstellungen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="175"/>
+ <source>Configure Mumble</source>
+ <translation>Mumble konfigurieren</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="176"/>
+ <source>Allows you to change most settings for Mumble.</source>
+ <translation>Erlaubt die meisten Einstellungen von Mumble zu ändern.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="178"/>
+ <source>&What's This?</source>
+ <translation>&Was ist das?</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="180"/>
+ <source>Enter What's This? mode</source>
+ <translation>Was ist das Modus?-Modus betreten</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="182"/>
+ <source>Click this to enter "What's This?" mode. Your cursor will turn into a question mark. Click on any button, menu choice or area to show a description of what it is.</source>
+ <translation>Dies anklicken um den "Was ist das?"-Modus zu aktivieren. Der Cursor wird sich in ein Fragezeichen ändern. Klickt man damit auf irgendeinen Button, Menüeintrag oder ein Gebiet, wird eine kurze Beschreibung ausgegeben.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="183"/>
+ <source>&About</source>
+ <translation>Ü&ber</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="185"/>
+ <source>Information about Mumble</source>
+ <translation>Informationen über Mumble</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="186"/>
+ <source>Shows a small dialog with information and license for Mumble.</source>
+ <translation>Zeigt einen kleinen Dialog mit Informationen und Lizenzierung von Mumble.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="187"/>
+ <source>About &Speex</source>
+ <translation>Über &Speex</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="189"/>
+ <source>Information about Speex</source>
+ <translation>Informationen über Speex</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="190"/>
+ <source>Shows a small dialog with information about Speex.</source>
+ <translation>Zeigt einen kleinen Dialog mit Informationen über Speex.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="191"/>
+ <source>&About Qt</source>
+ <translation>Über &Qt</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="193"/>
+ <source>Information about Qt</source>
+ <translation>Informationen über Qt</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="194"/>
+ <source>Shows a small dialog with information about Qt.</source>
+ <translation>Zeigt einen kleinen Dialog mit Informationen über Qt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="195"/>
+ <source>Check for &Updates</source>
+ <translation>Nach &Updates suchen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="197"/>
+ <source>Check for new version of Mumble</source>
+ <translation>Nach einer neuen Version von Mumble suchen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="199"/>
+ <source>Connects to the Mumble webpage to check if a new version is available, and notifies you with an appropriate download URL if this is the case.</source>
+ <translation>Verbindet sich mit der Mumble Webseite um zu prüfen ob eine neue Version verfügbar ist. In dem Fall wird mit einer passenden Download URL darauf hingewiesen.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="203"/>
+ <source>Mumble -- %1</source>
+ <translation>Mumble -- %1</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="220"/>
+ <source>Log of messages</source>
+ <translation>Log mit Nachrichten</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="222"/>
+ <source>This shows all recent activity. Connecting to servers, errors and information messages all show up here.<br />To configure exactly which messages show up here, use the <b>Settings</b> command from the menu.</source>
+ <translation>Dies zeigt die neuesten Aktivitäten an. Verbindungen zum Server, Fehler und Informationsnachrichten werden hier angezeigt.<br />Um genau zu konfigurieren welche Nachrichten angezeigt werden sollen, kann das <b>Einstellungen</b> Kommando vom Menü genutzt werden.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="224"/>
+ <source>&Server</source>
+ <translation>&Server</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="225"/>
+ <source>&Player</source>
+ <translation>S&pieler</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="226"/>
+ <source>&Channel</source>
+ <translation>&Kanal</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="227"/>
+ <source>&Audio</source>
+ <translation>&Audio</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="228"/>
+ <source>C&onfigure</source>
+ <translation>K&onfiguration</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="229"/>
+ <source>&Help</source>
+ <translation>&Hilfe</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="503"/>
+ <source>Kicking player %1</source>
+ <translation>Kicke Spieler %1</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="519"/>
+ <source>Enter reason</source>
+ <translation>Grund eingeben</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="519"/>
+ <source>Banning player %1</source>
+ <translation>Banne Spieler %1</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="587"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="571"/>
+ <source>Channel Name</source>
+ <translation>Kanalname</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="577"/>
+ <source>Are you sure you want to delete %1 and all it's subchannels?</source>
+ <translation type="obsolete">Sind Sie sicher Kanal %1 und all seine Unterkanäle zu löschen?</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="664"/>
+ <source>Unmuted and undeafened.</source>
+ <translation>Stumm- und Taubstellen ausgeschalten.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="666"/>
+ <source>Unmuted.</source>
+ <translation>Stummstellen aus.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="668"/>
+ <source>Muted.</source>
+ <translation>Stumm.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="683"/>
+ <source>Muted and deafened.</source>
+ <translation>Stumm und taub gestellt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="685"/>
+ <source>Deafened.</source>
+ <translation>Taubgestellt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="687"/>
+ <source>Undeafened.</source>
+ <translation>Taubstellung aus.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="737"/>
+ <source>About Qt</source>
+ <translation>Über Qt</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="813"/>
+ <source>Joining %1.</source>
+ <translation>Betrete %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="839"/>
+ <source>Connected to server.</source>
+ <translation>Mit Server verbunden.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="872"/>
+ <source>Server connection failed: %1.</source>
+ <translation>Serververbindung fehlgeschlagen: %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="878"/>
+ <source>Disconnected from server.</source>
+ <translation>Verbindung zum Server abgebrochen.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="884"/>
+ <source>Reconnecting.</source>
+ <translation>Neuverbindung.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="917"/>
+ <source>Joined server: %1.</source>
+ <translation>Server beigetreten: %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="932"/>
+ <source>Left server: %1.</source>
+ <translation>Server verlassen: %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="967"/>
+ <source>%1 is now muted and deafened.</source>
+ <translation>%1 ist jetzt stumm und taubgestellt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="969"/>
+ <source>%1 is now muted.</source>
+ <translation>%1 ist jetzt stummgestellt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="971"/>
+ <source>%1 is now unmuted.</source>
+ <translation>%1 ist jetzt nicht mehr stummgestellt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1002"/>
+ <source>server</source>
+ <translation>Server</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="987"/>
+ <source>You were muted by %1.</source>
+ <translation>Du wurdest von %1 stummgestellt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="987"/>
+ <source>You were unmuted by %1.</source>
+ <translation>Stummstellen wurde von %1 deaktiviert.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="989"/>
+ <source>%1 muted by %2.</source>
+ <translation>%1 von %2 stummgestellt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="989"/>
+ <source>%1 unmuted by %2.</source>
+ <translation>Stummstellung von %1 durch %2 aufgehoben.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1005"/>
+ <source>You were deafened by %1.</source>
+ <translation>Du wurdest von %1 taubgestellt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1005"/>
+ <source>You were undeafened by %1.</source>
+ <translation>Taubstellen von %1 deaktiviert.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1007"/>
+ <source>%1 deafened by %2.</source>
+ <translation>%1 wurde von %2 taubgestellt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1007"/>
+ <source>%1 undeafened by %2.</source>
+ <translation>Taubstellen von %1 durch %2 deaktiviert.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1014"/>
+ <source>You were kicked from the server by %1: %2.</source>
+ <translation>Du wurdest von %1 vom Server gekickt: %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1018"/>
+ <source>%3 was kicked from the server by %1: %2.</source>
+ <translation>%3 wurde von %1 vom Server gekickt: %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1026"/>
+ <source>You were kicked and banned from the server by %1: %2.</source>
+ <translation>Du wurdest von %1 vom Server gekickt und gebannet: %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1030"/>
+ <source>%3 was kicked and banned from the server by %1: %2.</source>
+ <translation>%3 wurde von %1 vom Server gekickt und gebannet: %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1049"/>
+ <source>%1 left channel.</source>
+ <translation>%1 hat den Kanal verlassen.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1051"/>
+ <source>%1 moved out by %2.</source>
+ <translation>%1 von %2 rausgeschoben.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1058"/>
+ <source>%1 entered channel.</source>
+ <translation>%1 betrat den Kanal.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1060"/>
+ <source>%1 moved in by %2.</source>
+ <translation>%1 von %2 reingeschoben.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1112"/>
+ <source>Server connection rejected: %1.</source>
+ <translation>Serververbindung abgelehnt : %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1117"/>
+ <source>Denied: %1.</source>
+ <translation>Abgelehnt: %1.</translation>
+ </message>
+ <message>
+ <location filename="main.cpp" line="129"/>
+ <source>Welcome to Mumble.</source>
+ <translation>Willkommen zu Mumble.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="284"/>
+ <source>Push-to-Talk</source>
+ <comment>Global Shortcut</comment>
+ <translation>Push-To-Talk</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="287"/>
+ <source>Reset Audio Processor</source>
+ <comment>Global Shortcut</comment>
+ <translation>Audio Prozessor zurücksetzen</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="291"/>
+ <source>Toggle Mute Self</source>
+ <comment>Global Shortcut</comment>
+ <translation>Selbst stummstellen umschalten</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="295"/>
+ <source>Toggle Deafen Self</source>
+ <comment>Global Shortcut</comment>
+ <translation>Selbst taubstellen umschalten</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="303"/>
+ <source>Force Center Position</source>
+ <comment>Global Shortcut</comment>
+ <translation>Erzwinge Mittelposition</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="308"/>
+ <source>Chan Parent</source>
+ <comment>Global Shortcut</comment>
+ <translation>Kanal Eltern</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="313"/>
+ <source>Chan Sub#%1</source>
+ <comment>Global Shortcut</comment>
+ <translation>Kanal Sub#%1</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="318"/>
+ <source>Chan All Subs</source>
+ <comment>Global Shortcut</comment>
+ <translation>Kanal alle Subs</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="322"/>
+ <source>Push-to-Mute</source>
+ <comment>Global Shortcut</comment>
+ <translation>Push-To-Mute</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="325"/>
+ <source>Join Channel</source>
+ <comment>Global Shortcut</comment>
+ <translation>Kanal betreten</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="329"/>
+ <source>Toggle Overlay</source>
+ <comment>Global Shortcut</comment>
+ <translation>Overlay umschalten</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="334"/>
+ <source>Alt Push-to-Talk</source>
+ <comment>Global Shortcut</comment>
+ <translation>Alt Push-To-Talk</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="299"/>
+ <source>Unlink Plugin</source>
+ <comment>Global Shortcut</comment>
+ <translation>Plugin ausklinken</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="587"/>
+ <source>Are you sure you want to delete %1 and all its sub-channels?</source>
+ <translation>Sicher %1 und all seine Unterkanäle zu löschen?</translation>
+ </message>
+</context>
+<context>
+ <name>Overlay</name>
+ <message>
+ <location filename="Overlay_win.cpp" line="363"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="366"/>
+ <source>Failed to load overlay library. This means either that:
+- the library (mumble_ol.dll) wasn't found in the directory you ran Mumble from
+- you're on an OS earlier than WinXP SP2
+- you do not have the August 2006 updated version of DX9.0c</source>
+ <translation>Konnte Overlaybibliothek nicht laden. Dies bedeutet:
+- Die Bibliothek (mumble_ol.dll) wurde nicht in dem Verzeichnis gefunden in dem mumble gestartet wurde
+- Es wird ein älteres OS als WinXP SP2 genutzt
+- Die August 2006 Version von DX9.0c wurde nicht installiert</translation>
+ </message>
+</context>
+<context>
+ <name>OverlayConfig</name>
+ <message>
+ <location filename="Overlay_win.cpp" line="43"/>
+ <source>Options</source>
+ <translation>Optionen</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="44"/>
+ <source>Position</source>
+ <translation>Position</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="45"/>
+ <source>Font</source>
+ <translation>Schriftart</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="52"/>
+ <source>Enable Overlay</source>
+ <translation>Overlay einschalten</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="54"/>
+ <source>Enable overlay.</source>
+ <translation>Overlay einschalten.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="60"/>
+ <source>This sets whether the overlay is enabled or not. This setting is only checked with D3D9 applications are started, so make sure Mumble is running and this option is on before you start the application.<br />Please note that if you start the application after starting Mumble, or if you disable the overlay while running, there is no safe way to restart the overlay without also restarting the application.</source>
+ <translation>Dies legt fest ob das Overlay angeschalten ist oder nicht. Diese Einstellung ist nur aktiviert wenn D3D9-Anwendungen gestartet sind, somit muss sichergestellt werden, dass Mumble läuft und die Einstellungen gesetzt ist, bevor die Anwendung gestartet wurde.<br />Achtung: Wird die Anwendung nach Mumble gestartet oder das Overlay während des Betriebs abgestellt, gibt es keinen sicheren Weg das Overlay wieder zu aktivieren außer die Anwendung neu zu starten.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="65"/>
+ <source>No one</source>
+ <translation>Niemand</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="66"/>
+ <source>Only talking</source>
+ <translation>Nur Sprechende</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="67"/>
+ <source>Everyone</source>
+ <translation>Jeder</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="69"/>
+ <source>Show</source>
+ <translation>Anzeigen</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="71"/>
+ <source>Who to show on the overlay</source>
+ <translation>Wer auf dem Overlay angezeigt werden soll</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="77"/>
+ <source><b>This sets who to show in the in-game overlay.</b><br />If many people are connected to the same channel, the overlay list might be very long. Use this to shorten it.<br /><i>No one</i> - Don't show anyone (but leave overlay running).<br /><i>Only talking</i> - Only show talking people.<br /><i>Everyone</i> - Show everyone.</source>
+ <translation><b>Dies legt fest, wer im Ingame-Overlay angezeigt wird.</b><br />Falls viele Leute im selben Kanal sind, könnte die Overlayliste sehr lang werden. Mit diesen Einstellungen kann sie gekürzt werden.<i>Niemand</i> - Zeige niemanden an (aber lass das Overlay laufen).<br /><i>Nur Sprechende</i> - Nur sprechende Leute.<br /><i>Jeder</i> - Zeige jeden an.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="81"/>
+ <source>Always Show Self</source>
+ <translation>Immer selbst anzeigen</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="83"/>
+ <source>Always show yourself on overlay.</source>
+ <translation>Immer selbst auf dem Overlay anzeigen.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="87"/>
+ <source>This sets whether to always show yourself or not. This setting is useful if you aren't showing everyone in the overlay, as then you would only see your own status if you were talking, which wouldn't let you see that you were deafened or muted.</source>
+ <translation>Dies legt fest ob man selbst angezeigt wird oder nicht. Diese Einstellung ist nützlich, wenn man nicht jeden im Overlay anzeigen lässt, da man dann seinen eigenen Status nur sehen könnte wenn man spricht.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="95"/>
+ <source>Grow Left</source>
+ <translation>Links vergrößern</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="97"/>
+ <source>Let overlay grow to the left</source>
+ <translation>Das Overlay nach links vergrößern lassen</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="100"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow to the left if needed.</source>
+ <translation>Das Overlay versucht so klein wie möglich und an der Position die man gewählt hat zu bleiben. Dies erlaubt das Overlay sich, falls nötig, nach links zu vergrößern.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="102"/>
+ <source>Grow Right</source>
+ <translation>Nach rechts vergrößern</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="104"/>
+ <source>Let overlay grow to the Right</source>
+ <translation>Das Overlay nach rechts vergrößern lassen</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="107"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow to the Right if needed.</source>
+ <translation>Das Overlay versucht so klein wie möglich und an der Position die man gewählt hat zu bleiben. Dies erlaubt das Overlay sich, falls nötig, nach rechts zu vergrößern.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="109"/>
+ <source>Grow Up</source>
+ <translation>Nach oben vergrößern</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="111"/>
+ <source>Let overlay grow upwards</source>
+ <translation>Das Overlay nach oben vergrößern lassen</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="114"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow to the top if needed.</source>
+ <translation>Das Overlay versucht so klein wie möglich und an der Position die man gewählt hat zu bleiben. Dies erlaubt das Overlay sich, falls nötig, nach oben zu vergrößern.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="116"/>
+ <source>Grow Down</source>
+ <translation>Nach unten vergrößern</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="118"/>
+ <source>Let overlay grow downwards</source>
+ <translation>Das Overlay nach unten vergrößern lassen</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="121"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow towards the bottom if needed.</source>
+ <translation>Das Overlay versucht so klein wie möglich und an der Position die man gewählt hat zu bleiben. Dies erlaubt das Overlay sich, falls nötig, nach unten zu vergrößern.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="126"/>
+ <source>X-Position of Overlay</source>
+ <translation>X-Position des Overlay</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="127"/>
+ <source>This sets the relative X position of the overlay.</source>
+ <translation>Dies setzt die relative X-Position des Overlay.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="135"/>
+ <source>Y-Position of Overlay</source>
+ <translation>Y-Position des Overlay</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="136"/>
+ <source>This sets the relative Y position of the overlay.</source>
+ <translation>Dies setzt die relative Y-Position des Overlay.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="161"/>
+ <source>Current Font</source>
+ <translation>Aktuelle Schrift</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="165"/>
+ <source>Set Font</source>
+ <translation>Schrift setzen</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="174"/>
+ <source>Maximum width of names.</source>
+ <translation>Maximale Breite der Namen.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="177"/>
+ <source>This sets the maximum width of names shown, relative to the height. If you set this to 100%, no line of text will be wider than it is high. At 500%, no line will be more than 5 times wider than its height, and so on.</source>
+ <translation>Dies legt die maximale Breite der angezeigten Namen relativ zur Höhe fest. Wird es auf 100% gesetzt, ist keine Zeile Text breiter als hoch. Bei 500%, ist keine Zeile mehr als fünf mal breiter als hoch.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="187"/>
+ <source>Maximum width</source>
+ <translation>Maximale Breite</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="235"/>
+ <source>Change</source>
+ <translation>Wechseln</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="197"/>
+ <source>Color for players</source>
+ <translation>Farbe für Spieler</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="207"/>
+ <source>Color for talking players</source>
+ <translation>Farbe für sprechende Spieler</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="217"/>
+ <source>Color for alt-talking players</source>
+ <translation>Farbe für alt-sprechende Spieler</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="227"/>
+ <source>Color for Channels</source>
+ <translation>Farbe für Kanäle</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="237"/>
+ <source>Color for active Channels</source>
+ <translation>Farbe für aktive Kanäle</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="323"/>
+ <source>Overlay</source>
+ <translation>Overlay</translation>
+ </message>
+</context>
+<context>
+ <name>PlayerModel</name>
+ <message>
+ <location filename="PlayerModel.cpp" line="368"/>
+ <source>Name of player</source>
+ <translation>Name des Spielers</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="368"/>
+ <source>Name of channel</source>
+ <translation>Name des Kanals</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="370"/>
+ <source>Player flags</source>
+ <translation>Spielerattribute</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="381"/>
+ <source>This is a player connected to the server. The icon to the left of the player indicates whether or not they are talking:<br /><img src=":/icons/talking_on.png" /> Talking<br /><img src=":/icons/talking_off.png" /> Not talking</source>
+ <translation>Dies ist ein mit dem Server verbundener Spieler. Das Icon links des Spieler zeigt an ob er spricht: <br /><img src=":/icons/talking_on.png" /> Sprechend<br /><img src=":/icons/talking_off.png" /> Nicht sprechen</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="383"/>
+ <source>This is a channel on the server. Only players in the same channel can hear each other.</source>
+ <translation>Dies ist ein Kanal auf dem Server. Nur Spieler im selben Kanal können sich gegenseitig hören.</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="393"/>
+ <source>This shows the flags the player has on the server, if any:<br /><img src=":/icons/authenticated.png" />Authenticated user<br /><img src=":/icons/muted_self.png" />Muted (by self)<br /><img src=":/icons/muted_server.png" />Muted (by admin)<br /><img src=":/icons/deafened_self.png" />Deafened (by self)<br /><img src=":/icons/deafened_server.png" />Deafened (by admin)<br />A player muted by himself is probably just away, talking on the phone or something like that.<br />A player muted by an admin is probably also just away, and the noise the player is making was annoying enough that an admin muted him.</source>
+ <translation>Dies zeigt die Attribute die der Spieler auf dem Server hat an: <br /><img src=":/icons/authenticated.png" />Authorisierter Nutzer<br /><img src=":/icons/muted_self.png" />Stumm (durch sich selbst)<br /><img src=":/icons/muted_server.png" />Stumm (durch Admin)<br /><img src=":/icons/deafened_self.png" />Taub (durch sich selbst)<br /><img src=":/icons/deafened_server.png" />Taub (durch Admin)<br />Ein Spieler der sich selbst stummgestellt hat ist vermutlich vom PC entfernt.</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="410"/>
+ <source>Name</source>
+ <translation>Name</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="412"/>
+ <source>Flags</source>
+ <translation>Attribute</translation>
+ </message>
+</context>
+<context>
+ <name>PluginConfig</name>
+ <message>
+ <location filename="Plugins.cpp" line="59"/>
+ <source>Options</source>
+ <translation>Optionen</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="114"/>
+ <source>Plugins</source>
+ <translation>Plugins</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="68"/>
+ <source>Enable plugins and transmit positional information</source>
+ <translation>Plugins einschalten und Positionsinformationen übertragen</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="72"/>
+ <source>This enables plugins for supported games to fetch your in-game position and transmit that with each voice packet. This enables other players to hear your voice in-game from the direction your character is in relation to their own.</source>
+ <translation>Dies aktiviert Plugins für unterstützte Spiele, um Ingame-Positionen zu ermitteln und diese mit jedem Sprachpaket zu übertragen. Dies lässt andere Spieler die eigene Stimme aus der Richtung hören in der sich der sprechende Spielcharakter befindet.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="81"/>
+ <source>Reloads all plugins</source>
+ <translation>Lädt alle Plugins nach</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="83"/>
+ <source>This rescans and reloads plugins. Use this if you just added or changed a plugin to the plugins directory.</source>
+ <translation>Dies sucht erneut nach Plugins und lädt diese nach. Dies sollte benutzt werden, wenn neue Plugins zum Plugin-Verzeichnis hinzugefügt wurden.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="86"/>
+ <source>Information about plugin</source>
+ <translation>Information über Plugin</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="87"/>
+ <source>This shows a small information message about the plugin.</source>
+ <translation>Dies zeigt eine kleine Informationsnachricht über das Plugin.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="90"/>
+ <source>Show configuration page of plugin</source>
+ <translation>Zeige Konfigurationsdialog des Plugins</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="91"/>
+ <source>This shows the configuration page of the plugin, if any.</source>
+ <translation>Dies zeigt, falls vorhanden, einen Konfigurationsdialog des Plugins an.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="146"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="134"/>
+ <source>Plugin has no configure function.</source>
+ <translation>Plugin hat keine Konfigurationsfunktion.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="146"/>
+ <source>Plugin has no about function</source>
+ <translation type="obsolete">Plugin hat keine Über-Funktion</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="66"/>
+ <source>Link to Game and Transmit Position</source>
+ <translation>Mit Spiel verbinden und Position übertragen</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="79"/>
+ <source>&Reload plugins</source>
+ <translation>Plugins &neuladen</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="84"/>
+ <source>&About</source>
+ <translation>Ü&ber</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="88"/>
+ <source>&Configure</source>
+ <translation>&Konfigurieren</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="146"/>
+ <source>Plugin has no about function.</source>
+ <translation>Plugin hat keine Über-Funktion.</translation>
+ </message>
+</context>
+<context>
+ <name>Plugins</name>
+ <message>
+ <location filename="Plugins.cpp" line="243"/>
+ <source>Plugin %1 lost link.</source>
+ <translation>Plugin %1 verlor Verbindung.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="244"/>
+ <source>%1 lost link.</source>
+ <translation>%1 verlor Verbindung.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="285"/>
+ <source>Plugin %1 linked.</source>
+ <translation>Plugin %1 verbunden.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="286"/>
+ <source>%1 linked.</source>
+ <translation>%1 verbunden.</translation>
+ </message>
+</context>
+<context>
+ <name>VersionCheck</name>
+ <message>
+ <location filename="VersionCheck.cpp" line="75"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="VersionCheck.cpp" line="75"/>
+ <source>Failed to retrieve version information from server.</source>
+ <translation type="obsolete">Konnte Versionsinformation nicht vom Server ermitteln.</translation>
+ </message>
+ <message>
+ <location filename="VersionCheck.cpp" line="48"/>
+ <source>VersionCheck: Failed to open binary</source>
+ <translation type="obsolete">Versionscheck: Konnte Binärdatei nicht öffnen</translation>
+ </message>
+ <message>
+ <location filename="VersionCheck.cpp" line="54"/>
+ <source>VersionCheck: suspiciously small binary</source>
+ <translation type="obsolete">Versionscheck: ungewöhnlich kleine Binärdatei</translation>
+ </message>
+ <message>
+ <location filename="VersionCheck.cpp" line="75"/>
+ <source>Mumble failed to retrieve version information from the SourceForge server.</source>
+ <translation>Konnte Versionsinformation nicht vom SourceForge-Server ermitteln.</translation>
+ </message>
+</context>
+<context>
+ <name>XInputKeyWidget</name>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="63"/>
+ <source>Press Shortcut</source>
+ <translation>Shortcut drücken</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/mumble/mumble_es.ts b/src/mumble/mumble_es.ts new file mode 100644 index 000000000..75decb347 --- /dev/null +++ b/src/mumble/mumble_es.ts @@ -0,0 +1,2977 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1">
+<context>
+ <name>ACLEditor</name>
+ <message>
+ <location filename="ACLEditor.cpp" line="51"/>
+ <source>Active ACLs</source>
+ <translation>LCAs activas</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="52"/>
+ <source>Context</source>
+ <translation>Contexto</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="53"/>
+ <source>User/Group</source>
+ <translation>Usuario/Grupo</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="54"/>
+ <source>Permissions</source>
+ <translation>Permisos</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="102"/>
+ <source>Group</source>
+ <translation>Grupo</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="57"/>
+ <source>Members</source>
+ <translation>Miembros</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="61"/>
+ <source>&Add</source>
+ <translation>&Agregar</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="63"/>
+ <source>&Remove</source>
+ <translation>&Eliminar</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="65"/>
+ <source>&Up</source>
+ <translation>A&rriba</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="67"/>
+ <source>&Down</source>
+ <translation>A&bajo</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="69"/>
+ <source>Inherit ACLs</source>
+ <translation>Heredar LCAs</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="88"/>
+ <source>Applies to this channel</source>
+ <translation>Se aplica a este canal</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="90"/>
+ <source>Applies to sub-channels</source>
+ <translation>Se aplica a los subcanales</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="109"/>
+ <source>User ID</source>
+ <translation>ID de usuario</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="119"/>
+ <source>Deny</source>
+ <translation>Denegar</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="121"/>
+ <source>Allow</source>
+ <translation>Permitir</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="208"/>
+ <source>Remove</source>
+ <translation>Eliminar</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="189"/>
+ <source>Inherit</source>
+ <translation>Heredar</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="166"/>
+ <source>Inheritable</source>
+ <translation>Heredable</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="169"/>
+ <source>Inherited</source>
+ <translation>Heredado</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="206"/>
+ <source>Add</source>
+ <translation>Agregar</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="211"/>
+ <source>Add to Remove</source>
+ <translation>Agregar a Eliminar</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="232"/>
+ <source>&Groups</source>
+ <translation>&Grupos</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="233"/>
+ <source>&ACL</source>
+ <translation>&LCA</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="235"/>
+ <source>&OK</source>
+ <translation>&Aceptar</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="237"/>
+ <source>Accept changes</source>
+ <translation>Aceptar cambios</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="240"/>
+ <source>This button will accept current groups/ACLs and send them to the server. Note that if you mistakenly remove write permission from yourself, the server will add it.</source>
+ <translation>Este botón aceptará los grupos/LCAs actuales y los enviará al servidor. Fíjese en que si elimina por error su propio permiso de escritura, el servidor lo agregará.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="241"/>
+ <source>&Cancel</source>
+ <translation>&Cancelar</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="243"/>
+ <source>Reject changes</source>
+ <translation>Rechazar cambios</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>This button will cancels all changes and closes the dialog without updating the ACLs or groups on the server.</source>
+ <translation type="obsolete">Este botón cancelará todos los cambios y cerrará el cuadro de diálogo sin actualizar las LCAs o los grupos en el servidor.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="321"/>
+ <source>List of entries</source>
+ <translation>Lista de entradas</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="323"/>
+ <source>This shows all the entries active on this channel. Entries inherited from parent channels will be show in italics.</source>
+ <translation>Muestra todas las entradas activas en este canal. Las entradas heredadas de los canales padre se mostrarán en cursiva.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="324"/>
+ <source>Inherit ACL of parent?</source>
+ <translation>¿Heredar LCA del padre?</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="326"/>
+ <source>This sets whether or not the ACL up the chain of parent channels are applied to this object. Only those entries that are marked in the parent as "Apply to sub-channels" will be inherited.</source>
+ <translation>Establece si la LCA de la cadena de canales padre se aplican a este objeto o no. Sólo se heredarán aquellas entradas que estén marcadas en el padre con "Aplicar a Subcanales".</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="327"/>
+ <source>Add new entry</source>
+ <translation>Agregar nueva entrada</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="328"/>
+ <source>This adds a new entry, initially set with no permissions and applying to all.</source>
+ <translation>Agrega una nueva entrada, inicialmente establecida sin permisos y aplicándose a todo.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="329"/>
+ <source>Remove entry</source>
+ <translation>Eliminar entrada</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="330"/>
+ <source>This removes the currently selected entry.</source>
+ <translation>Elimina la entrada actualmente seleccionada.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="331"/>
+ <source>Move entry up</source>
+ <translation>Mover entrada hacia arriba</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="334"/>
+ <source>This moves the entry up in the list. As entries are evaluated in order, this may change the effective permissions of users. You cannot move an entry above an inherited entry, if you really need that you'll have to duplicate the inherited entry.</source>
+ <translation>Mueve la entrada hacia arriba en la lista. Ya que las entradas se evalúan en orden, esto podría cambiar los permisos efectivos de los usuarios. No puede mover una entrada por encima de una entrada heredada. Si realmente necesita hacer eso, tendrá que duplicar la entrada heredada.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="335"/>
+ <source>Move entry down</source>
+ <translation>Mover entrada hacia abajo</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="337"/>
+ <source>This moves the entry down in the list. As entries are evaluated in order, this may change the effective permissions of users.</source>
+ <translation>Mueve la entrada hacia abajo en la lista. Ya que las entradas se evalúan en orden, esto podría cambiar los permisos efectivos de los usuarios.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="338"/>
+ <source>Entry should apply to this channel</source>
+ <translation>La entrada debería aplicarse a este canal</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="339"/>
+ <source>This makes the entry apply to this channel.</source>
+ <translation>Hace que la entrada se aplique a este canal.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="340"/>
+ <source>Entry should apply to sub-channels.</source>
+ <translation>La entrada debería aplicarse a los subcanales.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="341"/>
+ <source>This makes the entry apply to sub-channels of this channel.</source>
+ <translation>Hace que la entrada se aplique a los subcanales de este canal.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="342"/>
+ <source>Group this entry applies to</source>
+ <translation>Grupo al que se aplica esta entrada</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="358"/>
+ <source>User this entry applies to</source>
+ <translation>Usuario al que se aplica esta entrada</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="360"/>
+ <source>This controls which user this entry applies to. Just type in the user name and hit enter to query the server for a match.</source>
+ <translation>Controla a qué usuario se le aplica esta entrada. Tan sólo escriba el nombre del usuario y pulse intro para buscar una correspondencia en el servidor.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="367"/>
+ <source>Allow %1</source>
+ <translation>Permitir %1</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="368"/>
+ <source>Deny %1</source>
+ <translation>Denegar %1</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="369"/>
+ <source>This grants the %1 privilege. If a privilege is both allowed and denied, it is denied.<br />%2</source>
+ <translation>Concede el privilegio %1. Si un privilegio es permitido y denegado a la vez, entonces es denegado.<br />%2</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="370"/>
+ <source>This revokes the %1 privilege. If a privilege is both allowed and denied, it is denied.<br />%2</source>
+ <translation>Revoca el privilegio %1. Si un privilegio es permitido y denegado a la vez, entonces es denegado.<br />%2</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="374"/>
+ <source>List of groups</source>
+ <translation>Lista de grupos</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="376"/>
+ <source>This is all the groups currently defined for the channel. To create a new group, just type in the name and press enter.</source>
+ <translation>Estos son todos los grupos actualmente definidos para el canal. Para crear un nuevo grupo, tan sólo escriba el nombre y presione intro.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="377"/>
+ <source>Remove selected group</source>
+ <translation>Elimina el grupo seleccionado</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="379"/>
+ <source>This removes the currently selected group. If the group was inherited, it will not be removed from the list, but all local information about the group will be cleared.</source>
+ <translation>Elimina el grupo actualmente seleccionado. Si el grupo fue heredado, no se eliminará de la lista, pero toda la información sobre el grupo se borrará.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="380"/>
+ <source>Inherit group members from parent</source>
+ <translation>Heredar miembros del grupo del padre</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="382"/>
+ <source>This inherits all the members in the group from the parent, if the group is marked as <i>Inheritable</i> in the parent channel.</source>
+ <translation>Hereda todos los miembros del grupo del padre, si el grupo está marcado como <i>Heredable</i> en el canal padre.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="383"/>
+ <source>Make group inheritable to sub-channels</source>
+ <translation>Hace al grupo heredable a los subcanales</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="385"/>
+ <source>This makes this group inheritable to sub-channels. If the group is non-inheritable, sub-channels are still free to create a new group with the same name.</source>
+ <translation>Hace este grupo heredable a los subcanales. Si el grupo no es heredable, los subcaneles son libres de crear un nuevo grupo con el mismo nombre.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="386"/>
+ <source>Group was inherited from parent channel</source>
+ <translation>El grupo fue heredado del canal padre</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="388"/>
+ <source>This indicates that the group was inherited from the parent channel. You cannot edit this flag, it's just for information.</source>
+ <translation>Indica que el grupo fue heredado del canal padre. No se puede editar este atributo, sólo es informativo.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="389"/>
+ <source>Add member to group</source>
+ <translation>Agregar miembro al grupo</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="390"/>
+ <source>Type in the name of a player you wish to add to the group and press enter.</source>
+ <translation>Escriba el nombre de un jugador que desee agregar al grupo y presione intro.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="391"/>
+ <source>Remove member from group</source>
+ <translation>Eliminar miembro del grupo</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="392"/>
+ <source>Type in the name of a player you wish to remove from the group and press enter.</source>
+ <translation>Escriba el nombre de un jugador que desee eliminar del grupo y presione intro.</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>This control which group of users this entry applies to.<br />Note that the group is evaluated in the context of the channel the entry is used in. For example, the default ACL on the Root channel gives <i>Write</i> permission to the <i>admin</i> group. This entry, if inherited by a channel, will give a user write privileges if he belongs to the <i>admin</i> group in that channel, even if he doesn't belong to the <i>admin</i> group in the channel where the ACL originated.<br />If a groupname starts with a !, it's membership is negated, and if it starts with a ~, it is evaluated in the channel the ACL was defined in, rather than the channel the ACL is active in. Order is important; <i>!~in</i> is valid, but <i>~!in</i> is not.<br />A few special predefined groups are:<br /><b>all</b> - Everyone will match.<br /><b>auth</b> - All authenticated users will match.<br /><b>sub,a,b,c</b> - User currently in a subchannel minimum <i>a</i> common parents, and between <i>b</i> and <i>c</i> channels down the chain. See the website for more extensive documentation on this one.<br /><b>in</b> - Users currently in the channel will match (convenience for <i>sub,0,0,0</i>.<br /><b>out</b> - Users outside the channel will match (convenience for <i>!sub,0,0,0</i>.<br />Note that an entry applies to either a user or a group, not both.</source>
+ <translation type="obsolete">Controla a que grupo de usuarios se aplica esta entrada.<br />Preste atención a que el grupo se evalúa en el contexto del canal en el que se usa la entrada. Por ejemplo, la LCA en el canal Raíz da el permiso <i>Escritura</i> al grupo <i>admin</i>. Esta entrada, si es heredada por un canal, dará al usuario privilegios de escritura si pertenece al grupo <i>admin</i> en ese canal, incluso si no pertence al grupo <i>admin</i> en el canal donde la LCA se originó. <br />Si un nombre de grupo comienza por !, se niega su pertenencia, y si comienza por ~, se evalúa en el canal en el que la LCA se definió, en vez de en el canal en el que la LCA está activa. El orden es importante; <i>!~in</i> es válido, pero <i>~!in</i> no lo es.<br/>Algunos grupos especiales predefinidos son:<br /><b>all</b> - Representa a todos los usuarios.<br /><b>auth</b> - Representa a todos los usuarios autenticados.<br /><b>sub,a,b,c</b> - Representa a un usuario en un subcanal con un mínimo de <i>a</i> padres comunes, y entre <i>b</i> y <i>c</i> canales por debajo en la cadena. Vea nuestro sitio web para una documentación más extensa acerca de éste.<br /><b>in</b> - Representa a los usuarios actualmente en el canal (abreviatura de <i>sub,0,0,0</i>).<br /><b>out</b> - Representa los usuarios fuera del canal (abreviatura de <i>!sub,0,0,0</i>).<br />Fíjese en que una entrada se aplica o a un usuario o a un grupo, no a ambos.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="45"/>
+ <source>Mumble - Edit ACL for %1</source>
+ <translation>Mumble- Editar LCA para %1</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="245"/>
+ <source>This button will cancel all changes and closes the dialog without updating the ACLs or groups on the server.</source>
+ <translation>Este botón cancelará todos los cambios y cerrará el cuadro de diálogo sin actualizar las LCAs o los grupos en el servidor.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="357"/>
+ <source>This controls which group of users this entry applies to.<br />Note that the group is evaluated in the context of the channel the entry is used in. For example, the default ACL on the Root channel gives <i>Write</i> permission to the <i>admin</i> group. This entry, if inherited by a channel, will give a user write privileges if he belongs to the <i>admin</i> group in that channel, even if he doesn't belong to the <i>admin</i> group in the channel where the ACL originated.<br />If a group name starts with a !, its membership is negated, and if it starts with a ~, it is evaluated in the channel the ACL was defined in, rather than the channel the ACL is active in. Order is important; <i>!~in</i> is valid, but <i>~!in</i> is not.<br />A few special predefined groups are:<br /><b>all</b> - Everyone will match.<br /><b>auth</b> - All authenticated users will match.<br /><b>sub,a,b,c</b> - User currently in a sub-channel minimum <i>a</i> common parents, and between <i>b</i> and <i>c</i> channels down the chain. See the website for more extensive documentation on this one.<br /><b>in</b> - Users currently in the channel will match (convenience for <i>sub,0,0,0</i>).<br /><b>out</b> - Users outside the channel will match (convenience for <i>!sub,0,0,0</i>).<br />Note that an entry applies to either a user or a group, not both.</source>
+ <translation>Controla a que grupo de usuarios se aplica esta entrada.<br />Preste atención a que el grupo se evalúa en el contexto del canal en el que se usa la entrada. Por ejemplo, la LCA en el canal Raíz da el permiso <i>Escritura</i> al grupo <i>admin</i>. Esta entrada, si es heredada por un canal, dará al usuario privilegios de escritura si pertenece al grupo <i>admin</i> en ese canal, incluso si no pertence al grupo <i>admin</i> en el canal donde la LCA se originó. <br />Si un nombre de grupo comienza por !, se niega su pertenencia, y si comienza por ~, se evalúa en el canal en el que la LCA se definió, en vez de en el canal en el que la LCA está activa. El orden es importante; <i>!~in</i> es válido, pero <i>~!in</i> no lo es.<br/>Algunos grupos especiales predefinidos son:<br /><b>all</b> - Representa a todos los usuarios.<br /><b>auth</b> - Representa a todos los usuarios autenticados.<br /><b>sub,a,b,c</b> - Representa a un usuario en un subcanal con un mínimo de <i>a</i> padres comunes, y entre <i>b</i> y <i>c</i> canales por debajo en la cadena. Vea nuestro sitio web para una documentación más extensa acerca de éste.<br /><b>in</b> - Representa a los usuarios actualmente en el canal (abreviatura de <i>sub,0,0,0</i>).<br /><b>out</b> - Representa los usuarios fuera del canal (abreviatura de <i>!sub,0,0,0</i>).<br />Fíjese en que una entrada se aplica o a un usuario o a un grupo, no a ambos.</translation>
+ </message>
+</context>
+<context>
+ <name>ASIOConfig</name>
+ <message>
+ <location filename="ASIOInput.cpp" line="113"/>
+ <source>Device selection</source>
+ <translation>Selección del dispositivo</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="114"/>
+ <source>Capabilities</source>
+ <translation>Capacidades</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="115"/>
+ <source>Channels</source>
+ <translation>Canales</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="116"/>
+ <source>Configure input channels</source>
+ <translation>Configurar canales de entrada</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="121"/>
+ <source>This will configure the input channels for ASIO. Make sure you select at least one channel as microphone and speaker. <i>Microphone</i> should be where your microphone is attached, and <i>Speaker</i> should be a channel that samples "What you hear".<br />For example, on the Audigy 2 ZS, a good selection for Microphone would be "Mic L" while Speaker should be "Mix L" and "Mix R"</source>
+ <translation>Configura los canales de entrada para ASIO. Asegúrese de seleccionar al menos un canal como micrófono y altavoz. <i>Micrófono</i> debería ser al que está conectado su micrófono, y <i>Altavoz</i> debería ser un canal que muestree "Lo que oye".<br />Por ejemplo, para Audigy 2 ZS, una buena selección para Micrófono sería "Mic L" mientras que Altavoz debería ser "Mix L" y "Mix R"</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="125"/>
+ <source>Device to use for microphone</source>
+ <translation>Dispositivo a usar para el micrófono</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="127"/>
+ <source>This chooses what device to query. You still need to actually query the device and select which channels to use.</source>
+ <translation>Elige qué dispositivo consultar. Aún debe consultar realmente el dispositivo y seleccionar qué canales usar.</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="128"/>
+ <source>Device</source>
+ <translation>Dispositivo</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="132"/>
+ <source>Query selected device</source>
+ <translation>Consulta el dispositivo seleccionado</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="135"/>
+ <source>This queries the selected device for channels. Be aware that many ASIO drivers are buggy to the extreme, and querying them might cause a crash of either the application or the system.</source>
+ <translation>Consulta el dispositivo seleccionado en busca de canales. Sea consciente de que muchos controladores ASIO son extremadamente poco fiables, y consultarlos podría bloquear la aplicación o el sistema.</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="139"/>
+ <source>Configure selected device</source>
+ <translation>Configura el dispositivo seleccionado</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="142"/>
+ <source>This configures the selected device. Be aware that many ASIO drivers are buggy to the extreme, and querying them might cause a crash of either the application or the system.</source>
+ <translation>Configura el dispositivo seleccionado. Sea consciente de que muchos controladores ASIO son extremadamente poco fiables, y consultarlos podría bloquear la aplicación o el sistema.</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="157"/>
+ <source>Driver name</source>
+ <translation>Nombre del controlador</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="162"/>
+ <source>Buffersize</source>
+ <translation>Tamaño del búfer</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="178"/>
+ <source>Microphone</source>
+ <translation>Micrófono</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="208"/>
+ <source><-</source>
+ <translation><-</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="205"/>
+ <source>-></source>
+ <translation>-></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="200"/>
+ <source>Unused</source>
+ <translation>Sin usar</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="222"/>
+ <source>Speakers</source>
+ <translation>Altavoces</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="318"/>
+ <source>%1 (ver %2)</source>
+ <translation>%1 (ver %2)</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="322"/>
+ <source>%1 ms -> %2 ms (%3 ms resolution) %4Hz</source>
+ <translation>%1 ms -> %2 ms (resolución %3 ms) %4Hz</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="324"/>
+ <source>%1 ms -> %2 ms (%3 ms resolution) %4Hz -- Unusable</source>
+ <translation>%1 ms -> %2 ms (resolución %3 ms) %4Hz -- Inutilizable</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="400"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="396"/>
+ <source>ASIO Initialization failed: %1</source>
+ <translation>Falló la inicialización de ASIO: %1</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>Failed to instanciate ASIO driver</source>
+ <translation type="obsolete">No se pudo instanciar el controlador ASIO</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="437"/>
+ <source>ASIO</source>
+ <translation>ASIO</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="400"/>
+ <source>Failed to instantiate ASIO driver</source>
+ <translation>No se pudo instanciar el controlador ASIO</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="130"/>
+ <source>&Query</source>
+ <translation>&Consultar</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="137"/>
+ <source>&Configure</source>
+ <translation>&Configurar</translation>
+ </message>
+</context>
+<context>
+ <name>ASIOInput</name>
+ <message>
+ <location filename="ASIOInput.cpp" line="580"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="512"/>
+ <source>You need to select at least one microphone and one speaker source to use ASIO. If you just need microphone sampling, use DirectSound.</source>
+ <translation>Debe seleccionar al menos un micrófono y una fuente altavoz para usar ASIO. Si sólo necesita el muestreo del micrófono, use DirectSound.</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="580"/>
+ <source>Opening selected ASIO device failed. No input will be done.</source>
+ <translation>Falló la apertura del dispositivo ASIO seleccionado. No se realizará ninguna entrada.</translation>
+ </message>
+</context>
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="About.cpp" line="53"/>
+ <source><h3>Mumble v0.9.5</h3><p>Copyright 2005-2006 Thorvald Natvig<br />slicer@users.sourceforge.net</p><p><b>A voice-chat utility for gamers</b></p><p><tt>http://mumble.sourceforge.net/</tt></p></source>
+ <translation type="obsolete"><h3>Mumble v0.9.5</h3><p>Copyright 2005-2006 Thorvald Natvig<br />slicer@users.sourceforge.net</p><p>Traducción al español por Álvaro M. Recio Pérez<br />naproxeno@users.sourceforge.net</p><p><b>Una utilidad de charla por voz para jugadores</b></p><p><tt>http://mumble.sourceforge.net/</tt></p></translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="60"/>
+ <source>&About Mumble</source>
+ <translation>&Acerca de Mumble</translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="61"/>
+ <source>&License</source>
+ <translation>&Licencia</translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="63"/>
+ <source>OK</source>
+ <translation>Aceptar</translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="54"/>
+ <source><h3>Mumble v%1</h3><p>Copyright 2005-2006 Thorvald Natvig<br />slicer@users.sourceforge.net</p><p><b>A voice-chat utility for gamers</b></p><p><tt>http://mumble.sourceforge.net/</tt></p></source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AboutSpeexDialog</name>
+ <message>
+ <location filename="About.cpp" line="84"/>
+ <source><h3>About Speex</h3><p><tt>http://www.speex.org/</tt></p><p>This program uses Speex version %1</p><p>Speex is used for echo cancellation, noise<br />filtering, voice activity detection and speech<br />compression.</p></source>
+ <translation><h3>Acerca de Speex</h3><p><tt>http://www.speex.org/</tt></p><p>Este programa usa Speex versión %1</p><p>Speex se usa para cancelación de eco, filtrado de<br />ruido, detección de actividad vocal y compresión del<br />habla.</p></translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="86"/>
+ <source>OK</source>
+ <translation>Aceptar</translation>
+ </message>
+</context>
+<context>
+ <name>AudioConfigDialog</name>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="51"/>
+ <source>Interfaces</source>
+ <translation>Interfaces</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="52"/>
+ <source>Transmission</source>
+ <translation>Transmisión</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="53"/>
+ <source>Compression</source>
+ <translation>Compresión</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="54"/>
+ <source>Jitter Buffer</source>
+ <translation>Búfer de jitter</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="67"/>
+ <source>Input</source>
+ <translation>Entrada</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="70"/>
+ <source>Input method for audio</source>
+ <translation>Método de entrada para el audio</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="71"/>
+ <source><b>This is the input method to use for audio.</b><br />Most likely you want to use DirectSound.</source>
+ <translation><b>Éste es el método de entrada a usar para el audio.</b><br />Muy probablemente desee utilizar DirectSound.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="85"/>
+ <source>Output</source>
+ <translation>Salida</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="88"/>
+ <source>Output method for audio</source>
+ <translation>Método de salida para el audio</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="89"/>
+ <source><b>This is the output method to use for audio.</b><br />Most likely you want to use DirectSound.</source>
+ <translation><b>Éste es el método de salida a usar para el audio.</b><br />Muy probablemente quiera utilizar DirectSound.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="100"/>
+ <source>Voice Activity</source>
+ <translation>Actividad Vocal</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="101"/>
+ <source>Push To Talk</source>
+ <translation>Presionar Para Hablar</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="103"/>
+ <source>Transmit</source>
+ <translation>Transmitir</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="106"/>
+ <source>When to transmit your speech</source>
+ <translation>Cuándo transmitir su habla</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="121"/>
+ <source>Voice Hold</source>
+ <translation>Persistencia de la voz</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="128"/>
+ <source>How long to keep transmitting after silence</source>
+ <translation>Cuánto tiempo continuar transmitiendo tras un silencio</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="131"/>
+ <source><b>This selects how long after a perceived stop in speech transmission should continue.</b><br />Set this higher if your voice breaks up when you speak (seen by a rapidly blinking voice icon next to your name).<br />Only makes sense when used with Voice Activity transmission.</source>
+ <translation><b>Selecciona cuánto tiempo debería continuar la transmisión tras una detección percibida en el habla.</b>Auméntelo si su voz es interrumpida mientras habla (observable por un icono de voz parpadeando rápidamente junto a su nombre).<br />Sólo tiene sentido cuando se usa con la transmisión por Actividad Vocal.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="144"/>
+ <source>Audio per packet</source>
+ <translation>Audio por paquete</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="153"/>
+ <source>How many audio frames to send per packet</source>
+ <translation>Cuántas tramas de audio enviar por paquete</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="156"/>
+ <source><b>This selects how many audio frames should be put in one packet.</b><br />Increasing this will increase the latency of your voice, but will also reduce bandwidth requirements.</source>
+ <translation><b>Selecciona cuántas tramas de audio deberían insertarse en un paquete.</b><br />Aumentar esto incrementará la latencia de su voz, pero también reducirá los requisitos de ancho de banda.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="161"/>
+ <source>Use TCP mode</source>
+ <translation>Usar modo TCP</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="170"/>
+ <source>Automatic Reconnect</source>
+ <translation>Reconexión automática</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="172"/>
+ <source>Reconnect when disconnected</source>
+ <translation>Volver a conectar cuando sea desconectado</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="176"/>
+ <source><b>Reconnect when disconnected</b>.<br />This will make Mumble try to automatically reconnect after 10 seconds if your server connection fails.</source>
+ <translation><b>Volver a conectar cuando sea desconectado</b>.<br />Hace que Mumble intente volver a conectar automáticamente después de 10 segundos si falla su conexión con el servidor.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="184"/>
+ <source>Maximum bandwidth used for sent audio</source>
+ <translation>Ancho de banda máximo usado para el audio enviado</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="191"/>
+ <source>Outgoing Bitrate</source>
+ <translation>Velocidad de transmisión saliente</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="209"/>
+ <source>Quality</source>
+ <translation>Calidad</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="216"/>
+ <source>Quality of compression (peak bandwidth)</source>
+ <translation>Calidad de la compresión (ancho de banda máximo)</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="218"/>
+ <source><b>This sets the quality of compression.</b><br />This determines how much bandwidth Mumble is allowed to use for outgoing audio.</source>
+ <translation><b>Establece la calidad de compresión.</b><br />Esto determina cuánto ancho de banda se le permite usar a Mumble para el audio saliente.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="231"/>
+ <source>Complexity</source>
+ <translation>Complejidad</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="237"/>
+ <source>Complexity of compression (CPU)</source>
+ <translation>Complejidad de la compresión (CPU)</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="240"/>
+ <source><b>This sets the complexity of compression.</b><br />This determines how much CPU Mumble is allowed to use to increase transmitted voice quality. Settings above 5 give only marginal gain.</source>
+ <translation><b>Establece la complejidad de la compresión.</b><br />Esto determina cuánta CPU se le permite usar a Mumble para incrementer la calidad de la voz transmitida. Valores por encima de 5 proporcionan sólo una ganancia marginal.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="253"/>
+ <source>Amp</source>
+ <translation>Amplificación</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="259"/>
+ <source>Maximum amplification of input sound</source>
+ <translation>Amplificación máxima del sonido de entrada</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="270"/>
+ <source><b>Maximum amplification of input.</b><br />Mumble normalizes the input volume before compressing, and this sets how much it's allowed to amplify.<br />The actual level is continually updated based on your current speech pattern, but it will never go above the level specified here.<br />If the <i>Microphone loudness</i> 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.<br />Ideally, set it so <i>Microphone Loudness * Amplification Factor >= 100</i>, even when you're speaking really soft.<br /><br />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.</source>
+ <translation><b>Amplificación máxima de la entrada.</b><br />Mumble normaliza el volumen de entrada antes de la compresión, y esto establece cuánto se le permite amplificar.<br />El nivel real se actualiza continuamente basado en su patrón de habla actual, pero nunca sobrepasará el nivel especificado aquí.<br />Si el nivel de <i>Volumen del Micrófono</i> de las estadísticas de audio ronda el 100%, probablemente desee establecer esto alrededor de 2.0, pero si, como la mayoría de la gente, no consigue alcanzar 100%, establezca esto a algo mucho mayor.<br />Idealmente, fíjelo de tal forma que <i>Volumen del Micrófono * Factor de Amplificación >= 100</i>, incluso cuando está hablando realmente bajo.<br /><br />Fíjese en que no es perjudicial establecer esto al máximo, pero Mumble comenzará a captar otras conversaciones si lo deja autoajustarse a ese nivel.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="288"/>
+ <source>Default Jitter Buffer</source>
+ <translation>Búfer de jitter por defecto</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="295"/>
+ <source>How long to prebuffer on first packet</source>
+ <translation>Cuánto tiempo prealmacenar con el primer paquete</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="302"/>
+ <source><b>This sets the default buffer size for the jitter buffer</b>.<br />All incoming audio is buffered, and the jitter buffer continually tries to push the buffer to the minimum sustainable by your network, so latency can be as low as possible. This sets the default buffer size to use on the first packet to arrive from a new speaker, or when a speaker using Voice Activity or Push-To-Talk just started talking again. If the start of sentences you hear is very jittery, increase this value.</source>
+ <translation><b>Establece el tamaño de búfer por defecto del búfer de jitter</b>.<br />Todo el audio entrante es almacenado en un búfer, y el búfer de jitter contínuamente trata de reducir el búfer al mínimo sostenible por su red, para que la latencia sea tan baja como sea posible. Esto establece el tamaño de búfer por defecto a usar con el primer paquete que llegue de un nuevo interlocutor, o cuando un interlocutor que use Actividad Vocal o Presionar-Para-Hablar comience a hablar de nuevo. Si el comienzo de las frases que oye es muy fluctuante, incremente este valor.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="321"/>
+ <source>Basic Audio</source>
+ <translation>Audio básico</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="369"/>
+ <source>%1 ms</source>
+ <translation>%1 ms</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="350"/>
+ <source>%1s</source>
+ <translation>%1 s</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="99"/>
+ <source>Continuous</source>
+ <translation>Contínuo</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="110"/>
+ <source><b>This sets when speech should be transmitted.</b><br /><i>Continuous</i> - All the time<br /><i>Voice Activity</i> - When you are speaking clearly.<br /><i>Push To Talk</i> - When you hold down the hotkey set under <i>Shortcuts</i>.</source>
+ <translation><b>Establece cuándo debería transmitirse el habla.</b><br /><i>Contínuo</i> - Todo el tiempo<br /><i>Actividad Vocal</i> - Cuando está hablando claramente.<br /><i>Presionar Para Hablar</i> - Cuando presiona la tecla establecida en <i>Métodos abreviados</i>.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="163"/>
+ <source>Use TCP Compatibility mode</source>
+ <translation>Usar modo de Compatibilidad TCP</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="168"/>
+ <source><b>Enable TCP Compatibility mode</b>.<br />This will make Mumble use only TCP when communicating with the server. This will increase overhead and cause lost packets to produce noticeable pauses in communication, so this should only be used if you are unable to use the default (which uses UDP for voice and TCP for control).</source>
+ <translation><b>Habilita el modo de Compatibilidad TCP</b>.<br />Esto hará que Mumble sólo use TCP cuando se comunique con el servidor. Esto Incrementará la sobrecarga y provocará que los paquetes perdidos produzcan notables pausas en la comunicación, así que sólo debería ser usado si no consigue usar la opción por defecto (que emplea UDP para la voz y TCP para el control).</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="189"/>
+ <source><b>This shows peak outgoing bandwidth used.</b><br />This shows the peak amount of bandwidth sent out from your machine. Audio bitrate is the maximum bitrate (as we use VBR) for the audio data alone. Position is the bitrate used for positional information. Overhead is our framing and the IP packet headers (IP and UDP is 90% of this overhead).</source>
+ <translation><b>Muestra el ancho de banda máximo empleado.</b><br />Esto muestra la cantidad máxima de ancho de banda enviado desde su máquina. Velocidad de transmisión del audio es la velocidad de transmisión máxima (ya que se usa VBR) únicamente para los datos de audio. Posición es la velocidad de transmisión usada para información posicional. Sobrecarga son las cabeceras de nuestras tramas y de los paquetes IP (IP y UDP son el 90% de esta sobrecarga).</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="418"/>
+ <source>%1kbit/s (Audio %2, Position %4, Overhead %3)</source>
+ <translation>%1kbit/s (Audio %2, Posición %4, Sobrecarga %3)</translation>
+ </message>
+</context>
+<context>
+ <name>AudioStats</name>
+ <message>
+ <location filename="AudioStats.cpp" line="342"/>
+ <source>Peak microphone level</source>
+ <translation>Nivel máximo del micrófono</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="347"/>
+ <source>Peak speaker level</source>
+ <translation>Nivel máximo de los altavoces</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="352"/>
+ <source>Peak clean level</source>
+ <translation>Nivel limpio máximo</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="357"/>
+ <source>Microphone loudness</source>
+ <translation>Intensidad del micrófono</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="362"/>
+ <source>Mic Signal-To-Noise</source>
+ <translation>Señal/Ruido del micrófono</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="367"/>
+ <source>Speech Probability</source>
+ <translation>Probabilidad de habla</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="372"/>
+ <source>Audio bitrate</source>
+ <translation>Velocidad de transmisión del audio</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="432"/>
+ <source>Peak power in last frame</source>
+ <translation>Potencia máxima en la última trama</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="426"/>
+ <source>This shows the peak power in the last frame (20 ms), and is the same measurement as you would usually find displayed as "input power". Please disregard this and look at <b>Loudness</b> instead, which is much more steady and disregards outliers.</source>
+ <translation>Muestra la potencia máxima en la última trama (20 ms), y es la misma medida que generalmente encontrará mostrada como "potencia de entrada". Por favor, no haga caso a esto y vea <b>Intensidad</b> en su lugar, que es mucho más estable e ignora las muestras extremas.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="431"/>
+ <source>This shows the peak power in the last frame (20 ms) of the speakers. 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.</source>
+ <translation>Muestra la potencia máxima de los altavoces en la última trama (20 ms). A menos que esté usando un método de muestreo multicanal (como ASIO) con los canales de altavoces configurados, esto será 0. Si tiene configurada tal instalación, y esto aún muestra 0 mientras reproduce audio de otros programas, su instalación no está funcionando.</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>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 -20dB. When you are talking, this should rise to somewhere between -5 and -10 dB.<br />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 players with echos.</source>
+ <translation type="obsolete">Muestra la potencia máxima en la última trama (20 ms) tras todo el procesado. Idealmente, esto debería ser -96 dB cuando no esté hablando. En realidad, un estudio de sonido debería ver -60 dB, y usted debería ver con suerte en torno a -20 dB. Cuando hable, ésto debería aumentar hasta un valor entre -5 y -10 dB.<br />Si está usando cancelación de eco, y ésto sube a más de -15 dB cuando no está hablando, su instalación no está funcionando, y molestará con ecos a otros jugadores.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="439"/>
+ <source>How close the current input level is to ideal</source>
+ <translation>Cuán próximo al ideal está el nivel de entrada actual</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="446"/>
+ <source>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.<br /><b>Talk loud, as you would when you're upset over getting fragged by a noob.</b><br />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</source>
+ <translation>Muestra cuán próximo al ideal está su nivel de volumen de entrada actual. Para ajustar su nivel de micrófono, abra el programa que emplea para ajustar el volumen de grabación, y observe el valor de aquí mientras habla.<br /><b>Hable alto, como lo haría cuando está enfadado por perder ante un novato.</b><br />Ajuste el volumen hasta que este valor esté próximo a 100%, pero asegúrese de que no lo sobrepasa. Si lo sobrepasa, es probable que se produzcan cortes en partes de su habla, lo que degradará la calidad de sonido</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="447"/>
+ <source>Signal-To-Noise ratio from the microphone</source>
+ <translation>Tasa Señal/Ruido del micrófono</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="452"/>
+ <source>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.<br />If this value is below 1.0, there's more noise than voice in the signal, and so quality is reduced.<br />There is no upper limit to this value, but don't expect to see much above 40-50 without a sound studio.</source>
+ <translation>Muestra la Tasa Señal/Ruido (SNR) del micrófono en la última trama (20 ms). Muestra cuán clara es la voz en comparación con el ruido.<br />Si este valor está por debajo de 1.0, hay más ruido que voz en la señal, y por tanto se reduce la calidad.<br />No hay límite superior para este valor, pero no espere ver muy por encima de 40-50 sin un estudio de sonido.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="453"/>
+ <source>Probability of speech</source>
+ <translation>Probabilidad de habla</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="459"/>
+ <source>Bitrate of last frame</source>
+ <translation>Velocidad de transmisión de la última trama</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="462"/>
+ <source>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. To adjust the peak bitrate, adjust <b>Compression Complexity</b> in the Settings dialog.</source>
+ <translation>Ésta es la velocidad de transmisión de la última trama comprimida (20 ms), y como tal aumentará y disminuirá mientras VBR ajusta la calidad. Para ajustar la velocidad de transmisión máxima, ajuste <b>Complejidad de Compresión</b> en el cuadro de diálogo Opciones.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="464"/>
+ <source>Power spectrum of input signal and noise estimate</source>
+ <translation>Espectro de potencia de la señal de entrada y estimación del ruido</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="474"/>
+ <source>Weights of the echo canceller</source>
+ <translation>Pesos del cancelador de eco</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="458"/>
+ <source>This is the probability that the last frame (20 ms) was speech and not environment noise.<br />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'.<br />If this is in bold font, it means Mumble is currently transmitting (if you're connected).</source>
+ <translation>Esta es la probabilidad de que la última trama (20 ms) fuera habla y no ruido ambiental.<br />La transmisión por Actividad Vocal depende de la correción de este valor. El truco de esto es que el medio de una frase se detecta siempre como habla, el problema son las pausas entre palabras y el comienzo del discurso. Es difícil distinguir un suspiro de una palabra que empiece con 'h'.<br />Si esto está en fuente negrita, significa que Mumble está transmitiendo actualmente (si está conectado).</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="471"/>
+ <source>This shows the power spectrum of the current input signal (red line) and the current noise estimate (filled blue).<br />All amplitudes are multiplied by 30 to show the interesting parts (how much more signal than noise is present in each waveband).<br />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.</source>
+ <translation>Muestra el espectro de potencia de la señal de entrada actual (línea roja) y la estimación de ruido actual (relleno azul).<br />Todas las amplitudes se multiplican por 30 para mostrar las partes interesantes (cuánta más señal que ruido está presente en cada banda de onda).<br />Esto probablemente tiene interés solo si está tratando de ajustar con precisión las condiciones de ruido de su micrófono. Bajo buenas condiciones, debería haber tan sólo una pequeña palpitación de azul en la parte inferior. Si el azul sobrepasa más de la mitad del gráfico, posee un entorno seriamente ruidoso.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="487"/>
+ <source>This shows the weights of the echo canceller, with time increasing downwards and frequency increasing to the right.<br />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.<br />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. <br />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.<br />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.</source>
+ <translation>Muestra los pesos del cancelador de eco, con el tiempo incrementándose hacia abajo y la frecuencia incrementándose hacia la derecha.<br />Idealmente, esto debería ser negro, indicando que no existe eco alguno. Más comúnmente, habrá una o más bandas horizontales de color azulado que representan eco retrasado en el tiempo. Debería poder ver los pesos actualizados en tiempo real.<br />Fíjese en que mientras no haya nada a lo que cancelar el eco, no verá datos muy útiles aquí. Reproduzca algo de música y las cosas deberían estabilizarse.<br />Puede elegir ver las partes reales o imaginarias de los pesos en el dominio de la frecuencia, o de forma alternativa el módulo y la fase calculados. Lo más útil de estos probablemente sea el módulo, que es la amplitud del eco, y muestra cuánto de la señal saliente está siendo eliminado en este paso de tiempo. Los otros modos de visionado son útiles en su mayoría a personas que quieren ajustar los algoritmos de cancelación de eco.<br />Por favor fíjese en que si la imagen completa fluctúa ampliamente mientras está en el modo módulo, el cancelador de eco no logra encontrar ninguna correlación entre las dos fuentes de entrada (altavoces y micrófono). O tiene un retraso muy largo en el eco, o una de las fuentes de entrada está mal configurada.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="438"/>
+ <source>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.<br />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 players with echoes.</source>
+ <translation>Muestra la potencia máxima en la última trama (20 ms) tras todo el procesado. Idealmente, esto debería ser -96 dB cuando no esté hablando. En realidad, un estudio de sonido debería ver -60 dB, y usted debería ver con suerte en torno a -20 dB. Cuando hable, esto debería aumentar hasta un valor entre -5 y -10 dB.<br />Si está usando cancelación de eco, y esto sube a más de -15 dB cuando no está hablando, su instalación no está funcionando, y molestará con ecos a otros jugadores.</translation>
+ </message>
+</context>
+<context>
+ <name>BanEditor</name>
+ <message>
+ <location filename="BanEditor.cpp" line="42"/>
+ <source>&Add</source>
+ <translation>&Agregar</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="43"/>
+ <source>&Update</source>
+ <translation>A&ctualizar</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="44"/>
+ <source>&Remove</source>
+ <translation>&Eliminar</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="69"/>
+ <source>&OK</source>
+ <translation>&Aceptar</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="71"/>
+ <source>Accept changes</source>
+ <translation>Aceptar cambios</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="74"/>
+ <source>This button will accept current groups/ACLs and send them to the server. Note that if you mistakenly remove write permission from yourself, the server will add it.</source>
+ <translation>Este botón aceptará los grupos/LCAs actuales y los enviará al servidor. Fíjese en que si elimina por error su propio permiso de escritura, el servidor lo agregará.</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="75"/>
+ <source>&Cancel</source>
+ <translation>&Cancelar</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="77"/>
+ <source>Reject changes</source>
+ <translation>Rechazar cambios</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="79"/>
+ <source>This button will cancels all changes and closes the dialog without updating the ACLs or groups on the server.</source>
+ <translation>Este botón cancelará todos los cambios y cerrá el cuadro de diálogo sin actualizar las LCAs o los grupos en el servidor.</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="37"/>
+ <source>Mumble - Edit Bans</source>
+ <translation>Mumble - Editar prohibiciones</translation>
+ </message>
+</context>
+<context>
+ <name>ChanACL</name>
+ <message>
+ <location filename="../ACL.cpp" line="129"/>
+ <source>W</source>
+ <translation>E</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="131"/>
+ <source>T</source>
+ <translation>R</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="133"/>
+ <source>E</source>
+ <translation>I</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="135"/>
+ <source>S</source>
+ <translation>H</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="137"/>
+ <source>A</source>
+ <translation>A</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="139"/>
+ <source>M</source>
+ <translation>M</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="141"/>
+ <source>K</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="143"/>
+ <source>C</source>
+ <translation>C</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="145"/>
+ <source>L</source>
+ <translation>V</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="155"/>
+ <source>None</source>
+ <translation>Ninguno</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="157"/>
+ <source>Write</source>
+ <translation>Escritura</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="159"/>
+ <source>Traverse</source>
+ <translation>Recorrer</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="161"/>
+ <source>Enter</source>
+ <translation>Ingresar</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="163"/>
+ <source>Speak</source>
+ <translation>Hablar</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="165"/>
+ <source>AltSpeak</source>
+ <translation>HablarAlt</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="167"/>
+ <source>Mute/Deafen</source>
+ <translation>Enmudecer/Ensordecer</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="169"/>
+ <source>Move/Kick</source>
+ <translation>Mover/Expulsar</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="171"/>
+ <source>Make channel</source>
+ <translation>Crear canal</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="173"/>
+ <source>Link channel</source>
+ <translation>Vincular canal</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="183"/>
+ <source>This represents no privileges.</source>
+ <translation>No representa ningún privilegio.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="186"/>
+ <source>This represents total access to the channel, including the ability to change group and ACL information. This privilege implies all other privileges.</source>
+ <translation>Representa acceso total al canal, incluyendo la habilidad de cambiar la información de grupo y LCA. Este privilegio implica los otros privilegios.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="190"/>
+ <source>This represents the permission to traverse the channel. If a user is denied this privilege, he will be unable to access this channel and any sub-channels in any way, regardless of other permissions in the sub-channels.</source>
+ <translation>Representa el permiso de recorrer el canal. Si se deniega este privilegio a un usuario, no podrá acceder a este canal ni ningún subcanal de ninguna forma, sin importar otros permisos en los subcanales.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="193"/>
+ <source>This represents the permission to join the channel. If you have a hierarchical channel structure, you might want to give everyone Traverse, but restrict Enter in the root of your hierarchy.</source>
+ <translation>Representa el permiso de unirse al canal. Si tiene una estructura de canales jerárquica, podría desear dar Recorrer a todos, pero restringir Ingresar en la raíz de su jerarquía.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="197"/>
+ <source>This represents the permission to speak in a channel. Users without this privilege will be suppressed by the server (seen as muted), and will be unable to speak until they are unmuted by someone with the appropriate privileges.</source>
+ <translation>Representa el permiso de hablar en un canal. Los usuarios sin este privilegio serán suprimidos por el servidor (vistos como mudos), y no podrán hablar hasta que se les de voz con los privilegios adecuados.</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>This represents the permission to speak in a channel with flagged speech. This works exactly like the <i>speek</i> privilege, but applies to packets spoken with AltPushToTalk held down. Used to broadcast to a hierarchy of channels without linking.</source>
+ <translation type="obsolete">Representa el permiso de hablar en un canal con habla marcada. Funciona exactamente como el privilegio <i>hablar</i>, pero se aplica a paquetes hablados con PresionarParaHablarAlt pulsado. Se usa para transmitir a una jerarquía de canales sin vincular.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="204"/>
+ <source>This represents the permission to mute and deafen other players. Once muted, a player will stay muted until he is unmuted by another privileged player or reconnects to the server.</source>
+ <translation>Representa el permiso de enmudecer y ensordecer a otros jugadores. Una vez enmudecido, un jugador permanecerá enmudecido hasta que se le otorgue voz por otro jugador con privilegios o se vuelva a conectar al servidor.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="209"/>
+ <source>This represents the permission to move a player to another channel or kick him from the server. To actually move the player, either the moving player must have Move/Kick privileges in the destination channel, or the player must normally be allowed to enter the channel. Players with this privilege can move players into channels the target player normally wouldn't have permission to enter.</source>
+ <translation>Representa el permiso de mover a un jugador a otro canal o expulsarlo del servidor. Para mover realmente al jugador, o bien el jugador que mueve debe tener privilegios Mover/Expulsar en el canal de destino, o debe estar permitido al jugador ingresar normalmente en el canal. Los jugadores con este privilegio pueden trasladar a jugadores a canales a los que el jugador objetivo normalmente no tendría permiso para entrar.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="212"/>
+ <source>This represents the permission to make sub-channels. The player making the sub-channel will be added to the admin group of the sub-channel.</source>
+ <translation>Representa el permiso de crear subcanales. El jugador que crea el subcanal será agregado al grupo admin del subcanal.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="216"/>
+ <source>This represents the permission to link channels. Players in linked channels hear each other, as long as the speaking player has the <i>speak</i> privilege in the channel of the listener. You need the link privilege in both channels to create a link, but just in either channel to remove it.</source>
+ <translation>Representa el permiso de vincular canales. Los jugadores en canales vinculados se oirán entre sí, siempre que el jugador que habla tenga el privilegio <i>hablar</i> en el canal del oyente. Necesita el privilegio de vincular en ambos canales para crear un vínculo, pero sólo en uno de los caneles para eliminarlo.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="201"/>
+ <source>This represents the permission to speak in a channel with flagged speech. This works exactly like the <i>speak</i> privilege, but applies to packets spoken with AltPushToTalk held down. This may be used to broadcast to a hierarchy of channels without linking.</source>
+ <translation>Representa el permiso de hablar en un canal con habla marcada. Funciona exactamente como el privilegio <i>hablar</i>, pero se aplica a paquetes hablados con PresionarParaHablarAlt pulsado. Se usa para transmitir a una jerarquía de canales sin vincular.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigDialog</name>
+ <message>
+ <location filename="ConfigDialog.cpp" line="57"/>
+ <source>Mumble Configuration</source>
+ <translation>Configuración de Mumble</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="69"/>
+ <source>&OK</source>
+ <translation>&Aceptar</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="72"/>
+ <source>Accept changes</source>
+ <translation>Aceptar cambios</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="74"/>
+ <source>This button will accept current settings and return to the application.<br />The settings will be stored to disk when you leave the application.</source>
+ <translation>Este botón aceptará las opciones actuales y volverá a la aplicación.<br />Las opciones se almacenarán en el disco cuando salga de la aplicación.</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="75"/>
+ <source>&Cancel</source>
+ <translation>&Cancelar</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="77"/>
+ <source>Reject changes</source>
+ <translation>Rechazar cambios</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="79"/>
+ <source>This button will reject all changes and return to the application.<br />The settings will be reset to the previous positions.</source>
+ <translation>Este botón rechazará todos los cambios y volverá a la aplicación.<br />Las opciones se restaurarán a las posiciones previas.</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="80"/>
+ <source>&Apply</source>
+ <translation>&Aplicar</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="82"/>
+ <source>Apply changes</source>
+ <translation>Aplicar cambios</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="83"/>
+ <source>This button will immediately apply all changes.</source>
+ <translation>Este botón aplicará inmediatamente todos los cambios.</translation>
+ </message>
+</context>
+<context>
+ <name>ConnectDialog</name>
+ <message>
+ <location filename="ConnectDialog.cpp" line="65"/>
+ <source>&Name</source>
+ <translation>&Nombre</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="72"/>
+ <source>A&ddress</source>
+ <translation>&Dirección</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="80"/>
+ <source>&Port</source>
+ <translation>&Puerto</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="87"/>
+ <source>&Username</source>
+ <translation>Nombre de &usuario</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="95"/>
+ <source>&Password</source>
+ <translation>&Contraseña</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="101"/>
+ <source>&Connect</source>
+ <translation>&Conectar</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="105"/>
+ <source>Cancel</source>
+ <translation>Cancelar</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="108"/>
+ <source>&Add</source>
+ <translation>&Agregar</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="111"/>
+ <source>&Remove</source>
+ <translation>&Eliminar</translation>
+ </message>
+</context>
+<context>
+ <name>DXAudioInput</name>
+ <message>
+ <location filename="DXAudioInput.cpp" line="108"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="DXAudioInput.cpp" line="103"/>
+ <source>Opening chosen DirectSound Input failed. No microphone capture will be done.</source>
+ <translation>Falló la apertura del dispositivo DirectSound elegido. No se realizará ninguna captura del micrófono.</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>Opening chosen DirectSound Input failed. Using defaults.</source>
+ <translation type="obsolete">Falló la apertura del dispositivo DirectSound elegido. Usando los valores por defecto.</translation>
+ </message>
+ <message>
+ <location filename="DXAudioInput.cpp" line="108"/>
+ <source>Opening chosen DirectSound Input failed. Default device will be used.</source>
+ <translation>Falló la apertura del dispositivo DirectSound elegido. Se usará el dispositivo por defecto.</translation>
+ </message>
+</context>
+<context>
+ <name>DXAudioOutput</name>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="341"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="336"/>
+ <source>Opening chosen DirectSound Output failed. No audio will be heard.</source>
+ <translation>Falló la apertura del dispositivo DirectSound elegido. No se oirá el audio.</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>Opening chosen DirectSound Output failed. Using defaults.</source>
+ <translation type="obsolete">Falló la apertura del dispositivo DirectSound elegido. Usando los valores por defecto.</translation>
+ </message>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="341"/>
+ <source>Opening chosen DirectSound Output failed. Default device will be used.</source>
+ <translation>Falló la apertura del dispositivo DirectSound elegido. Se usará el dispositivo por defecto.</translation>
+ </message>
+</context>
+<context>
+ <name>DXConfigDialog</name>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="61"/>
+ <source>Default DirectSound Voice Input</source>
+ <translation>Entrada de voz DirectSound por defecto</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="62"/>
+ <source>Default DirectSound Voice Output</source>
+ <translation>Salida de voz DirectSound por defecto</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="84"/>
+ <source>Device selection</source>
+ <translation>Selección del dispositivo</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="87"/>
+ <source>Device to use for microphone</source>
+ <translation>Dispositivo a usar para el micrófono</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="89"/>
+ <source>This sets the input device to use, which is where you have connected the microphone.</source>
+ <translation>Establece el dispositivo de entrada a usar, que es donde ha conectado el micrófono.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="90"/>
+ <source>Input</source>
+ <translation>Entrada</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="95"/>
+ <source>Device to use for speakers/headphones</source>
+ <translation>Dispositivo a usar para los altavoces/auriculares</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>This sets the output device to use, which is where you have connected your speakers or your headset</source>
+ <translation type="obsolete">Establece el dispositivo de salida a usar, que es donde ha conectado sus altavoces o auriculares.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="98"/>
+ <source>Output</source>
+ <translation>Salida</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="105"/>
+ <source>Output Options</source>
+ <translation>Opciones de salida</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="114"/>
+ <source>Output Delay</source>
+ <translation>Retraso de salida</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="119"/>
+ <source>Amount of data to buffer for DirectSound</source>
+ <translation>Cantidad de datos a almacenar en el búfer para DirectSound</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>This sets the amount of data to prebuffer in the directsound buffer. Experiment with different values and set it to the lowest which doesn't cause rapid jitter in the sound.</source>
+ <translation type="obsolete">Establece la cantidad de datos que prealmacenar en el búfer directsound. Experimente con diferentes valores y fíjelo al más bajo que no cause fluctuaciones rápidas en el sonido.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="130"/>
+ <source>Positional Audio</source>
+ <translation>Audio posicional</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="134"/>
+ <source>None</source>
+ <translation>Ninguno</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="135"/>
+ <source>Panning</source>
+ <translation>Panning</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="136"/>
+ <source>Light HRTF</source>
+ <translation>HRTF ligera</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="137"/>
+ <source>Full HRTF</source>
+ <translation>HRTF completa</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="140"/>
+ <source>3D Sound Algorithm</source>
+ <translation>Algoritmo de sonido 3D</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="148"/>
+ <source>This sets what 3D Sound algorithm to use.<br /><b>None</b> - Disable 3D Sound (least CPU).<br /><b>Panning</b> - Just use stereo panning (some CPU).<br /><b>Light/Full HRTF</b> - Head-Related Transfer Functions enabled. This may use a small amount of CPU.<br />Note that if you have a soundcard with <i>hardware</i> 3D processing, HRTF processing will be done on the soundcard and will use practically no processing power.</source>
+ <translation>Establece qué algoritmo de Sonido 3D usar.<br /><b>Ninguno</b> - Desactiva el Sonido 3D (menos CPU).<br /><b>Panning</b> - Sólo usa panning estéreo (poca CPU).<br /><b>HRTF Ligera/Completa</b> - Habilita las Funciones de Transferencia Relacionadas a la Cabeza (HRTF). Esto podría usar una pequeña cantidad de CPU.<br />Fíjese en que si tiene una tarjeta de sonido con procesamiento 3D <i>hardware</i>, el procesado de HRTF se hará en la tarjeta de sonido y casi no usará poder de procesamiento.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="149"/>
+ <source>Method</source>
+ <translation>Método</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="164"/>
+ <source>MinDistance</source>
+ <translation>Distancia mínima</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="169"/>
+ <source>Minimum distance to player before sound decreases</source>
+ <translation>Distancia mínima al jugador antes de que el sonido disminuya</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="171"/>
+ <source>This sets the minimum distance for sound calculations. The volume of other players' speech will not decrease until they are at least this far away from you.</source>
+ <translation>Establece la distancia mínima para cálculos de sonido. El volumen del habla de otros jugadores no disminuirá hasta que estén al menos a esta distancia de ti.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="181"/>
+ <source>MaxDistance</source>
+ <translation>Distancia máxima</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="186"/>
+ <source>Maximum distance, beyond which sound won't decrease</source>
+ <translation>Distancia máxima, más allá de la cual el sonido no disminuirá</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="188"/>
+ <source>This sets the maximum distance for sound calculations. When farther away than this, other players' sound volume will not decrease any more.</source>
+ <translation>Establece la distancia máxima para cálculos de sonido. Cuando estén más lejos que esto, el volumen del sonido de otros jugadores no disminuirá más.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="198"/>
+ <source>RollOff</source>
+ <translation>Atenuación</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="203"/>
+ <source>Factor for sound volume decrease</source>
+ <translation>Factor para la disminución del volumen del sonido</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="206"/>
+ <source>How fast should sound volume drop when passing beyond the minimum distance. The normal (1.0) is that sound volume halves each time the distance doubles. Increasing this value means sound volume drops faster, while decreasing it means it drops slower.</source>
+ <translation>Cuán rápido debería disminuir el volumen del sonido cuando pase más allá de la distancia mínima. Lo normal (1.0) es que el volumen del sonido disminuya la mitad cada vez que la distancia se doble. Aumentar este valor quiere decir que el volumen disminuye más rápidamente, mientras que reducirlo quiere decir que disminuye más lentamente.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="226"/>
+ <source>DirectSound</source>
+ <translation>DirectSound</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>%1 ms</source>
+ <translation type="obsolete">%1 ms</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="255"/>
+ <source>%1m</source>
+ <translation>%1m</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="263"/>
+ <source>%1</source>
+ <translation>%1</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="273"/>
+ <source>Players more than %1 meters away have %2% intensity</source>
+ <translation>Los jugadores alejados más de %1 metros tendrán %2% de intensidad</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="97"/>
+ <source>This sets the output device to use, which is where you have connected your speakers or your headset.</source>
+ <translation>Establece el dispositivo de salida a usar, que es donde ha conectado sus altavoces o sus auriculares.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="122"/>
+ <source>This sets the amount of data to prebuffer in the DirectSound buffer. Experiment with different values and set it to the lowest which doesn't cause rapid jitter in the sound.</source>
+ <translation>Establece la cantidad de datos que prealmacenar en el búfer directsound. Experimente con diferentes valores y fíjelo al más bajo que no cause fluctuaciones rápidas en el sonido.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="245"/>
+ <source>%1ms</source>
+ <translation>%1ms</translation>
+ </message>
+</context>
+<context>
+ <name>Database</name>
+ <message>
+ <location filename="Database.cpp" line="67"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="Database.cpp" line="67"/>
+ <source>Mumble failed to initialize a database in any
+of the possible locations.</source>
+ <translation>Mumble no logró inicializar una base de datos en ninguna
+de las ubicaciones posibles.</translation>
+ </message>
+</context>
+<context>
+ <name>DirectInputKeyWidget</name>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="72"/>
+ <source>Press Shortcut</source>
+ <translation>Presione la tecla de método abreviado</translation>
+ </message>
+</context>
+<context>
+ <name>GlobalShortcutWinConfig</name>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="169"/>
+ <source>Shortcuts</source>
+ <translation>Métodos abreviados</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="135"/>
+ <source>Function</source>
+ <translation>Función</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="137"/>
+ <source>Shortcut</source>
+ <translation>Método abreviado</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="150"/>
+ <source>Shortcut bound to %1.</source>
+ <translation>Método abreviado asignado a %1.</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="153"/>
+ <source><b>This is the global shortcut bound to %1</b><br />Click this field and then the desired key/button combo to rebind. Double-click to clear.</source>
+ <translation><b>Éste es el método abreviado global asociado a %1</b><br />Haga click en este campo y después presione la combinación de teclas/botones deseada para volver a asociarlo. Haga doble click para borrar.</translation>
+ </message>
+</context>
+<context>
+ <name>GlobalShortcutXConfig</name>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="156"/>
+ <source>Shortcuts</source>
+ <translation>Métodos abreviados</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="122"/>
+ <source>Function</source>
+ <translation>Función</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="124"/>
+ <source>Shortcut</source>
+ <translation>Método abreviado</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="137"/>
+ <source>Shortcut bound to %1.</source>
+ <translation>Método abreviado asignado a %1.</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="140"/>
+ <source><b>This is the global shortcut bound to %1</b><br />Click this field and then the desired key combo to rebind. Double-click to clear.</source>
+ <translation><b>Éste es el método abreviado global asociado a %1</b><br />Haga click en este campo y después presione la combinación de teclas deseada para volver a asociarlo. Haga doble click para borrar.</translation>
+ </message>
+</context>
+<context>
+ <name>Log</name>
+ <message>
+ <location filename="Log.cpp" line="219"/>
+ <source>[%2] %1</source>
+ <translation>[%2] %1</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="146"/>
+ <source>Debug</source>
+ <translation>Depuración</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="147"/>
+ <source>Critical</source>
+ <translation>Crítico</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="148"/>
+ <source>Warning</source>
+ <translation>Advertencia</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="149"/>
+ <source>Information</source>
+ <translation>Información</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="150"/>
+ <source>Server Connected</source>
+ <translation>Conectado al servidor</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="151"/>
+ <source>Server Disconnected</source>
+ <translation>Desconectado del servidor</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="152"/>
+ <source>Player Joined Server</source>
+ <translation>Jugador se unió al servidor</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="153"/>
+ <source>Player Left Server</source>
+ <translation>Jugador abandonó el servidor</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="154"/>
+ <source>Player kicked (you or by you)</source>
+ <translation>Jugador expulsado (usted o por usted)</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="155"/>
+ <source>Player kicked</source>
+ <translation>Jugador expulsado</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="156"/>
+ <source>You self-muted/deafened</source>
+ <translation>Se enmudeció/ensordeció a sí mismo</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="157"/>
+ <source>Other self-muted/deafened</source>
+ <translation>Otro se enmudeció/ensordeció a sí mismo</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="158"/>
+ <source>Player muted (you)</source>
+ <translation>Jugador le enmudeció (a usted)</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="159"/>
+ <source>Player muted (by you)</source>
+ <translation>Jugador enmudecido (por usted)</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="160"/>
+ <source>Player muted (other)</source>
+ <translation>Jugador enmudecido (otro)</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="161"/>
+ <source>Player Joined Channel</source>
+ <translation>Jugador se unió al canal</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="162"/>
+ <source>Player Left Channel</source>
+ <translation>Jugador abandonó el canal</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="164"/>
+ <source>Permission Denied</source>
+ <translation>Permiso denegado</translation>
+ </message>
+</context>
+<context>
+ <name>LogConfig</name>
+ <message>
+ <location filename="Log.cpp" line="113"/>
+ <source>Messages</source>
+ <translation>Mensajes</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="56"/>
+ <source>Console</source>
+ <translation>Consola</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="58"/>
+ <source>TTS</source>
+ <translation>TTS</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="72"/>
+ <source>Enable console for %1</source>
+ <translation>Habilitar consola para %1</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="78"/>
+ <source>Enable Text-To-Speech for %1</source>
+ <translation>Habilitar Texto-A-Voz (TTS) para %1</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="85"/>
+ <source>Text To Speech</source>
+ <translation>Texto A Voz (TTS)</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="88"/>
+ <source>Volume</source>
+ <translation>Volumen</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="96"/>
+ <source>Volume of Text-To-Speech Engine</source>
+ <translation>Volumen del motor Texto-A-Voz (TTS)</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="97"/>
+ <source><b>This is the volume used for the speech synthesis.</b></source>
+ <translation><b>Este es el volumen usado para la síntesis de voz.</b></translation>
+ </message>
+</context>
+<context>
+ <name>LookConfig</name>
+ <message>
+ <location filename="LookConfig.cpp" line="67"/>
+ <source>Language</source>
+ <translation>Idioma</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="51"/>
+ <source>Look and Feel</source>
+ <translation>Apariencia</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="82"/>
+ <source>System default</source>
+ <translation>Predeterminada del sistema</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="69"/>
+ <source>Language to use (requires restart)</source>
+ <translation>Idioma a usar (requiere reiniciar)</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source><b>This sets which language mumble should use.</b><br />You have to restart Mumble to use the new language.</source>
+ <translation type="obsolete"><b>Establece qué idioma debería usar mumble</b><br />Tendrá que reiniciar Mumble para usar el nuevo idioma.</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="89"/>
+ <source>Style</source>
+ <translation>Estilo</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="91"/>
+ <source>Basic widget style</source>
+ <translation>Estilo de control básico</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="92"/>
+ <source><b>This sets the basic look and feel to use.</b></source>
+ <translation><b>Establece la apariecia básica a usar.</b></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="97"/>
+ <source>Skin</source>
+ <translation>Máscara</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="101"/>
+ <source>Skin file to use</source>
+ <translation>Archivo de máscara a usar</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="102"/>
+ <source><b>This sets which skin Mumble should use.</b><br />The skin is a style file applied on top of the basic widget style.</source>
+ <translation><b>Establece qué carátula debería usar Mumble.</b><br />La carátula es un archivo de estilo aplicado sobre el estilo de control básico.</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="109"/>
+ <source>Use Horizontal Splitter</source>
+ <translation>Usar divisor horizontal</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="111"/>
+ <source>Use horizontal or vertical splitter for the main window</source>
+ <translation>Usar un divisor horizontal o vertical para la ventana principal</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="112"/>
+ <source><b>This sets whether the split in the main window is horizontal or vertical.</b></source>
+ <translation><b>Establece si la división en la ventana principal es horizontal o vertical.</b></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="127"/>
+ <source>User Interface</source>
+ <translation>Interfaz de usuario</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="99"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="171"/>
+ <source>Choose skin file</source>
+ <translation>Elija el archivo de carátula</translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="70"/>
+ <source><b>This sets which language Mumble should use.</b><br />You have to restart Mumble to use the new language.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="main.cpp" line="129"/>
+ <source>Welcome to Mumble.</source>
+ <translation>Bienvenido a Mumble.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="53"/>
+ <source>Root</source>
+ <translation>Root</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="73"/>
+ <source>&Connect</source>
+ <translation>&Conectar</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="74"/>
+ <source>Open the server connection dialog</source>
+ <translation>Abre el cuadro de diálogo de conexión con el servidor</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="75"/>
+ <source>Shows a dialog of registered servers, and also allows quick connect.</source>
+ <translation>Muestra un cuadro de diálogo de servidores registrados, y también permite conectarse rápidamente.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="77"/>
+ <source>&Disconnect</source>
+ <translation>&Desconectar</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="78"/>
+ <source>Disconnect from server</source>
+ <translation>Desconectarse del servidor</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="79"/>
+ <source>Disconnects you from the server.</source>
+ <translation>Le desconecta del servidor.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="82"/>
+ <source>&Ban lists</source>
+ <translation>&Prohibiciones</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="83"/>
+ <source>Edit ban lists on server</source>
+ <translation>Edita la lista de prohibiciones en el servidor</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="84"/>
+ <source>This lets you edit the server-side IP ban lists.</source>
+ <translation>Le permite editar la lista de IPs prohibidas en el lado del servidor.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="88"/>
+ <source>&Kick</source>
+ <translation>&Expulsar</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="90"/>
+ <source>Kick player (with reason)</source>
+ <translation>Expulsar jugador (con motivo)</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="91"/>
+ <source>Kick selected player off server. You'll be asked to specify a reason.</source>
+ <translation>Expulsa del servidor al jugador seleccionado. Se le pedirá que especifique un motivo.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="92"/>
+ <source>&Ban</source>
+ <translation>&Prohibir</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="94"/>
+ <source>Kick and ban player (with reason)</source>
+ <translation>Expulsar y prohibir jugador (con motivo)</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="95"/>
+ <source>Kick and ban selected player from server. You'll be asked to specify a reason.</source>
+ <translation>Expulsa y prohibe en el servidor al jugador seleccionado. Se le pedirá que especifique un motivo.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="96"/>
+ <source>&Mute</source>
+ <translation>En&mudecer</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="109"/>
+ <source>Mute player</source>
+ <translation>Enmudece al jugador</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="100"/>
+ <source>Mute or unmute player on server. Unmuting a deafened player will also undeafen them.</source>
+ <translation>Enmudece o da voz a un jugador en el servidor. Dar voz a un jugador ensordecido también le dará escucha.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="101"/>
+ <source>&Deafen</source>
+ <translation>En&sordecer</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="104"/>
+ <source>Deafen player</source>
+ <translation>Ensordece al jugador</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="105"/>
+ <source>Deafen or undeafen player on server. Deafening a player will also mute them.</source>
+ <translation>Ensordece o da escucha a un jugador en el servidor. Ensordecer a un jugador también lo enmudecerá.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="106"/>
+ <source>&Local Mute</source>
+ <translation>Enmudecer &localmente</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="110"/>
+ <source>Mute or unmute player locally.</source>
+ <translation>Enmudece o da voz localmente a un jugador.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="112"/>
+ <source>&Add</source>
+ <translation>&Agregar</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="114"/>
+ <source>Add new channel</source>
+ <translation>Agrega un nuevo canal</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="115"/>
+ <source>This adds a new sub-channel to the currently selected channel.</source>
+ <translation>Agrega un nuevo subcanal al canal actualmente seleccionado.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="116"/>
+ <source>&Remove</source>
+ <translation>&Eliminar</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="118"/>
+ <source>Remove channel</source>
+ <translation>Elimina un canal</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="119"/>
+ <source>This removes a channel and all sub-channels.</source>
+ <translation>Elimina un canal y todos sus subcanales.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="120"/>
+ <source>&Edit ACL</source>
+ <translation>&Editar LCA</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="122"/>
+ <source>Edit Groups and ACL for channel</source>
+ <translation>Edita los Grupos y la LCA para el canal</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="123"/>
+ <source>This opens the Group and ACL dialog for the channel, to control permissions.</source>
+ <translation>Abre el cuadro de diálogo Grupo y LCA para el canal, para controlar los permisos.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="124"/>
+ <source>&Link</source>
+ <translation>&Vincular</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="126"/>
+ <source>Link your channel to another channel</source>
+ <translation>Vincula su canal a otro canal</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="129"/>
+ <source>This links your current channel to the selected channel. If they have permission to speak in the other channel, players can now hear each other. This is a permanent link, and will last until manually unlinked or the server is restarted. Please see the shortcuts for push-to-link.</source>
+ <translation>Vincula su canal actual al canal seleccionado. Si tienen permiso para hablar en el otro canal, los jugadores pueden oirse entre sí. Éste es un vínculo permanente, y durará hasta que se desvincule manualmente o se reinicie el servidor. Por favor vea los métodos abreviados para presionar-para-vincular.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="167"/>
+ <source>&Unlink</source>
+ <translation>&Desvincular</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="132"/>
+ <source>Unlink your channel from another channel</source>
+ <translation>Desvincula su canal de otro canal</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="133"/>
+ <source>This unlinks your current channel from the selected channel.</source>
+ <translation>Desvincula su canal actual del canal seleccionado.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="134"/>
+ <source>Unlink &All</source>
+ <translation>Desvincular &Todo</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="136"/>
+ <source>Unlinks your channel from all linked channels.</source>
+ <translation>Desvincula su canal de todos los canales vinculados.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="137"/>
+ <source>This unlinks your current channel (not the selected one) from all linked channels.</source>
+ <translation>Desvincula su canal actual (no el seleccionado) de todos los canales vinculados.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="139"/>
+ <source>&Reset</source>
+ <translation>&Reiniciar</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="141"/>
+ <source>Reset audio preprocessor</source>
+ <translation>Reinicia el preprocesador de audio</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="145"/>
+ <source>&Mute Self</source>
+ <translation>En&mudecerse</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="149"/>
+ <source>Mute yourself</source>
+ <translation>Se enmudece a sí mismo</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="150"/>
+ <source>Mute or unmute yourself. When muted, you will not send any data to the server. Unmuting while deafened will also undeafen.</source>
+ <translation>Se enmudece o se da voz a sí mismo. Cuando enmudezca, no enviará ningún dato al servidor. Darse voz mientras está ensordecido también le dará escucha.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="151"/>
+ <source>&Deafen Self</source>
+ <translation>En&sordecerse</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="155"/>
+ <source>Deafen yourself</source>
+ <translation>Se ensordece a sí mismo</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="156"/>
+ <source>Deafen or undeafen yourself. When deafened, you will not hear anything. Deafening yourself will also mute.</source>
+ <translation>Se ensordece o se da escucha a sí mismo. Cuando ensordezca, no oirá nada. Darse escucha a sí mismo también le dará voz.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="157"/>
+ <source>&Text-To-Speech</source>
+ <translation>&Texto-A-Voz</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="161"/>
+ <source>Toggle Text-To-Speech</source>
+ <translation>Conmuta Texto-A-Voz</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="162"/>
+ <source>Enable or disable the text-to-speech engine. Only messages enabled for TTS in the Configuration dialog will actually be spoken.</source>
+ <translation>Activa o desactiva el motor de texto-a-voz (TTS). Realmente sólo se dirán los mensajes habilitados para TTS en el cuadro de diálogo Configuración.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="163"/>
+ <source>S&tatistics</source>
+ <translation>&Estadísticas</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="165"/>
+ <source>Display audio statistics</source>
+ <translation>Muestra estadísticas de audio</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="166"/>
+ <source>Pops up a small dialog with information about your current audio input.</source>
+ <translation>Muestra un pequeño cuadro de diálogo con información acerca de la entrada de audio.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="169"/>
+ <source>Forcibly unlink plugin</source>
+ <translation>Desvincular plugin forzosamente</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="171"/>
+ <source>This forces the current plugin to unlink, which is handy if it is reading completely wrong data.</source>
+ <translation>Fuerza al plugin actual a desvincularse, lo que es útil si está leyendo datos completamente erróneos.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="173"/>
+ <source>&Settings</source>
+ <translation>&Opciones</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="175"/>
+ <source>Configure Mumble</source>
+ <translation>Configura Mumble</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="176"/>
+ <source>Allows you to change most settings for Mumble.</source>
+ <translation>Le permite cambiar la mayoría de opciones de Mumble.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="178"/>
+ <source>&What's This?</source>
+ <translation>¿&Qué es esto?</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="180"/>
+ <source>Enter What's This? mode</source>
+ <translation>Entra en el modo ¿Qué es esto?</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="182"/>
+ <source>Click this to enter "What's This?" mode. Your cursor will turn into a question mark. Click on any button, menu choice or area to show a description of what it is.</source>
+ <translation>Haga click en esto para entrar en el modo "¿Qué es esto?". Su cursor se transformará en una interrogación. Haga click en cualquier botón, opción de menú o área para mostrar una descripción de lo que es.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="183"/>
+ <source>&About</source>
+ <translation>&Acerca de</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="185"/>
+ <source>Information about Mumble</source>
+ <translation>Información acerca de Mumble</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="186"/>
+ <source>Shows a small dialog with information and license for Mumble.</source>
+ <translation>Muestra un pequeño cuadro de diálogo con información y la licencia de Mumble.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="187"/>
+ <source>About &Speex</source>
+ <translation>Acerca de &Speex</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="189"/>
+ <source>Information about Speex</source>
+ <translation>Información acerca de Speex</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="190"/>
+ <source>Shows a small dialog with information about Speex.</source>
+ <translation>Muestra un pequeño cuadro de diálogo con información acerca de Speex.</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>&About QT</source>
+ <translation type="obsolete">&Acerca de Qt</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="193"/>
+ <source>Information about Qt</source>
+ <translation>Información acerca de Qt</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="194"/>
+ <source>Shows a small dialog with information about Qt.</source>
+ <translation>Muestra un pequeño cuadro de diálogo con información acerca de Qt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="195"/>
+ <source>Check for &Updates</source>
+ <translation>Comprobar &actualizaciones</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="197"/>
+ <source>Check for new version of Mumble</source>
+ <translation>Comprueba si existe una nueva versión de Mumble</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="199"/>
+ <source>Connects to the Mumble webpage to check if a new version is available, and notifies you with an appropriate download URL if this is the case.</source>
+ <translation>Se conecta a la página web de Mumble para comprobar si está disponible una nueva versión, y le notifica con una URL de descarga apropiada si éste es el caso.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="203"/>
+ <source>Mumble -- %1</source>
+ <translation>Mumble -- %1</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="220"/>
+ <source>Log of messages</source>
+ <translation>Registro de mensajes</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="224"/>
+ <source>&Server</source>
+ <translation>&Servidor</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="225"/>
+ <source>&Player</source>
+ <translation>&Jugador</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="226"/>
+ <source>&Channel</source>
+ <translation>&Canal</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="227"/>
+ <source>&Audio</source>
+ <translation>&Audio</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="228"/>
+ <source>C&onfigure</source>
+ <translation>C&onfigurar</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="229"/>
+ <source>&Help</source>
+ <translation>&Ayuda</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="503"/>
+ <source>Kicking player %1</source>
+ <translation>Expulsando al jugador %1</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="519"/>
+ <source>Enter reason</source>
+ <translation>Introduzca el motivo</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="519"/>
+ <source>Banning player %1</source>
+ <translation>Prohibiendo al jugador %1</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="587"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="571"/>
+ <source>Channel Name</source>
+ <translation>Nombre del canal</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>Are you sure you want to delete %1 and all it's subchannels?</source>
+ <translation type="obsolete">¿Está seguro de que desa borrar %1 y todos sus subcanales?</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="664"/>
+ <source>Unmuted and undeafened.</source>
+ <translation>Con voz y escucha.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="666"/>
+ <source>Unmuted.</source>
+ <translation>Con escucha.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="668"/>
+ <source>Muted.</source>
+ <translation>Mudo.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="683"/>
+ <source>Muted and deafened.</source>
+ <translation>Mudo y sordo.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="685"/>
+ <source>Deafened.</source>
+ <translation>Sordo.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="687"/>
+ <source>Undeafened.</source>
+ <translation>Con escucha.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="737"/>
+ <source>About Qt</source>
+ <translation>Acerca de Qt</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="813"/>
+ <source>Joining %1.</source>
+ <translation>Uniéndose a %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="839"/>
+ <source>Connected to server.</source>
+ <translation>Conectado al servidor.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="872"/>
+ <source>Server connection failed: %1.</source>
+ <translation>Falló la conexión con el servidor: %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="878"/>
+ <source>Disconnected from server.</source>
+ <translation>Desconectado del servidor.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="884"/>
+ <source>Reconnecting.</source>
+ <translation>Volviendo a conectar.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="917"/>
+ <source>Joined server: %1.</source>
+ <translation>Se ha unido al servidor: %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="932"/>
+ <source>Left server: %1.</source>
+ <translation>Abandonó el servidor: %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="967"/>
+ <source>%1 is now muted and deafened.</source>
+ <translation>%1 está ahora mudo y sordo.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="969"/>
+ <source>%1 is now muted.</source>
+ <translation>%1 está ahora mudo.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="971"/>
+ <source>%1 is now unmuted.</source>
+ <translation>%1 ahora tiene voz.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1002"/>
+ <source>server</source>
+ <translation>servidor</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="987"/>
+ <source>You were muted by %1.</source>
+ <translation>Fue enmudecido por %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="987"/>
+ <source>You were unmuted by %1.</source>
+ <translation>%1 le dio voz.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="989"/>
+ <source>%1 muted by %2.</source>
+ <translation>%1 enmudecido por %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="989"/>
+ <source>%1 unmuted by %2.</source>
+ <translation>%2 dio voz a %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1005"/>
+ <source>You were deafened by %1.</source>
+ <translation>Fue ensordecido por %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1005"/>
+ <source>You were undeafened by %1.</source>
+ <translation>%1 le dio escucha.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1007"/>
+ <source>%1 undeafened by %2.</source>
+ <translation>%2 dio voz a %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1014"/>
+ <source>You were kicked from the server by %1: %2.</source>
+ <translation>Fue expulsado del servidor por %1: %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1018"/>
+ <source>%3 was kicked from the server by %1: %2.</source>
+ <translation>%3 fue expulsado del servidor por %1: %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1026"/>
+ <source>You were kicked and banned from the server by %1: %2.</source>
+ <translation>Fue expulsado y prohibido del servidor por %1: %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1030"/>
+ <source>%3 was kicked and banned from the server by %1: %2.</source>
+ <translation>%3 fue expulsado y prohibido del servidor por %1: %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1049"/>
+ <source>%1 left channel.</source>
+ <translation>%1 abandonó el canal.</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>%1 moved out by %2</source>
+ <translation type="obsolete">%2 mudó a %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1058"/>
+ <source>%1 entered channel.</source>
+ <translation>%1 ingresó en el canal.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1060"/>
+ <source>%1 moved in by %2.</source>
+ <translation>%2 trajo a %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1112"/>
+ <source>Server connection rejected: %1.</source>
+ <translation>Conexión con el servidor rechazada: %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1117"/>
+ <source>Denied: %1.</source>
+ <translation>Denegado: %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="144"/>
+ <source>This will reset the audio preprocessor, including noise cancellation, automatic gain and voice activity detection. If something suddenly worsens the audio environment (like dropping the microphone) and it was temporary, use this to avoid having to wait for the preprocessor to readjust.</source>
+ <translation>Reinicia el preprocesador de audio, incluyendo cancelación de ruido, ganancia automática y detección de actividad vocal. Si algo de pronto empeora el entorno de audio (como dejar caer el micrófono) y fue temporal, use esto para evitar tener que esperar a que se reajuste el preprocesador.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="222"/>
+ <source>This shows all recent activity. Connecting to servers, errors and information messages all show up here.<br />To configure exactly which messages show up here, use the <b>Settings</b> command from the menu.</source>
+ <translation>Muestra toda la actividad reciente. Aquí se muestran conexiones a servidores, errores y mensajes informativos.<br />Para configurar exactamente qué mensajes se muestran aquí, use la orden <b>Opciones</b> del menú.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1007"/>
+ <source>%1 deafened by %2.</source>
+ <translation>%1 enmudecido por %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="284"/>
+ <source>Push-to-Talk</source>
+ <comment>Global Shortcut</comment>
+ <translation>Presionar-para-Hablar</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="287"/>
+ <source>Reset Audio Processor</source>
+ <comment>Global Shortcut</comment>
+ <translation>Reiniciar el preprocesador de audio</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="291"/>
+ <source>Toggle Mute Self</source>
+ <comment>Global Shortcut</comment>
+ <translation>Conmutar enmudecerse</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="295"/>
+ <source>Toggle Deafen Self</source>
+ <comment>Global Shortcut</comment>
+ <translation>Conmutar ensordecerse</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="299"/>
+ <source>Unlink Plugin</source>
+ <comment>Global Shortcut</comment>
+ <translation>Desvincular plugin</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="303"/>
+ <source>Force Center Position</source>
+ <comment>Global Shortcut</comment>
+ <translation>Forzar posición central</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="308"/>
+ <source>Chan Parent</source>
+ <comment>Global Shortcut</comment>
+ <translation>Canal padre</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="313"/>
+ <source>Chan Sub#%1</source>
+ <comment>Global Shortcut</comment>
+ <translation>Subcanal nº %1</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="318"/>
+ <source>Chan All Subs</source>
+ <comment>Global Shortcut</comment>
+ <translation>Todos los subcanales</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="322"/>
+ <source>Push-to-Mute</source>
+ <comment>Global Shortcut</comment>
+ <translation>Presionar-para-Enmudecer</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="325"/>
+ <source>Join Channel</source>
+ <comment>Global Shortcut</comment>
+ <translation>Unirse al canal</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="329"/>
+ <source>Toggle Overlay</source>
+ <comment>Global Shortcut</comment>
+ <translation>Conmutar sobreimpresión</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="334"/>
+ <source>Alt Push-to-Talk</source>
+ <comment>Global Shortcut</comment>
+ <translation>Presionar-para-Hablar Alt</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="191"/>
+ <source>&About Qt</source>
+ <translation>&Acerca de Qt</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1051"/>
+ <source>%1 moved out by %2.</source>
+ <translation>%2 mudó a %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="587"/>
+ <source>Are you sure you want to delete %1 and all its sub-channels?</source>
+ <translation>¿Está seguro de que desa borrar %1 y todos sus subcanales?</translation>
+ </message>
+</context>
+<context>
+ <name>Overlay</name>
+ <message>
+ <location filename="Overlay_win.cpp" line="363"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>Failed to load overlay library. This means either that:
+- the library (mumble_ol.dll) wasn't found in the directory you ran mumble from
+- you're on an OS earlier than WinXP SP2
+- you do not have the August 2006 updated version of DX9.0c</source>
+ <translation type="obsolete">No se pudo cargar la biblioteca de sobreimpresión. Esto quiere decir que:
+- la biblioteca (mumble_ol.dll) no fue encontrada en el directorio desde el que ejecutó Mumble
+- está usando un SO anterior a WinXP SP2
+- no tiene la versión actualizada en agosto de 2006 de DX9.0c</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="366"/>
+ <source>Failed to load overlay library. This means either that:
+- the library (mumble_ol.dll) wasn't found in the directory you ran Mumble from
+- you're on an OS earlier than WinXP SP2
+- you do not have the August 2006 updated version of DX9.0c</source>
+ <translation>No se pudo cargar la biblioteca de sobreimpresión. Esto quiere decir que:
+- la biblioteca (mumble_ol.dll) no fue encontrada en el directorio desde el que ejecutó Mumble
+- está usando un SO anterior a WinXP SP2
+- no tiene la versión actualizada en agosto de 2006 de DX9.0c</translation>
+ </message>
+</context>
+<context>
+ <name>OverlayConfig</name>
+ <message>
+ <location filename="Overlay_win.cpp" line="43"/>
+ <source>Options</source>
+ <translation>Opciones</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="44"/>
+ <source>Position</source>
+ <translation>Posición</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="45"/>
+ <source>Font</source>
+ <translation>Fuente</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="52"/>
+ <source>Enable Overlay</source>
+ <translation>Habilitar Sobreimpresión</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="54"/>
+ <source>Enable overlay.</source>
+ <translation>Habilita la sobreimpresión.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="60"/>
+ <source>This sets whether the overlay is enabled or not. This setting is only checked with D3D9 applications are started, so make sure Mumble is running and this option is on before you start the application.<br />Please note that if you start the application after starting Mumble, or if you disable the overlay while running, there is no safe way to restart the overlay without also restarting the application.</source>
+ <translation>Establece si la sobreimpresión está habilitada o no. Esta opción se comprueba sólo cuando se inician aplicaciones D3D9, así que asegúrese de que Mumble está ejecutándose y esta opción está activada antes de que inicie la aplicación.<br />Por favor fíjese en que si inicia la aplicación después de iniciar Mumble, o si desactiva la sobreimpresión mientras se está ejecutando, no existe ninguna forma segura de reiniciar la sobreimpresión sin reiniciar también la aplicación.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="65"/>
+ <source>No one</source>
+ <translation>Nadie</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="66"/>
+ <source>Only talking</source>
+ <translation>Sólo hablando</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="67"/>
+ <source>Everyone</source>
+ <translation>Todos</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="69"/>
+ <source>Show</source>
+ <translation>Mostrar</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="71"/>
+ <source>Who to show on the overlay</source>
+ <translation>Quién mostrar en la sobreimpresión</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source><b>This sets who to show in the ingame overlay.</b><br />If many people are connected to the same channel, the overlay list might be very long. Use this to shorten it.<i>No one</i> - Don't show anyone (but leave overlay running).<br /><i>Only talking</i> - Only show talking people.<br /><i>Everyone</i> - Show everyone.</source>
+ <translation type="obsolete"><b>Establece a quién mostrar en la sobreimpresión durante el juego.</b><br />Si muchas personas están conectadas al mismo canal, la lista sobreimpresionada podría ser muy larga. Use ésto para acortarla.<br /><i>Nadie</i> - No mostrar a nadie (pero dejar funcionando la sobreimpresión).<br /><i>Sólo hablando</i> - Sólo mostrar las personas que hablan.<br /><i>Todos</i> - Mostrar todos.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="81"/>
+ <source>Always Show Self</source>
+ <translation>Siempre mostrarse a sí mismo</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="83"/>
+ <source>Always show yourself on overlay.</source>
+ <translation>Siempre le muestra en la sobreimpresión.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="95"/>
+ <source>Grow Left</source>
+ <translation>Crecer hacia la izquierda</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="97"/>
+ <source>Let overlay grow to the left</source>
+ <translation>Hacer crecer la sobreimpresión hacia la izquierda</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="100"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow to the left if needed.</source>
+ <translation>La sobreimpresión trata de permanecer lo más pequeña posible y en la posición que ha seleccionado. Esto permite que la sobreimpresión crezca hacia la izquierda si es necesario.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="102"/>
+ <source>Grow Right</source>
+ <translation>Crecer hacia la derecha</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="104"/>
+ <source>Let overlay grow to the Right</source>
+ <translation>Hacer crecer la sobreimpresión hacia la derecha</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="107"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow to the Right if needed.</source>
+ <translation>La sobreimpresión trata de permanecer lo más pequeña posible y en la posición que ha seleccionado. Esto permite que la sobreimpresión crezca hacia la derecha si es necesario.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="109"/>
+ <source>Grow Up</source>
+ <translation>Crecer hacia arriba</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="111"/>
+ <source>Let overlay grow upwards</source>
+ <translation>Hacer crecer la sobreimpresión hacia arriba</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="114"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow to the top if needed.</source>
+ <translation>La sobreimpresión trata de permanecer lo más pequeña posible y en la posición que ha seleccionado. Esto permite que la sobreimpresión crezca hacia la arriba si es necesario.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="116"/>
+ <source>Grow Down</source>
+ <translation>Crecer hacia abajo</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="118"/>
+ <source>Let overlay grow downwards</source>
+ <translation>Hacer crecer la sobreimpresión hacia abajo</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="121"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow towards the bottom if needed.</source>
+ <translation>La sobreimpresión trata de permanecer lo más pequeña posible y en la posición que ha seleccionado. Esto permite que la sobreimpresión crezca hacia la abajo si es necesario.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="126"/>
+ <source>X-Position of Overlay</source>
+ <translation>Posición-X de la sobreimpresión</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="127"/>
+ <source>This sets the relative X position of the overlay.</source>
+ <translation>Establece la posición X relativa de la sobreimpresión.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="135"/>
+ <source>Y-Position of Overlay</source>
+ <translation>Posición-Y de la sobreimpresión</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="136"/>
+ <source>This sets the relative Y position of the overlay.</source>
+ <translation>Establece la posición Y relativa de la sobreimpresión.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="161"/>
+ <source>Current Font</source>
+ <translation>Fuente actual</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="165"/>
+ <source>Set Font</source>
+ <translation>Establece la fuente</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="174"/>
+ <source>Maximum width of names.</source>
+ <translation>Anchura máxima de los nombres.</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>This sets the maximum width of names shows, relative to the height. If you set this to 100%, no line of text will be wider than it is high. At 500%, no line will be more than 5 times wider than it's height, and so on.</source>
+ <translation type="obsolete">Establece la anchura máxima de los nombres mostrados, relativa a la altura. Si fija ésto a 100%, ninguna línea de texto será más ancha que su altura. A 500%, ninguna línea será 5 veces más ancha que su altura, y así sucesivamente.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="187"/>
+ <source>Maximum width</source>
+ <translation>Anchura máxima</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="235"/>
+ <source>Change</source>
+ <translation>Cambiar</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="197"/>
+ <source>Color for players</source>
+ <translation>Color de los jugadores</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="207"/>
+ <source>Color for talking players</source>
+ <translation>Color de los jugadores que hablan</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="217"/>
+ <source>Color for alt-talking players</source>
+ <translation>Color de los jugadores que hablan-alt</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="227"/>
+ <source>Color for Channels</source>
+ <translation>Color de los canales</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="237"/>
+ <source>Color for active Channels</source>
+ <translation>Color de los canales activos</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="323"/>
+ <source>Overlay</source>
+ <translation>Sobreimpresión</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="87"/>
+ <source>This sets whether to always show yourself or not. This setting is useful if you aren't showing everyone in the overlay, as then you would only see your own status if you were talking, which wouldn't let you see that you were deafened or muted.</source>
+ <translation>Establece si mostrarse siempre a sí mismo o no. Esta opción es útil si no está mostrando a todos en la sobreimpresión, ya que entonces sólo vería su propio estado si usted está hablando, lo que no le permitiría ver si fue ensordecido o enmudecido.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="77"/>
+ <source><b>This sets who to show in the in-game overlay.</b><br />If many people are connected to the same channel, the overlay list might be very long. Use this to shorten it.<br /><i>No one</i> - Don't show anyone (but leave overlay running).<br /><i>Only talking</i> - Only show talking people.<br /><i>Everyone</i> - Show everyone.</source>
+ <translation><b>Establece a quién mostrar en la sobreimpresión durante el juego.</b><br />Si muchas personas están conectadas al mismo canal, la lista sobreimpresionada podría ser muy larga. Use esto para acortarla.<br /><i>Nadie</i> - No mostrar a nadie (pero dejar funcionando la sobreimpresión).<br /><i>Sólo hablando</i> - Sólo mostrar las personas que hablan.<br /><i>Todos</i> - Mostrar todos.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="177"/>
+ <source>This sets the maximum width of names shown, relative to the height. If you set this to 100%, no line of text will be wider than it is high. At 500%, no line will be more than 5 times wider than its height, and so on.</source>
+ <translation>Establece la anchura máxima de los nombres mostrados, relativa a la altura. Si fija esto a 100%, ninguna línea de texto será más ancha que su altura. A 500%, ninguna línea será 5 veces más ancha que su altura, y así sucesivamente.</translation>
+ </message>
+</context>
+<context>
+ <name>PlayerModel</name>
+ <message>
+ <location filename="PlayerModel.cpp" line="368"/>
+ <source>Name of player</source>
+ <translation>Nombre del jugador</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="368"/>
+ <source>Name of channel</source>
+ <translation>Nombre del canal</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="370"/>
+ <source>Player flags</source>
+ <translation>Emblemas del jugador</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="381"/>
+ <source>This is a player connected to the server. The icon to the left of the player indicates whether or not they are talking:<br /><img src=":/icons/talking_on.png" /> Talking<br /><img src=":/icons/talking_off.png" /> Not talking</source>
+ <translation>Éste es un jugador conectado al servidor. El icono a la izquierda del jugador indica si está hablando o no:<br /><img src=":/icons/talking_on.png" /> Hablando<br /><img src=":/icons/talking_off.png" /> Sin hablar</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="383"/>
+ <source>This is a channel on the server. Only players in the same channel can hear each other.</source>
+ <translation>Esto es un canal del servidor. Sólo los jugadores en el mismo canal pueden oirse entre sí.</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="393"/>
+ <source>This shows the flags the player has on the server, if any:<br /><img src=":/icons/authenticated.png" />Authenticated user<br /><img src=":/icons/muted_self.png" />Muted (by self)<br /><img src=":/icons/muted_server.png" />Muted (by admin)<br /><img src=":/icons/deafened_self.png" />Deafened (by self)<br /><img src=":/icons/deafened_server.png" />Deafened (by admin)<br />A player muted by himself is probably just away, talking on the phone or something like that.<br />A player muted by an admin is probably also just away, and the noise the player is making was annoying enough that an admin muted him.</source>
+ <translation>Muestra los emblemas que el jugador tiene en el servidor, si los hubiera:<br /><img src=":/icons/authenticated.png" />Usuario autenticado<br /><img src=":/icons/muted_self.png" />Mudo (por sí mismo)<br /><img src=":/icons/muted_server.png" />Mudo (por un administrador)<br /><img src=":/icons/deafened_self.png" />Sordo (por sí mismo)<br /><img src=":/icons/deafened_server.png" />Sordo (por un administrador)<br />Un jugador enmudecido por sí mismo probablemente sólo está ausente, hablando por teléfono o algo similar.<br />Un jugador enmudecido por un administrador probablemente está también sólo ausente, y el ruido que estaba haciendo el jugador era lo bastante molesto para que un adminstrador lo haya enmudecido.</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="410"/>
+ <source>Name</source>
+ <translation>Nombre</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="412"/>
+ <source>Flags</source>
+ <translation>Emblemas</translation>
+ </message>
+</context>
+<context>
+ <name>PluginConfig</name>
+ <message>
+ <location filename="Plugins.cpp" line="59"/>
+ <source>Options</source>
+ <translation>Opciones</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="114"/>
+ <source>Plugins</source>
+ <translation>Plugins</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="68"/>
+ <source>Enable plugins and transmit positional information</source>
+ <translation>Habilita los plugins y transmite información posicional</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="72"/>
+ <source>This enables plugins for supported games to fetch your in-game position and transmit that with each voice packet. This enables other players to hear your voice in-game from the direction your character is in relation to their own.</source>
+ <translation>Habilita que los plugins para los juegos soportados extraigan su posición en el juego y transmitan eso con cada paquete de voz. Esto permite que otros jugadores oigan su voz en el juego desde la dirección en la que su personaje está en relación al suyo.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="81"/>
+ <source>Reloads all plugins</source>
+ <translation>Recarga todos los plugins</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="83"/>
+ <source>This rescans and reloads plugins. Use this if you just added or changed a plugin to the plugins directory.</source>
+ <translation>Vuelve a buscar y a recargar los plugins. Use esto si acaba de agregar o cambiar un plugin en el directorio de plugins.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="86"/>
+ <source>Information about plugin</source>
+ <translation>Información acerca del plugin</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="87"/>
+ <source>This shows a small information message about the plugin.</source>
+ <translation>Muestra un pequeño mensaje de información acerca del plugin.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="90"/>
+ <source>Show configuration page of plugin</source>
+ <translation>Muestra la página de configuración del plugin</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="91"/>
+ <source>This shows the configuration page of the plugin, if any.</source>
+ <translation>Muestra la página de configuración del plugin, si la hay.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="146"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="134"/>
+ <source>Plugin has no configure function.</source>
+ <translation>El plugin no tiene función de configuración.</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>Plugin has no about function</source>
+ <translation type="obsolete">El plugin no tiene información acerca de</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="66"/>
+ <source>Link to Game and Transmit Position</source>
+ <translation>Vincularse al juego y transmitir la posición</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="79"/>
+ <source>&Reload plugins</source>
+ <translation>&Recargar plugins</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="84"/>
+ <source>&About</source>
+ <translation>&Acerca de</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="88"/>
+ <source>&Configure</source>
+ <translation>&Configurar</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="146"/>
+ <source>Plugin has no about function.</source>
+ <translation>El plugin no tiene información acerca de.</translation>
+ </message>
+</context>
+<context>
+ <name>Plugins</name>
+ <message>
+ <location filename="Plugins.cpp" line="243"/>
+ <source>Plugin %1 lost link.</source>
+ <translation>El plugin %1 perdió el vínculo.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="244"/>
+ <source>%1 lost link.</source>
+ <translation>%1 perdió el vínculo.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="285"/>
+ <source>Plugin %1 linked.</source>
+ <translation>Plugin %1 vínculado.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="286"/>
+ <source>%1 linked.</source>
+ <translation>%1 vinculado.</translation>
+ </message>
+</context>
+<context>
+ <name>VersionCheck</name>
+ <message>
+ <location filename="VersionCheck.cpp" line="75"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="" line="7143521"/>
+ <source>Failed to retrieve version information from server.</source>
+ <translation type="obsolete">No se pudo recuperar del servidor la información de la versión.</translation>
+ </message>
+ <message>
+ <location filename="VersionCheck.cpp" line="75"/>
+ <source>Mumble failed to retrieve version information from the SourceForge server.</source>
+ <translation>Mumble no pudo recuperar la información de la versión del servidor SourceForge.</translation>
+ </message>
+</context>
+<context>
+ <name>XInputKeyWidget</name>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="63"/>
+ <source>Press Shortcut</source>
+ <translation>Presione la tecla de método abreviado</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/mumble/mumble_pch.h b/src/mumble/mumble_pch.h new file mode 100644 index 000000000..32a76ab4d --- /dev/null +++ b/src/mumble/mumble_pch.h @@ -0,0 +1,32 @@ +#ifdef WIN32 +#define _WIN32_IE 0x0600 +#include <windows.h> +#include <shellapi.h> +#else +#define CopyMemory(dst,ptr,len) memcpy(dst,ptr,len) +#define ZeroMemory(ptr,len) memset(ptr, 0, len) +#define __cdecl +#endif + +#define QT_NO_DEBUG_STREAM +#define QT_ASCII_CAST_WARNINGS +#define QT_NO_CAST_TO_ASCII +#include <QtCore> +#include <QtGui> +#include <QtNetwork> +#include <QtOpenGL> +#include <QtSql> +#include <speex/speex.h> + +#include <math.h> +#include <speex/speex.h> +#include <speex/speex_jitter.h> +#include <speex/speex_preprocess.h> +#include <speex/speex_echo.h> + +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> + +#ifndef WIN32 +typedef WId HWND; +#endif diff --git a/src/mumble/mumble_ru.ts b/src/mumble/mumble_ru.ts new file mode 100644 index 000000000..d329f7d05 --- /dev/null +++ b/src/mumble/mumble_ru.ts @@ -0,0 +1,2878 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1">
+<context>
+ <name>ACLEditor</name>
+ <message>
+ <location filename="ACLEditor.cpp" line="45"/>
+ <source>Mumble - Edit ACL for %1</source>
+ <translation>Mumble - Изменить ACL (список контроля доступа) для %1</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="51"/>
+ <source>Active ACLs</source>
+ <translation>Активные ACL(списки контроля доступа)</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="52"/>
+ <source>Context</source>
+ <translation>Контекст</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="53"/>
+ <source>User/Group</source>
+ <translation>Пользователь/Группа</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="54"/>
+ <source>Permissions</source>
+ <translation>Разрешения</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="102"/>
+ <source>Group</source>
+ <translation>Группа</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="57"/>
+ <source>Members</source>
+ <translation>Члены группы</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="61"/>
+ <source>&Add</source>
+ <translation>&Добавить</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="63"/>
+ <source>&Remove</source>
+ <translation>&Удалить</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="65"/>
+ <source>&Up</source>
+ <translation>&Вверх</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="67"/>
+ <source>&Down</source>
+ <translation>Вн&из</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="69"/>
+ <source>Inherit ACLs</source>
+ <translation>Наследовать ACL(список контроля доступа)</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="88"/>
+ <source>Applies to this channel</source>
+ <translation>Примененные к этому каналу</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="90"/>
+ <source>Applies to sub-channels</source>
+ <translation>Примененные к подканалам</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="109"/>
+ <source>User ID</source>
+ <translation>Идентификатор пользователя</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="119"/>
+ <source>Deny</source>
+ <translation>Отказать</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="121"/>
+ <source>Allow</source>
+ <translation>Разрешить</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="208"/>
+ <source>Remove</source>
+ <translation>Убрать</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="189"/>
+ <source>Inherit</source>
+ <translation>Наследовать</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="166"/>
+ <source>Inheritable</source>
+ <translation>Наследуемый</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="169"/>
+ <source>Inherited</source>
+ <translation>Унаследованный</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="206"/>
+ <source>Add</source>
+ <translation>Добавить</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="211"/>
+ <source>Add to Remove</source>
+ <translation>Добавить к удаленным</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="232"/>
+ <source>&Groups</source>
+ <translation>&Группы</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="233"/>
+ <source>&ACL</source>
+ <translation>&Списки контроля доступа (ACL)</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="235"/>
+ <source>&OK</source>
+ <translation>&Ок</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="237"/>
+ <source>Accept changes</source>
+ <translation>Принять изменения</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="240"/>
+ <source>This button will accept current groups/ACLs and send them to the server. Note that if you mistakenly remove write permission from yourself, the server will add it.</source>
+ <translation>Эта кнопка примет текущие группы/списки ACL и пошлет их на сервер.Заметьте, что если вы по ошибке сняли с себя разрешения на запись, сервер добавит его.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="241"/>
+ <source>&Cancel</source>
+ <translation>Отм&енить</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="243"/>
+ <source>Reject changes</source>
+ <translation>Отклонить изменения</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="245"/>
+ <source>This button will cancel all changes and closes the dialog without updating the ACLs or groups on the server.</source>
+ <translation>Эта кнопка отменит все изменения и закроет диалоговое окно без обновления списков ACL или групп на сервере.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="321"/>
+ <source>List of entries</source>
+ <translation>Список вхождений</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="323"/>
+ <source>This shows all the entries active on this channel. Entries inherited from parent channels will be show in italics.</source>
+ <translation>Это покажет все активные вхождения на этом канале. Вхождения, наследованные от родительских каналов будут выделены курсивом.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="324"/>
+ <source>Inherit ACL of parent?</source>
+ <translation>Унаследовать списки ACL от предков?</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="326"/>
+ <source>This sets whether or not the ACL up the chain of parent channels are applied to this object. Only those entries that are marked in the parent as "Apply to sub-channels" will be inherited.</source>
+ <translation>Это унаследует либо нет цепочку списков ACL от каналов-предков для этого объекта. Унаследованы будут только вхождения, помеченные у предков как "Применить к подканалам".</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="327"/>
+ <source>Add new entry</source>
+ <translation>Добавить новый</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="328"/>
+ <source>This adds a new entry, initially set with no permissions and applying to all.</source>
+ <translation>Добавляет новое вхождение, по умолчанию без прав доступа иприменяемое ко всем.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="329"/>
+ <source>Remove entry</source>
+ <translation>Удалить вхождение</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="330"/>
+ <source>This removes the currently selected entry.</source>
+ <translation>Удалит выделенное вхождение.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="331"/>
+ <source>Move entry up</source>
+ <translation>Переместить выше</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="334"/>
+ <source>This moves the entry up in the list. As entries are evaluated in order, this may change the effective permissions of users. You cannot move an entry above an inherited entry, if you really need that you'll have to duplicate the inherited entry.</source>
+ <translation>Перемещает вхождение выше по списку. Так как вхождения упорядочены, это может изменить эффективные привелегии пользователей. Вы не сможете переместить вхождение выше унаследованного, если вы действительно хотите его переместить, вам придется продублировать унаследованое вхождение.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="335"/>
+ <source>Move entry down</source>
+ <translation>Переместить ниже</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="337"/>
+ <source>This moves the entry down in the list. As entries are evaluated in order, this may change the effective permissions of users.</source>
+ <translation>Перемещает вхождение ниже по списку. Так как вхождения упорядочены, это может изменить эффективные привелегии пользователей.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="338"/>
+ <source>Entry should apply to this channel</source>
+ <translation>Применять к каналу</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="339"/>
+ <source>This makes the entry apply to this channel.</source>
+ <translation>Применяет вхождение к этому каналу.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="340"/>
+ <source>Entry should apply to sub-channels.</source>
+ <translation>Применять к подканалам.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="341"/>
+ <source>This makes the entry apply to sub-channels of this channel.</source>
+ <translation>Применяет вхождение ко всем подканалам этого канала.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="342"/>
+ <source>Group this entry applies to</source>
+ <translation>Применить к группе</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="357"/>
+ <source>This controls which group of users this entry applies to.<br />Note that the group is evaluated in the context of the channel the entry is used in. For example, the default ACL on the Root channel gives <i>Write</i> permission to the <i>admin</i> group. This entry, if inherited by a channel, will give a user write privileges if he belongs to the <i>admin</i> group in that channel, even if he doesn't belong to the <i>admin</i> group in the channel where the ACL originated.<br />If a group name starts with a !, its membership is negated, and if it starts with a ~, it is evaluated in the channel the ACL was defined in, rather than the channel the ACL is active in. Order is important; <i>!~in</i> is valid, but <i>~!in</i> is not.<br />A few special predefined groups are:<br /><b>all</b> - Everyone will match.<br /><b>auth</b> - All authenticated users will match.<br /><b>sub,a,b,c</b> - User currently in a sub-channel minimum <i>a</i> common parents, and between <i>b</i> and <i>c</i> channels down the chain. See the website for more extensive documentation on this one.<br /><b>in</b> - Users currently in the channel will match (convenience for <i>sub,0,0,0</i>).<br /><b>out</b> - Users outside the channel will match (convenience for <i>!sub,0,0,0</i>).<br />Note that an entry applies to either a user or a group, not both.</source>
+ <translation>Управляет, к какой группе пользователей применять вхождение.<br />Заметьте, что группа оценивается как контекст канала, в котором используется вхождение. Например, по умолчанию список ACL канала Root дает права на <i>Запись</i> группе <i>admin</i>. Если это вхождение унаследуется каналом, даст пользователю права на запись если он принадлежит к группе <i>admin</i> на этом канале, даже если он не принадлежит к группе <i>admin</i> на канале, от которого унаследовали список ACL.<br />Если имя группы начинается с a !, его членство отрицается, а если начинается с a ~, оно оценивается для канала, которому ACL был определен, чем канал, в котором есть активный ACL. Порядок важен; <i>!~in</i> принимается, но <i>~!in</i> нет.<br />Несколько особых предопределенных групп:<br /><b>all</b> - Соответствует всем.<br /><b>auth</b> - Соответствует всем авторизованным пользователям.<br /><b>sub,a,b,c</b> - Пользователи на подканале с минимум <i>a</i> общими предками, а также между <i>b</i> и <i>c</i> каналами в цепочке. Для более подробной информации посетите вебсайт.<br /><b>in</b> - Подойдут пользователи на текущем канале (удобство для <i>sub,0,0,0</i>).<br /><b>out</b> - Соответствует всем пользователям вне канала (удобство для <i>!sub,0,0,0</i>).<br />Заметьте, что вхождение применяется только к пользователю или к группе, а не к обоим.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="358"/>
+ <source>User this entry applies to</source>
+ <translation>Применять для пользователя</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="360"/>
+ <source>This controls which user this entry applies to. Just type in the user name and hit enter to query the server for a match.</source>
+ <translation>Управляет, к какому пользователю применять вхождение. Просто введите имя пользователя и нажмите Enter, чтобы послать запрос на сервер.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="367"/>
+ <source>Allow %1</source>
+ <translation>Позволить %1</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="368"/>
+ <source>Deny %1</source>
+ <translation>Запретить %1</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="369"/>
+ <source>This grants the %1 privilege. If a privilege is both allowed and denied, it is denied.<br />%2</source>
+ <translation>Это дает привелегию на %1. Если привелегия и позволяет, и запрещает, она запрещает.<br />%2</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="370"/>
+ <source>This revokes the %1 privilege. If a privilege is both allowed and denied, it is denied.<br />%2</source>
+ <translation>Отбирает привелегию на %1. Если привелегия и позволяет, и запрещает, она запрещает.<br />%2</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="374"/>
+ <source>List of groups</source>
+ <translation>Список групп</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="376"/>
+ <source>This is all the groups currently defined for the channel. To create a new group, just type in the name and press enter.</source>
+ <translation>Показывает все группы, определенные для канала. Чтобы создать новую группу, просто введите имя и нажмите Enter.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="377"/>
+ <source>Remove selected group</source>
+ <translation>Удалить выбранную группу</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="379"/>
+ <source>This removes the currently selected group. If the group was inherited, it will not be removed from the list, but all local information about the group will be cleared.</source>
+ <translation>Удаляет выбранную группу. Если группа была унаследована, она не уберется из списка, но вся локальная информация о группе будет стерта.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="380"/>
+ <source>Inherit group members from parent</source>
+ <translation>Унаследовать членов группы от</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="382"/>
+ <source>This inherits all the members in the group from the parent, if the group is marked as <i>Inheritable</i> in the parent channel.</source>
+ <translation>Наследует всех членов группы от предка, если группа отмечена как <i>Наследуемая</i> в родительском канале.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="383"/>
+ <source>Make group inheritable to sub-channels</source>
+ <translation>Сделать группу наследуемой для подканалов</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="385"/>
+ <source>This makes this group inheritable to sub-channels. If the group is non-inheritable, sub-channels are still free to create a new group with the same name.</source>
+ <translation>Сделать группу наследуемой для подканалов. Если группа ненаследованная, подканалы все еще свободны для создания новой группы с таким же именем.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="386"/>
+ <source>Group was inherited from parent channel</source>
+ <translation>Группа была унаследована от родительского канала</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="388"/>
+ <source>This indicates that the group was inherited from the parent channel. You cannot edit this flag, it's just for information.</source>
+ <translation>Показывает, что группа унаследована от канала-родителя. Вы не можете изменить этот флаг, он просто для информации.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="389"/>
+ <source>Add member to group</source>
+ <translation>Добавить члена группы</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="390"/>
+ <source>Type in the name of a player you wish to add to the group and press enter.</source>
+ <translation>Введите имя игрока, которого вы хотите добавить в группу и нажмите Enter.</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="391"/>
+ <source>Remove member from group</source>
+ <translation>Удалить члена группы</translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="392"/>
+ <source>Type in the name of a player you wish to remove from the group and press enter.</source>
+ <translation>Введите имя игрока, которого вы хотите удалить из группы и нажмите Enter.</translation>
+ </message>
+</context>
+<context>
+ <name>ASIOConfig</name>
+ <message>
+ <location filename="ASIOInput.cpp" line="113"/>
+ <source>Device selection</source>
+ <translation>Выбор устройства</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="114"/>
+ <source>Capabilities</source>
+ <translation>Возможности</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="115"/>
+ <source>Channels</source>
+ <translation>Каналы</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="116"/>
+ <source>Configure input channels</source>
+ <translation>Настроить каналы ввода</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="121"/>
+ <source>This will configure the input channels for ASIO. Make sure you select at least one channel as microphone and speaker. <i>Microphone</i> should be where your microphone is attached, and <i>Speaker</i> should be a channel that samples "What you hear".<br />For example, on the Audigy 2 ZS, a good selection for Microphone would be "Mic L" while Speaker should be "Mix L" and "Mix R"</source>
+ <translation>Настраивает входные каналы для ASIO. Убедитесь, что выбрали минимум 1 канал как микрофон и динамик. <i>Микрофон</i> должен быть там, куда подсоединен ваш микрофон, и <i>Динамик</i> должен быть каналом, передающим "Что вы слышите".<br />Например, на Audigy 2 ZS, отличным выбором для Микрофона будет "Mic L", в то время, как динамик - "Mix L" и "Mix R"</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="125"/>
+ <source>Device to use for microphone</source>
+ <translation>Устройство для подключения микрофона</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="127"/>
+ <source>This chooses what device to query. You still need to actually query the device and select which channels to use.</source>
+ <translation>Выбирает, какое устройство опрашивать. Вам все еще надо опросить устройство и выбрать канал, который будете использовать.</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="128"/>
+ <source>Device</source>
+ <translation>Устройство</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="130"/>
+ <source>&Query</source>
+ <translation>&Запрос</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="132"/>
+ <source>Query selected device</source>
+ <translation>Опросить выбранное устройство</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="135"/>
+ <source>This queries the selected device for channels. Be aware that many ASIO drivers are buggy to the extreme, and querying them might cause a crash of either the application or the system.</source>
+ <translation>Опрашивает выбранное устройство поканально. Опасайтесь того, что многие драйвера ASIO не доделаны, и опрос их может вызвать "зависание" приложения или всей системы.</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="137"/>
+ <source>&Configure</source>
+ <translation>Н&астроить</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="139"/>
+ <source>Configure selected device</source>
+ <translation>Настроить выбранное устройство</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="142"/>
+ <source>This configures the selected device. Be aware that many ASIO drivers are buggy to the extreme, and querying them might cause a crash of either the application or the system.</source>
+ <translation>Настраивает выбранное устройство. Опасайтесь того, что многие драйвера ASIO не доделаны, и опрос их может вызвать "зависание" приложения или всей системы.</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="157"/>
+ <source>Driver name</source>
+ <translation>Имя привода</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="162"/>
+ <source>Buffersize</source>
+ <translation>Размер буфера</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="178"/>
+ <source>Microphone</source>
+ <translation>Микрофон</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="208"/>
+ <source><-</source>
+ <translation><-</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="205"/>
+ <source>-></source>
+ <translation>-></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="200"/>
+ <source>Unused</source>
+ <translation>Не используется</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="222"/>
+ <source>Speakers</source>
+ <translation>Динамики</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="318"/>
+ <source>%1 (ver %2)</source>
+ <translation>%1 (версия %2)</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="322"/>
+ <source>%1 ms -> %2 ms (%3 ms resolution) %4Hz</source>
+ <translation>%1 мс -> %2 мс (%3 мс разрешение) %4Гц</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="324"/>
+ <source>%1 ms -> %2 ms (%3 ms resolution) %4Hz -- Unusable</source>
+ <translation>%1 мс -> %2 мс (%3 мс разрешение) %4Гц -- Не используемо</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="400"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="396"/>
+ <source>ASIO Initialization failed: %1</source>
+ <translation>Ошибка инициализации ASIO: %1</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="400"/>
+ <source>Failed to instantiate ASIO driver</source>
+ <translation>Невозможно установить драйвер ASIO</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="437"/>
+ <source>ASIO</source>
+ <translation>ASIO</translation>
+ </message>
+</context>
+<context>
+ <name>ASIOInput</name>
+ <message>
+ <location filename="ASIOInput.cpp" line="580"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="512"/>
+ <source>You need to select at least one microphone and one speaker source to use ASIO. If you just need microphone sampling, use DirectSound.</source>
+ <translation>Вам нужно выбрать хотя бы одно устройство для микрофона и одно для динамика для использования ASIO. Если вам нужно только протестировать микрофон, используйет DirectSound.</translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="580"/>
+ <source>Opening selected ASIO device failed. No input will be done.</source>
+ <translation>Невозможно открыть устройство ASIO. Входных данных не получено.</translation>
+ </message>
+</context>
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="About.cpp" line="53"/>
+ <source><h3>Mumble v0.9.5</h3><p>Copyright 2005-2006 Thorvald Natvig<br />slicer@users.sourceforge.net</p><p><b>A voice-chat utility for gamers</b></p><p><tt>http://mumble.sourceforge.net/</tt></p></source>
+ <translation type="obsolete"><h3>Mumble v0.9.5</h3><p>Все права принадлежат 2005-2006 Thorvald Natvig<br />slicer@users.sourceforge.net</p><p>
+Русский перевод Khazov Nick<br />m2k3d0n@users.sourceforge.net</p><p><b>Программа для голосового общения игроков</b></p><p><tt>http://mumble.sourceforge.net/</tt></p></translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="60"/>
+ <source>&About Mumble</source>
+ <translation>&О Mumble</translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="61"/>
+ <source>&License</source>
+ <translation>Л&ицензия</translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="63"/>
+ <source>OK</source>
+ <translation>ОК</translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="54"/>
+ <source><h3>Mumble v%1</h3><p>Copyright 2005-2006 Thorvald Natvig<br />slicer@users.sourceforge.net</p><p><b>A voice-chat utility for gamers</b></p><p><tt>http://mumble.sourceforge.net/</tt></p></source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AboutSpeexDialog</name>
+ <message>
+ <location filename="About.cpp" line="84"/>
+ <source><h3>About Speex</h3><p><tt>http://www.speex.org/</tt></p><p>This program uses Speex version %1</p><p>Speex is used for echo cancellation, noise<br />filtering, voice activity detection and speech<br />compression.</p></source>
+ <translation><h3>О Speex</h3><p><tt>http://www.speex.org/</tt></p><p>Эта программа использует Speex версии %1</p><p>Speex используется для подавления эха, шумов<br />фильтрации, определения голосовой активности и сжатия<br />речи.</p></translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="86"/>
+ <source>OK</source>
+ <translation>ОК</translation>
+ </message>
+</context>
+<context>
+ <name>AudioConfigDialog</name>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="51"/>
+ <source>Interfaces</source>
+ <translation>Интерфейсы</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="52"/>
+ <source>Transmission</source>
+ <translation>Передача</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="53"/>
+ <source>Compression</source>
+ <translation>Сжатие</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="54"/>
+ <source>Jitter Buffer</source>
+ <translation>Буфер Jitter</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="67"/>
+ <source>Input</source>
+ <translation>Ввод</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="70"/>
+ <source>Input method for audio</source>
+ <translation>Метод ввода для аудио</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="71"/>
+ <source><b>This is the input method to use for audio.</b><br />Most likely you want to use DirectSound.</source>
+ <translation><b>Этот метод ввода используется для аудио.</b><br />Скорее всего вы захотите использовать DirectSound.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="85"/>
+ <source>Output</source>
+ <translation>Вывод</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="88"/>
+ <source>Output method for audio</source>
+ <translation>Мытод вывода для аудио</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="89"/>
+ <source><b>This is the output method to use for audio.</b><br />Most likely you want to use DirectSound.</source>
+ <translation><b>Этот метод вывода используется для аудио.</b><br />Скорее всего вы захотите использовать DirectSound.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="99"/>
+ <source>Continuous</source>
+ <translation>Продолжительный</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="100"/>
+ <source>Voice Activity</source>
+ <translation>Голосовая активность</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="101"/>
+ <source>Push To Talk</source>
+ <translation>Нажмите для разговора</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="103"/>
+ <source>Transmit</source>
+ <translation>Передать</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="106"/>
+ <source>When to transmit your speech</source>
+ <translation>Когда передавать вашу речь</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="110"/>
+ <source><b>This sets when speech should be transmitted.</b><br /><i>Continuous</i> - All the time<br /><i>Voice Activity</i> - When you are speaking clearly.<br /><i>Push To Talk</i> - When you hold down the hotkey set under <i>Shortcuts</i>.</source>
+ <translation><b>Устанавливает, когда передавать речь.</b><br /><i>Постоянно</i> - Все время<br /><i>При активность голоса</i> - Когда вы говорите четко.<br /><i>Нажмите для разговора</i> - Когда вы зажимаете горячую клавишу, установленную в <i>Ярлыках</i>.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="121"/>
+ <source>Voice Hold</source>
+ <translation>Задержка голоса</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="128"/>
+ <source>How long to keep transmitting after silence</source>
+ <translation>Как долго передавать после тишины</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="131"/>
+ <source><b>This selects how long after a perceived stop in speech transmission should continue.</b><br />Set this higher if your voice breaks up when you speak (seen by a rapidly blinking voice icon next to your name).<br />Only makes sense when used with Voice Activity transmission.</source>
+ <translation><b>Выбирает, как долго после паузы в речи продолжать передачу.</b><br />Установите ее больше, если у вас много пауз в речи (можно увидеть по часто мигающей иконке голоса рядом с вашим именем).<br />Имеет смысл только когда используется с передачей по активности голоса.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="144"/>
+ <source>Audio per packet</source>
+ <translation>Аудио на пакет</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="153"/>
+ <source>How many audio frames to send per packet</source>
+ <translation>Как много аудио фрагментов посылать в пакете</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="156"/>
+ <source><b>This selects how many audio frames should be put in one packet.</b><br />Increasing this will increase the latency of your voice, but will also reduce bandwidth requirements.</source>
+ <translation><b>Выбирает, сколько аудиофрагментов помещать в пакет.</b><br />Увеличение ее увеличит задержку голоса, но также уменьшит требования к толщине канала связи.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="161"/>
+ <source>Use TCP mode</source>
+ <translation>Использовать режим TCP</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="163"/>
+ <source>Use TCP Compatibility mode</source>
+ <translation>Включить режим TCP совместимости</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="168"/>
+ <source><b>Enable TCP Compatibility mode</b>.<br />This will make Mumble use only TCP when communicating with the server. This will increase overhead and cause lost packets to produce noticeable pauses in communication, so this should only be used if you are unable to use the default (which uses UDP for voice and TCP for control).</source>
+ <translation><b>Включить режим TCP совместимости</b>.<br />Заставит Mumble использовать только TCP при связи с сервером. Это увеличит загрузку, вызовет потерю пакетов, что приведет к существенным паузам в общении, поэтому следует его использовать только в случае, если вы не можете использовать режим по умолчанию (применяющий UDP для голоса и TCP для контроля).</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="170"/>
+ <source>Automatic Reconnect</source>
+ <translation>Автоматическое восстановление связи</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="172"/>
+ <source>Reconnect when disconnected</source>
+ <translation>Восстановление связи при обрыве</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="176"/>
+ <source><b>Reconnect when disconnected</b>.<br />This will make Mumble try to automatically reconnect after 10 seconds if your server connection fails.</source>
+ <translation><b>Восстановить связь при обрыве</b>.<br />Mumble будет пытаться автоматически восстановить связь через 10 в случае обрыва связи с сервером.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="184"/>
+ <source>Maximum bandwidth used for sent audio</source>
+ <translation>Максимальная скорость, используемая для посылки аудио</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="189"/>
+ <source><b>This shows peak outgoing bandwidth used.</b><br />This shows the peak amount of bandwidth sent out from your machine. Audio bitrate is the maximum bitrate (as we use VBR) for the audio data alone. Position is the bitrate used for positional information. Overhead is our framing and the IP packet headers (IP and UDP is 90% of this overhead).</source>
+ <translation><b>Показывает пиковую загрузку посланных сигналов.</b><br />Показывает пиковое значение скорости передачи данных от вашего компьютера. Битрейт аудио - максимальный битрейт (по принятым сокращениям, VBR) только для аудиоданных. Позиция - битрейт, используемый для информации о позиции. Загрузка - раскадровка и заголовки IP пакетов (IP и UDP - 90% этой загрузки).</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="191"/>
+ <source>Outgoing Bitrate</source>
+ <translation>Исходящий битрейт</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="209"/>
+ <source>Quality</source>
+ <translation>Качество</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="216"/>
+ <source>Quality of compression (peak bandwidth)</source>
+ <translation>Качество сжатия (пиковая передача)</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="218"/>
+ <source><b>This sets the quality of compression.</b><br />This determines how much bandwidth Mumble is allowed to use for outgoing audio.</source>
+ <translation><b>Устанавливает качество сжатия.</b><br />Определяет, какую загрузку канала Mumble разрешено использовать для исходящего аудио.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="231"/>
+ <source>Complexity</source>
+ <translation>Сложность</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="237"/>
+ <source>Complexity of compression (CPU)</source>
+ <translation>Сложность сжатия (ЦП)</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="240"/>
+ <source><b>This sets the complexity of compression.</b><br />This determines how much CPU Mumble is allowed to use to increase transmitted voice quality. Settings above 5 give only marginal gain.</source>
+ <translation><b>Устанавливает сложность сжатия.</b><br />Определяет, сколько мощности ЦП Mumble разрешено использовать для увеличения качества передаваемого голоса. Установка выше 5 даст только предельный прирост.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="253"/>
+ <source>Amp</source>
+ <translation>Усиление</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="259"/>
+ <source>Maximum amplification of input sound</source>
+ <translation>Максимальное усиление входящего звука</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="270"/>
+ <source><b>Maximum amplification of input.</b><br />Mumble normalizes the input volume before compressing, and this sets how much it's allowed to amplify.<br />The actual level is continually updated based on your current speech pattern, but it will never go above the level specified here.<br />If the <i>Microphone loudness</i> 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.<br />Ideally, set it so <i>Microphone Loudness * Amplification Factor >= 100</i>, even when you're speaking really soft.<br /><br />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.</source>
+ <translation><b>Максимальное усиление входного голоса.</b><br />Mumble нормализует входную громкость до сжатия, эта опция устанавливает, на сколько можно его усилить.<br />Текущий уровень постоянно обновляется на основе текущего образца речи, но никогда не будет выше установленного здесь уровня.<br />Если уровень <i>Громкости микрофона</i> аудиостатистики держится на уровне 100%, вы можете установить его на 2.0 или выше, но если, как многие люди, вы не можете достичь 100%, установите его на чуть более высоком уровне.<br />В идеале, установите его так, чтобы <i>Громкость микрофона * Фактор усиления >= 100</i>, даже если вы говорите слишком мягко.<br /><br />Заметьте, что ничего плохого не случится, если вы установите его на максимум, но Mumble начнет подбирать другие переговоры, если вы оставите его на автоматической настройке на данном уровне.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="288"/>
+ <source>Default Jitter Buffer</source>
+ <translation>Буфер Jitter по умолчанию</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="295"/>
+ <source>How long to prebuffer on first packet</source>
+ <translation>Как долго буферизовать первый пакет</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="302"/>
+ <source><b>This sets the default buffer size for the jitter buffer</b>.<br />All incoming audio is buffered, and the jitter buffer continually tries to push the buffer to the minimum sustainable by your network, so latency can be as low as possible. This sets the default buffer size to use on the first packet to arrive from a new speaker, or when a speaker using Voice Activity or Push-To-Talk just started talking again. If the start of sentences you hear is very jittery, increase this value.</source>
+ <translation><b>Устанавливает размер буфера Jitter по умолчанию</b>.<br />Все входящее аудио буферизуется, и буфер jitter постоянно пытается послать буфер в минимально поддерживаемый вашей сетью, так что задержка может быть как можно меньшей. Устанавливает размер буфера по умолчанию для использования при приходе первого пакета от нового говорящего, или когда говорящий использует Голосовую активность или Нажмите для разговора и только начал говорить. Если начало предложений, которые вы слишите, неразборчивы, увеличьте это значение.</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="321"/>
+ <source>Basic Audio</source>
+ <translation>Основное аудио</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="369"/>
+ <source>%1 ms</source>
+ <translation>%1 мс</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="350"/>
+ <source>%1s</source>
+ <translation>%1 с</translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="418"/>
+ <source>%1kbit/s (Audio %2, Position %4, Overhead %3)</source>
+ <translation>%1кбит/с (Аудио %2, Позиция %4, Загрузка %3)</translation>
+ </message>
+</context>
+<context>
+ <name>AudioStats</name>
+ <message>
+ <location filename="AudioStats.cpp" line="342"/>
+ <source>Peak microphone level</source>
+ <translation>Пиковый уровень микрофона</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="347"/>
+ <source>Peak speaker level</source>
+ <translation>Пиковый уровень динамика</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="352"/>
+ <source>Peak clean level</source>
+ <translation>Пиковый уровень очистки</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="357"/>
+ <source>Microphone loudness</source>
+ <translation>Громкость микрофона</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="362"/>
+ <source>Mic Signal-To-Noise</source>
+ <translation>Сигнал-шум Микрофона</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="367"/>
+ <source>Speech Probability</source>
+ <translation>Вероятность речи</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="372"/>
+ <source>Audio bitrate</source>
+ <translation>Битрейт аудио</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="432"/>
+ <source>Peak power in last frame</source>
+ <translation>Пиковая мощность в последнем фрагменте</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="426"/>
+ <source>This shows the peak power in the last frame (20 ms), and is the same measurement as you would usually find displayed as "input power". Please disregard this and look at <b>Loudness</b> instead, which is much more steady and disregards outliers.</source>
+ <translation>Показывает пиковую мощность в последнем фрагменте (20 мс), и измеряется также, как и "входная мощность". Пожалуйста, оставьте ее и посмотрите на <b>Громкость</b>, которая более устойчива и не пренебрегается.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="431"/>
+ <source>This shows the peak power in the last frame (20 ms) of the speakers. 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.</source>
+ <translation>Показывает пиковую мощность в последнем фрагменте (20 мс) динамиков. Пока вы используете мультиканальный метод проб (такой как ASIO),настроенный на каналы динамика, она будет равна 0. Если у вас они так сконфигурированы, все еще будет отображаться 0, пока вы будете прослушивать звук из других программ, ваши настройки не будут работать.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="438"/>
+ <source>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.<br />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 players with echoes.</source>
+ <translation>Показывает пиковую мощность в последнем фрагменте (20 мс) после всех обработок. В идеале, должна быть -96 dB когда вы не разговариваете. На самом деле, звуковая программа примет -60 dB, мы можете с большой вероятностью увидеть -20 dB. Когда вы говорите, его следует поднять до уровня от -5 до -10 dB.<br />Если вы используете подавление эха, и оно поднимается выше -15 dB когда вы не говорите, ваши настройки не работают, и вы будете надоедать другим своим эхом.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="439"/>
+ <source>How close the current input level is to ideal</source>
+ <translation>Как близок текущий входной уровень к идеалу</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="446"/>
+ <source>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.<br /><b>Talk loud, as you would when you're upset over getting fragged by a noob.</b><br />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</source>
+ <translation>Как близок текущий входной уровень к идеалу. Чтобы приспособить уровень микрофона, откройте любую программу, которую вы используете для приспособления громкости записи, и посмотрите на значение во время разговора.<br /><b>Говорите громко, как будто от досады, когда вас только что подстрелил нуб.</b><br />Подгоняйте громкость пока это значение не приблизится к 100%, но убедитесь, что оно не выше. Если оно заходит выше, вы будто обрезаете часть своей речи, что понизит качество звука</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="447"/>
+ <source>Signal-To-Noise ratio from the microphone</source>
+ <translation>Соотношение Сигнал-шум микрофона</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="452"/>
+ <source>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.<br />If this value is below 1.0, there's more noise than voice in the signal, and so quality is reduced.<br />There is no upper limit to this value, but don't expect to see much above 40-50 without a sound studio.</source>
+ <translation>Отношение Сигнал-шум (SNR) микрофона в последнем фрагменте (20 мс). Показывает, насколько чист голос по сравнению с шумом.<br />Если значение ниже 1.0, в сигнале больше шума, нежели голоса, и поэтому ухудшается качество.<br />Верхнего предела не существует, но не ожидайте увидеть выше 40-50 без студии звукозаписи.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="453"/>
+ <source>Probability of speech</source>
+ <translation>Вероятность речи</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="458"/>
+ <source>This is the probability that the last frame (20 ms) was speech and not environment noise.<br />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'.<br />If this is in bold font, it means Mumble is currently transmitting (if you're connected).</source>
+ <translation>Вероятность того, что последний фрагмент (20 мс) был речью, а не шумом.<br />Передача голосовой активности зависит от верности этого. Фишка в том, что середина предложения всегда распознается как речь; проблема в паузах между словами и началом разговора. Трудно отделить кашель от слова, начинающегося на 'х'.<br />Если это выделено жирным шрифтом, значит Mumble сейчас передает (если вы подключены).</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="459"/>
+ <source>Bitrate of last frame</source>
+ <translation>Битрейт последнего фрагмента</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="462"/>
+ <source>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. To adjust the peak bitrate, adjust <b>Compression Complexity</b> in the Settings dialog.</source>
+ <translation>Аудио битрейт последнего сжатого фрагмента (20 мс), и поэтому оно может варьироваться, как и VBR применительно к качеству. Чтобы приспособить пиковый битрейт, приспособьте <b>Сложность сжатия</b> в диалоге настроек.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="464"/>
+ <source>Power spectrum of input signal and noise estimate</source>
+ <translation>Мощностной спектр входного сигнала и оценка шума</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="471"/>
+ <source>This shows the power spectrum of the current input signal (red line) and the current noise estimate (filled blue).<br />All amplitudes are multiplied by 30 to show the interesting parts (how much more signal than noise is present in each waveband).<br />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.</source>
+ <translation>Показывает мощностной спектр текущего входного сигнала (красная линия) и текущая оценка шума (выполнена синим).<br />Все амплитуды умножены на 30, чтобы показать интересные части (на сколько больше сигнала, чем шума представлено в каждом отрезке).<br />Это только если вы интересуетесь точными условиями шума вашего микрофона. При хороших условиях, это будет всего лишь как крошечный синий отрезок внизу. Если синего на графике более половины, у вас серьезные проблемы с шумом среды.</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="474"/>
+ <source>Weights of the echo canceller</source>
+ <translation>Значения подавителя эха</translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="487"/>
+ <source>This shows the weights of the echo canceller, with time increasing downwards and frequency increasing to the right.<br />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.<br />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. <br />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.<br />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.</source>
+ <translation>Отображает значение подавителя эха, со временем растущего вниз и увеличением частоты вправо.<br />В идеале, должен быть черным, отображая, что эха нет. В общем, у вас будут 1 или несколько горизонтальных полосок синеватого цвета, отображая время задержки эха. Вы должны увидеть значения в реальном времени.<br />Заметьте пожалуйста, что если вы не отключите эхо, вы не увидите здесь полезной информации. Запустите музыку, и все должно нормализоваться. <br />Вы можете выбрать просмотр реальных или воображаемых частей доменно-частотных значений, или наоборот высчитанные модуль и фазу. Наиболее применимым из них будут, скорее всего модуль, который является амплитудой эха, и показывает, сколько сигнала было вырезано за промежуток времени. Другие режимы просмотра более полезны для людей, желающих настроить алгоритмы глушения эха.<br />Пожалуйста, заметьте: Если картина в целом обширно варьируется в режиме модулей, подавитель эха не модет найти никакого соответствия между двумя входными источниками (динамиками и микрофоном). Или у вас очень долгая задержка эха, или один из входных источников настроен неправильно.</translation>
+ </message>
+</context>
+<context>
+ <name>BanEditor</name>
+ <message>
+ <location filename="BanEditor.cpp" line="37"/>
+ <source>Mumble - Edit Bans</source>
+ <translation>Mumble - изменить баны</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="42"/>
+ <source>&Add</source>
+ <translation>&Добавить</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="43"/>
+ <source>&Update</source>
+ <translation>&Обновить</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="44"/>
+ <source>&Remove</source>
+ <translation>&Удалить</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="69"/>
+ <source>&OK</source>
+ <translation>О&к</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="71"/>
+ <source>Accept changes</source>
+ <translation>Принять изменения</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="74"/>
+ <source>This button will accept current groups/ACLs and send them to the server. Note that if you mistakenly remove write permission from yourself, the server will add it.</source>
+ <translation>Данная кнопка примет текущие группы/списки ACL и пошлет их серверу. Заметьте, что если вы по ошибке уберете права на запись с себя, сервер добавит их.</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="75"/>
+ <source>&Cancel</source>
+ <translation>Отм&ена</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="77"/>
+ <source>Reject changes</source>
+ <translation>Отменяет изменения</translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="79"/>
+ <source>This button will cancels all changes and closes the dialog without updating the ACLs or groups on the server.</source>
+ <translation>Данная кнопка отменит все изменения и закроет окно без обновления списков ACL или групп на сервере.</translation>
+ </message>
+</context>
+<context>
+ <name>ChanACL</name>
+ <message>
+ <location filename="../ACL.cpp" line="129"/>
+ <source>W</source>
+ <translation>З</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="131"/>
+ <source>T</source>
+ <translation>Пер</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="133"/>
+ <source>E</source>
+ <translation>В</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="135"/>
+ <source>S</source>
+ <translation>Р</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="137"/>
+ <source>A</source>
+ <translation>А</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="139"/>
+ <source>M</source>
+ <translation>Г</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="141"/>
+ <source>K</source>
+ <translation>П</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="143"/>
+ <source>C</source>
+ <translation>Сзд</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="145"/>
+ <source>L</source>
+ <translation>Св</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="155"/>
+ <source>None</source>
+ <translation>Нет</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="157"/>
+ <source>Write</source>
+ <translation>Запись</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="159"/>
+ <source>Traverse</source>
+ <translation>Пересекать</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="161"/>
+ <source>Enter</source>
+ <translation>Вход</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="163"/>
+ <source>Speak</source>
+ <translation>Разговор</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="165"/>
+ <source>AltSpeak</source>
+ <translation>Альтернативный разговор</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="167"/>
+ <source>Mute/Deafen</source>
+ <translation>Глушить</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="169"/>
+ <source>Move/Kick</source>
+ <translation>Переместить/выкинуть</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="171"/>
+ <source>Make channel</source>
+ <translation>Создать канал</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="173"/>
+ <source>Link channel</source>
+ <translation>Связать канал</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="183"/>
+ <source>This represents no privileges.</source>
+ <translation>Представляет отсутствие привелегий.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="186"/>
+ <source>This represents total access to the channel, including the ability to change group and ACL information. This privilege implies all other privileges.</source>
+ <translation>Предоставляет полный доступ к каналу, веключая возможность изменять информацию о группах и ACL. Эта привелегия включает все привелегии.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="190"/>
+ <source>This represents the permission to traverse the channel. If a user is denied this privilege, he will be unable to access this channel and any sub-channels in any way, regardless of other permissions in the sub-channels.</source>
+ <translation>Предоставляет разрешение пересекать канал. Если у пользователя нет такой привилегии, он никак не сможет получить доступ к каналу и подканалам, независимо от разрешений подканалов.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="193"/>
+ <source>This represents the permission to join the channel. If you have a hierarchical channel structure, you might want to give everyone Traverse, but restrict Enter in the root of your hierarchy.</source>
+ <translation>Предоставляет разрешение входить на канал. Если у канала иерархическая структура, вы можете дать каждому право пересекать канал, но ограничивать Вход в корне вашей иерархии.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="197"/>
+ <source>This represents the permission to speak in a channel. Users without this privilege will be suppressed by the server (seen as muted), and will be unable to speak until they are unmuted by someone with the appropriate privileges.</source>
+ <translation>Предоставляет разрешение говорить на канале. Пользователи без этой привелегии будут заглушены сервером, и неспособны будут говорить, пока не получат разрешение от кого-то, имеющего соответствующие привелегии.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="201"/>
+ <source>This represents the permission to speak in a channel with flagged speech. This works exactly like the <i>speak</i> privilege, but applies to packets spoken with AltPushToTalk held down. This may be used to broadcast to a hierarchy of channels without linking.</source>
+ <translation>Преставляет разрешение говорить на канале с ослабленной речью. Работает также, как а привелегия <i>говорить</i>, но применяется к пакетам, с зажатой кнопкой Нажмите чтобы говорить - переменно. Может использоваться для передачи к иерархии каналов без связи.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="204"/>
+ <source>This represents the permission to mute and deafen other players. Once muted, a player will stay muted until he is unmuted by another privileged player or reconnects to the server.</source>
+ <translation>Предоставляет привелегии заглушать других игроков. Будучи заглушенным, игрок останется заглушенным пока не будет разглушен другим привелигериванным игроком или не переподключится к серверу.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="209"/>
+ <source>This represents the permission to move a player to another channel or kick him from the server. To actually move the player, either the moving player must have Move/Kick privileges in the destination channel, or the player must normally be allowed to enter the channel. Players with this privilege can move players into channels the target player normally wouldn't have permission to enter.</source>
+ <translation>Предоставляет разрешение перемещать игрока на другие каналы или выкидывать его с сервера. Чтобы действительно переместить игрока, игрок должен обладать привелегиями Переместить/Выкинуть на сервере назначения, или игрок должен быть допущен ко входу на сервер. Игроки с этой привелегией могут перемещать игроков на каналы, входить на которые у пользователя нет разрешения.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="212"/>
+ <source>This represents the permission to make sub-channels. The player making the sub-channel will be added to the admin group of the sub-channel.</source>
+ <translation>Предоставляет разрешение создавать подканалы. Игрок, создающий подканал, будет добавлен в группу администраторов подканала.</translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="216"/>
+ <source>This represents the permission to link channels. Players in linked channels hear each other, as long as the speaking player has the <i>speak</i> privilege in the channel of the listener. You need the link privilege in both channels to create a link, but just in either channel to remove it.</source>
+ <translation>Предоставляет привелегию связывать каналы. Игроки на связанных каналах могут слышат друг друга, также, как и говорящий игрок имеет право <i>говорить</i> на канале слушающего. Вы должны связать привелегии на обоих каналах, чтобы создать связь, но на другом канале, чтобы удалить ее.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigDialog</name>
+ <message>
+ <location filename="ConfigDialog.cpp" line="57"/>
+ <source>Mumble Configuration</source>
+ <translation>Настройки Mumble</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="69"/>
+ <source>&OK</source>
+ <translation>&Ок</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="72"/>
+ <source>Accept changes</source>
+ <translation>Принять изменения</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="74"/>
+ <source>This button will accept current settings and return to the application.<br />The settings will be stored to disk when you leave the application.</source>
+ <translation>Кнопка примет текущие настройки и вернется в приложении.<br />Настройки будут сохранены на диск, когда вы выйдете из приложения.</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="75"/>
+ <source>&Cancel</source>
+ <translation>&Отмена</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="77"/>
+ <source>Reject changes</source>
+ <translation>Отменить изменения</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="79"/>
+ <source>This button will reject all changes and return to the application.<br />The settings will be reset to the previous positions.</source>
+ <translation>Отменит все изменения и вернется в приложение.<br />Настройки будут сброшены до предыдущих значений.</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="80"/>
+ <source>&Apply</source>
+ <translation>&Применить</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="82"/>
+ <source>Apply changes</source>
+ <translation>Применить изменения</translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="83"/>
+ <source>This button will immediately apply all changes.</source>
+ <translation>Немедленно применяет все изменения.</translation>
+ </message>
+</context>
+<context>
+ <name>ConnectDialog</name>
+ <message>
+ <location filename="ConnectDialog.cpp" line="65"/>
+ <source>&Name</source>
+ <translation>&Имя</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="72"/>
+ <source>A&ddress</source>
+ <translation>А&дрес</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="80"/>
+ <source>&Port</source>
+ <translation>&Порт</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="87"/>
+ <source>&Username</source>
+ <translation>Имя п&ользователя</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="95"/>
+ <source>&Password</source>
+ <translation>П&ароль</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="101"/>
+ <source>&Connect</source>
+ <translation>&Связаться</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="105"/>
+ <source>Cancel</source>
+ <translation>Отмена</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="108"/>
+ <source>&Add</source>
+ <translation>&Добавить</translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="111"/>
+ <source>&Remove</source>
+ <translation>&Удалить</translation>
+ </message>
+</context>
+<context>
+ <name>DXAudioInput</name>
+ <message>
+ <location filename="DXAudioInput.cpp" line="108"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="DXAudioInput.cpp" line="103"/>
+ <source>Opening chosen DirectSound Input failed. No microphone capture will be done.</source>
+ <translation>Открытие выбранного входа DirectSound невозможно. Ввод с микрофона неосуществим.</translation>
+ </message>
+ <message>
+ <location filename="DXAudioInput.cpp" line="108"/>
+ <source>Opening chosen DirectSound Input failed. Default device will be used.</source>
+ <translation>Открытие выбранного входа DirectSound невозможно. Будет использовано устройство по умолчанию.</translation>
+ </message>
+</context>
+<context>
+ <name>DXAudioOutput</name>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="341"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="336"/>
+ <source>Opening chosen DirectSound Output failed. No audio will be heard.</source>
+ <translation>Открытие выбранного выхода DirectSound невозможно. Вы не услышите звука.</translation>
+ </message>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="341"/>
+ <source>Opening chosen DirectSound Output failed. Default device will be used.</source>
+ <translation>Открытие выбранного выхода DirectSound невозможно. Будет использовано устройство по умолчанию.</translation>
+ </message>
+</context>
+<context>
+ <name>DXConfigDialog</name>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="61"/>
+ <source>Default DirectSound Voice Input</source>
+ <translation>Голосовой вход DirectSound по умолчанию</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="62"/>
+ <source>Default DirectSound Voice Output</source>
+ <translation>Голосовой выход DirectSound по умолчанию</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="84"/>
+ <source>Device selection</source>
+ <translation>Выбор устройства</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="87"/>
+ <source>Device to use for microphone</source>
+ <translation>Устройство, используемое как микрофон</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="89"/>
+ <source>This sets the input device to use, which is where you have connected the microphone.</source>
+ <translation>Устанавливает используемое устройство ввода, куда вы подсоединили микрофон.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="90"/>
+ <source>Input</source>
+ <translation>Ввод</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="95"/>
+ <source>Device to use for speakers/headphones</source>
+ <translation>Устройство, используемое как динамики/наушники</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="97"/>
+ <source>This sets the output device to use, which is where you have connected your speakers or your headset.</source>
+ <translation>Устанавливает используемое устройство вывода, куда вы подсоединили динамики или наушники.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="98"/>
+ <source>Output</source>
+ <translation>Вывод</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="105"/>
+ <source>Output Options</source>
+ <translation>Свойства вывода</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="114"/>
+ <source>Output Delay</source>
+ <translation>Задержка вывода</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="119"/>
+ <source>Amount of data to buffer for DirectSound</source>
+ <translation>Количество данных буфера для DirectSound</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="122"/>
+ <source>This sets the amount of data to prebuffer in the DirectSound buffer. Experiment with different values and set it to the lowest which doesn't cause rapid jitter in the sound.</source>
+ <translation>Устанавливает размер данных для буферизации в буфере DirectSound. Экспериментируйте с различными значениями и установите его на наименьшее, которое не вызывает быстрого ухудшения звука.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="130"/>
+ <source>Positional Audio</source>
+ <translation>Позиционированное аудио</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="134"/>
+ <source>None</source>
+ <translation>Нет</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="135"/>
+ <source>Panning</source>
+ <translation>Размытие</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="136"/>
+ <source>Light HRTF</source>
+ <translation>Легкий HRTF</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="137"/>
+ <source>Full HRTF</source>
+ <translation>Полный HRTF</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="140"/>
+ <source>3D Sound Algorithm</source>
+ <translation>Алгоритм 3D звучания</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="148"/>
+ <source>This sets what 3D Sound algorithm to use.<br /><b>None</b> - Disable 3D Sound (least CPU).<br /><b>Panning</b> - Just use stereo panning (some CPU).<br /><b>Light/Full HRTF</b> - Head-Related Transfer Functions enabled. This may use a small amount of CPU.<br />Note that if you have a soundcard with <i>hardware</i> 3D processing, HRTF processing will be done on the soundcard and will use practically no processing power.</source>
+ <translation>Устанавливает, какой алгоритм 3D звучания использовать.<br /><b>Нет</b> - Выключить 3D Звучание (наименьшая нагрузка на ЦП).<br /><b>Размытие</b> - Просто ипсользоватьб стьерео размытие (небольшая загрузка ЦП).<br /><b>Легкое/Полное HRTF</b> - Включает Направленные функции передачи. Может немного загружать ЦП.<br />Заметьте, что если ваша звуковая карта поддерживает <i>аппаратную</i> обработку 3D, обработка HRTF будет осуществлятся практически без использования ЦП.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="149"/>
+ <source>Method</source>
+ <translation>Метод</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="164"/>
+ <source>MinDistance</source>
+ <translation>МинДистанция</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="169"/>
+ <source>Minimum distance to player before sound decreases</source>
+ <translation>Минимальное расстояние до игрока до уменьшения звука</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="171"/>
+ <source>This sets the minimum distance for sound calculations. The volume of other players' speech will not decrease until they are at least this far away from you.</source>
+ <translation>Устанавливает минимальное расстояние для расчета звука. Громкость речи других игроков не уменьшится до тех пор, пока они по крайней мере на таком расстоянии от вас.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="181"/>
+ <source>MaxDistance</source>
+ <translation>МаксДистанция</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="186"/>
+ <source>Maximum distance, beyond which sound won't decrease</source>
+ <translation>Максимальное расстояние, больше которого звук не уменьшится</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="188"/>
+ <source>This sets the maximum distance for sound calculations. When farther away than this, other players' sound volume will not decrease any more.</source>
+ <translation>Устанавливает максимальное расстояния для рассчета звука. Даже дальше от вас, громкость других игроков больше не уменьшится.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="198"/>
+ <source>RollOff</source>
+ <translation>Откат</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="203"/>
+ <source>Factor for sound volume decrease</source>
+ <translation>Фактор уменьшения громкости звука</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="206"/>
+ <source>How fast should sound volume drop when passing beyond the minimum distance. The normal (1.0) is that sound volume halves each time the distance doubles. Increasing this value means sound volume drops faster, while decreasing it means it drops slower.</source>
+ <translation>Как быстро будет падать громкость звука после достижения определеной дистанции. Обычно (1.0) - громкость уменьшается вполовину при удвоении расстояния. Увеличение этого значения обозначает, что громкость падает быстрее, уменьшение - замедление падения.</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="226"/>
+ <source>DirectSound</source>
+ <translation>DirectSound</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="245"/>
+ <source>%1ms</source>
+ <translation>%1мс</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="255"/>
+ <source>%1m</source>
+ <translation>%1м</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="263"/>
+ <source>%1</source>
+ <translation>%1</translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="273"/>
+ <source>Players more than %1 meters away have %2% intensity</source>
+ <translation>Игроки дальше, чем %1 метров дальше будут слышать сигнал %2% интенсивности</translation>
+ </message>
+</context>
+<context>
+ <name>Database</name>
+ <message>
+ <location filename="Database.cpp" line="67"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="Database.cpp" line="67"/>
+ <source>Mumble failed to initialize a database in any
+of the possible locations.</source>
+ <translation>Mumble не удалось инициализировать базу данных ни в
+одном из доступных мест.</translation>
+ </message>
+</context>
+<context>
+ <name>DirectInputKeyWidget</name>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="72"/>
+ <source>Press Shortcut</source>
+ <translation>Нажмите ярлык</translation>
+ </message>
+</context>
+<context>
+ <name>GlobalShortcutWinConfig</name>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="169"/>
+ <source>Shortcuts</source>
+ <translation>Ярлыки</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="135"/>
+ <source>Function</source>
+ <translation>Функция</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="137"/>
+ <source>Shortcut</source>
+ <translation>Ярлык</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="150"/>
+ <source>Shortcut bound to %1.</source>
+ <translation>Ярлык указывает на %1.</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="153"/>
+ <source><b>This is the global shortcut bound to %1</b><br />Click this field and then the desired key/button combo to rebind. Double-click to clear.</source>
+ <translation><b>Этот глобальный ярлык указывает на %1</b><br />Щелкните на этом поле и затем на выбренной клавише/сочетании клавиш для применения. Дважды кликните для очистки.</translation>
+ </message>
+</context>
+<context>
+ <name>GlobalShortcutXConfig</name>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="156"/>
+ <source>Shortcuts</source>
+ <translation>Ярлыки</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="122"/>
+ <source>Function</source>
+ <translation>Функция</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="124"/>
+ <source>Shortcut</source>
+ <translation>Ярлык</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="137"/>
+ <source>Shortcut bound to %1.</source>
+ <translation>Ярлык указывает на %1.</translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="140"/>
+ <source><b>This is the global shortcut bound to %1</b><br />Click this field and then the desired key combo to rebind. Double-click to clear.</source>
+ <translation><b>Этот глобальный ярлык указывает на %1</b><br />Щелкните на этом поле и затем на выбренной клавише/сочетании клавиш для применения. Дважды кликните для очистки.</translation>
+ </message>
+</context>
+<context>
+ <name>Log</name>
+ <message>
+ <location filename="Log.cpp" line="146"/>
+ <source>Debug</source>
+ <translation>Отладка</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="147"/>
+ <source>Critical</source>
+ <translation>Критичный</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="148"/>
+ <source>Warning</source>
+ <translation>Предупреждение</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="149"/>
+ <source>Information</source>
+ <translation>Информация</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="150"/>
+ <source>Server Connected</source>
+ <translation>Сервер подключен</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="151"/>
+ <source>Server Disconnected</source>
+ <translation>Сервер отключен</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="152"/>
+ <source>Player Joined Server</source>
+ <translation>Игнрок присоединился к серверу</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="153"/>
+ <source>Player Left Server</source>
+ <translation>Игрок покинул сервер</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="154"/>
+ <source>Player kicked (you or by you)</source>
+ <translation>Игрок выброшен (Вы или Вами)</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="155"/>
+ <source>Player kicked</source>
+ <translation>Игрок выброшен</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="156"/>
+ <source>You self-muted/deafened</source>
+ <translation>Вы заглушили сами себя</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="157"/>
+ <source>Other self-muted/deafened</source>
+ <translation>Другой игрок заглушил сам себя</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="158"/>
+ <source>Player muted (you)</source>
+ <translation>Игрок заглушил вас</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="159"/>
+ <source>Player muted (by you)</source>
+ <translation>Вы заглушили игрока</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="160"/>
+ <source>Player muted (other)</source>
+ <translation>Игрок заглушен (другим)</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="161"/>
+ <source>Player Joined Channel</source>
+ <translation>Игрок присоединился к каналу</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="162"/>
+ <source>Player Left Channel</source>
+ <translation>Игрок покинул канал</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="164"/>
+ <source>Permission Denied</source>
+ <translation>Доступ запрещен</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="219"/>
+ <source>[%2] %1</source>
+ <translation>[%2] %1</translation>
+ </message>
+</context>
+<context>
+ <name>LogConfig</name>
+ <message>
+ <location filename="Log.cpp" line="113"/>
+ <source>Messages</source>
+ <translation>Сообщения</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="56"/>
+ <source>Console</source>
+ <translation>Консоль</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="58"/>
+ <source>TTS</source>
+ <translation>Текст в речь</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="72"/>
+ <source>Enable console for %1</source>
+ <translation>Включить консоль для %1</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="78"/>
+ <source>Enable Text-To-Speech for %1</source>
+ <translation>Включить преобразователь текста в речь для %1</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="85"/>
+ <source>Text To Speech</source>
+ <translation>Текст в речь</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="88"/>
+ <source>Volume</source>
+ <translation>Громкость</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="96"/>
+ <source>Volume of Text-To-Speech Engine</source>
+ <translation>Громкость преобразователя текста в речь</translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="97"/>
+ <source><b>This is the volume used for the speech synthesis.</b></source>
+ <translation><b>Эта громкость используется для создания речи.</b></translation>
+ </message>
+</context>
+<context>
+ <name>LookConfig</name>
+ <message>
+ <location filename="LookConfig.cpp" line="67"/>
+ <source>Language</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="51"/>
+ <source>Look and Feel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="82"/>
+ <source>System default</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="69"/>
+ <source>Language to use (requires restart)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="70"/>
+ <source><b>This sets which language Mumble should use.</b><br />You have to restart Mumble to use the new language.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="89"/>
+ <source>Style</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="91"/>
+ <source>Basic widget style</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="92"/>
+ <source><b>This sets the basic look and feel to use.</b></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="97"/>
+ <source>Skin</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="99"/>
+ <source>...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="101"/>
+ <source>Skin file to use</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="102"/>
+ <source><b>This sets which skin Mumble should use.</b><br />The skin is a style file applied on top of the basic widget style.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="109"/>
+ <source>Use Horizontal Splitter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="111"/>
+ <source>Use horizontal or vertical splitter for the main window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="112"/>
+ <source><b>This sets whether the split in the main window is horizontal or vertical.</b></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="127"/>
+ <source>User Interface</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="171"/>
+ <source>Choose skin file</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="MainWindow.cpp" line="53"/>
+ <source>Root</source>
+ <translation>Корень</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="73"/>
+ <source>&Connect</source>
+ <translation>&Присоединиться</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="74"/>
+ <source>Open the server connection dialog</source>
+ <translation>Открыть окно соединения с сервером</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="75"/>
+ <source>Shows a dialog of registered servers, and also allows quick connect.</source>
+ <translation>Показывает окно зарегистрированных серверов, а также позволяет быстро связываться.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="77"/>
+ <source>&Disconnect</source>
+ <translation>&Отключиться</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="78"/>
+ <source>Disconnect from server</source>
+ <translation>Отключить от сервера</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="79"/>
+ <source>Disconnects you from the server.</source>
+ <translation>Отключает вас от сервера.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="82"/>
+ <source>&Ban lists</source>
+ <translation>&Списки забаненных</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="83"/>
+ <source>Edit ban lists on server</source>
+ <translation>Редактировать список забаненных на сервере</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="84"/>
+ <source>This lets you edit the server-side IP ban lists.</source>
+ <translation>Позволяет вам изменять серверные банлисты по IP.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="88"/>
+ <source>&Kick</source>
+ <translation>В&ыбросить</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="90"/>
+ <source>Kick player (with reason)</source>
+ <translation>Выкинуть игрока (с причиной)</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="91"/>
+ <source>Kick selected player off server. You'll be asked to specify a reason.</source>
+ <translation>Выкидывает игрока с сервера. Вас спросят о причине.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="92"/>
+ <source>&Ban</source>
+ <translation>За&банить</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="94"/>
+ <source>Kick and ban player (with reason)</source>
+ <translation>Выкинуть и забанить игрока (с причиной)</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="95"/>
+ <source>Kick and ban selected player from server. You'll be asked to specify a reason.</source>
+ <translation>Выкидывает и банит выбранного игрока с сервера. Вас попросят уточнить причину.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="96"/>
+ <source>&Mute</source>
+ <translation>Сделать &немым</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="109"/>
+ <source>Mute player</source>
+ <translation>Сделать игрока немым</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="100"/>
+ <source>Mute or unmute player on server. Unmuting a deafened player will also undeafen them.</source>
+ <translation>Глушить или разглушить игрока на сервере. Разглушение игрока даст ему возможность говорить.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="101"/>
+ <source>&Deafen</source>
+ <translation>Заг&лушить</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="104"/>
+ <source>Deafen player</source>
+ <translation>Заглушить игрока</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="105"/>
+ <source>Deafen or undeafen player on server. Deafening a player will also mute them.</source>
+ <translation>Глушит или снимает глушение с игрока на сервере. Глушение игрока также сделает его немым.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="106"/>
+ <source>&Local Mute</source>
+ <translation>&Местное глушение</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="110"/>
+ <source>Mute or unmute player locally.</source>
+ <translation>Делает игрока немым локально.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="112"/>
+ <source>&Add</source>
+ <translation>&Добавить</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="114"/>
+ <source>Add new channel</source>
+ <translation>Добавить новый канал</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="115"/>
+ <source>This adds a new sub-channel to the currently selected channel.</source>
+ <translation>Добавляет новый подканал в выбранный канал.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="116"/>
+ <source>&Remove</source>
+ <translation>&Удалить</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="118"/>
+ <source>Remove channel</source>
+ <translation>Удалить канал</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="119"/>
+ <source>This removes a channel and all sub-channels.</source>
+ <translation>Удаляет канал и все подканалы.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="120"/>
+ <source>&Edit ACL</source>
+ <translation>&Редактировать списки ACL</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="122"/>
+ <source>Edit Groups and ACL for channel</source>
+ <translation>Редактировать Группы и списки ACL для канала</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="123"/>
+ <source>This opens the Group and ACL dialog for the channel, to control permissions.</source>
+ <translation>Открывает диалоговое окно групп и списков ACL для каналов, чтобы управлять привилегиями.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="124"/>
+ <source>&Link</source>
+ <translation>&Связать</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="126"/>
+ <source>Link your channel to another channel</source>
+ <translation>Связывает ваш канал с другими каналами</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="129"/>
+ <source>This links your current channel to the selected channel. If they have permission to speak in the other channel, players can now hear each other. This is a permanent link, and will last until manually unlinked or the server is restarted. Please see the shortcuts for push-to-link.</source>
+ <translation>Связывает ваш текущий канал с выбранным каналом. Если у игроков есть разрешение говорить на канале, они могут слышать друг друга. Эта связь постоянна, и длится до разрыва связи вручную или перезапуска сервера. Пожалуйста, просмотрите ярлыки для нажмите-для-связи.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="167"/>
+ <source>&Unlink</source>
+ <translation>&Разорвать связь</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="132"/>
+ <source>Unlink your channel from another channel</source>
+ <translation>Разорвать связь канала с другим</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="133"/>
+ <source>This unlinks your current channel from the selected channel.</source>
+ <translation>Разрывает связь вашего канала с выбранным каналом.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="134"/>
+ <source>Unlink &All</source>
+ <translation>Разорвать &Все</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="136"/>
+ <source>Unlinks your channel from all linked channels.</source>
+ <translation>Разрывает канал с остальными каналами.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="137"/>
+ <source>This unlinks your current channel (not the selected one) from all linked channels.</source>
+ <translation>Разрывает связь вашего канала со всеми (не только с выбранными) каналами.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="139"/>
+ <source>&Reset</source>
+ <translation>Сб&росить</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="141"/>
+ <source>Reset audio preprocessor</source>
+ <translation>Сбросить аудио препроцессор</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="144"/>
+ <source>This will reset the audio preprocessor, including noise cancellation, automatic gain and voice activity detection. If something suddenly worsens the audio environment (like dropping the microphone) and it was temporary, use this to avoid having to wait for the preprocessor to readjust.</source>
+ <translation>Сбросит аудио препроцессор, включая подавление шумов, автоматическое получение и определение активности голоса. Если что-то внезапно вмешается в звуковую среду (например падение микрофона) на короткое время, используйте это, чтобы не ждать приспособления препроцессора.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="145"/>
+ <source>&Mute Self</source>
+ <translation>Загл&ушить себя</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="149"/>
+ <source>Mute yourself</source>
+ <translation>Заглушить себя</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="150"/>
+ <source>Mute or unmute yourself. When muted, you will not send any data to the server. Unmuting while deafened will also undeafen.</source>
+ <translation>Заглушает себя. При включении, вы не сможете посылать данных на сервер. </translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="151"/>
+ <source>&Deafen Self</source>
+ <translation>Сделать се&бя немым</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="155"/>
+ <source>Deafen yourself</source>
+ <translation>Делает себя немым</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="156"/>
+ <source>Deafen or undeafen yourself. When deafened, you will not hear anything. Deafening yourself will also mute.</source>
+ <translation>Делает себя немым или возвращает голос. Когда вы нем, вы не услышите никого. Онемение также и заглушает.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="157"/>
+ <source>&Text-To-Speech</source>
+ <translation>&Text-To-Speech</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="161"/>
+ <source>Toggle Text-To-Speech</source>
+ <translation>Включить Text-To-Speech</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="162"/>
+ <source>Enable or disable the text-to-speech engine. Only messages enabled for TTS in the Configuration dialog will actually be spoken.</source>
+ <translation>Включает или выключает движок Text-To-Speech. Будут произносится сообщения, включенные в диалоговом окне конфигурации TTS.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="163"/>
+ <source>S&tatistics</source>
+ <translation>С&татистика</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="165"/>
+ <source>Display audio statistics</source>
+ <translation>Показывает аудио статистику</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="166"/>
+ <source>Pops up a small dialog with information about your current audio input.</source>
+ <translation>Вызывает маленькую подсказку о вашем текущем аудио вводе.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="169"/>
+ <source>Forcibly unlink plugin</source>
+ <translation>Насильно отключить плагин</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="171"/>
+ <source>This forces the current plugin to unlink, which is handy if it is reading completely wrong data.</source>
+ <translation>Заставляет текущий плагин разорвать связь, что удобно про считываниии абсолютно неверных данных.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="173"/>
+ <source>&Settings</source>
+ <translation>&Настройки</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="175"/>
+ <source>Configure Mumble</source>
+ <translation>Настроить Mumble</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="176"/>
+ <source>Allows you to change most settings for Mumble.</source>
+ <translation>Позволяет вам изменять большинство настроек Mumble.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="178"/>
+ <source>&What's This?</source>
+ <translation>&Что это?</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="180"/>
+ <source>Enter What's This? mode</source>
+ <translation>Войти в Что это? режим </translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="182"/>
+ <source>Click this to enter "What's This?" mode. Your cursor will turn into a question mark. Click on any button, menu choice or area to show a description of what it is.</source>
+ <translation>Нажмите, чтобы войти в режим "Что это?". Ваш курсор превратится в знак вопроса. Нажмите на любой кнопке, пункту меню или зоне, чтобы получить пояснение.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="183"/>
+ <source>&About</source>
+ <translation>&О программе</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="185"/>
+ <source>Information about Mumble</source>
+ <translation>Информация о Mumble</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="186"/>
+ <source>Shows a small dialog with information and license for Mumble.</source>
+ <translation>Показывает маленькое диалоговое окно с информацией и лицензией для Mumble.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="187"/>
+ <source>About &Speex</source>
+ <translation>О &Speex</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="189"/>
+ <source>Information about Speex</source>
+ <translation>Информация о Speex</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="190"/>
+ <source>Shows a small dialog with information about Speex.</source>
+ <translation>Показывает небольшое диалоговое окно с информацией о Speex.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="191"/>
+ <source>&About Qt</source>
+ <translation>&О Qt</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="193"/>
+ <source>Information about Qt</source>
+ <translation>Информация о Qt</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="194"/>
+ <source>Shows a small dialog with information about Qt.</source>
+ <translation>Показывает небольшое диалоговое окно с информацией о Qt.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="195"/>
+ <source>Check for &Updates</source>
+ <translation>Проверить об&новления</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="197"/>
+ <source>Check for new version of Mumble</source>
+ <translation>Проверить новую версию Mumble</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="199"/>
+ <source>Connects to the Mumble webpage to check if a new version is available, and notifies you with an appropriate download URL if this is the case.</source>
+ <translation>Присоединяется к вебстранице Mumble, чтобы проверить доступность новой версии, и уведомляет вас о ссылке для закачки в этом случае.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="203"/>
+ <source>Mumble -- %1</source>
+ <translation>Mumble -- %1</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="220"/>
+ <source>Log of messages</source>
+ <translation>История сообщений</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="222"/>
+ <source>This shows all recent activity. Connecting to servers, errors and information messages all show up here.<br />To configure exactly which messages show up here, use the <b>Settings</b> command from the menu.</source>
+ <translation>Показывает все недавние действия. Соединяясь с сервером, сообщения о всех ошибках и информации будут показаны все.<br />Чтобы настроить, какие точно сообщения отображать там, используйте команду <b>Настройки</b> из меню.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="224"/>
+ <source>&Server</source>
+ <translation>&Сервер</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="225"/>
+ <source>&Player</source>
+ <translation>&Игрок</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="226"/>
+ <source>&Channel</source>
+ <translation>&Канал</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="227"/>
+ <source>&Audio</source>
+ <translation>&Аудио</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="228"/>
+ <source>C&onfigure</source>
+ <translation>Н&астройки</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="229"/>
+ <source>&Help</source>
+ <translation>&Помощь</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="284"/>
+ <source>Push-to-Talk</source>
+ <comment>Global Shortcut</comment>
+ <translation>Нажмите чтобы говорить</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="287"/>
+ <source>Reset Audio Processor</source>
+ <comment>Global Shortcut</comment>
+ <translation>Сбросить Аудио препроцессор</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="291"/>
+ <source>Toggle Mute Self</source>
+ <comment>Global Shortcut</comment>
+ <translation>Включить заглушение себя</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="295"/>
+ <source>Toggle Deafen Self</source>
+ <comment>Global Shortcut</comment>
+ <translation>Включить онемение себя</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="299"/>
+ <source>Unlink Plugin</source>
+ <comment>Global Shortcut</comment>
+ <translation>Отключить плагин</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="303"/>
+ <source>Force Center Position</source>
+ <comment>Global Shortcut</comment>
+ <translation>Принудительная центральная позиция</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="308"/>
+ <source>Chan Parent</source>
+ <comment>Global Shortcut</comment>
+ <translation>
+Предок канала</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="313"/>
+ <source>Chan Sub#%1</source>
+ <comment>Global Shortcut</comment>
+ <translation>Подканал#%1</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="318"/>
+ <source>Chan All Subs</source>
+ <comment>Global Shortcut</comment>
+ <translation>Все подканалы</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="322"/>
+ <source>Push-to-Mute</source>
+ <comment>Global Shortcut</comment>
+ <translation>Нажмите чтобы заглушить</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="325"/>
+ <source>Join Channel</source>
+ <comment>Global Shortcut</comment>
+ <translation>Присоединиться к каналу</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="329"/>
+ <source>Toggle Overlay</source>
+ <comment>Global Shortcut</comment>
+ <translation>Включить Замещение</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="334"/>
+ <source>Alt Push-to-Talk</source>
+ <comment>Global Shortcut</comment>
+ <translation>Усиленное нажмите чтобы говорить</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="503"/>
+ <source>Kicking player %1</source>
+ <translation>Выкидывание игрока %1</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="519"/>
+ <source>Enter reason</source>
+ <translation>Введите причину</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="519"/>
+ <source>Banning player %1</source>
+ <translation>Бан игрока %1</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="587"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="571"/>
+ <source>Channel Name</source>
+ <translation>Имя канала</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="587"/>
+ <source>Are you sure you want to delete %1 and all its sub-channels?</source>
+ <translation>Вы уверены, что хотите удалить %1 и все его подканалы?</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="664"/>
+ <source>Unmuted and undeafened.</source>
+ <translation>Онемение и глушение снято.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="666"/>
+ <source>Unmuted.</source>
+ <translation>Глушение снято.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="668"/>
+ <source>Muted.</source>
+ <translation>Заглушен.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="683"/>
+ <source>Muted and deafened.</source>
+ <translation>В онемении и заглушен.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="685"/>
+ <source>Deafened.</source>
+ <translation>В онемении.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="687"/>
+ <source>Undeafened.</source>
+ <translation>Онемение снято.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="737"/>
+ <source>About Qt</source>
+ <translation>О Qt</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="813"/>
+ <source>Joining %1.</source>
+ <translation>Соединяемся с %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="839"/>
+ <source>Connected to server.</source>
+ <translation>Соединен с сервером.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="872"/>
+ <source>Server connection failed: %1.</source>
+ <translation>Попытка соединения с сервером не удалась: %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="878"/>
+ <source>Disconnected from server.</source>
+ <translation>Отключен от сервера.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="884"/>
+ <source>Reconnecting.</source>
+ <translation>Повтор связи.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="917"/>
+ <source>Joined server: %1.</source>
+ <translation>Присединились к серверу: %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="932"/>
+ <source>Left server: %1.</source>
+ <translation>Покинут сервер: %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="967"/>
+ <source>%1 is now muted and deafened.</source>
+ <translation>%1 сейчас в онемении и заглушен.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="969"/>
+ <source>%1 is now muted.</source>
+ <translation>%1 сейчас заглушен.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="971"/>
+ <source>%1 is now unmuted.</source>
+ <translation>С %1 снято глушение.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1002"/>
+ <source>server</source>
+ <translation>сервер</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="987"/>
+ <source>You were muted by %1.</source>
+ <translation>Вы заглушили %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="987"/>
+ <source>You were unmuted by %1.</source>
+ <translation>Вы сняли глушение с %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="989"/>
+ <source>%1 muted by %2.</source>
+ <translation>%1 заглушен %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="989"/>
+ <source>%1 unmuted by %2.</source>
+ <translation>%2 снял глушение с %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1005"/>
+ <source>You were deafened by %1.</source>
+ <translation>На вас наложил онемение %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1005"/>
+ <source>You were undeafened by %1.</source>
+ <translation>С вас снял онемение %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1007"/>
+ <source>%1 deafened by %2.</source>
+ <translation>%1 в онемении из-за %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1007"/>
+ <source>%1 undeafened by %2.</source>
+ <translation>С %1 снято онемение %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1014"/>
+ <source>You were kicked from the server by %1: %2.</source>
+ <translation>Вы были выкинуты с сервера благодаря %1: %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1018"/>
+ <source>%3 was kicked from the server by %1: %2.</source>
+ <translation>%3 был выкинут с сервера %1: %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1026"/>
+ <source>You were kicked and banned from the server by %1: %2.</source>
+ <translation>Вы были выкинуты и забанены на сервере администратором %1: %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1030"/>
+ <source>%3 was kicked and banned from the server by %1: %2.</source>
+ <translation>%3 был выкинут с сервера и забанен администратором %1: %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1049"/>
+ <source>%1 left channel.</source>
+ <translation>%1 покинул канал.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1051"/>
+ <source>%1 moved out by %2.</source>
+ <translation>%1 перемещен из канала %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1058"/>
+ <source>%1 entered channel.</source>
+ <translation>%1 вошел на канал.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1060"/>
+ <source>%1 moved in by %2.</source>
+ <translation>%1 перемещен сюда %2.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1112"/>
+ <source>Server connection rejected: %1.</source>
+ <translation>Попытка подключения отклонена: %1.</translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1117"/>
+ <source>Denied: %1.</source>
+ <translation>Запрещено: %1.</translation>
+ </message>
+ <message>
+ <location filename="main.cpp" line="129"/>
+ <source>Welcome to Mumble.</source>
+ <translation>Добро пожаловать в Mumble.</translation>
+ </message>
+</context>
+<context>
+ <name>Overlay</name>
+ <message>
+ <location filename="Overlay_win.cpp" line="363"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="366"/>
+ <source>Failed to load overlay library. This means either that:
+- the library (mumble_ol.dll) wasn't found in the directory you ran Mumble from
+- you're on an OS earlier than WinXP SP2
+- you do not have the August 2006 updated version of DX9.0c</source>
+ <translation>Невозможно загрузить библиотеку замещения. Это означает, что либо:
+- библиотека (mumble_ol.dll) не была найдена в папке, из которой вы запустили Mumble
+- Ваша ОС старше, чем WinXP SP2
+- у Вас нет обновленной в августе 2006 версии DX9.0c</translation>
+ </message>
+</context>
+<context>
+ <name>OverlayConfig</name>
+ <message>
+ <location filename="Overlay_win.cpp" line="43"/>
+ <source>Options</source>
+ <translation>Настройки</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="44"/>
+ <source>Position</source>
+ <translation>Позиция</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="45"/>
+ <source>Font</source>
+ <translation>Шрифт</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="52"/>
+ <source>Enable Overlay</source>
+ <translation>Включить замещение</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="54"/>
+ <source>Enable overlay.</source>
+ <translation>Включить замещение.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="60"/>
+ <source>This sets whether the overlay is enabled or not. This setting is only checked with D3D9 applications are started, so make sure Mumble is running and this option is on before you start the application.<br />Please note that if you start the application after starting Mumble, or if you disable the overlay while running, there is no safe way to restart the overlay without also restarting the application.</source>
+ <translation>Устанавливает, включить замещение или нет. Опция отмечена, если запущены приложения D3D9, так что удостоверьтесь, что Mumble запущен и эта опция включена перед стартом приложения.<br />Заметьте, что если вы запустите приложение после запуска Mumble, или вы отключите замещение во время действия программы, не будет безопасного способа заново включить замещение без перезапуска программы.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="65"/>
+ <source>No one</source>
+ <translation>Никого</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="66"/>
+ <source>Only talking</source>
+ <translation>Только разговаривающие</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="67"/>
+ <source>Everyone</source>
+ <translation>Всем</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="69"/>
+ <source>Show</source>
+ <translation>Показать</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="71"/>
+ <source>Who to show on the overlay</source>
+ <translation>Кого показывать при замещении</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="77"/>
+ <source><b>This sets who to show in the in-game overlay.</b><br />If many people are connected to the same channel, the overlay list might be very long. Use this to shorten it.<br /><i>No one</i> - Don't show anyone (but leave overlay running).<br /><i>Only talking</i> - Only show talking people.<br /><i>Everyone</i> - Show everyone.</source>
+ <translation><b>Определяет, кого показывать в игровом замещении.</b><br />Если много людей на одном канале, замещение может быть очень долгим. Используйте это, чтобы сократить его.<br /><i>Никого</i> -Не показывать никого (но оставить замещение включенным).<br /><i>Только говорящих</i> - Показывать только говорящих людей.<br /><i>Всех</i> - Показывать всех.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="81"/>
+ <source>Always Show Self</source>
+ <translation>Всегда показывать меня</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="83"/>
+ <source>Always show yourself on overlay.</source>
+ <translation>Всегда показывать себя в замещении.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="87"/>
+ <source>This sets whether to always show yourself or not. This setting is useful if you aren't showing everyone in the overlay, as then you would only see your own status if you were talking, which wouldn't let you see that you were deafened or muted.</source>
+ <translation>Устанавливает, всегда ли показывать себя. Настройка применима если вы не отображаете всех в замещении, поэтому тогда вы увидите собственный статус при разговоре, который вы не услышите, если в онемении или заглушены.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="95"/>
+ <source>Grow Left</source>
+ <translation>Расти влево</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="97"/>
+ <source>Let overlay grow to the left</source>
+ <translation>Позволяет замещению расти влево</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="100"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow to the left if needed.</source>
+ <translation>Замещение пытается остаться как можно меньшим и на выбранной позиции. Позволяет замещению расти влево при необходимости.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="102"/>
+ <source>Grow Right</source>
+ <translation>Расти вправо</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="104"/>
+ <source>Let overlay grow to the Right</source>
+ <translation>Позволяет замещению расти вправо</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="107"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow to the Right if needed.</source>
+ <translation>Замещение попытается оставться как можно меньшим и на установленой позиции. Позволяет замещению расти вправо при необходимости.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="109"/>
+ <source>Grow Up</source>
+ <translation>Расти вверх</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="111"/>
+ <source>Let overlay grow upwards</source>
+ <translation>Позволяет замещению расти вверх</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="114"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow to the top if needed.</source>
+ <translation>Замещение попытается оставться как можно меньшим и на установленой позиции. Позволяет замещению расти вверх при необходимости.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="116"/>
+ <source>Grow Down</source>
+ <translation>Расти вниз</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="118"/>
+ <source>Let overlay grow downwards</source>
+ <translation>Позволяет замещению расти вниз</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="121"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow towards the bottom if needed.</source>
+ <translation>Замещение попытается оставться как можно меньшим и на установленой позиции. Позволяет замещению расти вниз при необходимости.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="126"/>
+ <source>X-Position of Overlay</source>
+ <translation>Позиция Х замещения</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="127"/>
+ <source>This sets the relative X position of the overlay.</source>
+ <translation>Устанавливает относительную Х позицию замещения.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="135"/>
+ <source>Y-Position of Overlay</source>
+ <translation>Y-позиция замещения</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="136"/>
+ <source>This sets the relative Y position of the overlay.</source>
+ <translation>Устанавливает относительную Y - позицию замещения.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="161"/>
+ <source>Current Font</source>
+ <translation>Текущий шрифт</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="165"/>
+ <source>Set Font</source>
+ <translation>Установить шрифт</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="174"/>
+ <source>Maximum width of names.</source>
+ <translation>Максимальная длина имен.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="177"/>
+ <source>This sets the maximum width of names shown, relative to the height. If you set this to 100%, no line of text will be wider than it is high. At 500%, no line will be more than 5 times wider than its height, and so on.</source>
+ <translation>Устанавливает максимальную ширину отображаемых имен, относительно высоты. Если установить на 100%, ни одна строка в тексте не будет шире его высоты. На 500% ни одна строка не будет больше ширины в 5 раз, и так далее.</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="187"/>
+ <source>Maximum width</source>
+ <translation>Максимальная ширина</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="235"/>
+ <source>Change</source>
+ <translation>Изменить</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="197"/>
+ <source>Color for players</source>
+ <translation>Цвет для игроков</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="207"/>
+ <source>Color for talking players</source>
+ <translation>Цвет для разговаривающих игроков</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="217"/>
+ <source>Color for alt-talking players</source>
+ <translation>Цвета для усиленно говорящих игроков</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="227"/>
+ <source>Color for Channels</source>
+ <translation>Цвет для каналов</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="237"/>
+ <source>Color for active Channels</source>
+ <translation>Цвет для Активных каналов</translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="323"/>
+ <source>Overlay</source>
+ <translation>Замещение</translation>
+ </message>
+</context>
+<context>
+ <name>PlayerModel</name>
+ <message>
+ <location filename="PlayerModel.cpp" line="368"/>
+ <source>Name of player</source>
+ <translation>Имя игрока</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="368"/>
+ <source>Name of channel</source>
+ <translation>Имя канала</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="370"/>
+ <source>Player flags</source>
+ <translation>Флаги игрока</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="381"/>
+ <source>This is a player connected to the server. The icon to the left of the player indicates whether or not they are talking:<br /><img src=":/icons/talking_on.png" /> Talking<br /><img src=":/icons/talking_off.png" /> Not talking</source>
+ <translation>Этот игрок подключен к серверу. Иконка слева от игрока отображает, разговаривает он сейчас или нет:<br /><img src=":/icons/talking_on.png" />Говорит<br /><img src=":/icons/talking_off.png" />Не говорит</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="383"/>
+ <source>This is a channel on the server. Only players in the same channel can hear each other.</source>
+ <translation>Канал на сервере. Только игроки на одном канале могут слушать друг друга.</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="393"/>
+ <source>This shows the flags the player has on the server, if any:<br /><img src=":/icons/authenticated.png" />Authenticated user<br /><img src=":/icons/muted_self.png" />Muted (by self)<br /><img src=":/icons/muted_server.png" />Muted (by admin)<br /><img src=":/icons/deafened_self.png" />Deafened (by self)<br /><img src=":/icons/deafened_server.png" />Deafened (by admin)<br />A player muted by himself is probably just away, talking on the phone or something like that.<br />A player muted by an admin is probably also just away, and the noise the player is making was annoying enough that an admin muted him.</source>
+ <translation>Показывает флаги игрока на сервере,если любой:<br /><img src=":/icons/authenticated.png" />Зарегистрированный пользователь<br /><img src=":/icons/muted_self.png" />Заглушенный (собой)<br /><img src=":/icons/muted_server.png" />Заглушенный (администратором)<br /><img src=":/icons/deafened_self.png" />В онемении (собственном)<br /><img src=":/icons/deafened_server.png" />В онемении (администратора)<br />Пользователь,заглушенный собой, обычно отошел от компьютера, говорит по телефону, или что-то в этом роде.<br />Игрок, заглушенный администратором, возможно просто отошел, и шум игрока надоедал администратору,и он заглушил его.</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="410"/>
+ <source>Name</source>
+ <translation>Имя</translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="412"/>
+ <source>Flags</source>
+ <translation>Флаги</translation>
+ </message>
+</context>
+<context>
+ <name>PluginConfig</name>
+ <message>
+ <location filename="Plugins.cpp" line="59"/>
+ <source>Options</source>
+ <translation>Настройки</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="114"/>
+ <source>Plugins</source>
+ <translation>Плагины</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="66"/>
+ <source>Link to Game and Transmit Position</source>
+ <translation>Связаться с игрой и передать позицию</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="68"/>
+ <source>Enable plugins and transmit positional information</source>
+ <translation>Включить плагины и передать информацию о позиции</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="72"/>
+ <source>This enables plugins for supported games to fetch your in-game position and transmit that with each voice packet. This enables other players to hear your voice in-game from the direction your character is in relation to their own.</source>
+ <translation>Включает плагины для поддерживаемых игр, чтобы соответствовать вашей позиции в игре и передавать их с каждым голосовым пакетом. Позволяет другим игрокам слушать ваш голос в игре оттуда, откуда он расположен по отношению к ним.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="79"/>
+ <source>&Reload plugins</source>
+ <translation>&Перезагрузить плагины</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="81"/>
+ <source>Reloads all plugins</source>
+ <translation>Перезагрузить все плагины</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="83"/>
+ <source>This rescans and reloads plugins. Use this if you just added or changed a plugin to the plugins directory.</source>
+ <translation>Пересканирует и перезагружает плагины. Используйте, если вы только что добавили или изменили плагин в папке с плагинами.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="84"/>
+ <source>&About</source>
+ <translation>&О плагине</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="86"/>
+ <source>Information about plugin</source>
+ <translation>Информация о плагине</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="87"/>
+ <source>This shows a small information message about the plugin.</source>
+ <translation>Покажет небольшую справку о плагине.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="88"/>
+ <source>&Configure</source>
+ <translation>&Настроить</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="90"/>
+ <source>Show configuration page of plugin</source>
+ <translation>Показать страницу настроек плагина</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="91"/>
+ <source>This shows the configuration page of the plugin, if any.</source>
+ <translation>Показывает страницу настроек плагина, если есть.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="146"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="134"/>
+ <source>Plugin has no configure function.</source>
+ <translation>Плагин не имеет функции конфигурирования.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="146"/>
+ <source>Plugin has no about function.</source>
+ <translation>У плагина нет функции информации.</translation>
+ </message>
+</context>
+<context>
+ <name>Plugins</name>
+ <message>
+ <location filename="Plugins.cpp" line="243"/>
+ <source>Plugin %1 lost link.</source>
+ <translation>Плагин %1 потерял связь.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="244"/>
+ <source>%1 lost link.</source>
+ <translation>%1 потерял связь.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="285"/>
+ <source>Plugin %1 linked.</source>
+ <translation>Плагин %1 подключен.</translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="286"/>
+ <source>%1 linked.</source>
+ <translation>%1 подключен.</translation>
+ </message>
+</context>
+<context>
+ <name>VersionCheck</name>
+ <message>
+ <location filename="VersionCheck.cpp" line="75"/>
+ <source>Mumble</source>
+ <translation>Mumble</translation>
+ </message>
+ <message>
+ <location filename="VersionCheck.cpp" line="75"/>
+ <source>Mumble failed to retrieve version information from the SourceForge server.</source>
+ <translation>Mumble возвратил ошибку при запросе информации о версии с сервера SourceForge.</translation>
+ </message>
+</context>
+<context>
+ <name>XInputKeyWidget</name>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="63"/>
+ <source>Press Shortcut</source>
+ <translation>Нажмите ярлык</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/mumble/mumble_tr.ts b/src/mumble/mumble_tr.ts new file mode 100644 index 000000000..309c2161d --- /dev/null +++ b/src/mumble/mumble_tr.ts @@ -0,0 +1,2867 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1">
+<context>
+ <name>ACLEditor</name>
+ <message>
+ <location filename="ACLEditor.cpp" line="51"/>
+ <source>Active ACLs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="52"/>
+ <source>Context</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="53"/>
+ <source>User/Group</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="54"/>
+ <source>Permissions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="102"/>
+ <source>Group</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="57"/>
+ <source>Members</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="61"/>
+ <source>&Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="63"/>
+ <source>&Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="65"/>
+ <source>&Up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="67"/>
+ <source>&Down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="69"/>
+ <source>Inherit ACLs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="88"/>
+ <source>Applies to this channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="90"/>
+ <source>Applies to sub-channels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="109"/>
+ <source>User ID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="119"/>
+ <source>Deny</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="121"/>
+ <source>Allow</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="208"/>
+ <source>Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="189"/>
+ <source>Inherit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="166"/>
+ <source>Inheritable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="169"/>
+ <source>Inherited</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="206"/>
+ <source>Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="211"/>
+ <source>Add to Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="232"/>
+ <source>&Groups</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="233"/>
+ <source>&ACL</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="235"/>
+ <source>&OK</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="237"/>
+ <source>Accept changes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="240"/>
+ <source>This button will accept current groups/ACLs and send them to the server. Note that if you mistakenly remove write permission from yourself, the server will add it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="241"/>
+ <source>&Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="243"/>
+ <source>Reject changes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="245"/>
+ <source>This button will cancel all changes and closes the dialog without updating the ACLs or groups on the server.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="321"/>
+ <source>List of entries</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="323"/>
+ <source>This shows all the entries active on this channel. Entries inherited from parent channels will be show in italics.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="324"/>
+ <source>Inherit ACL of parent?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="326"/>
+ <source>This sets whether or not the ACL up the chain of parent channels are applied to this object. Only those entries that are marked in the parent as "Apply to sub-channels" will be inherited.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="327"/>
+ <source>Add new entry</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="328"/>
+ <source>This adds a new entry, initially set with no permissions and applying to all.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="329"/>
+ <source>Remove entry</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="330"/>
+ <source>This removes the currently selected entry.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="331"/>
+ <source>Move entry up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="334"/>
+ <source>This moves the entry up in the list. As entries are evaluated in order, this may change the effective permissions of users. You cannot move an entry above an inherited entry, if you really need that you'll have to duplicate the inherited entry.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="335"/>
+ <source>Move entry down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="337"/>
+ <source>This moves the entry down in the list. As entries are evaluated in order, this may change the effective permissions of users.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="338"/>
+ <source>Entry should apply to this channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="339"/>
+ <source>This makes the entry apply to this channel.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="340"/>
+ <source>Entry should apply to sub-channels.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="341"/>
+ <source>This makes the entry apply to sub-channels of this channel.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="342"/>
+ <source>Group this entry applies to</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="357"/>
+ <source>This controls which group of users this entry applies to.<br />Note that the group is evaluated in the context of the channel the entry is used in. For example, the default ACL on the Root channel gives <i>Write</i> permission to the <i>admin</i> group. This entry, if inherited by a channel, will give a user write privileges if he belongs to the <i>admin</i> group in that channel, even if he doesn't belong to the <i>admin</i> group in the channel where the ACL originated.<br />If a group name starts with a !, its membership is negated, and if it starts with a ~, it is evaluated in the channel the ACL was defined in, rather than the channel the ACL is active in. Order is important; <i>!~in</i> is valid, but <i>~!in</i> is not.<br />A few special predefined groups are:<br /><b>all</b> - Everyone will match.<br /><b>auth</b> - All authenticated users will match.<br /><b>sub,a,b,c</b> - User currently in a sub-channel minimum <i>a</i> common parents, and between <i>b</i> and <i>c</i> channels down the chain. See the website for more extensive documentation on this one.<br /><b>in</b> - Users currently in the channel will match (convenience for <i>sub,0,0,0</i>).<br /><b>out</b> - Users outside the channel will match (convenience for <i>!sub,0,0,0</i>).<br />Note that an entry applies to either a user or a group, not both.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="358"/>
+ <source>User this entry applies to</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="360"/>
+ <source>This controls which user this entry applies to. Just type in the user name and hit enter to query the server for a match.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="367"/>
+ <source>Allow %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="368"/>
+ <source>Deny %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="369"/>
+ <source>This grants the %1 privilege. If a privilege is both allowed and denied, it is denied.<br />%2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="370"/>
+ <source>This revokes the %1 privilege. If a privilege is both allowed and denied, it is denied.<br />%2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="374"/>
+ <source>List of groups</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="376"/>
+ <source>This is all the groups currently defined for the channel. To create a new group, just type in the name and press enter.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="377"/>
+ <source>Remove selected group</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="379"/>
+ <source>This removes the currently selected group. If the group was inherited, it will not be removed from the list, but all local information about the group will be cleared.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="380"/>
+ <source>Inherit group members from parent</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="382"/>
+ <source>This inherits all the members in the group from the parent, if the group is marked as <i>Inheritable</i> in the parent channel.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="383"/>
+ <source>Make group inheritable to sub-channels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="385"/>
+ <source>This makes this group inheritable to sub-channels. If the group is non-inheritable, sub-channels are still free to create a new group with the same name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="386"/>
+ <source>Group was inherited from parent channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="388"/>
+ <source>This indicates that the group was inherited from the parent channel. You cannot edit this flag, it's just for information.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="389"/>
+ <source>Add member to group</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="390"/>
+ <source>Type in the name of a player you wish to add to the group and press enter.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="391"/>
+ <source>Remove member from group</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="392"/>
+ <source>Type in the name of a player you wish to remove from the group and press enter.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ACLEditor.cpp" line="45"/>
+ <source>Mumble - Edit ACL for %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ASIOConfig</name>
+ <message>
+ <location filename="ASIOInput.cpp" line="113"/>
+ <source>Device selection</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="114"/>
+ <source>Capabilities</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="115"/>
+ <source>Channels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="116"/>
+ <source>Configure input channels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="121"/>
+ <source>This will configure the input channels for ASIO. Make sure you select at least one channel as microphone and speaker. <i>Microphone</i> should be where your microphone is attached, and <i>Speaker</i> should be a channel that samples "What you hear".<br />For example, on the Audigy 2 ZS, a good selection for Microphone would be "Mic L" while Speaker should be "Mix L" and "Mix R"</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="125"/>
+ <source>Device to use for microphone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="127"/>
+ <source>This chooses what device to query. You still need to actually query the device and select which channels to use.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="128"/>
+ <source>Device</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="132"/>
+ <source>Query selected device</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="135"/>
+ <source>This queries the selected device for channels. Be aware that many ASIO drivers are buggy to the extreme, and querying them might cause a crash of either the application or the system.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="139"/>
+ <source>Configure selected device</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="142"/>
+ <source>This configures the selected device. Be aware that many ASIO drivers are buggy to the extreme, and querying them might cause a crash of either the application or the system.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="157"/>
+ <source>Driver name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="162"/>
+ <source>Buffersize</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="178"/>
+ <source>Microphone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="208"/>
+ <source><-</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="205"/>
+ <source>-></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="200"/>
+ <source>Unused</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="222"/>
+ <source>Speakers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="318"/>
+ <source>%1 (ver %2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="322"/>
+ <source>%1 ms -> %2 ms (%3 ms resolution) %4Hz</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="324"/>
+ <source>%1 ms -> %2 ms (%3 ms resolution) %4Hz -- Unusable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="400"/>
+ <source>Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="396"/>
+ <source>ASIO Initialization failed: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="400"/>
+ <source>Failed to instantiate ASIO driver</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="437"/>
+ <source>ASIO</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="130"/>
+ <source>&Query</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="137"/>
+ <source>&Configure</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ASIOInput</name>
+ <message>
+ <location filename="ASIOInput.cpp" line="580"/>
+ <source>Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="512"/>
+ <source>You need to select at least one microphone and one speaker source to use ASIO. If you just need microphone sampling, use DirectSound.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ASIOInput.cpp" line="580"/>
+ <source>Opening selected ASIO device failed. No input will be done.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="About.cpp" line="60"/>
+ <source>&About Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="61"/>
+ <source>&License</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="63"/>
+ <source>OK</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="54"/>
+ <source><h3>Mumble v%1</h3><p>Copyright 2005-2006 Thorvald Natvig<br />slicer@users.sourceforge.net</p><p><b>A voice-chat utility for gamers</b></p><p><tt>http://mumble.sourceforge.net/</tt></p></source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AboutSpeexDialog</name>
+ <message>
+ <location filename="About.cpp" line="84"/>
+ <source><h3>About Speex</h3><p><tt>http://www.speex.org/</tt></p><p>This program uses Speex version %1</p><p>Speex is used for echo cancellation, noise<br />filtering, voice activity detection and speech<br />compression.</p></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="About.cpp" line="86"/>
+ <source>OK</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AudioConfigDialog</name>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="51"/>
+ <source>Interfaces</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="52"/>
+ <source>Transmission</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="53"/>
+ <source>Compression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="54"/>
+ <source>Jitter Buffer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="67"/>
+ <source>Input</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="70"/>
+ <source>Input method for audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="71"/>
+ <source><b>This is the input method to use for audio.</b><br />Most likely you want to use DirectSound.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="85"/>
+ <source>Output</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="88"/>
+ <source>Output method for audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="89"/>
+ <source><b>This is the output method to use for audio.</b><br />Most likely you want to use DirectSound.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="99"/>
+ <source>Continuous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="100"/>
+ <source>Voice Activity</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="101"/>
+ <source>Push To Talk</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="103"/>
+ <source>Transmit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="106"/>
+ <source>When to transmit your speech</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="110"/>
+ <source><b>This sets when speech should be transmitted.</b><br /><i>Continuous</i> - All the time<br /><i>Voice Activity</i> - When you are speaking clearly.<br /><i>Push To Talk</i> - When you hold down the hotkey set under <i>Shortcuts</i>.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="121"/>
+ <source>Voice Hold</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="128"/>
+ <source>How long to keep transmitting after silence</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="131"/>
+ <source><b>This selects how long after a perceived stop in speech transmission should continue.</b><br />Set this higher if your voice breaks up when you speak (seen by a rapidly blinking voice icon next to your name).<br />Only makes sense when used with Voice Activity transmission.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="144"/>
+ <source>Audio per packet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="153"/>
+ <source>How many audio frames to send per packet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="156"/>
+ <source><b>This selects how many audio frames should be put in one packet.</b><br />Increasing this will increase the latency of your voice, but will also reduce bandwidth requirements.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="161"/>
+ <source>Use TCP mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="163"/>
+ <source>Use TCP Compatibility mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="168"/>
+ <source><b>Enable TCP Compatibility mode</b>.<br />This will make Mumble use only TCP when communicating with the server. This will increase overhead and cause lost packets to produce noticeable pauses in communication, so this should only be used if you are unable to use the default (which uses UDP for voice and TCP for control).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="170"/>
+ <source>Automatic Reconnect</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="172"/>
+ <source>Reconnect when disconnected</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="176"/>
+ <source><b>Reconnect when disconnected</b>.<br />This will make Mumble try to automatically reconnect after 10 seconds if your server connection fails.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="184"/>
+ <source>Maximum bandwidth used for sent audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="189"/>
+ <source><b>This shows peak outgoing bandwidth used.</b><br />This shows the peak amount of bandwidth sent out from your machine. Audio bitrate is the maximum bitrate (as we use VBR) for the audio data alone. Position is the bitrate used for positional information. Overhead is our framing and the IP packet headers (IP and UDP is 90% of this overhead).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="191"/>
+ <source>Outgoing Bitrate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="209"/>
+ <source>Quality</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="216"/>
+ <source>Quality of compression (peak bandwidth)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="218"/>
+ <source><b>This sets the quality of compression.</b><br />This determines how much bandwidth Mumble is allowed to use for outgoing audio.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="231"/>
+ <source>Complexity</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="237"/>
+ <source>Complexity of compression (CPU)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="240"/>
+ <source><b>This sets the complexity of compression.</b><br />This determines how much CPU Mumble is allowed to use to increase transmitted voice quality. Settings above 5 give only marginal gain.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="253"/>
+ <source>Amp</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="259"/>
+ <source>Maximum amplification of input sound</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="270"/>
+ <source><b>Maximum amplification of input.</b><br />Mumble normalizes the input volume before compressing, and this sets how much it's allowed to amplify.<br />The actual level is continually updated based on your current speech pattern, but it will never go above the level specified here.<br />If the <i>Microphone loudness</i> 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.<br />Ideally, set it so <i>Microphone Loudness * Amplification Factor >= 100</i>, even when you're speaking really soft.<br /><br />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.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="288"/>
+ <source>Default Jitter Buffer</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="295"/>
+ <source>How long to prebuffer on first packet</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="302"/>
+ <source><b>This sets the default buffer size for the jitter buffer</b>.<br />All incoming audio is buffered, and the jitter buffer continually tries to push the buffer to the minimum sustainable by your network, so latency can be as low as possible. This sets the default buffer size to use on the first packet to arrive from a new speaker, or when a speaker using Voice Activity or Push-To-Talk just started talking again. If the start of sentences you hear is very jittery, increase this value.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="321"/>
+ <source>Basic Audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="369"/>
+ <source>%1 ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="350"/>
+ <source>%1s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioConfigDialog.cpp" line="418"/>
+ <source>%1kbit/s (Audio %2, Position %4, Overhead %3)</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AudioStats</name>
+ <message>
+ <location filename="AudioStats.cpp" line="342"/>
+ <source>Peak microphone level</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="347"/>
+ <source>Peak speaker level</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="352"/>
+ <source>Peak clean level</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="357"/>
+ <source>Microphone loudness</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="362"/>
+ <source>Mic Signal-To-Noise</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="367"/>
+ <source>Speech Probability</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="372"/>
+ <source>Audio bitrate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="432"/>
+ <source>Peak power in last frame</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="426"/>
+ <source>This shows the peak power in the last frame (20 ms), and is the same measurement as you would usually find displayed as "input power". Please disregard this and look at <b>Loudness</b> instead, which is much more steady and disregards outliers.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="431"/>
+ <source>This shows the peak power in the last frame (20 ms) of the speakers. 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.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="438"/>
+ <source>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.<br />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 players with echoes.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="439"/>
+ <source>How close the current input level is to ideal</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="446"/>
+ <source>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.<br /><b>Talk loud, as you would when you're upset over getting fragged by a noob.</b><br />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</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="447"/>
+ <source>Signal-To-Noise ratio from the microphone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="452"/>
+ <source>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.<br />If this value is below 1.0, there's more noise than voice in the signal, and so quality is reduced.<br />There is no upper limit to this value, but don't expect to see much above 40-50 without a sound studio.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="453"/>
+ <source>Probability of speech</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="458"/>
+ <source>This is the probability that the last frame (20 ms) was speech and not environment noise.<br />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'.<br />If this is in bold font, it means Mumble is currently transmitting (if you're connected).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="459"/>
+ <source>Bitrate of last frame</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="462"/>
+ <source>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. To adjust the peak bitrate, adjust <b>Compression Complexity</b> in the Settings dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="464"/>
+ <source>Power spectrum of input signal and noise estimate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="471"/>
+ <source>This shows the power spectrum of the current input signal (red line) and the current noise estimate (filled blue).<br />All amplitudes are multiplied by 30 to show the interesting parts (how much more signal than noise is present in each waveband).<br />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.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="474"/>
+ <source>Weights of the echo canceller</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="AudioStats.cpp" line="487"/>
+ <source>This shows the weights of the echo canceller, with time increasing downwards and frequency increasing to the right.<br />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.<br />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. <br />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.<br />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.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>BanEditor</name>
+ <message>
+ <location filename="BanEditor.cpp" line="42"/>
+ <source>&Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="43"/>
+ <source>&Update</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="44"/>
+ <source>&Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="69"/>
+ <source>&OK</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="71"/>
+ <source>Accept changes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="74"/>
+ <source>This button will accept current groups/ACLs and send them to the server. Note that if you mistakenly remove write permission from yourself, the server will add it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="75"/>
+ <source>&Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="77"/>
+ <source>Reject changes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="79"/>
+ <source>This button will cancels all changes and closes the dialog without updating the ACLs or groups on the server.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="BanEditor.cpp" line="37"/>
+ <source>Mumble - Edit Bans</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ChanACL</name>
+ <message>
+ <location filename="../ACL.cpp" line="129"/>
+ <source>W</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="131"/>
+ <source>T</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="133"/>
+ <source>E</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="135"/>
+ <source>S</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="137"/>
+ <source>A</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="139"/>
+ <source>M</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="141"/>
+ <source>K</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="143"/>
+ <source>C</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="145"/>
+ <source>L</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="155"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="157"/>
+ <source>Write</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="159"/>
+ <source>Traverse</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="161"/>
+ <source>Enter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="163"/>
+ <source>Speak</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="165"/>
+ <source>AltSpeak</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="167"/>
+ <source>Mute/Deafen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="169"/>
+ <source>Move/Kick</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="171"/>
+ <source>Make channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="173"/>
+ <source>Link channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="183"/>
+ <source>This represents no privileges.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="186"/>
+ <source>This represents total access to the channel, including the ability to change group and ACL information. This privilege implies all other privileges.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="190"/>
+ <source>This represents the permission to traverse the channel. If a user is denied this privilege, he will be unable to access this channel and any sub-channels in any way, regardless of other permissions in the sub-channels.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="193"/>
+ <source>This represents the permission to join the channel. If you have a hierarchical channel structure, you might want to give everyone Traverse, but restrict Enter in the root of your hierarchy.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="197"/>
+ <source>This represents the permission to speak in a channel. Users without this privilege will be suppressed by the server (seen as muted), and will be unable to speak until they are unmuted by someone with the appropriate privileges.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="201"/>
+ <source>This represents the permission to speak in a channel with flagged speech. This works exactly like the <i>speak</i> privilege, but applies to packets spoken with AltPushToTalk held down. This may be used to broadcast to a hierarchy of channels without linking.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="204"/>
+ <source>This represents the permission to mute and deafen other players. Once muted, a player will stay muted until he is unmuted by another privileged player or reconnects to the server.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="209"/>
+ <source>This represents the permission to move a player to another channel or kick him from the server. To actually move the player, either the moving player must have Move/Kick privileges in the destination channel, or the player must normally be allowed to enter the channel. Players with this privilege can move players into channels the target player normally wouldn't have permission to enter.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="212"/>
+ <source>This represents the permission to make sub-channels. The player making the sub-channel will be added to the admin group of the sub-channel.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../ACL.cpp" line="216"/>
+ <source>This represents the permission to link channels. Players in linked channels hear each other, as long as the speaking player has the <i>speak</i> privilege in the channel of the listener. You need the link privilege in both channels to create a link, but just in either channel to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ConfigDialog</name>
+ <message>
+ <location filename="ConfigDialog.cpp" line="57"/>
+ <source>Mumble Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="69"/>
+ <source>&OK</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="72"/>
+ <source>Accept changes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="74"/>
+ <source>This button will accept current settings and return to the application.<br />The settings will be stored to disk when you leave the application.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="75"/>
+ <source>&Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="77"/>
+ <source>Reject changes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="79"/>
+ <source>This button will reject all changes and return to the application.<br />The settings will be reset to the previous positions.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="80"/>
+ <source>&Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="82"/>
+ <source>Apply changes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConfigDialog.cpp" line="83"/>
+ <source>This button will immediately apply all changes.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ConnectDialog</name>
+ <message>
+ <location filename="ConnectDialog.cpp" line="65"/>
+ <source>&Name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="72"/>
+ <source>A&ddress</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="80"/>
+ <source>&Port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="87"/>
+ <source>&Username</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="95"/>
+ <source>&Password</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="101"/>
+ <source>&Connect</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="105"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="108"/>
+ <source>&Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="ConnectDialog.cpp" line="111"/>
+ <source>&Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>DXAudioInput</name>
+ <message>
+ <location filename="DXAudioInput.cpp" line="108"/>
+ <source>Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXAudioInput.cpp" line="103"/>
+ <source>Opening chosen DirectSound Input failed. No microphone capture will be done.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXAudioInput.cpp" line="108"/>
+ <source>Opening chosen DirectSound Input failed. Default device will be used.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>DXAudioOutput</name>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="341"/>
+ <source>Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="336"/>
+ <source>Opening chosen DirectSound Output failed. No audio will be heard.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXAudioOutput.cpp" line="341"/>
+ <source>Opening chosen DirectSound Output failed. Default device will be used.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>DXConfigDialog</name>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="61"/>
+ <source>Default DirectSound Voice Input</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="62"/>
+ <source>Default DirectSound Voice Output</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="84"/>
+ <source>Device selection</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="87"/>
+ <source>Device to use for microphone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="89"/>
+ <source>This sets the input device to use, which is where you have connected the microphone.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="90"/>
+ <source>Input</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="95"/>
+ <source>Device to use for speakers/headphones</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="97"/>
+ <source>This sets the output device to use, which is where you have connected your speakers or your headset.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="98"/>
+ <source>Output</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="105"/>
+ <source>Output Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="114"/>
+ <source>Output Delay</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="119"/>
+ <source>Amount of data to buffer for DirectSound</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="122"/>
+ <source>This sets the amount of data to prebuffer in the DirectSound buffer. Experiment with different values and set it to the lowest which doesn't cause rapid jitter in the sound.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="130"/>
+ <source>Positional Audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="134"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="135"/>
+ <source>Panning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="136"/>
+ <source>Light HRTF</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="137"/>
+ <source>Full HRTF</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="140"/>
+ <source>3D Sound Algorithm</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="148"/>
+ <source>This sets what 3D Sound algorithm to use.<br /><b>None</b> - Disable 3D Sound (least CPU).<br /><b>Panning</b> - Just use stereo panning (some CPU).<br /><b>Light/Full HRTF</b> - Head-Related Transfer Functions enabled. This may use a small amount of CPU.<br />Note that if you have a soundcard with <i>hardware</i> 3D processing, HRTF processing will be done on the soundcard and will use practically no processing power.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="149"/>
+ <source>Method</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="164"/>
+ <source>MinDistance</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="169"/>
+ <source>Minimum distance to player before sound decreases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="171"/>
+ <source>This sets the minimum distance for sound calculations. The volume of other players' speech will not decrease until they are at least this far away from you.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="181"/>
+ <source>MaxDistance</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="186"/>
+ <source>Maximum distance, beyond which sound won't decrease</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="188"/>
+ <source>This sets the maximum distance for sound calculations. When farther away than this, other players' sound volume will not decrease any more.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="198"/>
+ <source>RollOff</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="203"/>
+ <source>Factor for sound volume decrease</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="206"/>
+ <source>How fast should sound volume drop when passing beyond the minimum distance. The normal (1.0) is that sound volume halves each time the distance doubles. Increasing this value means sound volume drops faster, while decreasing it means it drops slower.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="226"/>
+ <source>DirectSound</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="255"/>
+ <source>%1m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="263"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="273"/>
+ <source>Players more than %1 meters away have %2% intensity</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="DXConfigDialog.cpp" line="245"/>
+ <source>%1ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>Database</name>
+ <message>
+ <location filename="Database.cpp" line="67"/>
+ <source>Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Database.cpp" line="67"/>
+ <source>Mumble failed to initialize a database in any
+of the possible locations.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>DirectInputKeyWidget</name>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="72"/>
+ <source>Press Shortcut</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>GlobalShortcutWinConfig</name>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="169"/>
+ <source>Shortcuts</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="135"/>
+ <source>Function</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="137"/>
+ <source>Shortcut</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="150"/>
+ <source>Shortcut bound to %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_win.cpp" line="153"/>
+ <source><b>This is the global shortcut bound to %1</b><br />Click this field and then the desired key/button combo to rebind. Double-click to clear.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>GlobalShortcutXConfig</name>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="156"/>
+ <source>Shortcuts</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="122"/>
+ <source>Function</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="124"/>
+ <source>Shortcut</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="137"/>
+ <source>Shortcut bound to %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="140"/>
+ <source><b>This is the global shortcut bound to %1</b><br />Click this field and then the desired key combo to rebind. Double-click to clear.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>Log</name>
+ <message>
+ <location filename="Log.cpp" line="219"/>
+ <source>[%2] %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="146"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="147"/>
+ <source>Critical</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="148"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="149"/>
+ <source>Information</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="150"/>
+ <source>Server Connected</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="151"/>
+ <source>Server Disconnected</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="152"/>
+ <source>Player Joined Server</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="153"/>
+ <source>Player Left Server</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="154"/>
+ <source>Player kicked (you or by you)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="155"/>
+ <source>Player kicked</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="156"/>
+ <source>You self-muted/deafened</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="157"/>
+ <source>Other self-muted/deafened</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="158"/>
+ <source>Player muted (you)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="159"/>
+ <source>Player muted (by you)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="160"/>
+ <source>Player muted (other)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="161"/>
+ <source>Player Joined Channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="162"/>
+ <source>Player Left Channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="164"/>
+ <source>Permission Denied</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>LogConfig</name>
+ <message>
+ <location filename="Log.cpp" line="113"/>
+ <source>Messages</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="56"/>
+ <source>Console</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="58"/>
+ <source>TTS</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="72"/>
+ <source>Enable console for %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="78"/>
+ <source>Enable Text-To-Speech for %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="85"/>
+ <source>Text To Speech</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="88"/>
+ <source>Volume</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="96"/>
+ <source>Volume of Text-To-Speech Engine</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Log.cpp" line="97"/>
+ <source><b>This is the volume used for the speech synthesis.</b></source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>LookConfig</name>
+ <message>
+ <location filename="LookConfig.cpp" line="67"/>
+ <source>Language</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="51"/>
+ <source>Look and Feel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="82"/>
+ <source>System default</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="69"/>
+ <source>Language to use (requires restart)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="70"/>
+ <source><b>This sets which language Mumble should use.</b><br />You have to restart Mumble to use the new language.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="89"/>
+ <source>Style</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="91"/>
+ <source>Basic widget style</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="92"/>
+ <source><b>This sets the basic look and feel to use.</b></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="97"/>
+ <source>Skin</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="101"/>
+ <source>Skin file to use</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="102"/>
+ <source><b>This sets which skin Mumble should use.</b><br />The skin is a style file applied on top of the basic widget style.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="109"/>
+ <source>Use Horizontal Splitter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="111"/>
+ <source>Use horizontal or vertical splitter for the main window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="112"/>
+ <source><b>This sets whether the split in the main window is horizontal or vertical.</b></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="127"/>
+ <source>User Interface</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="99"/>
+ <source>...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="LookConfig.cpp" line="171"/>
+ <source>Choose skin file</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="MainWindow.cpp" line="53"/>
+ <source>Root</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="73"/>
+ <source>&Connect</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="74"/>
+ <source>Open the server connection dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="75"/>
+ <source>Shows a dialog of registered servers, and also allows quick connect.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="77"/>
+ <source>&Disconnect</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="78"/>
+ <source>Disconnect from server</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="79"/>
+ <source>Disconnects you from the server.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="82"/>
+ <source>&Ban lists</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="83"/>
+ <source>Edit ban lists on server</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="84"/>
+ <source>This lets you edit the server-side IP ban lists.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="88"/>
+ <source>&Kick</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="90"/>
+ <source>Kick player (with reason)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="91"/>
+ <source>Kick selected player off server. You'll be asked to specify a reason.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="92"/>
+ <source>&Ban</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="94"/>
+ <source>Kick and ban player (with reason)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="95"/>
+ <source>Kick and ban selected player from server. You'll be asked to specify a reason.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="96"/>
+ <source>&Mute</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="109"/>
+ <source>Mute player</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="100"/>
+ <source>Mute or unmute player on server. Unmuting a deafened player will also undeafen them.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="101"/>
+ <source>&Deafen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="104"/>
+ <source>Deafen player</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="105"/>
+ <source>Deafen or undeafen player on server. Deafening a player will also mute them.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="106"/>
+ <source>&Local Mute</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="110"/>
+ <source>Mute or unmute player locally.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="112"/>
+ <source>&Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="114"/>
+ <source>Add new channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="115"/>
+ <source>This adds a new sub-channel to the currently selected channel.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="116"/>
+ <source>&Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="118"/>
+ <source>Remove channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="119"/>
+ <source>This removes a channel and all sub-channels.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="120"/>
+ <source>&Edit ACL</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="122"/>
+ <source>Edit Groups and ACL for channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="123"/>
+ <source>This opens the Group and ACL dialog for the channel, to control permissions.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="124"/>
+ <source>&Link</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="126"/>
+ <source>Link your channel to another channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="129"/>
+ <source>This links your current channel to the selected channel. If they have permission to speak in the other channel, players can now hear each other. This is a permanent link, and will last until manually unlinked or the server is restarted. Please see the shortcuts for push-to-link.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="167"/>
+ <source>&Unlink</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="132"/>
+ <source>Unlink your channel from another channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="133"/>
+ <source>This unlinks your current channel from the selected channel.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="134"/>
+ <source>Unlink &All</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="136"/>
+ <source>Unlinks your channel from all linked channels.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="137"/>
+ <source>This unlinks your current channel (not the selected one) from all linked channels.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="139"/>
+ <source>&Reset</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="141"/>
+ <source>Reset audio preprocessor</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="144"/>
+ <source>This will reset the audio preprocessor, including noise cancellation, automatic gain and voice activity detection. If something suddenly worsens the audio environment (like dropping the microphone) and it was temporary, use this to avoid having to wait for the preprocessor to readjust.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="145"/>
+ <source>&Mute Self</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="149"/>
+ <source>Mute yourself</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="150"/>
+ <source>Mute or unmute yourself. When muted, you will not send any data to the server. Unmuting while deafened will also undeafen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="151"/>
+ <source>&Deafen Self</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="155"/>
+ <source>Deafen yourself</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="156"/>
+ <source>Deafen or undeafen yourself. When deafened, you will not hear anything. Deafening yourself will also mute.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="157"/>
+ <source>&Text-To-Speech</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="161"/>
+ <source>Toggle Text-To-Speech</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="162"/>
+ <source>Enable or disable the text-to-speech engine. Only messages enabled for TTS in the Configuration dialog will actually be spoken.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="163"/>
+ <source>S&tatistics</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="165"/>
+ <source>Display audio statistics</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="166"/>
+ <source>Pops up a small dialog with information about your current audio input.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="169"/>
+ <source>Forcibly unlink plugin</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="171"/>
+ <source>This forces the current plugin to unlink, which is handy if it is reading completely wrong data.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="173"/>
+ <source>&Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="175"/>
+ <source>Configure Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="176"/>
+ <source>Allows you to change most settings for Mumble.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="178"/>
+ <source>&What's This?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="180"/>
+ <source>Enter What's This? mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="182"/>
+ <source>Click this to enter "What's This?" mode. Your cursor will turn into a question mark. Click on any button, menu choice or area to show a description of what it is.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="183"/>
+ <source>&About</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="185"/>
+ <source>Information about Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="186"/>
+ <source>Shows a small dialog with information and license for Mumble.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="187"/>
+ <source>About &Speex</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="189"/>
+ <source>Information about Speex</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="190"/>
+ <source>Shows a small dialog with information about Speex.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="191"/>
+ <source>&About Qt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="193"/>
+ <source>Information about Qt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="194"/>
+ <source>Shows a small dialog with information about Qt.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="195"/>
+ <source>Check for &Updates</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="197"/>
+ <source>Check for new version of Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="199"/>
+ <source>Connects to the Mumble webpage to check if a new version is available, and notifies you with an appropriate download URL if this is the case.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="203"/>
+ <source>Mumble -- %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="220"/>
+ <source>Log of messages</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="222"/>
+ <source>This shows all recent activity. Connecting to servers, errors and information messages all show up here.<br />To configure exactly which messages show up here, use the <b>Settings</b> command from the menu.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="224"/>
+ <source>&Server</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="225"/>
+ <source>&Player</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="226"/>
+ <source>&Channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="227"/>
+ <source>&Audio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="228"/>
+ <source>C&onfigure</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="229"/>
+ <source>&Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="503"/>
+ <source>Kicking player %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="519"/>
+ <source>Enter reason</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="519"/>
+ <source>Banning player %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="587"/>
+ <source>Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="571"/>
+ <source>Channel Name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="664"/>
+ <source>Unmuted and undeafened.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="666"/>
+ <source>Unmuted.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="668"/>
+ <source>Muted.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="683"/>
+ <source>Muted and deafened.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="685"/>
+ <source>Deafened.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="687"/>
+ <source>Undeafened.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="737"/>
+ <source>About Qt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="813"/>
+ <source>Joining %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="839"/>
+ <source>Connected to server.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="872"/>
+ <source>Server connection failed: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="878"/>
+ <source>Disconnected from server.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="884"/>
+ <source>Reconnecting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="917"/>
+ <source>Joined server: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="932"/>
+ <source>Left server: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="967"/>
+ <source>%1 is now muted and deafened.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="969"/>
+ <source>%1 is now muted.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="971"/>
+ <source>%1 is now unmuted.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1002"/>
+ <source>server</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="987"/>
+ <source>You were muted by %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="987"/>
+ <source>You were unmuted by %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="989"/>
+ <source>%1 muted by %2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="989"/>
+ <source>%1 unmuted by %2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1005"/>
+ <source>You were deafened by %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1005"/>
+ <source>You were undeafened by %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1007"/>
+ <source>%1 deafened by %2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1007"/>
+ <source>%1 undeafened by %2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1014"/>
+ <source>You were kicked from the server by %1: %2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1018"/>
+ <source>%3 was kicked from the server by %1: %2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1026"/>
+ <source>You were kicked and banned from the server by %1: %2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1030"/>
+ <source>%3 was kicked and banned from the server by %1: %2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1049"/>
+ <source>%1 left channel.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1051"/>
+ <source>%1 moved out by %2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1058"/>
+ <source>%1 entered channel.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1060"/>
+ <source>%1 moved in by %2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1112"/>
+ <source>Server connection rejected: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="1117"/>
+ <source>Denied: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="main.cpp" line="129"/>
+ <source>Welcome to Mumble.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="284"/>
+ <source>Push-to-Talk</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="287"/>
+ <source>Reset Audio Processor</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="291"/>
+ <source>Toggle Mute Self</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="295"/>
+ <source>Toggle Deafen Self</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="303"/>
+ <source>Force Center Position</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="308"/>
+ <source>Chan Parent</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="313"/>
+ <source>Chan Sub#%1</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="318"/>
+ <source>Chan All Subs</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="322"/>
+ <source>Push-to-Mute</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="325"/>
+ <source>Join Channel</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="329"/>
+ <source>Toggle Overlay</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="334"/>
+ <source>Alt Push-to-Talk</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="299"/>
+ <source>Unlink Plugin</source>
+ <comment>Global Shortcut</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="MainWindow.cpp" line="587"/>
+ <source>Are you sure you want to delete %1 and all its sub-channels?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>Overlay</name>
+ <message>
+ <location filename="Overlay_win.cpp" line="363"/>
+ <source>Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="366"/>
+ <source>Failed to load overlay library. This means either that:
+- the library (mumble_ol.dll) wasn't found in the directory you ran Mumble from
+- you're on an OS earlier than WinXP SP2
+- you do not have the August 2006 updated version of DX9.0c</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>OverlayConfig</name>
+ <message>
+ <location filename="Overlay_win.cpp" line="43"/>
+ <source>Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="44"/>
+ <source>Position</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="45"/>
+ <source>Font</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="52"/>
+ <source>Enable Overlay</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="54"/>
+ <source>Enable overlay.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="60"/>
+ <source>This sets whether the overlay is enabled or not. This setting is only checked with D3D9 applications are started, so make sure Mumble is running and this option is on before you start the application.<br />Please note that if you start the application after starting Mumble, or if you disable the overlay while running, there is no safe way to restart the overlay without also restarting the application.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="65"/>
+ <source>No one</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="66"/>
+ <source>Only talking</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="67"/>
+ <source>Everyone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="69"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="71"/>
+ <source>Who to show on the overlay</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="77"/>
+ <source><b>This sets who to show in the in-game overlay.</b><br />If many people are connected to the same channel, the overlay list might be very long. Use this to shorten it.<br /><i>No one</i> - Don't show anyone (but leave overlay running).<br /><i>Only talking</i> - Only show talking people.<br /><i>Everyone</i> - Show everyone.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="81"/>
+ <source>Always Show Self</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="83"/>
+ <source>Always show yourself on overlay.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="87"/>
+ <source>This sets whether to always show yourself or not. This setting is useful if you aren't showing everyone in the overlay, as then you would only see your own status if you were talking, which wouldn't let you see that you were deafened or muted.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="95"/>
+ <source>Grow Left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="97"/>
+ <source>Let overlay grow to the left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="100"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow to the left if needed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="102"/>
+ <source>Grow Right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="104"/>
+ <source>Let overlay grow to the Right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="107"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow to the Right if needed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="109"/>
+ <source>Grow Up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="111"/>
+ <source>Let overlay grow upwards</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="114"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow to the top if needed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="116"/>
+ <source>Grow Down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="118"/>
+ <source>Let overlay grow downwards</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="121"/>
+ <source>The overlay tries to stay as small as possible and at the position you have selected. This allows the overlay to grow towards the bottom if needed.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="126"/>
+ <source>X-Position of Overlay</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="127"/>
+ <source>This sets the relative X position of the overlay.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="135"/>
+ <source>Y-Position of Overlay</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="136"/>
+ <source>This sets the relative Y position of the overlay.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="161"/>
+ <source>Current Font</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="165"/>
+ <source>Set Font</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="174"/>
+ <source>Maximum width of names.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="177"/>
+ <source>This sets the maximum width of names shown, relative to the height. If you set this to 100%, no line of text will be wider than it is high. At 500%, no line will be more than 5 times wider than its height, and so on.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="187"/>
+ <source>Maximum width</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="235"/>
+ <source>Change</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="197"/>
+ <source>Color for players</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="207"/>
+ <source>Color for talking players</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="217"/>
+ <source>Color for alt-talking players</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="227"/>
+ <source>Color for Channels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="237"/>
+ <source>Color for active Channels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Overlay_win.cpp" line="323"/>
+ <source>Overlay</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PlayerModel</name>
+ <message>
+ <location filename="PlayerModel.cpp" line="368"/>
+ <source>Name of player</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="368"/>
+ <source>Name of channel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="370"/>
+ <source>Player flags</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="381"/>
+ <source>This is a player connected to the server. The icon to the left of the player indicates whether or not they are talking:<br /><img src=":/icons/talking_on.png" /> Talking<br /><img src=":/icons/talking_off.png" /> Not talking</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="383"/>
+ <source>This is a channel on the server. Only players in the same channel can hear each other.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="393"/>
+ <source>This shows the flags the player has on the server, if any:<br /><img src=":/icons/authenticated.png" />Authenticated user<br /><img src=":/icons/muted_self.png" />Muted (by self)<br /><img src=":/icons/muted_server.png" />Muted (by admin)<br /><img src=":/icons/deafened_self.png" />Deafened (by self)<br /><img src=":/icons/deafened_server.png" />Deafened (by admin)<br />A player muted by himself is probably just away, talking on the phone or something like that.<br />A player muted by an admin is probably also just away, and the noise the player is making was annoying enough that an admin muted him.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="410"/>
+ <source>Name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="PlayerModel.cpp" line="412"/>
+ <source>Flags</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginConfig</name>
+ <message>
+ <location filename="Plugins.cpp" line="59"/>
+ <source>Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="114"/>
+ <source>Plugins</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="68"/>
+ <source>Enable plugins and transmit positional information</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="72"/>
+ <source>This enables plugins for supported games to fetch your in-game position and transmit that with each voice packet. This enables other players to hear your voice in-game from the direction your character is in relation to their own.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="81"/>
+ <source>Reloads all plugins</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="83"/>
+ <source>This rescans and reloads plugins. Use this if you just added or changed a plugin to the plugins directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="86"/>
+ <source>Information about plugin</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="87"/>
+ <source>This shows a small information message about the plugin.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="90"/>
+ <source>Show configuration page of plugin</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="91"/>
+ <source>This shows the configuration page of the plugin, if any.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="146"/>
+ <source>Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="134"/>
+ <source>Plugin has no configure function.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="66"/>
+ <source>Link to Game and Transmit Position</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="79"/>
+ <source>&Reload plugins</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="84"/>
+ <source>&About</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="88"/>
+ <source>&Configure</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="146"/>
+ <source>Plugin has no about function.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>Plugins</name>
+ <message>
+ <location filename="Plugins.cpp" line="243"/>
+ <source>Plugin %1 lost link.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="244"/>
+ <source>%1 lost link.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="285"/>
+ <source>Plugin %1 linked.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="Plugins.cpp" line="286"/>
+ <source>%1 linked.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionCheck</name>
+ <message>
+ <location filename="VersionCheck.cpp" line="75"/>
+ <source>Mumble</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="VersionCheck.cpp" line="75"/>
+ <source>Mumble failed to retrieve version information from the SourceForge server.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>XInputKeyWidget</name>
+ <message>
+ <location filename="GlobalShortcut_unix.cpp" line="63"/>
+ <source>Press Shortcut</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/src/mumble/murmur_pch.h b/src/mumble/murmur_pch.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/mumble/murmur_pch.h diff --git a/src/mumble/os_win.cpp b/src/mumble/os_win.cpp new file mode 100644 index 000000000..261e091ff --- /dev/null +++ b/src/mumble/os_win.cpp @@ -0,0 +1,143 @@ +/* Copyright (C) 2005-2006, 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 <windows.h>
+#include <tlhelp32.h>
+
+static FILE *fConsole;
+static void mumbleMessageOutput(QtMsgType type, const char *msg)
+{
+ char c;
+ switch (type) {
+ case QtDebugMsg:
+ c='D';
+ break;
+ case QtWarningMsg:
+ c='W';
+ break;
+ case QtFatalMsg:
+ c='F';
+ break;
+ default:
+ c='X';
+ }
+ fprintf(fConsole, "<%c>%s %s\n", c, qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz")), msg);
+ fflush(fConsole);
+}
+
+#define ER ExceptionInfo->ExceptionRecord
+#define CT ExceptionInfo->ContextRecord
+static LONG WINAPI MumbleUnhandledExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo) {
+ EXCEPTION_RECORD *er = ExceptionInfo->ExceptionRecord;
+ CONTEXT *ctx = ExceptionInfo->ContextRecord;
+
+ qWarning("=============================================================================");
+ qWarning("Mumble crash report. Cut and paste this entire report as well as the previos");
+ qWarning("10 seconds (see the timestamps). Build " __DATE__ " " __TIME__);
+ qWarning("=============================================================================");
+
+ while (er) {
+ qWarning("Unhandled exception. Code 0x%08lx, Flags 0x%08lx, Address %p.",er->ExceptionCode,er->ExceptionFlags,er->ExceptionAddress);
+
+ MODULEENTRY32 me;
+ me.dwSize = sizeof(me);
+ HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
+ if (hSnap != INVALID_HANDLE_VALUE) {
+ bool ok = Module32First(hSnap, &me);
+
+ while (ok) {
+ BYTE *start = me.modBaseAddr;
+ BYTE *stop = me.modBaseAddr + me.modBaseSize;
+ if ((er->ExceptionAddress >= start) && (er->ExceptionAddress <= stop)) {
+ qWarning("Modulematch: %ls (%p %08x)", me.szModule, start, static_cast<BYTE *>(er->ExceptionAddress) - start);
+ }
+ ok = Module32Next(hSnap, &me);
+ }
+ CloseHandle(hSnap);
+ }
+
+ for(unsigned int i=0;i<er->NumberParameters;i++) {
+ qWarning("Parameter %4d: %08lx", i, er->ExceptionInformation[i]);
+ }
+ if (er->ExceptionRecord)
+ qWarning("Chaining record...");
+ er = er->ExceptionRecord;
+ }
+
+ qWarning("EAX %08lx EBX %08lx ECX %08lx EDX %08lx",ctx->Eax,ctx->Ebx,ctx->Ecx,ctx->Edx);
+ qWarning("ESI %08lx EDI %08lx EBP %08lx ESP %08lx",ctx->Esi,ctx->Edi,ctx->Ebp,ctx->Esp);
+
+ DWORD Sp=ctx->Esp;
+ DWORD Bp=ctx->Ebp;
+
+ DWORD start,stop;
+ start = Sp;
+ stop = Bp + 0x40;
+ if ((stop < start) || (stop-start >512))
+ stop = start + 512;
+ for(Sp=stop;Sp>=start;Sp-=sizeof(DWORD)) {
+ DWORD val[4] = {0,0,0,0};
+ DWORD nbytes = sizeof(DWORD)*4;
+ if (ReadProcessMemory(GetCurrentProcess(), reinterpret_cast<const void *>(Sp), &val[0],sizeof(DWORD)*4, &nbytes))
+ qWarning("%08lx[%04lx][%08lx] %08lx %08lx %08lx %08lx", Sp, Sp-ctx->Esp,Sp-Bp, val[0],val[1],val[2],val[3]);
+ else
+ break;
+ }
+
+ qWarning("=============================================================================");
+ qWarning("Crash report ends");
+ qWarning("=============================================================================");
+
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+void os_init() {
+#define cpuid(func,ax,bx,cx,dx) __asm__ __volatile__ ("cpuid": "=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (func));
+#define MMXSSE 0x02800000
+ unsigned int ax, bx, cx, dx;
+ cpuid(1,ax,bx,cx,dx);
+ if ((dx & MMXSSE) != MMXSSE) {
+ ::MessageBoxA(NULL, "Mumble requires a SSE capable processor (Pentium 3 / Ahtlon-XP)", "Mumble", MB_OK | MB_ICONERROR);
+ exit(0);
+ }
+
+
+#ifdef QT_NO_DEBUG
+ fConsole=fopen("Console.txt", "a+");
+ if (fConsole)
+ qInstallMsgHandler(mumbleMessageOutput);
+ SetUnhandledExceptionFilter(MumbleUnhandledExceptionFilter);
+
+ // Increase our priority class to live alongside games.
+ if (!SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS))
+ qWarning("Application: Failed to set priority!");
+#endif
+}
|