Статьи

Цикл ‘for each’

Это обычное требование для цикла, который производит какое-либо действие над каждым нотоносцем в партитуре, или над каждым тактом на нотоносце, или над каждым объектом в такте, или над каждой нотой в NoteRest. Есть и другие более сложные требования, которые возникают обычно, если требуется произвести операцию над каждым тактовым объектом в партитуре в хронологическом порядке, или над каждым тактовым объектом в множественном выделении. В ManuScript имеется цикл for each, который может в одном операторе получить к ним доступ.
Самая простая форма цикла for each выглядит так:
thisscore = Sibelius.ActiveScore;
for each s in thisscore // устанавливает s в каждый нотоносец по очереди
{
   // ...делаем что-либо с s
}

Здесь thisscore – переменная, содержащая партитуру, переменная s устанавливается в значение каждого нотоносца партитуры по очереди. Это потому, что нотоносцы стоят на следующем уровне иерархии после партитуры (см. Иерархия объектов в Справочнике). Для каждого нотоносца в партитуре выполняются операторы в {} фигурных скобках.
Объект Партитура (Score) содержит нотоносцы, но, как мы видели, она может содержать объект Выделение (Selection), например, если пользователь выделил какой-либо пассаж перед запуском плагина. Объект Selection – отдельный случай, он никогда не возвращается в цикле for each, потому что в партитуре может быть только один такой объект; если Вы используете объект Selection в цикле for each, по умолчанию он возвращает тактовые объекты (BarObjects) (не Нотоносцы (Staves), Такты (Bars) или что-нибудь ещё!).
Давайте возьмём другой пример, на этот раз для нот в NoteRest:
noterest = bar.NthBarObject(1);
for each n in noterest // устанавливает n в каждую ноту по очереди
{
   Sibelius.MessageBox(“Pitch is “ & n.Name);
}

n устанавливается в каждую ноту в аккорде по очереди, а затем показывается её имя. Это работает, потому что Ноты (Notes) – это нижестоящий объект в иерархии после NoteRests. Если NoteRest – это, фактически, пауза (а не нота или аккорд), цикл не будет выполнен – Вы можете не проверять это отдельно.
Та же самая форма цикла возвратит каждый такт для нотоносца или системного нотоносца и каждый тактовый объект для такта. Эти циклы часто бывают вложенными, так что Вы можете, например, получить все такты из всех нотоносцев.
Первая форма цикла for each получает все объекты, расположенные на нижестоящем уровне иерархии от объекта, для которого выполняется цикл. Вторая форма цикла for each позволяет пропускать уровни иерархии, определяя, какой тип объектов Вы хотите получить. Это уберегает от множественного вложения циклов:
thisscore = Sibelius.ActiveScore;
for each NoteRest n in thisscore
{
   n.AddNote(60); // добавить среднюю До
}

Определение NoteRest после for each указывает Sibelius производить действия над каждой NoteRest в каждом такте каждого нотоносца в партитуре, в противном случае действие затронуло бы только объекты типа Нотоносец (Staff), потому что они расположены на следующем уровне иерархии от партитуры. NoteRests обрабатываются в правильном порядке, от верхнего к нижнему нотоносцу, слева направо. Это хронологический порядок. Если нужен другой порядок (скажем, все NoteRests в первом такте каждого нотоносца, затем все NoteRests во втором такте каждого нотоносца, и т.д.), Вы должны будете использовать вложенные циклы.
Вот один полезный фрагмент кода, удваивающий в октаву каждую ноту в партитуре:
score = Sibelius.ActiveScore;
for each NoteRest chord in score{
   if(not(chord.NoteCount = 0)){ // пропускаем паузы
      note = chord.Highest; // добавляем над верхней нотой
      chord.AddNote(note.Pitch+12); // 12 – число полутонов
   }
}

Этот код легко исправить так, чтобы в октаву удваивались лишь ноты в определённых тактах, нотоносцах, имеющие определённую звуковысотность или длительность, и т.д.
Этот вид цикла также очень полезен в сочетании с текущим выбором пользователя (выделением). Это выделение может быть получено из переменной, содержащей Партитуру (Score), следующим образом:
selection = score.Selection;
Затем мы можем проверить, является ли это выделением пассажа, и, если это так, то можно просмотреть, скажем, все такты в выделении средствами цикла for each:
if (selection.IsPassage) {
   for each Bar b in selection {
      // делаем что-либо с этим тактом
      …
   }
}