|
diff --git a/main.c b/main.c
|
| ... |
| 2 |
#include <stdlib.h> |
2 |
#include <stdlib.h> |
| 3 |
#include <stdarg.h> |
3 |
#include <stdarg.h> |
| 4 |
#include <getopt.h> |
4 |
#include <getopt.h> |
|
|
5 |
#include <pthread.h> |
| 5 |
|
6 |
|
| 6 |
#include "version.h" |
7 |
#include "version.h" |
|
|
8 |
#include "midi.h" |
| 7 |
|
9 |
|
| 8 |
void help(const char *argv0) { |
10 |
void help(const char *argv0) { |
| 9 |
printf("Usage: %s [options]\n" |
11 |
printf("Usage: %s [options]\n" |
| 10 |
"\nAvailable options:\n" |
12 |
"\nAvailable options:\n" |
| 11 |
" -h,--help this help\n" |
13 |
" -l,--list list available devices\n" |
| 12 |
" -v,--version show version\n" |
14 |
" -p,--port=client:port device port\n" |
| 13 |
" -l,--list list available devices\n" |
15 |
" -s,--soundfont=file.sf2 soundfont file\n" |
| 14 |
" -p,--port=client:port device port\n", |
16 |
" -h,--help this help\n" |
|
|
17 |
" -v,--version show version\n", |
| 15 |
argv0); |
18 |
argv0); |
| 16 |
} |
19 |
} |
| 17 |
|
20 |
|
| 18 |
int main(int argc, char *argv[]) { |
21 |
int main(int argc, char *argv[]) { |
| 19 |
const char short_options[] = "hvlp"; |
22 |
const char short_options[] = "lp:s:hv:"; |
| 20 |
const struct option long_options[] = { |
23 |
const struct option long_options[] = { |
|
|
24 |
{ "list", 0, NULL, 'l' }, |
|
|
25 |
{ "port", 1, NULL, 'p' }, |
|
|
26 |
{ "soundfont", 1, NULL, 's' }, |
| 21 |
{ "help", 0, NULL, 'h' }, |
27 |
{ "help", 0, NULL, 'h' }, |
| 22 |
{ "version", 0, NULL, 'v' }, |
28 |
{ "version", 0, NULL, 'v' }, |
| 23 |
{ "list", 0, NULL, 'l' }, |
|
|
| 24 |
{ "port", 1, NULL, 'p' }, |
|
|
| 25 |
{ 0 }, |
29 |
{ 0 }, |
| 26 |
}; |
30 |
}; |
| 27 |
|
31 |
|
| 28 |
int c; |
32 |
char *port_name = NULL; |
| 29 |
while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { |
33 |
char *soundfont_file = NULL; |
| 30 |
switch (c) { |
34 |
|
|
|
35 |
int opt; |
|
|
36 |
while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { |
|
|
37 |
switch (opt) { |
|
|
38 |
case 'l': |
|
|
39 |
fprintf(stderr, "List feature is NOT implemented yet.\n"); |
|
|
40 |
return 0; |
|
|
41 |
case 'p': |
|
|
42 |
port_name = optarg; |
|
|
43 |
break; |
|
|
44 |
case 's': |
|
|
45 |
soundfont_file = optarg; |
|
|
46 |
break; |
| 31 |
case 'h': |
47 |
case 'h': |
| 32 |
help(argv[0]); |
48 |
help(argv[0]); |
| 33 |
return 0; |
49 |
return 0; |
| ... |
| 38 |
fprintf(stdout, "%s\n", TTDAW_WARRANTY); |
54 |
fprintf(stdout, "%s\n", TTDAW_WARRANTY); |
| 39 |
fprintf(stdout, "\n%s\n", TTDAW_AUTHOR); |
55 |
fprintf(stdout, "\n%s\n", TTDAW_AUTHOR); |
| 40 |
return 0; |
56 |
return 0; |
| 41 |
case 'l': |
|
|
| 42 |
fprintf(stderr, "List feature is NOT implemented yet.\n"); |
|
|
| 43 |
return 0; |
|
|
| 44 |
case 'p': |
|
|
| 45 |
fprintf(stderr, "Port feature is NOT implemented yet.\n"); |
|
|
| 46 |
return 0; |
|
|
| 47 |
default: |
57 |
default: |
| 48 |
fprintf(stdout, "No option provided\n"); |
58 |
fprintf(stdout, "Missing options. Check help.\n"); |
| 49 |
return 0; |
59 |
return 0; |
| 50 |
} |
60 |
} |
| 51 |
} |
61 |
} |
| 52 |
|
62 |
|
|
|
63 |
if (port_name == NULL || soundfont_file == NULL) { |
|
|
64 |
fprintf(stdout, "Missing options. Check help.\n\n"); |
|
|
65 |
fprintf(stdout, "Port and soundfile are required fields.\n\n"); |
|
|
66 |
return 1; |
|
|
67 |
} |
|
|
68 |
|
|
|
69 |
fprintf(stdout, "> Device port: %s\n", port_name); |
|
|
70 |
fprintf(stdout, "> Soundfont: %s\n", soundfont_file); |
|
|
71 |
|
|
|
72 |
// Create and start MIDI thread. |
|
|
73 |
pthread_t midi_thread; |
|
|
74 |
MidiArgs midi_args = { port_name }; |
|
|
75 |
|
|
|
76 |
if (pthread_create(&midi_thread, NULL, midi, (void*)&midi_args) != 0) { |
|
|
77 |
fprintf(stderr, "Error creating midi thread\n"); |
|
|
78 |
return 1; |
|
|
79 |
} |
|
|
80 |
|
|
|
81 |
pthread_join(midi_thread, NULL); |
|
|
82 |
|
|
|
83 |
fprintf(stdout, "Exiting...\n"); |
| 53 |
return 0; |
84 |
return 0; |
| 54 |
} |
85 |
} |
| 55 |
|
86 |
|
|
diff --git a/midi.c b/midi.c
|
|
|
1 |
#include <stdio.h> |
|
|
2 |
#include <stdlib.h> |
|
|
3 |
#include <alloca.h> |
|
|
4 |
#include <unistd.h> |
|
|
5 |
#include <alsa/asoundlib.h> |
|
|
6 |
|
|
|
7 |
#include "midi.h" |
|
|
8 |
|
|
|
9 |
static snd_seq_t *seq_handle; |
|
|
10 |
static snd_seq_addr_t *ports; |
|
|
11 |
|
|
|
12 |
void *midi(void *arg) { |
|
|
13 |
MidiArgs* args = (MidiArgs*)arg; |
|
|
14 |
|
|
|
15 |
while (1) { |
|
|
16 |
if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) { |
|
|
17 |
fprintf(stderr, "Error opening ALSA sequencer.\n"); |
|
|
18 |
exit(1); |
|
|
19 |
} |
|
|
20 |
|
|
|
21 |
if (snd_seq_set_client_name(seq_handle, CLIENT_NAME) < 0) { |
|
|
22 |
fprintf(stderr, "Could not set up client name.\n"); |
|
|
23 |
exit(1); |
|
|
24 |
} |
|
|
25 |
|
|
|
26 |
if (snd_seq_create_simple_port(seq_handle, CLIENT_NAME, |
|
|
27 |
SND_SEQ_PORT_CAP_WRITE | |
|
|
28 |
SND_SEQ_PORT_CAP_SUBS_WRITE, |
|
|
29 |
SND_SEQ_PORT_TYPE_MIDI_GENERIC | |
|
|
30 |
SND_SEQ_PORT_TYPE_APPLICATION) < 0) { |
|
|
31 |
fprintf(stderr, "Error creating sequencer port.\n"); |
|
|
32 |
exit(1); |
|
|
33 |
} |
|
|
34 |
|
|
|
35 |
// Connecting ports. |
|
|
36 |
ports = realloc(ports, MAX_MIDI_PORTS * sizeof(snd_seq_addr_t)); |
|
|
37 |
if (snd_seq_parse_address(seq_handle, &ports[0], args->port_name) < 0) { |
|
|
38 |
fprintf(stderr, "Invalid port %s.\n", args->port_name); |
|
|
39 |
exit(1); |
|
|
40 |
} |
|
|
41 |
|
|
|
42 |
// Listing assigned ports. |
|
|
43 |
fprintf(stdout, "Ports:\n"); |
|
|
44 |
for (int j = 0; j < MAX_MIDI_PORTS; j++) { |
|
|
45 |
fprintf(stdout, " client: %d, port: %d\n", ports[j].client, ports[j].port); |
|
|
46 |
} |
|
|
47 |
|
|
|
48 |
// Connecting ports. |
|
|
49 |
for (int i = 0; i < MAX_MIDI_PORTS; ++i) { |
|
|
50 |
int err = snd_seq_connect_from(seq_handle, 0, ports[i].client, ports[i].port); |
|
|
51 |
if (err < 0) { |
|
|
52 |
fprintf(stderr, "Cannot connect from port %d:%d - %s", ports[i].client, ports[i].port, snd_strerror(err)); |
|
|
53 |
exit(1); |
|
|
54 |
} |
|
|
55 |
} |
|
|
56 |
|
|
|
57 |
if (snd_seq_nonblock(seq_handle, 1) < 0) { |
|
|
58 |
fprintf(stderr, "Set nonblock mode failed."); |
|
|
59 |
exit(1); |
|
|
60 |
} |
|
|
61 |
|
|
|
62 |
|
|
|
63 |
// Reading MIDI device. |
|
|
64 |
struct pollfd *pfds; |
|
|
65 |
size_t npfds; |
|
|
66 |
|
|
|
67 |
npfds = snd_seq_poll_descriptors_count(seq_handle, POLLIN); |
|
|
68 |
pfds = alloca(sizeof(*pfds) * npfds); |
|
|
69 |
|
|
|
70 |
for (;;) { |
|
|
71 |
snd_seq_poll_descriptors(seq_handle, pfds, npfds, POLLIN); |
|
|
72 |
if (poll(pfds, npfds, -1) < 0) { |
|
|
73 |
break; |
|
|
74 |
} |
|
|
75 |
|
|
|
76 |
for (;;) { |
|
|
77 |
snd_seq_event_t *ev; |
|
|
78 |
|
|
|
79 |
if (snd_seq_event_input(seq_handle, &ev) < 0) { |
|
|
80 |
break; |
|
|
81 |
} |
|
|
82 |
|
|
|
83 |
if (ev) { |
|
|
84 |
switch (ev->type) { |
|
|
85 |
case SND_SEQ_EVENT_NOTEON: |
|
|
86 |
printf("%3d:%-3dNote on %2d, note %d, velocity: %3d\n", |
|
|
87 |
ev->source.client, ev->source.port, |
|
|
88 |
ev->data.note.channel, |
|
|
89 |
ev->data.note.note, |
|
|
90 |
ev->data.note.velocity); |
|
|
91 |
break; |
|
|
92 |
|
|
|
93 |
case SND_SEQ_EVENT_NOTEOFF: |
|
|
94 |
printf("%3d:%-3dNote off\t%2d, note %d\n", |
|
|
95 |
ev->source.client, ev->source.port, |
|
|
96 |
ev->data.note.channel, |
|
|
97 |
ev->data.note.note); |
|
|
98 |
break; |
|
|
99 |
|
|
|
100 |
|
|
|
101 |
} |
|
|
102 |
} |
|
|
103 |
} |
|
|
104 |
|
|
|
105 |
fflush(stdout); |
|
|
106 |
} |
|
|
107 |
} |
|
|
108 |
} |
|
|
109 |
|