1
0
mirror of https://github.com/mist-devel/mist-board.git synced 2026-02-26 16:23:32 +00:00

MIDI out redirection

This commit is contained in:
harbaum
2014-03-07 08:20:24 +00:00
parent 823ef5f496
commit 50dca1481d
6 changed files with 752 additions and 11 deletions

View File

@@ -13,14 +13,19 @@ module acia (
output midi_out,
input midi_in,
// data from io controller to acia
// data from io controller to ikbd acia
input ikbd_strobe_in,
input [7:0] ikbd_data_in,
// data from acia to io controller
// data from ikbd acia to io controller
output ikbd_data_out_available,
input ikbd_strobe_out,
output [7:0] ikbd_data_out
output [7:0] ikbd_data_out,
// data from midi acia to io controller
output midi_data_out_available,
input midi_strobe_out,
output [7:0] midi_data_out
);
// --- ikbd output fifo ---
@@ -32,7 +37,7 @@ io_fifo ikbd_out_fifo (
.in_clk (!clk), // latch incoming data on negedge
.in (din),
.in_strobe (1'b0),
.in_enable (sel && ~ds && ~rw && (addr == 2'd1)),
.in_enable (sel && ~ds && ~rw && (addr == 2'd1)), // ikbd acia data write
.out_clk (clk),
.out (ikbd_data_out),
@@ -61,6 +66,26 @@ io_fifo ikbd_in_fifo (
.data_available (ikbd_rx_data_available)
);
// --- midi output fifo ---
// filled by the CPU when writing to the acia data register
// emptied by the io controller when reading via SPI
// This happens in parallel to the real midi generation, so
// physical and USB MIDI can be used at the same time
io_fifo midi_out_fifo (
.reset (reset),
.in_clk (!clk), // latch incoming data on negedge
.in (din),
.in_strobe (1'b0),
.in_enable (sel && ~ds && ~rw && (addr == 2'd3)), // midi acia data write
.out_clk (clk),
.out (midi_data_out),
.out_strobe (midi_strobe_out),
.out_enable (1'b0),
.data_available (midi_data_out_available)
);
// timer to let bytes arrive at a reasonable speed
reg [13:0] readTimer;

View File

@@ -51,9 +51,6 @@ wire [22:0] video_address;
wire st_de, st_hs, st_vs;
wire [15:0] video_adj;
// on-board io
wire [1:0] buttons;
// generate dtack for all implemented peripherals
wire io_dtack = vreg_sel || mmu_sel || mfp_sel || mfp_iack ||
acia_sel || psg_sel || dma_sel || auto_iack || blitter_sel ||
@@ -330,16 +327,21 @@ acia acia (
.dout (acia_data_out),
.irq (acia_irq ),
// MIDI interface
// physical MIDI interface
.midi_out (UART_TX ),
.midi_in (UART_RX ),
// ikbd interface
.ikbd_data_out_available (ikbd_data_from_acia_available),
.ikbd_strobe_out (ikbd_strobe_from_acia),
.ikbd_data_out (ikbd_data_from_acia),
.ikbd_strobe_in (ikbd_strobe_to_acia),
.ikbd_data_in (ikbd_data_to_acia)
.ikbd_data_in (ikbd_data_to_acia),
// redirected midi interface
.midi_data_out_available (midi_data_from_acia_available),
.midi_strobe_out (midi_strobe_from_acia),
.midi_data_out (midi_data_from_acia)
);
wire [23:1] blitter_master_addr;
@@ -1057,6 +1059,11 @@ wire [7:0] ikbd_data_from_acia;
wire ikbd_strobe_from_acia;
wire ikbd_data_from_acia_available;
// connection to transfer midi data from acia to io controller
wire [7:0] midi_data_from_acia;
wire midi_strobe_from_acia;
wire midi_data_from_acia_available;
// connection to transfer serial/rs232 data from mfp to io controller
wire [7:0] serial_data_from_mfp;
wire serial_strobe_from_mfp;
@@ -1071,11 +1078,11 @@ wire parallel_data_out_available;
//// user io has an extra spi channel outside minimig core ////
user_io user_io(
// the spi interface
.SPI_CLK(SPI_SCK),
.SPI_SS_IO(CONF_DATA0),
.SPI_MISO(user_io_sdo),
.SPI_MOSI(SPI_DI),
.BUTTONS(buttons),
// ikbd interface
.ikbd_strobe_out(ikbd_strobe_from_acia),
@@ -1096,6 +1103,11 @@ user_io user_io(
.parallel_data_out(parallel_data_out),
.parallel_data_out_available(parallel_data_out_available),
// midi interface
.midi_strobe_out(midi_strobe_from_acia),
.midi_data_out(midi_data_from_acia),
.midi_data_out_available(midi_data_from_acia_available),
.CORE_TYPE(8'ha3) // mist core id
);

View File

@@ -28,6 +28,11 @@ module user_io(
input parallel_data_out_available,
input [7:0] parallel_data_out,
// midi data from acia to io controller
output reg midi_strobe_out,
input midi_data_out_available,
input [7:0] midi_data_out,
output [1:0] BUTTONS,
output [1:0] SWITCHES
);
@@ -68,6 +73,14 @@ module user_io(
else
SPI_MISO <= parallel_data_out[15-cnt];
end
// midi->io controller
if(cmd == 8) begin
if(!toggle)
SPI_MISO <= midi_data_out_available;
else
SPI_MISO <= midi_data_out[15-cnt];
end
end
end
@@ -79,6 +92,7 @@ module user_io(
ikbd_strobe_out <= 1'b0;
serial_strobe_out <= 1'b0;
parallel_strobe_out <= 1'b0;
midi_strobe_out <= 1'b0;
end else begin
sbuf[6:1] <= sbuf[5:0];
sbuf[0] <= SPI_MOSI;
@@ -103,6 +117,7 @@ module user_io(
serial_strobe_in <= 1'b0;
serial_strobe_out <= 1'b0;
parallel_strobe_out <= 1'b0;
midi_strobe_out <= 1'b0;
end
// payload byte
@@ -135,6 +150,10 @@ module user_io(
// give strobe after second parallel byte (toggle ==1)
if((cmd == 6) && toggle)
parallel_strobe_out <= 1'b1;
// give strobe after second midi byte (toggle ==1)
if((cmd == 8) && toggle)
midi_strobe_out <= 1'b1;
end
end
end

8
tools/ttymidi/Makefile Normal file
View File

@@ -0,0 +1,8 @@
all:
gcc src/ttymidi.c -o ttymidi -lasound -lpthread
clean:
rm ttymidi
install:
install -m 0755 ttymidi /usr/local/bin
uninstall:
rm /usr/local/bin/ttymidi

87
tools/ttymidi/README Normal file
View File

@@ -0,0 +1,87 @@
ttyMIDI is a GPL-licensed program that allows external serial devices to
interface with the ALSA sequencer.
COMPILATION
The ttyMIDI source code is comprised of a single C file. To compile it, just
run the following command:
make
This program depends on libasound2, so you should have the development headers
for that installed. In Debian or Ubuntu, you can install it by running:
apt-get install libasound2-dev
After you compile ttyMIDI, you may wish to copy it to /usr/bin for easy
access. This can be done simply by following command:
sudo make install
USAGE
First, you need an external device that can send MIDI commands through the
serial port. To find out more about programming an external device, read the
TTYMIDI SPECIFICATION section. If you are using an Arduino board
(http://www.arduino.cc), read the instructions under the arduino folder. Once
your device is programmed and connected to your PC's serial port, follow the
instructions below.
To connect to ttyS0 at 2400bps:
ttymidi -s /dev/ttyS0 -b 2400
To connect to ttyUSB port at default speed (115200bps) and display information
about incoming MIDI events:
ttymidi -s /dev/ttyUSB0 -v
ttyMIDI creates an ALSA MIDI output port that can be interfaced to any
compatible program. This is done in the following manner:
ttymidi -s /dev/ttyUSB0 & # start ttyMIDI
timidity -iA & # start some ALSA compatible MIDI program
aconnect -i # list available MIDI input clients
aconnect -o # list available MIDI output clients
aconnect 128:0 129:0 # where 128 and 129 are the client numbers for
# ttyMIDI and timidity
Further, ttyMIDI creates an ALSA MIDI input port that feeds incoming MIDI events
back to the serial port. Before better documentation exists, check the header file of
the ardumidi library to figure out how to read this data at the Arduino end.
If you would like to use a GUI to connect your MIDI clients, there are many
available. One of my favorites is qjackctl.
TTYMIDI SPECIFICATION
The message format expected by ttyMIDI is based on what I could gather about the
MIDI specification online. I tried to make it as similar as possible to the
specification, but some differences exist. The good news is that as long as you
follow the specification described below, everything should work.
Every MIDI command is sent through the serial port as 3 bytes. The first byte
contains the command type and channel. After that, 2 parameter bytes are
transmitted. To simplify the decoding process, ttyMIDI does not support
"running status", and it also forces every command into 3 bytes. So even
commands which only have 1 parameter must transmit byte #3 (transmitting a 0 in
this case). This is described in more details in the table below:
byte1 byte2 byte3 Command name
0x80-0x8F Key # (0-127) Off Velocity (0-127) Note OFF
0x90-0x90 Key # (0-127) On Velocity (0-127) Note ON
0xA0-0xA0 Key # (0-127) Pressure (0-127) Poly Key Pressure
0xB0-0xB0 Control # (0-127) Control Value (0-127) Control Change
0xC0-0xC0 Program # (0-127) Not Used (send 0) Program Change
0xD0-0xD0 Pressure Value (0-127) Not Used (send 0) Mono Key Pressure (Channel Pressure)
0xE0-0xE0 Range LSB (0-127) Range MSB (0-127) Pitch Bend
Not implemented:
0xF0-0xF0 Manufacturer's ID Model ID System
Byte #1 is given as COMMAND + CHANNEL. So, for example, 0xE3 is the Pitch Bend
command (0xE0) for channel 4 (0x03).

590
tools/ttymidi/src/ttymidi.c Normal file
View File

@@ -0,0 +1,590 @@
/*
This file is part of ttymidi.
ttymidi is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ttymidi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ttymidi. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <argp.h>
#include <alsa/asoundlib.h>
#include <signal.h>
#include <pthread.h>
// Linux-specific
#include <linux/serial.h>
#include <linux/ioctl.h>
#include <asm/ioctls.h>
#define FALSE 0
#define TRUE 1
#define MAX_DEV_STR_LEN 32
#define MAX_MSG_SIZE 1024
/* change this definition for the correct port */
//#define _POSIX_SOURCE 1 /* POSIX compliant source */
int run;
int serial;
int port_out_id;
/* --------------------------------------------------------------------- */
// Program options
static struct argp_option options[] =
{
{"serialdevice" , 's', "DEV" , 0, "Serial device to use. Default = /dev/ttyUSB0" },
{"baudrate" , 'b', "BAUD", 0, "Serial port baud rate. Default = 115200" },
{"verbose" , 'v', 0 , 0, "For debugging: Produce verbose output" },
{"printonly" , 'p', 0 , 0, "Super debugging: Print values read from serial -- and do nothing else" },
{"quiet" , 'q', 0 , 0, "Don't produce any output, even when the print command is sent" },
{"name" , 'n', "NAME", 0, "Name of the Alsa MIDI client. Default = ttymidi" },
{ 0 }
};
typedef struct _arguments
{
int silent, verbose, printonly;
char serialdevice[MAX_DEV_STR_LEN];
int baudrate;
char name[MAX_DEV_STR_LEN];
} arguments_t;
void exit_cli(int sig)
{
run = FALSE;
printf("\rttymidi closing down ... ");
}
static error_t parse_opt (int key, char *arg, struct argp_state *state)
{
/* Get the input argument from argp_parse, which we
know is a pointer to our arguments structure. */
arguments_t *arguments = state->input;
int baud_temp;
switch (key)
{
case 'p':
arguments->printonly = 1;
break;
case 'q':
arguments->silent = 1;
break;
case 'v':
arguments->verbose = 1;
break;
case 's':
if (arg == NULL) break;
strncpy(arguments->serialdevice, arg, MAX_DEV_STR_LEN);
break;
case 'n':
if (arg == NULL) break;
strncpy(arguments->name, arg, MAX_DEV_STR_LEN);
break;
case 'b':
if (arg == NULL) break;
baud_temp = strtol(arg, NULL, 0);
if (baud_temp != EINVAL && baud_temp != ERANGE)
switch (baud_temp)
{
case 1200 : arguments->baudrate = B1200 ; break;
case 2400 : arguments->baudrate = B2400 ; break;
case 4800 : arguments->baudrate = B4800 ; break;
case 9600 : arguments->baudrate = B9600 ; break;
case 19200 : arguments->baudrate = B19200 ; break;
case 38400 : arguments->baudrate = B38400 ; break;
case 57600 : arguments->baudrate = B57600 ; break;
case 115200 : arguments->baudrate = B115200; break;
default: printf("Baud rate %i is not supported.\n",baud_temp); exit(1);
}
case ARGP_KEY_ARG:
case ARGP_KEY_END:
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
void arg_set_defaults(arguments_t *arguments)
{
char *serialdevice_temp = "/dev/ttyUSB0";
arguments->printonly = 0;
arguments->silent = 0;
arguments->verbose = 0;
arguments->baudrate = B115200;
char *name_tmp = (char *)"ttymidi";
strncpy(arguments->serialdevice, serialdevice_temp, MAX_DEV_STR_LEN);
strncpy(arguments->name, name_tmp, MAX_DEV_STR_LEN);
}
const char *argp_program_version = "ttymidi 0.60";
const char *argp_program_bug_address = "tvst@hotmail.com";
static char doc[] = "ttymidi - Connect serial port devices to ALSA MIDI programs!";
static struct argp argp = { options, parse_opt, 0, doc };
arguments_t arguments;
/* --------------------------------------------------------------------- */
// MIDI stuff
int open_seq(snd_seq_t** seq)
{
int port_out_id, port_in_id; // actually port_in_id is not needed nor used anywhere
if (snd_seq_open(seq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
{
fprintf(stderr, "Error opening ALSA sequencer.\n");
exit(1);
}
snd_seq_set_client_name(*seq, arguments.name);
if ((port_out_id = snd_seq_create_simple_port(*seq, "MIDI out",
SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
SND_SEQ_PORT_TYPE_APPLICATION)) < 0)
{
fprintf(stderr, "Error creating sequencer port.\n");
}
if ((port_in_id = snd_seq_create_simple_port(*seq, "MIDI in",
SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
SND_SEQ_PORT_TYPE_APPLICATION)) < 0)
{
fprintf(stderr, "Error creating sequencer port.\n");
}
return port_out_id;
}
void parse_midi_command(snd_seq_t* seq, int port_out_id, char *buf)
{
/*
MIDI COMMANDS
-------------------------------------------------------------------
name status param 1 param 2
-------------------------------------------------------------------
note off 0x80+C key # velocity
note on 0x90+C key # velocity
poly key pressure 0xA0+C key # pressure value
control change 0xB0+C control # control value
program change 0xC0+C program # --
mono key pressure 0xD0+C pressure value --
pitch bend 0xE0+C range (LSB) range (MSB)
system 0xF0+C manufacturer model
-------------------------------------------------------------------
C is the channel number, from 0 to 15;
-------------------------------------------------------------------
source: http://ftp.ec.vanderbilt.edu/computermusic/musc216site/MIDI.Commands.html
In this program the pitch bend range will be transmitter as
one single 8-bit number. So the end result is that MIDI commands
will be transmitted as 3 bytes, starting with the operation byte:
buf[0] --> operation/channel
buf[1] --> param1
buf[2] --> param2 (param2 not transmitted on program change or key press)
*/
snd_seq_event_t ev;
snd_seq_ev_clear(&ev);
snd_seq_ev_set_direct(&ev);
snd_seq_ev_set_source(&ev, port_out_id);
snd_seq_ev_set_subs(&ev);
int operation, channel, param1, param2;
operation = buf[0] & 0xF0;
channel = buf[0] & 0x0F;
param1 = buf[1];
param2 = buf[2];
switch (operation)
{
case 0x80:
if (!arguments.silent && arguments.verbose)
printf("Serial 0x%x Note off %03u %03u %03u\n", operation, channel, param1, param2);
snd_seq_ev_set_noteoff(&ev, channel, param1, param2);
break;
case 0x90:
if (!arguments.silent && arguments.verbose)
printf("Serial 0x%x Note on %03u %03u %03u\n", operation, channel, param1, param2);
snd_seq_ev_set_noteon(&ev, channel, param1, param2);
break;
case 0xA0:
if (!arguments.silent && arguments.verbose)
printf("Serial 0x%x Pressure change %03u %03u %03u\n", operation, channel, param1, param2);
snd_seq_ev_set_keypress(&ev, channel, param1, param2);
break;
case 0xB0:
if (!arguments.silent && arguments.verbose)
printf("Serial 0x%x Controller change %03u %03u %03u\n", operation, channel, param1, param2);
snd_seq_ev_set_controller(&ev, channel, param1, param2);
break;
case 0xC0:
if (!arguments.silent && arguments.verbose)
printf("Serial 0x%x Program change %03u %03u\n", operation, channel, param1);
snd_seq_ev_set_pgmchange(&ev, channel, param1);
break;
case 0xD0:
if (!arguments.silent && arguments.verbose)
printf("Serial 0x%x Channel change %03u %03u\n", operation, channel, param1);
snd_seq_ev_set_chanpress(&ev, channel, param1);
break;
case 0xE0:
param1 = (param1 & 0x7F) + ((param2 & 0x7F) << 7);
if (!arguments.silent && arguments.verbose)
printf("Serial 0x%x Pitch bend %03u %05i\n", operation, channel, param1);
snd_seq_ev_set_pitchbend(&ev, channel, param1 - 8192); // in alsa MIDI we want signed int
break;
/* Not implementing system commands (0xF0) */
default:
if (!arguments.silent)
printf("0x%x Unknown MIDI cmd %03u %03u %03u\n", operation, channel, param1, param2);
break;
}
snd_seq_event_output_direct(seq, &ev);
snd_seq_drain_output(seq);
}
void write_midi_action_to_serial_port(snd_seq_t* seq_handle)
{
snd_seq_event_t* ev;
char bytes[] = {0x00, 0x00, 0xFF};
do
{
snd_seq_event_input(seq_handle, &ev);
switch (ev->type)
{
case SND_SEQ_EVENT_NOTEOFF:
bytes[0] = 0x80 + ev->data.control.channel;
bytes[1] = ev->data.note.note;
bytes[2] = ev->data.note.velocity;
if (!arguments.silent && arguments.verbose)
printf("Alsa 0x%x Note off %03u %03u %03u\n", bytes[0]&0xF0, bytes[0]&0xF, bytes[1], bytes[2]);
break;
case SND_SEQ_EVENT_NOTEON:
bytes[0] = 0x90 + ev->data.control.channel;
bytes[1] = ev->data.note.note;
bytes[2] = ev->data.note.velocity;
if (!arguments.silent && arguments.verbose)
printf("Alsa 0x%x Note on %03u %03u %03u\n", bytes[0]&0xF0, bytes[0]&0xF, bytes[1], bytes[2]);
break;
case SND_SEQ_EVENT_KEYPRESS:
bytes[0] = 0x90 + ev->data.control.channel;
bytes[1] = ev->data.note.note;
bytes[2] = ev->data.note.velocity;
if (!arguments.silent && arguments.verbose)
printf("Alsa 0x%x Pressure change %03u %03u %03u\n", bytes[0]&0xF0, bytes[0]&0xF, bytes[1], bytes[2]);
break;
case SND_SEQ_EVENT_CONTROLLER:
bytes[0] = 0xB0 + ev->data.control.channel;
bytes[1] = ev->data.control.param;
bytes[2] = ev->data.control.value;
if (!arguments.silent && arguments.verbose)
printf("Alsa 0x%x Controller change %03u %03u %03u\n", bytes[0]&0xF0, bytes[0]&0xF, bytes[1], bytes[2]);
break;
case SND_SEQ_EVENT_PGMCHANGE:
bytes[0] = 0xC0 + ev->data.control.channel;
bytes[1] = ev->data.control.value;
if (!arguments.silent && arguments.verbose)
printf("Alsa 0x%x Program change %03u %03u %03u\n", bytes[0]&0xF0, bytes[0]&0xF, bytes[1], bytes[2]);
break;
case SND_SEQ_EVENT_CHANPRESS:
bytes[0] = 0xD0 + ev->data.control.channel;
bytes[1] = ev->data.control.value;
if (!arguments.silent && arguments.verbose)
printf("Alsa 0x%x Channel change %03u %03u %03u\n", bytes[0]&0xF0, bytes[0]&0xF, bytes[1], bytes[2]);
break;
case SND_SEQ_EVENT_PITCHBEND:
bytes[0] = 0xE0 + ev->data.control.channel;
ev->data.control.value += 8192;
bytes[1] = (int)ev->data.control.value & 0x7F;
bytes[2] = (int)ev->data.control.value >> 7;
if (!arguments.silent && arguments.verbose)
printf("Alsa 0x%x Pitch bend %03u %5d\n", bytes[0]&0xF0, bytes[0]&0xF, ev->data.control.value);
break;
default:
break;
}
if (bytes[0]!=0x00)
{
bytes[1] = (bytes[1] & 0x7F); // just to be sure that one bit is really zero
if (bytes[2]==0xFF) {
write(serial, bytes, 2);
} else {
bytes[2] = (bytes[2] & 0x7F);
write(serial, bytes, 3);
}
}
snd_seq_free_event(ev);
} while (snd_seq_event_input_pending(seq_handle, 0) > 0);
}
void* read_midi_from_alsa(void* seq)
{
int npfd;
struct pollfd* pfd;
snd_seq_t* seq_handle;
seq_handle = seq;
npfd = snd_seq_poll_descriptors_count(seq_handle, POLLIN);
pfd = (struct pollfd*) alloca(npfd * sizeof(struct pollfd));
snd_seq_poll_descriptors(seq_handle, pfd, npfd, POLLIN);
while (run)
{
if (poll(pfd,npfd, 100) > 0)
{
write_midi_action_to_serial_port(seq_handle);
}
}
printf("\nStopping [PC]->[Hardware] communication...");
}
void* read_midi_from_serial_port(void* seq)
{
char buf[3], msg[MAX_MSG_SIZE];
int i, msglen;
/* Lets first fast forward to first status byte... */
if (!arguments.printonly) {
do read(serial, buf, 1);
while (buf[0] >> 7 == 0);
}
while (run)
{
/*
* super-debug mode: only print to screen whatever
* comes through the serial port.
*/
if (arguments.printonly)
{
read(serial, buf, 1);
printf("%x\t", (int) buf[0]&0xFF);
fflush(stdout);
continue;
}
/*
* so let's align to the beginning of a midi command.
*/
int i = 1;
while (i < 3) {
read(serial, buf+i, 1);
if (buf[i] >> 7 != 0) {
/* Status byte received and will always be first bit!*/
buf[0] = buf[i];
i = 1;
} else {
/* Data byte received */
if (i == 2) {
/* It was 2nd data byte so we have a MIDI event
process! */
i = 3;
} else {
/* Lets figure out are we done or should we read one more byte. */
if ((buf[0] & 0xF0) == 0xC0 || (buf[0] & 0xF0) == 0xD0) {
i = 3;
} else {
i = 2;
}
}
}
}
/* print comment message (the ones that start with 0xFF 0x00 0x00 */
if (buf[0] == (char) 0xFF && buf[1] == (char) 0x00 && buf[2] == (char) 0x00)
{
read(serial, buf, 1);
msglen = buf[0];
if (msglen > MAX_MSG_SIZE-1) msglen = MAX_MSG_SIZE-1;
read(serial, msg, msglen);
if (arguments.silent) continue;
/* make sure the string ends with a null character */
msg[msglen] = 0;
puts("0xFF Non-MIDI message: ");
puts(msg);
putchar('\n');
fflush(stdout);
}
/* parse MIDI message */
else parse_midi_command(seq, port_out_id, buf);
}
}
/* --------------------------------------------------------------------- */
// Main program
main(int argc, char** argv)
{
//arguments arguments;
struct termios oldtio, newtio;
struct serial_struct ser_info;
char* modem_device = "/dev/ttyS0";
snd_seq_t *seq;
arg_set_defaults(&arguments);
argp_parse(&argp, argc, argv, 0, 0, &arguments);
/*
* Open MIDI output port
*/
port_out_id = open_seq(&seq);
/*
* Open modem device for reading and not as controlling tty because we don't
* want to get killed if linenoise sends CTRL-C.
*/
serial = open(arguments.serialdevice, O_RDWR | O_NOCTTY );
if (serial < 0)
{
perror(arguments.serialdevice);
exit(-1);
}
/* save current serial port settings */
tcgetattr(serial, &oldtio);
/* clear struct for new port settings */
bzero(&newtio, sizeof(newtio));
/*
* BAUDRATE : Set bps rate. You could also use cfsetispeed and cfsetospeed.
* CRTSCTS : output hardware flow control (only used if the cable has
* all necessary lines. See sect. 7 of Serial-HOWTO)
* CS8 : 8n1 (8bit, no parity, 1 stopbit)
* CLOCAL : local connection, no modem contol
* CREAD : enable receiving characters
*/
newtio.c_cflag = arguments.baudrate | CS8 | CLOCAL | CREAD; // CRTSCTS removed
/*
* IGNPAR : ignore bytes with parity errors
* ICRNL : map CR to NL (otherwise a CR input on the other computer
* will not terminate input)
* otherwise make device raw (no other input processing)
*/
newtio.c_iflag = IGNPAR;
/* Raw output */
newtio.c_oflag = 0;
/*
* ICANON : enable canonical input
* disable all echo functionality, and don't send signals to calling program
*/
newtio.c_lflag = 0; // non-canonical
/*
* set up: we'll be reading 4 bytes at a time.
*/
newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
newtio.c_cc[VMIN] = 1; /* blocking read until n character arrives */
/*
* now clean the modem line and activate the settings for the port
*/
tcflush(serial, TCIFLUSH);
tcsetattr(serial, TCSANOW, &newtio);
// Linux-specific: enable low latency mode (FTDI "nagling off")
// ioctl(serial, TIOCGSERIAL, &ser_info);
// ser_info.flags |= ASYNC_LOW_LATENCY;
// ioctl(serial, TIOCSSERIAL, &ser_info);
if (arguments.printonly)
{
printf("Super debug mode: Only printing the signal to screen. Nothing else.\n");
}
/*
* read commands
*/
/* Starting thread that is polling alsa midi in port */
pthread_t midi_out_thread, midi_in_thread;
int iret1, iret2;
run = TRUE;
iret1 = pthread_create(&midi_out_thread, NULL, read_midi_from_alsa, (void*) seq);
/* And also thread for polling serial data. As serial is currently read in
blocking mode, by this we can enable ctrl+c quiting and avoid zombie
alsa ports when killing app with ctrl+z */
iret2 = pthread_create(&midi_in_thread, NULL, read_midi_from_serial_port, (void*) seq);
signal(SIGINT, exit_cli);
signal(SIGTERM, exit_cli);
while (run)
{
sleep(100);
}
void* status;
pthread_join(midi_out_thread, &status);
/* restore the old port settings */
tcsetattr(serial, TCSANOW, &oldtio);
printf("\ndone!\n");
}