From 1ac990b098911994d6967fbb8c7ed4e5afe298e6 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 4 Dec 2024 14:29:26 +0800 Subject: [PATCH] multi segment audio playback (still needs work) --- apps/desktop/src-tauri/src/recording.rs | 58 ++++++++++----------- apps/desktop/src/routes/editor/Editor.tsx | 22 -------- apps/desktop/src/routes/editor/Timeline.tsx | 55 +++++++------------ apps/desktop/src/routes/editor/context.ts | 30 ++++++++++- crates/editor/src/playback.rs | 7 +-- crates/flags/src/lib.rs | 4 +- crates/media/src/feeds/audio.rs | 1 + crates/recording/src/lib.rs | 2 +- 8 files changed, 85 insertions(+), 94 deletions(-) diff --git a/apps/desktop/src-tauri/src/recording.rs b/apps/desktop/src-tauri/src/recording.rs index 7b810b9e..8d7491fb 100644 --- a/apps/desktop/src-tauri/src/recording.rs +++ b/apps/desktop/src-tauri/src/recording.rs @@ -308,35 +308,35 @@ fn generate_zoom_segments_from_clicks( const ZOOM_SEGMENT_AFTER_CLICK_PADDING: f64 = 1.5; // single-segment only - for click in &recording.cursor_data.clicks { - let time = click.process_time_ms / 1000.0; - - if segments.last().is_none() { - segments.push(ZoomSegment { - start: (click.process_time_ms / 1000.0 - (ZOOM_DURATION + 0.2)).max(0.0), - end: click.process_time_ms / 1000.0 + ZOOM_SEGMENT_AFTER_CLICK_PADDING, - amount: 2.0, - }); - } else { - let last_segment = segments.last_mut().unwrap(); - - if click.down { - if last_segment.end > time { - last_segment.end = - (time + ZOOM_SEGMENT_AFTER_CLICK_PADDING).min(recordings.duration()); - } else if time < max_duration - ZOOM_DURATION { - segments.push(ZoomSegment { - start: (time - ZOOM_DURATION).max(0.0), - end: time + ZOOM_SEGMENT_AFTER_CLICK_PADDING, - amount: 2.0, - }); - } - } else { - last_segment.end = - (time + ZOOM_SEGMENT_AFTER_CLICK_PADDING).min(recordings.duration()); - } - } - } + // for click in &recording.cursor_data.clicks { + // let time = click.process_time_ms / 1000.0; + + // if segments.last().is_none() { + // segments.push(ZoomSegment { + // start: (click.process_time_ms / 1000.0 - (ZOOM_DURATION + 0.2)).max(0.0), + // end: click.process_time_ms / 1000.0 + ZOOM_SEGMENT_AFTER_CLICK_PADDING, + // amount: 2.0, + // }); + // } else { + // let last_segment = segments.last_mut().unwrap(); + + // if click.down { + // if last_segment.end > time { + // last_segment.end = + // (time + ZOOM_SEGMENT_AFTER_CLICK_PADDING).min(recordings.duration()); + // } else if time < max_duration - ZOOM_DURATION { + // segments.push(ZoomSegment { + // start: (time - ZOOM_DURATION).max(0.0), + // end: time + ZOOM_SEGMENT_AFTER_CLICK_PADDING, + // amount: 2.0, + // }); + // } + // } else { + // last_segment.end = + // (time + ZOOM_SEGMENT_AFTER_CLICK_PADDING).min(recordings.duration()); + // } + // } + // } segments } diff --git a/apps/desktop/src/routes/editor/Editor.tsx b/apps/desktop/src/routes/editor/Editor.tsx index 89761a20..6c7353af 100644 --- a/apps/desktop/src/routes/editor/Editor.tsx +++ b/apps/desktop/src/routes/editor/Editor.tsx @@ -119,28 +119,6 @@ function Inner() { ) ); - const togglePlayback = async () => { - try { - if (playing()) { - await commands.stopPlayback(videoId); - setPlaying(false); - } else { - await commands.startPlayback(videoId); - setPlaying(true); - } - } catch (error) { - console.error("Error toggling playback:", error); - setPlaying(false); - } - }; - - createEventListener(document, "keydown", async (e: KeyboardEvent) => { - if (e.code === "Space" && e.target === document.body) { - e.preventDefault(); - await togglePlayback(); - } - }); - return (
diff --git a/apps/desktop/src/routes/editor/Timeline.tsx b/apps/desktop/src/routes/editor/Timeline.tsx index d4255331..4f23900f 100644 --- a/apps/desktop/src/routes/editor/Timeline.tsx +++ b/apps/desktop/src/routes/editor/Timeline.tsx @@ -1,36 +1,29 @@ import "~/styles/timeline.css"; import { createElementBounds } from "@solid-primitives/bounds"; import { - Accessor, ComponentProps, For, Show, - createContext, createRoot, createSignal, onMount, - useContext, } from "solid-js"; import { createEventListenerMap } from "@solid-primitives/event-listener"; import { cx } from "cva"; -import { createStore, produce } from "solid-js/store"; +import { produce } from "solid-js/store"; import { mergeRefs } from "@solid-primitives/refs"; -import { createContextProvider } from "@solid-primitives/context"; import { createMemo } from "solid-js"; import { commands, TimelineSegment } from "~/utils/tauri"; -import { useEditorContext } from "./context"; +import { + TimelineContextProvider, + TrackContextProvider, + useEditorContext, + useTimelineContext, + useTrackContext, +} from "./context"; import { formatTime } from "./utils"; -const [TimelineContextProvider, useTimelineContext] = createContextProvider( - (props: { duration: number }) => { - return { - duration: () => props.duration, - }; - }, - null! -); - export function Timeline() { const { project, @@ -290,6 +283,12 @@ export function Timeline() { segment.recordingSegment ?? 0 ].display.duration; + console.log( + editorInstance.recordings.segments[ + segment.recordingSegment ?? 0 + ] + ); + const availableTimelineDuration = editorInstance.recordingDuration - segments().reduce( @@ -320,7 +319,11 @@ export function Timeline() { i(), "end", Math.max( - Math.min(newEnd, segment.start + maxDuration), + Math.min( + newEnd, + maxSegmentDuration, + availableTimelineDuration + ), segment.start + 1 ) ); @@ -612,26 +615,6 @@ export function Timeline() { ); } -const [TrackContextProvider, useTrackContext] = createContextProvider( - (props: { - ref: Accessor; - isFreeForm: Accessor; - }) => { - const [trackState, setTrackState] = createStore({ - draggingHandle: false, - }); - const bounds = createElementBounds(() => props.ref()); - - return { - trackBounds: bounds, - isFreeForm: () => props.isFreeForm(), - trackState, - setTrackState, - }; - }, - null! -); - function TrackRoot(props: ComponentProps<"div"> & { isFreeForm: boolean }) { const [ref, setRef] = createSignal(); diff --git a/apps/desktop/src/routes/editor/context.ts b/apps/desktop/src/routes/editor/context.ts index 7285da2c..47f7e8b6 100644 --- a/apps/desktop/src/routes/editor/context.ts +++ b/apps/desktop/src/routes/editor/context.ts @@ -3,7 +3,7 @@ import { trackStore } from "@solid-primitives/deep"; import { createEventListener } from "@solid-primitives/event-listener"; import { createUndoHistory } from "@solid-primitives/history"; import { debounce } from "@solid-primitives/scheduled"; -import { createEffect, createSignal, on } from "solid-js"; +import { Accessor, createEffect, createSignal, on } from "solid-js"; import { createStore, reconcile, unwrap } from "solid-js/store"; import type { PresetsStore } from "../../store"; @@ -15,6 +15,7 @@ import { } from "~/utils/tauri"; import { useEditorInstanceContext } from "./editorInstanceContext"; import { DEFAULT_PROJECT_CONFIG } from "./projectConfig"; +import { createElementBounds } from "@solid-primitives/bounds"; export type CurrentDialog = | { type: "createPreset" } @@ -148,3 +149,30 @@ type Static = [K in number | string]: T; } | T[]; + +export const [TimelineContextProvider, useTimelineContext] = + createContextProvider((props: { duration: number }) => { + return { + duration: () => props.duration, + }; + }, null!); + +export const [TrackContextProvider, useTrackContext] = createContextProvider( + (props: { + ref: Accessor; + isFreeForm: Accessor; + }) => { + const [trackState, setTrackState] = createStore({ + draggingHandle: false, + }); + const bounds = createElementBounds(() => props.ref()); + + return { + trackBounds: bounds, + isFreeForm: () => props.isFreeForm(), + trackState, + setTrackState, + }; + }, + null! +); diff --git a/crates/editor/src/playback.rs b/crates/editor/src/playback.rs index 381bb6e0..28991e43 100644 --- a/crates/editor/src/playback.rs +++ b/crates/editor/src/playback.rs @@ -216,9 +216,10 @@ impl AudioPlayback { audio_renderer.set_playhead(playhead, project.borrow().timeline()); // Prerender enough for smooth playback - while !audio_renderer.buffer_reaching_limit() { - audio_renderer.render(project.borrow().timeline()); - } + // Disabled for now as it was infinite looping + // while !audio_renderer.buffer_reaching_limit() { + // audio_renderer.render(project.borrow().timeline()); + // } let mut config = supported_config.config(); // Low-latency playback diff --git a/crates/flags/src/lib.rs b/crates/flags/src/lib.rs index 550e9a80..be57136c 100644 --- a/crates/flags/src/lib.rs +++ b/crates/flags/src/lib.rs @@ -11,7 +11,7 @@ pub struct Flags { pub const FLAGS: Flags = Flags { record_mouse: false, // cfg!(debug_assertions), split: false, // cfg!(debug_assertions), - pause_resume: false, // cfg!(debug_assertions), - zoom: false, // cfg!(debug_assertions), + pause_resume: cfg!(debug_assertions), + zoom: false, // cfg!(debug_assertions), custom_s3: true, }; diff --git a/crates/media/src/feeds/audio.rs b/crates/media/src/feeds/audio.rs index f0bbb12e..ed9a1e2e 100644 --- a/crates/media/src/feeds/audio.rs +++ b/crates/media/src/feeds/audio.rs @@ -156,6 +156,7 @@ impl AudioFrameBuffer { let buffer = &self.data[self.cursor.0].buffer; if self.cursor.1 >= buffer.len() { + self.elapsed_samples += samples; return None; } diff --git a/crates/recording/src/lib.rs b/crates/recording/src/lib.rs index edbfd40c..486896bd 100644 --- a/crates/recording/src/lib.rs +++ b/crates/recording/src/lib.rs @@ -2,7 +2,7 @@ pub mod actor; mod cursor; pub mod segmented_actor; -pub use actor::{spawn_recording_actor, ActorHandle, CompletedRecording, RecordingError}; +pub use segmented_actor::{spawn_recording_actor, ActorHandle, CompletedRecording, RecordingError}; use cap_media::sources::*; use serde::{Deserialize, Serialize};