Merge branch 'master' into classic-channels
|
@ -299,6 +299,7 @@ For example, memory_db would be set by adding `memory_db = true` under the line
|
||||||
| `save_state` | boolean | true | save the state of the gui (active channels, tabs, expanded channels) |
|
| `save_state` | boolean | true | save the state of the gui (active channels, tabs, expanded channels) |
|
||||||
| `alt_menu` | boolean | false | keep the menu hidden unless revealed with alt key |
|
| `alt_menu` | boolean | false | keep the menu hidden unless revealed with alt key |
|
||||||
| `hide_to_tray` | boolean | false | hide abaddon to the system tray on window close |
|
| `hide_to_tray` | boolean | false | hide abaddon to the system tray on window close |
|
||||||
|
| `show_deleted_indicator` | boolean | true | show \[deleted\] indicator next to deleted messages instead of actually deleting the message |
|
||||||
| `font_scale` | double | | scale font rendering. 1 is unchanged |
|
| `font_scale` | double | | scale font rendering. 1 is unchanged |
|
||||||
|
|
||||||
#### style
|
#### style
|
||||||
|
@ -324,7 +325,13 @@ For example, memory_db would be set by adding `memory_db = true` under the line
|
||||||
|
|
||||||
| Setting | Type | Default | Description |
|
| Setting | Type | Default | Description |
|
||||||
|---------|--------|------------------------------------|------------------------------------------------------------|
|
|---------|--------|------------------------------------|------------------------------------------------------------|
|
||||||
| vad | string | rnnoise if enabled, gate otherwise | Method used for voice activity detection. Changeable in UI |
|
| `vad` | string | rnnoise if enabled, gate otherwise | Method used for voice activity detection. Changeable in UI |
|
||||||
|
|
||||||
|
#### windows
|
||||||
|
|
||||||
|
| Setting | Type | Default | Description |
|
||||||
|
|---------------|---------|---------|-------------------------|
|
||||||
|
| `hideconsole` | boolean | true | Hide console on startup |
|
||||||
|
|
||||||
### Environment variables
|
### Environment variables
|
||||||
|
|
||||||
|
|
BIN
res/res/crunchyroll.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
res/res/e1e96d89e192de1997f73730db26e94f.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
res/res/ebay.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
res/res/instagram.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
res/res/paypal.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
res/res/playstation.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
res/res/riotgames.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
res/res/tiktok.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 5.1 KiB |
|
@ -419,13 +419,23 @@ void AudioManager::OnCapturedPCM(const int16_t *pcm, ma_uint32 frames) {
|
||||||
if (m_opus_buffer == nullptr || !m_should_capture) return;
|
if (m_opus_buffer == nullptr || !m_should_capture) return;
|
||||||
|
|
||||||
const double gain = m_capture_gain;
|
const double gain = m_capture_gain;
|
||||||
// i have a suspicion i can cast the const away... but i wont
|
|
||||||
std::vector<int16_t> new_pcm(pcm, pcm + frames * 2);
|
std::vector<int16_t> new_pcm(pcm, pcm + frames * 2);
|
||||||
for (auto &val : new_pcm) {
|
for (auto &val : new_pcm) {
|
||||||
const int32_t unclamped = static_cast<int32_t>(val * gain);
|
const int32_t unclamped = static_cast<int32_t>(val * gain);
|
||||||
val = std::clamp(unclamped, INT16_MIN, INT16_MAX);
|
val = std::clamp(unclamped, INT16_MIN, INT16_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_mix_mono) {
|
||||||
|
for (size_t i = 0; i < frames * 2; i += 2) {
|
||||||
|
const int sample_L = new_pcm[i];
|
||||||
|
const int sample_R = new_pcm[i + 1];
|
||||||
|
const int16_t mixed = static_cast<int16_t>((sample_L + sample_R) / 2);
|
||||||
|
new_pcm[i] = mixed;
|
||||||
|
new_pcm[i + 1] = mixed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UpdateCaptureVolume(new_pcm.data(), frames);
|
UpdateCaptureVolume(new_pcm.data(), frames);
|
||||||
|
|
||||||
static std::array<float, 480> denoised_L;
|
static std::array<float, 480> denoised_L;
|
||||||
|
@ -629,6 +639,14 @@ bool AudioManager::GetSuppressNoise() const {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void AudioManager::SetMixMono(bool value) {
|
||||||
|
m_mix_mono = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioManager::GetMixMono() const {
|
||||||
|
return m_mix_mono;
|
||||||
|
}
|
||||||
|
|
||||||
AudioManager::type_signal_opus_packet AudioManager::signal_opus_packet() {
|
AudioManager::type_signal_opus_packet AudioManager::signal_opus_packet() {
|
||||||
return m_signal_opus_packet;
|
return m_signal_opus_packet;
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,9 @@ public:
|
||||||
bool GetSuppressNoise() const;
|
bool GetSuppressNoise() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void SetMixMono(bool value);
|
||||||
|
bool GetMixMono() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnCapturedPCM(const int16_t *pcm, ma_uint32 frames);
|
void OnCapturedPCM(const int16_t *pcm, ma_uint32 frames);
|
||||||
|
|
||||||
|
@ -144,6 +147,7 @@ private:
|
||||||
std::atomic<double> m_prob_threshold = 0.5;
|
std::atomic<double> m_prob_threshold = 0.5;
|
||||||
std::atomic<float> m_vad_prob = 0.0;
|
std::atomic<float> m_vad_prob = 0.0;
|
||||||
std::atomic<bool> m_enable_noise_suppression = false;
|
std::atomic<bool> m_enable_noise_suppression = false;
|
||||||
|
std::atomic<bool> m_mix_mono = false;
|
||||||
|
|
||||||
std::unordered_set<uint32_t> m_muted_ssrcs;
|
std::unordered_set<uint32_t> m_muted_ssrcs;
|
||||||
std::unordered_map<uint32_t, double> m_volume_ssrc;
|
std::unordered_map<uint32_t, double> m_volume_ssrc;
|
||||||
|
|
|
@ -187,8 +187,15 @@ void ChatList::DeleteMessage(Snowflake id) {
|
||||||
if (widget == m_id_to_widget.end()) return;
|
if (widget == m_id_to_widget.end()) return;
|
||||||
|
|
||||||
auto *x = dynamic_cast<ChatMessageItemContainer *>(widget->second);
|
auto *x = dynamic_cast<ChatMessageItemContainer *>(widget->second);
|
||||||
if (x != nullptr)
|
|
||||||
x->UpdateAttributes();
|
if (x != nullptr) {
|
||||||
|
if (Abaddon::Get().GetSettings().ShowDeletedIndicator) {
|
||||||
|
x->UpdateAttributes();
|
||||||
|
} else {
|
||||||
|
RemoveMessageAndHeader(x);
|
||||||
|
m_id_to_widget.erase(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatList::RefetchMessage(Snowflake id) {
|
void ChatList::RefetchMessage(Snowflake id) {
|
||||||
|
|
|
@ -1057,10 +1057,9 @@ void ChatMessageHeader::UpdateName() {
|
||||||
const auto chan = discord.GetChannel(ChannelID);
|
const auto chan = discord.GetChannel(ChannelID);
|
||||||
bool is_guild = chan.has_value() && chan->GuildID.has_value();
|
bool is_guild = chan.has_value() && chan->GuildID.has_value();
|
||||||
if (is_guild) {
|
if (is_guild) {
|
||||||
const auto member = discord.GetMember(UserID, *chan->GuildID);
|
|
||||||
const auto role_id = discord.GetMemberHoistedRole(*chan->GuildID, UserID, true);
|
const auto role_id = discord.GetMemberHoistedRole(*chan->GuildID, UserID, true);
|
||||||
const auto role = discord.GetRole(role_id);
|
const auto role = discord.GetRole(role_id);
|
||||||
const auto name = GetEscapedDisplayName(*user, member);
|
const auto name = user->GetDisplayNameEscaped(*chan->GuildID);
|
||||||
|
|
||||||
std::string md;
|
std::string md;
|
||||||
if (role.has_value())
|
if (role.has_value())
|
||||||
|
@ -1090,13 +1089,6 @@ void ChatMessageHeader::AttachUserMenuHandler(Gtk::Widget &widget) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Glib::ustring ChatMessageHeader::GetEscapedDisplayName(const UserData &user, const std::optional<GuildMember> &member) {
|
|
||||||
if (member.has_value() && !member->Nickname.empty())
|
|
||||||
return Glib::Markup::escape_text(member->Nickname);
|
|
||||||
else
|
|
||||||
return Glib::Markup::escape_text(user.GetDisplayNameEscaped());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ChatMessageHeader::on_author_button_press(GdkEventButton *ev) {
|
bool ChatMessageHeader::on_author_button_press(GdkEventButton *ev) {
|
||||||
if (ev->button == GDK_BUTTON_PRIMARY && (ev->state & GDK_SHIFT_MASK)) {
|
if (ev->button == GDK_BUTTON_PRIMARY && (ev->state & GDK_SHIFT_MASK)) {
|
||||||
m_signal_action_insert_mention.emit();
|
m_signal_action_insert_mention.emit();
|
||||||
|
|
|
@ -84,7 +84,6 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void AttachUserMenuHandler(Gtk::Widget &widget);
|
void AttachUserMenuHandler(Gtk::Widget &widget);
|
||||||
static Glib::ustring GetEscapedDisplayName(const UserData &user, const std::optional<GuildMember> &member);
|
|
||||||
|
|
||||||
bool on_author_button_press(GdkEventButton *ev);
|
bool on_author_button_press(GdkEventButton *ev);
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ void SettingsManager::ReadSettings() {
|
||||||
SMBOOL("gui", "unreads", Unreads);
|
SMBOOL("gui", "unreads", Unreads);
|
||||||
SMBOOL("gui", "alt_menu", AltMenu);
|
SMBOOL("gui", "alt_menu", AltMenu);
|
||||||
SMBOOL("gui", "hide_to_tray", HideToTray);
|
SMBOOL("gui", "hide_to_tray", HideToTray);
|
||||||
|
SMBOOL("gui", "show_deleted_indicator", ShowDeletedIndicator);
|
||||||
SMFLT("gui", "font_scale", FontScale);
|
SMFLT("gui", "font_scale", FontScale);
|
||||||
SMINT("http", "concurrent", CacheHTTPConcurrency);
|
SMINT("http", "concurrent", CacheHTTPConcurrency);
|
||||||
SMSTR("http", "user_agent", UserAgent);
|
SMSTR("http", "user_agent", UserAgent);
|
||||||
|
@ -153,6 +154,7 @@ void SettingsManager::Close() {
|
||||||
SMBOOL("gui", "unreads", Unreads);
|
SMBOOL("gui", "unreads", Unreads);
|
||||||
SMBOOL("gui", "alt_menu", AltMenu);
|
SMBOOL("gui", "alt_menu", AltMenu);
|
||||||
SMBOOL("gui", "hide_to_tray", HideToTray);
|
SMBOOL("gui", "hide_to_tray", HideToTray);
|
||||||
|
SMBOOL("gui", "show_deleted_indicator", ShowDeletedIndicator);
|
||||||
SMFLT("gui", "font_scale", FontScale);
|
SMFLT("gui", "font_scale", FontScale);
|
||||||
SMINT("http", "concurrent", CacheHTTPConcurrency);
|
SMINT("http", "concurrent", CacheHTTPConcurrency);
|
||||||
SMSTR("http", "user_agent", UserAgent);
|
SMSTR("http", "user_agent", UserAgent);
|
||||||
|
|
|
@ -30,6 +30,7 @@ public:
|
||||||
bool Unreads { true };
|
bool Unreads { true };
|
||||||
bool AltMenu { false };
|
bool AltMenu { false };
|
||||||
bool HideToTray { false };
|
bool HideToTray { false };
|
||||||
|
bool ShowDeletedIndicator { true };
|
||||||
double FontScale { -1.0 };
|
double FontScale { -1.0 };
|
||||||
|
|
||||||
// [http]
|
// [http]
|
||||||
|
|
|
@ -29,10 +29,22 @@ std::optional<std::pair<std::string, std::string>> ParseCookie(const Glib::ustri
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Glib::ustring> GetJavascriptFileFromAppPage(const Glib::ustring &contents) {
|
std::optional<Glib::ustring> GetJavascriptFileFromAppPage(const Glib::ustring &contents) {
|
||||||
auto regex = Glib::Regex::create(R"(app-mount.*(/assets/[\w\d]*.js).*/assets/[\w\d]*.js)");
|
auto regex = Glib::Regex::create(R"(/assets/\w{20}.js)");
|
||||||
|
std::vector<Glib::ustring> matches;
|
||||||
|
|
||||||
|
// regex->match_all doesnt work for some reason
|
||||||
|
int start_position = 0;
|
||||||
Glib::MatchInfo match;
|
Glib::MatchInfo match;
|
||||||
if (regex->match(contents, match)) {
|
while (regex->match(contents, start_position, match)) {
|
||||||
return match.fetch(1);
|
const auto str = match.fetch(0);
|
||||||
|
matches.push_back(str);
|
||||||
|
int foo;
|
||||||
|
match.fetch_pos(0, start_position, foo);
|
||||||
|
start_position += str.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches.size() >= 6) {
|
||||||
|
return matches[matches.size() - 6];
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -52,7 +64,7 @@ std::optional<uint32_t> GetBuildNumberFromJSURL(const Glib::ustring &url, const
|
||||||
auto res = req.execute();
|
auto res = req.execute();
|
||||||
if (res.error) return {};
|
if (res.error) return {};
|
||||||
|
|
||||||
auto regex = Glib::Regex::create(R"("buildNumber",null!==\(t="(\d+)\"\))");
|
auto regex = Glib::Regex::create("buildNumber:\"(\\d+)\"");
|
||||||
Glib::MatchInfo match;
|
Glib::MatchInfo match;
|
||||||
Glib::ustring string = res.text;
|
Glib::ustring string = res.text;
|
||||||
if (regex->match(string, match)) {
|
if (regex->match(string, match)) {
|
||||||
|
|
|
@ -1,6 +1,34 @@
|
||||||
#include "userinfopane.hpp"
|
#include "userinfopane.hpp"
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
|
static std::string GetConnectionURL(const ConnectionData &conn) {
|
||||||
|
if (conn.Type == "github") {
|
||||||
|
return "https://github.com/" + conn.Name;
|
||||||
|
} else if (conn.Type == "steam") {
|
||||||
|
return "https://steamcommunity.com/profiles/" + conn.ID;
|
||||||
|
} else if (conn.Type == "twitch") {
|
||||||
|
return "https://twitch.tv/" + conn.Name;
|
||||||
|
} else if (conn.Type == "twitter") {
|
||||||
|
return "https://twitter.com/i/user/" + conn.ID;
|
||||||
|
} else if (conn.Type == "spotify") {
|
||||||
|
return "https://open.spotify.com/user/" + conn.ID;
|
||||||
|
} else if (conn.Type == "reddit") {
|
||||||
|
return "https://reddit.com/u/" + conn.Name;
|
||||||
|
} else if (conn.Type == "youtube") {
|
||||||
|
return "https://www.youtube.com/channel/" + conn.ID;
|
||||||
|
} else if (conn.Type == "facebook") {
|
||||||
|
return "https://www.facebook.com/" + conn.ID;
|
||||||
|
} else if (conn.Type == "ebay") {
|
||||||
|
return "https://www.ebay.com/usr/" + conn.Name;
|
||||||
|
} else if (conn.Type == "instagram") {
|
||||||
|
return "https://www.instagram.com/" + conn.Name;
|
||||||
|
} else if (conn.Type == "tiktok") {
|
||||||
|
return "https://www.tiktok.com/@" + conn.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
ConnectionItem::ConnectionItem(const ConnectionData &conn)
|
ConnectionItem::ConnectionItem(const ConnectionData &conn)
|
||||||
: m_box(Gtk::ORIENTATION_HORIZONTAL)
|
: m_box(Gtk::ORIENTATION_HORIZONTAL)
|
||||||
, m_name(conn.Name) {
|
, m_name(conn.Name) {
|
||||||
|
@ -8,23 +36,7 @@ ConnectionItem::ConnectionItem(const ConnectionData &conn)
|
||||||
try {
|
try {
|
||||||
pixbuf = Gdk::Pixbuf::create_from_file(Abaddon::GetResPath("/" + conn.Type + ".png"), 32, 32);
|
pixbuf = Gdk::Pixbuf::create_from_file(Abaddon::GetResPath("/" + conn.Type + ".png"), 32, 32);
|
||||||
} catch (const Glib::Exception &e) {}
|
} catch (const Glib::Exception &e) {}
|
||||||
std::string url;
|
std::string url = GetConnectionURL(conn);
|
||||||
if (conn.Type == "github")
|
|
||||||
url = "https://github.com/" + conn.Name;
|
|
||||||
else if (conn.Type == "steam")
|
|
||||||
url = "https://steamcommunity.com/profiles/" + conn.ID;
|
|
||||||
else if (conn.Type == "twitch")
|
|
||||||
url = "https://twitch.tv/" + conn.Name;
|
|
||||||
else if (conn.Type == "twitter")
|
|
||||||
url = "https://twitter.com/i/user/" + conn.ID;
|
|
||||||
else if (conn.Type == "spotify")
|
|
||||||
url = "https://open.spotify.com/user/" + conn.ID;
|
|
||||||
else if (conn.Type == "reddit")
|
|
||||||
url = "https://reddit.com/u/" + conn.Name;
|
|
||||||
else if (conn.Type == "youtube")
|
|
||||||
url = "https://www.youtube.com/channel/" + conn.ID;
|
|
||||||
else if (conn.Type == "facebook")
|
|
||||||
url = "https://www.facebook.com/" + conn.ID;
|
|
||||||
if (pixbuf) {
|
if (pixbuf) {
|
||||||
m_image = Gtk::manage(new Gtk::Image(pixbuf));
|
m_image = Gtk::manage(new Gtk::Image(pixbuf));
|
||||||
m_image->get_style_context()->add_class("profile-connection-image");
|
m_image->get_style_context()->add_class("profile-connection-image");
|
||||||
|
@ -83,25 +95,33 @@ void ConnectionsContainer::SetConnections(const std::vector<ConnectionData> &con
|
||||||
|
|
||||||
static const std::unordered_set<std::string> supported_services = {
|
static const std::unordered_set<std::string> supported_services = {
|
||||||
"battlenet",
|
"battlenet",
|
||||||
|
"ebay",
|
||||||
|
"epicgames",
|
||||||
|
"facebook",
|
||||||
"github",
|
"github",
|
||||||
|
"instagram",
|
||||||
"leagueoflegends",
|
"leagueoflegends",
|
||||||
|
"paypal",
|
||||||
|
"playstation",
|
||||||
"reddit",
|
"reddit",
|
||||||
|
"riotgames",
|
||||||
"skype",
|
"skype",
|
||||||
"spotify",
|
"spotify",
|
||||||
"steam",
|
"steam",
|
||||||
|
"tiktok",
|
||||||
"twitch",
|
"twitch",
|
||||||
"twitter",
|
"twitter",
|
||||||
"xbox",
|
"xbox",
|
||||||
"youtube",
|
"youtube",
|
||||||
"facebook"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (size_t i = 0; i < connections.size(); i++) {
|
int i = 0;
|
||||||
const auto &conn = connections[i];
|
for (const auto &conn : connections) {
|
||||||
if (supported_services.find(conn.Type) == supported_services.end()) continue;
|
if (supported_services.find(conn.Type) == supported_services.end()) continue;
|
||||||
auto widget = Gtk::manage(new ConnectionItem(conn));
|
auto widget = Gtk::manage(new ConnectionItem(conn));
|
||||||
widget->show();
|
widget->show();
|
||||||
attach(*widget, static_cast<int>(i % 2), static_cast<int>(i / 2), 1, 1);
|
attach(*widget, i % 2, i / 2, 1, 1);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_halign(Gtk::ALIGN_FILL);
|
set_halign(Gtk::ALIGN_FILL);
|
||||||
|
|
|
@ -89,6 +89,7 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
|
||||||
, m_mute("Mute")
|
, m_mute("Mute")
|
||||||
, m_deafen("Deafen")
|
, m_deafen("Deafen")
|
||||||
, m_noise_suppression("Suppress Noise")
|
, m_noise_suppression("Suppress Noise")
|
||||||
|
, m_mix_mono("Mix Mono")
|
||||||
, m_channel_id(channel_id)
|
, m_channel_id(channel_id)
|
||||||
, m_menu_view("View")
|
, m_menu_view("View")
|
||||||
, m_menu_view_settings("More _Settings", true) {
|
, m_menu_view_settings("More _Settings", true) {
|
||||||
|
@ -178,6 +179,11 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
|
||||||
Abaddon::Get().GetAudio().SetSuppressNoise(m_noise_suppression.get_active());
|
Abaddon::Get().GetAudio().SetSuppressNoise(m_noise_suppression.get_active());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_mix_mono.set_active(audio.GetMixMono());
|
||||||
|
m_mix_mono.signal_toggled().connect([this]() {
|
||||||
|
Abaddon::Get().GetAudio().SetMixMono(m_mix_mono.get_active());
|
||||||
|
});
|
||||||
|
|
||||||
auto *playback_renderer = Gtk::make_managed<Gtk::CellRendererText>();
|
auto *playback_renderer = Gtk::make_managed<Gtk::CellRendererText>();
|
||||||
m_playback_combo.set_valign(Gtk::ALIGN_END);
|
m_playback_combo.set_valign(Gtk::ALIGN_END);
|
||||||
m_playback_combo.set_hexpand(true);
|
m_playback_combo.set_hexpand(true);
|
||||||
|
@ -223,6 +229,7 @@ VoiceWindow::VoiceWindow(Snowflake channel_id)
|
||||||
m_controls.add(m_mute);
|
m_controls.add(m_mute);
|
||||||
m_controls.add(m_deafen);
|
m_controls.add(m_deafen);
|
||||||
m_controls.add(m_noise_suppression);
|
m_controls.add(m_noise_suppression);
|
||||||
|
m_controls.add(m_mix_mono);
|
||||||
m_main.add(m_menu_bar);
|
m_main.add(m_menu_bar);
|
||||||
m_main.add(m_controls);
|
m_main.add(m_controls);
|
||||||
m_main.add(m_vad_value);
|
m_main.add(m_vad_value);
|
||||||
|
|
|
@ -54,6 +54,7 @@ private:
|
||||||
Gtk::Scale m_capture_gain;
|
Gtk::Scale m_capture_gain;
|
||||||
|
|
||||||
Gtk::CheckButton m_noise_suppression;
|
Gtk::CheckButton m_noise_suppression;
|
||||||
|
Gtk::CheckButton m_mix_mono;
|
||||||
|
|
||||||
Gtk::ComboBoxText m_vad_combo;
|
Gtk::ComboBoxText m_vad_combo;
|
||||||
Gtk::ComboBox m_playback_combo;
|
Gtk::ComboBox m_playback_combo;
|
||||||
|
|