Files
AsuroImgui/AsuroTool/AsuroTool.cpp

349 lines
9.7 KiB
C++

//Disables console window
#if !_DEBUG
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
#endif
// we need commctrl v6 for LoadIconMetric()
// see https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/winui/shell/appshellintegration/NotificationIcon/NotificationIcon.cpp
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib, "comctl32.lib")
#include "Util.h"
#include "AudioApi.h"
#include "Settings.h"
#include "AsuroTool.h"
#include "WindowsShell.h"
#include <array>
#include <functional>
#include <iostream>
#include <sstream>
const size_t MAX_FONT_PATH_LENGTH = 2048;
// Globals for use in callbacks
DrawData* gDrawData;
ApplicationData* gAppData;
bool justDocked = false;
bool isHidden = false;
int main()
{
std::wstring appDir;
getAppDir(appDir);
if (_wchdir(appDir.c_str()) != 0)
{
std::cout << "Failed to set working dir." << std::endl;
}
ApplicationData applicationData{};
ImGuiCallbacks callbacks{};
callbacks.initFunc = std::bind(init, std::placeholders::_1, std::ref(applicationData));
callbacks.drawFunc = std::bind(draw, std::placeholders::_1, std::ref(applicationData));
callbacks.cleanupFunc = std::bind(cleanup, std::placeholders::_1, std::ref(applicationData));
startImgui(callbacks, "Audio Thingy", 600, 400);
}
void init(DrawData& drawData, ApplicationData& appData)
{
std::wstring appPath;
getAppDir(appPath);
char appPathStr[MAX_FONT_PATH_LENGTH];
HRESULT convResult = WideCharToMultiByte(CP_UTF8, NULL, &appPath[0], -1, appPathStr, MAX_FONT_PATH_LENGTH, NULL, nullptr);
isError(convResult, "Failed to convert path: ");
// sad :(
gDrawData = &drawData;
gAppData = &appData;
// Load text font
ImGuiIO& io = ImGui::GetIO();
std::string fontPath = std::string(appPathStr);
fontPath.append("\\Montserrat-Regular.ttf");
io.Fonts->AddFontFromFileTTF(fontPath.c_str(), 18.0f);
// Load icon font
static const ImWchar icons_ranges[] = { 0xEA01, 0xF2DF, 0 };
ImFontConfig icons_config;
icons_config.MergeMode = true;
icons_config.PixelSnapH = true;
std::string iconFontPath = std::string(appPathStr);
iconFontPath.append("\\remixicon.ttf");
io.Fonts->AddFontFromFileTTF(iconFontPath.c_str(), 14.0f, &icons_config, icons_ranges);
initShell(drawData);
initSettings(drawData, appData);
initAudio(appData);
}
void draw(DrawData& drawData, ApplicationData& appData)
{
justDocked = false;
if (isHidden)
{
glfwWaitEvents();
return;
}
float customYCursor = 0;
ImVec2 viewportSize = ImGui::GetMainViewport()->Size;
// Menu Bar
customYCursor += menuBar(drawData, appData).y;
// Playback Devices
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
customYCursor += audioDeviceWindow(appData, appData.audioData->playbackDevices, " \xEE\xB8\x84 Playback").y;
customYCursor += 5.;
// Recording devices
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
customYCursor += audioDeviceWindow(appData, appData.audioData->recordingDevices, " \xEE\xBD\x8F Recording").y;
// Resize viewport
drawData.window_size.y = customYCursor;
if (appData.settings.docked)
{
int monitorX, monitorY, monitorW, monitorH;
glfwGetMonitorWorkarea(glfwGetPrimaryMonitor(), &monitorX, &monitorY, &monitorW, &monitorH);
glfwSetWindowPos(drawData.window, monitorX + monitorW - drawData.window_size.x, monitorY + monitorH - drawData.window_size.y);
}
}
void cleanup(DrawData& drawData, ApplicationData& appData)
{
cleanupShell(drawData);
}
ImVec2 menuBar(DrawData& drawData, ApplicationData& appData)
{
ImVec2 size{};
bool closeMenu = false;
if (ImGui::BeginMainMenuBar())
{
if (ImGui::BeginMenu("Settings"))
{
if (ImGui::Checkbox("Docked", &appData.settings.docked))
{
closeMenu = true;
updateDocked(drawData, appData);
}
ImGui::Checkbox("Show Disabled Devices", &appData.settings.showDisabledDevices);
if (ImGui::Checkbox("Autostart", &appData.settings.autostart))
{
setAutostart(appData.settings.autostart);
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Debug"))
{
if (ImGui::Button("Manual Refresh"))
{
reloadDeviceLists(*appData.audioData);
}
ImGui::EndMenu();
}
if (appData.settings.docked)
{
ImVec2 availableSpace = ImGui::GetContentRegionAvail();
ImVec2 cursorPos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(cursorPos.x + availableSpace.x - 33.);
if (ImGui::SmallButton("-"))
{
glfwIconifyWindow(drawData.window);
}
if (ImGui::SmallButton("x"))
{
glfwSetWindowShouldClose(drawData.window, GLFW_TRUE);
}
}
size = ImGui::GetWindowSize();
ImGui::EndMainMenuBar();
}
if (closeMenu)
{
ImGui::SetWindowFocus();
}
return size;
}
bool customButton(const char* id_start, const char* id_end, const char* title, bool visible)
{
std::string buttonId(id_start);
buttonId.append(id_end);
bool result = false;
if (visible)
{
ImGui::PushID(buttonId.c_str());
result = ImGui::SmallButton(title);
ImGui::PopID();
}
else
{
ImGui::InvisibleButton(buttonId.c_str(), ImGui::CalcTextSize(title));
}
return result;
}
ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& deviceList, const char* title)
{
if (ImGui::Begin(title, 0, ImGuiWindowFlags_NoResize))
{
if (ImGui::BeginTable("DeviceTable", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders | ImGuiTableFlags_NoSavedSettings))
{
ImGui::TableSetupColumn("Devices", ImGuiTableColumnFlags_WidthStretch, 3.);
ImGui::TableSetupColumn("Volume", ImGuiTableColumnFlags_WidthStretch, 1.);
ImGui::TableSetupColumn("Defaults", ImGuiTableColumnFlags_WidthFixed, 55.);
ImGui::TableHeadersRow();
for (auto& dev : deviceList)
{
std::string deviceIdUtf8 = utf8Encode(dev.id);
if (dev.state == DEVICE_STATE_ACTIVE)
{
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1., 1., 1., 1.));
}
else if (!appData.settings.showDisabledDevices)
{
continue;
}
else
{
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(.7, .7, .7, 1.));
}
// Device Name
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text(dev.name.c_str());
// Volume
ImGui::TableNextColumn();
if (dev.state == DEVICE_STATE_ACTIVE)
{
// Mute button
ImGui::PushID(std::string("bn_mute_").append(deviceIdUtf8).c_str());
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
bool isDeviceMuted = isMuted(dev.volumeInterface);
if (ImGui::Button(isDeviceMuted ? "\xEF\x8A\x9D" : "\xEF\x8A\xA1"))
{
setMuted(dev.volumeInterface, !isDeviceMuted);
}
ImGui::PopStyleColor();
ImGui::PopID();
ImGui::SameLine(0, 2);
// Meter
static std::array<float, 2> meterValues{};
UINT channelCount = getMeterValues(dev.meterInterface, meterValues);
auto drawList = ImGui::GetWindowDrawList();
ImVec2 windowPos = ImGui::GetWindowPos();
ImVec2 cursorPos = ImGui::GetCursorScreenPos();
ImVec2 space = ImGui::GetContentRegionAvail();
float lineY = cursorPos.y + ImGui::GetTextLineHeight() / 2. + 2.;
const float linePaddingX = 3.;
cursorPos.x += linePaddingX;
// BG line
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX), lineY), IM_COL32(120, 120, 120, 255), 2.);
// Channel lines
if (channelCount == 1)
{
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX) * meterValues[0], lineY), IM_COL32(200, 200, 255, 255), 3.);
}
if (channelCount == 2)
{
drawList->AddLine(ImVec2(cursorPos.x, lineY - 2), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX) * meterValues[0], lineY - 2), IM_COL32(200, 200, 255, 255), 3.);
drawList->AddLine(ImVec2(cursorPos.x, lineY + 2), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX) * meterValues[1], lineY + 2), IM_COL32(200, 200, 255, 255), 3.);
}
ImGui::SetNextItemWidth(space.x);
ImGui::PushID(&dev.id);
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0, 0, 0, 0));
if (isDeviceMuted) ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(.4, .4, .4, 1.));
float volume = getVolume(dev.volumeInterface);
if (ImGui::SliderFloat("", &volume, 0., 1., "", ImGuiSliderFlags_NoRoundToFormat | ImGuiSliderFlags_AlwaysClamp))
{
setVolume(dev.volumeInterface, volume);
}
if (isDeviceMuted) ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopID();
}
// Defaults
ImGui::TableNextColumn();
if (dev.state == DEVICE_STATE_ACTIVE)
{
if (dev.isDefaultConsole)
{
drawCircle(5, IM_COL32(50, 50, 222, 255));
}
if (customButton("bn_d_", deviceIdUtf8.c_str(), "\xEE\xBE\x82", !dev.isDefaultConsole))
{
setDefaultAudioDevice(*appData.audioData, dev.id.c_str(), ERole::eConsole);
}
ImGui::SameLine();
if (dev.isDefaultCommunication)
{
drawCircle(5, IM_COL32(222, 50, 50, 255));
}
if (customButton("bn_c_", deviceIdUtf8.c_str(), "\xEE\xBF\xA9", !dev.isDefaultCommunication))
{
setDefaultAudioDevice(*appData.audioData, dev.id.c_str(), ERole::eCommunications);
}
}
ImGui::PopStyleColor();
}
ImGui::EndTable();
}
}
ImVec2 size = ImGui::GetWindowSize();
ImGui::End();
return size;
}
void drawCircle(float radius, ImU32 color)
{
ImGui::Dummy(ImVec2(0, 0));
ImGui::SameLine();
auto drawList = ImGui::GetWindowDrawList();
ImVec2 cursorPos = ImGui::GetCursorPos();
ImVec2 windowPos = ImGui::GetWindowPos();
drawList->AddCircleFilled(ImVec2(cursorPos.x + windowPos.x, cursorPos.y + windowPos.y + ImGui::GetTextLineHeight() / 2.), radius, color);
}