Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "CommonFramework/AudioPipeline/AudioInputChoice.h"
#include "CommonFramework/AudioPipeline/Backends/NativeAudioFormats.h"
#include "CommonFramework/Logging/Logger.h"
#include "CommonFramework/Options/DeviceDropdownAliases.h"
#include "AudioDeviceInfo.h"

namespace PokemonAutomation{
Expand Down Expand Up @@ -192,7 +193,16 @@ AudioDeviceInfo::AudioDeviceInfo(const std::string& device_name){
AudioDeviceInfo::operator bool() const{
return m_body && !m_body->device_name.empty();
}
const std::string& AudioDeviceInfo::display_name() const{
std::string AudioDeviceInfo::display_name() const{
if (!*this){
return std::string();
}
if (auto alias = device_dropdown_name(DeviceAliasType::AUDIO, m_body->device_name)){
return *alias;
}
return m_body->display_name;
}
const std::string& AudioDeviceInfo::original_name() const{
return m_body->display_name;
}
const std::string& AudioDeviceInfo::device_name() const{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class AudioDeviceInfo{

explicit operator bool() const;

const std::string& display_name() const;
std::string display_name() const;
const std::string& original_name() const;
const std::string& device_name() const;

// The device-driven format list (see input_choices()). Populated for
Expand Down
4 changes: 4 additions & 0 deletions SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "Controllers/ControllerSettings.h"
//#include "CommonFramework/Environment/Environment.h"
#include "Integrations/DiscordSettingsOption.h"
#include "CommonFramework/Options/DeviceDropdownAliases.h"
#include "GlobalSettingsPanel.h"

// #include <iostream>
Expand Down Expand Up @@ -126,6 +127,7 @@ GlobalSettings::GlobalSettings()
LockMode::UNLOCK_WHILE_RUNNING,
KeyboardLayout::QWERTY
)
, DEVICE_ALIASES(CONSTRUCT_TOKEN)
, STREAM_HISTORY(CONSTRUCT_TOKEN)
, SLEEP_SUPPRESS(CONSTRUCT_TOKEN)
, m_discord_settings(
Expand Down Expand Up @@ -205,6 +207,7 @@ GlobalSettings::GlobalSettings()
PA_ADD_OPTION(LOG_WINDOW_STARTUP);

PA_ADD_OPTION(KEYBOARD_CONTROLS_LAYOUT);
PA_ADD_OPTION(DEVICE_ALIASES);
PA_ADD_OPTION(STREAM_HISTORY);

#ifdef PA_ENABLE_SLEEP_SUPPRESS
Expand Down Expand Up @@ -349,6 +352,7 @@ GlobalSettings_Descriptor::Wrapper& GlobalSettings_Descriptor::instance(){
GlobalSettingsPanel::GlobalSettingsPanel()
: settings(GlobalSettings::instance())
{
settings.DEVICE_ALIASES->add_connected_devices();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review: Note: this runs synchronously on the GUI thread every time Global Settings is opened. AudioDeviceInfo::all_input_devices() probes every input device's supported formats (and get_all_cameras() enumerates the video backend), so on a machine with many virtual audio devices or a stuck USB camera this can stall the UI for several seconds while the panel is being constructed. Consider deferring it (e.g. a one-shot QTimer::singleShot after the panel is shown) or enumerating on a worker thread.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is fine I think. We already have this delay when loading any program. If it becomes a problem in the future, we can add an async refresher like what the video sources already do.

PA_ADD_OPTION(settings);
}

Expand Down
3 changes: 3 additions & 0 deletions SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class ResolutionOption;
class KeyboardLayoutOption;
class StreamHistoryOption;
class SleepSuppressOptions;
class DeviceAliasOptions;
namespace Integration{
class DiscordSettingsOption;
}
Expand Down Expand Up @@ -94,6 +95,8 @@ class GlobalSettings : public BatchOption, private ConfigOption::Listener, priva

Pimpl<KeyboardLayoutOption> KEYBOARD_CONTROLS_LAYOUT;

Pimpl<DeviceAliasOptions> DEVICE_ALIASES;

Pimpl<StreamHistoryOption> STREAM_HISTORY;
Pimpl<SleepSuppressOptions> SLEEP_SUPPRESS;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/* Device Dropdown Aliases
*
* From: https://github.com/PokemonAutomation/
*
*/

#include "Common/Cpp/Json/JsonValue.h"
#include "CommonFramework/GlobalSettingsPanel.h"
#include "CommonFramework/AudioPipeline/Backends/AudioDeviceInfo.h"
#include "CommonFramework/VideoPipeline/Backends/CameraImplementations.h"
#include "DeviceDropdownAliases.h"

namespace PokemonAutomation{
namespace{

const EnumDropdownDatabase<DeviceAliasType>& categories(){
static const EnumDropdownDatabase<DeviceAliasType> database{
{DeviceAliasType::VIDEO, "Video", "Video"},
{DeviceAliasType::AUDIO, "Audio", "Audio"},
};
return database;
}

}

DeviceAliasRow::DeviceAliasRow(EditableTableOption& parent)
: EditableTableRow(parent)
, category(categories(), LockMode::UNLOCK_WHILE_RUNNING, DeviceAliasType::VIDEO)
, device_id(false, LockMode::UNLOCK_WHILE_RUNNING, "", "Device ID")
, device_name(false, LockMode::READ_ONLY, "", "")
, alias(false, LockMode::UNLOCK_WHILE_RUNNING, "", "Use original name")
{
PA_ADD_OPTION(category);
PA_ADD_OPTION(device_id);
PA_ADD_OPTION(device_name);
PA_ADD_OPTION(alias);
}

std::unique_ptr<EditableTableRow> DeviceAliasRow::clone() const{
auto row = std::make_unique<DeviceAliasRow>(parent());
row->category.set(category);
row->device_id.set(device_id);
row->device_name.set(device_name);
row->alias.set(alias);
return row;
}

DeviceDropdownAliases::DeviceDropdownAliases()
: EditableTableOption_t<DeviceAliasRow>(
"<b>Device Aliases:</b><br>"
"Choose names for cameras and audio devices. "
"Connected devices are added when Global Settings opens.<br>"
"An empty alias uses the original name.",
LockMode::UNLOCK_WHILE_RUNNING,
true,
10
)
{}

std::vector<std::string> DeviceDropdownAliases::make_header() const{
return {"Type", "Device ID", "Original Name", "Alias"};
}

std::optional<std::string> DeviceDropdownAliases::lookup(DeviceAliasType category, const std::string& device_id) const{
std::optional<std::string> result;
EditableTableOption::run_on_all_rows<DeviceAliasRow>([&](const DeviceAliasRow& row){
if (row.category != category || std::string(row.device_id) != device_id){
return false;
}
std::string alias = row.alias;
if (!alias.empty()){
result = std::move(alias);
}
return true;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review: Minor: the lookup stops at the first row matching (category, device ID), even when that row's alias is empty. If the settings JSON ever ends up with two rows for the same device (only possible by hand-editing the JSON, since add_device() deduplicates), an empty-alias row earlier in the table would shadow a later row with a real alias. Consider only returning true (stopping) when a non-empty alias was found, i.e. keep scanning while result is still empty.

});
return result;
}

void DeviceDropdownAliases::add_device(DeviceAliasType category, const std::string& id, const std::string& name){
bool found = false;
run_on_all_rows([&](DeviceAliasRow& row){
if (row.category != category || std::string(row.device_id) != id){
return false;
}
row.device_name.set(name);
found = true;
return true;
});
if (!found){
auto row = std::make_unique<DeviceAliasRow>(*this);
row->category.set(category);
row->device_id.set(id);
row->device_name.set(name);
append_row(std::move(row));
}
}

void DeviceDropdownAliases::add_connected_devices(){

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review: Design question: auto-added rows are persisted into the settings JSON (via GlobalSettings::to_json()) and never pruned, so over time the table accumulates rows for devices that are no longer connected (the table is collapsed by default past 10 rows, so this is mostly invisible). A stale alias could also apply to a different device that later gets the same ID (device IDs from Qt/ALSA are not guaranteed stable across reboots or when other devices are plugged in). It may be worth pruning rows for devices that aren't currently connected when refreshing here (keeping user-edited rows with aliases?), or at least mentioning the persistence in the table label.

for (const CameraInfo& info : get_all_cameras()){
add_device(DeviceAliasType::VIDEO, info.device_name(), get_camera_name(info));
}
for (const AudioDeviceInfo& info : AudioDeviceInfo::all_input_devices()){
add_device(DeviceAliasType::AUDIO, info.device_name(), info.original_name());
}
for (const AudioDeviceInfo& info : AudioDeviceInfo::all_output_devices()){
add_device(DeviceAliasType::AUDIO, info.device_name(), info.original_name());
}
}

DeviceAliasOptions::DeviceAliasOptions()
: BatchOption(LockMode::UNLOCK_WHILE_RUNNING, false)
{
PA_ADD_OPTION(m_aliases);
}

JsonValue DeviceAliasOptions::to_json() const{
return m_aliases.to_json();
}
void DeviceAliasOptions::load_json(const JsonValue& json){
m_aliases.load_json(json);
}

void DeviceAliasOptions::add_connected_devices(){
m_aliases.add_connected_devices();
}
std::optional<std::string> DeviceAliasOptions::lookup(DeviceAliasType category, const std::string& device_id) const{
return m_aliases.lookup(category, device_id);
}

std::optional<std::string> device_dropdown_name(DeviceAliasType category, const std::string& device_id){
return GlobalSettings::instance().DEVICE_ALIASES->lookup(category, device_id);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* Device Dropdown Aliases
*
* From: https://github.com/PokemonAutomation/
*
*/

#ifndef PokemonAutomation_DeviceDropdownAliases_H
#define PokemonAutomation_DeviceDropdownAliases_H

#include <optional>
#include <string>
#include "Common/Cpp/Options/EditableTableOption.h"
#include "Common/Cpp/Options/EnumDropdownOption.h"
#include "Common/Cpp/Options/BatchOption.h"
#include "Common/Cpp/Options/StringOption.h"

namespace PokemonAutomation{

enum class DeviceAliasType{
VIDEO,
AUDIO,
};

class DeviceAliasRow : public EditableTableRow{
public:
DeviceAliasRow(EditableTableOption& parent);
virtual std::unique_ptr<EditableTableRow> clone() const override;

EnumDropdownCell<DeviceAliasType> category;
StringCell device_id;
StringCell device_name;
StringCell alias;
};

class DeviceDropdownAliases : public EditableTableOption_t<DeviceAliasRow>{
public:
DeviceDropdownAliases();

virtual std::vector<std::string> make_header() const override;
void add_connected_devices();
std::optional<std::string> lookup(DeviceAliasType category, const std::string& device_id) const;

private:
void add_device(DeviceAliasType category, const std::string& id, const std::string& name);
};

class DeviceAliasOptions : public BatchOption{
public:
DeviceAliasOptions();

virtual JsonValue to_json() const override;
virtual void load_json(const JsonValue& json) override;

void add_connected_devices();
std::optional<std::string> lookup(DeviceAliasType category, const std::string& device_id) const;

private:
DeviceDropdownAliases m_aliases;
};

// Presentation only: never use this result for device selection or serialization.
// Returns std::nullopt when no non-empty alias is configured.
// Configured in Global Settings. Device IDs remain unchanged.
std::optional<std::string> device_dropdown_name(
DeviceAliasType category,
const std::string& device_id
);

}
#endif
23 changes: 23 additions & 0 deletions SerialPrograms/Source/CommonFramework/VideoPipeline/CameraInfo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* Camera Info
*
* From: https://github.com/PokemonAutomation/
*
*/

#include "CommonFramework/Options/DeviceDropdownAliases.h"
#include "Backends/CameraImplementations.h"
#include "CameraInfo.h"

namespace PokemonAutomation{

std::string CameraInfo::display_name() const{
if (!*this){
return std::string();
}
if (auto alias = device_dropdown_name(DeviceAliasType::VIDEO, m_device_name)){
return *alias;
}
return get_camera_name(*this);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class CameraInfo{
explicit operator bool() const{ return !m_device_name.empty(); }
const std::string& device_name() const{ return m_device_name; }

std::string display_name() const;

bool operator==(const CameraInfo& info) const{
return m_device_name == info.m_device_name;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ bool VideoSourceDescriptor_Camera::operator==(const VideoSourceDescriptor& x) co
return m_info == static_cast<const VideoSourceDescriptor_Camera&>(x).m_info;
}
std::string VideoSourceDescriptor_Camera::display_name() const{
return get_camera_name(m_info);
return m_info.display_name();
}

JsonValue VideoSourceDescriptor_Camera::to_json() const{
Expand Down
3 changes: 3 additions & 0 deletions SerialPrograms/cmake/SourceFiles.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,8 @@ file(GLOB LIBRARY_SOURCES
Source/CommonFramework/Notifications/SenderNotificationTable.h
Source/CommonFramework/Options/BoxOption.cpp
Source/CommonFramework/Options/BoxOption.h
Source/CommonFramework/Options/DeviceDropdownAliases.cpp
Source/CommonFramework/Options/DeviceDropdownAliases.h
Source/CommonFramework/Options/Environment/OnnxOptions.h
Source/CommonFramework/Options/Environment/SleepSuppressOption.cpp
Source/CommonFramework/Options/Environment/SleepSuppressOption.h
Expand Down Expand Up @@ -745,6 +747,7 @@ file(GLOB LIBRARY_SOURCES
Source/CommonFramework/VideoPipeline/Backends/SnapshotManager.h
Source/CommonFramework/VideoPipeline/Backends/VideoFrameQt.cpp
Source/CommonFramework/VideoPipeline/Backends/VideoFrameQt.h
Source/CommonFramework/VideoPipeline/CameraInfo.cpp
Source/CommonFramework/VideoPipeline/CameraInfo.h
Source/CommonFramework/VideoPipeline/Stats/CpuUtilizationStats.cpp
Source/CommonFramework/VideoPipeline/Stats/CpuUtilizationStats.h
Expand Down
Loading