//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") #pragma comment(lib, "rpcrt4.lib") #include "Util.h" #include "AudioApi.h" #include "Settings.h" #include "AsuroTool.h" #include "WindowsShell.h" #include #include #include #include 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& 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::TableSetupColumn("Proc", ImGuiTableColumnFlags_WidthFixed, 30.); 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 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); } } // Advanced /*ImGui::TableNextColumn(); AudioProcessingState procState = getAudioProcessing(dev); bool procChecked = procState == AudioProcessingState::Enabled; std::string procCbId("cb_proc_"); procCbId.append(deviceIdUtf8.c_str()); ImGui::PushID(procCbId.c_str()); if (ImGui::Checkbox("", &procChecked)) { setAudioProcessing(dev, procChecked); } ImGui::PopID();*/ 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); }