用MuseScore和Node.js实现音乐的随机化

231 阅读4分钟

我的一位来自圣莫尼卡学院的尊敬的教授,Dr. Driscoll就如何使用一张乐谱并重新调整一些措施来为每个学生生成一个独特的练习征求意见。结果这比预期的要有趣,这里是我用免费的记谱软件MuseScore和一个Node.js脚本想出的一个解决方案。我希望它能在未来对其他人的音乐教育或为什么不对生成性音乐有用。

对于病人来说,这里是代码

三步程序

  1. 创建一个MuseScore文件,作为 "模板 "使用
  2. 通过在Node.js脚本中操作XML来重新排列措施,并吐出N个不同的MuseScore文件
  3. 使用MuseScore中的批量转换插件,将新文件转换为PDF、MP3、MIDI或任何其他需要的输出格式。

模板

对于模板来说,你可以在MuseScore中创建一个新文件,或者将其他文件导入MuseScore进行最后润色。在我的例子中,教授给了我一个从Sibelius(一种比MuseScore更笨重的商业替代品)导出的MusicXML文件。

一旦乐谱符合你的要求,就把它导出为未压缩的XML,即MuseScore的本地文件(*.mscx)。

事实证明,MuseScore的本地文件是压缩的xml(mscz)或其未压缩的兄弟(mscx)。我使用了未压缩的版本,这样我就可以查看XML,也不必在我的Node脚本中处理压缩问题。

为什么是MuseScore的XML而不是MusicXML?除了方便、习惯和减少一个变量外,我没有一个好的答案。

在模板中,你选择某些措施被重复使用和重新洗牌,例如,动机A包括第2和第3措施,动机C只是第8措施,等等。这些动机将被定义在节点脚本中。

脚本

脚本是这样做的

  1. 使用xml-js将XML模板读入一个JavaScript对象,以便进行操作
  2. 从XML中提取动机措施
  3. 生成100个所需动机及其数量的随机排列组合
  4. 编写100个新的XML文件,其中包括重新组合的措施

但首先...

配置

const MAX = 100; // how many combinations to generate
const ADJOK = false; // is it ok to have adjacent repeated motives
const motives = {
  A: [2, 3], // Motive A is measures 2 and 3
  B: [4, 5],
  C: [8],
  D: [10, 11],
  E: [16],
  F: [17],
  G: [19],
  H: [22],
  I: [23],
};
// we want motive A to happen twice in the new score,
// motive C to happen 4 times and so on
const distribution = 'AADDFFEEEBGHICCCC';
const OUT = 'out/';

看起来很滑稽的AADDFFEEEBGHICCCC是对你希望每个动机重复多少次的定义。这就是要被重新洗牌以创造新的组合的内容。

读取XML

// imports
const convert = require('xml-js');
const fs = require('fs');

const xml = fs.readFileSync('Template.mscx', 'utf8');
const options = {compact: true, ignoreComment: true, spaces: 4};
const source = convert.xml2js(xml, options);

convert 是XML-JS库,可以让你在XML、JSON和JavaScript对象之间转换。在这里,我们将XML转换为JavaScript对象,以方便操作。

接下来,记住措施的位置(在结果对象中的一个Measure 数组),以减少输入:

// an array of all measures
const origMeasures = source.museScore.Score.Staff.Measure;

然后,通过动机配置,从模板分数中读取它们:

// extract the patterns from the template
const patterns = {};
Object.keys(motives).forEach((letter) => {
  patterns[letter] = [];
  motives[letter].forEach((m) => {
    // measures start from 1, arrays from 0
    patterns[letter].push(origMeasures[m - 1]); 
  });
});

生成100个随机排列组合

变量combinations 将包含新的重新洗牌的字符串(例如:ACGFCDCEFIHEDEBCA,GIECBFCADCHAEFCED 等)。

使用Set ,可以防止重复:

// generate MAX random combinations
const combinations = new Set();
let these = distribution.split('');
while (combinations.size < MAX) {
  these.sort(() => 0.5 - Math.random());
  if (checkAdjecents(these)) {
    combinations.add(these.join(''));
  }
}

如果需要的话,一个辅助函数可以不允许相邻的动机:

function checkAdjecents(combo) {
  if (ADJOK) {
    return true;
  }
  for (let i = 1; i < combo.length; i++) {
    if (combo[i] === combo[i - 1]) {
      return false;
    }
  }
  return true;
}

编写100个新的XML文件

最后一步--遍历每一个新的组合,创建一个新的措施数组。在这里,第一个和最后一个措施总是相同的,因为这是一个要求,但你不一定要这样做。

编写新的XML是通过将修改后的JS对象重新转换为XML来完成的:

combinations.forEach((combo) => {
  // first and last measures are always the same
  const last = origMeasures[origMeasures.length - 1];
  const first = origMeasures[0];

  const newMeasures = [first];
  combo.split('').forEach((letter) => {
    patterns[letter].forEach((_, idx) => {
      newMeasures.push(patterns[letter][idx]);
    });
  });
  newMeasures.push(last);

  source.museScore.Score.Staff.Measure = newMeasures;
  source.museScore.Score.Staff.VBox.Text[0].text._text = combo;

  fs.writeFileSync(OUT + combo + '.mscx', convert.js2xml(source, options));
});

VBox.Text[0].text._text = combo; 是可选的,它将组合写成乐谱的标题。

在MuseScore中打开的结果示例:

完整的代码列表在GitHub上

批量转换

在这一点上,一切都已完成。但我们可以做得更好,生成PDF,分发给不使用MuseScore的音乐家/学生们。多亏了批量转换插件,这是很快速和痛苦的。

有很多格式可以选择!你点击 "确定",然后指向out ,脚本将所有MuseScore的XML写入该目录。

而这就是了。现在,out/ 目录包含了100个MuseScore文件和100个PDF,都是以字母动机的随机组合命名的。

重复使用脚本

如果你想为自己的目的、练习和创作音乐而重复使用这个脚本呢?为什么呢,这将给我带来最大的快乐

只要克隆github repo,改变Template.mscx ,编辑配置。然后运行...

$ node msms.js

...并在你的out/ 目录中找到一堆文件。然后,如果需要的话,按照上面的描述做批量转换为PDF。

初次接触Node?

给那些认为上面的部分主要是胡言乱语的人一个附带说明。如果你是Node.js的新手,这里还有一些提示。

  1. 从这里下载并安装Node

  2. 获取代码的副本:到github.com/stoyan/msms,然后 "下载ZIP"。

    解压缩到你想要的地方。导航到该目录。

  3. 通过运行

    $ npm i 
    
    

    ,安装依赖项

  4. 现在编辑msms.js 中的配置,改变模板Template.mscx ,然后运行

    $ node msms.js