diff --git a/main.cpp b/main.cpp index 4d7b193..be07f81 100644 --- a/main.cpp +++ b/main.cpp @@ -23,6 +23,9 @@ bool is_playing = false; float playback_start_time = 0.0f; float current_time = 0.0f; float timeline_duration = 10.0f; // 10 seconds by default +float bpm = 120.0f; // Default BPM +float zoom_level = 1.0f; // Zoom level +float pan_offset = 0.0f; // Pan offset const int num_channels = 512; @@ -178,7 +181,11 @@ void setupImGui(GLFWwindow* window) { ImGui_ImplOpenGL3_Init("#version 130"); } + void renderImGui() { + // Calculate time per beat based on BPM + float beat_interval = 60.0f / bpm; + // Temporary variables to hold the slider values int channel1 = buffer[0]; int channel2 = buffer[1]; @@ -197,18 +204,11 @@ void renderImGui() { buffer[1] = static_cast(channel2); buffer[2] = static_cast(channel3); - // Allow user to set the total duration of the timeline + // Allow user to set the total duration of the timeline and BPM ImGui::SliderFloat("Timeline Duration (seconds)", &timeline_duration, 1.0f, 300.0f); + ImGui::SliderFloat("BPM", &bpm, 30.0f, 240.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(); + // Play/Pause button if (ImGui::Button(is_playing ? "Pause" : "Play")) { is_playing = !is_playing; if (is_playing) { @@ -216,43 +216,57 @@ void renderImGui() { } } - // 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)); - } + // Time Slider (Timeline Position) + ImGui::SliderFloat("Timeline Position", &timeline_position, 0.0f, timeline_duration, "%.2f sec"); - // 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); + // Custom timeline rendering with grid lines + ImGui::Text("Timeline"); + ImGui::PushID("CustomTimeline"); + ImGui::BeginChild("##Timeline", ImVec2(ImGui::GetContentRegionAvail().x, 100), true); + + // Draw grid lines for beats + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + ImVec2 cursor_pos = ImGui::GetCursorScreenPos(); + float timeline_width = ImGui::GetContentRegionAvail().x; + + for (int i = 0; i <= static_cast(timeline_duration / beat_interval); ++i) { + float beat_position = i * beat_interval * zoom_level - pan_offset; + float x_position = cursor_pos.x + (beat_position / timeline_duration) * timeline_width; + + if (x_position >= cursor_pos.x && x_position <= cursor_pos.x + timeline_width) { + // Draw major beat line + if (i % 4 == 0) { + draw_list->AddLine(ImVec2(x_position, cursor_pos.y), ImVec2(x_position, cursor_pos.y + 100), IM_COL32(255, 0, 0, 255), 2.0f); + } + else { + // Draw minor beat line + draw_list->AddLine(ImVec2(x_position, cursor_pos.y), ImVec2(x_position, cursor_pos.y + 100), IM_COL32(255, 255, 255, 255), 1.0f); } } + } - // 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; + // Allow adding markers with snapping to grid + if (ImGui::Button("Add Marker")) { + float snap_position = std::round(timeline_position / beat_interval) * beat_interval; + markers.emplace_back(snap_position / timeline_duration, buffer[0], buffer[1], buffer[2]); + } + + // Draw markers on the timeline + for (const auto& marker : markers) { + float marker_position = std::get<0>(marker) * timeline_duration * zoom_level - pan_offset; + float x_position = cursor_pos.x + (marker_position / timeline_duration) * timeline_width; + + if (x_position >= cursor_pos.x && x_position <= cursor_pos.x + timeline_width) { + draw_list->AddRectFilled(ImVec2(x_position - 2.0f, cursor_pos.y), ImVec2(x_position + 2.0f, cursor_pos.y + 100), IM_COL32(0, 255, 0, 255)); } } - // 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::EndChild(); + ImGui::PopID(); + + // Place Zoom and Pan sliders below the timeline to avoid overlap + ImGui::SliderFloat("Zoom", &zoom_level, 0.1f, 10.0f); + ImGui::SliderFloat("Pan", &pan_offset, 0.0f, timeline_duration * zoom_level); ImGui::End(); } \ No newline at end of file