2013-04-30 08:40:36 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
# Copyright (C) 2013 Jonathan Moore Liles #
|
|
|
|
# #
|
|
|
|
# This program is free software; you can redistribute it and/or modify it #
|
|
|
|
# under the terms of the GNU General Public License as published by the #
|
|
|
|
# Free Software Foundation; either version 2 of the License, or (at your #
|
|
|
|
# option) any later version. #
|
|
|
|
# #
|
|
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
|
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
|
|
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
|
|
|
# more details. #
|
|
|
|
# #
|
|
|
|
# You should have received a copy of the GNU General Public License along #
|
|
|
|
# with This program; see the file COPYING. If not,write to the Free Software #
|
|
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
|
|
|
|
|
|
|
|
import xml.etree.ElementTree as et
|
|
|
|
import random
|
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
from datetime import date
|
|
|
|
import shutil
|
|
|
|
|
2013-05-01 07:38:49 +02:00
|
|
|
if len(sys.argv) == 1:
|
2013-04-30 08:40:36 +02:00
|
|
|
print "Usage: import-ardour-session [PATH_TO_ARDOUR_FILE] [NAME_OF_NON_TIMELINE_PROJECT]"
|
|
|
|
sys.exit( 1 )
|
|
|
|
|
2013-05-01 07:38:49 +02:00
|
|
|
Overwrite=False
|
|
|
|
|
|
|
|
i = 1;
|
|
|
|
|
|
|
|
if ( sys.argv[i] == "--overwrite" ):
|
|
|
|
Overwrite = True
|
|
|
|
i = i + 1
|
|
|
|
|
|
|
|
ArdourFilePath = sys.argv[i]
|
|
|
|
i = i + 1
|
|
|
|
NonTimelineProjectPath = sys.argv[i]
|
|
|
|
i = i + 1
|
2013-04-30 08:40:36 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
os.makedirs( NonTimelineProjectPath );
|
|
|
|
os.makedirs( NonTimelineProjectPath + "/sources");
|
|
|
|
except:
|
2013-05-01 07:38:49 +02:00
|
|
|
if not Overwrite:
|
|
|
|
print "Output path already exists!"
|
|
|
|
sys.exit( 1 )
|
2013-04-30 08:40:36 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
tree = et.parse( ArdourFilePath );
|
|
|
|
except:
|
|
|
|
print "Invalid XML input"
|
|
|
|
sys.exit( 1 )
|
|
|
|
|
|
|
|
root = tree.getroot();
|
|
|
|
|
|
|
|
Sources = {}
|
|
|
|
DiskStreams = {}
|
|
|
|
|
|
|
|
print "Gathering session info"
|
|
|
|
if root.tag != "Session":
|
|
|
|
print "Not an Ardour session?"
|
|
|
|
sys.exit(1)
|
2013-04-30 08:40:36 +02:00
|
|
|
|
|
|
|
print "Ardour session file version is " + root.attrib["version"]
|
2013-04-30 08:50:05 +02:00
|
|
|
print "This program is known to work with versions <= 3.0.1"
|
2013-04-30 08:40:36 +02:00
|
|
|
|
|
|
|
ProjectName = root.attrib["name"]
|
|
|
|
|
|
|
|
print "Converting Ardour session \"" + ProjectName + "\" to Non Timeline format. Please be patient."
|
|
|
|
|
|
|
|
|
|
|
|
print "Gathering sources."
|
|
|
|
for node in root.findall( "./Sources/Source" ):
|
|
|
|
Sources[node.attrib["id"]] = node;
|
|
|
|
# print "\tFound source " + node.attrib["name"]
|
|
|
|
|
2013-04-30 08:50:05 +02:00
|
|
|
print "Gathering version 3 diskstreams."
|
|
|
|
for node in root.findall( "./Routes/Route/Diskstream" ):
|
|
|
|
DiskStreams[node.attrib["id"]] = node;
|
|
|
|
print "\tFound diskstream " + node.attrib["name"];
|
|
|
|
|
2013-04-30 08:40:36 +02:00
|
|
|
print "Gathering version 2 diskstreams."
|
2013-04-30 08:40:36 +02:00
|
|
|
for node in root.findall( "./DiskStreams/AudioDiskstream" ):
|
|
|
|
DiskStreams[node.attrib["id"]] = node;
|
|
|
|
# print "\tFound diskstream " + node.attrib["name"];
|
|
|
|
|
2013-04-30 08:40:36 +02:00
|
|
|
|
|
|
|
print "Gathering version 1 diskstreams."
|
|
|
|
for node in root.findall( "./DiskStreams/DiskStream" ):
|
|
|
|
DiskStreams[node.attrib["id"]] = node;
|
|
|
|
# print "\tFound diskstream " + node.attrib["name"];
|
|
|
|
|
2013-04-30 08:40:36 +02:00
|
|
|
LoggableID = 1;
|
|
|
|
|
|
|
|
def NewID():
|
|
|
|
global LoggableID
|
|
|
|
ID="0x%x" % LoggableID
|
|
|
|
LoggableID = LoggableID + 1
|
|
|
|
return ID
|
|
|
|
|
|
|
|
|
2013-05-01 07:38:49 +02:00
|
|
|
History = "{\n"
|
|
|
|
|
2013-04-30 08:40:36 +02:00
|
|
|
print "Processing tempo."
|
|
|
|
|
|
|
|
for node in root.findall("./TempoMap/Tempo"):
|
|
|
|
TempoID = NewID()
|
2013-05-01 07:38:49 +02:00
|
|
|
History += ( "\tTempo_Point " + TempoID + " create :start 0 :tempo " + node.attrib["beats-per-minute"] + "\n")
|
2013-04-30 08:40:36 +02:00
|
|
|
|
|
|
|
for node in root.findall("./TempoMap/Meter"):
|
|
|
|
TimeID = NewID()
|
2013-04-30 08:50:05 +02:00
|
|
|
try:
|
|
|
|
BPB = node.attrib["beats-per-bar"]
|
|
|
|
except:
|
|
|
|
BPB = node.attrib["divisions-per-bar"]
|
|
|
|
|
2013-05-01 07:38:49 +02:00
|
|
|
History += ( "\tTime_Point " + TimeID + " create :start 0 :beats_per_bar " + BPB + " :beat_type " + node.attrib["note-type"] + "\n")
|
2013-04-30 08:40:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
print "Processing playlists."
|
|
|
|
for node in root.findall( "./Playlists/Playlist" ):
|
|
|
|
|
|
|
|
try:
|
|
|
|
Track = DiskStreams[node.attrib["orig_diskstream_id"]]
|
|
|
|
except:
|
2013-04-30 08:50:05 +02:00
|
|
|
try:
|
|
|
|
Track = DiskStreams[node.attrib["orig-track-id"]]
|
|
|
|
except:
|
|
|
|
print "\tSkipping playlist " + node.attrib["name"] + " for unknown diskstream"
|
|
|
|
continue
|
2013-04-30 08:40:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
if node.attrib["name"] == Track.attrib["playlist"]:
|
|
|
|
print "\tFound playlist " + node.attrib["name"]
|
|
|
|
|
2013-05-01 02:26:57 +02:00
|
|
|
# for chan in range(0, int( Track.attrib["channels"] )):
|
|
|
|
TrackID = NewID()
|
|
|
|
SequenceID = NewID()
|
|
|
|
# if int(Track.attrib["channels"]) > 1:
|
|
|
|
# TrackName = Track.attrib["name"] + "-" + ( "%i" % chan )
|
|
|
|
# else:
|
|
|
|
TrackName = Track.attrib["name"]
|
|
|
|
Channels = int(Track.attrib["channels"])
|
|
|
|
|
2013-05-01 07:38:49 +02:00
|
|
|
History += ( "\tTrack " + TrackID + " create :name \"" + TrackName + "\"" + ( " :sequence " + SequenceID ) + " :color " + ( "%i" % random.randint(256,123123123)) + ( " :inputs %i :outputs %i\n" % ( Channels, Channels ) ) )
|
|
|
|
History += ( "\tAudio_Sequence " + SequenceID + " create :track " + TrackID + " :name \"" + node.attrib["name"] + "\"\n" )
|
2013-05-01 02:26:57 +02:00
|
|
|
for n2 in node.findall("./Region"):
|
|
|
|
RegionID = NewID();
|
2013-04-30 08:40:36 +02:00
|
|
|
|
2013-05-01 02:26:57 +02:00
|
|
|
if ( int( Track.attrib["channels"] ) > 1 ):
|
|
|
|
|
|
|
|
SourceList = []
|
|
|
|
APath1 = os.path.dirname(ArdourFilePath) + "/sounds/"
|
|
|
|
APath2 = os.path.dirname(ArdourFilePath) + "/interchange/" + ProjectName + "/audiofiles/"
|
|
|
|
|
|
|
|
for chan in range(0, int( Track.attrib["channels"] )):
|
|
|
|
SourceName = Sources[n2.attrib["source-" + ( "%i" % chan )]].attrib["name"]
|
|
|
|
|
|
|
|
if os.path.exists( APath1 + SourceName):
|
|
|
|
SourceName = APath1 + SourceName
|
|
|
|
elif os.path.exists( APath2 + SourceName ):
|
|
|
|
SourceName = APath2 + SourceName
|
|
|
|
else:
|
|
|
|
print "source not found!"
|
|
|
|
|
|
|
|
SourceList.append( "'" + SourceName + "'" );
|
|
|
|
|
|
|
|
s1,s2,s3 = Sources[n2.attrib["source-0"]].attrib["name"].partition( '%' )
|
|
|
|
|
|
|
|
SourceName = s1 + ".wav"
|
|
|
|
OutputName = NonTimelineProjectPath + "/sources/" + SourceName
|
|
|
|
|
|
|
|
if not os.path.exists( OutputName ):
|
|
|
|
print "Combining multifile source into multichannel source"
|
|
|
|
os.system( "sox -S -M " + " ".join(SourceList) + " " + "'" + OutputName + "'" )
|
|
|
|
|
|
|
|
else:
|
|
|
|
SourceName = Sources[n2.attrib["source-0"]].attrib["name"];
|
2013-04-30 08:40:36 +02:00
|
|
|
if not os.path.exists( NonTimelineProjectPath + "/sources/" + SourceName ):
|
|
|
|
print "\t\tCopying source: " + SourceName;
|
2013-05-01 02:26:57 +02:00
|
|
|
|
2013-04-30 08:40:36 +02:00
|
|
|
try:
|
|
|
|
shutil.copy( os.path.dirname(ArdourFilePath) + "/interchange/" + ProjectName + "/audiofiles/" + SourceName,
|
2013-05-01 02:26:57 +02:00
|
|
|
NonTimelineProjectPath + "/sources/" )
|
2013-04-30 08:40:36 +02:00
|
|
|
except:
|
|
|
|
shutil.copy( os.path.dirname(ArdourFilePath) + "/sounds/" + SourceName,
|
2013-05-01 02:26:57 +02:00
|
|
|
NonTimelineProjectPath + "/sources/" )
|
2013-04-30 08:40:36 +02:00
|
|
|
|
2013-04-30 08:40:36 +02:00
|
|
|
|
2013-05-01 07:38:49 +02:00
|
|
|
History += ("\tAudio_Region " + RegionID +
|
2013-05-01 02:26:57 +02:00
|
|
|
" create :source \"" + SourceName +
|
2013-04-30 08:40:36 +02:00
|
|
|
"\" :start " + n2.attrib["position"] +
|
|
|
|
" :length " + n2.attrib["length"] +
|
|
|
|
" :offset " + n2.attrib["start"] +
|
|
|
|
" :sequence " + SequenceID + "\n")
|
|
|
|
else:
|
|
|
|
print "\tSkipping inactive playlist"
|
2013-05-01 07:38:49 +02:00
|
|
|
|
|
|
|
History += ("}\n")
|
|
|
|
|
|
|
|
print "Comitting to disk."
|
|
|
|
|
|
|
|
with open( NonTimelineProjectPath + "/info", 'w' ) as InfoFile:
|
|
|
|
try:
|
|
|
|
SampleRate = root.attrib["sample-rate"]
|
|
|
|
except:
|
|
|
|
print "Couldn't find sample rate... Using default."
|
|
|
|
SampleRate = "48000"
|
|
|
|
|
|
|
|
InfoFile.write( "created by\n\tNon-Timeline 1.2.0\ncreated on\n\t" + date.today().ctime() + "\nversion\n\t2\nsample rate\n\t" + SampleRate + "\n" )
|
|
|
|
|
|
|
|
with open( NonTimelineProjectPath + "/history", 'w' ) as HistoryFile:
|
|
|
|
HistoryFile.write( History )
|
2013-04-30 08:40:36 +02:00
|
|
|
|
|
|
|
print "Done. You've been freed. Go make music!"
|
|
|
|
|