MIDI is now read in threaded way

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2024-10-08 16:14:22 +0200
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2024-10-08 16:14:22 +0200
Commit c05e271a0507231eaf131930b5b9711ef85ae4a8 (patch)
-rw-r--r-- Makefile 4
-rw-r--r-- documents/sfspec24.pdf bin 0 B -> 518.0 KB
-rw-r--r-- main.c 65
-rw-r--r-- midi.c 109
-rw-r--r-- midi.h 16
5 files changed, 175 insertions, 19 deletions
diff --git a/Makefile b/Makefile
1
CC      := cc
1
CC      := cc
2
CFLAGS  := -Wall -Wextra -Wshadow -Wunused -Wswitch-enum -Wpedantic -Wundef
2
CFLAGS  := -Wall -Wextra -Wshadow -Wunused -Wswitch-enum -Wpedantic -Wundef
3
LDFLAGS := -lm -ldl -lpthread -lasound
3
LDFLAGS := -lm -ldl -lpthread -lasound
4
CFILES  := main.c minisdl_audio.c
4
FILES   := main.c midi.c minisdl_audio.c
5
PROG    := ttdaw
5
PROG    := 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
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
  
diff --git a/midi.h b/midi.h
  
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
  
  
9
typedef struct {
  
10
	char *port_name;
  
11
} MidiArgs;
  
12
  
  
13
void *midi(void *arg);
  
14
  
  
15
#endif //  MIDI_H_
  
16