diff --git a/find-loudness.ts b/find-loudness.ts new file mode 100644 index 0000000..500a716 --- /dev/null +++ b/find-loudness.ts @@ -0,0 +1,56 @@ +import { createReadStream } from "fs"; + +export type SwapPoint = { + position_start: number; + duration: number; + loud: boolean; + label: string; +}; + +export default async function ( + file: import("fs").PathLike, + threshold_at_point: number, + inertia_samples: number, + label: string +): Promise { + const stream = createReadStream(file); + let position = 0; + const results: SwapPoint[] = []; + let last_swap_position = 0; + let keep_loud_until = 0; + let was_loud_last_time = false; + return new Promise((resolve, reject) => { + stream.on("readable", () => { + let chunk: Buffer | null; + while ((chunk = stream.read()) !== null) { + for (let i = 0; i < chunk.byteLength; i++) { + position++; + const byte = chunk[i]; + const volume = Math.abs(byte - 128); + if (position >= keep_loud_until) { + const is_loud: boolean = volume > threshold_at_point; + if (is_loud != was_loud_last_time) { + const swap_point = { + position_start: last_swap_position, + duration: position - last_swap_position, + loud: was_loud_last_time, + label, + }; + results.push(swap_point); + last_swap_position = position; + was_loud_last_time = is_loud; + } + } + if (volume > threshold_at_point) { + keep_loud_until = position + inertia_samples; + } + } + } + }); + stream.on("end", () => { + resolve(results); + }); + + stream.on("error", reject); + }); +} diff --git a/generate.sh b/generate.sh new file mode 100755 index 0000000..f08dcfb --- /dev/null +++ b/generate.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# +# Musisz mieć globalnie zainstalowane ts-node (npm install -g ts-node) +# +# W katalogu z tym skryptem musisz mieć katalog "pics", w którym są pliki "left.png", "right.png", "none.png" i "both.png" +# + +input=/home/kuba/Downloads/podcast-01-after-effects.mp3 # tutaj dajemy ścieżkę do pliku mp3 z Arkiem w jednym kanale i Kubą w drugim + +aresample=8000 # to bez zmian + +echo dzielimy mp3 na dwa osobne wav +ffmpeg -i $input -map_channel 0.0.0 /tmp/left.wav -map_channel 0.0.1 /tmp/right.wav + +echo na dwóch wątkach generujemy surowe pliki +ffmpeg -i /tmp/left.wav -ac 1 -filter:a aresample=$aresample -map 0:a -c:a pcm_u8 -f data - > /tmp/leftraw & +ffmpeg -i /tmp/right.wav -ac 1 -filter:a aresample=$aresample -map 0:a -c:a pcm_u8 -f data - > /tmp/rightraw & + +# czekamy aż obydwa wątki się zakończą +wait; + +echo "generating the demuxers..."; + +# generuje ścieżki do złożenia przez ffmpega: +ts-node generate-demuxer.ts > /tmp/demuxer.txt + +mkdir -f out + +# używa demuxer.txt żeby skleić końcowe video z dźwiękiem: +echo generowanie całości +ffmpeg -y -f concat -i /tmp/demuxer.txt -r 30 -tune stillimage -vsync vfr -pix_fmt yuv420p out/video.mp4 + +echo łączenie video z dźwiękiem: +ffmpeg -i video.mp4 -i $input -ac 1 -tune stillimage out/video-and-audio.mp4 diff --git a/pics/both.png b/pics/both.png new file mode 100644 index 0000000..199dca3 Binary files /dev/null and b/pics/both.png differ diff --git a/pics/left.png b/pics/left.png new file mode 100644 index 0000000..9bfb28a Binary files /dev/null and b/pics/left.png differ diff --git a/pics/none.png b/pics/none.png new file mode 100644 index 0000000..1a6ab25 Binary files /dev/null and b/pics/none.png differ diff --git a/pics/right.png b/pics/right.png new file mode 100644 index 0000000..9f891d3 Binary files /dev/null and b/pics/right.png differ diff --git a/window.ts b/window.ts new file mode 100644 index 0000000..399d6ea --- /dev/null +++ b/window.ts @@ -0,0 +1,26 @@ +export default class Window { + length: number; + buffer: number[] = []; + constructor(length: number) { + this.length = length; + } + + push(element: number) { + if (this.buffer.length == this.length) { + this.buffer.shift(); + } + this.buffer.push(element); + } + + sum() { + return this.buffer.reduce((a, b) => a + b, 0); + } + + clear() { + this.buffer = []; + } + + isFull() { + return this.buffer.length == this.length; + } +}