From d71a584f19fb945ea1e1c77b1c5018f1f05cc3ab Mon Sep 17 00:00:00 2001 From: illyum <90023277+itzilly@users.noreply.github.com> Date: Thu, 8 Aug 2024 23:10:01 -0600 Subject: [PATCH] Add timeline and replay feature --- main.cpp | 111 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 76 insertions(+), 35 deletions(-) diff --git a/main.cpp b/main.cpp index e135f18..4d7b193 100644 --- a/main.cpp +++ b/main.cpp @@ -9,40 +9,45 @@ #include "imgui_impl_opengl3.h" #include -// Forward declarations + 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(); -// DMX data buffer + +// 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 }; -// Function to initialize OpenGL and create a window GLFWwindow* initOpenGL(); -// Serial port handle HANDLE hSerial; std::atomic running(true); void dmxThreadFunc() { while (running) { - // Send DMX data with a refresh rate of 25ms + // 25ms refresh rate sendDMXData(hSerial, buffer, num_channels); std::this_thread::sleep_for(std::chrono::milliseconds(25)); } } int main() { - // Open the serial port 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; } - // Configure the serial port DCB dcbSerialParams = { 0 }; dcbSerialParams.DCBlength = sizeof(dcbSerialParams); if (!GetCommState(hSerial, &dcbSerialParams)) { @@ -51,9 +56,9 @@ int main() { return 1; } - dcbSerialParams.BaudRate = 250000; // DMX512 uses 250000 baud + dcbSerialParams.BaudRate = 250000; dcbSerialParams.ByteSize = 8; - dcbSerialParams.StopBits = TWOSTOPBITS; // DMX512 uses 2 stop bits + dcbSerialParams.StopBits = TWOSTOPBITS; // <-- don't forget about this dcbSerialParams.Parity = NOPARITY; if (!SetCommState(hSerial, &dcbSerialParams)) { @@ -62,7 +67,6 @@ int main() { return 1; } - // Set timeouts COMMTIMEOUTS timeouts = { 0 }; timeouts.ReadIntervalTimeout = 50; timeouts.ReadTotalTimeoutConstant = 50; @@ -76,33 +80,24 @@ int main() { return 1; } - // Initialize OpenGL and create a window GLFWwindow* window = initOpenGL(); if (!window) { CloseHandle(hSerial); return 1; } - // Setup ImGui setupImGui(window); - - // Start the DMX thread std::thread dmxThread(dmxThreadFunc); - // Main loop while (!glfwWindowShouldClose(window)) { - // Poll events glfwPollEvents(); - // Start the ImGui frame ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); - // Render ImGui components renderImGui(); - // Rendering ImGui::Render(); int display_w, display_h; glfwGetFramebufferSize(window, &display_w, &display_h); @@ -111,15 +106,12 @@ int main() { glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - // Swap buffers glfwSwapBuffers(window); } - // Stop the DMX thread running = false; dmxThread.join(); - // Cleanup ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); @@ -137,13 +129,12 @@ void generateRandomColors(unsigned char& r, unsigned char& g, unsigned char& b) } void sendDMXData(HANDLE hSerial, unsigned char* data, int length) { - // Start with a DMX break condition + // break condition EscapeCommFunction(hSerial, SETBREAK); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); // Break duration + std::this_thread::sleep_for(std::chrono::milliseconds(1)); EscapeCommFunction(hSerial, CLRBREAK); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); // Mark-after-break duration + std::this_thread::sleep_for(std::chrono::milliseconds(1)); // this is the mab - // Write the start code followed by the DMX data DWORD bytesWritten; unsigned char startCode = 0; WriteFile(hSerial, &startCode, 1, &bytesWritten, NULL); @@ -151,13 +142,11 @@ void sendDMXData(HANDLE hSerial, unsigned char* data, int length) { } GLFWwindow* initOpenGL() { - // Initialize GLFW if (!glfwInit()) { std::cerr << "Failed to initialize GLFW" << std::endl; return nullptr; } - // Create a windowed mode window and its OpenGL context GLFWwindow* window = glfwCreateWindow(1280, 720, "DMX Controller", NULL, NULL); if (!window) { std::cerr << "Failed to create GLFW window" << std::endl; @@ -165,11 +154,9 @@ GLFWwindow* initOpenGL() { return nullptr; } - // Make the window's context current glfwMakeContextCurrent(window); - glfwSwapInterval(1); // Enable vsync + glfwSwapInterval(1); // I think this is vsync?? - // Initialize GLEW if (glewInit() != GLEW_OK) { std::cerr << "Failed to initialize GLEW" << std::endl; glfwDestroyWindow(window); @@ -181,15 +168,12 @@ GLFWwindow* initOpenGL() { } void setupImGui(GLFWwindow* window) { - // Setup ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - // Setup ImGui style ImGui::StyleColorsDark(); - // Setup Platform/Renderer bindings ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init("#version 130"); } @@ -213,5 +197,62 @@ void renderImGui() { 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(); -} +} \ No newline at end of file