Differences

This shows you the differences between two versions of the page.

Link to this comparison view

test-and-measurement:analog-discovery-pro-3x50:midi [2022/04/04 10:32] – [User Interface Design] Álmos Veres-Vitályostest-and-measurement:analog-discovery-pro-3x50:midi [2022/10/04 11:38] (current) – external edit 127.0.0.1
Line 170: Line 170:
 --> The User Interface Circuit # --> The User Interface Circuit #
 <WRAP group> <WRAP group>
 +<WRAP group>
 +=== Parts of the User Interface ===
 The user interface consists of five main parts: 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).   * 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).
Line 177: Line 179:
   * 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.   * 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.
 </WRAP> </WRAP>
-<-- 
-</WRAP> 
----- 
  
-==== Connecting the User Interface ==== +<WRAP group> 
-<WRAP group><WRAP half column>+=== 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). 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 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.
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:adp_connector.png?600 |}} 
-</WRAP></WRAP> 
  
-<WRAP group><WRAP half column> 
 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. 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.
-</WRAP><WRAP half column> +</WRAP> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:arty_connector.png?400 |}} +</WRAP> 
-</WRAP></WRAP>+<-- 
 +</WRAP>
 ---- ----
  
Line 211: Line 207:
  
 <WRAP group> <WRAP group>
-For a detailed presentation of the driver circuitry, you can check the drop-down below, or you can design your own driver on an FPGA board.+For a detailed presentation of the driver circuitry used in this guide, you can check the drop-down below, or you can design your own driver on an FPGA board.
  
 --> The Driver Circuit # --> The Driver Circuit #
 +<WRAP group>
 <WRAP group> <WRAP group>
 === Hierarchical Blocks === === Hierarchical Blocks ===
-<WRAP group> +Before creating the driver, design and test certain circuits which will be used multiple times in the driver, to make your final design easier to create and to understand. Some blocks to create:   
-Before creating the driver, design and test certain circuits which will be used multiple times in the driver, to make your final design easier to create and to understand. +  * 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. 
-<WRAP group><WRAP half column> +  * latch - so buttons can be used as switches (on press turns the signal high, the next one turns it low). 
-The first hierarchical block created is the 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. +  * 1:2 demultiplexer - Multisim contains only larger circuits (1:8). 
-</WRAP><WRAP half column> +  * up-down counter to N (0<N<15) - this circuit will be used to change the octaves (there are only 11 valid octaves, so counting to 15 is unnecessary). 
-{{ :test-and-measurement:analog-discovery-pro-3x50:clock_divider.png?600 |}} +  * pulse counter - as the Python script will use polling to measure every signal, it might miss certain events of the rotary encoders, so they should be re-encoded as numbers. This is why a pulse counter is designed, which is an up-down counter clocked by the two waves of the rotary encoder in quadrature. The block turns the incremental rotary encoders into 4-bit absolute encoders. 
-</WRAP></WRAP> +  * 7 segment display decoder - while 4-bit binary numbers might be easy to understand for machines, on a user interface (created for humans) numbers should appear in decimal representation. This is why a decoder must be used for the dual 7 segment displays, which displays a number between 1 and 16 (received in binary) in decimal representation. 
- +  * display selector - while most FPGA boards have many pins, there are some cases, when "many" is still not enough. This is why the cathodes of the two dual displays should be multiplexed (the same cathodes are used in different times). This block takes two binary numbers (and one clock) as inputs and displays the first number on the first display (decoded) and the second number on the second display.
-<WRAP group><WRAP half column> +
-One of the most important building blocks is the 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. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:debouncer.png?600 |}} +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-A simple latch should also be implemented, so buttons can be used as switches (on press turns the signal high, the next one turns it low). +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:latch.png?400 |}} +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-A 1:2 demultiplexer is also implemented as Multisim contains only larger circuits (1:8). +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:dmx12.png?600 |}} +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-The next block to be prepared is an up-down counter to N (0<N<15). This circuit will be used to change the octaves (there are only 11 valid octaves, so counting to 15 is unnecessary). +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:n_counter.png?600 |}} +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-As the Python script will use polling to measure every signal, it might miss certain events of the rotary encoders, so they should be re-encoded as numbers. This is why a pulse counter is designed, which is an up-down counter clocked by the two waves of the rotary encoder in quadrature. The block turns the incremental rotary encoders into 4-bit absolute encoders. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:pulse_counter.png?600 |}} +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-While 4-bit binary numbers might be easy to understand for machines, on a user interface (created for humans) numbers should appear in decimal representation. This is why a decoder must be used for the dual 7 segment displays, which displays a number between 1 and 16 (received in binary) in decimal representation. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:7sd_decoder.png?600 |}} +
-</WRAP></WRAP> +
- +
-<WRAP group><WRAP half column> +
-While most FPGA boards have many pins, there are some cases, when "many" is still not enough. This is why the cathodes of the two dual displays should be multiplexed (the same cathodes are used in different times). This block takes two binary numbers (and one clock) as inputs and displays the first number on the first display (decoded) and the second number on the second display. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:7sd_mux.png?600 |}} +
-</WRAP></WRAP>+
 </WRAP> </WRAP>
  
 +<WRAP group>
 === Driver === === Driver ===
-<WRAP group> 
 After all the blocks were designed and tested, the actual driver can be built. After all the blocks were designed and tested, the actual driver can be built.
  
-<WRAP group><WRAP half column> 
 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. 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.
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:clock_divider_wrapper.png?600 |}} 
-</WRAP></WRAP> 
  
-<WRAP group><WRAP half column> +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 RGB LED control signals are just passed through the FPGA, they are entirely generated by the ADP. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:rgb_passthrough.png?400 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> +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 play/pause and record buttons are debounced and latched to act like switches. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:ply_rec_btn.png?400 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> +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 PWM signals coming from the potentiometer conditioning circuits are again buffered, then sent to the multiplexers. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:potentiometers_buffered.png?600 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> +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 button selecting the MIDI channel is debounced, then connected to an up counter. The output of the counter is connected to the multiplexers. +
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:channel_counter.png?600 |}} +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column> 
-The pulse counter block is used to turn the incremental encoder used as modulation wheel into an absolute encoder. 
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:modulation_counter.png?400 |}} 
-</WRAP></WRAP> 
- 
-<WRAP group><WRAP half column> 
-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). 
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:timing_counter.png?400 |}} 
-</WRAP></WRAP> 
- 
-<WRAP group><WRAP half column> 
-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). 
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:octave_counter.png?600 |}} 
-</WRAP></WRAP> 
- 
-<WRAP group><WRAP half column> 
-The piano keys are debounced and sent to the multiplexers. 
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:piano_keys.png?600 |}} 
-</WRAP></WRAP> 
- 
-<WRAP group><WRAP half column> 
 The previously created hierarchical blocks are used to display the channel number and the octave index on the 7 segment displays. The previously created hierarchical blocks are used to display the channel number and the octave index on the 7 segment displays.
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:7sd_wrapper.png?600 |}} 
-</WRAP></WRAP> 
  
-<WRAP group><WRAP half column> 
 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. 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.
-</WRAP><WRAP half column> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:driver_mux.png?200 |}} 
-</WRAP></WRAP> 
 </WRAP> </WRAP>
-</WRAP> 
-<-- 
-</WRAP> 
----- 
  
-==== Connecting the Driver ==== 
 <WRAP group> <WRAP group>
 +=== Connecting the Driver ===
 In Multisim, define the connections between the FPGA board and the ADP. In Multisim, define the connections between the FPGA board and the ADP.
- +</WRAP> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:driver_connection.png?800 |}}+</WRAP> 
 +<--
 </WRAP> </WRAP>
 ---- ----
Line 358: Line 259:
 ---- ----
 ==== Configuring the FPGA ==== ==== Configuring the FPGA ====
-<WRAP group><WRAP half column>+<WRAP group>
 To configure the FPGA board connected to the ADP3450, with the bit file created with Multisim, the [[https://mautic.digilentinc.com/adept-utilities-download|Adept Utilities]] must be installed on your device. To install the software, follow the [[test-and-measurement:analog-discovery-pro-3x50:programming-an-fpga|]] guide. To configure the FPGA board connected to the ADP3450, with the bit file created with Multisim, the [[https://mautic.digilentinc.com/adept-utilities-download|Adept Utilities]] must be installed on your device. To install the software, follow the [[test-and-measurement:analog-discovery-pro-3x50:programming-an-fpga|]] guide.
  
Line 365: Line 266:
 and and
 <code>djtgcfg prog -d ArtyS7 -i 0 -f ./FPGA/midi_driver_ArtyS7.bit</code> <code>djtgcfg prog -d ArtyS7 -i 0 -f ./FPGA/midi_driver_ArtyS7.bit</code>
 +
 **Note: **//Don't forget to change the device name and the name of the bit file to match to your preferences.// **Note: **//Don't forget to change the device name and the name of the bit file to match to your preferences.//
-</WRAP><WRAP half column><code python> 
-""" This module realizes communication with the FPGA board """ 
  
-from subprocess import Popen, PIPE +<WRAP center round tip 80%> 
- +In your script, check for the responses of the commands to determine if the operation was successful, or not. You can try the commands first in a terminal to check possible responses. 
-"""-------------------------------------------------------------------""" +</WRAP></WRAP>
-""" UPLOADING THE BIT FILE """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def configure(description_file="./FPGA/MIDI_UI_Arty_S7.bit", device_name="ArtyS7", chip_index="0"): +
-    """ +
-        configures the connected FPGA board +
- +
-        parameters: - bit file +
-                    - device name +
-                    - chip index +
- +
-        returns:    - True on success, False on error +
-    """ +
-    # initialize the FPGA board +
-    process = Popen(["djtgcfg", "init", "-d", device_name], +
-                     stdout=PIPE, stderr=PIPE) +
-    stdout, stderr = process.communicate() +
-    stdout = stdout.decode("UTF-8") +
-    stderr = stderr.decode("UTF-8") +
- +
-    # check response +
-    if stderr != "" or not ("Found Device ID" in stdout): +
-        return False +
- +
-    # upload the bit file +
-    process = Popen(["djtgcfg", "prog", "-d", device_name, +
-                     "-i", chip_index, "-f", description_file], +
-                     stdout=PIPE, stderr=PIPE) +
-    stdout, stderr = process.communicate() +
-    stdout = stdout.decode("UTF-8") +
-    stderr = stderr.decode("UTF-8") +
- +
-    # check response +
-    if stderr != "" or not ("Programming succeeded" in stdout): +
-        return False +
- +
-    return True +
- +
- +
-"""-------------------------------------------------------------------""" +
- +
-</code></WRAP></WRAP>+
 ---- ----
  
 ==== MIDI ==== ==== MIDI ====
-<WRAP group><WRAP half column>+<WRAP group>
 Start raveloxmidi in the background as a subprocess, to be able to communicate using RTPMIDI. Use the following command: Start raveloxmidi in the background as a subprocess, to be able to communicate using RTPMIDI. Use the following command:
 <code>sudo raveloxmidi -c ./MIDI/address.conf</code> <code>sudo raveloxmidi -c ./MIDI/address.conf</code>
 Don't forget to change the path of the configuration file, to a file, which contains the bind address: "0.0.0.0". Don't forget to change the path of the configuration file, to a file, which contains the bind address: "0.0.0.0".
 <code>network.bind_address = 0.0.0.0</code> <code>network.bind_address = 0.0.0.0</code>
-</WRAP><WRAP half column><code python> +</WRAP>
-""" This module contains the functions needed to send MIDI messages """ +
-""" More information:  +
-            https://github.com/ravelox/pimidi/tree/master/python """+
  
-"""-------------------------------------------------------------------""" +<WRAP group>
-""" GLOBAL """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-import sys  # interactions with the interpreter +
-import socket   # socket operations +
-import struct   # conversion between Python values and C type data +
-from subprocess import Popen, PIPE # system calls +
-from MIDI.message import *   # import all MIDI messages +
- +
-sock = None +
- +
-"""-------------------------------------------------------------------""" +
-""" INITIALIZATION """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def open(configuration_file="./MIDI/address.conf", localport=5006): +
-    """ +
-        start ravelomidi +
-         +
-        parameters: - configuration file path +
-                    - local port +
-    """ +
-    # start raveloxmidi in the background +
-    Popen(["sudo", "raveloxmidi", "-c", configuration_file]) +
- +
-    global sock # load variable +
- +
-    # local variables +
-    family = None +
-    connect_tuple = None +
- +
-    # defining connection parameters +
-    # code snippet from the RaveloxMIDI examples +
-    if len(sys.argv) == 1: +
-        family = socket.AF_INET +
-        connect_tuple = ("localhost", localport) +
-    else: +
-        details = socket.getaddrinfo( +
-            sys.argv[1], localport, socket.AF_UNSPEC, socket.SOCK_DGRAM) +
-        family = details[0][0] +
-        if family == socket.AF_INET6: +
-            connect_tuple = (sys.argv[1], localport, 0, 0) +
-        else: +
-            connect_tuple = (sys.argv[1], localport) +
- +
-    # connect socket +
-    sock = socket.socket(family, socket.SOCK_DGRAM) +
-    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +
-    sock.connect(connect_tuple) +
-    return +
-</code></WRAP></WRAP> +
- +
-<WRAP group><WRAP half column>+
 While the background process is running, you can send data sockets containing MIDI data. 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]]. While the background process is running, you can send data sockets containing MIDI data. 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]].
  
-Open the drop-downs below for the most important MIDI messages.+See the drop-downs below for the most important MIDI messages.
  
---> MIDI Message Types #+--> MIDI Message Types ^
 <WRAP group> <WRAP group>
 |<100% 10% 10% 50% 10% 10% 10%>| |<100% 10% 10% 50% 10% 10% 10%>|
Line 503: Line 302:
 <-- <--
  
---> MIDI Notes #+<WRAP group><WRAP column half> 
 +--> MIDI Notes ^
 <WRAP group> <WRAP group>
 |<100% 50% 50%>| |<100% 50% 50%>|
Line 521: Line 321:
 </WRAP> </WRAP>
 <-- <--
- +</WRAP><WRAP column half> 
---> MIDI Octaves #+--> MIDI Octaves ^
 <WRAP group> <WRAP group>
 |<100% 20% 10% 70%>| |<100% 20% 10% 70%>|
Line 539: Line 339:
 </WRAP> </WRAP>
 <-- <--
-</WRAP><WRAP half column><code python> +</WRAP></WRAP> 
-"""-------------------------------------------------------------------""" +</WRAP>
-""" SENDING """ +
-"""-------------------------------------------------------------------"""+
  
- +<WRAP group>
-def send(msg_type, channel, data): +
-    """ +
-        send a message +
-         +
-        parameters: - message type: from MIDI.message +
-                    - midi channel: 0-15 +
-                    - data: from MIDI.message +
-    """ +
-    # get data length +
-    length = len(data) +
- +
-    # get message length +
-    byte_count = "B" +
-    for _ in range(length): +
-        byte_count = byte_count + "B" +
- +
-    # construct the message +
-    # pack a binary structure with all the data words +
-    message = None +
-    if length == 1: +
-        message = struct.pack(byte_count, msg_type + channel, data[0]) +
-    elif length == 2: +
-        message = struct.pack(byte_count, msg_type + channel, data[0], data[1]) +
-    elif length == 3: +
-        message = struct.pack(byte_count, msg_type + +
-                              channel, data[0], data[1], data[2]) +
-    elif length == 4: +
-        message = struct.pack(byte_count, msg_type + channel, +
-                              data[0], data[1], data[2], data[3]) +
-    elif length == 5: +
-        message = struct.pack(byte_count, msg_type + channel, +
-                              data[0], data[1], data[2], data[3], data[4]) +
-    elif length == 6: +
-        message = struct.pack(byte_count, msg_type + channel, +
-                              data[0], data[1], data[2], data[3], data[4], data[5]) +
-    else: +
-        message = struct.pack(byte_count, msg_type + channel, +
-                              data[0], data[1], data[2], data[3], data[4], data[5], data[6]) +
- +
-    # send message +
-    sock.send(message) +
-    return +
-</code></WRAP></WRAP> +
- +
-<WRAP group><WRAP half column>+
 When you are finished, kill every process with the name "raveloxmidi" to terminate the connection. When you are finished, kill every process with the name "raveloxmidi" to terminate the connection.
-</WRAP><WRAP half column><code python> +</WRAP> 
-"""-------------------------------------------------------------------""" +----
-""" CLEANUP """ +
-"""-------------------------------------------------------------------"""+
  
 +==== MIDI Controller Script ====
 +<WRAP group>
 +In your wrapper script, define the connections between the devices and other parameters like the frequency and amplitude/offset of the output signals, then import your 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.
  
-def close(): +Start the WaveForms instruments needed in your 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 your script! Also stop raveloxmidi when the script is terminated.)
-    """ +
-        stop ravelomidi +
-    """ +
-    # close socket +
-    sock.close()+
  
-    # find remaining processes +The rest of your 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.
-    process = Popen(["sudo", "ps", "u"], stdout=PIPE, stderr=PIPE) +
-    stdout, _ = process.communicate() +
-    stdout = stdout.decode("UTF-8") +
-    processes = stdout.split("root") +
-    processes.pop(0) +
-    proc_id = [] +
-    for index in range(len(processes)): +
-        processes[index] = processes[index].split() +
-        for property in processes[index]: +
-            if property == "raveloxmidi": +
-                proc_id.append(processes[index][0]) +
-                continue+
  
-    # kill the processes +You might also want to set some visual effects using the RGB LEDs. Use your creativity to modify the colors according to the received data. 
-    success_flag = True +</WRAP>
-    for pid in proc_id: +
-        try: +
-            Popen(["sudo", "kill", "-SIGKILL", pid]) +
-        except: +
-            success_flag = False +
-    return success_flag +
- +
- +
-"""-------------------------------------------------------------------""" +
- +
-</code></WRAP></WRAP> +
----- +
- +
-==== Program Body ==== +
-[[content]]+
 ---- ----
  
Line 645: Line 367:
 <code>sudo python3 midi_controller.py</code> <code>sudo python3 midi_controller.py</code>
 </WRAP><WRAP half column> </WRAP><WRAP half column>
-[[image]]+{{ :test-and-measurement:analog-discovery-pro-3x50:sftp-winscp.png?600 |}}
 </WRAP></WRAP> </WRAP></WRAP>