Compare commits
3 Commits
2dfd52e33d
...
e3c843d65c
| Author | SHA1 | Date | |
|---|---|---|---|
| e3c843d65c | |||
| 530238a328 | |||
| d7b3824e1e |
@@ -15,9 +15,9 @@ Video File: 274_transect.mp4
|
||||
Video AR Mode: 4
|
||||
Video AR Value: 1.777778
|
||||
Video Zoom Percent: 0.500000
|
||||
Scroll Position: 107
|
||||
Active Line: 124
|
||||
Video Position: 18574
|
||||
Scroll Position: 74
|
||||
Active Line: 82
|
||||
Video Position: 8587
|
||||
|
||||
[V4+ Styles]
|
||||
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
||||
@@ -123,7 +123,7 @@ Dialogue: 0,0:04:38.70,0:04:41.02,274-place,,0,0,0,,福山大橋
|
||||
Dialogue: 0,0:04:41.02,0:04:43.29,274-place,,0,0,0,,千坂橋
|
||||
Dialogue: 0,0:04:43.29,0:04:45.12,274-place,,0,0,0,,オコタン橋
|
||||
Dialogue: 0,0:04:45.12,0:04:46.22,274-place,,0,0,0,,モトツトンネル
|
||||
Dialogue: 0,0:04:46.22,0:04:48.98,274-place,,0,0,0,,モトツ橋にモトツシェルター
|
||||
Dialogue: 0,0:04:46.22,0:04:48.98,274-place,,0,0,0,,モトツ橋 / モトツシェルター
|
||||
Dialogue: 0,0:04:48.98,0:04:51.39,274-place,,0,0,0,,福山トンネル
|
||||
Dialogue: 0,0:04:51.39,0:04:53.61,274-place,,0,0,0,,福穂橋
|
||||
Dialogue: 0,0:04:53.61,0:04:55.52,274-place,,0,0,0,,穂高トンネル
|
||||
|
||||
@@ -4,7 +4,7 @@ import { loadTransectData } from "./data";
|
||||
import { darkPalette } from "./palette";
|
||||
import { Transect, type TransectProps } from "./Transect";
|
||||
|
||||
const FPS = 30;
|
||||
const FPS = 60;
|
||||
/** Length of song-274/audio/274_release2.wav (see BOM.md). */
|
||||
const AUDIO_DURATION_S = 619.270083;
|
||||
|
||||
|
||||
@@ -27,6 +27,11 @@ export const ZOOM_IN_S = 3;
|
||||
* This is the standard deviation, in seconds; the window spans ±3 of these.
|
||||
*/
|
||||
export const FOLLOW_SMOOTH_S = 0.6;
|
||||
/**
|
||||
* How often that window is sampled, in seconds. Independent of fps: finer
|
||||
* sampling costs more per frame and changes the average only slightly.
|
||||
*/
|
||||
export const FOLLOW_SAMPLE_S = 1 / 30;
|
||||
/**
|
||||
* An overview frames the covered road plus some road ahead of the head, so the
|
||||
* head does not sit against the frame edge: this fraction of the distance
|
||||
@@ -95,7 +100,7 @@ export const overviewWeight = (parts: Span[], t: number): number => {
|
||||
|
||||
/** The head's position averaged over a Gaussian window around `t`. */
|
||||
export const smoothedFocus = (focusAt: (t: number) => Point, t: number): Point => {
|
||||
const step = 1 / 30;
|
||||
const step = FOLLOW_SAMPLE_S;
|
||||
const reach = 3 * FOLLOW_SMOOTH_S;
|
||||
let sumX = 0;
|
||||
let sumY = 0;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
/** Seconds of complete black at start */
|
||||
export const FADE_IN_PRE = 0.4;
|
||||
/** Seconds of complete black at end */
|
||||
export const FADE_OUT_POST = 0.4;
|
||||
/** Seconds from black to the picture at the start. 0 disables the fade-in. */
|
||||
export const FADE_IN_S = 0.3;
|
||||
/** Seconds from the picture to black at the end. 0 disables the fade-out. */
|
||||
@@ -19,6 +21,8 @@ const clamp01 = (v: number) => Math.min(1, Math.max(0, v));
|
||||
*/
|
||||
export const blackOpacity = (t: number, lastFrameS: number): number => {
|
||||
const fadeIn = (t < FADE_IN_PRE) ? 1 : (FADE_IN_S > 0 ? 1 - clamp01((t - FADE_IN_PRE) / FADE_IN_S) : 0);
|
||||
const fadeOut = FADE_OUT_S > 0 ? clamp01((t - (lastFrameS - FADE_OUT_S)) / FADE_OUT_S) : 0;
|
||||
// Black from here to the last frame; the fade-out ramp ends exactly here.
|
||||
const blackFromS = lastFrameS - FADE_OUT_POST;
|
||||
const fadeOut = (t >= blackFromS) ? 1 : (FADE_OUT_S > 0 ? clamp01((t - (blackFromS - FADE_OUT_S)) / FADE_OUT_S) : 0);
|
||||
return Math.max(fadeIn, fadeOut);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { visualizeAudio, type MediaUtilsAudioData } from "@remotion/media-utils";
|
||||
import { random } from "remotion";
|
||||
import { secondsToFrames } from "./time";
|
||||
|
||||
/**
|
||||
* The outro spectrum: from SPECTRUM_START_S the nodes stop following the lyric
|
||||
@@ -43,11 +44,16 @@ export const TREBLE_TILT = 0.5;
|
||||
export const CURVE = 0.6;
|
||||
|
||||
/**
|
||||
* Release: a node's level is the loudest of its last RELEASE_FRAMES frames, each
|
||||
* frame back multiplied by RELEASE_DECAY once more. Hits jump up at once and
|
||||
* fall back smoothly, without any state carried between frames.
|
||||
* Release: a node's level is the loudest reading over the RELEASE_WINDOW_S
|
||||
* before it, sampled every RELEASE_STEP_S and multiplied by RELEASE_DECAY once
|
||||
* more per step back. Hits jump up at once and fall back smoothly, without any
|
||||
* state carried between frames.
|
||||
*
|
||||
* Sampling by time rather than by frame keeps the tail the same length, and the
|
||||
* work per frame the same, whatever the fps.
|
||||
*/
|
||||
export const RELEASE_FRAMES = 6;
|
||||
export const RELEASE_WINDOW_S = 0.2;
|
||||
export const RELEASE_STEP_S = 1 / 30;
|
||||
export const RELEASE_DECAY = 0.75;
|
||||
|
||||
/** FFT size for visualizeAudio: bins are (sample rate / 2) / FFT_SAMPLES wide (23.4 Hz). */
|
||||
@@ -69,7 +75,7 @@ export const spectrumWeight = (t: number): number =>
|
||||
* otherwise the spectrum's start, so nothing is fetched for the rest of the song.
|
||||
*/
|
||||
export const audioWindowFrame = (frame: number, fps: number): number =>
|
||||
Math.max(frame, Math.floor((SPECTRUM_START_S - SPECTRUM_FADE_S) * fps));
|
||||
Math.max(frame, secondsToFrames(SPECTRUM_START_S - SPECTRUM_FADE_S, fps));
|
||||
|
||||
/** `count` log-spaced band centres from FREQ_LOW_HZ to FREQ_HIGH_HZ. */
|
||||
export const bandFrequencies = (count: number): number[] =>
|
||||
@@ -131,7 +137,12 @@ export const bandLevels = (args: {
|
||||
frequencies.length > 1 ? Math.pow(FREQ_HIGH_HZ / FREQ_LOW_HZ, 1 / (frequencies.length - 1)) : 2;
|
||||
const levels = frequencies.map(() => 0);
|
||||
|
||||
for (let back = 0; back < RELEASE_FRAMES && frame - back >= 0; back++) {
|
||||
const steps = Math.max(1, Math.round(RELEASE_WINDOW_S / RELEASE_STEP_S));
|
||||
for (let step = 0; step < steps; step++) {
|
||||
const back = secondsToFrames(step * RELEASE_STEP_S, fps);
|
||||
if (frame - back < 0) {
|
||||
break;
|
||||
}
|
||||
const spectrum = visualizeAudio({
|
||||
audioData,
|
||||
frame: frame - back,
|
||||
@@ -140,7 +151,7 @@ export const bandLevels = (args: {
|
||||
dataOffsetInSeconds,
|
||||
optimizeFor: "accuracy",
|
||||
});
|
||||
const decay = Math.pow(RELEASE_DECAY, back);
|
||||
const decay = Math.pow(RELEASE_DECAY, step);
|
||||
frequencies.forEach((hz, i) => {
|
||||
const raw = bandValue(spectrum, hz, ratio, binHz);
|
||||
const level =
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Frame-rate conversions.
|
||||
*
|
||||
* Every timing constant in this composition is in seconds, so the animation is
|
||||
* identical at any fps. Only Remotion's own API counts frames, so convert here
|
||||
* rather than writing frame counts into the constants.
|
||||
*/
|
||||
|
||||
/** Seconds to a whole number of frames, rounded to the nearest. */
|
||||
export const secondsToFrames = (seconds: number, fps: number): number =>
|
||||
Math.round(seconds * fps);
|
||||
|
||||
/** A frame number back to its time in seconds. */
|
||||
export const framesToSeconds = (frames: number, fps: number): number => frames / fps;
|
||||
Reference in New Issue
Block a user