├── .gitignore ├── html ├── diagram.png ├── main.css └── index.html ├── Makefile ├── arduino ├── ardumidi │ ├── examples │ │ ├── from_alsa_to_arduino │ │ │ └── from_alsa_to_arduino.pde │ │ ├── ardumidi_test │ │ │ └── ardumidi_test.pde │ │ └── ardumidi_test_all_instruments │ │ │ └── ardumidi_test_all_instruments.pde │ ├── ardumidi.h │ └── ardumidi.cpp ├── README └── MIDI │ └── examples │ └── MIDI_Basic_IO_ttymidi │ └── MIDI_Basic_IO_ttymidi.pde ├── src ├── mod-semaphore.h └── ttymidi.c ├── README └── test └── serial-fuzzy-write.py /.gitignore: -------------------------------------------------------------------------------- 1 | ttymidi 2 | ttymidi.so 3 | 4 | -------------------------------------------------------------------------------- /html/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mod-audio/mod-ttymidi/HEAD/html/diagram.png -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | DESTDIR ?= 3 | PREFIX = /usr/local 4 | 5 | CC ?= gcc 6 | CFLAGS += -std=gnu99 -Wall -Wextra -Wshadow -Werror -fvisibility=hidden 7 | LDFLAGS += -Wl,--no-undefined 8 | 9 | ifeq ($(DEBUG),1) 10 | CFLAGS += -O0 -g -DDEBUG 11 | else 12 | CFLAGS += -O2 -DNDEBUG 13 | endif 14 | 15 | all: ttymidi ttymidi.so 16 | 17 | debug: 18 | $(MAKE) DEBUG=1 19 | 20 | ttymidi: src/ttymidi.c src/mod-semaphore.h 21 | $(CC) $< $(CFLAGS) $(shell pkg-config --cflags --libs jack) $(LDFLAGS) -lpthread -o $@ 22 | 23 | ttymidi.so: src/ttymidi.c src/mod-semaphore.h 24 | $(CC) $< $(CFLAGS) $(shell pkg-config --cflags --libs jack) $(LDFLAGS) -fPIC -lpthread -shared -o $@ 25 | 26 | install: ttymidi ttymidi.so 27 | install -m 755 ttymidi $(DESTDIR)$(PREFIX)/bin/ 28 | install -m 755 ttymidi.so $(DESTDIR)$(shell pkg-config --variable=libdir jack)/jack/ 29 | 30 | clean: 31 | rm -f ttymidi ttymidi.so 32 | 33 | uninstall: 34 | rm $(DESTDIR)$(PREFIX)/bin/ttymidi 35 | rm $(DESTDIR)$(shell pkg-config --variable=libdir jack)/jack/ttymidi.so 36 | -------------------------------------------------------------------------------- /arduino/ardumidi/examples/from_alsa_to_arduino/from_alsa_to_arduino.pde: -------------------------------------------------------------------------------- 1 | /* 2 | This file is public domain. Use it as you like. 3 | 4 | This is example program to showcase reading MIDI 5 | coming from Alsa sequencer with Arduino. We check 6 | whether there is MIDI message waiting at the serial 7 | buffer, then read the message and send it back 8 | to ttyMidi (if it happened to be either noteon or 9 | noteof message) 10 | 11 | */ 12 | 13 | #include 14 | 15 | void setup() 16 | { 17 | Serial.begin(115200); 18 | } 19 | 20 | int counter = 0; 21 | 22 | void loop() 23 | { 24 | while (midi_message_available()>0) { 25 | MidiMessage m = read_midi_message(); 26 | if (m.command == MIDI_NOTE_ON) { 27 | midi_note_on(m.channel, m.param1, m.param1); 28 | } 29 | else if (m.command == MIDI_NOTE_OFF) { 30 | midi_note_off(m.channel, m.param1, m.param2); 31 | } 32 | else if (m.command == MIDI_PITCH_BEND) { 33 | midi_pitch_bend(m.channel,get_pitch_bend(m)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /arduino/README: -------------------------------------------------------------------------------- 1 | NOTE that ardumidi library is becoming obsolete, but will still be present 2 | in our distribution package as an example how to communicate with ttymidi at 3 | computer end. For those of you who are not interested in such an excursion, 4 | we advice downloading Arduino MIDI library, which is now (almost (read further)) 5 | compatible with ttymidi: 6 | 7 | http://www.arduino.cc/playground/Main/MIDILibrary 8 | 9 | When using MIDI library, please check our example patch as there is one small 10 | detail that you have to remember when using MIDI library together with 11 | ttymidi (you have to call Serial.begin(115200) after MIDI.begin(int) command. 12 | You can copy the example patch demonstrating this like so: 13 | 14 | cp -r MIDI /path/to/arduino/sketchbook/libraries/ 15 | 16 | You should install MIDILibrary before doing this. Example patch will appear 17 | to examples dialog of Arduino IDE. 18 | 19 | If you still want to install the Arduino ttymidi library (which I so aptly 20 | named "ardumidi"), just copy the ttymidi folder into the Arduino libraries folder: 21 | 22 | cp -r ardumidi /path/to/arduino/sketchbook/libraries/ 23 | 24 | This library comes with example sketches. Please take 25 | a look at them, as they contains information on how to use the library. 26 | -------------------------------------------------------------------------------- /arduino/MIDI/examples/MIDI_Basic_IO_ttymidi/MIDI_Basic_IO_ttymidi.pde: -------------------------------------------------------------------------------- 1 | #include 2 | /* 3 | This example is a modified version of Basic I/O MIDI tutorial 4 | by Franky. It tries to demonstrate how to use Arduino MIDI 5 | library together with ttyMIDI. You will need to install 6 | Arduino MIDI library for this to work: 7 | 8 | http://www.arduino.cc/playground/Main/MIDILibrary 9 | 10 | All example patches shipped with the Arduino MIDI library 11 | will work with ttyMIDI with same change (if they wont, please 12 | report a bug). 13 | 14 | */ 15 | 16 | #define LED 13 // LED pin on Arduino board 17 | 18 | void setup() { 19 | pinMode(LED, OUTPUT); 20 | MIDI.begin(4); // Launch MIDI with default options 21 | // input channel is set to 4 22 | 23 | /* Now the problem is that serial port at the computer is only 24 | able to read serial data at 'standard' baud rates. MIDI baud rate 25 | that Arduino MIDI library is using, is not one of these. 26 | One way of fixing this is to edit line 54 of file MIDI.h and set 27 | Arduino MIDI library to use some other baud rate. 28 | 29 | However, one can just simply change the baud rate 30 | of serial line to some standard baud rate by calling Serial.begin(int) 31 | immediately after calling MIDI.begin(int). Here we set the speed 32 | to default baud rate of ttymidi, 115200! This is all you need to 33 | remember to do if you want to read the data with ttymidi running 34 | on your computer. 35 | */ 36 | Serial.begin(115200); 37 | } 38 | 39 | void loop() { 40 | if (MIDI.read()) { 41 | digitalWrite(LED,HIGH); // Blink the LED 42 | MIDI.sendNoteOn(42,127,1); // Send a Note (pitch 42, velo 127 on channel 1) 43 | delay(1000); // Wait for a second 44 | MIDI.sendNoteOff(42,0,1); // Stop the note 45 | digitalWrite(LED,LOW); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /arduino/ardumidi/examples/ardumidi_test/ardumidi_test.pde: -------------------------------------------------------------------------------- 1 | /* 2 | This file is public domain. Use it as you like. 3 | */ 4 | 5 | #include 6 | 7 | int ledPin = 13; 8 | int note_on = 0; 9 | 10 | void setup() 11 | { 12 | Serial.begin(115200); 13 | pinMode(ledPin, OUTPUT); 14 | } 15 | 16 | void loop() 17 | { 18 | 19 | if (!note_on) 20 | { 21 | // play a Cminor chord on channel 0, at maximum volume (127) 22 | midi_note_on(0, MIDI_C, 127); 23 | midi_note_on(0, MIDI_E + MIDI_FLAT + MIDI_OCTAVE, 127); 24 | midi_note_on(0, MIDI_G + MIDI_OCTAVE, 127); 25 | 26 | // The MIDI_* macros were created to make your life easier, but you 27 | // don't have to use them. You may enter numbers instead if you know 28 | // what you're doing. 29 | 30 | note_on = 1; 31 | digitalWrite(ledPin, HIGH); 32 | } 33 | 34 | else 35 | { 36 | // stop the Cminor chord 37 | midi_note_off(0, MIDI_C, 127); 38 | midi_note_off(0, MIDI_E + MIDI_FLAT + MIDI_OCTAVE, 127); 39 | midi_note_off(0, MIDI_G + MIDI_OCTAVE, 127); 40 | 41 | note_on = 0; 42 | digitalWrite(ledPin, LOW); 43 | } 44 | 45 | delay(500); 46 | } 47 | 48 | // Available commands: 49 | // 50 | // Start/stop playing a certain note: 51 | // midi_note_on(byte channel, byte key, byte velocity); 52 | // midi_note_off(byte channel, byte key, byte velocity); 53 | // 54 | // Change pressure of specific keys: 55 | // midi_key_pressure(byte channel, byte key, byte value); 56 | // 57 | // Change controller value (used for knobs, etc): 58 | // midi_controller_change(byte channel, byte controller, byte value); 59 | // 60 | // Change "program" (change the instrument): 61 | // midi_program_change(byte channel, byte program); 62 | // 63 | // Change key pressure of entire channels: 64 | // midi_channel_pressure(byte channel, byte value); 65 | // 66 | // Change pitch-bend wheel: 67 | // midi_pitch_bend(byte channel, int value); 68 | // 69 | // Send a comment: 70 | // midi_comment(char* str); 71 | // 72 | // Send a series of bytes (to be interpreted by another program): 73 | // midi_printbytes(char* bytes, int len); 74 | // 75 | // Parameters: 76 | // channel an integer from 0 to 15 77 | // pitch-bend value an integer from 0 to 16383 78 | // all other values an integer from 0 to 127 79 | // 80 | 81 | -------------------------------------------------------------------------------- /src/mod-semaphore.h: -------------------------------------------------------------------------------- 1 | 2 | #define MOD_SEMAPHORE_USE_FUTEX 3 | 4 | #ifdef MOD_SEMAPHORE_USE_FUTEX 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #else 11 | #include 12 | #endif 13 | 14 | #ifdef MOD_SEMAPHORE_USE_FUTEX 15 | /* --------------------------------------------------------------------- */ 16 | // Linux futex 17 | 18 | typedef struct _sem_t { 19 | int value, pshared; 20 | } sem_t; 21 | 22 | static inline 23 | void sem_init(sem_t* sem, int pshared, int value) 24 | { 25 | sem->value = value; 26 | sem->pshared = pshared; 27 | } 28 | 29 | static inline 30 | void sem_destroy(sem_t* sem) 31 | { 32 | // unused 33 | return; (void)sem; 34 | } 35 | 36 | static inline 37 | void sem_post(sem_t* sem) 38 | { 39 | if (! __sync_bool_compare_and_swap(&sem->value, 0, 1)) { 40 | // already unlocked, do not wake futex 41 | return; 42 | } 43 | 44 | syscall(__NR_futex, &sem->value, sem->pshared ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0); 45 | return; 46 | } 47 | 48 | static inline 49 | int sem_wait(sem_t* sem) 50 | { 51 | for (;;) 52 | { 53 | if (__sync_bool_compare_and_swap(&sem->value, 1, 0)) 54 | return 0; 55 | 56 | if (syscall(__NR_futex, &sem->value, sem->pshared ? FUTEX_WAIT : FUTEX_WAIT_PRIVATE, 0, NULL, NULL, 0) != 0 && errno != EWOULDBLOCK) 57 | return 1; 58 | } 59 | } 60 | // 0 = ok 61 | static inline 62 | int sem_timedwait_secs(sem_t* sem, int secs) 63 | { 64 | const struct timespec timeout = { secs, 0 }; 65 | 66 | for (;;) 67 | { 68 | if (__sync_bool_compare_and_swap(&sem->value, 1, 0)) 69 | return 0; 70 | 71 | if (syscall(__NR_futex, &sem->value, sem->pshared ? FUTEX_WAIT : FUTEX_WAIT_PRIVATE, 0, &timeout, NULL, 0) != 0 && errno != EWOULDBLOCK) 72 | return 1; 73 | } 74 | } 75 | #else 76 | /* --------------------------------------------------------------------- */ 77 | // POSIX Semaphore 78 | 79 | static inline 80 | int sem_timedwait_secs(sem_t* sem, int secs) 81 | { 82 | struct timespec timeout; 83 | clock_gettime(CLOCK_REALTIME, &timeout); 84 | timeout.tv_sec += secs; 85 | return sem_timedwait(sem, &timeout); 86 | } 87 | #endif 88 | -------------------------------------------------------------------------------- /arduino/ardumidi/examples/ardumidi_test_all_instruments/ardumidi_test_all_instruments.pde: -------------------------------------------------------------------------------- 1 | /* 2 | This file is public domain. Use it as you like. 3 | */ 4 | 5 | #include 6 | 7 | int ledPin = 13; 8 | int note_on = 0; 9 | int voice = 0; 10 | 11 | void setup() 12 | { 13 | Serial.begin(115200); 14 | pinMode(ledPin, OUTPUT); 15 | } 16 | 17 | void loop() 18 | { 19 | 20 | if (!note_on) 21 | { 22 | // play a Cminor chord on channel 0, at maximum volume (127) 23 | midi_note_on(0, MIDI_C, 127); 24 | midi_note_on(0, MIDI_E + MIDI_FLAT + MIDI_OCTAVE, 127); 25 | midi_note_on(0, MIDI_G + MIDI_OCTAVE, 127); 26 | 27 | // The MIDI_* macros were created to make your life easier, but you 28 | // don't have to use them. You may enter numbers instead if you know 29 | // what you're doing. 30 | 31 | note_on = 1; 32 | digitalWrite(ledPin, HIGH); 33 | } 34 | 35 | else 36 | { 37 | // stop the Cminor chord 38 | midi_note_off(0, MIDI_C, 127); 39 | midi_note_off(0, MIDI_E + MIDI_FLAT + MIDI_OCTAVE, 127); 40 | midi_note_off(0, MIDI_G + MIDI_OCTAVE, 127); 41 | 42 | // go to next instrument 43 | voice++; 44 | if (voice > 255) voice = 0; 45 | midi_program_change(0, voice); 46 | 47 | note_on = 0; 48 | digitalWrite(ledPin, LOW); 49 | } 50 | 51 | delay(500); 52 | } 53 | 54 | // Available commands: 55 | // 56 | // Start/stop playing a certain note: 57 | // midi_note_on(byte channel, byte key, byte velocity); 58 | // midi_note_off(byte channel, byte key, byte velocity); 59 | // 60 | // Change pressure of specific keys: 61 | // midi_key_pressure(byte channel, byte key, byte value); 62 | // 63 | // Change controller value (used for knobs, etc): 64 | // midi_controller_change(byte channel, byte controller, byte value); 65 | // 66 | // Change "program" (change the instrument): 67 | // midi_program_change(byte channel, byte program); 68 | // 69 | // Change key pressure of entire channels: 70 | // midi_channel_pressure(byte channel, byte value); 71 | // 72 | // Change pitch-bend wheel: 73 | // midi_pitch_bend(byte channel, int value); 74 | // 75 | // Send a comment: 76 | // midi_comment(char* str); 77 | // 78 | // Send a series of bytes (to be interpreted by another program): 79 | // midi_printbytes(char* bytes, int len); 80 | // 81 | // Parameters: 82 | // channel an integer from 0 to 15 83 | // pitch-bend value an integer from 0 to 16383 84 | // all other values an integer from 0 to 127 85 | // 86 | 87 | -------------------------------------------------------------------------------- /arduino/ardumidi/ardumidi.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of ttymidi. 3 | 4 | ttymidi is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | ttymidi is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with ttymidi. If not, see . 16 | */ 17 | // file version 0.60 18 | 19 | #ifndef ardumidi_h 20 | #define ardumidi_h 21 | 22 | #include "WProgram.h" 23 | 24 | // MIDI notes 25 | #define MIDI_C0 0 26 | #define MIDI_D0 2 27 | #define MIDI_E0 4 28 | #define MIDI_F0 5 29 | #define MIDI_G0 7 30 | #define MIDI_A0 9 31 | #define MIDI_B0 11 32 | #define MIDI_C 60 33 | #define MIDI_D 62 34 | #define MIDI_E 64 35 | #define MIDI_F 65 36 | #define MIDI_G 67 37 | #define MIDI_A 69 38 | #define MIDI_B 71 39 | #define MIDI_SHARP 1 40 | #define MIDI_FLAT -1 41 | #define MIDI_OCTAVE 12 42 | 43 | // MIDI out 44 | #define MIDI_NOTE_OFF 0x80 45 | #define MIDI_NOTE_ON 0x90 46 | #define MIDI_PRESSURE 0xA0 47 | #define MIDI_CONTROLLER_CHANGE 0xB0 48 | #define MIDI_PROGRAM_CHANGE 0xC0 49 | #define MIDI_CHANNEL_PRESSURE 0xD0 50 | #define MIDI_PITCH_BEND 0xE0 51 | 52 | struct MidiMessage { 53 | byte command; 54 | byte channel; 55 | byte param1; 56 | byte param2; 57 | }; 58 | 59 | // MIDI in 60 | void midi_note_off(byte channel, byte key, byte velocity); 61 | void midi_note_on(byte channel, byte key, byte velocity); 62 | void midi_key_pressure(byte channel, byte key, byte value); 63 | void midi_controller_change(byte channel, byte control, byte value); 64 | void midi_program_change(byte channel, byte program); 65 | void midi_channel_pressure(byte channel, byte value); 66 | void midi_pitch_bend(byte channel, int value); 67 | void midi_command(byte command, byte channel, byte param1, byte param2); 68 | void midi_command_short(byte command, byte channel, byte param1); 69 | 70 | // MIDI out 71 | int midi_message_available(); 72 | MidiMessage read_midi_message(); 73 | int get_pitch_bend(MidiMessage msg); 74 | 75 | // Other 76 | void midi_print(char* msg, int len); 77 | void midi_comment(char* msg); 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ttyMIDI is a GPL-licensed program that allows external serial devices to 2 | interface with JACK MIDI clients. 3 | 4 | 5 | COMPILATION 6 | 7 | The ttyMIDI source code is comprised of a single C file. To compile it, just 8 | run the following command: 9 | 10 | make 11 | 12 | This program depends on JACK, so you should have the development headers 13 | for that installed. In Debian or Ubuntu, you can install it by running: 14 | 15 | apt-get install libjack-jackd2-dev 16 | 17 | After you compile ttyMIDI, you may wish to copy it to /usr/bin for easy 18 | access. This can be done simply by following command: 19 | 20 | sudo make install 21 | 22 | USAGE 23 | 24 | First, you need an external device that can send MIDI commands through the 25 | serial port. To find out more about programming an external device, read the 26 | TTYMIDI SPECIFICATION section. If you are using an Arduino board 27 | (http://www.arduino.cc), read the instructions under the arduino folder. Once 28 | your device is programmed and connected to your PC's serial port, follow the 29 | instructions below. 30 | 31 | To connect to ttyS0 at 2400bps: 32 | 33 | ttymidi -s /dev/ttyS0 -b 2400 34 | 35 | To connect to ttyUSB port at default speed (115200bps) and display information 36 | about incoming MIDI events: 37 | 38 | ttymidi -s /dev/ttyUSB0 -v 39 | 40 | ttyMIDI also creates a JACK MIDI input port that feeds incoming MIDI events 41 | back to the serial port. 42 | 43 | If you would like to use a GUI to connect your MIDI clients, there are many 44 | available. One of my favorites is qjackctl. 45 | 46 | 47 | TTYMIDI SPECIFICATION 48 | 49 | The message format expected by ttyMIDI is based on what I could gather about the 50 | MIDI specification online. I tried to make it as similar as possible to the 51 | specification, but some differences exist. The good news is that as long as you 52 | follow the specification described below, everything should work. 53 | 54 | Every MIDI command is sent through the serial port as 3 bytes. The first byte 55 | contains the command type and channel. After that, 2 parameter bytes are 56 | transmitted. To simplify the decoding process, ttyMIDI does not support 57 | "running status", and it also forces every command into 3 bytes. So even 58 | commands which only have 1 parameter must transmit byte #3 (transmitting a 0 in 59 | this case). This is described in more details in the table below: 60 | 61 | byte1 byte2 byte3 Command name 62 | 63 | 0x80-0x8F Key # (0-127) Off Velocity (0-127) Note OFF 64 | 0x90-0x90 Key # (0-127) On Velocity (0-127) Note ON 65 | 0xA0-0xA0 Key # (0-127) Pressure (0-127) Poly Key Pressure 66 | 0xB0-0xB0 Control # (0-127) Control Value (0-127) Control Change 67 | 0xC0-0xC0 Program # (0-127) Not Used (send 0) Program Change 68 | 0xD0-0xD0 Pressure Value (0-127) Not Used (send 0) Mono Key Pressure (Channel Pressure) 69 | 0xE0-0xE0 Range LSB (0-127) Range MSB (0-127) Pitch Bend 70 | 71 | Not implemented: 72 | 0xF0-0xF0 Manufacturer's ID Model ID System 73 | 74 | Byte #1 is given as COMMAND + CHANNEL. So, for example, 0xE3 is the Pitch Bend 75 | command (0xE0) for channel 4 (0x03). 76 | 77 | -------------------------------------------------------------------------------- /html/main.css: -------------------------------------------------------------------------------- 1 | * 2 | { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | a 8 | { 9 | text-decoration: none; 10 | color: #CE3020; 11 | border-bottom: 1px solid #ddd; 12 | } 13 | 14 | a:hover 15 | { 16 | text-decoration: none; 17 | color: #CE3020; 18 | border-bottom: 1px solid #CE3020; 19 | } 20 | 21 | p 22 | { 23 | line-height: 1.75; 24 | margin: 0.5em 0 1em 0; 25 | } 26 | 27 | .sectiontext 28 | { 29 | } 30 | 31 | .title 32 | { 33 | margin-left: -4em; 34 | padding-left: 3.5em; 35 | margin-bottom: 4em; 36 | border-left: 0.5em solid #CE3020; 37 | } 38 | 39 | .title .caption 40 | { 41 | color: #aaa; 42 | font-size: 1.25em; 43 | line-height: 1.66em; 44 | } 45 | 46 | h1, h2, h3, h4, h5, h6 47 | { 48 | font-weight: normal; 49 | padding: 0; 50 | margin: 0; 51 | } 52 | 53 | h1 54 | { 55 | color: #aaa; 56 | font-size: 1.5em; 57 | line-height: 1.66em; 58 | } 59 | 60 | h2 61 | { 62 | color: #888; 63 | text-transform: uppercase; 64 | font-size: 1em; 65 | width: 10em; 66 | margin: 4em auto 0.25em 0; 67 | padding: 1.5em 0 0.5em 0; 68 | border-bottom: 0.25em solid #ddd; 69 | } 70 | 71 | h3 72 | { 73 | color: #666; 74 | background-color: #fafafa; 75 | border-left: 0.25em solid #CE3020; 76 | font-weight: bold; 77 | font-size: 1em; 78 | line-height: 1.75em; 79 | margin: 2em 0 0 -1.25em; 80 | padding: 1em .5em .25em 1em; 81 | } 82 | 83 | html, 84 | body 85 | { 86 | font-family: sans-serif; 87 | font-size: 95%; 88 | color: #222; 89 | min-height: 100%; 90 | background-color: #ddd; 91 | } 92 | 93 | .mainwrapper 94 | { 95 | padding: 4em 8em 8em 4em; 96 | background-color: #fff; 97 | width: 36em; 98 | } 99 | 100 | strong 101 | { 102 | color: #444; 103 | } 104 | 105 | ul 106 | { 107 | padding-left: 1em; 108 | } 109 | 110 | .nobullets li 111 | { 112 | display: block; 113 | } 114 | 115 | .nobullets 116 | { 117 | padding-left: 0; 118 | } 119 | 120 | li 121 | { 122 | line-height: 1.5em; 123 | } 124 | 125 | dl li 126 | { 127 | display: block; 128 | } 129 | 130 | dt 131 | { 132 | display: block; 133 | padding: 0; 134 | margin: 1em 0 0 0; 135 | color: #333; 136 | font-weight: bold; 137 | } 138 | 139 | pre 140 | { 141 | display: block; 142 | border: 1px solid #ddd; 143 | margin: 1em 0 1em 1em; 144 | padding: 1em; 145 | font-size: 0.90em; 146 | line-height: 1.67em; 147 | max-height: 20em; 148 | max-width: 40em; 149 | overflow: auto; 150 | background-color: #fafafa; 151 | } 152 | 153 | code 154 | { 155 | border: 1px solid #ddd; 156 | font-size: 0.90em; 157 | background-color: #fafafa; 158 | } 159 | 160 | table 161 | { 162 | border-collapse: collapse; 163 | margin: 0; 164 | } 165 | 166 | th 167 | { 168 | vertical-align: top; 169 | text-align: left; 170 | margin: 0; 171 | font-weight: normal; 172 | padding: .5em; 173 | color: #444; 174 | } 175 | 176 | td 177 | { 178 | vertical-align: top; 179 | padding: .5em; 180 | } 181 | 182 | th.leftheader 183 | { 184 | text-align: right; 185 | } 186 | 187 | td.hex 188 | { 189 | font-family: monospace; 190 | } 191 | 192 | img 193 | { 194 | border: none; 195 | } 196 | 197 | .return 198 | { 199 | margin-top: 4em; 200 | margin-left: -4em; 201 | } 202 | 203 | .return a 204 | { 205 | background-color: #eee; 206 | padding: 0 3em 0 0.5em; 207 | color: #444; 208 | border-bottom: 1px solid #ddd; 209 | border-right: 1px solid #ddd; 210 | } 211 | 212 | .return a:before 213 | { 214 | content: "\21A9"; 215 | padding-right: 0.5em; 216 | font-family: "Lucida Sans Unicode", "Lucida Grande", "DejaVu Sans", sans-serif; 217 | } 218 | 219 | .return a:hover 220 | { 221 | background-color: #CE3020; 222 | border-color: #9E372C; 223 | color: #fff; 224 | } 225 | -------------------------------------------------------------------------------- /arduino/ardumidi/ardumidi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of ttymidi. 3 | 4 | ttymidi is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | ttymidi is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with ttymidi. If not, see . 16 | */ 17 | // file version 0.60 18 | 19 | #include "WProgram.h" 20 | #include "HardwareSerial.h" 21 | #include "ardumidi.h" 22 | 23 | void midi_note_off(byte channel, byte key, byte velocity) 24 | { 25 | midi_command(0x80, channel, key, velocity); 26 | } 27 | 28 | void midi_note_on(byte channel, byte key, byte velocity) 29 | { 30 | midi_command(0x90, channel, key, velocity); 31 | } 32 | 33 | void midi_key_pressure(byte channel, byte key, byte value) 34 | { 35 | midi_command(0xA0, channel, key, value); 36 | } 37 | 38 | void midi_controller_change(byte channel, byte control, byte value) 39 | { 40 | midi_command(0xB0, channel, control, value); 41 | } 42 | 43 | void midi_program_change(byte channel, byte program) 44 | { 45 | midi_command_short(0xC0, channel, program); 46 | } 47 | 48 | void midi_channel_pressure(byte channel, byte value) 49 | { 50 | midi_command_short(0xD0, channel, value); 51 | } 52 | 53 | void midi_pitch_bend(byte channel, int value) 54 | { 55 | midi_command(0xE0, channel, value & 0x7F, value >> 7); 56 | } 57 | 58 | void midi_command(byte command, byte channel, byte param1, byte param2) 59 | { 60 | Serial.print(command | (channel & 0x0F), BYTE); 61 | Serial.print(param1 & 0x7F, BYTE); 62 | Serial.print(param2 & 0x7F, BYTE); 63 | } 64 | 65 | void midi_command_short(byte command, byte channel, byte param1) 66 | { 67 | Serial.print(command | (channel & 0x0F), BYTE); 68 | Serial.print(param1 & 0x7F, BYTE); 69 | } 70 | 71 | void midi_print(char* msg, int len) 72 | { 73 | Serial.print(0xFF, BYTE); 74 | Serial.print(0x00, BYTE); 75 | Serial.print(0x00, BYTE); 76 | Serial.print(len , BYTE); 77 | Serial.print(msg); 78 | } 79 | 80 | void midi_comment(char* msg) 81 | { 82 | int len = 0; 83 | char* ptr = msg; 84 | while (*ptr++) len++; 85 | midi_print(msg, len); 86 | } 87 | 88 | int midi_message_available() { 89 | /* 90 | This bit will check that next bytes to be read would actually 91 | have the midi status bit. If not it will remove uncorrect bytes 92 | from internal buffer 93 | */ 94 | while ((Serial.available() > 0) && ((Serial.peek() & B10000000) != 0x80)) { 95 | Serial.read(); 96 | } 97 | 98 | /* Well we don't exactly know how many commands there might be in the Serial buffer 99 | so we'll just guess it according the type of message that happens to be waiting 100 | in the buffer. At least we get first one right! */ 101 | byte command = Serial.peek() & 11110000; 102 | if (command != MIDI_PROGRAM_CHANGE && command != MIDI_CHANNEL_PRESSURE) { 103 | return (Serial.available()/2); 104 | } 105 | return (Serial.available()/3); 106 | } 107 | 108 | MidiMessage read_midi_message() { 109 | MidiMessage message; 110 | byte midi_status = Serial.read(); 111 | message.command = (midi_status & B11110000); 112 | message.channel = (midi_status & B00001111); 113 | message.param1 = Serial.read(); 114 | if (message.command != MIDI_PROGRAM_CHANGE && message.command != MIDI_CHANNEL_PRESSURE) { 115 | message.param2 = Serial.read(); 116 | } 117 | return message; 118 | } 119 | 120 | int get_pitch_bend(MidiMessage m) { 121 | return (m.param1 & 0x7F) + ((m.param2 & 0x7F) << 7); 122 | } 123 | 124 | -------------------------------------------------------------------------------- /test/serial-fuzzy-write.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import serial 4 | import random 5 | import time 6 | 7 | #random.randint(0, 255) 8 | 9 | # Testing with `socat` 10 | s = serial.Serial('/dev/pts/8', 31250, timeout=0.1) 11 | 12 | # noteon = b'\x80\x18\x00' 13 | # noteoff = b'\x90\x18\x64' 14 | # polyphonickeypressure = None 15 | # controlchange = None 16 | # programchange = None 17 | 18 | noteon = bytes([ 19 | int('10000000', 2), 20 | int('00000001', 2), 21 | int('00000001', 2) 22 | ]) 23 | 24 | noteoff = bytes([ 25 | int('10010000', 2), 26 | int('00000001', 2), 27 | int('00000001', 2) 28 | ]) 29 | 30 | poly = bytes([ 31 | int('10100000', 2), 32 | int('00000001', 2), 33 | int('00000001', 2) 34 | ]) 35 | 36 | controlchange = bytes([ 37 | int('10110000', 2), 38 | int('00000001', 2), 39 | int('00000001', 2) 40 | ]) 41 | 42 | programchange = bytes([ 43 | int('11000000', 2), 44 | int('00000001', 2) 45 | ]) 46 | 47 | channelpressure = bytes([ 48 | int('11010000', 2), 49 | int('00000001', 2) 50 | ]) 51 | 52 | pitchbend = bytes([ 53 | int('11100000', 2), 54 | int('00000001', 2), 55 | int('00000001', 2) 56 | ]) 57 | 58 | # same as control change? Yes, special controller numbers. 59 | channelmodemessage = bytes([ 60 | int('10110000', 2), 61 | 120, 62 | 0 63 | ]) 64 | 65 | sysex_begin = bytes([ 66 | int('11110000', 2), 67 | int('01111111', 2), 68 | int('01111111', 2) 69 | ]) 70 | sysex_end = bytes([ 71 | int('11110111', 2) 72 | ]) 73 | 74 | timecode_quarter_frame = bytes([ 75 | int('11110001', 2), 76 | int('01110001', 2) 77 | ]) 78 | 79 | song_position_pointer = bytes([ 80 | int('11110010', 2), 81 | int('01110010', 2), 82 | int('01110010', 2) 83 | ]) 84 | 85 | song_select = bytes([ 86 | int('11110011', 2), 87 | int('01110010', 2) 88 | ]) 89 | 90 | time_clock = bytes([ 91 | int('11111000', 2) 92 | ]) 93 | 94 | time_start = bytes([ 95 | int('11111010', 2) 96 | ]) 97 | 98 | time_continue = bytes([ 99 | int('11111011', 2) 100 | ]) 101 | 102 | time_stop = bytes([ 103 | int('11111100', 2) 104 | ]) 105 | 106 | active_sensing = bytes([ 107 | int('11111110', 2) 108 | ]) 109 | 110 | reset = bytes([ 111 | int('11111111', 2) 112 | ]) 113 | 114 | # s.write(noteon) 115 | # s.write(noteoff) 116 | # s.write(poly) 117 | # s.write(controlchange) 118 | 119 | # s.write(programchange) 120 | # s.write(channelpressure) 121 | # s.write(pitchbend) 122 | # s.write(channelmodemessage) 123 | 124 | # s.write(timecode_quarter_frame) 125 | # s.write(song_position_pointer) 126 | # s.write(song_select) 127 | # s.write(time_clock) 128 | 129 | # s.write(time_start) 130 | # s.write(time_continue) 131 | # s.write(time_stop) 132 | # s.write(active_sensing) 133 | 134 | # s.write(reset) 135 | 136 | # s.write(sysex_begin) 137 | # s.write(sysex_end) 138 | 139 | bootmsg = [ 0xf0, 0x7f, 0x7f, 0x4, 0x1, 0x7f, 0x7f, 0xf7, 0xb0, 0x0, 140 | 0x0, 0xb0, 0x20, 0x0, 0xc0, 0x00, 0x0b0, 0x4d, 0x40, 141 | 0xb0, 0x4c, 0x40, 0xb0, 0x46 , 0x40, 0xb0, 0xa, 0x40, 142 | 0xb0, 0x49, 0x40, 0xb0, 0x4b, 0x40, 0xb0, 0x48 , 0x40, 143 | 0xb0, 0x5b, 0xd, 0xb0, 0x5e, 0x0, 0xb0, 0x4a, 0x40, 0xb0, 144 | 0xa , 0x40, 0xb0, 0x65, 0x0, 0xb0, 0x64, 0x1, 0xb0, 0x6, 145 | 0x40, 0xb0, 0x26 , 0x0, 0xb0, 0x65, 0x7f, 0xb0, 0x64, 146 | 0x7f, 0xb0, 0x7, 0x7f, 0xb0, 0x65 , 0x0, 0xb0, 0x64, 0x0, 147 | 0xb0, 0x6, 0x2, 0xb0, 0x65, 0x7f, 0xb0, 0x64 , 0x7f, 0xb0, 148 | 0x5c, 0x0, 0xb0, 0x7, 0x7f, 0xb0, 0x1, 0x19, 0xb0, 0xb , 149 | 0x7f, 0x90, 0x47, 0x6a, 0x90, 0x48, 0x5a, 0x90, 0x47, 0x0, 150 | 0x90, 0x46 , 0x66, 0x90, 0x48, 0x0, 0x90, 0x44, 0x62, 151 | 0x90, 0x46, 0x0, 0x90, 0x46 , 0x52, 0x90, 0x44, 0x0, 0x90, 152 | 0x48, 0x44, 0x90, 0x44, 0x66, 0x90, 0x48 , 0x0, 0x90, 153 | 0x46, 0x0, 0x90, 0x41, 0x6a, 0x90, 0x44, 0x0, 0x90, 0x41 , 154 | 0x0, 0x90, 0x40, 0x6b, 0x90, 0x41, 0x44, 0x90, 0x40, 0x0, 155 | 0x90, 0x3f , 0x64, 0x90, 0x41, 0x0, 0x90, 0x3c, 0x59, 156 | 0x90, 0x3f, 0x0, 0x90, 0x3c , 0x0, 0x90, 0x3f, 0x61, 0x90, 157 | 0x3c, 0x63, 0x90, 0x3f, 0x0, 0x90, 0x3c , 0x0, 0x90, 0x3a, 158 | 0x66, 0x90, 0x38, 0x62, 0x90, 0x3a, 0x0, 0x90, 0x35 , 159 | 0x60, 0x90, 0x38, 0x0, 0x90, 0x33, 0x69, 0x90, 0x35, 0x0, 160 | 0x90, 0x33 , 0x0, 0x90, 0x30, 0x57, 0x90, 0x33, 0x4c, 161 | 0x90, 0x30, 0x0, 0x90, 0x33 , 0x0, 0x90, 0x34, 0x61, 0x90, 162 | 0x35, 0x43, 0x90, 0x34, 0x0, 0x90, 0x35 , 0x0, 0x90, 0x39, 163 | 0x31, 0x90, 0x39, 0x0, 0x90, 0x3c, 0x5f, 0x90, 0x3f , 164 | 0x60, 0x90, 0x3c, 0x0, 0x90, 0x3f, 0x0, 0x90, 0x41, 0x46, 165 | 0x90, 0x40 , 0x3a, 0x90, 0x40, 0x0, 0x90, 0x41, 0x0, 0x90, 166 | 0x44, 0x69, 0x90, 0x44, 0x0, 0x90, 0x41, 0x55, 0x90, 0x44, 167 | 0x6d, 0x90, 0x41, 0x0, 0x90, 0x46 , 0x52, 0x90, 0x44, 0x0, 168 | 0x90, 0x46, 0x0, 0x90, 0x48, 0x46, 0x90, 0x48 , 0x0, 0x90, 169 | 0x4b, 0x64, 0x90, 0x4b, 0x0, 0x90, 0x4c, 0x57, 0x90, 0x4d 170 | , 0x55, 0x90, 0x4c, 0x0, 0x90, 0x4b, 0x6c, 0x90, 0x4d, 171 | 0x0, 0x90, 0x4b , 0x0, 0x90, 0x48, 0x54, 0x90, 0x48, 0x0, 172 | 0x90, 0x4b, 0x67, 0x90, 0x4b , 0x0, 0x90, 0x48, 0x5e, 173 | 0x90, 0x48, 0x0, 0x90, 0x46, 0x5d, 0x90, 0x44 , 0x60, 174 | 0x90, 0x46, 0x0, 0x90, 0x44, 0x0, 0x90, 0x46, 0x46, 0x90, 175 | 0x44 , 0x5a, 0x90, 0x46, 0x0, 0x90, 0x41, 0x5d, 0x90, 176 | 0x44, 0x0, 0x90, 0x41 , 0x0 ] 177 | 178 | events = [ 179 | int('11110111', 2), 180 | int('11110110', 2), 181 | int('11110000', 2) 182 | ] 183 | 184 | print("Bootmsg len {0}".format(len(bootmsg))) 185 | s.write(bytes(bootmsg)) 186 | 187 | # sleeptime = 32/48000 188 | # for k in range(1, 100000000): 189 | # values = random.sample(list(range(0, 256)), 1) 190 | 191 | # time.sleep(sleeptime) 192 | # print("{0}, {1}".format(bytes(values), sleeptime)) 193 | # s.write(bytes(values)) 194 | # if sleeptime > 2/48000: 195 | # sleeptime /= 2 196 | -------------------------------------------------------------------------------- /html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ttymidi 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

ttymidi

15 |
MIDI for your serial devices
16 |
17 | 18 | 19 | 20 |

In a nutshell

21 |
22 | 23 |

24 | ttymidi is a GPL-licensed program that allows external 25 | serial devices to interface with ALSA MIDI applications. 26 | The main motivation behind ttymidi was to make 27 | Arduino boards talk to MIDI applications 28 | without the need to use (or build) 29 | any extra hardware. 30 |

31 | 32 |
33 | 34 |

Usage

35 |
36 |

37 | If you are using an Arduino board, simplest will be to use 38 | 39 | Arduino MIDI library, as described in the following sections. 40 | You may also use less sophisticated ardumidi library that is provided 41 | with ttymidi download package. If you are 42 | using another device, you should read the rest of this page to see how 43 | your device should communicate. Once your device is programmed and 44 | connected to your PC, ttymidi is executed in the 45 | following manner:

46 | 47 | 53 |

54 | 55 |

56 |

ttymidi -s /dev/ttyS0 -b 9600
57 |

58 | 59 |

60 | Where /dev/ttyS0 is the serial port you want to read from, 61 | and 9600 is the bitrate. For Arduino with serial connection 62 | runnning at 115200bps (the default rate for ttymidi) 63 | the command would be: 64 |

65 | 66 |

67 | Arduino Diecimila: 68 |

ttymidi -s /dev/ttyUSB0 -v
69 |

70 |

Arduino UNO: 71 |

ttymidi -s /dev/ttyACM0 -v
72 |

73 | 74 |

75 | Where the -v gives me verbose output, which helps 76 | me when I'm debugging. 77 |

78 | 79 |

80 | ttymidi creates an ALSA MIDI input and output ports that can be 81 | interfaced to any compatible program. This is done in the following manner: 82 |

83 | 84 |

85 |

 86 | # start ttymidi
 87 | ttymidi -s /dev/ttyUSB0 &
 88 | 
 89 | # start some ALSA compatible MIDI 
 90 | # program (timidity, in this case)
 91 | timidity -iA &                    
 92 | 
 93 | # list available MIDI input clients
 94 | aconnect -i                       
 95 | 
 96 | # list available MIDI output clients
 97 | aconnect -o                       
 98 | 
 99 | # connect
100 | aconnect 128:0 129:0
101 | 
102 | # ...where 128 and 129 are the client 
103 | # numbers for ttymidi and timidity,
104 | # found with the commands above
105 |

106 | 107 |

108 | If you would like to use a graphical interface to connect your MIDI 109 | clients, you can use something like 110 | qjackctl. As for 111 | the program that will consume the MIDI data, there are many out there 112 | (other than timidity, which 113 | was used in the previous example). Some crowd pleasers are 114 | fluidsynth, 115 | puredata, 116 | sooperlooper 117 | and ardour, 118 | to cite a few. 119 |

120 | 121 |

If you are using automated patchbay connecting application, such as 122 | the patchbay feature of qjackctl or 123 | aj-snapshot you might want to rename the name of ttymidi client created by using 124 | -n flag.

125 |

ttymidi -s /dev/ttyUSB0 -v -n my_weird_controller

126 | 127 |
128 | 129 | 130 |

Programming your serial device

131 |

Arduino MIDI library

132 |
133 |

ttymidi is nowadays compatible with 134 | Arduino MIDI library 135 | and we strongly recommend using it when communicating from Arduino to ttymidi. 136 | You only need to remember to call Serial.begin(int baud_rate) 137 | immediately after MIDI.begin(int channel) on your patch as 138 | the serial port of your computer is not able to operate at MIDI baud rate 139 | that MIDI library is using. For example:

140 |

141 | void setup() {
142 |   MIDI.begin(4);        // will listen incoming MIDI at channel 4
143 |   Serial.begin(115200); // will change baud rate of MIDI traffic from 31250 to 115200
144 | }
145 | 

146 |

You'll find MIDI library examples with these changes made in ttymidi 147 | download package.

148 |
149 |

Arduino interface

150 |
151 |

This chapter explains how to use deprecated (but still functional) 152 | ardumidi library instead of Arduino MIDI library. To interface to 153 | ttymidi, the serial-attached device must 154 | send data in MIDI-format. 155 | In the case of Arduino boards, a library is provided that abstracts all 156 | the nitty-gritty details away. Below is a list of the available 157 | functions: 158 |

159 | 160 |

161 |

162 | // Start/stop playing a certain note:
163 | midi_note_on(byte channel, byte key, byte velocity);
164 | midi_note_off(byte channel, byte key, byte velocity);
165 | 
166 | // Change pressure of specific keys:
167 | midi_key_pressure(byte channel, byte key, byte value);
168 | 
169 | // Change controller value (used for knobs, etc):
170 | midi_controller_change(byte channel, byte controller, byte value);
171 | 
172 | // Change "program" (change the instrument):
173 | midi_program_change(byte channel, byte program);
174 | 
175 | // Change key pressure of entire channels:
176 | midi_channel_pressure(byte channel, byte value);
177 | 
178 | // Change pitch-bend wheel:
179 | midi_pitch_bend(byte channel, int value);
180 | 
181 | // Check if a MIDI message has arrived from the PC
182 | int midi_message_available();
183 | 
184 | // Get a MIDI message from the PC
185 | MidiMessage read_midi_message();
186 | 
187 | // Get the pitch bend value from a MIDI message
188 | int get_pitch_bend(MidiMessage msg);
189 | 
190 | // Send a comment:
191 | midi_comment(char* str);
192 | 
193 | // Send a series of bytes (to be interpreted by another program):
194 | midi_printbytes(char* bytes, int len);
195 |

196 | 197 |

198 | Where the parameters are in the range: channel (0–15), 199 | pitch-bend value (0–16383), all other values (0–127). 200 | The center position of the pitch-bend wheel is 8192. 201 |

202 | 203 |

204 | Since MIDI uses numbers to represent notes, a few useful constants 205 | are defined in this library. With them, it is much easier to deal with 206 | MIDI nodes. For example: 207 |

208 | 209 |

210 |

midi_note_on(0, MIDI_C + MIDI_SHARP - 2*MIDI_OCTAVE, 127)
211 |

212 | 213 |

214 | All notes refer to the 4th octave by default. So the line above 215 | plays a C#2. As a shortcut to the 1st octave, constants such as 216 | MIDI_C0 can be used. 217 |

218 | 219 |

220 | Similarly, when receiving MIDI messages from the PC, you can use 221 | constants to figure out type of the incoming message: 222 |

223 | 224 |

225 |

if (midi_message_available() > 0) {
226 | 	MidiMessage msg = read_midi_message();
227 | 	if (msg.command == MIDI_NOTE_ON && msg.channel == 0) {
228 | 		/* 
229 | 		Since this is a "Note On" command, we know that 
230 | 		- msg.param1 is the MIDI note 
231 | 		- msg.param2 is the "velocity" of the note 
232 | 
233 | 		How do we know this? See the table below.
234 | 		*/
235 | 	}
236 | }
237 |

238 | 239 |

240 | There are a couple of things to keep in mind when using MIDI in features of 241 | ardumidi. Firstly midi_message_available() will always 242 | flush the incoming Serial buffer of all non-MIDI data. In practice this 243 | means that you can't mix using the Serial library and 244 | ardumidi 245 | (unless you know what you are doing). Secondly the incoming Serial buffer 246 | of arduino can store at most 42 MIDI commands so you have to take care 247 | that buffer does not overflow. However this does not cause your patch to crash, 248 | only couple MIDI messages are lost. 249 |

250 | 251 |

252 | To install the ardumidi library, just copy its folder 253 | into Arduino's sketchbook/libraries directory. 254 | Also note that an example Arduino sketches are included in the 255 | ttymidi package. They should appear under Examples 256 | in Arduino IDE after you have succesfully installed the library. 257 | For instance, ardumidi_test, will play one C-minor 258 | chord every second. 259 |

260 | 261 | 262 | 263 |
264 | 265 |
266 |

General interface

267 |

268 | If you are using another serial device in place of the Arduino, you'll 269 | need to program it to follow the MIDI specification. To sum this up, most 270 | of the commands sent to ttymidi are comprised of 3 bytes: 271 |

272 | 273 |

274 |

275 | byte 1       byte 2           byte 3
276 | status       parameter #1     parameter #2
277 |

278 | 279 |

280 | Where status is a combination of a command and a 281 | channel. Why is it called status? I haven't a clue, but that's 282 | the name in the MIDI spec. The table below describes the codes for each 283 | command as well as their associated parameters. 284 |

285 | 286 |

287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 |
codeparameter #1parameter #2
note off0x80-0x8Fkey velocity
note on0x90-0x9Fkey velocity
pressure0xA0-0xAFkey pressure
controller change0xB0-0xBFcontrollervalue
program change0xC0-0xCFprogram-
channel pressure0xD0-0xDFvalue-
pitch bend0xE0-0xEFrange LSBrange MSB
bytes0xFF00
344 |

345 | 346 |

347 | Most of these commands and parameters are self-explanatory. All parameters 348 | are given as a number in the range 0–127. That is, they're all 7-bit 349 | numbers with a 0 padded to their left (i.e. 0xxxxxxx) in order 350 | to make up 1 full byte (8 bits). For channel pressure and program change you 351 | should send only one data byte. 352 |

353 | 354 |

355 | One odd MIDI command is the pitch bend, whose parameter is a 356 | 14-bit number. For this reason, it is split into two parts: a least 357 | significant byte (LSB) and a most significant byte (MSB). These can be 358 | easily computed by quick logical and and shift operations: 359 |

360 | 361 |

362 |

363 | int range = 12345;
364 | byte range_lsb = range & 0x7F;
365 | byte range_msb = range >> 7;
366 |

367 | 368 |

369 | The bytes command provides a way to send non-MIDI data 370 | through the serial port without messing up with ttymidi. 371 | All you need to do is send 0xFF 0x00 0x00 len data[0] data[1] ..., 372 | where len is the number of data[•] in 373 | the message. When seeing such messages, ttymidi will 374 | just print them to the screen (but not parse them), so you can use this 375 | to print comments from your serial device. You can also use it with 376 | sprintf for debugging. 377 |

378 | 379 |

380 | You may have noticed that, for all commands, the 1st byte always has a 1 381 | as the most-significant bit, while the other 2 (or 1) bytes always have a 0. 382 | This comes from the MIDI spec, and is used by ttymidi 383 | for data alignment purposes. Do not try to change that in your 384 | serial device's program, or it will royally mess things up. 385 |

386 | 387 |
388 | 389 |

Downloads

390 |
391 | 392 |

393 | Get ttymidi here 394 |

395 | 396 |

397 | The archive includes the ttymidi program, examples 398 | for Arduino MIDI library as 399 | well as the ardumidi libraries for Arduino. Also, don't 400 | forget to read the README file in that is included in the 401 | archive. It has important information about compiling ttymidi. 402 |

403 | 404 |

405 | It is possible that a more recent version is available at 406 | the project's trunk series. 407 |

408 | 409 |
410 | 411 |

Found a Bug?

412 |
413 | 414 |

415 | If you find a bug, please report it! 416 | I especially like when the report comes with a patch :) but that's not a requirement. 417 |

418 | 419 |
420 | 421 |

Links

422 |
423 |

424 |

438 |

439 |
440 | 441 |

Authors

442 |
443 |

444 |

445 |
Thiago Teixeira
446 |
  • Original developer and maintainer.
  • 447 | 448 |
    Jari Suominen
    449 |
  • New co-maintainer of ttymidi. Originally contributed the MIDI-out code :)
  • 450 |
    451 |

    452 |
    453 | 454 |

    Changelog

    455 |
    456 |

    457 |

    458 |
    0.60
    459 |
  • Bugfix release.
  • 460 | 461 |
    0.50
    462 |
  • Now with MIDI output capability!
  • 463 | 464 |
    0.30
    465 |
  • Added "super debug mode". Activate it by pass "-p" (which stands for "print only").
  • 466 | 467 |
    0.21
    468 |
  • Apparently WConstants.h is no longer distributed with the Arduino software. So I had to replace that with WProgram.h .
  • 469 | 470 |
    0.20
    471 |
  • Added ability to print comments and random bytes to serial port. Fixed the alignment code.
  • 472 | 473 |
    0.11
    474 |
  • Changed some function names in the Arduino sketch
  • 475 | 476 |
    0.10
    477 |
  • Initial release
  • 478 |
    479 |

    480 |
    481 | 482 | 485 |
    486 | 487 | 488 | 489 | 490 | 491 | -------------------------------------------------------------------------------- /src/ttymidi.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of ttymidi. 3 | 4 | ttymidi is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | ttymidi is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with ttymidi. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | // MOD specific 38 | #include "mod-semaphore.h" 39 | 40 | #define MAX_DEV_STR_LEN 32 41 | #define MAX_MSG_SIZE 1024 42 | 43 | /* import ioctl definition here, as we can't include both "sys/ioctl.h" and "asm/termios.h" */ 44 | extern int ioctl (int __fd, unsigned long int __request, ...) __THROW; 45 | 46 | /* --------------------------------------------------------------------- */ 47 | // Globals 48 | 49 | volatile bool run; 50 | int serial; 51 | 52 | /* --------------------------------------------------------------------- */ 53 | // Program options 54 | 55 | static struct argp_option options[] = 56 | { 57 | {"serialdevice" , 's', "DEV" , 0, "Serial device to use. Default = /dev/ttyUSB0", 0 }, 58 | {"baudrate" , 'b', "BAUD", 0, "Serial port baud rate. Default = 31250", 0 }, 59 | #ifdef DEBUG 60 | {"verbose" , 'v', 0 , 0, "For debugging: Produce verbose output", 0 }, 61 | {"printonly" , 'p', 0 , 0, "Super debugging: Print values read from serial -- and do nothing else", 0 }, 62 | #endif 63 | {"name" , 'n', "NAME", 0, "Name of the JACK client. Default = ttymidi", 0 }, 64 | { 0 } 65 | }; 66 | 67 | typedef struct _arguments 68 | { 69 | #ifdef DEBUG 70 | int verbose; 71 | int printonly; 72 | #endif 73 | char serialdevice[MAX_DEV_STR_LEN]; 74 | int baudrate; 75 | char name[MAX_DEV_STR_LEN]; 76 | } arguments_t; 77 | 78 | typedef struct _jackdata 79 | { 80 | jack_client_t* client; 81 | jack_port_t* port_in; 82 | jack_port_t* port_out; 83 | jack_ringbuffer_t* ringbuffer_in; 84 | jack_ringbuffer_t* ringbuffer_out; 85 | jack_nframes_t bufsize_compensation; 86 | sem_t sem; 87 | bool internal; 88 | volatile jack_nframes_t last_frame_time; 89 | } jackdata_t; 90 | 91 | void exit_cli(int sig) 92 | { 93 | run = false; 94 | printf("\rttymidi closing down ... "); 95 | 96 | // unused 97 | return; (void)sig; 98 | } 99 | 100 | static error_t parse_opt (int key, char *arg, struct argp_state *state) 101 | { 102 | /* Get the input argument from argp_parse, which we 103 | know is a pointer to our arguments structure. */ 104 | arguments_t *arguments = state->input; 105 | int baud_temp; 106 | 107 | switch (key) 108 | { 109 | #ifdef DEBUG 110 | case 'p': 111 | arguments->printonly = 1; 112 | break; 113 | case 'v': 114 | arguments->verbose = 1; 115 | break; 116 | #endif 117 | case 's': 118 | if (arg == NULL) break; 119 | strncpy(arguments->serialdevice, arg, MAX_DEV_STR_LEN-1); 120 | break; 121 | case 'n': 122 | if (arg == NULL) break; 123 | strncpy(arguments->name, arg, MAX_DEV_STR_LEN-1); 124 | break; 125 | case 'b': 126 | if (arg == NULL) break; 127 | errno = 0; 128 | baud_temp = strtol(arg, NULL, 0); 129 | if (errno == EINVAL || errno == ERANGE) 130 | { 131 | printf("Baud rate %s is invalid.\n",arg); 132 | exit(1); 133 | } 134 | arguments->baudrate = baud_temp; 135 | break; 136 | case ARGP_KEY_ARG: 137 | case ARGP_KEY_END: 138 | break; 139 | 140 | default: 141 | return ARGP_ERR_UNKNOWN; 142 | } 143 | 144 | return 0; 145 | } 146 | 147 | void arg_set_defaults(arguments_t *arguments) 148 | { 149 | #ifdef DEBUG 150 | arguments->verbose = 0; 151 | arguments->printonly = 0; 152 | #endif 153 | arguments->baudrate = 31250; 154 | strncpy(arguments->serialdevice, "/dev/ttyUSB0", MAX_DEV_STR_LEN); 155 | strncpy(arguments->name, "ttymidi", MAX_DEV_STR_LEN); 156 | } 157 | 158 | const char *argp_program_version = "ttymidi 1.0.0"; 159 | const char *argp_program_bug_address = "falktx@moddevices.com"; 160 | static char doc[] = "ttymidi - Connect serial port devices to JACK MIDI programs!"; 161 | static struct argp argp = { options, parse_opt, NULL, doc, NULL, NULL, NULL }; 162 | static arguments_t arguments; 163 | 164 | /* --------------------------------------------------------------------- */ 165 | // The following read/write wrappers handle the case of interruption by system signals 166 | 167 | static inline uint8_t read_retry_or_error(int fd, void* dst) 168 | { 169 | int error; 170 | do { 171 | error = read(fd, dst, 1); 172 | } while (error == -1 && errno == EINTR); 173 | return error; 174 | } 175 | 176 | static inline uint8_t write_retry_or_success(int fd, const void* src, uint8_t size) 177 | { 178 | int error; 179 | do { 180 | error = write(fd, src, size); 181 | } while (error == -1 && errno == EINTR); 182 | // in case of error, return full size so outer loop can stop 183 | return error >= 0 ? (uint8_t)error : size; 184 | } 185 | 186 | /* --------------------------------------------------------------------- */ 187 | // JACK stuff 188 | 189 | const uint8_t ringbuffer_msg_size = 3 + sizeof(uint8_t) + sizeof(jack_nframes_t); 190 | 191 | static int process_client(jack_nframes_t frames, void* ptr) 192 | { 193 | if (! run) 194 | return 0; 195 | 196 | jackdata_t* jackdata = (jackdata_t*) ptr; 197 | 198 | const jack_nframes_t cycle_start = jack_last_frame_time(jackdata->client); 199 | 200 | void* portbuf_in = jack_port_get_buffer(jackdata->port_in, frames); 201 | void* portbuf_out = jack_port_get_buffer(jackdata->port_out, frames); 202 | 203 | // MIDI from serial to JACK 204 | jack_midi_clear_buffer(portbuf_in); 205 | 206 | char bufc[ringbuffer_msg_size]; 207 | jack_midi_data_t bufj[3]; 208 | uint8_t bsize; 209 | jack_nframes_t buf_frame, offset, last_buf_frame = 0; 210 | 211 | while (jack_ringbuffer_read(jackdata->ringbuffer_in, bufc, ringbuffer_msg_size) == ringbuffer_msg_size) 212 | { 213 | // Format: [data, data_size, frame] 214 | memcpy(&bsize, bufc+3, sizeof(uint8_t)); 215 | memcpy(&buf_frame, bufc+3+sizeof(uint8_t), sizeof(jack_nframes_t)); 216 | 217 | uint8_t i=0; 218 | for (; ibufsize_compensation; 224 | 225 | if (last_buf_frame > buf_frame) 226 | buf_frame = last_buf_frame; 227 | else 228 | last_buf_frame = buf_frame; 229 | 230 | if (buf_frame >= cycle_start) 231 | { 232 | offset = buf_frame - cycle_start; 233 | 234 | if (offset >= frames) 235 | offset = frames - 1; 236 | } 237 | else 238 | { 239 | offset = 0; 240 | } 241 | 242 | // fixup NoteOn with velocity 0 243 | if ((bufj[0] & 0xF0) == 0x90 && bufj[2] == 0x00) { 244 | bufj[0] = 0x80 + (bufj[0] & 0x0F); 245 | bufj[2] = 0x40; 246 | } 247 | 248 | jack_midi_event_write(portbuf_in, offset, bufj, bsize); 249 | } 250 | 251 | // MIDI from JACK to serial 252 | const uint32_t event_count = jack_midi_get_event_count(portbuf_out); 253 | 254 | if (event_count > 0) 255 | { 256 | bool needs_post = false; 257 | jack_midi_event_t event; 258 | 259 | for (uint32_t i=0; i 3) 264 | continue; 265 | 266 | // set first byte as size 267 | bufc[0] = event.size; 268 | 269 | // copy the rest 270 | uint8_t j=0; 271 | for (; jringbuffer_out, bufc, 4); 278 | 279 | buf_frame = cycle_start + event.time; 280 | jack_ringbuffer_write(jackdata->ringbuffer_out, (char*)&buf_frame, sizeof(jack_nframes_t)); 281 | 282 | needs_post = true; 283 | } 284 | 285 | if (needs_post) 286 | { 287 | // Tell MIDI-out thread we have data 288 | jackdata->last_frame_time = cycle_start; 289 | sem_post(&jackdata->sem); 290 | } 291 | } 292 | 293 | return 0; 294 | } 295 | 296 | bool open_client(jackdata_t* jackdata, jack_client_t* client) 297 | { 298 | jack_port_t *port_in, *port_out; 299 | jack_ringbuffer_t *ringbuffer_in, *ringbuffer_out; 300 | bzero(jackdata, sizeof(*jackdata)); 301 | 302 | if (client == NULL) 303 | { 304 | client = jack_client_open(arguments.name, JackNoStartServer|JackUseExactName, NULL); 305 | 306 | if (client == NULL) 307 | { 308 | fprintf(stderr, "Error opening JACK client.\n"); 309 | return false; 310 | } 311 | } 312 | else 313 | { 314 | jackdata->internal = true; 315 | } 316 | 317 | if ((port_in = jack_port_register(client, "MIDI_in", JACK_DEFAULT_MIDI_TYPE, 318 | JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal, 319 | 0x0)) == NULL) 320 | { 321 | fprintf(stderr, "Error creating input port.\n"); 322 | } 323 | 324 | if ((port_out = jack_port_register(client, "MIDI_out", JACK_DEFAULT_MIDI_TYPE, 325 | JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal, 326 | 0x0)) == NULL) 327 | { 328 | fprintf(stderr, "Error creating output port.\n"); 329 | } 330 | 331 | if ((ringbuffer_in = jack_ringbuffer_create(MAX_MSG_SIZE*2-1)) == NULL) 332 | { 333 | fprintf(stderr, "Error creating JACK input ringbuffer.\n"); 334 | } 335 | 336 | if ((ringbuffer_out = jack_ringbuffer_create(MAX_MSG_SIZE*2-1)) == NULL) 337 | { 338 | fprintf(stderr, "Error creating JACK output ringbuffer.\n"); 339 | } 340 | 341 | if (port_in == NULL || port_out == NULL || ringbuffer_in == NULL || ringbuffer_out == NULL) 342 | { 343 | jack_client_close(client); 344 | return false; 345 | } 346 | 347 | jackdata->client = client; 348 | jackdata->port_in = port_in; 349 | jackdata->port_out = port_out; 350 | jackdata->ringbuffer_in = ringbuffer_in; 351 | jackdata->ringbuffer_out = ringbuffer_out; 352 | jackdata->bufsize_compensation = (int)((double)jack_get_buffer_size(jackdata->client) / 10.0 + 0.5); 353 | 354 | jack_set_process_callback(client, process_client, jackdata); 355 | 356 | if (jack_activate(client) != 0) 357 | { 358 | fprintf(stderr, "Error activating JACK client.\n"); 359 | jack_client_close(client); 360 | return false; 361 | } 362 | 363 | sem_init(&jackdata->sem, 0, 0); 364 | 365 | jack_ringbuffer_mlock(ringbuffer_in); 366 | jack_ringbuffer_mlock(ringbuffer_out); 367 | 368 | if (jack_port_by_name(client, "mod-host:midi_in") != NULL) 369 | { 370 | char ourportname[255]; 371 | sprintf(ourportname, "%s:MIDI_in", jack_get_client_name(client)); 372 | jack_connect(client, ourportname, "mod-host:midi_in"); 373 | } 374 | 375 | return true; 376 | } 377 | 378 | void close_client(jackdata_t* jackdata) 379 | { 380 | jack_deactivate(jackdata->client); 381 | jack_port_unregister(jackdata->client, jackdata->port_in); 382 | jack_port_unregister(jackdata->client, jackdata->port_out); 383 | jack_ringbuffer_free(jackdata->ringbuffer_in); 384 | jack_ringbuffer_free(jackdata->ringbuffer_out); 385 | 386 | if (! jackdata->internal) 387 | jack_client_close(jackdata->client); 388 | 389 | sem_destroy(&jackdata->sem); 390 | bzero(jackdata, sizeof(*jackdata)); 391 | } 392 | 393 | /* --------------------------------------------------------------------- */ 394 | // MIDI stuff 395 | 396 | void* write_midi_from_jack(void* ptr) 397 | { 398 | jackdata_t* jackdata = (jackdata_t*) ptr; 399 | 400 | char bufc[4]; 401 | jack_nframes_t buf_frame, buf_diff, cycle_start; 402 | uint8_t size, written; 403 | 404 | const jack_nframes_t sample_rate = jack_get_sample_rate(jackdata->client); 405 | 406 | /* used for select sleep 407 | * (compared to usleep which sleeps at *least* x us, select sleeps at *most*) 408 | */ 409 | fd_set fd; 410 | FD_ZERO(&fd); 411 | struct timeval tv = { 0, 0 }; 412 | 413 | while (run) 414 | { 415 | if (sem_timedwait_secs(&jackdata->sem, 1) != 0) 416 | continue; 417 | 418 | if (! run) break; 419 | 420 | cycle_start = jackdata->last_frame_time; 421 | buf_diff = 0; 422 | 423 | while (jack_ringbuffer_read(jackdata->ringbuffer_out, bufc, 4) == 4) 424 | { 425 | if (jack_ringbuffer_read(jackdata->ringbuffer_out, (char*)&buf_frame, 426 | sizeof(jack_nframes_t)) != sizeof(jack_nframes_t)) 427 | continue; 428 | 429 | if (buf_frame > cycle_start) 430 | { 431 | buf_diff = buf_frame - cycle_start - buf_diff; 432 | tv.tv_usec = (buf_diff * 1000000) / sample_rate; 433 | 434 | if (tv.tv_usec > 60 && tv.tv_usec < 10000 /* 10 ms */) 435 | { 436 | // assume write takes 50 us 437 | tv.tv_usec -= 50; 438 | select(0, &fd, NULL, NULL, &tv); 439 | } 440 | } 441 | else 442 | { 443 | buf_diff = 0; 444 | } 445 | 446 | size = (uint8_t)bufc[0]; 447 | written = 0; 448 | 449 | do { 450 | written += write_retry_or_success(serial, bufc+1+written, size-written); 451 | } while (written < size); 452 | } 453 | } 454 | 455 | return NULL; 456 | } 457 | 458 | void* read_midi_from_serial_port(void* ptr) 459 | { 460 | jack_midi_data_t buffer[ringbuffer_msg_size]; 461 | 462 | //char buf[3 + sizeof(jack_nframes_t)]; 463 | //char msg[MAX_MSG_SIZE]; 464 | //int msglen; 465 | 466 | jackdata_t* jackdata = (jackdata_t*) ptr; 467 | 468 | #ifdef DEBUG 469 | /* 470 | * Super-debug mode: 471 | * 472 | * Print to screen whatever comes through the serial port. Send 473 | * nothing into the MIDI-event queue. 474 | */ 475 | if (arguments.printonly) 476 | { 477 | while (run) { 478 | if (read(serial, buffer, 1) == 1) { 479 | printf("%02x\t", buffer[0] & 0xFF); 480 | fflush(stdout); 481 | } 482 | } 483 | } 484 | else 485 | #endif 486 | { 487 | 488 | int error; 489 | ssize_t read_cnt; 490 | uint8_t data_bytes_cnt = 0; 491 | uint8_t last_status_byte = 0; 492 | bool has_status_byte; 493 | 494 | rerun: 495 | while (run) { 496 | // Clean the buffer 497 | memset(buffer, 0, ringbuffer_msg_size); 498 | 499 | // Read a byte and go ahead iff it is a valid status byte. 500 | read_cnt = read(serial, buffer, 1); 501 | if (read_cnt != 1) { 502 | // Nothing to read. Try again in the next loop. 503 | continue; 504 | } 505 | #ifdef DEBUG 506 | if (arguments.verbose) { 507 | printf("%02x\t", buffer[0] & 0xFF); 508 | fflush(stdout); 509 | } 510 | #endif 511 | 512 | // Ignore active-sensing 513 | if (buffer[0] == 0xFE) { 514 | continue; 515 | } 516 | 517 | // Check if the first bit is set... 518 | has_status_byte = (buffer[0] & 0x80) == 0x80; 519 | if (has_status_byte || last_status_byte != 0) { 520 | // ...then is a MIDI message. No SysEx data. 521 | if (!has_status_byte) { 522 | buffer[1] = buffer[0]; 523 | buffer[0] = last_status_byte; 524 | read_cnt = 2; 525 | } 526 | 527 | if (buffer[0] < 0xF0) { 528 | // Channel Voice or Mode Message ahead 529 | last_status_byte = buffer[0]; 530 | 531 | // Program Change and Channel Pressure only have 1 data byte following! 532 | switch(buffer[0] & 0xF0) { 533 | case 0xC0: // Program Change 534 | case 0xD0: // Channel Pressure 535 | data_bytes_cnt = 1; 536 | break; 537 | default: 538 | data_bytes_cnt = 2; 539 | break; 540 | } 541 | 542 | } else { 543 | // Compare https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message 544 | switch(buffer[0]) { 545 | case 0xF0: 546 | // System exclusive begin 547 | 548 | // Unknown data byte count. Note that Real-Time messages 549 | // may be interleaved with a System Exclusive! 550 | // Every SysEx byte until 0xF7 should start with a 0-bit, so skipping is safe. 551 | last_status_byte = 0; 552 | continue; 553 | 554 | case 0xF7: 555 | // System exclusive end 556 | last_status_byte = 0; 557 | continue; 558 | 559 | case 0xF2: 560 | // Song Position Pointer 561 | data_bytes_cnt = 2; 562 | last_status_byte = 0; 563 | break; 564 | 565 | case 0xF1: 566 | // MIDI Time Code Quarter Frame 567 | case 0xF3: 568 | // Song Select 569 | data_bytes_cnt = 1; 570 | last_status_byte = 0; 571 | break; 572 | 573 | case 0xF8: 574 | // Clock 575 | case 0xFA: 576 | // Start 577 | case 0xFB: 578 | // Continue 579 | case 0xFC: 580 | // Stop 581 | // NOTE: we do not reset `last_status_byte` here 582 | data_bytes_cnt = 0; 583 | break; 584 | 585 | default: 586 | // Others, like Tune Request and Reserved 587 | data_bytes_cnt = 0; 588 | last_status_byte = 0; 589 | break; 590 | } 591 | } 592 | 593 | while (read_cnt < data_bytes_cnt+1) { 594 | error = read_retry_or_error(serial, buffer+read_cnt); 595 | 596 | if (error == 0) { 597 | continue; 598 | } 599 | if (error < 0) { 600 | #ifdef DEBUG 601 | if (arguments.verbose) { 602 | printf("error %i while reading serial: %s\n", error, strerror(error)); 603 | fflush(stdout); 604 | } 605 | #endif 606 | goto rerun; 607 | break; 608 | } 609 | 610 | // Ignore or handle some stuff in the middle of voice messages 611 | switch (buffer[read_cnt]) { 612 | // Ignore active-sensing 613 | case 0xFE: 614 | continue; 615 | // Handle clock messages, see below for the same code 616 | case 0xF8: 617 | case 0xFA: 618 | case 0xFB: 619 | case 0xFC: 620 | { 621 | // remaining bytes 622 | buffer[1] = buffer[2] = 0; 623 | // size 624 | buffer[3] = 1; 625 | // timestamp 626 | const jack_nframes_t frames = jack_frame_time(jackdata->client); 627 | memcpy(buffer+4, &frames, sizeof(jack_nframes_t)); 628 | // done 629 | jack_ringbuffer_write(jackdata->ringbuffer_in, (const char *) buffer, ringbuffer_msg_size); 630 | } 631 | continue; 632 | } 633 | 634 | read_cnt += error; 635 | } 636 | 637 | // Whole payload in the buffer, ready to forward 638 | #ifdef DEBUG 639 | if (arguments.verbose) { 640 | for (uint8_t i=1U; iclient); 659 | memcpy(buffer+4, &frames, sizeof(jack_nframes_t)); 660 | 661 | // Sanity check 662 | if ((buffer[0] & 0x80) && !(buffer[1] & 0x80) && !(buffer[2] & 0x80)) { 663 | jack_ringbuffer_write(jackdata->ringbuffer_in, (const char *) buffer, ringbuffer_msg_size); 664 | } else { 665 | // Bad bytes. Discard the event. 666 | #ifdef DEBUG 667 | if (arguments.verbose) { 668 | printf("Sanity check failed, bad bytes: %02x\t%02x\t%02x\n", buffer[0], buffer[1], buffer[2]); 669 | fflush(stdout); 670 | } 671 | #endif 672 | } 673 | } else { 674 | // Unexpected data byte. Discard it. 675 | #ifdef DEBUG 676 | if (arguments.verbose) { 677 | printf("Status byte check failed, first bad byte: %02x\n", buffer[0]); 678 | fflush(stdout); 679 | } 680 | #endif 681 | } 682 | } 683 | } 684 | return NULL; 685 | } 686 | 687 | 688 | /* --------------------------------------------------------------------- */ 689 | // Main program 690 | 691 | static struct termios2 oldtio, newtio; 692 | static jackdata_t jackdata; 693 | static pthread_t midi_out_thread; 694 | 695 | static bool _ttymidi_init(bool exit_on_failure, jack_client_t* client) 696 | { 697 | /* 698 | * Open JACK stuff 699 | */ 700 | 701 | if (! open_client(&jackdata, client)) 702 | { 703 | if (exit_on_failure) 704 | { 705 | fprintf(stderr, "Error creating jack client.\n"); 706 | exit(-1); 707 | } 708 | return false; 709 | } 710 | 711 | /* 712 | * Open modem device for reading and not as controlling tty because we don't 713 | * want to get killed if linenoise sends CTRL-C. 714 | */ 715 | 716 | serial = open(arguments.serialdevice, O_RDWR | O_NOCTTY); 717 | 718 | if (serial < 0) 719 | { 720 | if (exit_on_failure) 721 | { 722 | perror(arguments.serialdevice); 723 | exit(-1); 724 | } 725 | return false; 726 | } 727 | 728 | /* save current serial port settings */ 729 | ioctl(serial, TCGETS2, &oldtio); 730 | 731 | /* clear struct for new port settings */ 732 | bzero(&newtio, sizeof(newtio)); 733 | 734 | /* 735 | * CRTSCTS : output hardware flow control (only used if the cable has 736 | * all necessary lines. See sect. 7 of Serial-HOWTO) 737 | * CS8 : 8n1 (8bit, no parity, 1 stopbit) 738 | * CLOCAL : local connection, no modem contol 739 | * CREAD : enable receiving characters 740 | */ 741 | newtio.c_cflag = BOTHER | CS8 | CLOCAL | CREAD; // CRTSCTS removed 742 | 743 | /* 744 | * IGNPAR : ignore bytes with parity errors 745 | * ICRNL : map CR to NL (otherwise a CR input on the other computer will not terminate input) 746 | * otherwise make device raw (no other input processing) 747 | */ 748 | newtio.c_iflag = IGNPAR; 749 | 750 | /* Raw output */ 751 | newtio.c_oflag = 0; 752 | 753 | /* 754 | * ICANON : enable canonical input 755 | * disable all echo functionality, and don't send signals to calling program 756 | */ 757 | newtio.c_lflag = 0; // non-canonical 758 | 759 | /* Speed */ 760 | newtio.c_ispeed = arguments.baudrate; 761 | newtio.c_ospeed = arguments.baudrate; 762 | 763 | /* 764 | * set up: we'll be reading 4 bytes at a time. 765 | */ 766 | newtio.c_cc[VTIME] = 0; /* inter-character timer unused */ 767 | newtio.c_cc[VMIN] = 1; /* blocking read until n character arrives */ 768 | 769 | /* 770 | * now activate the settings for the port 771 | */ 772 | ioctl(serial, TCSETS2, &newtio); 773 | 774 | // Linux-specific: enable low latency mode (FTDI "nagling off") 775 | struct serial_struct ser_info; 776 | bzero(&ser_info, sizeof(ser_info)); 777 | ioctl(serial, TIOCGSERIAL, &ser_info); 778 | ser_info.flags |= ASYNC_LOW_LATENCY; 779 | ioctl(serial, TIOCSSERIAL, &ser_info); 780 | 781 | #ifdef DEBUG 782 | if (arguments.printonly) 783 | { 784 | printf("Super debug mode: Only printing the signal to screen. Nothing else.\n"); 785 | } 786 | #endif 787 | 788 | /* 789 | * read commands 790 | */ 791 | 792 | run = true; 793 | 794 | /* Give high priority to our threads */ 795 | pthread_attr_t attributes; 796 | pthread_attr_init(&attributes); 797 | pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_JOINABLE); 798 | pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED); 799 | pthread_attr_setscope(&attributes, (client != NULL) ? PTHREAD_SCOPE_PROCESS : PTHREAD_SCOPE_SYSTEM); 800 | pthread_attr_setschedpolicy(&attributes, SCHED_FIFO); 801 | 802 | struct sched_param rt_param; 803 | memset(&rt_param, 0, sizeof(rt_param)); 804 | rt_param.sched_priority = 80; 805 | 806 | pthread_attr_setschedparam(&attributes, &rt_param); 807 | 808 | /* Starting thread that is writing jack port data */ 809 | pthread_create(&midi_out_thread, &attributes, write_midi_from_jack, (void*) &jackdata); 810 | 811 | /* And also thread for polling serial data. As serial is currently read in 812 | blocking mode, by this we can enable ctrl+c quiting and avoid zombie 813 | alsa ports when killing app with ctrl+z */ 814 | pthread_t midi_in_thread; 815 | pthread_create(&midi_in_thread, NULL, read_midi_from_serial_port, (void*) &jackdata); 816 | 817 | pthread_attr_destroy(&attributes); 818 | return true; 819 | } 820 | 821 | void _ttymidi_finish(void) 822 | { 823 | close_client(&jackdata); 824 | pthread_join(midi_out_thread, NULL); 825 | 826 | /* restore the old port settings */ 827 | ioctl(serial, TCSETS2, &oldtio); 828 | printf("\ndone!\n"); 829 | } 830 | 831 | int main(int argc, char** argv) 832 | { 833 | arg_set_defaults(&arguments); 834 | argp_parse(&argp, argc, argv, 0, 0, &arguments); 835 | 836 | if (! _ttymidi_init(true, NULL)) 837 | return 1; 838 | 839 | signal(SIGINT, exit_cli); 840 | signal(SIGTERM, exit_cli); 841 | 842 | while (run) 843 | usleep(100000); // 100 ms 844 | 845 | _ttymidi_finish(); 846 | } 847 | 848 | __attribute__ ((visibility("default"))) 849 | int jack_initialize(jack_client_t* client, const char* load_init); 850 | 851 | int jack_initialize(jack_client_t* client, const char* load_init) 852 | { 853 | arg_set_defaults(&arguments); 854 | #ifdef DEBUG 855 | // Enable logs for debug build 856 | arguments.verbose = 1; 857 | #endif 858 | 859 | if (load_init != NULL && load_init[0] != '\0') 860 | { 861 | strncpy(arguments.serialdevice, load_init, MAX_DEV_STR_LEN-1); 862 | } 863 | else 864 | { 865 | const char* serialdevice_env = getenv("MOD_MIDI_SERIAL_PORT"); 866 | 867 | if (serialdevice_env != NULL && serialdevice_env[0] != '\0') 868 | strncpy(arguments.serialdevice, serialdevice_env, MAX_DEV_STR_LEN-1); 869 | } 870 | 871 | if (! _ttymidi_init(false, client)) 872 | return 1; 873 | 874 | return 0; 875 | } 876 | 877 | __attribute__ ((visibility("default"))) 878 | void jack_finish(void); 879 | 880 | void jack_finish(void) 881 | { 882 | run = false; 883 | _ttymidi_finish(); 884 | } 885 | --------------------------------------------------------------------------------