#include #include #include #include #include #include #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" #include void generateRandomColors(unsigned char& r, unsigned char& g, unsigned char& b); void sendDMXData(HANDLE hSerial, unsigned char* data, int length); void setupImGui(GLFWwindow* window); void renderImGui(); // ui timeline stuff std::vector> markers; // Time, R, G, B float timeline_position = 0.0f; bool is_playing = false; float playback_start_time = 0.0f; float current_time = 0.0f; float timeline_duration = 10.0f; // 10 seconds by default const int num_channels = 512; unsigned char buffer[num_channels] = { 0 }; GLFWwindow* initOpenGL(); HANDLE hSerial; std::atomic running(true); void dmxThreadFunc() { while (running) { // 25ms refresh rate sendDMXData(hSerial, buffer, num_channels); std::this_thread::sleep_for(std::chrono::milliseconds(25)); } } int main() { hSerial = CreateFileA("\\\\.\\COM3", GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hSerial == INVALID_HANDLE_VALUE) { std::cerr << "Error opening COM3 port" << std::endl; return 1; } DCB dcbSerialParams = { 0 }; dcbSerialParams.DCBlength = sizeof(dcbSerialParams); if (!GetCommState(hSerial, &dcbSerialParams)) { std::cerr << "Error getting COM3 state" << std::endl; CloseHandle(hSerial); return 1; } dcbSerialParams.BaudRate = 250000; dcbSerialParams.ByteSize = 8; dcbSerialParams.StopBits = TWOSTOPBITS; // <-- don't forget about this dcbSerialParams.Parity = NOPARITY; if (!SetCommState(hSerial, &dcbSerialParams)) { std::cerr << "Error setting COM3 state" << std::endl; CloseHandle(hSerial); return 1; } COMMTIMEOUTS timeouts = { 0 }; timeouts.ReadIntervalTimeout = 50; timeouts.ReadTotalTimeoutConstant = 50; timeouts.ReadTotalTimeoutMultiplier = 10; timeouts.WriteTotalTimeoutConstant = 50; timeouts.WriteTotalTimeoutMultiplier = 10; if (!SetCommTimeouts(hSerial, &timeouts)) { std::cerr << "Error setting COM3 timeouts" << std::endl; CloseHandle(hSerial); return 1; } GLFWwindow* window = initOpenGL(); if (!window) { CloseHandle(hSerial); return 1; } setupImGui(window); std::thread dmxThread(dmxThreadFunc); while (!glfwWindowShouldClose(window)) { glfwPollEvents(); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); renderImGui(); ImGui::Render(); int display_w, display_h; glfwGetFramebufferSize(window, &display_w, &display_h); glViewport(0, 0, display_w, display_h); glClearColor(0.45f, 0.55f, 0.60f, 1.00f); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); glfwSwapBuffers(window); } running = false; dmxThread.join(); ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); glfwDestroyWindow(window); glfwTerminate(); CloseHandle(hSerial); return 0; } void generateRandomColors(unsigned char& r, unsigned char& g, unsigned char& b) { r = rand() % 256; g = rand() % 256; b = rand() % 256; } void sendDMXData(HANDLE hSerial, unsigned char* data, int length) { // break condition EscapeCommFunction(hSerial, SETBREAK); std::this_thread::sleep_for(std::chrono::milliseconds(1)); EscapeCommFunction(hSerial, CLRBREAK); std::this_thread::sleep_for(std::chrono::milliseconds(1)); // this is the mab DWORD bytesWritten; unsigned char startCode = 0; WriteFile(hSerial, &startCode, 1, &bytesWritten, NULL); WriteFile(hSerial, data, length, &bytesWritten, NULL); } GLFWwindow* initOpenGL() { if (!glfwInit()) { std::cerr << "Failed to initialize GLFW" << std::endl; return nullptr; } GLFWwindow* window = glfwCreateWindow(1280, 720, "DMX Controller", NULL, NULL); if (!window) { std::cerr << "Failed to create GLFW window" << std::endl; glfwTerminate(); return nullptr; } glfwMakeContextCurrent(window); glfwSwapInterval(1); // I think this is vsync?? if (glewInit() != GLEW_OK) { std::cerr << "Failed to initialize GLEW" << std::endl; glfwDestroyWindow(window); glfwTerminate(); return nullptr; } return window; } void setupImGui(GLFWwindow* window) { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init("#version 130"); } void renderImGui() { // Temporary variables to hold the slider values int channel1 = buffer[0]; int channel2 = buffer[1]; int channel3 = buffer[2]; // Create a window ImGui::Begin("DMX Controller"); // Create sliders for the first three DMX channels ImGui::SliderInt("Channel 1", &channel1, 0, 255); ImGui::SliderInt("Channel 2", &channel2, 0, 255); ImGui::SliderInt("Channel 3", &channel3, 0, 255); // Assign the slider values back to the DMX buffer buffer[0] = static_cast(channel1); buffer[1] = static_cast(channel2); buffer[2] = static_cast(channel3); // Allow user to set the total duration of the timeline ImGui::SliderFloat("Timeline Duration (seconds)", &timeline_duration, 1.0f, 300.0f); // Timeline UI with time scale ImGui::Text("Timeline"); ImGui::SliderFloat("##Timeline", &timeline_position, 0.0f, timeline_duration, "%.2f sec"); if (ImGui::Button("Add Marker")) { markers.emplace_back(timeline_position / timeline_duration, buffer[0], buffer[1], buffer[2]); } ImGui::SameLine(); if (ImGui::Button(is_playing ? "Pause" : "Play")) { is_playing = !is_playing; if (is_playing) { playback_start_time = static_cast(glfwGetTime()); } } // Display markers with time indication for (const auto& marker : markers) { float marker_position = std::get<0>(marker) * timeline_duration; ImGui::Text("Marker at %.2f sec: R=%d, G=%d, B=%d", marker_position, std::get<1>(marker), std::get<2>(marker), std::get<3>(marker)); } // Playback logic with time synchronization if (is_playing) { current_time = static_cast(glfwGetTime()) - playback_start_time; for (const auto& marker : markers) { float marker_position = std::get<0>(marker) * timeline_duration; if (current_time >= marker_position) { buffer[0] = std::get<1>(marker); buffer[1] = std::get<2>(marker); buffer[2] = std::get<3>(marker); } } // Update the timeline slider position according to current playback time timeline_position = current_time; if (timeline_position >= timeline_duration) { is_playing = false; // Stop playback at the end timeline_position = timeline_duration; } } // Draw time scale ImGui::Separator(); ImGui::Text("Time Scale:"); for (float t = 0.0f; t <= timeline_duration; t += timeline_duration / 10.0f) { ImGui::SameLine(); ImGui::Text("%.1f sec", t); } ImGui::End(); }