Differences
This shows you the differences between two versions of the page.
| test-and-measurement:analog-discovery-pro-3x50:midi [2022/04/04 10:51] – [Driver Design] Álmos Veres-Vitályos | test-and-measurement:analog-discovery-pro-3x50:midi [2022/10/04 11:38] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 171: | Line 171: | ||
| <WRAP group> | <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: | * 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: | ||
| Line 179: | Line 180: | ||
| </ | </ | ||
| - | == Connecting the User Interface == | ||
| <WRAP group> | <WRAP group> | ||
| + | === Connecting the User Interface === | ||
| Connect the output signals of the user interface to the FPGA board, but keep track of the connections, | Connect the output signals of the user interface to the FPGA board, but keep track of the connections, | ||
| 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 group> | ||
| 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. | ||
| </ | </ | ||
| Line 260: | Line 259: | ||
| ---- | ---- | ||
| ==== Configuring the FPGA ==== | ==== Configuring the FPGA ==== | ||
| - | <WRAP group>< | + | <WRAP group> |
| To configure the FPGA board connected to the ADP3450, with the bit file created with Multisim, the [[https:// | To configure the FPGA board connected to the ADP3450, with the bit file created with Multisim, the [[https:// | ||
| Line 267: | Line 266: | ||
| and | and | ||
| < | < | ||
| + | |||
| **Note: **// | **Note: **// | ||
| - | </ | ||
| - | """ | ||
| - | from subprocess import Popen, PIPE | + | <WRAP center round tip 80%> |
| - | + | In your script, check for the responses of the commands to determine | |
| - | """ | + | </ |
| - | """ | + | |
| - | """ | + | |
| - | + | ||
| - | + | ||
| - | def configure(description_file=" | + | |
| - | """ | + | |
| - | configures | + | |
| - | + | ||
| - | parameters: - bit file | + | |
| - | - device name | + | |
| - | - chip index | + | |
| - | + | ||
| - | returns: | + | |
| - | """ | + | |
| - | # initialize | + | |
| - | process = Popen([" | + | |
| - | | + | |
| - | stdout, stderr = process.communicate() | + | |
| - | stdout = stdout.decode(" | + | |
| - | stderr = stderr.decode(" | + | |
| - | + | ||
| - | # check response | + | |
| - | | + | |
| - | return False | + | |
| - | + | ||
| - | # upload | + | |
| - | process = Popen([" | + | |
| - | " | + | |
| - | | + | |
| - | stdout, stderr = process.communicate() | + | |
| - | stdout = stdout.decode(" | + | |
| - | stderr = stderr.decode(" | + | |
| - | + | ||
| - | # check response | + | |
| - | if stderr != "" | + | |
| - | | + | |
| - | + | ||
| - | return True | + | |
| - | + | ||
| - | + | ||
| - | """ | + | |
| - | + | ||
| - | </ | + | |
| ---- | ---- | ||
| ==== MIDI ==== | ==== MIDI ==== | ||
| - | <WRAP group>< | + | <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: | ||
| < | < | ||
| Don't forget to change the path of the configuration file, to a file, which contains the bind address: " | Don't forget to change the path of the configuration file, to a file, which contains the bind address: " | ||
| < | < | ||
| - | </ | + | </ |
| - | """ | + | |
| - | """ | + | |
| - | https:// | + | |
| - | """ | + | <WRAP group> |
| - | """ | + | |
| - | """ | + | |
| - | + | ||
| - | + | ||
| - | import sys # interactions with the interpreter | + | |
| - | import socket | + | |
| - | import struct | + | |
| - | from subprocess import Popen, PIPE # system calls | + | |
| - | from MIDI.message import * # import all MIDI messages | + | |
| - | + | ||
| - | sock = None | + | |
| - | + | ||
| - | """ | + | |
| - | """ | + | |
| - | """ | + | |
| - | + | ||
| - | + | ||
| - | def open(configuration_file=" | + | |
| - | """ | + | |
| - | start ravelomidi | + | |
| - | + | ||
| - | parameters: - configuration file path | + | |
| - | - local port | + | |
| - | """ | + | |
| - | # start raveloxmidi in the background | + | |
| - | Popen([" | + | |
| - | + | ||
| - | 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 = (" | + | |
| - | else: | + | |
| - | details = socket.getaddrinfo( | + | |
| - | sys.argv[1], | + | |
| - | family = details[0][0] | + | |
| - | if family == socket.AF_INET6: | + | |
| - | connect_tuple = (sys.argv[1], | + | |
| - | else: | + | |
| - | connect_tuple = (sys.argv[1], | + | |
| - | + | ||
| - | # connect socket | + | |
| - | sock = socket.socket(family, | + | |
| - | sock.setsockopt(socket.SOL_SOCKET, | + | |
| - | sock.connect(connect_tuple) | + | |
| - | return | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP group>< | + | |
| 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:// | 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:// | ||
| - | 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 405: | Line 302: | ||
| <-- | <-- | ||
| - | --> MIDI Notes # | + | <WRAP group>< |
| + | --> MIDI Notes ^ | ||
| <WRAP group> | <WRAP group> | ||
| |<100% 50% 50%>| | |<100% 50% 50%>| | ||
| Line 423: | Line 321: | ||
| </ | </ | ||
| <-- | <-- | ||
| - | + | </ | |
| - | --> MIDI Octaves | + | --> MIDI Octaves |
| <WRAP group> | <WRAP group> | ||
| |<100% 20% 10% 70%>| | |<100% 20% 10% 70%>| | ||
| Line 441: | Line 339: | ||
| </ | </ | ||
| <-- | <-- | ||
| - | </ | + | </ |
| - | """ | + | </WRAP> |
| - | """ | + | |
| - | """ | + | |
| - | + | <WRAP group> | |
| - | def send(msg_type, | + | |
| - | """ | + | |
| - | 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 = " | + | |
| - | for _ in range(length): | + | |
| - | byte_count = byte_count + " | + | |
| - | + | ||
| - | # construct the message | + | |
| - | # pack a binary structure with all the data words | + | |
| - | message = None | + | |
| - | if length == 1: | + | |
| - | message = struct.pack(byte_count, | + | |
| - | elif length == 2: | + | |
| - | message = struct.pack(byte_count, | + | |
| - | elif length == 3: | + | |
| - | message = struct.pack(byte_count, | + | |
| - | channel, data[0], data[1], data[2]) | + | |
| - | elif length == 4: | + | |
| - | message = struct.pack(byte_count, | + | |
| - | data[0], data[1], data[2], data[3]) | + | |
| - | elif length == 5: | + | |
| - | message = struct.pack(byte_count, | + | |
| - | data[0], data[1], data[2], data[3], data[4]) | + | |
| - | elif length == 6: | + | |
| - | message = struct.pack(byte_count, | + | |
| - | data[0], data[1], data[2], data[3], data[4], data[5]) | + | |
| - | else: | + | |
| - | message = struct.pack(byte_count, | + | |
| - | data[0], data[1], data[2], data[3], data[4], data[5], data[6]) | + | |
| - | + | ||
| - | # send message | + | |
| - | sock.send(message) | + | |
| - | return | + | |
| - | </ | + | |
| - | + | ||
| - | <WRAP group>< | + | |
| When you are finished, kill every process with the name " | When you are finished, kill every process with the name " | ||
| - | </WRAP>< | + | </ |
| - | """ | + | ---- |
| - | """ | + | |
| - | """ | + | |
| + | ==== MIDI Controller Script ==== | ||
| + | <WRAP group> | ||
| + | In your wrapper script, define the connections between the devices and other parameters like the frequency and amplitude/ | ||
| - | 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.) |
| - | """ | + | |
| - | | + | |
| - | """ | + | |
| - | # 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 |
| - | process = Popen([" | + | |
| - | stdout, _ = process.communicate() | + | |
| - | stdout = stdout.decode(" | + | |
| - | processes = stdout.split(" | + | |
| - | processes.pop(0) | + | |
| - | proc_id = [] | + | |
| - | for index in range(len(processes)): | + | |
| - | processes[index] = processes[index].split() | + | |
| - | for property in processes[index]: | + | |
| - | | + | |
| - | 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 | + | </ |
| - | for pid in proc_id: | + | |
| - | try: | + | |
| - | Popen([" | + | |
| - | except: | + | |
| - | success_flag = False | + | |
| - | return success_flag | + | |
| - | + | ||
| - | + | ||
| - | """ | + | |
| - | + | ||
| - | </ | + | |
| - | ---- | + | |
| - | + | ||
| - | ==== Program Body ==== | + | |
| - | [[content]] | + | |
| ---- | ---- | ||
| Line 547: | Line 367: | ||
| < | < | ||
| </ | </ | ||
| - | [[image]] | + | {{ : |
| </ | </ | ||

