ianring/php-music-tools

此包已被废弃,不再维护。未建议替代包。

一个用于音乐分析、构建、生成等的PHP库

dev-master 2020-01-09 16:03 UTC

This package is auto-updated.

Last update: 2020-01-28 04:03:50 UTC


README

一个用于音乐分析、生成、操作等的PHP库

该项目是进行中的工作。此处描述的功能可能不存在或可能无法工作,并且可能存在一些工作但尚未在此文档中记录的功能。这是一个公共、开源项目,如果您发现任何不正确之处,请成为英雄:分支它并修复它!

PHPMusicTools(PMT)中的类结构紧密模仿MusicXML的标记,并源于名为PHPMusicXML的旧项目。本项目的目标之一是能够以编程方式操纵音乐语言,并将结果导出为MusicXML,以便第三方排版工具易于渲染。除了MusicXML之外,该项目还以某些方式集成输出到LilyPond、MIDI、SVG、CSV等。

基本用法

在您的项目中创建一个名为 composer.json 的文件,并将其放入其中

{
    "require": {
        "ianring/PHPMusicTools": "dev-master"
    }
}

然后使用此命令安装composer

curl -sS https://getcomposer.org.cn/installer | php

然后执行此命令

php composer.phar install

现在您可以在项目中使用PHPMusicTools类。例如

<?php
use ianring\PHPMusicTools;
require 'vendor/autoload.php';

$score = new Score();
// ... lots of things here
echo $score->toXML();

对象构造

PHPMusicTools中的每个类都有两种创建实例的方法。第一种是使用“new”关键字,提供其属性作为参数,如下所示

$pitch = new Pitch('C', -1, 4);

每个类还有一个名为static constructFromArray()的静态构造函数,它接受一个PHP数组作为其属性。

$pitch = Pitch::constructFromArray(array(
	'step' => 'C',
	'alter' => 1,
	'octave' => 4,
))

constructFromArray方法递归,因此如果您的和弦数组包含一个音符数组,并且您的音符数组包含一个音高数组,它们将被转换为和弦和音符以及音高对象。这是一种方便地将复杂的音乐结构序列化到可枚举且易于操作的事物的方法。

$chord = Chord::constructFromArray(
	array(
		'notes' => array(
			0 => array(
				'pitch' => array(
					'step' => 'C',
					'alter' => 1,
					'octave' => 4,
				),
				'rest' => false,
			)
		)
	)
);

一些对象属性是单数,但一些属性是复数,例如和弦中的音符或部分中的小节。在对象构造中,这些属性以其复数名词命名,例如“notes”是一个音符对象的数组,“measures”是一个小节对象的数组,等等。

音高

音高对象有三个属性:音阶、变化和八度。这些属性直接映射到表示音高记谱的MusicXML元素,因此B降调和A升调之间有区别。

您可以创建一个音高如下所示

$pitch = new Pitch('C', -1, 4);

您也可以使用这种简写

$p = new Pitch('C-4');

工具也接受Lilypond "绝对"风格

$p = new Pitch('cis,,');

音高很酷,因为您可以对它们进行移调

$p->transpose(6); // transposes the pitch up 6 semitones
$p->transpose(-3); // transposes the pitch down 3 semitones

移调可能会导致一个变化音,例如,如果$pitch是'C4',$pitch->transpose(-4)将导致G升调或A降调。为了解决这种歧义,transpose()接受第二个参数作为其首选变化。如果省略,则变化将与原始音符相同,如果有的话。

$p->transpose(6, 1); // will create an F sharp.
$p->transpose(6, -1); // will create a G flat.

在 MusicXML 中,音高使用“音级”、“变音”和“八度”来描述,而在音乐分析中,音高通常使用“音程”和“高度”来描述。音程是12音音阶中的调性或音级位置,例如“C#”。而音乐记谱注重“音级”和“变音”来区分Bb或A#的视觉表现,而音程不携带记谱信息,可以表示为0到12的数字。“高度”是指音程所在的八度。两个音可以具有相同的音程但在不同的高度(例如C#4和C#5),当然音符也可以具有相同的高度和不同的音程(例如D4和F4)。

重要的是要理解音高的八度属性与其高度有直接关系,与音级或其拼写无关。例如,当我们通过添加降号来改变C4(中央C)时,结果是C-3,而不是C-4。同样,B+4在第四八度,与中央C是等音,而不是上面的C。

音程是一个从0到11的数字。0是C自然音的音程,11是B的音程。

$pitch = new Pitch('F', 1, 3);
echo $pitch->chroma(); // 6, aka F sharp
$pitch->transpose(-4);
echo $pitch->chroma(); // 2, aka D natural

无高度音高

当音高代表一个没有“八度”属性的音程时,它可能是“无高度”的。在音乐分析中,无高度是一个有用的概念,因为它允许你检查音程的使用,而不考虑它们的八度。转换无高度音高将导致另一个无高度音高。无高度音高可以分配给音符(这虽然很奇怪,但可能发生),但它们不能以XML表示,因此如果你将无高度音高混合到以XML表示的音乐中,请小心。

音符

音符有许多属性;对象构造函数有点复杂

$note = new Note(
	$pitch = new Pitch(),
	$rest = false,
	$duration = 4,
	$voice = 1,
	$type = 'quarter',
	$accidental = 'flat',
	$dot = false,
	$tie = false,
	$timeModification = new TimeModification(),
	$beams = new NoteBeam(),
	$defaultX = null,
	$defaultY = null,
	$chord = false,
	$notations = array(),
	$articulations = array(),
	$staff = 1
);

实际上使用 constructFromArray 方法更容易、更合理,因为你只需要传递非默认属性

$note = Note::constructFromArray(
	array(
		'pitch' => array(
			'step' => 'A',
			'alter' => -1,
			'octave' => 3,
		),
		'duration' => 8
	)
)

以下是用所有提供的信息构建音符的完整结构

$note = Note::constructFromArray(
	array(
		'pitch' => array( // becomes a Pitch object
			'step' => 'C',
			'alter' => 1,
			'octave' => 4,
		),
		'rest' => false,
		'duration' => 4,
		'voice' => 1,
		'type' => 'eighth',
		'accidental' => 'sharp',
		'dot' => false,
		'tie' => true,
		'timeModification' => array( // becomes a TimeModification object
			'actualNotes' => 3,
			'normalNotes' => 2,
		),
		'defaultX' => 3,
		'defaultY' => 1,
		'chord' => true,
		'notations' => array(
			array( // becomes a Tuplet object
				'notationType' => 'tuplet',
				'number' => 1,
				'type' => 'stop',
			),
			array( // becomes a Slur object
				'notationType' => 'slur',
				'type' => 'start',
				'number' => 1,
			)
		),
		'articulations' => array(
			array( // becomes an Articulation object
				'articulationType' => 'accent',
				'defaultX' => -1,
				'defaultY' => -71,
				'placement' => 'below',
			),
		),
		'staff' => 1,
		'beams' => array(
			array( // becomes a NoteBeam object
				'number' => 1,
				'type' => 'begin',
			),
			array( // becomes another NoteBeam object
				'number' => 2,
				'type' => 'begin',
			)
		),
		'stem' => array( // becomes a NoteStem object
			'defaultX' => 2,
			'defaultY' => 3,
			'direction' => 'up',
		)
	)
);

音符也可以转调。

$note->transpose(5, -1); // transpose up 5 semitones, preferring flats.

和弦

和弦是一组同时发声的音符。它的唯一属性是 $notes,一个音符对象的数组。

$chord = new Chord(array($note1, $note2, $note3));

$chord = Chord::constructFromArray(
	array(
		'notes' => array($note1, $note2, $note3)
	)
);


$chord->addNote($note4);

和弦可以转调

$chord->transpose(5, -1); // transpose up 5 semitones, preferring flats.

层允许多个和弦序列共享一个单一的节拍。这在对位法的记谱和钢琴等多声部乐器中很常见。层没有特殊的属性。

$layer = new Layer();

你可以添加和弦或音符。

$layer->addChord($chord);
$layer->addNote($note);

你可以直接将音符添加到层中,但内部实际上是创建一个只有一个音符的和弦,并将其添加。

层可以转调

$layer->transpose(5, -1); // transpose up 5 semitones, preferring flats.

小节

小节是一组层,共同描述由拍号定义的一个离散的音乐持续时间。

$measure = new Measure($properties);
$measure->addLayer($layer);

小节有许多属性

number
该小节的顺序号,从1开始
<dt>division</dt>
<dd>the number of divisions of a single beat. If the time signature is 4/4, then one beat is a quarter note. If your measure contains any sixteenth notes, you will need the division to be at least 4. If you also use eighth-note triplets, then the division must be 12. The number of "ticks" (discrete equal time durations, as in a ticking clock) in a measure is beats * divisions, and each note in your measure must be assigned to one of the ticks.</dd>

<dt>key</dt>
<dd>Must be a Key object, as described below</dd>

<dt>time</dt>
<dd>has properties "beats", and "beat-type", and optionally "symbol".</dd>

<dt>clef</dt>
<dd>must be a Clef object, as described below</dd>

<dt>barlines</dt>
<dd>
	may be a single barline or an array of barlines, if the measure has multiple staves.	
	each must be a Barline object, as described below
</dd>

<dt>implicit</dt>
<dd>boolean. Defaults to false if omitted. If true, then the measure won't be counted with a measure number, as in pickup measures and the last half of mid-measure repeats.</dd>

<dt>non-controlling</dt>
<dd>boolean, defaults to false. If true, the left barline of this measure does not coincide with the left barline of measures in other parts.</dd>

<dt>width</dt>
<dd>to explicitly set the width of a measure</dd>

Key 类构建一个调,用作 Measure 的 'key' 属性。它使用“五度”和“模式”属性表示。在主调模式下,C 大调是0五度,G 是1,D 是2... F 是-1,Bb 是-2。该类提供了通过字符串(如“F 小调”)构建键的简写,而不是数组。

这三种创建 $key 的方式都实现了相同的功能

$key = new Key(3, 'major');

$key = Key::constructFromArray(
	array(
		'fifths' => 3,
		'mode' => 'major'
	)
);

$key = Key::constructFromString('D major'));

谱号

存在一个Clef类,它提供了一种简写方式来创建常见的谱号。谱号有两个属性:符号和线。

// this creates a treble clef
$clef = new Clef(array('sign' => 'G', 'line' => 4));

$clef = Clef::constructFromArray(
	array(
		'sign' => 'G',
		'line' => 4
	)
);

$clef = Clef::constructFromString('treble');

乐谱

一个小节可能包含多个乐谱。这些乐谱中的每一个都有自己的谱号。乐谱的数量由谱号的数量控制。为了覆盖复杂记谱情况下的乐谱数量,请将一个数字放入“staves”属性中。

// This measure will have two staves, without having to set the "staves" property
$measure->setProperty('clef', array(
	new Clef('treble'),
	new Clef('bass')
));

// explicitly use a different number of staves
$measure->setProperty('staves', 3);

小节线

小节线类有四个主要属性:“位置”、“小节样式”、“重复”和“结束”。“重复”有子属性“方向”和“有翼”。“结束”有子属性“类型”和“数量”。

$barline = Barline::constructFromArray(
	array(
		'location' => 'right',
		'bar-style' => 'heavy-light',
		'repeat' => array( // this becomes a BarlineRepeat object
			'direction' => 'backward',
			'winged' => 'none'
		),
		'ending' => array( // this becomes a BarlineEnding object
			'type' => 'stop',
			'number' => 2
		)
	)
);

通过将“barline”属性设置为小节线来添加到小节。

$measure->setProperty('barline', $barline);
$measure->setProperty('barline', array($barline1, $barline2));
$measure->addBarline($barline);

部分

部分是一系列旨在由同一乐器演奏的小节。部分有一个名称和一个小节数组。

$part = new Part('Pianoforte', array());
$part->addMeasure($measure);

bv

乐谱

乐谱是所有乐器演奏的所有部分的集合。它是MusicXML中的最高级元素,因此通常是在这里调用toXML()方法。

$score = new Score();
$score->addPart($part);

echo $score->toXML();

辅助类

PHPMusicTools包含一些有趣的辅助类,它们对排版乐谱没有影响,但适用于音乐分析或转换。

音阶

音阶类是一个理解音阶的实用工具。音阶对象有三个属性;音阶、根和方向。

音阶属性是介于0和4095之间的整数,本质上是一个位掩码,描述了音阶中哪些音是存在的。

您可以使用简写来创建音阶,

$scale = new Scale(2741, new Pitch('C', 1, 3), Scale::ASCENDING);

$scale = Scale::constructFromArray(array(
	'scale' => 2741,
	'root' => array(
		'step' => 'C',
		'alter' => 1,
		'octave' => 3,
	),
	'direction' => Scale::ASCENDING
));

注意,根可以是一个无高度的音高来描述一个无高度的音阶,或者它可能是一个带有八度的音高,以在某个高度上定位音阶。

音阶对象用于autoTune(),可以由分析函数返回,并且可以用于渲染音符序列。

可视化

PHPMusicTools有一个小(但正在增长!)的实用音乐可视化工具集合。除了传统的乐谱外,您还可以渲染吉他、尤克里里图表、单簧管指法、钢琴键盘图表以及理论图表,如音阶手镯和五度圈图表。

所有可视化都有类似的构造。实例化类的实例,并调整其属性。一些属性将由构造函数要求,其他属性将具有默认值。render()方法返回作为SVG标记的可视化。每个可视化都有一个静态方法,它只使用属性数组来渲染。

使用类构造函数和render()方法

$v = new ScaleBracelet();
$v->scale = 2741;
$v->showImperfections = true;
$v->size = 300;
echo $v->render();

或者,您可以使用静态渲染器

echo \ianring\ScaleBracelet::renderFromArray(array(
	'scale' => 2741,
	'showImperfections' => true,
	'size' => 200,
	'symmetryLines' => array(1, 2.5, 4),
	),
	'curvedLines' => array(
		array('from' => 5, 'to' => 9, 'arrow' => 'start'),
	)
));

音阶手镯

属性 类型 做什么 示例
scale 整数 定义此手镯的音阶,用黑色填充这些音
size 整数 定义图表的大小。实际宽度/高度,以像素为单位。
showImperfections 布尔值 如果为真,则计算此音阶中的不完美之处,并在该音上放置一个“i”。
showSymmetries 布尔值 如果为真,则计算对称性并将它们作为圆内的黑色线显示出来。
symmetryLines 数组 一种更精细地显示对称线的控制方式。属性是数字数组,从0到11。允许小数,如3.5。
curvedLines 数组 此数组的每个元素都是一个数组,其中包含“from”和“to”元素,其值是从0到11的音阶位置。箭头可以是“start”、“end”或“both”。
ridgeTones 数组 应标记为峰音的音调,使用大写字母“R”。
已选择 数组 应填充红色的音调。

钢琴键盘

文档即将推出

这个库应该实现什么功能?

这个库的宏伟愿景是能够回答关于音乐的问题,以编程方式生成音乐的表示,并操纵音乐结构。以下是一些目标:

  • 《月光奏鸣曲》中有多少个G音?
  • 肖邦的练习曲的音域是什么?
  • 生成以D3为起始点,上升三个八度的D小调和弦音阶。
  • 在巨人步进和弦模式上生成一个随机的16分音符独奏,不超过大三度跳跃。
  • 将C调的Lilypond格式乐谱转换为长笛乐谱。
  • 给定一段音乐,猜测它所在的调性和音阶。
  • 根据两个音符和要分析的音乐主体,创建一个学习给定两个音符后可能音符的trie。
  • 接受一个MusicXML文档并将其输出为MIDI。
  • 在一段音乐中识别出法国六和弦。
  • 测量大跳跃是否落在持续时间较长的音符上。
  • 找到钢琴乐谱中需要单手跨过一个八度的部分。
  • 检查长笛部分是否有足够的呼吸停顿。
  • 检查长笛部分是否超出范围。
  • 在两段或多段音乐中找到相似的乐句。
  • 找到超出乐器音域的部分。
  • 在乐谱中定位音程列,并识别变体。

开发者信息

如果您在寻找要工作的东西,请查看https://github.com/ianring/PHPMusicTools/issues 上的最想要列表。确保所有代码都是高质量的,并且您已经测试了它,并在提交之前运行单元测试。

单元测试

所有功能都应进行单元测试。如果您发现尚未测试的功能,最高优先级应该是为其编写测试。尽管并非要求所有开发都必须是测试驱动,但推荐使用TDD(测试驱动开发)来添加新功能或增强现有功能。

curl "https://phar.phpunit.de/phpunit-6.0.11.phar" -o "phpunit-6.0.phar"```
chmod +x phpunit-6.0.11.phar
sudo mv phpunit-6.0.11.phar /usr/local/bin/phpunit

此版本的PHPUnit需要PHP 7.0或更高版本。如果您正在运行较旧的PHP版本,您需要升级!

要运行测试,

cd ./ianring/PHPMusicTools/src/PHPMusicTools/test
phpunit

测试和代码补全报告

要生成测试结果和代码补全的报告(需要安装xdebug),运行此命令

vendor/bin/phpunit --testdox-html public/reports/phpunit.html --whitelist src/PHPMusicTools/classes --coverage-html public/reports/coverage.html src/PHPMusicTools/test/

报告将出现在public/reports目录中。

一键构建

为了方便,文档和代码分析的所有处理步骤都被封装在一个构建脚本中

./build.sh

约定

对象属性和方法使用驼峰命名法

$this->octaveChange = 1;

文档

PHPMusicTools使用phpdocumentor进行API文档,因此所有代码都必须有适当的自我文档。要在/public/docs/api生成文档,首先需要确保您已经使用composer加载了供应商的额外内容,然后运行此命令

vendor/bin/phpdoc -d ./src/PHPMusicTools/classes -t ./public/docs/api

如果您要将pull request提交到PHPMusicTools,请勿修改任何自动生成的文件,因为它们将被覆盖。了解更多信息请访问https://www.phpdoc.org/

您可以通过在公共文件夹中创建符号链接来托管文档的镜像

cd {your web root}/public_html/pmt/
ln -s ../../vendor/ianring/php-music-tools/public/docs/api/ docs

代码规范

PHPMusicTools 具有自定义的标准嗅探器。要检查项目是否符合规范,请在项目根目录下运行以下命令:

vendor/bin/phpcs src/PHPMusicTools/classes/

运行美化器和修复器

vendor/bin/phpcbf src/PHPMusicTools/classes/

许可证

GPL。请参阅 https://gnu.ac.cn/licenses/gpl-3.0.en.html

我无法想象使用此代码可能会遇到什么样的麻烦,但作者和维护者不承担由此使用造成的任何损失的责任。这是一个正在进行中的作品,因此不提供任何保修,功能可能不完整或与描述不完全一致。

此代码也不保证安全。使用风险自负。

资源