Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e3c843d65c | |||
| 530238a328 | |||
| d7b3824e1e |
@@ -15,9 +15,9 @@ Video File: 274_transect.mp4
|
|||||||
Video AR Mode: 4
|
Video AR Mode: 4
|
||||||
Video AR Value: 1.777778
|
Video AR Value: 1.777778
|
||||||
Video Zoom Percent: 0.500000
|
Video Zoom Percent: 0.500000
|
||||||
Scroll Position: 107
|
Scroll Position: 74
|
||||||
Active Line: 124
|
Active Line: 82
|
||||||
Video Position: 18574
|
Video Position: 8587
|
||||||
|
|
||||||
[V4+ Styles]
|
[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
|
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: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: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: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: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: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,,穂高トンネル
|
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 { darkPalette } from "./palette";
|
||||||
import { Transect, type TransectProps } from "./Transect";
|
import { Transect, type TransectProps } from "./Transect";
|
||||||
|
|
||||||
const FPS = 30;
|
const FPS = 60;
|
||||||
/** Length of song-274/audio/274_release2.wav (see BOM.md). */
|
/** Length of song-274/audio/274_release2.wav (see BOM.md). */
|
||||||
const AUDIO_DURATION_S = 619.270083;
|
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.
|
* This is the standard deviation, in seconds; the window spans ±3 of these.
|
||||||
*/
|
*/
|
||||||
export const FOLLOW_SMOOTH_S = 0.6;
|
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
|
* 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
|
* 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`. */
|
/** The head's position averaged over a Gaussian window around `t`. */
|
||||||
export const smoothedFocus = (focusAt: (t: number) => Point, t: number): Point => {
|
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;
|
const reach = 3 * FOLLOW_SMOOTH_S;
|
||||||
let sumX = 0;
|
let sumX = 0;
|
||||||
let sumY = 0;
|
let sumY = 0;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
/** Seconds of complete black at start */
|
/** Seconds of complete black at start */
|
||||||
export const FADE_IN_PRE = 0.4;
|
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. */
|
/** Seconds from black to the picture at the start. 0 disables the fade-in. */
|
||||||
export const FADE_IN_S = 0.3;
|
export const FADE_IN_S = 0.3;
|
||||||
/** Seconds from the picture to black at the end. 0 disables the fade-out. */
|
/** 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 => {
|
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 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);
|
return Math.max(fadeIn, fadeOut);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { visualizeAudio, type MediaUtilsAudioData } from "@remotion/media-utils";
|
import { visualizeAudio, type MediaUtilsAudioData } from "@remotion/media-utils";
|
||||||
import { random } from "remotion";
|
import { random } from "remotion";
|
||||||
|
import { secondsToFrames } from "./time";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The outro spectrum: from SPECTRUM_START_S the nodes stop following the lyric
|
* 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;
|
export const CURVE = 0.6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release: a node's level is the loudest of its last RELEASE_FRAMES frames, each
|
* Release: a node's level is the loudest reading over the RELEASE_WINDOW_S
|
||||||
* frame back multiplied by RELEASE_DECAY once more. Hits jump up at once and
|
* before it, sampled every RELEASE_STEP_S and multiplied by RELEASE_DECAY once
|
||||||
* fall back smoothly, without any state carried between frames.
|
* 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;
|
export const RELEASE_DECAY = 0.75;
|
||||||
|
|
||||||
/** FFT size for visualizeAudio: bins are (sample rate / 2) / FFT_SAMPLES wide (23.4 Hz). */
|
/** 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.
|
* otherwise the spectrum's start, so nothing is fetched for the rest of the song.
|
||||||
*/
|
*/
|
||||||
export const audioWindowFrame = (frame: number, fps: number): number =>
|
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. */
|
/** `count` log-spaced band centres from FREQ_LOW_HZ to FREQ_HIGH_HZ. */
|
||||||
export const bandFrequencies = (count: number): number[] =>
|
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;
|
frequencies.length > 1 ? Math.pow(FREQ_HIGH_HZ / FREQ_LOW_HZ, 1 / (frequencies.length - 1)) : 2;
|
||||||
const levels = frequencies.map(() => 0);
|
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({
|
const spectrum = visualizeAudio({
|
||||||
audioData,
|
audioData,
|
||||||
frame: frame - back,
|
frame: frame - back,
|
||||||
@@ -140,7 +151,7 @@ export const bandLevels = (args: {
|
|||||||
dataOffsetInSeconds,
|
dataOffsetInSeconds,
|
||||||
optimizeFor: "accuracy",
|
optimizeFor: "accuracy",
|
||||||
});
|
});
|
||||||
const decay = Math.pow(RELEASE_DECAY, back);
|
const decay = Math.pow(RELEASE_DECAY, step);
|
||||||
frequencies.forEach((hz, i) => {
|
frequencies.forEach((hz, i) => {
|
||||||
const raw = bandValue(spectrum, hz, ratio, binHz);
|
const raw = bandValue(spectrum, hz, ratio, binHz);
|
||||||
const level =
|
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