====== Building a MIDI Controller with Analog Discovery Pro (ADP3450/3250) ====== ~~TechArticle~~ {{ :test-and-measurement:analog-discovery-pro-3x50:bandlab_in_use.png?600 |}} A Musical Instrument Digital Interface (MIDI) controller is a key element in electronic music production. Such devices, both hardware and software, can be quite expensive, but with a bit of work, the Analog Discovery Pro can be set up as a highly customizable MIDI controller, with wireless connection capability. This guide presents, how to configure a Test & Measurement device and a computer to be able to send and receive MIDI commands over Ethernet or Wi-Fi. ---- ===== Prerequisites ===== == Hardware == * [[test-and-measurement:analog-discovery-pro-3x50:start|]] * [[[[programmable-logic:arty-s7:start|]], or other FPGA development board * hardware user interface elements (buttons, switches, sliders, etc.) * passive electronic components == Software == * On PC: * [[https://www.tobias-erichsen.de/software/rtpmidi.html|rtpMIDI]] * [[https://www.bandlab.com/mix-editor|BandLab]], [[https://serato.com/studio|Serato Studio]], or any other DAW * [[https://ttssh2.osdn.jp/|Tera Term]], or any other terminal emulator * [[https://code.visualstudio.com/|Visual Studio Code]], or any other editor * [[https://www.ni.com/ro-ro/support/downloads/software-products/download.multisim.html|Multisim Desktop]] Education version 14.2, or higher * [[https://www.xilinx.com/products/design-tools/vivado/vivado-ml.html|Vivado]] version 2021.1, or above * [[https://cloud.digilent.com/myproducts/Adept?pc=1&tab=2|Adept]] - optional * [[https://winscp.net/eng/index.php|WinSCP]] - optional * On Analog Discovery Pro: * [[https://github.com/ravelox/pimidi|RaveloxMIDI]] - installation instructions can be found below * [[https://www.python.org/downloads/|Python 3]] - preinstalled * WaveForms SDK - preinstalled * [[https://lp.digilent.com/complete-adept-utilities-download|Adept Utilities]] ---- ===== What is MIDI? What is RTP-MIDI? What is a DAW? ===== Musical Instrument Digital Interface (MIDI) is a technical standard which is used in electronic music production. MIDI controllers are not instruments, they do not produce sounds, but a send a specific set of commands to other devices, or software, like a Digital Audio Workstation (DAW). These commands contain all the information necessary to produce the desired sound, or to apply the desired effect. The most important advantage of a MIDI controller is, that with the same commands, the user can control any instrument of their choice (for example one can "play" a clarinet or a guitar with keypresses). Connecting a MIDI controller to a DAW usually requires specific connectors. Most laptops do not have such a MIDI connector, nor has the Analog Discovery Pro. While building such connectors is possible, it is simpler to send the MIDI commands over Ethernet or Wi-Fi, using a protocol called Real-time Transfer Protocol - MIDI (RTP-MIDI). RTP-MIDI uses the RTP network protocol to send the commands in real-time to a receiver. After the MIDI commands are received, they should be "converted" into sound. This conversion is usually done with a Digital Audio Workstation (DAW). A DAW is a hardware or software device which is controlled by MIDI commands and produces audio. In this project the BandLab online DAW will be used, as it is free, and doesn't have to be downloaded. ---- ===== Signal Transmission and Reception ===== ==== Setting Up the DAW ==== To connect to a DAW without a physical MIDI connector, a network MIDI driver is needed. Such a driver is included in the [[https://www.tobias-erichsen.de/software/rtpmidi.html|rtpMIDI]] by Tobias Erichsen. Download, install, and start the program, then rename and enable the first session. {{ :test-and-measurement:analog-discovery-pro-3x50:start_rtpmidi.jpeg?600 |}} Let the rtpMIDI run in the background and open the DAW. Open the DAW's settings, then find the MIDI settings, or MIDI devices and select the previously created rtpMIDI session as an input device. Add a new track (or more) to the project and select the desired instrument or effect to control. Before playing the selected instrument, the MIDI controller must be set up. {{ :test-and-measurement:analog-discovery-pro-3x50:bandlab_midi_device.png?600 |}} ---- ==== Setting Up the Analog Discovery Pro as a MIDI Controller ==== To successfully run this example, the latest Linux version should be installed on the device. To update the ADP3450/ADP3250 Linux image, follow this guide: [[test-and-measurement:analog-discovery-pro-3x50:recover-linux-mode]]. Now connect the device the internet. If internet is already enabled for Linux mode, just connect the Ethernet cable and skip this step. Else, follow this guide: [[test-and-measurement:analog-discovery-pro-3x50:connect-to-the-internet]]. To command the ADP3450/ADP3250, it has to be connected to it through a terminal. To do so, a terminal emulator program, like [[https://ttssh2.osdn.jp/|Tera Term]], [[https://www.chiark.greenend.org.uk/~sgtatham/putty/|PuTTY]] for Windows, or [[https://play.google.com/store/apps/details?id=de.kai_morich.serial_wifi_terminal|Serial WiFi Terminal]] for Android is needed (the Analog Discovery Pro can now be controlled from any device connected to the internet). Tera Term will be used in this guide, but the steps are similar for each of the mentioned program. Open the terminal emulator and establish a new Secure Shell (SSH) connection to the Analog Discovery Pro's address: digilent@ADPro. Leave every other setting as default. When required, enter the user name and the password: |<50% 25% 25%>| ^ user name: | digilent | ^ password: | digilent | The terminal should be connected now. {{ :test-and-measurement:analog-discovery-pro-3x50:ssh.png?400 |}} Install the necessary packages by entering the following command into the terminal: sudo apt-get install autoconf build-essential libavahi-client-dev libavahi-core-dev libasound2-dev git avahi-daemon -y When required, enter the sudo password: |<50% 25% 25%>| ^ sudo password: | digilent | Select the location with the "cd" command, then clone the [[https://github.com/ravelox/pimidi|RaveloxMIDI]] GitHub repository: git clone https://github.com/ravelox/pimidi.git Open the cloned directory: cd pimidi cd raveloxmidi Install RaveloxMIDI with: sh ./autogen.sh ./configure make sudo make install {{ :test-and-measurement:analog-discovery-pro-3x50:install_ravelox.png?400 |}} ---- ===== Designing and Building the User Interface ===== ==== Control Signals ==== The hardware user interface of the MIDI controller consists of several control elements, like buttons, switches, sliders, drum pads, etc. As these elements need certain conditioning circuits and because the ADP3450 has only 16 digital IO pins, 4 analog input and 2 analog output channels, so the large number of control signals must be multiplexed to the reduced number of inputs, an efficient "driver" must be designed. The control signals can be divided in three main categories: digital signals (coming from buttons, switches and rotary encoders), slowly changing analog signals (coming from potentiometers/sliders) and fast changing analog signals (coming from pressure sensitive pads). These three categories will be discussed separately, as the methods used to register them differ. Digital signals can be directly connected in the driver circuit. They must be debounced (the mechanical noise created by the "bouncing" of the pressed pushbuttons must be filtered out) and might be latched (momentary buttons acting like switches). If some signals have to increment/decrement certain counters, these counters can also be implemented in the driver circuit. When the input signals are conditioned, they can be multiplexed to the digital inputs of the ADP. Slowly changing analog signals can't be directly measured by the digital inputs of the ADP. While the analog inputs (oscilloscope channels) can measure them directly, they should be used to record fast changing analog signals, so another solution should be found. One method to transform the analog signals to digital ones is by using comparators: the comparators can compare the provided voltage level to a fast changing sawtooth signal, and output a PWM signal for which the duty cycle is proportional with the voltage level of the original signal. This duty cycle can be measured on a single digital pin, using the logic analyzer. The generated PWM signal comes connected to the driver circuit to be multiplexed to the digital inputs of the ADP. --> Click for Model and Simulation # <-- Fast changing analog signals can be measured by the oscilloscope channels. As there will be more signal sources, then channels, these signals have to be multiplexed as well. In the case of the drum pads, the generated pulses are all positive, so the easiest way of multiplexing them is for every channel to add the output of a drum pad to the inverted output of another drum pad. This way the number of input signals will be double the number of input channels and no digital signals are required to control the "multiplexer". The negative (inverted pulses) can be easily separated from the positive pulses via software. The only downside of this method is, that two simultaneous pulses with equal amplitude can cancel each other, but the probability of this is negligible (humans can't strike two drum pads in the exact same moment). --> Click for Model and Simulation # <-- ---- ==== User Interface Design ==== While a MIDI controller can be controlled with a mouse and/or a keyboard, usually a more convenient hardware is used. Such hardware can be quite expensive to buy, but can be easily designed and built on a custom PCB. An advantage of the DIY controller is, that the control elements can be tailored to the needs of the user. Design the user interface circuit to contain the desired control and indicator elements. Keep in mind the offered possibilities and limitations of the ADP3450, and the signal conditioning methods discussed above. {{ :test-and-measurement:analog-discovery-pro-3x50:pcb_3d.png?600 |}} For a detailed presentation of the user interface circuitry used in this guide, check the drop-down below. Any other custom user interface can be used as well. --> The User Interface Circuit # === Parts of the User Interface === The user interface consists of five main parts: * The most important control elements are the play/pause button, the record button, the volume slider and the channel selector button. To display the selected channel, this circuit part also contains a [[pmod:pmodssd:start|]] connected and driven by the digital driver (implemented on the Arty-S7 FPGA board). * To further shave the sound of the controller, the second part contains three filters (low, middle and high). The analog-to-PWM converter for the volume slider and the filters is present in this block as well. Incremental rotary encoders are used to set the modulation, the attack, or the release time of the sounds. * In the third block, six potentiometers (with analog-to-PWM converters) are used to control the effects set in the DAW. * While traditional keypads have more buttons, this controller only has 12: for the notes C, C#, D, D#, E, F, F#, G, G#, A, A# and B. Two additional buttons can increment and decrement the octave. The index of the chosen octave is displayed on a seven segment display. The RGB LEDs used to implement visual effects are connected to a connector for an open-drain MOSFET driver module ([[pmod:pmodod1:start|]]). * The actual drum pads are mechanical parts, each one a disk containing a piezo, but the driver is implemented on the PCB. The outputs of the piezos are clamped and "multiplexed" with the method presented above. === Connecting the User Interface === Connect the output signals of the user interface to the FPGA board, but keep track of the connections, as they will have to be defined in the PLD design later, in Multisim (see below). The BNC oscilloscope connectors are connected directly to the drum pad outputs (multiplexed) and the waveform generator channels are used to generate the reference signal for the PWM generator and the -5V DC voltage reference. The USB ports on the ADP are used to power and program the FPGA, connect the Wi-Fi dongle and supply power to the displays and LEDs through a USB connector. The Pmod connectors are used on the Arty-S7 to connect the user interface control elements, while the Chipkit analog headers are used to connect the displays and the LEDs. The ADP3450 digital pins are connected to the outer digital Chipkit header. <-- ---- ==== Driver Design ==== As most of the control signals are digital (including the converted "slow" analog signals), the conditioning circuits for them will be also digital. To prototype a complex digital circuit, the easiest way is to use an FPGA development board. In this project the Arty-S7 FPGA board will be used, for which the design will be created in Multisim, to avoid using VHDL, or Verilog (which can be challenging, especially for new users) and to make the final circuit easier to understand by drawing a schematic of it. If the chosen FPGA board isn't available in Multisim PLD design, follow the instructions in the [[learn:programmable-logic:tutorials:add_fpgas_to_multisim:start|]] guide to create the configuration files for it. Start a new PLD design in Multisim, select the desired board and create the necessary conditioning circuits for the input signals. Some digital I/O pins of the ADP3450 can be used as outputs to control the multiplexers connected to the input pins, this way much more input signals can be connected, then the number of input pins. When ready, click //Transfer -> Export to PLD... -> Generate and save programming file// to use Vivado and compile the bit file from the design. {{ :test-and-measurement:analog-discovery-pro-3x50:hackster_cover_w_arty.png?600 |}} For a detailed presentation of the driver circuitry used in this guide, check the drop-down below. Any other custom driver can also be designed on the FPGA board. --> The Driver Circuit # === Hierarchical Blocks === Before creating the driver, design and test certain circuits which will be used multiple times in the driver, to make the final design easier to create and to understand. Some blocks to create: * clock divider - several D flip-flops are used to reduce the clock frequency to the desired value. If the clock rate must be further reduced, multiple clock divider blocks can be cascaded. * debouncer - when a button is pressed, some mechanical noise appears in the signal (the bouncing of the two metal plates on each other), so if the action is taken on the edge of the signal, the circuit will be triggered multiple times. A debouncer "validates" the output signal only after a certain time, so the bouncing can come to an end before. * latch - so buttons can be used as switches (on press turns the signal high, the next one turns it low). * 1:2 demultiplexer - Multisim contains only larger circuits (1:8). * up-down counter to N (0 {{ :test-and-measurement:analog-discovery-pro-3x50:debouncer.png?600 |}} {{ :test-and-measurement:analog-discovery-pro-3x50:latch.png?600 |}} {{ :test-and-measurement:analog-discovery-pro-3x50:1_to_2_dmx.png?600 |}} === Driver === After all the blocks were designed and tested, the actual driver can be built. As almost all conditioning circuits need clock signals, the derived clocks (with the desired frequency) must be obtained from the FPGA clock. Use clock dividers to reduce the frequency. The RGB LED control signals are just passed through the FPGA, they are entirely generated by the ADP. The PWM signals coming from the potentiometer conditioning circuits are again buffered, then sent to the multiplexers. The play/pause and record buttons are debounced and latched to act like switches. The piano keys are debounced and sent to the multiplexers. The button selecting the MIDI channel is debounced, then connected to an up counter. The output of the counter is connected to the multiplexers. Octaves are set by two buttons: one buttons shifts the octave up, the other one down. The N counter block is used to set the upper limit of counting to 10 (the block will count from 0 to 10 - 11 positions). The pulse counter block is used to turn the incremental encoder used as modulation wheel into an absolute encoder. The same is done for the timing wheel, but the latched timing button decides, if the output is used for the attack time, or for the release time (the distinction is made from software). The previously created hierarchical blocks are used to display the channel number and the octave index on the 7 segment displays. Finally, the conditioned output signals are multiplexed (4:1) to the digital I/O pins of the ADP. To generate the multiplexer addresses, two digital pins of the ADP are used, the address being generated from software. {{ :test-and-measurement:analog-discovery-pro-3x50:simple_io_and_clock.png?600 |}} {{ :test-and-measurement:analog-discovery-pro-3x50:pulse_counters_wrapper.png?600 |}} {{ :test-and-measurement:analog-discovery-pro-3x50:octaves_mux_keypad.png?600 |}} === Connecting the Driver === In Multisim, define the connections between the FPGA board and the ADP. <-- ---- ===== Designing the Software ===== ==== Hardware Abstraction Layer ==== To make the program simpler and easier to understand, a sort of Hardware Abstraction Layer (HAL) is needed: a set of functions, which communicate with the hardware, and make the usage of the hardware easier. In this case the WaveForms SDK is used to control the ADP3450, but to further simplify the final script, the module created in the [[test-and-measurement:guides:waveforms-sdk-getting-started|]] guide will be used. ---- ==== Configuring the FPGA ==== To configure the FPGA board connected to the ADP3450, with the bit file created with Multisim, the [[https://lp.digilent.com/complete-adept-utilities-download|Adept Utilities]] must be installed on the Analog Discovery Pro. To install the software, follow the [[test-and-measurement:analog-discovery-pro-3x50:programming-an-fpga|]] guide. Create a module in which the Adept Utilities tool is used with the subprocess command. djtgcfg init -d ArtyS7 and djtgcfg prog -d ArtyS7 -i 0 -f ./FPGA/midi_driver_ArtyS7.bit **Note: **//Don't forget to change the device name and the name of the bit file.// In the script, check for the responses of the commands to determine if the operation was successful, or not. One can try the commands first in a terminal to check possible responses. ---- ==== MIDI ==== Start raveloxmidi in the background as a subprocess, to be able to communicate using RTPMIDI. Use the following command: sudo raveloxmidi -c ./MIDI/address.conf Don't forget to change the path of the configuration file, to a file, which contains the bind address: "0.0.0.0". network.bind_address = 0.0.0.0 While the background process is running, data sockets containing MIDI data can be sent. An extensive list of possible MIDI messages is available on the page [[https://wiki.cockos.com/wiki/index.php/MIDI_Specification|MIDI Specification]] and [[https://anotherproducer.com/online-tools-for-musicians/midi-cc-list/|MIDI CC List for Continuous Controllers]]. See the drop-downs below for the most important MIDI messages. --> MIDI Message Types ^# |<100% 10% 10% 50% 10% 10% 10%>| ^ Message Type ^ Code ^ Meaning ^ Data Byte 1 ^ Data Byte 2 ^ | NOTE_OFF | 0x80 | This message is sent when a note is stopped. | Note number | Velocity | | NOTE_ON | 0x90 | This message is sent when a note is started. | Note number | Velocity | | POLY_KEY_PRES | 0xA0 | Polyphonic Key Pressure (Aftertouch) | Note number | Pressure | | CTRL_CHG | 0xB0 | Sent when a controller value changes. | Controller number | ... | | PRG_CHG | 0xC0 | Sent when a patch number changes. | Program number | - | | CH_PRS_AFTT | 0xD0 | Channel Aftertouch | Pressure | - | | PITCH_BND | 0xE0 | This message is sent to indicate a change in the pitch wheel. | LSB | MSB | | SYS_EX_START | 0xF0 | This message makes up for all that MIDI doesn't support. | ... | ... | <-- --> MIDI Notes ^# |<100% 50% 50%>| ^ Note ^ Value ^ ^ | C | 0 | | C# | 1 | | D | 2 | | D# | 3 | | E | 4 | | F | 5 | | F# | 6 | | G | 7 | | G# | 8 | | A | 9 | | A# | 10 | | B | 11 | <-- --> MIDI Octaves ^# |<100% 20% 10% 70%>| ^ Notation ^ Value ^ Meaning ^ | DBL_CONTRA_O | 0 | Starts with $C_{-1}$. | | SUB_CONTRA_O | 12 | Starts with $C_{0}$. | | CONTRA_O | 24 | Starts with $C_{1}$. | | GREAT_O | 36 | Starts with $C_{2}$. | | SMALL_O | 48 | Starts with $C_{3}$. | | LINE1_O | 60 | Starts with $C_{4}$. | | LINE2_O | 72 | Starts with $C_{5}$. | | LINE3_O | 84 | Starts with $C_{6}$. | | LINE4_O | 96 | Starts with $C_{7}$. | | LINE5_O | 108 | Starts with $C_{8}$. | | LINE6_O | 120 | Starts with $C_{9}$. | <-- Finally, kill every process with the name "raveloxmidi" to terminate the connection. ---- ==== MIDI Controller Script ==== In the wrapper script, define the connections between the devices and other parameters like the frequency and amplitude/offset of the output signals, then import the HAL, the previously defined MIDI commands, functions and constants and the FPGA programming function. Use these modules to upload the bit file to the FPGA, start raveloxmidi and initialize the WaveForms SDK. Start the WaveForms instruments needed in the controller: start the power supplies, generate the necessary analog and digital signals, set static DIO pins to the desired state and initialize the oscilloscope and the logic analyzer. (Don't forget to close these instruments and disconnect from the ADP at the end of the script! Also stop raveloxmidi when the script is terminated.) The rest of the code should run in an infinite loop, broken only by pressing Ctrl+C. In each iteration set the address pins of the multiplexers in the driver to 0, 1, 2, or 3 (implement a 2 bit counter here), then read the current state of the multiplexer outputs. Decode these outputs according to the current address and compare the results to the previous ones. If there is no change in the state of a user interface element, no new command should be sent, but if there is a change, send the respective MIDI command with raveloxmidi. One might also want to set some visual effects using the RGB LEDs. Colors can be easily modified according to the received data. ---- ===== Testing ===== Copy the project containing the Python scripts onto the ADP3450, with the help of a FAT32 formatted USB flash drive, or using [[https://winscp.net/eng/index.php|WinSCP]]. Navigate into the project directory, then run the script by typing sudo python3 midi_controller.py {{ :test-and-measurement:analog-discovery-pro-3x50:sftp-winscp.png?600 |}} Open the rtpMIDI program, then connect to the directory called "raveloxmidi". The connection should appear in the participants tab. {{ :test-and-measurement:analog-discovery-pro-3x50:connected_rtp-midi.jpg?600 |}} The MIDI controller should now be fully functional, capable of "playing" any instrument the user wants, and is expandable with additional analog and digital control elements, like sliders, more buttons, switches, etc. {{ :test-and-measurement:analog-discovery-pro-3x50:bandlab_in_use.png?600 |}} ---- ===== Next Steps ===== For more information on WaveForms SDK, see its [[software:waveforms:waveforms-sdk:start|Resource Center]]. For technical support, please visit the [[https://forum.digilent.com/forum/8-test-and-measurement/|Test and Measurement]] section of the Digilent Forums.