Added boilerplate interface code

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2024-10-11 07:51:18 +0200
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2024-10-11 07:51:18 +0200
Commit 920bdc358831087c46db736120a718ed9a75fbd4 (patch)
-rw-r--r-- Makefile 2
-rw-r--r-- examples/sandbox.lua 30
-rw-r--r-- interface.c 14
-rw-r--r-- interface.h 7
-rw-r--r-- main.c 29
-rw-r--r-- midi.c 162
-rw-r--r-- mutex.h 6
-rw-r--r-- synth.h 4
-rw-r--r-- version.h 16
9 files changed, 167 insertions, 103 deletions
diff --git a/Makefile b/Makefile
1
CC      := cc
1
CC      := cc
2
CFLAGS  := -Wall -Wextra -Wshadow -Wunused -Wswitch-enum -Wpedantic -ggdb
2
CFLAGS  := -Wall -Wextra -Wshadow -Wunused -Wswitch-enum -Wpedantic -ggdb
3
LDFLAGS := -lm -ldl -lpthread -lasound
3
LDFLAGS := -lm -ldl -lpthread -lasound
4
FILES   := main.c midi.c synth.c mutex.c minisdl_audio.c
4
FILES   := main.c midi.c synth.c interface.c mutex.c minisdl_audio.c
5
PROG    := ttdaw
5
PROG    := ttdaw
6
  
6
  
7
$(PROG): main.c
7
$(PROG): main.c
...
diff --git a/examples/sandbox.lua b/examples/sandbox.lua
  
1
-- Song file structure
  
2
-- This is just an outline of it and this will change.
  
3
  
  
4
-- API specification:
  
5
--   note_on(note, sf_preset, effect)
  
6
--   note_off(note)
  
7
--   delay(millisecons)
  
8
--   play_pattern(pattern_name, repeat)
  
9
  
  
10
-- Effects:
  
11
--   delay
  
12
--   reverb
  
13
  
  
14
-- This is a pattern definition.
  
15
-- Columns go from A..F and rows go from 1..4.
  
16
-- This blocks will be visualized in the DAW.
  
17
pattern("A1", function(self)
  
18
	for i = 1, 10 do
  
19
		note_on(40 + i, 1, nil)
  
20
		delay(10)
  
21
	end
  
22
end)
  
23
  
  
24
-- This is the actual song timeline.
  
25
timeline(function(self)
  
26
	play_pattern("A1", 1)
  
27
	play_pattern("F2", 3)
  
28
	play_pattern("D4", 2)
  
29
end)
  
30
  
diff --git a/interface.c b/interface.c
  
1
#include <stdio.h>
  
2
#include <unistd.h>
  
3
  
1
#include "interface.h"
4
#include "interface.h"
  
5
#include "mutex.h"
2
  
6
  
  
7
void *interface(void *arg) {
  
8
	InterfaceArgs* args = (InterfaceArgs*)arg;
  
9
	(void)args;
  
10
  
  
11
	while(1) {
  
12
		// Do the thread stuff here.
  
13
		sleep(1);
  
14
		fprintf(stdout, "hi from interface thread\n");
  
15
	}
  
16
}
diff --git a/interface.h b/interface.h
1
#ifndef INTERFACE_H
1
#ifndef INTERFACE_H
2
#define INTERFACE_H
2
#define INTERFACE_H
3
  
3
  
  
4
typedef struct {
  
5
	char *soundfont_file;
  
6
	int soundfont_preset;
  
7
} InterfaceArgs;
  
8
  
  
9
void *interface(void *arg);
  
10
  
4
#endif // INTERFACE_H
11
#endif // INTERFACE_H
5
  
12
  
diff --git a/main.c b/main.c
...
7
#include "version.h"
7
#include "version.h"
8
#include "midi.h"
8
#include "midi.h"
9
#include "synth.h"
9
#include "synth.h"
  
10
#include "interface.h"
10
#include "mutex.h"
11
#include "mutex.h"
11
  
12
  
12
void help(const char *argv0) {
13
void help(const char *argv0) {
...
27
		{ "list", 0, NULL, 'l' },
28
		{ "list", 0, NULL, 'l' },
28
		{ "client", 1, NULL, 'c' },
29
		{ "client", 1, NULL, 'c' },
29
		{ "soundfont", 1, NULL, 's' },
30
		{ "soundfont", 1, NULL, 's' },
30
		{ "preset", 1, NULL, 'p' },
31
		{ "preset", 0, NULL, 'p' },
31
		{ "help", 0, NULL, 'h' },
32
		{ "help", 0, NULL, 'h' },
32
		{ "version", 0, NULL, 'v' },
33
		{ "version", 0, NULL, 'v' },
33
		{ 0 },
34
		{ 0 },
...
35
  
36
  
36
	char *port_name = NULL;
37
	char *port_name = NULL;
37
	char *soundfont_file = NULL;
38
	char *soundfont_file = NULL;
38
	int soundfont_preset = -1;
39
	int soundfont_preset = 0;
39
  
40
  
40
	int opt;
41
	int opt;
41
	while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
42
	while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
...
56
				help(argv[0]);
57
				help(argv[0]);
57
				return 0;
58
				return 0;
58
			case 'v':
59
			case 'v':
59
				fprintf(stdout, "ttdaw version %s\n", TTDAW_VERSION);
60
				fprintf(stdout, "ttdaw version %s\n", VERSION);
60
				fprintf(stdout, "Website: %s.\n", TTDAW_WEBSITE);
61
				fprintf(stdout, "Website: %s.\n", WEBSITE);
61
				fprintf(stdout, "%s\n", TTDAW_LICENSE);
62
				fprintf(stdout, "%s\n", LICENSE);
62
				fprintf(stdout, "%s\n", TTDAW_WARRANTY);
63
				fprintf(stdout, "%s\n", WARRANTY);
63
				fprintf(stdout, "\n%s\n", TTDAW_AUTHOR);
64
				fprintf(stdout, "\n%s\n", AUTHOR);
64
				return 0;
65
				return 0;
65
			default:
66
			default:
66
				fprintf(stdout, "Missing options. Check help.\n");
67
				fprintf(stdout, "Missing options. Check help.\n");
...
102
		return 1;
103
		return 1;
103
	}
104
	}
104
  
105
  
  
106
	// Create UI thread.
  
107
	pthread_t interface_thread;
  
108
	InterfaceArgs interface_args = {
  
109
		.soundfont_file = soundfont_file,
  
110
		.soundfont_preset = soundfont_preset,
  
111
	};
  
112
  
  
113
	if (pthread_create(&interface_thread, NULL, interface, (void*)&interface_args) != 0) {
  
114
		fprintf(stderr, "Error creating interface thread\n");
  
115
		return 1;
  
116
	}
  
117
  
  
118
	// Start threads.
105
	pthread_join(midi_thread, NULL);
119
	pthread_join(midi_thread, NULL);
106
	pthread_join(synth_thread, NULL);
120
	pthread_join(synth_thread, NULL);
  
121
	pthread_join(interface_thread, NULL);
107
  
122
  
108
	// Destroy mutex.
123
	// Destroy mutex.
109
	destroy_mutex();
124
	destroy_mutex();
...
diff --git a/midi.c b/midi.c
...
13
void *midi(void *arg) {
13
void *midi(void *arg) {
14
	MidiArgs* args = (MidiArgs*)arg;
14
	MidiArgs* args = (MidiArgs*)arg;
15
  
15
  
16
	while (1) {
16
	if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
17
		if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
17
		fprintf(stderr, "Error opening ALSA sequencer.\n");
18
			fprintf(stderr, "Error opening ALSA sequencer.\n");
18
		exit(1);
19
			exit(1);
19
	}
20
		}
  
21
  
20
  
22
		if (snd_seq_set_client_name(seq_handle, CLIENT_NAME) < 0) {
21
	if (snd_seq_set_client_name(seq_handle, CLIENT_NAME) < 0) {
23
			fprintf(stderr, "Could not set up client name.\n");
22
		fprintf(stderr, "Could not set up client name.\n");
24
			exit(1);
23
		exit(1);
25
		}
24
	}
26
  
25
  
27
		if (snd_seq_create_simple_port(seq_handle, CLIENT_NAME,
26
	if (snd_seq_create_simple_port(seq_handle, CLIENT_NAME,
28
					SND_SEQ_PORT_CAP_WRITE |
27
				SND_SEQ_PORT_CAP_WRITE |
29
					SND_SEQ_PORT_CAP_SUBS_WRITE,
28
				SND_SEQ_PORT_CAP_SUBS_WRITE,
30
					SND_SEQ_PORT_TYPE_MIDI_GENERIC |
29
				SND_SEQ_PORT_TYPE_MIDI_GENERIC |
31
					SND_SEQ_PORT_TYPE_APPLICATION) < 0) {
30
				SND_SEQ_PORT_TYPE_APPLICATION) < 0) {
32
			fprintf(stderr, "Error creating sequencer port.\n");
31
		fprintf(stderr, "Error creating sequencer port.\n");
33
			exit(1);
32
		exit(1);
34
		}
33
	}
35
  
34
  
36
		// Connecting ports.
35
	// Connecting ports.
37
		ports = realloc(ports, MAX_MIDI_PORTS * sizeof(snd_seq_addr_t));
36
	ports = realloc(ports, MAX_MIDI_PORTS * sizeof(snd_seq_addr_t));
38
		if (snd_seq_parse_address(seq_handle, &ports[0], args->port_name) < 0) {
37
	if (snd_seq_parse_address(seq_handle, &ports[0], args->port_name) < 0) {
39
			fprintf(stderr, "Invalid port %s.\n", args->port_name);
38
		fprintf(stderr, "Invalid port %s.\n", args->port_name);
40
			exit(1);
39
		exit(1);
41
		}
40
	}
42
  
  
43
		// Listing assigned ports.
  
44
		fprintf(stdout, "Ports:\n");
  
45
		for (int j = 0; j < MAX_MIDI_PORTS; j++) {
  
46
			fprintf(stdout, " client: %d, port: %d\n", ports[j].client, ports[j].port);
  
47
		}
  
48
  
41
  
49
		// Connecting ports.
42
	// Listing assigned ports.
50
		for (int i = 0; i < MAX_MIDI_PORTS; ++i) {
43
	fprintf(stdout, "Ports:\n");
51
			int err = snd_seq_connect_from(seq_handle, 0, ports[i].client, ports[i].port);
44
	for (int j = 0; j < MAX_MIDI_PORTS; j++) {
52
			if (err < 0) {
45
		fprintf(stdout, " client: %d, port: %d\n", ports[j].client, ports[j].port);
53
				fprintf(stderr, "Cannot connect from port %d:%d - %s", ports[i].client, ports[i].port, snd_strerror(err));
46
	}
54
				exit(1);
  
55
			}
  
56
		}
  
57
  
47
  
58
		if (snd_seq_nonblock(seq_handle, 1) < 0) {
48
	// Connecting ports.
59
			fprintf(stderr, "Set nonblock mode failed.");
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));
60
			exit(1);
53
			exit(1);
61
		}
54
		}
  
55
	}
62
  
56
  
  
57
	if (snd_seq_nonblock(seq_handle, 1) < 0) {
  
58
		fprintf(stderr, "Set nonblock mode failed.");
  
59
		exit(1);
  
60
	}
63
  
61
  
64
		// Reading MIDI device.
  
65
		struct pollfd *pfds;
  
66
		size_t npfds;
  
67
  
62
  
68
		npfds = snd_seq_poll_descriptors_count(seq_handle, POLLIN);
63
	// Reading MIDI device.
69
		pfds = alloca(sizeof(*pfds) * npfds);
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
		}
70
  
75
  
71
		for (;;) {
76
		for (;;) {
72
			snd_seq_poll_descriptors(seq_handle, pfds, npfds, POLLIN);
77
			snd_seq_event_t *ev;
73
			if (poll(pfds, npfds, -1) < 0) {
78
  
  
79
			if (snd_seq_event_input(seq_handle, &ev) < 0) {
74
				break;
80
				break;
75
			}
81
			}
76
  
82
  
77
			for (;;) {
83
			if (ev) {
78
				snd_seq_event_t *ev;
84
				pthread_mutex_lock(&mutex);
  
85
				switch (ev->type) {
  
86
					case SND_SEQ_EVENT_NOTEON:
  
87
						shared_data.note = ev->data.note.note;
  
88
						shared_data.state = 1;
  
89
						shared_data.velocity = ev->data.note.velocity;
  
90
						printf("%3d:%-3dNote on  %2d, note %d, velocity: %3d\n",
  
91
								ev->source.client, ev->source.port,
  
92
								ev->data.note.channel,
  
93
								ev->data.note.note,
  
94
								ev->data.note.velocity);
  
95
						break;
79
  
96
  
80
				if (snd_seq_event_input(seq_handle, &ev) < 0) {
97
					case SND_SEQ_EVENT_NOTEOFF:
81
					break;
98
						shared_data.note = ev->data.note.note;
82
				}
99
						shared_data.state = 0;
  
100
						shared_data.velocity = 0;
  
101
						printf("%3d:%-3dNote off\t%2d, note %d\n",
  
102
								ev->source.client, ev->source.port,
  
103
								ev->data.note.channel,
  
104
								ev->data.note.note);
  
105
						break;
83
  
106
  
84
				if (ev) {
  
85
					pthread_mutex_lock(&mutex);
  
86
					switch (ev->type) {
  
87
						case SND_SEQ_EVENT_NOTEON:
  
88
							shared_data.note = ev->data.note.note;
  
89
							shared_data.state = 1;
  
90
							shared_data.velocity = ev->data.note.velocity;
  
91
							printf("%3d:%-3dNote on  %2d, note %d, velocity: %3d\n",
  
92
									ev->source.client, ev->source.port,
  
93
									ev->data.note.channel,
  
94
									ev->data.note.note,
  
95
									ev->data.note.velocity);
  
96
							break;
  
97
  
107
  
98
						case SND_SEQ_EVENT_NOTEOFF:
108
				}
99
							shared_data.note = ev->data.note.note;
  
100
							shared_data.state = 0;
  
101
							shared_data.velocity = 0;
  
102
							printf("%3d:%-3dNote off\t%2d, note %d\n",
  
103
									ev->source.client, ev->source.port,
  
104
									ev->data.note.channel,
  
105
									ev->data.note.note);
  
106
							break;
  
107
						default:
  
108
							break;
  
109
					}
  
110
  
109
  
111
					shared_data.action = 1;
110
				shared_data.action = 1;
112
  
111
  
113
					pthread_cond_signal(&cond_synth);
112
				pthread_cond_signal(&cond_synth);
114
					pthread_mutex_unlock(&mutex);
113
				pthread_mutex_unlock(&mutex);
115
				}
  
116
			}
114
			}
  
115
		}
117
  
116
  
118
			fflush(stdout);
117
		fflush(stdout);
119
		}
  
120
	}
118
	}
121
}
119
}
122
  
120
  
diff --git a/mutex.h b/mutex.h
1
#ifndef MUTEX_H
1
#ifndef MUTEX_H_
2
#define MUTEX_H
2
#define MUTEX_H_
3
  
3
  
4
#include <pthread.h>
4
#include <pthread.h>
5
  
5
  
...
19
void initialize_mutex();
19
void initialize_mutex();
20
void destroy_mutex();
20
void destroy_mutex();
21
  
21
  
22
#endif // MUTEX_H
22
#endif // MUTEX_H_
23
  
23
  
diff --git a/synth.h b/synth.h
...
2
#define SYNTH_H_
2
#define SYNTH_H_
3
  
3
  
4
#define AUDIO_FREQ 44100
4
#define AUDIO_FREQ 44100
5
#define AUDIO_SAMPLES 64
5
#define AUDIO_SAMPLES 128
6
#define AUDIO_CHANNELS 2
6
#define AUDIO_CHANNELS 2
7
  
7
  
8
typedef struct {
8
typedef struct {
...
12
  
12
  
13
void *synth(void *arg);
13
void *synth(void *arg);
14
  
14
  
15
#endif //  SYNTH_H_
15
#endif // SYNTH_H_
16
  
16
  
diff --git a/version.h b/version.h
1
#ifndef TTDAW_VERSION_H
1
#ifndef VERSION_H_
2
#define TTDAW_VERSION_H
2
#define VERSION_H_
3
  
3
  
4
#define TTDAW_VERSION  "0.1"
4
#define VERSION  "0.1"
5
#define TTDAW_WEBSITE  "https://github.com/mitjafelicijan/ttdaw"
5
#define WEBSITE  "https://github.com/mitjafelicijan/ttdaw"
6
#define TTDAW_LICENSE  "This is free software: you are free to change and redistribute it."
6
#define LICENSE  "This is free software: you are free to change and redistribute it."
7
#define TTDAW_WARRANTY "There is NO WARRANTY, to the extent permitted by law."
7
#define WARRANTY "There is NO WARRANTY, to the extent permitted by law."
8
#define TTDAW_AUTHOR   "Written by Mitja Felicijan <https://mitjafelicijan.com>."
8
#define AUTHOR   "Written by Mitja Felicijan <https://mitjafelicijan.com>."
9
  
9
  
10
#endif
10
#endif // VERSION_H_
11
  
11