Created
November 17, 2020 08:24
-
-
Save muzzol/0423fe2f69f5e7b47ad11ea4709b0578 to your computer and use it in GitHub Desktop.
Copy tracks between Ardour 6/Mixbus 6 sessions including layers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# copy one track between ardour projects including layers | |
# àngel "mussol" bosch - muzzol@gmail.com | |
VER="0.4" | |
TOOLS="xmlstarlet sed" # external binaries needed | |
PREFIX_SOURCE="Imported-" # prefix used to avoid name collision | |
PREFIX_BCK=`date +%F_%H-%M-%S` # prefix used for project name backup | |
SOURCE_PROJECT="$1" | |
DEST_PROJECT="$2" | |
TRACK_NAME="$3" | |
SLEEP_SECONDS="0.1" # pause after each operation (mostly for debugging). can be 0 or fractions (0.2) | |
DEBUG="0" # any value different from 0 will activate debug mode. in this | |
# mode no changes are performed, no files copied or modified | |
# and final modifications will be shown on STDOUT | |
basic_check(){ | |
if [ "$DEST_PROJECT" == "" ]; then | |
echo "Copies one ardour track from one project to another INCLUDING layers." | |
echo "Tested with Ardour 6 and Mixbus 6." | |
echo "Use it at your own risk! always do backups before trying anything." | |
echo "" | |
echo "Usage: $0 <source project file> <destination project file> <track name>" | |
exit 1 | |
fi | |
if [ ! -e "$SOURCE_PROJECT" ]; then | |
echo "ERROR: can't find [$SOURCE_PROJECT] file" | |
exit 1 | |
fi | |
if [ ! -e "$DEST_PROJECT" ]; then | |
echo "ERROR: can't find [$DEST_PROJECT] file" | |
exit 1 | |
fi | |
# simple binary checker | |
for t in $TOOLS ; do | |
type $t > /dev/null | |
if [ "$?" != "0" ]; then | |
echo "ERROR: missing tool $t" | |
echo "Please, install it and execute again." | |
exit 1 | |
fi | |
done | |
} | |
list_tracks(){ | |
SOURCE_CONTENT="$1" | |
LIST_TRACKS=`echo "$SOURCE_CONTENT" | xmlstarlet sel -t -v "/Session/Routes/Route/@name"` | |
echo "Tracks in [$SOURCE_PROJECT]: " | |
echo "" | |
echo "$LIST_TRACKS" | |
exit 1 | |
} | |
version_check(){ | |
SOURCE_CONTENT="$1" | |
DEST_CONTENT="$2" | |
S_VERSION=`echo "$SOURCE_CONTENT" | xmlstarlet sel -t -v "/Session/@version"` | |
case $S_VERSION in | |
6000) | |
echo "Version 6 detected on original project ($S_VERSION)" | |
;; | |
*) | |
echo "Sorry, only version 6 of Ardour/Mixbus supported" | |
exit 1 | |
;; | |
esac | |
D_VERSION=`echo "$DEST_CONTENT" | xmlstarlet sel -t -v "/Session/@version"` | |
case $D_VERSION in | |
6000) | |
echo "Version 6 detected on destination project ($D_VERSION)" | |
;; | |
*) | |
echo "Sorry, only version 6 of Ardour/Mixbus supported" | |
exit 1 | |
;; | |
esac | |
} | |
newids(){ | |
# simple function that returns xml with ids changed | |
DEST_ID_COUNTER="$1" # this is the last used id | |
CONTENT="$2" # xml we want to parse | |
NEW_CONTENT="" | |
IFS=$'\n' | |
for l in $CONTENT ; do | |
WITH_ID=`echo "$l" | grep "[[:space:]]id=\"[[:digit:]]*\""` | |
if [ "$WITH_ID" == "" ]; then | |
NEW="$l" | |
else | |
O_ID=`echo "$l" | sed "s/.*[[:space:]]id=\"//" | cut -d'"' -f1` | |
N_ID=$((DEST_ID_COUNTER + O_ID)) | |
NEW=`echo "$l" | sed "s/[[:space:]]id=\"$O_ID\"/ id=\"$N_ID\"/"` | |
fi | |
NEW_CONTENT="$NEW_CONTENT | |
$NEW" | |
done | |
echo "$NEW_CONTENT" | |
} | |
convert_track(){ | |
SOURCE_CONTENT="$1" | |
DEST_CONTENT="$2" | |
# working directories | |
DIR_SOURCE="$( cd "$( dirname "${SOURCE_PROJECT}" )" >/dev/null 2>&1 && pwd )" | |
DIR_DEST="$( cd "$( dirname "${DEST_PROJECT}" )" >/dev/null 2>&1 && pwd )" | |
# get name and sample rate of both projects | |
SOURCE_SESSION_NAME=`echo "$SOURCE_CONTENT" | xmlstarlet sel -t -v "/Session/@name"` | |
SOURCE_SRATE=`echo "$SOURCE_CONTENT" | xmlstarlet sel -t -v "/Session/@sample-rate"` | |
DEST_SESSION_NAME=`echo "$DEST_CONTENT" | xmlstarlet sel -t -v "/Session/@name"` | |
DEST_SRATE=`echo "$DEST_CONTENT" | xmlstarlet sel -t -v "/Session/@sample-rate"` | |
[ "$DEBUG" != "0" ] && echo "DEBUG: SOURCE_SESSION_NAME [$SOURCE_SESSION_NAME] SOURCE_SRATE [$SOURCE_SRATE] DEST_SESSION_NAME [$DEST_SESSION_NAME] DEST_SRATE [$DEST_SRATE]" | |
# we don't support mixed sample rates | |
if [ "$SOURCE_SRATE" != "$DEST_SRATE" ]; then | |
echo "ERROR: different sample rates not supported." | |
echo "$SOURCE_PROJECT : $SOURCE_SRATE" | |
echo "$DEST_PROJECT : $DEST_SRATE" | |
exit 1 | |
fi | |
# get counter id of destination project | |
DEST_ID_COUNTER=`echo "$DEST_CONTENT" | xmlstarlet sel -t -v "/Session/@id-counter"` | |
# track and route are synonyms in ardour | |
TRACK_INFO=`echo "$SOURCE_CONTENT" | xmlstarlet sel -t -c "/Session/Routes/Route[@name=\"$TRACK_NAME\"]"` | |
if [ "$TRACK_INFO" == "" ]; then | |
echo "ERROR: can't find track $TRACK_NAME - Please check track name" | |
echo "" | |
list_tracks "$SOURCE_CONTENT" | |
fi | |
TRACK_ID=`echo "$TRACK_INFO" | xmlstarlet sel -t -v "//Route/@id"` | |
PLAYLIST_ID=`echo "$TRACK_INFO" | xmlstarlet sel -t -v "//Route/@audio-playlist"` | |
# [ "$DEBUG" != "0" ] && echo "DEBUG: DEST_ID_COUNTER [$DEST_ID_COUNTER] TRACK_INFO [$TRACK_INFO] TRACK_ID [$TRACK_ID] PLAYLIST_ID [$PLAYLIST_ID]" | |
if [ "$PLAYLIST_ID" == "" ]; then | |
echo "ERROR: can't find playlist for track [$TRACK_NAME]" | |
echo "Are you sure this track contains a playlist?" | |
echo "" | |
echo "Check your track name:" | |
list_tracks "$SOURCE_CONTENT" | |
exit 1 | |
fi | |
PLAYLIST_INFO=`echo "$SOURCE_CONTENT" | xmlstarlet sel -t -c "/Session/Playlists/Playlist[@id=\"$PLAYLIST_ID\"]"` | |
REGIONS=`echo "$PLAYLIST_INFO" | xmlstarlet sel -t -v "//Region/@name"` | |
if [ "$REGIONS" == "" ]; then | |
echo "ERROR: can't find any regions for track [$TRACK_NAME]" | |
echo "Are you sure this track contains regions?" | |
echo "" | |
echo "Check your track name:" | |
list_tracks "$SOURCE_CONTENT" | |
exit 1 | |
fi | |
PLAYLIST_NAME=`echo "$PLAYLIST_INFO" | xmlstarlet sel -t -v "/Playlist/@name"` | |
[ "$DEBUG" != "0" ] && echo "DEBUG: PLAYLIST_INFO [$PLAYLIST_INFO] REGIONS [$REGIONS] PLAYLIST_NAME [$PLAYLIST_NAME]" | |
# parsing track - real magic happens here | |
# we change names and IDs everywhere | |
TRACK_NAME_NEW="${PREFIX_SOURCE}${TRACK_NAME}" | |
PLAYLIST_NAME_NEW="${PREFIX_SOURCE}${PLAYLIST_NAME}" | |
PLAYLIST_ID_NEW=$((PLAYLIST_ID + DEST_ID_COUNTER)) | |
TRACK_INFO_NEW=`newids "$DEST_ID_COUNTER" "$TRACK_INFO" | sed "s/name=\"$TRACK_NAME\"/name=\"$TRACK_NAME_NEW\"/" | sed "s,name=\"$TRACK_NAME/,name=\"$TRACK_NAME_NEW/,g" | sed "s,name=\"meter-${TRACK_NAME}\",name=\"meter-${TRACK_NAME_NEW}\"," | sed "s/playlist=\"${PLAYLIST_NAME}\"/playlist=\"${PLAYLIST_NAME_NEW}\"/" | sed "s,audio-playlist=\"$PLAYLIST_ID\",audio-playlist=\"$PLAYLIST_ID_NEW\","` | |
[ "$DEBUG" != "0" ] && echo "DEBUG: TRACK_NAME_NEW [$TRACK_NAME_NEW] PLAYLIST_NAME_NEW [$PLAYLIST_NAME_NEW] TRACK_INFO_NEW [$TRACK_INFO_NEW]" | |
# simple check to see if we already modified destination project | |
echo "$DEST_CONTENT" | xmlstarlet sel -t -c "/Session/Routes/Route[@name=\"$TRACK_NAME_NEW\"]" > /dev/null | |
if [ "$?" == "0" ]; then | |
echo "ERROR: destination project already contains a track/region named [$TRACK_NAME_NEW]" | |
echo "be carefull running this script several times and check" | |
echo "PREFIX_SOURCE variable [$PREFIX_SOURCE] at the beggining of this script" | |
exit 1 | |
fi | |
echo "preparing track/route [$TRACK_NAME]" | |
DEST_CONTENT_NEW=`echo "$DEST_CONTENT" | xmlstarlet ed -P -s "/Session/Routes" -t text -n "" -v "$TRACK_INFO_NEW " | xmlstarlet unesc` | |
sleep "$SLEEP_SECONDS" | |
echo "preparing playlist [$PLAYLIST_NAME]" | |
PLAYLIST_ORIG_TRACK_ID=`echo "$PLAYLIST_INFO" | xmlstarlet sel -t -v "//Playlist/@orig-track-id"` | |
PLAYLIST_ORIG_TRACK_ID_NEW=$((PLAYLIST_ORIG_TRACK_ID + DEST_ID_COUNTER)) | |
PLAYLIST_ORIG_ELEMENT=`echo "$PLAYLIST_INFO" | grep '<Playlist id="'` | |
PLAYLIST_ORIG_ELEMENT_NEW=`newids "$DEST_ID_COUNTER" "${PLAYLIST_ORIG_ELEMENT} </Playlist>" | sed "s/orig-track-id=\"$PLAYLIST_ORIG_TRACK_ID\"/orig-track-id=\"$PLAYLIST_ORIG_TRACK_ID_NEW\"/" | sed "s/name=\"$PLAYLIST_NAME\"/name=\"$PLAYLIST_NAME_NEW\"/"` | |
DEST_CONTENT_NEW=`echo "$DEST_CONTENT_NEW" | xmlstarlet ed -P -s "/Session/Playlists" -t text -n "" -v "$PLAYLIST_ORIG_ELEMENT_NEW " | xmlstarlet unesc` | |
sleep "$SLEEP_SECONDS" | |
[ "$DEBUG" != "0" ] && echo "DEBUG: PLAYLIST_ORIG_ELEMENT_NEW [$PLAYLIST_ORIG_ELEMENT_NEW]" | |
IFS=$'\n' | |
# we can have lot of regions for a single track | |
for r in $REGIONS ; do | |
# parsing region | |
echo " preparing region [$r]" | |
R_INFO=`echo "$PLAYLIST_INFO" | xmlstarlet sel -t -c "//Region[@name=\"$r\"]"` | |
R_NAME_NEW="${PREFIX_SOURCE}${r}" | |
R_SOURCE_ID=`echo "$PLAYLIST_INFO" | xmlstarlet sel -t -v "//Region[@name=\"$r\"]/@source-0"` | |
R_SOURCE_ID_NEW=$((R_SOURCE_ID + DEST_ID_COUNTER)) | |
R_MASTER_SOURCE_ID=`echo "$PLAYLIST_INFO" | xmlstarlet sel -t -v "//Region[@name=\"$r\"]/@master-source-0"` | |
R_MASTER_SOURCE_ID_NEW=$((R_MASTER_SOURCE_ID + DEST_ID_COUNTER)) | |
R_INFO_NEW=`newids "$DEST_ID_COUNTER" "$R_INFO" | sed "s/source-0=\"${R_SOURCE_ID}\"/source-0=\"${R_SOURCE_ID_NEW}\"/" | sed "s/master-source-0=\"${R_MASTER_SOURCE_ID}\"/master-source-0=\"${R_MASTER_SOURCE_ID_NEW}\"/" | sed "s/name=\"${r}\"/name=\"${R_NAME_NEW}\"/"` | |
DEST_CONTENT_NEW=`echo "$DEST_CONTENT_NEW" | xmlstarlet ed -P -s "/Session/Playlists/Playlist[@name=\"${PLAYLIST_NAME_NEW}\"]" -t text -n "" -v "$R_INFO_NEW " | xmlstarlet unesc` | |
sleep "$SLEEP_SECONDS" | |
# [ "$DEBUG" != "0" ] && echo "DEBUG: R_INFO [$R_INFO] R_INFO_NEW [$R_INFO_NEW]" | |
# parsing source | |
S_INFO=`echo "$SOURCE_CONTENT" | xmlstarlet sel -t -c "/Session/Sources/Source[@id=\"$R_MASTER_SOURCE_ID\"]"` | |
S_WAV=`echo "$S_INFO" | xmlstarlet sel -t -v "/Source/@name"` | |
S_WAV_FULL="${DIR_SOURCE}/interchange/${SOURCE_SESSION_NAME}/audiofiles/${S_WAV}" | |
D_WAV="${PREFIX_SOURCE}${S_WAV}" | |
D_WAV_FULL="${DIR_DEST}/interchange/${DEST_SESSION_NAME}/audiofiles/${D_WAV}" | |
if [ -e "$D_WAV_FULL" ]; then | |
echo " WARN: destination file [$D_WAV_FULL] already exists (probably copied from previous region)" | |
else | |
echo " copying audio file [$S_WAV] to [$D_WAV]" | |
echo -n " " | |
if [ "$DEBUG" == "0" ]; then | |
cp -v "$S_WAV_FULL" "$D_WAV_FULL" | |
else | |
echo "DEBUG: cp -v \"$S_WAV_FULL\" \"$D_WAV_FULL\"" | |
fi | |
fi | |
sleep "$SLEEP_SECONDS" | |
echo " preparing source [$S_WAV]" | |
S_INFO_NEW=`newids "$DEST_ID_COUNTER" "$S_INFO" | sed "s/name=\"$S_WAV\"/name=\"$D_WAV\"/" | sed "s,captured-for=\"$TRACK_NAME\",captured-for=\"$TRACK_NAME_NEW\"," | sed "s,origin=\"$S_WAV\",origin=\"$D_WAV\","` | |
DEST_CONTENT_NEW=`echo "$DEST_CONTENT_NEW" | xmlstarlet ed -P -s "/Session/Sources" -t text -n "" -v "$S_INFO_NEW " | xmlstarlet unesc` | |
sleep "$SLEEP_SECONDS" | |
[ "$DEBUG" != "0" ] && echo "DEBUG: S_INFO [$S_INFO] S_INFO_NEW [$S_INFO_NEW]" | |
done | |
LAST_ID=`echo "$DEST_CONTENT_NEW" | xmlstarlet sel -t -v "//@id" | grep -o '[[:digit:]]*' | sort -n | tail -n1` | |
ID_COUNTER_NEW=$(($LAST_ID + 100)) # we add 100 to last used id just because | |
DEST_CONTENT_NEW=`echo "$DEST_CONTENT_NEW" | xmlstarlet edit -u "/Session/@id-counter" -v "$ID_COUNTER_NEW"` | |
BACKUP_PROJECT="${DIR_DEST}/${PREFIX_BCK}-`basename ${DEST_PROJECT}`" | |
echo "creating project backup [$BACKUP_PROJECT]" | |
if [ "$DEBUG" == "0" ]; then | |
cp -v "$DEST_PROJECT" "$BACKUP_PROJECT" | |
else | |
echo "DEBUG: cp -v \"$DEST_PROJECT\" \"$BACKUP_PROJECT\"" | |
fi | |
sleep "$SLEEP_SECONDS" | |
echo "modifying project file $DEST_PROJECT" | |
# beautifying | |
if [ "$DEBUG" == "0" ]; then | |
echo "$DEST_CONTENT_NEW" | xmlstarlet fo > "$DEST_PROJECT" | |
else | |
echo "DEBUG: showing modifications" | |
echo "" | |
sleep 5 | |
echo "$DEST_CONTENT_NEW" | xmlstarlet fo | |
fi | |
} | |
################################################################### | |
echo "$0 - ver: $VER - àngel \"mussol\" bosch - muzzol@metacorc.com" | |
basic_check | |
# we dump all project into a variable | |
SOURCE_CONTENT=`cat "$SOURCE_PROJECT"` | |
DEST_CONTENT=`cat "$DEST_PROJECT"` | |
version_check "$SOURCE_CONTENT" "$DEST_CONTENT" | |
if [ "$TRACK_NAME" == "" ]; then | |
list_tracks "$SOURCE_CONTENT" | |
fi | |
convert_track "$SOURCE_CONTENT" "$DEST_CONTENT" | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment