Продолжая тему джазовой гармонии на баяне

По следам предыдущих публикаций.

Сделал сводку по основным «джазовым» аккордам — см. рис. 1. Это для «До». Для остальных нот — всё по аналогии (у баянистов здесь не должно возникать вопросов).

На схеме в «числителе» указаны готовые аккорды, которые надо нажимать, а в «знаменателе» — басы. Нужный джазовый аккорд получается при одновременном нажатии баса и «готовых» аккордов.

Рис. 1

Эта информация будет полезна, например, для игры т.н. «turnaround progression» которая используется в очень многих джазовых произведениях:

Dm7 - G7 - Cmaj7 - A7

Вести с MIDI-полей

С момента публикации предыдущей заметки было сделано весьма много. Но работа велась исключительно над прошивкой, поэтому никаких фотографий или видео пока нет. (Какое-то видео запишу потом, когда всё сделаю; это будет видеопрезентация моего MIDI-баяна.)

А пока докладываю о том, что было сделано за всё это время:

  • В библиотеку работы со светодиодами я добавил режим медленного мигания светодиода. Пригодится для того, чтобы показывать, что данные текущего пресета (настроек тембров и прочих звуковых параметров) были изменены, но еще не сохранены в EEPROM.
  • Добавил глобальную настройку «Общая громкость» — чтобы можно было вручную настраивать громкость одновременно для всех активных MIDI-каналов.
  • Добавлено обновление значений громкости при включении/выключении датчика давления (значение общей громкости берется либо из датчика давления, либо из глобальной настройки соответственно).
  • Сделано обновление значений громкости в каналах левой руки при переключении режима готовой/выборной клавиатуры.
  • Сделано сохранение и восстановление глобальных настроек и пресетов в/из EEPROM (сохранение пресетов производится длительным нажатием кнопки соответствующего пресета, а глобальные настройки всегда автоматически сохраняются спустя 8 секунд с момента последнего изменения). В результате при включении баян приходит в то же состоянии, в каком он находился в момент выключения. Мне очень нравится, как всё получилось.
  • Сделал функцию отправки всех настроек в синтезатор. Настройки отправляются либо при смене пресета, либо по нажатию спец-кнопки на панели управления.
  • Добавил удобное переключения банков инструментов для синтезатора ATEMP. Для некоторых инструментов могут быть, а могут и не быть дополнительные варианты звучания; эти дополнительные варианты расположены в дополнительных т.н. «банках». В каких именно — описано в документации на синтезатор. Я сделал так, чтобы банки инструментов можно было менять только на те, в которых есть доп. инструменты; т.е. если для какого-то инструмента есть всего 3 варианта звучания, то для такого инструмента можно выбрать только один из этих 3-х вариантов (вместо того, чтобы перебирать все 127 возможных значений). Это на порядки упрощает поиск нужного тембра.
  • Сделан экран редактирования глобальных настроек (общая громкость, режим «running sttatus» и проч.)
  • Небольшой рефакторинг старого кода.

И в данный момент а баяне уже можно довольно комфортно играть, не тратя кучу времени на настройку звуков при каждом включении.

В исходниках уже 3910 строк; продолжаю работу над прошивкой.

Рис. 1

Мысли про скорость

Пока я нахожусь вдали от компьютера, приходят в голову разные мысли. Одна из них — это вопрос: способен ли будет мой баян перегрузить MIDI out порт командами в том случае, когда будут меняться настройки всех трёх активных midi каналов при нажатии кнопки «смена пресета». Для каждого канала надо будет установить примерно 15 параметров. Один параметр — это одна midi команда из 3-х байтов. Т.е. при смене пресета нужно будет отправить порядка 50 байт, и мне было интересно, сколько времени это может занять; будет ли эта задержка заметна во время игры.

А тут выдалось время на то, чтобы посчитать.

Скорость обмена данными у MIDI интерфейса — 31250 бод. То есть порядка 2900 байт в секунду. То есть порядка 1000 midi-команд в секунду. То есть мои несчастные 15 команд пролетят за 15 миллисекунд.

То есть можно вообще на этот счёт ни о чём не беспокоиться. Можно будет «щёлкать регистрами» во время игры сколько угодно. Главное, чтобы синтезатор был в состоянии обрабатывать команды с такой скоростью.

Очень хочется добраться до компа и продолжить доводить прошивку баяна до рабочего состояния, реализуя все задуманные функции.

Рис. 1 (КДПВ)

Баян впервые собран

В эти выходные я закончил настройку датчиков левой клавиатуры, начисто прикрутил плату датчиков, просверлил в деке отверстие, вывел жгут проводов от левой клавиатуры в герметичный объём, и подключил левую клавиатуру к общей схеме.

Можно играть обеими руками.

По механике — осталось герметизировать все отверстия и придумать, как крепить жгут проводов, идущий из одного полукорпуса в другой.

Черновая проверка датчика давления показала, что отработка нештатных ситуаций прошивкой работает отлично. 🙂 При одновременной работе датчика давления и большого количества музыкальных клавиш переполнялась внутренняя очередь MIDI-сообщений и прошивка вылетала в индикацию внутренней ошибки (баян перестаёт реагировать на внешние раздражители и высвечивает на светодиодах код ошибки). Видимо, очередь на 4 MIDI-сообщения была слишком маленькой; увеличил до 7. И слегка поменял внутреннюю логику разгребания этой очереди и отправки команд в синтезатор через MIDI-out порт.

Сейчас всё работает чётенько. 🙂

Но с прошивкой ещё возиться и возиться. Это для отладки электроники и механики там сейчас есть всё, что надо. А для игры не хватает ещё много чего. Продолжу работу над баяном после 15 августа. А пока другие дела.

Рис. 1

Режим готовых аккордов настроен

В предыдущей заметке я высказывал опасение, что после настройки аккордов перестанет работать выборный режим для этих же нот. Так и получилось. 🙁

Я сегодня потратил часа 2, чтобы настроить датчики для режима аккордов. И когда я переключил на выборку, то для 9 нот из 12 была сбита настройка выборки: магниты пролетали мимо датчиков, и в крайнем положении рычагов ноты не звучали.

Это потому, что, как оказалось, в разных случаях при нажатии клавиш рычаги опускаются на разную глубину:

  • ниже всего рычаги опускаются в режиме выборки;
  • при нажатии мажорных аккордов рычаги останавливаются чуть выше;
  • при нажатии минорных аккордов рычаги останавливаются чуть выше, чем для мажорных;
  • а при нажатии септаккордов и уменьшенных септаккордов — они вообще опускаются чуть-чуть.

Получается, что датчик должен ловить магнит в довольно большом диапазоне расстояний. Одиночный магнит с такой задачей не справляется, и пришлось наклеить ещё по одному магниту на каждый «аккордовый» рычаг (см. рис. 1). И только после этого начали как надо работать и аккорды, и выборка.

Сегодня я с этим возился аж до полуночи. Завтра на свежую голову тщательно проверю ещё раз все датчики и буду прикручивать плату датчиков в рабочем положении. После этого просверлю отверстие в левой деке и, пожалуй, продену в него провод. (Операция эта условно-необратимая, потому что потом я припаяю к концу провода разъём, и вытащить провод обратно уже будет нельзя.

Если повезёт, то завтра я смогу сыграть на баяне обеими руками, но без меха (потому что внутренний объём пока будет негерметичен). Герметизировать всё буду уже потом, завтра не успею.

Рис. 1

Настроены датчики левой клавиатуры

Почти настроены. Выборный режим полностью работает (см. видео ниже).

Несмотря на то, что в выборном режиме все датчики работают нормально, нужно будет дополнительно настраивать 12 датчиков, которые работают в режиме готовых аккордов. Дело в том, что в выборном режиме рычаги нажимаются «глубже», т.е. опускаются ниже на плату. А в режиме готовых аккордов те же рычаги останавливаются раньше. И часть датчиков (бОльшая часть) не срабатывает. Их надо будет поднимать над платой, и ловить промежуточное положение, чтобы они звучали и в крайнем положении рычага для выборного режима, и в крайнем положении для готового. Очень надеюсь, что такое положение поймать удастся. Иначе придётся на эти 12 рычагов переклеивать магниты, чтобы они были двойные.

Внешний вид настроенных датчиков — на рис. 1. Видно, что никакой одинаковостью тут не пахнет и близко. Каждый датчик надо подгибать персонально, много раз вынимая плату для работы с датчиками и засовывая обратно для проверки.

Рис. 1

Занимаюсь настройкой положения датчиков левой клавиатуры

Процесс весьма трудоёмкий. Места между рычагами крайне мало, все подгибания ножек датчиков надо делать так, чтобы оставаться в одной плоскости.

Дело, можно сказать, вообще не двигалось, пока я не нашёл способ фиксировать плату в одном и том же положении, не прикручивая её шурупами. (Прикручивать буду в самом конце, когда буду уверен, что все датчики работают как надо и дальнейшая коррекция положения платы уже точно не нужна). Положение «ближе-дальше» определяется шириной картонной проставки, временно (а может и не временно) приклеенной перед платой, между бортиком и платой (см. рис. 1). А положение «вправо-влево» определяется «приметами на местности»; в данном случае — центр крепёжного отверстия платы должен точно совпадать с краем проставки.

Эта проставка на рис. 1 — уже третья, шириной 5 мм. Предыдущие 2 (7 мм и 6 мм) оказались слишком широкими, не позволяющими правильно выставить дальний ряд датчиков. В текущем положении далчики ближнего ряда слишком высовываются вперёд, но эту проблему можно решить подгибкой ножек.

Процесс очень небыстрый. Вчера за 2 часа я настроил только половину басов. Т.е. 12 датчиков дальнего ряда. 12 — потому что в этом баяне басы играют октавами, т.е., например, одновременно с самым левым рычагом на рис. 1 (Фа контр октавы) нажимается и 13-й рычаг (Фа большой октавы).

Эта особенность механизма, кстати, является проблемой для MIDI инструмента. Далеко не каждый вариант басового звука будет хорошо звучать октавами. Поэтому в прошивке буду делать отключаемый режим программного запрета на октавы в басах; т.е. если нижняя басовая нота уже звучит, то контроллер не будет отправлять в синтезатор команду включения верхней басовой ноты даже если соответствующий датчик сработал. (Придётся израсходовать 2 байта ОЗУ на запоминание текущего состояния 12-ти нижних басовых нот.)

Рис. 1

Полбаяна уже умеет играть

В прошедшие выходные я окончательно смонтировал и распаял платы правого полукорпуса.

Декоративную решетку пока прикрутить не получится, т.к. она будет упираться в плату датчиков 1-го и 2-го рядов (см. рис. 1). Как я ни старался опустить её как можно ниже, всё равно пришлось её поднять над рычагами так, что она в результате не помещается в штатные габариты этой части баяна. Положение платы выверено очень точно; если её опустить вниз хотя бы на 0.5 миллиметра, то некоторые рычаги клапанов среднего ряда по ней уже будут стучать, что недопустимо. Ну вот такой у меня баян, ничего с этим не поделать.

Придётся декоративную решётку тоже поднимать миллиметра на 2-3. Буду изобретать какие-нибудь прокладки между решеткой и корпусом баяна.

А пока решетки нет — плату панели управления пришлось прилепить на внешнюю поверхность корпуса. С этим можно будет прожить до тех пор, когда будет прикручена решетка. Потом прилеплю на саму решётку. (Клею на 2-сторонний скотч, естественно.)

Рис. 1

Вот так выглядит правый полукорпус изнутри:

Рис. 2

Герметизировать отверстие для проводов я пока не стал; на тот случай, если вдруг придётся зачем-нибудь всё разбирать.

Но разъёмы (MIDI-out и питание) я уже герметизировал. Обернул каптоновым скотчем — получилась как-бы опалубка, которую я залил термоклеем. В результате все щели в разъёмах перекрыты. При самой финальной сборке останется дополнительно герметизировать отверстие MIDI разъёма, и всё. Отверстие разъёма питания герметизировать е буду, т.к. мне кажется, что этот разъём и так уже достаточно плотно сидит в корпусе на резьбе.

Думал я, что вчера и сегодня буду работать над левым полукорпусом. Но не получилось. Оба вечера я играл на правой половинке: разучиваю «The Cat» Джимми Смита. Очень,очень затягивающее занятие. Но надо будет хотя бы завтра вечером начать работу над левой клавиатурой.

А что там вообще случилось-то?

Несколько раз до меня донеслось, что на этой неделе что-то там стряслось с windows, и что кошмар-кошмар и всё пропало.

Судя по выдаче Яндекса — реально что-то произошло. (См. рис. 1.)

Надо будет почитать про всё это. А то мне с Линукса плохо видно детали этого происшествия.

Рис. 1

(не)переполнение millis()

Как, наверное, любой начинающий ардуинщик, когда я узнал о том, как можно реализовать всякое с использованием функции millis(), возвращающей 32-разрядное беззнаковое целое число миллисекунд, прошедших с момента запуска микроконтроллера, я задался вопросом: «а что будет, когда счётчик миллисекунд переполнится?»

Функция millis() для ардуинщика — это альфа и омега всей «многозадачности» ардуино. Если что-то нужно регулярно выполнять через заданный интервал времени, то обычно пишут какой-то такой код:

uint32_t  my_timer;    // где-то объявляем переменную "таймер"
#define MY_PERIOD 1000 // "раз в секунду"

...

my_timer = millis();    // "запускаем" таймер, запоминая в
                        // переменной текущее значение millis()

...

// и где-то в основном рабочем цикле пишем примерно такое:
if ( millis() - my_timer > MY_PERIOD ){
    // Заданное время ожидания прошло
    // делаем здесь, что хотели

    my_timer = millis(); // снова взводим таймер для следующего
                         // срабатывания
}

Так вот, предположим, что в «таймере» мы сохранили число 0xFFFFFFFE. Естественно, 32-разрядный счётчик переполняется через 1 миллисекунду и millis() начинает возвращать 0, 1, 2, и т.д.

И куча новичков-ардуинщиков в интернете пугается, когда понимает, что при таком раскладе «всё пропало», потому что в этом случае при проверке срабатывания таймера из 1 будет вычитаться 0xFFFFFFFE и, вроде бы, так нельзя. Поиск по клчевым словам «переполнение millis()» выдаёт кучу ссылок на страницы, где «знатоки» со знанием делом говорят «не ссыте, всё будет нормально, потому что вычитание беззнаковое», не объясняя при этом ничего.

А я объясню, мне не жалко. 🙂

Для того, чтобы убедиться, что «так можно», нужно всего лишь понимать, как в процессорах общего назначения происходит вычитание целых чисел. А происходит оно через сложение.

В этом месте, может, стоило бы рассказать про комбинационную схему «сумматор» и объяснить, как она работает. Для того, чтобы на низком уровне показать, как же, собственно, компьютер складывает числа. И чтобы стало понятно, что вычитать (подобно тому, как это делает человек) процессор не умеет. Он умеет только складывать. Но про (полный) сумматор читайте в другом месте. А здесь и сейчас достаточно понять, что кто-то очень умный придумал, как можно вычитать одно число из другого, имея в качестве инструмента комбинационную схему, которая умеет только складывать (суммировать).

Он придумал представлять отрицательные числа в т.н. «дополнительных кодах«. То есть, если нам надо вычесть из положительного числа А положительное число Б, нужно преобразовать число Б в дополнительный код (получив тем самым число -Б), и после этого спокойно складывать числа «А» и «-Б». Здесь важно то, что беззнаковые числа тоже преобразуются в дополнительный код при вычитании.

Т.е. если наше А = 3 и Б = 1 (00000011 и 00000001 в двоичном представлении), то при вычитании Б из А будут складываться числа 00000011 и 11111111. В результате такого сложения мы получим 00000010, а точнее (1)00000010, где единица, образующаяся при сложении старших разрядов улетает в переполнение (за пределы нашего 8-разрядного целого). То есть, при сложении числа 3 и представленного в дополнительном коде числа -1 мы получили число 2.

В случае с millis(), нам надо было из беззнакового числа 1 вычесть беззнаковое число 0xFFFFFFFE (т.е. двоичное 1111111111111110). Ну, хорошо, преобразуем вычитаемое в дополнительный код и получаем двоичное 0000000000000010. В результате мы складываем 1 и двоичное 10, получая двоичное 11, т.е 3. Т.е. результатом операции «1 — 0xFFFFFFFE» будет 3. И это именно то, что мы рассчитывали получить, проверяя таймер:

1111111111111110 <-- здесь "засекли" таймер
1111111111111111
0000000000000000
0000000000000001 <-- а здесь "1 - 0xFFFFFFFE = 3"

Т.е. всё, что надо, успешно вычитается, и получается именно то, что надо. Вот такая магия.

P.S. Только что осознал, что двоичное представление 32-разрядных чисел у меня получилось 16-разрядным :). Но не буду исправлять, т.к. будет хуже читаться. А на суть явления размерность чисел не влияет. Можно было хоть на 3-разрядных числах это показывать.