Продолжаем добавлять функциональность к музыкальному MIDI-синтезатору на двух чипах AY-3-8910 с помощью генератора огибающей.
Оглавление
В комментариях к предыдущей статье мне справедливо указали на то, что в чипе AY-3-8910, помимо тоновых генераторов прямоугольного сигнала, есть ещё генератор огибающей. Рассмотрим, как его можно использовать в музыкальных целях.
Генератор огибающей
В описании чипа AY сказано, что для управления амплитудой звучания в каналах A/B/C предназначены регистры R10/R11/R12. В каждом из них используются младшие 5 бит. Если в бите 4 (нумерация с нуля) значение 0, то младшие 4 бита задают громкость звучания без огибающей — доступны значения громкости от 0 (тишина) до 15 (максимальная). Если же в бите 4 значение 1, то в соответствующем канале включается использование генератора огибающей, частоту и форму которой можно выбрать с помощью дополнительных регистров R13/R14/R15. Задавать явно значение громкости в таком случае нельзя. Параметры огибающей общие для всех трех каналов.
Тактовая частота чипа AY делится на 256, полученное значение дополнительно делится на 16-битное значение делителя в R13/R14, и получается итоговая частота генератора огибающей. Например, если тактовая частота 1 МГц, и задано значение делителя 0 или 1, то частота генератора максимальна и равна 1000000/256 = 3906,25 Гц. В клонах ZX Spectrum часто используется тактовая частота 1,75 МГц, в таком случае максимальная частота генератора огибающей примерно равна 6836 Гц. Если задать значение делителя 2, то максимальная частота делится на 2, что вызовет её «скачок» сразу на октаву вниз. Точность задания частоты огибающей в области её максимальных значений невысока (об этом будет подробнее рассказано ниже).
В битах регистра R15 задается форма огибающей:
Как видим, возможны варианты с однократным изменением амплитуды, после чего звук либо уходит в тишину, либо остается на максимальном уровне. Один из таких вариантов со значением 9 (двоичное 1001) использован автором изначального устройства для реализации звуков ударных (см. вызов psg.setEnvelope в процедуре startFX). При этом, диапазон делителя огибающей от 300 до 1800 (параметр envdecay в структурах FXParams, заданных в массиве perc_params) соответствует частотам примерно от 13 Гц до 2 Гц, т. е. время затухания звука с такими огибающими — от 1/13 до 1/2 сек. Для имитации звука затухания ударных инструментов это нормально (самые короткие звуки - Side Stick и Closed Hi Hat, самый длинный - Crash Cymbal), но для генерации музыкальных тонов нужны, во-первых, более высокие частоты, а во-вторых — периодическое изменение огибающей.
Из периодических наиболее интересными являются варианты формы огибающей со значениями 8 (двоичное 1000) и 10 (двоичное 1010). Это пилообразные сигналы — первый из них с резкой атакой, второй должен звучать более мягко, и фактическая частота его в 2 раза меньше, т. е. он должен звучать на октаву ниже. Аналогичные варианты 1100 и 1110 начинаются с тишины, а не максимальной громкости, но больше они ничем не отличаются от двух предыдущих.
Изменения в коде
Как уже было сказано выше, в случае использования генератора огибающей явно задавать громкость звука в канале синтеза нельзя. В то же время, в изначальном коде с помощью плавного изменения громкости реализована огибающая ADSR:
При нажатии клавиши на MIDI-клавиатуре (т. е. включении ноты) сначала отрабатывается атака звука (Attack), затем спад (Decay) и поддержка (Sustain) – в этом режиме нота звучит до тех пор, пока клавиша не будет отпущена. При отпускании клавиши включается этап затухания (Release), в течение которого громкость плавно спадает до нуля. Всё это реализовано в функции update100Hz класса Voice, а значение текущего этапа ADSR хранится в переменной m_adsr (для удобства — прямо в виде символов 'A', 'D', 'S', 'R', а также 'X' в случае звучания ударных, который мы рассматривать не будем).
Поскольку явно управлять громкостью в режиме использования генератора огибающей невозможно, из всех этих этапов нужно оставить только Sustain. Также, необходимо добавить режим работы с генератором огибающей.
Итак, первые изменения, которые необходимо внести в код:
Добавить режим eSaw («пила») в список режимов работы синтезатора enum eDetuneType. Он будет вторым в списке, а далее будут следовать режимы detune, рассмотренные в предыдущей статье (их мы изменять и рассматривать здесь не будем).
В функции включения ноты start (класса Voice) в случае, если включен режим eSaw, сразу присвоить значение 'S' переменной m_adsr, а также задать значение амплитуды AMPL_MAX + 1, что равно 1024, чтобы при сдвиге вправо на 6 бит получилось значение 16. Тогда при передаче этого значения в функцию psg.setTone оно попадет в соответствующий каналу A/B/C регистр R10/R11/R12 и включит режим использования генератора огибающей.
В функции выключения ноты stop (класса Voice) в случае, если включен режим eSaw, не переходить к этапу 'R', а сразу выключать звучание ноты.
Добавить функцию setSaw, в которой производится расчет частоты огибающей и включается нужный режим генератора — для начала пусть это будет пилообразная огибающая с типом 8 (двоичное 1000).
Эту функцию необходимо вызывать как из функции start, так и из update100Hz, чтобы можно было изменять частоту огибающей во время звучания ноты.
Заодно я объединил две процедуры записи значений в регистры чипов AY в одну процедуру writeReg_AY и немного оптимизировал прочие процедуры, записывающие данные в регистры чипов, хотя собственно к решению данной задачи это не относится.
Первый эксперимент
Для начала попробуем включить генератор пилообразной огибающей, частоту которой будем изменять с помощью ранее добавленного переменного резистора. Как мы помним, в Arduino при считывании из АЦП могут поступать значения от 0 до 1023. Если использовать прямо этот диапазон значений в качестве делителя частоты огибающей, при тактовой частоте 1 МГц получим частоты огибающей примерно от 3906 Гц до 3,8 Гц. На мой взгляд, нет никакого смысла делать такую низкую частоту огибающей (по звучанию это будет похоже на треск или щелчки), поэтому значение, считанное из АЦП, разделим на 16 (при желании, можно этот коэффициент SAW_RATIO_DEN изменить по своему вкусу). В результате диапазон регулировки частот сузился до значений 3906 … 61 Гц. Первый вариант функции setSaw получился таким:
#define SAW_RATIO_DEN 16
void setSaw() {
int inval = analogRead(PIN_DETUNE_RATIO) / SAW_RATIO_DEN;
psg.setEnvelope(inval, 8);
}
Теперь на звучащую ноту накладывается пилообразная огибающая с частотой, регулируемой резистором. Звучит это примерно так:
Чтобы дополнительно разнообразить звучание, вспомним, что на MIDI-клавиатуре есть колеса Pitch Bend и Modulation, поступающие значения с которых постоянно обновляются и сохраняются в переменных g_pitchBend и g_modDepth. В предыдущей статье описывается, как с помощью колеса Modulation сделать «плавание» звука по высоте с некоторой частотой. Попробуем, по аналогии с этим, сделать периодическое изменение частоты огибающей туда-обратно. Для этого добавим в процедуру дополнительные параметры:
void setSaw(int modstep, int modlen) {
int modval = (modstep > modlen / 2) ? modlen - modstep : modstep,
inval = analogRead(PIN_DETUNE_RATIO) / SAW_RATIO_DEN,
enval = inval + int(32.0 * float(g_modDepth) * float(modval) / float(0x7f * modlen) + 0.5);
psg.setEnvelope(enval, 8);
}
Параметр modstep содержит текущий шаг модуляции, а modlen — длину периода модуляции. Текущий шаг постоянно обновляется в процедуре update100Hz (т. е. 100 раз в секунду), а длина задается значением 100 - g_pitchBend * 3 / 2. Как мы помним, от колеса Pitch Bend поступают значения в диапазоне -64...63, поэтому возможная длина периода модуляции находится в диапазоне 0,04 … 1,95 сек. В итоге, переменный резистор задает начальное значение частоты огибающей, а с помощью колес Pitch Bend и Modulation можно регулировать периодическую модуляцию этой частоты, а именно скорость и глубину:
При желании, можно изменить значения констант, чтобы получить другой диапазон скорости и глубины модуляции. Всё это дает довольно интересные звуковые эффекты, которые, на мой вкус, не очень-то отличаются музыкальностью.
Музыка с помощью огибающей
В предыдущей статье описывалось добавление режимов detune, что позволяет разнообразить звучание синтезатора более интересными и «сочными» звуками. Попробуем для подобной цели использовать генератор огибающей.
При написании музыки как для ZX Spectrum, так и прочих платформ, использующих чипы AY, генератор огибающей часто используется для получения именно музыкальных тонов, но с характером звучания, отличным от основных тоновых генераторов прямоугольного сигнала. Проблема заключается в том, что для задания частоты основных генераторов берется тактовая частота, деленная на 16, а в случае с генератором огибающей тактовая частота делится на 256, и уже относительно этой частоты дополнительным делителем задается нужная частота. Слышимый диапазон частот начинается примерно от 20 Гц. При тактовой частоте 1 МГц в этот диапазон ложатся частоты генератора огибающей, получаемые заданием величины делителя от 0 до примерно 195. В то время, как для генератора основной частоты возможный диапазон делителя в слышимой области — от 0 до 3125. Как видно, точность задания частоты генератора огибающей невысока.
Для пояснения этого момента рассмотрим, какие доступные значения частот огибающей максимально близки к музыкальным нотам. В рассматриваемом MIDI-синтезаторе самая низкая доступная нота - «до» контр-октавы с частотой 32,7 Гц. В диапазоне от этой частоты до максимально возможной 3906 Гц доступно всего 119 значений делителя частоты, и максимально близки к нотам только перечисленные в таблице.
Таблица 1
В таблице после колонки с названием ноты и октавы следует колонка с её эталонной частотой (в равно-темперированном строе), далее — колонка с максимально близкой к ней частотой огибающей и колонка с соответствующим значением делителя огибающей; последняя колонка — отклонение частоты огибающей от ближайшей ноты в полутонах. Отклонение менее 0.1 полутона будем условно считать мало различимым на слух. Отклонения более чем на 0,1/0,2/0,5 полутона отмечены соответственно желтым, оранжевым и красным цветами фона. Отклонение более чем на 0,5 полутона на слух уже воспринимается как серьезная фальшь, а более полутона — как совершенно другая нота. Очевидно, что чем выше нужная нота, тем сложнее подобрать для нее близкую частоту генератора огибающей.
Рассмотрим, как обстоит дело, если тактовую частоту чипа AY увеличить до 2 МГц.
Таблица 2
А что, если увеличить тактовую частоту чипа AY до 4 МГц?
Таблица 3
Как видим, отклонения гораздо меньше по сравнению с частотой 1 МГц, поэтому я решил переделать код так, чтобы тактовая частота равнялась 2 МГц. Для изменения частоты понадобилось изменить:
значение константы DIVISOR для процедуры настройки аппаратного таймера clockSetup
значение константы ayf базового значения частоты для процедуры расчета делителя музыкального тона getPitch (а также для расчета частоты огибающей — см. ниже)
параметры звучания ударных инструментов в процедуре startFX
Если в коде разрешен #define CLOCK_4MHZ, то частота увеличивается до 4 МГц, в противном случае она равна 2 МГц (либо 1 МГц, если разрешен #define CLOCK_1MHZ).
Я оставил возможность выбора тактовой частоты по нескольким причинам.
Во-первых, я не смог найти однозначно документированных ограничений на тактовую частоту. В одном из мест пишут, что якобы для чипа YM2149 максимальная частота 4 МГц, а для AY-3-8910 только 2,5 МГц. Мой чип AY-3-8910 спокойно работает на частоте 4 МГц (это показано на заглавной фотографии к статье), но дать гарантии того же для всех выпущенных чипов я не могу. Во-вторых, максимальное значение делителя частоты тона 4095 при тактовой частоте 4 МГц соответствует ноте с частотой 61 Гц, что почти на октаву выше самой низкой ноты данного MIDI-синтезатора. По этой причине в коде на случай частоты 4 МГц добавлена проверка — самые низкие 11 нот увеличиваются по высоте на октаву. Желающим реализовать данный проект я оставляю выбор самостоятельно решить, что важнее — весь диапазон нот либо лучшая точность воспроизведения музыкальных тонов в верхнем диапазоне (ниже будет пояснено, почему это важно при работе с огибающей).
Даже при увеличении тактовой частоты чем выше нота, тем меньше точность. В каждой из таблиц правая часть соответствует диапазону от первой октавы и выше — видно, что в этом диапазоне чистых нот, генерируемых огибающей, практически нет. Получается, что наибольшую точность «попадания» в ноты генератором огибающей можно достичь в басовых нотах. Поэтому, попробуем добавить «сочности» звучания, включая одновременно со звучащей основной нотой огибающую, частота которой соответствует аналогичной ноте — например, на октаву ниже.
Добавляем басы
Для того, чтобы рассчитывать частоту огибающей в соответствии с воспроизводимой нотой, добавим в процедуру setSaw параметр note, в котором будем передавать текущую ноту. При этом, режим с периодическим изменением частоты огибающей хочется сохранить. Поступим следующим образом: добавлять басовую ноту будем только в случае, если считанное с переменного резистора значение равно нулю. Если же резистор отклонен от нулевого положения, будет работать режим регулировки частоты огибающей, описанный выше в разделе «Первый эксперимент». Процедура теперь будет выглядеть так:
void setSaw(int modstep, int modlen, note_t note) {
int modval = (modstep > modlen / 2) ? modlen - modstep : modstep,
inval = analogRead(PIN_DETUNE_RATIO) / SAW_RATIO_DEN, enval;
if (inval < 1) {
int index = note - MIDI_MIN, oct = index % 12;
freq_t freq = freq_table[index];
enval = int(ayf / freq);
enval /= (oct < 2 ? 16 : 8);
}
else
enval = inval + int(32.0 * float(g_modDepth) * float(modval) / float(0x7f * modlen) + 0.5);
psg.setEnvelope(enval, 8);
}
Как видим, расчет значения делителя огибающей производится примерно так же, как и для генератора основного тона в процедуре getPitch – берется эталонное значение частоты ноты из таблицы freq_table, и на него делится значение базового делителя тактовой частоты ayf. Например, для тактовой частоты 1 МГц и ноты «ля» первой октавы получим значение делителя enval = 62500 / 440 = 142. Как мы помним, для генератора огибающей значение базового делителя в 16 раз меньше, поэтому значение 142 соответствует ноте, которая на целых 4 октавы ниже, и нужно разделить его на 8, чтобы поднять его на 3 октавы. Получим значение 17, что примерно соответствует «ля» малой октавы, которая как раз на октаву ниже основной ноты. Если нота сама по себе очень низкая, понижать её ещё на октаву смысла нет, поэтому для нот из контр-октавы и большой октавы будем генерировать огибающую с такой же высотой, как основная нота.
Выставим переменный резистор в нулевое положение и послушаем, как теперь звучат ноты:
По сравнению с чистыми нотами звук действительно изменился, но на большинстве нот возникают дополнительные колебания громкости с разной частотой — так называемые «биения». Попробуем разобраться, откуда они возникают.
Биения
При включении огибающей её пилообразный сигнал умножается на основной прямоугольный сигнал. Если частоты этих двух сигналов немного отличаются, как раз и возникают биения. Например, послушаем звук ноты «до» большой октавы, синтезированный чипом AY с тактовой частотой 1 МГц с включенной пилообразной огибающей:
В таблице частот freq_table найдем частоту ноты «до» большой октавы — это примерно 65,41 Гц. При тактовой частоте 1 МГц делитель частоты тона равен 625000/654 = 955 (после приведения к целому). Разделим его ещё на 16 для получения делителя частоты огибающей и получим 955/16 = 59 (после приведения к целому). Теперь рассчитаем, какие реально частоты будут синтезировать генераторы чипа AY, и чему равна разница между ними:
f1 = 1000000 / 16 / 955 = 65,445 Гц (частота тона)
f2 = 1000000 / 256 / 59 = 66,207 Гц (частота огибающей)
D = f2 – f1 = 0,762 Гц
Длина периода колебаний с такой частотой равна 1/D = 1,312 сек. Посмотрим на волновую картинку такого звука, записанного с выхода синтезатора:
Как видим, между повторениями звука как раз примерно 1,312 секунды. У других нот частота биений другая — она зависит от разницы между частотами основного тона и огибающей. Для каких-то музыкальных идей и эффектов биения вполне могут быть уместны, но что делать, если хочется получить звук без них?
Устраняем биения
Если ещё раз посмотреть видео из раздела «Первый эксперимент», можно заметить, что нота «ля» контр-октавы звучит без биений. Частота этой ноты 55 Гц — рассчитаем для неё значение делителя частоты тона при тактовой частоте 1 МГц: 625000/550 = 1136. Значение делителя огибающей для этой же ноты: 1136 / 16 = 71, при этом 1136 делится на 16 без остатка. Аналогично для ноты «ля диез» этой же октавы: делитель частоты тона для неё 625000/583 = 1072, что также делится на 16 без остатка, и эта нота с включенной огибащей звучит тоже без биений.
Получается, что если значение делителя частоты основного тона делится на 16 без остатка, то при делении его на 16 получим огибающую, которая при наложении на основной сигнал даст звук без биений. Подробнее об этом можно прочитать в статье [2] (спасибо Сергею Бульбе за ссылку).
Итак, нужно добавить режим работы, в котором значение делителя частоты тона будет округляться до 16, тогда и при получении из него делителя для генератора огибающей будет получаться частота, дающая звук без биений. Можно было бы добавить ещё одну константу в список режимов eDetuneMode и переключаться в этот режим ещё одним нажатием кнопки, но я решил поступить иначе. В данном режиме действие колес Pitch Bend и Modulation на MIDI-клавиатуре становится бессмысленным, т. к. даже малейшее отклонение высоты тона сразу же вызовет биения, от которых мы стремимся избавиться. Поэтому в случае, когда переменный резистор находится в нулевом положении, и частота огибающей вычисляется автоматически по текущей ноте, я решил наделить колесо Modulation функцией дополнительного переключателя параметров.
Полный диапазон значений, которые приходят из сообщения Modulation при отклонении колеса — от 0 до 127. Если делить нацело значение положения колеса на 26 (в коде это константа ENV_STEP_DIV), будем получать значения индекса от 0 до 4, т. е. сможем отклонением колеса переключаться между пятью значениями параметра:
В нижнем положении колеса Modulation значение параметра равно 0 — пусть в таком случае расчет делителя частоты тона будет вестись без округления, и колесо Pitch Bend будет действовать, как и раньше. Если отклонить колесо Modulation от нижнего положения чуть больше, чем на 1/5 диапазона, значение параметра будет равно 1 — в таком случае делитель частоты тона будет округляться до 16. Остальные значения 2...4 параметра будут обсуждаться ниже, а пока посмотрим, в чем разница по звучанию между режимами без округления и с округлением:
Как мы помним, чем выше тактовая частота AY, тем более точно можно воспроизвести музыкальные ноты как в основных тонах, так и в огибающей. Именно здесь может пригодиться установка тактовой частоты чипа AY в 4 МГц. К сожалению, даже такая частота в самых высоких нотах не дает нужную точность, если делитель частоты округлять до 16. Становится явно слышно не очень чистое звучание высоких нот (обратите внимание в конце видеоролика на звучание последней ноты), а некоторые ноты даже «сливаются» в одну.
При тактовой частоте 4 МГц в самой верхней, 3-й октаве MIDI-диапазона данного синтезатора хотя бы ноты на белых клавишах (без диезов) извлекаются более-менее чисто на слух, но некоторые ноты на черных клавишах извлекают те же ноты, что соседние белые клавиши. При тактовой частоте 2 МГц гамму «до мажор» в 3-й октаве уже вовсе не получается сыграть, а при частоте 1 МГц подобные «слияния» нот начинаются уже в 1-й октаве.
Поэтому, здесь приходится выбирать, что важнее — чистота извлекаемых нот или отсутствие биений, а во втором случае приходится ограничивать диапазон используемых нот (например, играть с огибающей только басы).
Дополнительные режимы
Для чего зарезервированы ещё 3 значения параметра, задаваемые колесом Modulation? Для получения делителя огибающей попробуем значение делителя частоты тона не только делить на 16, но и дополнительно умножать на некоторые натуральные дроби. Я выбрал значения 3:2, 2:3 и 5:4 как наиболее «благозвучные». Их применение дополнительно изменяет высоту звучания ноты с огибающей. Они соответствуют изменению высоты тона на квинту вниз/вверх и терцию вниз:
При желании можно задать другие значения в массиве констант autoEnv — например, можно руководствоваться соотношениями, приведенными в начале статьи [2]. Можно уменьшить в коде значение делителя ENV_STEP_DIV, чтобы увеличить количество значений параметра, задаваемого колесом. Понятно, что делать их слишком много нельзя, т. к. будет сложно установить и зафиксировать нужное значение параметра колесом.
В начале статьи упоминалось, что в чипе AY возможно задать два вида пилообразной огибающей. Выше рассматривался только режим 8, в котором повторяется спад амплитуды с максимального до минимального значения. В режиме 10 амплитуда изменяется с максимального значения до минимального, затем опять до максимального и т. д. «Пила» в этом режиме имеет частоту в 2 раза меньше, т. е. звучит на октаву ниже, и при этом имеет более «мягкий» звук:
Чтобы включить такой режим, поступим следующим образом: если нота поступает в MIDI-канале от 11 до 16, то вместо режима 8 будет устанавливаться режим огибающей 10. В частности, это позволит использовать разные режимы в одной композиции, играя ноты из разных MIDI-каналов, пусть даже и не одновременно (об этом ниже). Для разнообразия я добавил класс Mod, реализующий различные формы изменения делителя огибающей, аналогичные самим типам огибающих в чипе AY. Если играть ноты в разных MIDI-каналах, будут использоваться разные типы модуляции делителя:
Обратите внимание, что это не формы самого сигнала огибающей, а формы изменения значения делителя огибающей. То есть, если график значения делителя идет снизу вверх (например, в типе модуляции 13), значение делителя увеличивается, а высота звука при этом понижается.
В видео представлены разные режимы модуляции делителя огибающей. Заодно можно послушать, в чем разница между режимами огибающей (типами сигнала) 8 и 10:
Сдвиг фазы
В процессе разработки я заметил эффект, проявляющийся в режиме, когда значение делителя частоты тона округляется до 16, т.е. с включенной огибающей ноты воспроизводятся без биения. Его проще услышать, если много раз подряд извлекать одну и ту же ноту. При этом характер звучания ноты случайным образом изменяется — звук то как бы «ярче», то «глуше» (лучше слушать в наушниках):
Это похоже на изменение фазы при наложении пилообразной огибающей на прямоугольный сигнал основного тона. Сергей Бульба подтвердил мое предположение:
Правильное соотношение частот тона и огибающей гарантирует отсутствие биений по амплитуде, но не гарантирует стабильной начальной разности фаз. В результате и наблюдается с одними и теми же нотами и периодами огибающей, что одна звучит громко, другая тихо, третья почти не слышно.
Некоторая информация по этой теме имеется в статье [3].
Чтобы устранить рассинхронизацию по фазе, можно использовать то, что при каждой записи типа огибающей в регистр R15 происходит сброс счетчика цикла генератора огибающей. К сожалению, нет возможности явно сбросить счетчик цикла генератора тона, кроме как полным сбросом состояния всего чипа AY. Но можно воспользоваться следующим трюком: после установки типа огибающей в R15 установить значение 0 или 1 в регистры R0...R5, задающие делитель тона — это включит максимальную частоту тона, т. е. минимальную длительность периода прямоугольного сигнала. И сразу после этого установить уже нужное значение делителя тона. Это не устраняет случайность попадания в фазу тонового сигнала, но разнообразие звучаний при наложении тона и огибающей сводится всего к двум вариантам вместо многих. В коде я оставил возможность включить или выключить синхронизацию с помощью #define SYNC_ENV.
Утверждается, что полная синхронизация, когда звучит только один вариант сдвига при наложении огибающей, реализована в Fast Tracker для ZX Spectrum, но мне этого добиться пока не удалось. Если кто-нибудь знает правильный алгоритм, как это сделать, прошу написать в комментариях. Некоторая информация об этом имеется в обсуждениях [4].
Все режимы синтезатора
Обобщим результаты, достигнутые в обеих статьях, и составим список всех режимов, которые на данный момент реализованы в синтезаторе. Напомню, что переключение между основными режимами осуществляется кнопкой на плате. При включении устройства активен режим eNoDetune.
Режим |
Описание |
Полифония, голосов |
Переменный резистор |
Колесо Pitch Bend |
Колесо Modulation |
eNoDetune |
Базовый режим без эффектов |
6 |
- |
Отклонение высоты тона +/- полутон |
Плавание высоты тона |
eSaw |
Режим с огибающей |
6 * |
Управление частотой огибающей |
Отклонение высоты тона +/- полутон либо период модуляции частоты огибающей |
Глубина плавания либо 5 режимов (0...4) частоты огибающей |
eDetuneOn |
Доп. голос |
3 |
Отклонение частоты доп. голоса |
Отклонение высоты тона +/- полутон |
Плавание высоты тона |
eDetuneOctUp |
Доп. голос на октаву выше |
3 |
Отклонение частоты доп. голоса |
Отклонение высоты тона +/- полутон |
Плавание высоты тона |
eDetuneOctDown |
Доп. голос на октаву ниже |
3 |
Отклонение частоты доп. голоса |
Отклонение высоты тона +/- полутон |
Плавание высоты тона |
eDetune5 |
Доп. голос +5 полутонов |
3 |
Отклонение частоты доп. голоса |
Отклонение высоты тона +/- полутон |
Плавание высоты тона |
eDetune7 |
Доп. голос +7 полутонов |
3 |
Отклонение частоты доп. голоса |
Отклонение высоты тона +/- полутон |
Плавание высоты тона |
Последние 5 режимов с detune реализованы и описаны в предыдущей статье. Рассмотрим особенности нового режима eSaw:
Формально, в нем полифония 6 голосов (*), но реально имеет смысл играть, скорее всего, какую-то одноголосую мелодию — это может быть бас, сольная партия и т. п. Форма и частота огибающей задается только сразу для всех каналов обоих чипов AY в функции setEnvelope (так реализовано автором исходного проекта). Поэтому, если нажать на MIDI-клавиатуре какую-то ноту, включится огибающая с частотой, соответствующей этой ноте. Если параллельно нажать ещё какую-то ноту, частота огибающей будет пересчитана для второй ноты, и к предыдущей ноте она может не подойти. Можно провести эксперименты и набрать некоторый опыт извлечения аккордов, которые в этом режиме могут звучать приемлемо. Возможно, в будущем имеет смысл в этом режиме ограничить полифонию двумя голосами, и вторую ноту, если она звучит одновременно с уже звучащей первой, воспроизводить на втором чипе, чтобы была возможность независимо задавать частоту огибающей для второй ноты. Но поскольку на выходе они будут смешиваться в один моно-звук, ещё неизвестно, как две ноты с разными огибающими будут звучать вместе.
Если переменный резистор отклонен от нулевого положения, он задает делитель частоты огибающей. В таком случае можно также включить её периодическое «плавание», отклонив колесо Modulation на MIDI-клавиатуре от нулевого положения. Величина отклонения колеса задает глубину «плавания», а отклонение колеса Pitch Bend задает скорость «плавания» (отклонение вниз уменьшает частоту, вверх — увеличивает).
В случае, если ноты в синтезатор поступают по MIDI-каналам 1...9, форма огибающей задается режимом 8 (двоичное 1000). Если же ноты поступают по MIDI-каналам 11...16, форма огибающей задается режимом 10 (двоичное 1010). Кроме того, в зависимости от номера канала, меняется форма модуляции делителя частоты огибающей.
Если переменный резистор находится в нулевом положении, колесо Modulation позволяет задать один из пяти режимов расчета частоты огибающей. В первом (с индексом 0) сохраняется чистота звучания нот, т. к. делитель частоты тона никак не округляется, но на большинстве нот при этом возникают биения с разными частотами. Во втором и последующих режимах делитель частоты тона округляется до 16, в результате ноты вместе с огибающей звучат без биений (но не всегда точно по высоте). В режимах с индексами 2...4 делитель частоты огибающей дополнительно умножается на соотношения 3:2, 2:3 и 5:4, что дополнительно изменяет высоту звучания ноты с огибающей.
Выводы
Отметим основной недостаток данного синтезатора, уже упомянутый выше: параметры огибающей (частота и форма) задаются только одновременно для всех каналов обоих чипов AY (процедура setEnvelope). Так сделано потому, что в исходном проекте автор сделал универсальный алгоритм, автоматически распределяющий воспроизводимые ноты по 6 каналам синтезатора двух чипов, поэтому заранее невозможно знать, на каком из чипов будет играться звук ударного инструмента, если одновременно уже играется сколько-то нот. В отличие от трекерных редакторов, здесь нельзя явно задавать, на каком чипе и в каком канале A/B/C должна играться определенная нота. Звуки ударных также основаны на использовании огибающих, только с однократным изменением громкости (режим 9). Так что здесь нельзя одновременно играть звуки ударных и ноты с использованием огибающих. Чтобы можно было это делать, нужно переделать алгоритм — например, разрешать одну ноту с огибающей играть на первом чипе, а другую одновременно играющую ноту с огибающей — на втором. Возможно, я постараюсь реализовать это в будущем.
Ещё одним направлением развития синтезатора я вижу управление им через MIDI-команды. Прямо сейчас можно из MIDI-секвенсора воспроизводить не только последовательность нот (т. е. мелодию), но и передавать команды Pitch Bend и Modulation, имитируя вращение аналогичных колес на клавиатуре, чтобы изменять звучание соответствующим образом. Также, в зависимости от номера MIDI-канала, можно выбирать характер автоматического изменения огибающей, если переменный резистор находится не в нулевом положении.
В дальнейшем можно расширить набор команд Control Change и передавать в них параметры огибающей: значение делителя частоты либо дополнительные коэффициенты, на которые умножается делитель. Возможно, имеет смысл вообще переделать алгоритм работы — не пытаться автоматически распределять ноты по каналам синтезатора, а жестко связать определенные MIDI-каналы с каналами A/B/C каждого из чипов AY.
Итоговый код синтезатора представлен в репозитории (файл MIDI-DualAY-Detune-Bend-Saw.ino). Если у читателя есть идеи, как можно ещё расширить или улучшить возможности использования огибающей в данном синтезаторе, прошу об этом написать в комментариях.
Ссылки по теме
MIDI chiptune + detune (предыдущая статья)
Частотная таблица с нулевой погрешностью (И.Рощин)
Обсуждения рассинхронизации фазы между тоном и огибающей: 1, 2
Сайт Сергея Бульбы (всё про AY-музыку)