Официальный сайт компании Arduino по адресу arduino.cc
MIDI USB 2 CV
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
Всем привет! В данный момент пытаюсь перевести данный проект (https://github.com/elkayem/midi2cv) для поддержки USBMIDI. Переводит midi-сигналы в cv\gate сигналы (для аналоговых синтезаторов). Код рабочий, собирал под обычный MIDI.
Для начала решил протестировать выход с пинов Gate, trigger и clock. Clock и Trigger работают идеально, а вот с Gate проблемы. Когда первый раз после запуска нажимаешь какую нибудь ноту, Gate-пин переходит в состояние HIGH (как и должно быть). Но, когда отжимаешь ноту, напряжение на пине остаётся. Должно быть LOW. Иными словами, 5 вольт на Gate-пине должно быть только тогда, когда нота нажата. Если нажать любые ноты 20 раз, то пин переходит в LOW, и отзывается едва заметными скачками напряжения на каждое следующее нажатие. Думаю всё дело в функции CommandLastNote(), потому что там в цикле присутствует i<20. Ну и переход пина Gate в состояние LOW тоже там. От программирования я далёк, поэтому своими силами понять, что там происходит, не могу. Прошу любого совета по теме, буду очень признателен.
Если посмотреть на измененный код, можно заметить, что часть функций я убрал - DAC2, выбор NotePriority (оставил только LastNotePriority). Основные вещи старался вообще не трогать, только то, что касается MidiUSB.
Использую Arduino Pro Micro.
Код пока получается такой:
#include "MIDIUSB.h" #include <SPI.h> #define GATE 5 #define TRIG 6 #define CLOCK 4 #define DAC1 8 void setup() { pinMode(GATE, OUTPUT); pinMode(TRIG, OUTPUT); pinMode(CLOCK, OUTPUT); pinMode(DAC1, OUTPUT); digitalWrite(GATE,LOW); digitalWrite(TRIG,LOW); digitalWrite(CLOCK,LOW); digitalWrite(DAC1,HIGH); SPI.begin(); Serial.begin(115200); } bool notes[88] = {0}; int8_t noteOrder[20] = {0}, orderIndx = {0}; unsigned long trigTimer = 0; void loop() { int noteMsg, velocity, channel; static unsigned long clock_timer=0, clock_timeout=0; static unsigned int clock_count=0; if ((trigTimer > 0) && (millis() - trigTimer > 20)) { digitalWrite(TRIG,LOW); // Set trigger low after 20 msec trigTimer = 0; } if ((clock_timer > 0) && (millis() - clock_timer > 20)) { digitalWrite(CLOCK,LOW); // Set clock pulse low after 20 msec clock_timer = 0; } //NEW PART#############################################################3 midiEventPacket_t rx = MidiUSB.read(); //listen for new MIDI messages switch (rx.byte1) { case 0x90: //Note On message channel 1 noteMsg = rx.byte2 - 21; // A0 = 21, Top Note = 108 if ((noteMsg < 0) || (noteMsg > 87)) break; velocity = rx.byte3; if (velocity == 0) { notes[noteMsg] = false; } else { notes[noteMsg] = true; // velocity range from 0 to 4095 mV Left shift d2 by 5 to scale from 0 to 4095, // and choose gain = 2X setVoltage(DAC1, 1, 1, velocity<<5); // DAC1, channel 1, gain = 2X } if (notes[noteMsg]) { // If note is on and using last note priority, add to ordered list orderIndx = (orderIndx+1) % 20; noteOrder[orderIndx] = noteMsg; } commandLastNote(); case 0xF8: //Clock if (millis() > clock_timeout + 300) clock_count = 0; // Prevents Clock from starting in between quarter notes after clock is restarted! clock_timeout = millis(); if (clock_count == 0) { digitalWrite(CLOCK,HIGH); // Start clock pulse clock_timer = millis(); } clock_count++; if (clock_count == 24) { // MIDI timing clock sends 24 pulses per quarter note. Sent pulse only once every 24 pulses clock_count = 0; } break; } } void commandLastNote() { int8_t noteIndx; for (int i=0; i<20; i++) { noteIndx = noteOrder[ mod(orderIndx-i, 20) ]; if (notes[noteIndx]) commandNote(noteIndx); else return; } digitalWrite(GATE,LOW); // All notes are off } #define NOTE_SF 47.069f // This value can be tuned if CV output isn't exactly 1V/octave void commandNote(int noteMsg) { digitalWrite(GATE,HIGH); digitalWrite(TRIG,HIGH); trigTimer = millis(); unsigned int mV = (unsigned int) ((float) noteMsg * NOTE_SF + 0.5); setVoltage(DAC1, 0, 1, mV); // DAC1, channel 0, gain = 2X } void setVoltage(int dacpin, bool channel, bool gain, unsigned int mV) { unsigned int command = channel ? 0x9000 : 0x1000; command |= gain ? 0x0000 : 0x2000; command |= (mV & 0x0FFF); SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); digitalWrite(dacpin,LOW); SPI.transfer(command>>8); SPI.transfer(command&0xFF); digitalWrite(dacpin,HIGH); SPI.endTransaction(); } int mod(int a, int b) { int r = a % b; return r < 0 ? r + b : r; }