Skip to content

Instantly share code, notes, and snippets.

@muzzol
Created November 17, 2020 08:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save muzzol/0423fe2f69f5e7b47ad11ea4709b0578 to your computer and use it in GitHub Desktop.
Save muzzol/0423fe2f69f5e7b47ad11ea4709b0578 to your computer and use it in GitHub Desktop.
Copy tracks between Ardour 6/Mixbus 6 sessions including layers
#!/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