Skip to content

Instantly share code, notes, and snippets.

@scztt
Last active April 25, 2022 21:02
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scztt/626e6f205ec602a3467227f77fe90e7d to your computer and use it in GitHub Desktop.
Save scztt/626e6f205ec602a3467227f77fe90e7d to your computer and use it in GitHub Desktop.
// XMP parsing requires:
// Quarks.install("XML"); [in supercollider]
// brew install exiftool [in terminal]
+SoundFile {
exiftoolPath {
^"/usr/local/bin/exiftool"
}
extractMarkers {
if (File.exists(this.xmpPath) || File.exists(this.exiftoolPath)) {
^this.extractMarkersXMP
} {
switch (headerFormat)
{"AIFF"} { ^this.extractMarkersAiff() }
{"WAV"} { ^this.extractMarkersWav() }
}
}
xmpPath {
var pathName = PathName(path);
^(pathName.pathOnly +/+ pathName.fileNameWithoutDoubleExtension ++ ".xmp");
}
xmpDoc {
var docStr, result;
if ('DOMDocument'.asClass.notNil) {
if (File.exists(this.xmpPath)) {
result = DOMDocument(this.xmpPath);
} {
if (File.exists(this.exiftoolPath)) {
Pipe.callSync(
"% -xmp -b \"%\"".format(this.exiftoolPath, path.standardizePath),
{ |success| docStr = success },
{ |error| "Error calling exiftool: %".format(error).warn },
1024 ** 2
).();
if (docStr.notNil) {
result = DOMDocument().parseXML(docStr)
}
}
}
}
^result
}
extractMarkersWav {
var cueRe, markerList, cueStrings, headerString;
cueRe = "Cue ID :\\s*(\\d+)\\s*Pos :\\s*(\\d+)";
headerString = this.readHeaderAsString;
cueStrings = headerString.findRegexp(cueRe);
if((cueStrings.size % 3) == 0, {
^cueStrings.clump(3).collect({
| cue |
var label, id = cue[1][1];
label = headerString.findRegexp("labl :\\s*" ++ id.asString ++ " : (.*?)\n");
label = (label.size == 2).if({ label[1][1] }, id);
(
\position: cue[2][1].asInteger,
\label: label
)
})
},{
"Unexpected result searching for cue markers".warn;
this.readHeaderAsString.postln;
});
^[]
}
extractMarkersAiff {
var cueRe, markerList, cueStrings, headerString;
cueRe = "(Mark ID\\s*:\\s*([0-9]+).*?Position\\s*:\\s*([0-9]+).*?Name\\s*:\\s*(.*?)\n)";
headerString = this.readHeaderAsString;
cueStrings = headerString.findRegexp(cueRe);
if((cueStrings.size % 5) == 0, {
^cueStrings.clump(5).collect({
| cue |
var label, position;
label = cue[4][1];
position = cue[3][1];
(
\position: position.asInteger,
\label: label
)
})
},{
"Unexpected result searching for cue markers".warn;
this.readHeaderAsString.postln;
});
^[]
}
extractMarkersXMP {
var xmpDoc;
var results = [], errors = [];
xmpDoc = this.xmpDoc;
if (xmpDoc.notNil) {
xmpDoc.getElementsByTagName("xmpDM:markers").do {
|markers|
markers.getElement("rdf:Seq") !? {
|seq|
seq.getChildNodes.do {
|item|
try {
results = results.add(
(
position: item.getElement("xmpDM:startTime") !? _.getText.asInteger,
duration: item.getElement("xmpDM:duration") !? _.getText.asInteger,
label: item.getElement("xmpDM:name") !? _.getText,
)
);
} {
errors = errors.add(item.format)
}
}
}
};
if (errors.size > 0) {
"Problems parsing xmp marker items:".warn;
errors.do(_.postln);
};
} {
Error("Could not find XMP sidecar file (%), or embedded xmp (%) cannot be parsed.".format(this.xmpPath, path)).throw;
}
^results
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment