Checkpoint 2
This commit is contained in:
parent
db73d04f0d
commit
8c5cfd88d9
|
@ -1,65 +1,88 @@
|
||||||
const graph_density = 1000;
|
import findLoudness, { SwapPoint } from "./find-loudness";
|
||||||
|
|
||||||
|
const graph_density = 8000;
|
||||||
|
|
||||||
const threshold_at_point = 1;
|
const threshold_at_point = 1;
|
||||||
|
|
||||||
const inertia_s = 0.1;
|
const inertia_s = 0.3;
|
||||||
const inertia_samples = inertia_s * graph_density;
|
const inertia_samples = inertia_s * graph_density;
|
||||||
|
|
||||||
let position = 0;
|
const s = (n: number) => n / graph_density;
|
||||||
let last_swap_position = 0;
|
|
||||||
|
|
||||||
const s = (n: number) => Math.round(n / graph_density);
|
|
||||||
|
|
||||||
const minutes = (units: number) => Math.floor(s(units) / 60);
|
const minutes = (units: number) => Math.floor(s(units) / 60);
|
||||||
|
|
||||||
|
const hours = (units: number) => Math.floor(units / graph_density / 60 / 60);
|
||||||
|
|
||||||
const formatTime = (units: number) =>
|
const formatTime = (units: number) =>
|
||||||
`${minutes(units)}:${Math.floor(s(units) % 60)}`;
|
`${hours(units)}:${minutes(units)}:${Math.floor(s(units) % 60)}`;
|
||||||
|
|
||||||
let keep_loud_until = 0;
|
type Mode = { left: boolean; right: boolean };
|
||||||
|
|
||||||
let total_speaking = 0;
|
async function run() {
|
||||||
|
const [left_breaks, right_breaks] = await Promise.all([
|
||||||
const results: [string, number][] = [];
|
findLoudness(
|
||||||
|
"/tmp/leftraw",
|
||||||
process.stdin.on("readable", () => {
|
threshold_at_point,
|
||||||
let chunk: Buffer | null;
|
inertia_samples,
|
||||||
let was_loud_last_time = false;
|
"left"
|
||||||
while ((chunk = process.stdin.read()) !== null) {
|
),
|
||||||
for (let i = 0; i < chunk.byteLength; i++) {
|
findLoudness(
|
||||||
position++;
|
"/tmp/rightraw",
|
||||||
const byte = chunk[i];
|
threshold_at_point,
|
||||||
const volume = Math.abs(byte - 128);
|
inertia_samples,
|
||||||
const is_loud: boolean =
|
"right"
|
||||||
volume > threshold_at_point || position < keep_loud_until;
|
),
|
||||||
if (is_loud) {
|
|
||||||
total_speaking++;
|
|
||||||
}
|
|
||||||
if (is_loud != was_loud_last_time) {
|
|
||||||
results.push([
|
|
||||||
is_loud ? "silence" : "speaking",
|
|
||||||
position - last_swap_position,
|
|
||||||
]);
|
]);
|
||||||
last_swap_position = position;
|
|
||||||
was_loud_last_time = is_loud;
|
const merged = [...left_breaks, ...right_breaks].sort((a, b) =>
|
||||||
|
a.position_start < b.position_start
|
||||||
|
? -1
|
||||||
|
: a.position_start > b.position_start
|
||||||
|
? 1
|
||||||
|
: 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// console.log("left breaks:", left_breaks);
|
||||||
|
// console.log(`right_breaks`, right_breaks);
|
||||||
|
// console.log(`merged`, merged);
|
||||||
|
|
||||||
|
function new_mode(m: Mode, s: SwapPoint): Mode {
|
||||||
|
return { ...m, [s.label]: s.loud };
|
||||||
}
|
}
|
||||||
if (volume > threshold_at_point) {
|
|
||||||
keep_loud_until = position + inertia_samples;
|
function mode_to_string(mode: Mode) {
|
||||||
|
if (mode.left && mode.right) {
|
||||||
|
return "both";
|
||||||
|
}
|
||||||
|
for (const side of ["left", "right"]) {
|
||||||
|
if (mode[side as keyof Mode]) {
|
||||||
|
return side;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return "none";
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
const mode_to_image = (mode: string) => {
|
console.log("file", `${process.cwd()}/pics/none.png`);
|
||||||
return mode === "silence" ? "pics/cisza.png" : "pics/kuba.png";
|
let last_point = 0;
|
||||||
};
|
let mode: Mode = { left: false, right: false };
|
||||||
|
let last_file;
|
||||||
process.stdin.on("end", () => {
|
let total = 0;
|
||||||
results.forEach(([mode, duration_units]) => {
|
for (let i = 2; i < merged.length; i++) {
|
||||||
console.log("file", `'${mode_to_image(mode)}'`);
|
const point = merged[i];
|
||||||
console.log("duration", (duration_units / graph_density).toFixed(4));
|
mode = new_mode(mode, point);
|
||||||
});
|
const file = `${process.cwd()}/pics/${mode_to_string(mode)}.png`;
|
||||||
|
const duration = (point.position_start - last_point) / graph_density;
|
||||||
console.log("file", `'${mode_to_image(results[results.length - 1][0])}'`);
|
console.log(
|
||||||
|
"duration",
|
||||||
// console.log(formatTime(total_speaking), formatTime(position));
|
(point.position_start - last_point) / graph_density
|
||||||
});
|
);
|
||||||
|
console.log("file", file);
|
||||||
|
last_point = point.position_start;
|
||||||
|
last_file = file;
|
||||||
|
total += duration * graph_density;
|
||||||
|
}
|
||||||
|
console.log("duration", merged[merged.length - 1].duration / graph_density);
|
||||||
|
console.log("file", last_file);
|
||||||
|
console.error(total, formatTime(total));
|
||||||
|
}
|
||||||
|
run();
|
||||||
|
|
18
generate.sh
18
generate.sh
|
@ -11,25 +11,27 @@ input=/home/kuba/Downloads/podcast-01-after-effects.mp3 # tutaj dajemy ścieżk
|
||||||
aresample=8000 # to bez zmian
|
aresample=8000 # to bez zmian
|
||||||
|
|
||||||
echo dzielimy mp3 na dwa osobne wav
|
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
|
#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
|
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/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 &
|
#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ą
|
# czekamy aż obydwa wątki się zakończą
|
||||||
wait;
|
#wait;
|
||||||
|
|
||||||
echo "generating the demuxers...";
|
echo "generating the demuxers...";
|
||||||
|
|
||||||
# generuje ścieżki do złożenia przez ffmpega:
|
# generuje ścieżki do złożenia przez ffmpega:
|
||||||
ts-node generate-demuxer.ts > /tmp/demuxer.txt
|
ts-node generate-demuxer.ts > out/demuxer.txt
|
||||||
|
|
||||||
mkdir -f out
|
mkdir -p out
|
||||||
|
|
||||||
# używa demuxer.txt żeby skleić końcowe video z dźwiękiem:
|
# używa demuxer.txt żeby skleić końcowe video z dźwiękiem:
|
||||||
echo generowanie całości
|
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
|
ffmpeg -y -f concat -safe 0 -i out/demuxer.txt -r 30 -tune stillimage -vsync vfr -pix_fmt yuv420p out/video.mp4
|
||||||
|
# ^ daję safe 0 aby przyjmowało bezwzględne ścieżki
|
||||||
|
|
||||||
|
|
||||||
echo łączenie video z dźwiękiem:
|
echo łączenie video z dźwiękiem:
|
||||||
ffmpeg -i video.mp4 -i $input -ac 1 -tune stillimage out/video-and-audio.mp4
|
ffmpeg -i out/video.mp4 -i $input -ac 1 -tune stillimage out/video-and-audio.mp4
|
||||||
|
|
Loading…
Reference in New Issue
Block a user