1
Vývoj / Re:Skript pro míchání zvuků a mikrofonu v Audacity
« kdy: 20. 12. 2025, 11:16:15 »
Děkuji za reakce. Vrátím se k tomu později, až bude zase potřeba. Tak ffmpeg, musím si to pamatovat.
Tato sekce Vám umožňuje zobrazit všechny příspěvky tohoto uživatele. Prosím uvědomte si, že můžete vidět příspěvky pouze z oblastí Vám přístupných.
#!/usr/bin/env bash
set -euo pipefail
# ====== CONFIG ======
MIC_PATTERN='input' # pattern (lowercase) který hledá v 2. sloupci sources
NULL_SINK_NAME='MixSink' # null-sink pro nahrávání (mix)
VIRTUAL_SRC_NAME='MixSource' # virtuální zdroj pro Audacity (monitor null-sinku)
MONITOR_ATTEN='42%' # poslechová hlasitost mikrofonu pro sluchátka
ACTIVATE_TRIES=60 # kolikrát čekat na vznik sink-input (0.1s krok)
SLEEP_STEP=0.1
# ====== helper ======
cmd_exists() { command -v "$1" >/dev/null 2>&1; }
# ====== najdi fyzický sink (prefer RUNNING, jinak první) ======
DEFAULT_SINK=$(pactl list short sinks \
| sed -E 's/[[:space:]]+/ /g' \
| awk '{
if($4=="RUNNING" && !found){ print $2; found=1; next }
if(!first) first=$2
}
END{ if(!found) print first }' \
|| true)
if [ -z "$DEFAULT_SINK" ]; then
echo "Nelze zjistit fyzický sink." >&2
exit 1
fi
# ====== najdi mikrofon (preferuj sources, 2. sloupec obsahuje 'input') ======
MIC_SRC=$(pactl list short sources \
| sed -E 's/[[:space:]]+/ /g' \
| awk -v pat="$MIC_PATTERN" 'BEGIN{lc_pat=tolower(pat)}
{
s=tolower($2)
if(s ~ lc_pat){ print $2; exit }
if(!first) first=$2
}
END{ if(NR>0 && !found) print first }' \
|| true)
if [ -z "$MIC_SRC" ]; then
echo "Nenalezen source mikrofonu (pattern: $MIC_PATTERN)." >&2
exit 1
fi
echo "Fyzický výstup (selected): $DEFAULT_SINK"
echo "Mikrofon (selected): $MIC_SRC"
# ====== cleanup předchozích modulů vztahujících se k našim jménům / mic ======
echo "Odstraňuji předchozí modul-loopback / MixSink / MixSource (pokud existují)..."
pactl list short modules | while read -r id name rest; do
if [ "$name" = "module-loopback" ] && echo "$rest" | grep -q -F "$MIC_SRC"; then
echo " unload-module $id (loopback referející mic)"
pactl unload-module "$id" || true
elif echo "$rest" | grep -q -F "$NULL_SINK_NAME"; then
echo " unload-module $id (referuje $NULL_SINK_NAME)"
pactl unload-module "$id" || true
elif echo "$rest" | grep -q -F "$VIRTUAL_SRC_NAME"; then
echo " unload-module $id (referuje $VIRTUAL_SRC_NAME)"
pactl unload-module "$id" || true
fi
done
# také pro jistotu odpojit moduly přes přesný řetězec jména
pactl list short modules | awk -v n1="$NULL_SINK_NAME" -v n2="$VIRTUAL_SRC_NAME" '$0 ~ n1 || $0 ~ n2 {print $1}' \
| xargs -r -n1 -I{} pactl unload-module {} >/dev/null 2>&1 || true
# ====== vytvoř null-sink MixSink pokud neexistuje ======
if pactl list short sinks | sed -E 's/[[:space:]]+/ /g' | awk -v n="$NULL_SINK_NAME" '$2==n{print; exit}' >/dev/null 2>&1; then
echo "Null-sink $NULL_SINK_NAME existuje (nebudu znovu vytvářet)."
else
echo "Vytvářím null-sink $NULL_SINK_NAME..."
pactl load-module module-null-sink sink_name="$NULL_SINK_NAME" sink_properties=device.description="$NULL_SINK_NAME"
fi
NULL_SINK_IDX=$(pactl list short sinks | sed -E 's/[[:space:]]+/ /g' | awk -v n="$NULL_SINK_NAME" '$2==n{print $1; exit}' || true)
echo "MixSink index: ${NULL_SINK_IDX:-<unknown>}"
# ====== vytvoř loopback mikrofon -> MixSink (pro nahrávání Audacity) ======
echo "Vytvářím loopback: $MIC_SRC -> $NULL_SINK_NAME ..."
RECORD_LOOP_MOD=$(pactl load-module module-loopback source="$MIC_SRC" sink="$NULL_SINK_NAME" latency_msec=1 || true)
if [ -z "$RECORD_LOOP_MOD" ]; then
echo "Chyba: nelze vytvořit record-loop." >&2
exit 1
fi
echo "Record loop module id: $RECORD_LOOP_MOD"
# ====== probuď mic dočasným čtením (pokud dostupné) ======
ACT_PID=""
if cmd_exists parec; then
parec -d "$MIC_SRC" >/dev/null 2>&1 &
ACT_PID=$!
echo "Spuštěn temporary parec pid=$ACT_PID"
elif cmd_exists pw-cat; then
pw-cat -d "$MIC_SRC" >/dev/null 2>&1 &
ACT_PID=$!
echo "Spuštěn temporary pw-cat pid=$ACT_PID"
else
echo "parec/pw-cat nenalezeny — pokud bude MIC SUSPENDED, nahrávání ho probudí."
fi
# ====== počkej na sink-input pro record loop (nezbytné pro případné nastavení) ======
REC_SINK_INPUT=""
for i in $(seq 1 $ACTIVATE_TRIES); do
REC_SINK_INPUT=$(pactl list short sink-inputs | sed -E 's/[[:space:]]+/ /g' | awk -v m="$RECORD_LOOP_MOD" '$3==m{print $1; exit}' || true)
if [ -n "$REC_SINK_INPUT" ]; then break; fi
sleep "$SLEEP_STEP"
done
if [ -n "$REC_SINK_INPUT" ]; then
echo "Record sink-input id: $REC_SINK_INPUT"
else
echo "Poznámka: record sink-input zatím nevznikl (Audacity při spuštění nahrávání ho probudí)."
fi
# ====== vytvoř monitorovací loopback: mic -> fyzický sink (pro poslech), a ztlum jej ======
echo "Vytvářím monitor loopback: $MIC_SRC -> $DEFAULT_SINK (attenuated monitoring)..."
MON_LOOP_MOD=$(pactl load-module module-loopback source="$MIC_SRC" sink="$DEFAULT_SINK" latency_msec=1 || true)
if [ -n "$MON_LOOP_MOD" ]; then
echo "Monitor loop module id: $MON_LOOP_MOD"
MON_SINK_INPUT=""
for i in $(seq 1 $ACTIVATE_TRIES); do
MON_SINK_INPUT=$(pactl list short sink-inputs | sed -E 's/[[:space:]]+/ /g' | awk -v m="$MON_LOOP_MOD" '$3==m{print $1; exit}' || true)
if [ -n "$MON_SINK_INPUT" ]; then
pactl set-sink-input-volume "$MON_SINK_INPUT" "$MONITOR_ATTEN"
echo "Monitor sink-input $MON_SINK_INPUT nastavena na $MONITOR_ATTEN"
break
fi
sleep "$SLEEP_STEP"
done
if [ -z "$MON_SINK_INPUT" ]; then
echo "Poznámka: monitor sink-input nevznikl — probuď mic nahráváním v Audacity a pak spusť 'pactl list short sink-inputs' pro nastavení."
fi
else
echo "Nepodařilo se vytvořit monitor loopback." >&2
fi
# ====== vytvoř virtuální zdroj (monitor MixSink) pro Audacity ======
if pactl list short sources | sed -E 's/[[:space:]]+/ /g' | awk -v n="$VIRTUAL_SRC_NAME" '$2==n{print $2; exit}'; then
echo "Virtual source $VIRTUAL_SRC_NAME již existuje."
else
echo "Vytvářím virtual source $VIRTUAL_SRC_NAME z $NULL_SINK_NAME.monitor..."
pactl load-module module-virtual-source source_name="$VIRTUAL_SRC_NAME" source_properties=device.description="$VIRTUAL_SRC_NAME" master="$NULL_SINK_NAME.monitor"
fi
VIRT_SRC_IDX=$(pactl list short sources | sed -E 's/[[:space:]]+/ /g' | awk -v n="$VIRTUAL_SRC_NAME" '$2==n{print $1; exit}' || true)
echo "Virtual source index: ${VIRT_SRC_IDX:-<unknown>}"
# ====== cleanup activator ======
if [ -n "${ACT_PID:-}" ]; then
sleep 0.1
kill "$ACT_PID" >/dev/null 2>&1 || true
fi
echo
echo "HOTOVO: Audacity vyber zdroj '$VIRTUAL_SRC_NAME' (Mix of system apps + mic at full volume)."
echo "Pro zahrnutí VLC do záznamu: přesuň jeho sink-input do $NULL_SINK_NAME nebo duplikuj fyzický monitor do MixSink."
#!/usr/bin/env bash
set -euo pipefail
# ===== Nastavení =====
VIRTUAL_SRC_NAME='MixSource' # virtuální zdroj pro Audacity
NULL_SINK_NAME='MixSink' # null-sink pro smíchání zvuků
# ===== Odpojit všechny loopback moduly =====
echo "Odpojuji všechny loopbacky..."
while IFS= read -r id; do
if pactl unload-module "$id"; then
echo "Odpojen loopback modul $id"
else
echo "Chyba při odpojování loopback modulu $id" >&2
fi
done < <(pactl list short modules | awk '/module-loopback/ {print $1}')
# ===== Odstranit virtuální zdroj =====
if pactl list short sources | awk '{print $2}' | grep -Fxq "$VIRTUAL_SRC_NAME"; then
echo "Odstraňuji virtuální zdroj '$VIRTUAL_SRC_NAME'..."
MODULE_ID=$(pactl list short modules | awk -v src="$VIRTUAL_SRC_NAME" '$0 ~ src {print $1; exit}')
if [ -n "$MODULE_ID" ]; then
pactl unload-module "$MODULE_ID" && echo "Odpojen virtuální zdroj '$VIRTUAL_SRC_NAME'"
fi
fi
# ===== Odstranit null-sink =====
if pactl list short sinks | awk '{print $2}' | grep -Fxq "$NULL_SINK_NAME"; then
echo "Odstraňuji null-sink '$NULL_SINK_NAME'..."
MODULE_ID=$(pactl list short modules | awk -v sink="$NULL_SINK_NAME" '$0 ~ sink {print $1; exit}')
if [ -n "$MODULE_ID" ]; then
pactl unload-module "$MODULE_ID" && echo "Odpojen null-sink '$NULL_SINK_NAME'"
fi
fi
echo "Hotovo. Všechny loopbacky, virtuální zdroje a null-sinky odstraněny."
systemctl --user restart pipewire pipewire-pulse wireplumber || systemctl --user restart pipewire pipewire-pulse pulseaudio || true