Как да започнете да пишете свой собствен фърмуер за Mutable Instruments Clouds

През последните няколко месеца изучавах основите на DSP програмирането, използвайки модул за синтезатор на Mutable Instruments Clouds. Беше много забавно и исках да споделя какво научих.

Моят аматьорски опит в кодирането беше предимно на Arduino, използвайки тяхната много приятелска среда за програмиране, така че работата в C++ и използването на инструменти за команден ред бяха нови за мен.

Всичко, което знам по тази тема, е в тази публикация, така че не мога да ви помогна освен това, което е написано тук. Прочетете отказите от отговорност на страницата Mutable Instruments Clouds Open Source:

„След този момент приемаме, че знаете какво правите и не носим отговорност за каквато и да е повреда на вашия модул!“

Какво ви трябва, за да започнете: Хардуер

  • Модул „Mutable Instruments Clouds“ (купих моя от „Post Modular“)
  • Компютър. Тези инструкции предполагат, че сте на Mac, но можете също да използвате компютър или Linux машина.
  • Програмист. Използвам ST-Link V2, който струва около £60. Émilie Gillet използва Olimex ARM-USB-OCD-H JTAG, който е приблизително на същата цена. Има и по-евтини начини да го направите – можете да закупите ST-Link V2 с различна марка за £7,99 от Amazon или някои платки за разработка имат вграден хардуер. Тези по-евтини инструменти може да работят, не знам. Исках едно нещо по-малко да се обърка, затова купих истинското нещо.
  • Вие също се нуждаете от този Olimex JTAG адаптер, за да поставите малките щифтове на гърба на модулите на Mutable Instruments. Струва около £8.

PS: Можете да започнете без да купувате програмист. Инструментите, които ще използваме, могат да създадат .wav файлове за метода за актуализиране на аудио фърмуера, описан в Ръководството за облаци. Въпреки това, целият процес - създаване на .wav, качването му в модула - отнема 3-4 минути, което бих намерил за доста досадно, докато се опитвам да пиша и тествам код.

PPS: Ако просто искате да научите за DSP кодирането, има много други по-евтини платформи, включително Teensy, Axoloti или Bela.

Първи стъпки: Инсталиране на вашата среда

„Променливата среда за разработка“ е създадена от Émilie Gillet като начин да помогне на хората да напишат фърмуер за променливи модули.

Ето как работи: Използвайки безплатни инструменти, наречени VirtualBox и Vagrant, вие създавате виртуална машина във вашия компютър, работеща под Linux. Цялата тежка работа - компилирането на фърмуера, качването му във вашия модул - се извършва от Linux машината. Вие контролирате машината чрез командния ред, използвайки терминал.

Това е чудесен начин за всеки, който иска да напише фърмуер за модул Mutable Instruments, за достъп до копие на настройката на Émilie Gillet, без да се налага да инсталира и конфигурира десетки инструменти.

Една директория (в този случай копие на цялото фърмуерно хранилище на Mutable Instruments Eurorack в GitHub) се споделя между виртуалната машина и вашата нормална OSX система – така че можете да редактирате файлове в тази папка и виртуалната машина може да ги вижда и използва за компилирайте фърмуера.

Системата вече е много добре документирана и работи добре за мен, пълен ноуб за Linux, командни редове или настройка на среда — моля, прочетете тези инструкции и тези на страницата GitHub за околната среда.

  1. Изтеглете файловете на Mutable Environment Следвайте тази връзка към страницата GitHub и щракнете върху зеления бутон „Клониране или изтегляне“. Изберете „Изтегляне на ZIP“. Отворете zip файла и поставете папката на сигурно място. Създадох папка „MutableEnvironment“ в Documents, като папката „mutable-dev-environment-master“ не беше променена в нея.
  2. Инсталирайте софтуера Следвайте връзките в страницата на GitHub за среда за променлива разработка за VirtualBox, VirtualBox Extension Pack и Vagrant. Открих, че и трите са инсталирани нормално и не трябваше да използвам командния ред за Extension Pack.
  3. Вземете текстов редактор Използвам Text Wrangler, който е безплатен и работи добре, както и много други.
  4. Отворете папката, в която сте оставили файловете в терминалаАко сте толкова невежи, колкото бях аз с терминала, ето как да го направите:
    1. Стартирайте терминала.
    2. Подредете прозорец за търсене, така че да можете да видите папката „mutable-dev-environment-master“.
    3. В терминала напишете “cd” (промяна на директорията) и натиснете интервала.
    4. Плъзнете папката “mutable-dev-environment-master” в прозореца на терминала.
    5. Натиснете return в прозореца на терминала и подканата ще покаже, че вече сте в правилната директория.
    6. Въведете „ls“ и натиснете return, за да видите списък с файловете, само за да сте сигурни.
  5. Стартиране на средата Всички подробности са в документацията на GitHub, но е много просто: въвеждате „vagrant up“ и системата отива, за да изгради виртуалната машина — отнема 15–30 минути първия път . Не ставайте нетърпеливи и се опитайте да го отмените. След това въвеждате „vagrant ssh“, за да влезете във виртуалната машина. Сега, ако въведете „ls“, ще видите списък с всички файлове в GitHub за променливи инструменти; плитки, клони и др.
  6. Проверете споделената папкаАко се върнете към Finder и погледнете в mutable-dev-environment-master, ще намерите папка, наречена „eurorack-modules“. Това е споделената папка. Редактирайте файловете тук, компилирайте и ги качете през виртуалната машина.
  7. ИзпробвайтеКакто е обяснено в документацията, ако напишете „make -f clouds/bootloader/makefile hex“ и натиснете return, системата трябва да изчезне и да компилира програмата за зареждане за Clouds. Файловете, които системата създава, се появяват в папка, наречена „build“ в „„eurorack-modules“
  8. Ами ако не работи? Е, не мога да ви помогна там, освен да се върна и да прочета отново документацията на GitHub. Можете да опитате да търсите във форума на Mutable Instruments с всякакви кодове за грешки или да поискате да се присъедините към Facebook групи като „Euro SMD DIY Noobs“.

Качване на код в модула Clouds

Следващата стъпка е свързването към модула.

Използвам ST-Link V2. Драйверите са включени в средата, но преди да качите нещо, трябва да кажете на системата кой програмист използвате, като следвате тези инструкции за персонализиране. Всеки път, когато стартирам системата, трябва да напиша:

export PGM_INTERFACE=stlink-v2
export PGM_INTERFACE_TYPE=hla

За да проверите дали системата може да види вашия програмист, въведете „lsusb“, което ще покаже всички свързани USB устройства.

Свързване към модула

  1. Clouds се нуждае от захранване от вашия модулен корпуси STLink USB връзката едновременно, за да получи фърмуер. Моят е инсталиран в кутията, като STLink вися отзад и USB кабел излиза в компютъра.
  2. Кабелът с малкия адаптер Red Olimex не е ясно обозначен. За мен щифтът, означен с 1, изглежда е свързан с щифта, означен с JTAG на модула Clouds. Опитах го и в двете посоки и нищо не се взриви (но не работи в обратната посока).

Можете да компилирате кода и да го качите с тази команда:

make -f clouds/makefile upload

Въпреки че открих, че това не работи - даде грешка - така че трябва да използвам това: (Благодарение на съвет от форума от bennelong.bicyclist)

make -f clouds/makefile upload_combo_jtag

И така, когато разработвам код, моят работен процес е следният: Редактирайте файл в Text Wrangler, натиснете cmd+s, за да го запишете, след това превключете към терминал и натиснете стрелката нагоре, за да се върнете към последната команда, която използвах, която неизбежно беше „make - f облаци/makefile upload_combo_jtag”.

Натиснете return и системата проверява за грешки, компилира кода и го пуска в модула. Отнема около 30 секунди, което все още е досадно дълго време. През това време модулът замръзва и издава странни звуци. Терминалът показва някои тревожно изглеждащи съобщения като „текущ режим: Handler HardFault“ и „извикана команда за изключване“, но това е нормално.

Нека напишем малко код: Hello World for Clouds

Когато започнах да се уча да използвам Arduino, като повечето хора започнах с „Blink“:

void setup() {
 pinMode(13, OUTPUT); // The LED is connected to Pin 13
}
void loop() {
 digitalWrite(13, HIGH); // turn the LED on
 delay(1000); // wait for a second
 digitalWrite(13, LOW); // turn the LED off 
 delay(1000); // wait for a second
}

„Blink“ е класически „Hello World“. След като заработи, можете да погледнете кода и да кажете неща като „Какво ще стане, ако променя това първо забавяне (1000) на забавяне (100)?“ И тогава, изведнъж, вие кодирате.

Кодът на Emilie за Clouds е почти противоположен на „Hello World“. Той е щателно организиран, правилно оптимизиран и често - за начинаещ кодер като мен - объркващ.

За щастие успях да вляза.

Наскоро срещнах Matthias Puech, компютърният учен, който пише Алтернативния фърмуер на паразити за модули с променящи се модули (и също така написа кода за Xaoc Devices Batumi).

Попитах Матиас откъде да започна и — много великодушно — той написа „Hello World“ за Clouds. „Ето го — проста версия на clouds.cc“ (щракнете върху „суров“ или „изтегляне на ZIP“, за да вземете кода).

Ако замените стандартната версия на clouds.cc с този код (той все още трябва да се казва clouds.cc), оставяйки всичко останало непроменено, след това компилирате и го качите в модула, трябва незабавно да видите модел на мигащи оранжеви и зелени светодиоди .

Какво прави това „Hello World“:

  • Светодиодите мигат
  • Отвежда левия вход към левия изход, като силата на звука се контролира от контрола на позицията (точно като пасивен атенюатор!)
  • Ring модулира левия и десния вход, изпращайки резултата към десния изход.

След като Hello World работи, най-накрая сте готови да започнете да се забавлявате. Във възможно най-широкия смисъл на думата „забавление“.

Да напишем малко код: Правене на промени

Докато четете кода Hello World, той се разпада на ясни блокове.

  1. Най-отгоре е лицензът, в коментарите.
  2. След това има различни редове за настройка, изтегляне на външни библиотеки и правене на неща, които все още не разбирам.
  3. Създава се една променлива (брояч), която ще се използва по-късно.
  4. След това намирате функция, наречена SysTick_Handler. Всичко в този блок - както обясняват коментарите - се извиква на всяка милисекунда. Тук може да се случат някои неща на човешкия интерфейс - проверка на копчета, изключване и включване на светодиоди. Те не са критични по отношение на времето, така че не е необходимо да се случват постоянно.
  5. Под това има функция, наречена FillBuffer. Това е туптящото сърце на модула. Извиква се на всеки 32 проби, или 1500 пъти в секунда, и това е мястото, където системата запълва буферите за кодека, чипът, който приема аудио вход и изпраща аудио изход.
  6. Вътре в FillBuffer има цикъл около while (n — ); това е мястото, където се обработва всяка отделна проба, така че трябва да работи 48 000 пъти в секунда.
  7. По-долу е Init(), който се изпълнява веднъж при стартиране на модула, където се задават честоти на дискретизация и т.н.
  8. Накрая в долната част е main(void), който е празният основен цикъл, който не прави нищо, докато таймерите карат всички други рутинни процедури да работят.

Сега, най-накрая, всичко е на мястото си, можете да започнете да правите промени.

В SysTick_Handler намерете този ред:

// a little LED animation for fun 
for(int i=0; i<4; i++) {

Променете 4 на 3, компилирайте и качете в модула. Вместо да светят 4 светлини, сега имате три.

Честито. Вие пишете свой собствен фърмуер.

Сега наистина можете да започнете да експериментирате. Не се страхувайте да променяте нещата и да експериментирате, докато не намерите нещо интересно.

Изграждане на просто забавяне

Можете да създадете много просто забавяне, като използвате масив с команди като тази:

// create an array with space for one second of audio
uint32_t delayLength = 48000;
int16_t delayLine[48000];
// create variables to store record and playback positions 
// Use floats for these because we might want to have them 
// move faster or slower.
// Start playback one sample *ahead* of the record head, because 
// they're going to be in a loop. 
double recordHead = 0;
double playHead = 1; 
// Write the Left input to the delay line
// Using the modulo "% delayLength" means it writes in a loop
// starting again at the beginning when it gets to the end 
// Use (long) to convert the float into an integer, you can't 
// use a float to set the index on an array 
delayLine[(long)recordHead % delayLength] = input->l;
recordHead++;
// Read the Left output from the delay line
output->l = delayLine[(long)playHead % delayLength];
playHead++;

Можете да видите тези команди да работят заедно в този фърмуер. Това е просто 1 секунда аудио забавяне на левия канал - каквото и да поставите, излиза 1 секунда по-късно.

Очевидните следващи стъпки биха били смесването на чистия сигнал и добавянето на обратна връзка. С още няколко стъпки (включително научаване за линейна интерполация) можете да създадете нещо нелепо като това:

Неща, които научих по пътя, без определен ред

  • Аудиото влиза и излиза като 16-битови цели числа (int16_t); -32 768 до 32 767. Обработката на сигнала обикновено се извършва като 32-битови плаващи числа; където -1 до +1 представлява сигнала. За да конвертирате между двете:
int16_t y = static_cast<int16_t>(x * 32768.0);
float y = static cast<float>(x) / 32768.0;
  • Добавянето на два аудио сигнала заедно работи като смесване. Умножаването им работи като амплитудна (пръстенова) модулация. И в двата случая ще трябва да направите известно разделяне след това, за да сте сигурни, че изходът няма да изреже (има и други начини да направите това.)
  • Когато получите нещо „погрешно“, ще чуете странни пукащи звуци на изходите – например ако числото препълва и води до неочаквани резултати от време на време, това, което се случва при 48khz, звучи като странно пукане. Понякога странното пращене звучи интересно. Понякога грешното звучи страхотно.
  • Потовете и CV входовете произвеждат плаващи стойности нула към едно. Ето защо умножаването на входа по позиция на пот работи като атенюатор. Можете да намерите списък на наличните входове в enum_AdcChannel тук.
  • Голяма разлика между този и простия код на Arduino е, че той е проектиран да не блокира. Кодът на Arduino обикновено прави едно нещо наведнъж. Ако извикате „закъснение (1000)“, цялата система спира и чака. Кодът на Emilie за Clouds работи по различен начин. Ако извикате „ adc.Convert()“, системата се задейства, за да прочете всички потенциометри и CV входове, което отнема стотици микросекунди, но все още може да прави други неща (като възпроизвеждане на аудио) едновременно.
  • Когато станете по-уверени, започнете да четете останалата част от фърмуера на Clouds, за да намерите много полезни процедури и идеи.

Следващи стъпки и допълнително четене

Това наистина е докъдето стигнах досега. Постепенно прелиствам „The Computer Music Tutorial by Curtis Roads“ и разглеждам сайтове като „musicdsp.org“.

Благодаря за четенето, ако сте стигнали дотук, моля, публикувайте връзки към вашите изобретения в отговорите.