aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2024-10-08 16:14:22 +0200
committerMitja Felicijan <mitja.felicijan@gmail.com>2024-10-08 16:14:22 +0200
commitc05e271a0507231eaf131930b5b9711ef85ae4a8 (patch)
tree768eeb6b03a2e7f5485d9a739618920a8f442d6f
parent65ffdf410ca5c36f059bee8619f87147f802a82e (diff)
downloadttdaw-c05e271a0507231eaf131930b5b9711ef85ae4a8.tar.gz
MIDI is now read in threaded way
-rw-r--r--Makefile4
-rw-r--r--documents/sfspec24.pdfbin0 -> 530464 bytes
-rw-r--r--main.c65
-rw-r--r--midi.c109
-rw-r--r--midi.h16
5 files changed, 175 insertions, 19 deletions
diff --git a/Makefile b/Makefile
index 41533ef..98a41d3 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,9 @@
1CC := cc 1CC := cc
2CFLAGS := -Wall -Wextra -Wshadow -Wunused -Wswitch-enum -Wpedantic -Wundef 2CFLAGS := -Wall -Wextra -Wshadow -Wunused -Wswitch-enum -Wpedantic -Wundef
3LDFLAGS := -lm -ldl -lpthread -lasound 3LDFLAGS := -lm -ldl -lpthread -lasound
4CFILES := main.c minisdl_audio.c 4FILES := main.c midi.c minisdl_audio.c
5PROG := ttdaw 5PROG := ttdaw
6 6
7$(PROG): main.c 7$(PROG): main.c
8 $(CC) $(CFLAGS) -o $(PROG) $(CFILES) $(LDFLAGS) 8 $(CC) $(CFLAGS) -o $(PROG) $(FILES) $(LDFLAGS)
9 9
diff --git a/documents/sfspec24.pdf b/documents/sfspec24.pdf
new file mode 100644
index 0000000..252aff9
--- /dev/null
+++ b/documents/sfspec24.pdf
Binary files differ
diff --git a/main.c b/main.c
index c196cdd..3a45592 100644
--- a/main.c
+++ b/main.c
@@ -2,32 +2,48 @@
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
8void help(const char *argv0) { 10void 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
18int main(int argc, char *argv[]) { 21int 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[] = {
21 { "help", 0, NULL, 'h' },
22 { "version", 0, NULL, 'v' },
23 { "list", 0, NULL, 'l' }, 24 { "list", 0, NULL, 'l' },
24 { "port", 1, NULL, 'p' }, 25 { "port", 1, NULL, 'p' },
26 { "soundfont", 1, NULL, 's' },
27 { "help", 0, NULL, 'h' },
28 { "version", 0, NULL, 'v' },
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,18 +54,33 @@ int main(int argc, char *argv[]) {
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
new file mode 100644
index 0000000..3d21f79
--- /dev/null
+++ b/midi.c
@@ -0,0 +1,109 @@
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
9static snd_seq_t *seq_handle;
10static snd_seq_addr_t *ports;
11
12void *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
diff --git a/midi.h b/midi.h
new file mode 100644
index 0000000..81b105b
--- /dev/null
+++ b/midi.h
@@ -0,0 +1,16 @@
1#include <alsa/asoundlib.h>
2
3#ifndef MIDI_H_
4#define MIDI_H_
5
6#define CLIENT_NAME "ttdaw"
7#define MAX_MIDI_PORTS 1
8
9typedef struct {
10 char *port_name;
11} MidiArgs;
12
13void *midi(void *arg);
14
15#endif // MIDI_H_
16