Skip to content

Instantly share code, notes, and snippets.

@palon7
Created January 27, 2023 07:51
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 palon7/6ae5405ceaec6b4beeb9f9928af0d7f6 to your computer and use it in GitHub Desktop.
Save palon7/6ae5405ceaec6b4beeb9f9928af0d7f6 to your computer and use it in GitHub Desktop.
/**
* MusicXMLから歌詞を読み込んでマーカーとして配置するスクリプト
*
* Copyright (c) 2023 Ryota Uno <palon@palon.org>
*
* Released under the MIT license.
* see https://opensource.org/licenses/MIT
*
* CeVIO AIから書き出したMusicXMLでのみ動作を確認しました。
* AfterEffects 2023で動作確認済み
*
*/
function getDivision(attributes) {
var divisions = 0;
for (var i = 0; i < attributes.length(); ++i) {
if (attributes[i].divisions.length() > 0) {
divisions = parseInt(attributes[i].divisions[0]);
}
}
if (divisions <= 0) {
return null;
}
return divisions;
}
/**
* @param {string} contents
*/
function parseXML(contents) {
var xml = new XML(contents);
var partList = xml.descendants("part");
var part = partList[0];
if (partList.length() < 1) {
alert("Can't parse MusicXML: No part in file");
return;
} else if (partList.length() > 1) {
alert("More than 1 part detected. Using first part.");
}
var measures = part.measure;
if (measures.length() < 1) {
alert("No measure found");
return;
}
var attributes = measures[0].attributes;
var division = getDivision(attributes);
if (!division) {
alert("Invalid division");
return;
}
$.writeln("Division:" + division);
var bpm = parseInt(measures[0].sound[0].attributes("tempo")[0]);
if (!bpm || bpm < 1) {
alert("Failed to get tempo");
return;
}
$.writeln("BPM:" + bpm);
// parsing note
var time = 0;
var lyricsCount = 0;
var lyricsList = {};
for (var i = 0; i < measures.length(); ++i) {
var notes = measures[i].note;
for (var j = 0; j < notes.length(); ++j) {
var note = notes[j];
if (note.duration.length() < 1) continue; // skip if no duation
if (note.lyric.length() > 0) {
lyricsList[time.toString()] = note.lyric.text[0];
lyricsCount++;
}
time = time + parseInt(note.duration[0]);
}
}
if (lyricsCount < 1) {
alert("No lyrics in file");
return;
}
app.beginUndoGroup("Add lyrics");
app.activeViewer.setActive();
var activeComp = app.project.activeItem;
if (!activeComp || !(activeComp instanceof CompItem)) {
alert("Please open composition");
return;
}
var layer;
var newLayer = true;
if (
activeComp.selectedLayers.length > 0 &&
activeComp.selectedLayers[0] instanceof AVLayer
) {
var cnf = confirm("Add marker to current layer? (or add new null layer)");
if (cnf == true) {
layer = activeComp.selectedLayers[0];
newLayer = false;
}
}
if (newLayer) {
layer = activeComp.layers.addNull(activeComp.duration);
}
layer.source.name = "Lyrics";
for (var note in lyricsList) {
var timeDuration = parseInt(note);
var timeSec = (timeDuration / division) * (60 / bpm);
var lyric = lyricsList[note];
$.writeln(timeSec + ":" + lyric);
var myMarker = new MarkerValue(lyric);
layer.marker.setValueAtTime(timeSec, myMarker);
}
app.endUndoGroup();
}
function main() {
var xmlFile = File.openDialog(
"Choose source musicxml",
"MusicXML:*.musicxml;*.xml"
);
if (!xmlFile) return;
xmlFile.encoding = "UTF8";
if (!xmlFile.exists || !xmlFile.open("r")) {
throw new Error("Could not open file.");
}
var content = xmlFile.read();
xmlFile.close();
parseXML(content);
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment