как правильно принимать MIDI CLOCK сигнал?
- Войдите на сайт для отправки комментариев
Hello world! Делаю девайс на основе Arduino Mega 2560 для синхронизации нескольких синтезаторов, и встал вопрос приема и обработки сигнала Midi Clock. Для синхронизации используется байт 0xf8 который отправляется 24 раза за интервал между ударами (тактами). Для теста написал такой скетч:
int clk = 0; unsigned long int lastinHigh=0; unsigned long int inHigh=0; int duration=0; void setup() { Serial1.begin(31250); // скорость миди интерфейса Serial.begin(9600); } void loop() { if((Serial1.available() > 0)&&(Serial1.read()==0xf8)) { Sync(); //дополнительная функция используется что б потом встроить в другой скетч } } void Sync() { clk++; if (clk == 24) { inHigh=millis(); duration=inHigh-lastinHigh; lastinHigh=inHigh; clk=0; Serial.println(duration); } }
Подключил ардуину к korg kaoss pad, выставил в нем темп 120 bpm , и в идеале сериал монитор должен был печатать мне ровные интервалы по 500 миллисекунд. Однако выходит следущее:
500 500 501 500 500 500 501 499 501 501 500 500 500 501 499 501 500
У меня несколько предположений что происходит:
1) Чудит Korg
2) Туплю я
3) Нужно использовать прерывание
4) Что-то еще происходит пока неведомое мне
Подскажите пожалуйста, куда гуглить, с учетом того, что дальше это будет встравиваться в другой код, будет вывод миди клок сигнала через другие порты, подключены экраны, кнопки и прочее.
А почему Вы решили, что кто-то "должен печатеть ровные интервалы"?
Собственно, это - основной вопрос. Попытайтесь на него ответить.
Но я еще допрлню одно замечание, которое имеет место быть, хотя и не относится к основному вопросу: millis в Ардуино "тикает" не совсем регулярно.
С лирической точки зрения я надеялся на ровные интервалы, потому что темп он на то и темп что б быть равномерным, и для того что б не париться по поводу его ровности я и делегировал эти полномочия вычислительной технике. А до вашего комментария я и не подозревал, что компьютеры могут меня обманывать. Ухом эту разницу в миллисекунду между ударами понятно дело не слышно.
С технической же стороны присутствует слишком много вводных, не все из которых я могу проверить. Например я действительно не знаю периодично ли тикает Корг, к тому же у него всем известные проблемы с чувством ритма и с синхронизацией в целом. Еще я слишком зеленый, что бы знать, что mills не может посчитать миллисекунды, хотя и подозревал что-то подобное. Поэтому изменил функцию sync следующим образом:
Пин обозначеный CV1 подключен к другому синтезатору, и при установлении на нем высокого уровня звучит одиночный удар барабана. То есть формируется синхросигнал на CV1. Запустил на Корге ритм, синхронизация пошла, лампочка мигает и все работает ровно без расхождения, удар в удар. В принципе можно обойтись без mills для синхронизации в данной ситуации. Но что делать если стоит обратная задача? Вот синтезатор дает мне синхросигнал и я должен преобразовать его в MIDI clock - тогда все равно надо брать временные интервалы и делить их на 24. А некоторые синты еще и синхросигнал могуть дать раз в такт, тогда надо делить на 96, и если синхросигнал плавает (а я не могу понять пока что плавает ли он или это я считаю его в ардуине неправильно) тогда уже будет слышно ухом.
Сдается мне, что Serial - это не тот объект, который годится для гарантированной синхронизации по времени в высоком темпе. Накрутите побольше логики и все снова поплывет.
Об ошибках округления было известно задолго до появления первых образцов вычислительной техники, но, почему-то, никто раньше не называл их обманом. Просто всегда считалось, что тот, кто использует вычислительную технику, должен знать, как ее правильно применять.
Что касается Корга, то из общих соображений могу заключить, что тикает он непериодично - просто потому, что "нет в Мире совершенства", а потому вообще ни что не может тикать строго периодично.
Но т.к. человеческое чувство ритма тоже не отличается идеальностью, ситуация не выглядит совершенно безнадежной: достаточно лишь обеспечить, чтобы погрешности округления:
- были ниже порога чувствительности человеческого слуха,
- не приводили к накапливанию ошибки.
Кстати, мне вот не попадались оценки (впрочем, я специально и не искал) чувствительности человеческого слуха к неравномерности ритма и задержке сигнала. Так что, если поделитесь подобной информацией, буду благодарен.
Не уверен, что овчинка стоит выделки, но, если бы мне потребовалось использовать точные отсчеты времени, я бы немного перепрограммировал Ардуину, чтобы добиться равномерности отсчетов хотя бы по ее внутренним часам. Благо, это не так сложно.
Но, опять же, любая техническая задача имеет, минимум, несколько решений. Чтобы выбрать из них наиболее подходящее, нужно достаточно хорошо представлять саму задачу.
Сдается мне, что Serial - это не тот объект, который годится для гарантированной синхронизации по времени в высоком темпе. Накрутите побольше логики и все снова поплывет.
Тем не менее, MIDI основан именно на передаче данных по последовательному порту. Да еще и на сравнительно невысокой скорости.
Желаемая точность должна быть основана на характеристиках акустического анализатора человека (т.е. уха). Если она находится в допустимых пределах - все нормально.
Согласен с предыдущими авторами. Передача по серийному порту - это куча операций, да и скорость 9600 не внушает доверия.
Скопите данные времени в массив размером 100 или 200, а потом выдайте его на порт. Уверен - разброса не будет.
Сдается мне, что Serial - это не тот объект, который годится для гарантированной синхронизации по времени в высоком темпе. Накрутите побольше логики и все снова поплывет.
Тем не менее, MIDI основан именно на передаче данных по последовательному порту. Да еще и на сравнительно невысокой скорости.
Я отметил это с учётом уровня среднестатистического местного "кодо-копипастера", который будет делеи совать в середину скетча. Так-то понятно, что можно даже подвеситься на прерывание USART и слушать его, ничего не пропуская, в принципе.
А про MIDI мне спорить не с руки - я только на 286-х какие-то .mid слушал и всё ))
Не уверен, что овчинка стоит выделки, но, если бы мне потребовалось использовать точные отсчеты времени, я бы немного перепрограммировал Ардуину, чтобы добиться равномерности отсчетов хотя бы по ее внутренним часам. Благо, это не так сложно.
Это мне в сторону таймеров копать?
Это мне в сторону таймеров копать?
Пока задача не сформулирована, куда копать - не ясно.
Но, вполне вероятно, в сторону таймеров.
Собственно, разброс результатов в пределах одной единицы младшего разряда - это хороший результат. Поэтому для меня вообще не очевидно, что следует куда-то копать.
Если Вас смущает непериодичность millis(), то, вероятно, лучше всего написать свой аналог: настройка Ардуино по умолчанию - некоторый баланс компромиссов. Если например, время нужно получать точнее, а ШИМ не нужна, то вполне логично изменить настройки таймеров и соответственно переписать millis() и micros().
Если Вам нужно не только читать внешний сигнал, но и формировать внутренний, тут тоже наверняка понадобятся таймеры. Если нужен импульс на выходе (а код F8 - это как раз одиночный импульс определенной длительности), то можно запрограммировать таймер на аппаратную выдачу такого импульса.
Если нужно предпринять какие-то действия (например, послать ноту в MIDI-порт), то настраиваются прерывания по таймеру.
В последнем случае millis() и micros() вообще не нужны, а вся работа с временем происходит во внутренних единицах, задаваемыхъ таймером. Например, в MIDI Timing Clock. Или даже в его долях.
PS. Да, сейчас перечитал начало первого сообщения. У Вас такт длительностью в четверть?
Я пытаюсь понять где косяк. Сейчас запустил корг отдельно, импульсы с ардуино отдельно запускают бочку на другом синте. За формирование импульсов CV1 и заодно миди сигнала (в данном случае не использовался) отвечает кусок кода:
Ну и естественно при одинаковых выставленых значениях bpm все разбегается довольно быстро. То есть получается что сейчас эти cv импульсы формируются с неправильным интервалом, и bpm формируемый адрдуиной не соответствует реальному. Вставил этот же код в отдельный чистый скетч - все работает синхронно. Наверное точности micros() хватит, а что б выдача сигналов не вспотыкалась об остальной код буду пытаться перевести все это дело на прерывание что ли по таймеру. Хотя не уверен что это верный путь запихивать туда Serial
Кстати только что прочитал на этом сайте -
Вот так вот.
Если нужен импульс на выходе (а код F8 - это как раз одиночный импульс определенной длительности), то можно запрограммировать таймер на аппаратную выдачу такого импульса.
Одиночный потому что 11111000 ? И считаться его длительность будет исходя из скорости передачи по serial для midi?
PS. Да, сейчас перечитал начало первого сообщения. У Вас такт длительностью в четверть?
Да.
Я пытаюсь понять где косяк. Сейчас запустил корг отдельно, импульсы с ардуино отдельно запускают бочку на другом синте. За формирование импульсов CV1 и заодно миди сигнала (в данном случае не использовался) отвечает кусок кода:
Ну и естественно при одинаковых выставленых значениях bpm все разбегается довольно быстро.
К сожалению, в приведенном фрагменте Вы упустили важную информацию - тип переменных bps и mps. Подозреваю, что они целочисленные - отсюда и косяк.
PS. А вообще, я что-то сомневаюсь в возможности синхронизации "раз и навсегда". Всегда есть ошибки измерения, ошибки округления, неточность задания частоты, а также нестабильность последней. В общем, рано или поздно, все это обязатенльно разойдется. Весь вопрос лишь в том, что произойдет раньше: эта рассинхронизация будет заметна на слух или исчезнет необходимость в синхронизации (например, будет отыграна последняя нота в композиции).
Да, сорян - эти переменные float
В данном случае с четкостью синхронизации проблем не будет, просто хотелось бы что б номинальные 120 bpm не становились 114bpm как у меня сейчас. И все это из-за остального кода, как я уже выяснил. Теперь вот решаю что с этим делать. Но это уже не имеет отношения к первоначальной теме.
Думаю, (повторюсь) за одну операцию проблему синхронизации не решить.
Для начала я бы измерил длительность между командами синхронизации. Минимум - два интервала (три команды). Если два измерения дали достаточно близкий результат, - хорошо, если нет - минимум третье измерение и - медиана.
Настроить таймер на выдачу прерываний с нужной частотой (например, в 24 или 96 раз выше - как нужно).
Дальше отслеживать рассинхронизацию и периодически корректировать таймер.
PS. У меня сейчас Мега, работающая в качестве MIDI-контроллера, имеет тактовую частоту примерно на 0.6-0.7% ниже номинала. Т.е. 437 Гц вместо 440 и порт пришлось настроить на 31500 вместо 31250, иначе время от времени появлялись ошибки. Так что надеяться, что все числа будут именно такими, как хотелось бы, не следует. Наоборот, рассчитывать, что "все очень плохо" и в общем случае частоты и фазы внутренних часов различных устройств могут находиться в произвольном соотношении. Я, например, не стал менять Мегу на другой экземпляр, хотя в загашнике лежит несколько штук в запасе. Нужно писать код так, чтобы работало в самом худшем случае. Поэтому то, что на одном 120, на другом запросто может быть 114 и к этому надо быть готовым заранее.
Я не пояснял ранее: у меня в моей синхрокоробочке будет реализована менюшка с выводом на lcd В ней три пункта -
1) синхронизация по выставленному с помощью энкодера bpm
2) tap tempo, то есть бпм задается не энкодером а настукиванием по кнопке
3) синхронизация от внешнего миди сигнала.
Во всех случаях должен происходить вывод миди клок сигнала и так называемого cv сигнала, ну или клик трека.
Так вот очень сильные тормоза с вычислением bpm в первых двух пунктах происходили именно из-за обновления lcd экрана. Немного переписал, проблема пропала.
За синхронизацию по миди отвечает примерно такой код (написал отдельный скетч для проверки):
И в принципе он работает вообще без времени. Единственный прикол - на некоторых значениях bpm все равно расплываются ритмы генерируемые ардуинкой и коргом, то тут я подозреваю что это именно корг не умеет ритмы четко делать, это все-таки его не основная функция.
Даже не знаю что еще написать, наверное надо тестировать на других синтезаторах.
Я не пояснял ранее: у меня в моей синхрокоробочке будет реализована менюшка с выводом на lcd В ней три пункта -
Если всего три пункта, LCD не нужен - достаточно трех светодиодов.
2) tap tempo, то есть бпм задается не энкодером а настукиванием по кнопке
Вряд ли от руки можно настучать достаточно стабильно, чтобы после деления на 96 все сходилось точка в точку.
Во всех случаях должен происходить вывод миди клок сигнала и так называемого cv сигнала, ну или клик трека.
Насколько я понял, сигнала gate?
Так вот очень сильные тормоза с вычислением bpm в первых двух пунктах происходили именно из-за обновления lcd экрана.
Не следует смешивать вычисления и работу с LCD. По поводу последнего - специально создавал тему: http://arduino.ru/forum/apparatnye-voprosy/medlennaya-rabota-liquidcrystali2c
Хотя и вычисления с плавающей точкой тоже вещь не быстрая.
Даже не знаю что еще написать, наверное надо тестировать на других синтезаторах.
Можно тестировать и на Ардуино. Ну, например, подвести статистику по интервалам между посылками MIDI в тех режимах, где замечены проблемы.
Вообще, написание кода - довольно трудоемкое занятие. По моему опыту, на каждую строчку, которая войдет в финальный код, нужно не менее 10 строк всевозможных программ подготовки данных, проверки, подведения стстистики и т.п., которые в окончателный код не войдут.
за Serial спасибо, поправлю. От LCD не откажусь - на него выводится значение bpm. Сигнал gate, да. На Pocket Operator они называют его click-track.
По поводу отладки - у меня есть подозрение, что корг внутри себя бит не держит. void offtop() { Ну то есть конкретно звуки ударов воспроизводит не в соответствии с значением bpm, которое верно передается через midi посредством clock сигнала. У него точно есть такие проблемы с семплами, пришлось облазить кучу забугорных форумов что б понять что я не верблюд. Можно было бы конечно проверить например записывая одновременно аудиодорожку с его ритмом и с тиками миди-клока, ну, как-то аппаратно тики вывести на микшер. Но чо-то лень заморачиваться. Впереди еще куча идей - надо сделать темп-корректор для синтезаторов без какого-либо интерфеса вообще. думаю на основе attiny13 попробовать }
От LCD не откажусь - на него выводится значение bpm.
Ну так я тоже не отказывался. Просто вывод на дисплей происходит посимвольно. Оптимизированной процедурой. В результате задержка не более 0.2 мс.
По поводу отладки - у меня есть подозрение, что корг внутри себя бит не держит.
Ну так как раз это достаточно легко проверить при помощи Ардуино.