2015年10月29日木曜日

MuseScore Plugin - MusicXML miscellaneous import/export

MuseScore プラグインで、import/export 後に 独自のメタ情報を付加する方法


進捗状況: importは出来た。

MusicXMLをMuseScoreへ読み込み、miscellaneous情報をメタ情報として設定するプラグイン。
https://gist.github.com/teamikl/0443922aa0a29de2641a


exportはいくつか課題在り。

Research

  • Plugin内でのメタ情報のKeys一覧の所得方法
    • C++のメンバには menuTags があるが、プラグインからはアクセスできない。
  • Plugin内でQDomDocumentクラスへのアクセスが可能かどうか調査
    • C++でコンポーネント作れば可能だけど、プラグインのみで完結出来る方法を探す。

Tasks

  • [*] メニュー [プラグイン][MusicXML misc][Import / Export]
  • [*] ファイル・ダイアログ (QtQuickDialogs 1.2)
  • [*] MusicXML ファイルの import/export (readScore, writeScore)
  • [*] ファイルの読み込み (MuseScoreのFileIOクラス)
  • [  ] DOM操作
    • [  ] QDomDocument
    • [*] (QtQuick.XmlListModel 2.0) 読み込み用
      • onRowsInserted event
  • [*] score.setMetaTag/metaTag で情報を設定/読出
  • [  ] メタ情報のKeys一覧の所得 (Export時に必要)

楽譜のメタ情報

MuseScore、MusicXML でのメタ情報について

まず前置きとして、ここでのメタ情報は、作詞・作曲者、作譜に用いたソフトウェア等の情報を指します。

MusicXML 3.0 の規格では、主にidentification要素として定義されています。

MuseScore では、メニューの[ファイル][情報...]で確認・編集できます。
メタ情報は、[スタイル][一般][ヘッダ、フッダ] から、タグ名を指定して譜表に反映させる事が可能です。
MuseScoreの情報は、ハンドブックに詳細解説在り。(日本語)ハンドブック/スコア情報

そして、今回の本題、
MusicXMLとMuseScore双方に、独自のメタ情報を扱う仕組みはあるのですが、
MuseScore では、独自に定義した情報の import/export へは対応していません。[2015/10/29現在]

Patch

そこで、インポートに対応する Patcheを書いてみました。(文字のエスケープは考慮してません)

// https://github.com/musescore/MuseScore/blob/master/mscore/importmxmlpass1.cpp

else if (ee.tagName() == "miscellaneous") {
    for (QDomElement eee = ee.firstChildElement();
            !eee.isNull(); eee = ee.nextSiblingElement()) {
        if (eee.tagName() === "miscellaneous-field")
            score->setMetaTag(eee.attribute(QString("name")), eee.text());
    }
}

実装の自己分析

  • 名前空間がない為、タグ名の衝突を避ける手段が必要。
  • 接頭子を付けるなどして、運用で注意すればいいが、汎用的な機能の実装としては問題あり。
    メタ情報のキーの一覧を所得するとき、独自のものかどうかを区別できない。
  • 率直な解決策としては、別の格納場所を用意すればいいだけかもしれないけど、
    MuseScoreで対応している他のフォーマットのimport/exportとも折り合いを付ける必要がある。
    データ構造をMusicXMLのみに特化というわけにはいかないので、作業コストが高くなりそう。
  • 基本機能として実装してしまうと、多くの利用者に影響が在り、
    場合によっては、利用者の意図しない仕様変更になってしまう懸念があります。

機能としては数行で対応できる事なのに、未実装なのを見ると、仕様が固まってないのかなという感じ。
開発メーリングリスト等は読んでないので、実際のところどうなのかはわかりません。個人的な雑感です。
何れは、対応されるはずですが、今回はこのアプローチは見送ります。

MuseScore Plugin

次に、プラグインで扱う方法を模索。

機能的には同じ事をするのだけど、プラグインとして実装した場合は、
プラグイン利用者のみが注意すればいい為、影響は軽微。勝手に独自拡張出来るのが利点です。

プラグインからメタ情報を設定する方法。(code snippet)

  // メタ情報を設定
  curScore.setMetaTag("tagName", "text");

  // メタ情報を所得
  var text = curScore.metaTag("tagName");

今回はここまで。後は、残りの作業を書き留めておく。

TODO

  • プラグインからMusicXMLを読み込む。MuseScore::readScore(fileName)
  • プラグインからXMLを読書。DOMパーサが提供されていたはず。
  • メタ情報のキーの一覧の所得方法

2015年10月28日水曜日

Google App Script - XMLBuilder

XmlServiceの簡易ラッパー

書いてみた。以下は、MusicXMLチュートリアルの Hello, World!を記述した例

Project key: ME6vSmFQZ_s_2Mvpt0A6O_35chaeZMmGx


  var EL = XMLBuilder.EL; // 頻繁に呼び出す関数なので、エイリアスを作っておく。

  var root = EL("score-partwise", {version:"3.0"}).add(
    EL("part-list").add(
      EL("score-part", {id:"P1"}).add(
        EL("part-name").text("Music"))).add(
      EL("part", {id:"P1"}).add(
        EL("measure", {number:"1"}).add(
          EL("attributes").add(
            EL("divisions").text("1"),
            EL("key").add(
              EL("fifths").text("0")),
            EL("time").add(
              EL("beats").text("4"),
              EL("beat-type").text("4")),
            EL("clef").add(
              EL("sign").text("G"),
              EL("line").text("2"))),
          EL("note").add(
            EL("pitch").add(
              EL("step").text("C"),
              EL("octave").text("4")))))));
  
  var docType = XMLBuilder.DocType(
    "score-partwise",
    "-//Recordare//DTD MusicXML 3.0 Partwise//EN",
    "http://www.musicxml.org/dtds/partwise.dtd");

  Logger.log(XMLBuilder.Doc(docType, root).toText());

編集のポイント

  • 要素を生成 EL(要素名, 属性)
  • 子要素を追加 .add(改行 インデントは、スクリプト・エディタ任せ
  • テキストを設定 .text(テキスト)
    数値は、暗黙的な型変換を避けるために文字列で渡す。※少数を渡す場合注意
  • 要素を閉じる ) エディタが対応する開始括弧をハイライトしてくれるので、任意の位置まで閉じる。

2015年10月27日火曜日

Google App Script - SheetUtil

スプレッド・シートを簡単に扱う為のクラスをライブラリに纏めてみた。

Project Key: MTbsvdt4qqdldpYH52zqe5q7mwghSyEXE

  
  // 1: Shell 簡単なシート操作 get/set/clear
  var shell = SheetUtil.Shell.attach('Sheet01');
  Logger.log(shell.get("B2"));
  
  
  // 2: Pipe 2つのシート間でのデータ・書式のコピー copy(Format|Contents|Values)
  var pipe = SheetUtil.Pipe.from('Sheet01').to('Sheet02');
  pipe.copy("B1 to B2");

  // 纏めてコピー
  ["C1 to D1", "C10 to D10", "B12 to D12"].forEach(pipe.copyContents.bind(pipe));

  
  // 3: NamedAccessor セルに名前付きでアクセス getXXX/setXXX
  var InfoClass = SheetUtil.NamedAccessor.generate({
    Name: "B2",
    Age: "C2",
  });
  var info = new InfoClass('Sheet01');

  // プロパティでアクセス
  info.Name = "test"; // "B2" へ値を設定
  Logger.log(info.Name);

  // 関数呼び出し
  info.setName("test02");
  Logger.log(info.getName());


  // 4: ResourceManager A:key, B:Value のシート"Resources"を用意。
  var rc = SheetUtil.ResourceManager.load("Resources");
  Logger.log(rc.AppVersion);

  • 他のスプレッド上のシートを指定するには、シート名の前に接頭子で指定。
    • GRID_ID$SHEET_NAME のように、"$" で区切ります。
      (これはgoogleのAPIではなく、このライブラリの仕様)
  •  
  • SheetUtil.Pipe で、他のシートの値をコピーするには copyValues() を使います。
    • 異なるスプレッドシート間での範囲のコピーには制限がある為、
      内部では、setValues/getValuesで値をコピー。

2015年10月26日月曜日

Syntax Highlight のテスト

google pretty print 利用テスト

https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js
テンプレートのヘッダ内に記述して、投稿の編集はHTMLで

<pre class="prettyprint">
</pre>
  • Python (Music21)
  • JavaScript (Google App Script, MuseScore plugin, Node.js)
  • XML (MusicXML)
  • C++ (MuseScore, Qt5)
if __name__ == "__main__":
    print("Hello, world!")
console.log("Hello, world!");
<?xml version="1.0" ?>
<!DOCTYPE score-partwise PUBLIC
  "-//Recordare//DTD MusicXML 3.0 Partwise//EN"
  "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise>
   ...
</score-partwise>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>

int main(int argc, char **argv) {
  QApplication app(argc, argv);
  QMainWindow win;

  win.show();

  return app.exec();
}

長くなりそうだったら Gist を使おう

2015年10月25日日曜日

オンライン・セッション関連ツールの開発日記

始めてみます。


URL書いてないのは、作業場所・公開場所を検討中。