Added basic interface and keyboard capture of terminal

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2024-10-12 06:14:14 +0200
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2024-10-12 06:14:14 +0200
Commit 39bd41ad4d4832d4022a99e3cfcc49409f8b2da6 (patch)
-rw-r--r-- documents/Standard MIDI Files Specification, v1.0.pdf bin 0 B -> 82.1 KB
-rw-r--r-- documents/SoundFont Technical Specification, v2.04.pdf bin 518.0 KB -> 518.0 KB
-rw-r--r-- examples/sandbox.lua 12
-rw-r--r-- interface.c 100
-rw-r--r-- main.c 45
-rw-r--r-- midi.c 30
-rw-r--r-- synth.c 3
7 files changed, 149 insertions, 41 deletions
diff --git a/documents/Standard MIDI Files Specification, v1.0.pdf b/documents/Standard MIDI Files Specification, v1.0.pdf
diff --git a/documents/SoundFont Technical Specification, v2.04.pdf b/documents/SoundFont Technical Specification, v2.04.pdf
diff --git a/examples/sandbox.lua b/examples/sandbox.lua
...
5
--   note_on(note, sf_preset, effect)
5
--   note_on(note, sf_preset, effect)
6
--   note_off(note)
6
--   note_off(note)
7
--   delay(millisecons)
7
--   delay(millisecons)
8
--   play_pattern(pattern_name, repeat)
8
--   play_block(pattern_name, repeat)
9
  
9
  
10
-- Effects:
10
-- Effects:
11
--   delay
11
--   delay
12
--   reverb
12
--   reverb
13
  
13
  
14
-- This is a pattern definition.
14
-- This is a block/pattern definition.
15
-- Columns go from A..F and rows go from 1..4.
15
-- Columns go from A..F and rows go from 1..4.
16
-- This blocks will be visualized in the DAW.
16
-- This blocks will be visualized in the DAW.
17
pattern("A1", function(self)
17
block("A1", function(self)
18
	for i = 1, 10 do
18
	for i = 1, 10 do
19
		note_on(40 + i, 1, nil)
19
		note_on(40 + i, 1, nil)
20
		delay(10)
20
		delay(10)
...
23
  
23
  
24
-- This is the actual song timeline.
24
-- This is the actual song timeline.
25
timeline(function(self)
25
timeline(function(self)
26
	play_pattern("A1", 1)
26
	play_block("A1", 1)
27
	play_pattern("F2", 3)
27
	play_block("F2", 3)
28
	play_pattern("D4", 2)
28
	play_block("D4", 2)
29
end)
29
end)
30
  
30
  
diff --git a/interface.c b/interface.c
  
1
#include <stdlib.h>
1
#include <stdio.h>
2
#include <stdio.h>
  
3
#include <locale.h>
2
#include <unistd.h>
4
#include <unistd.h>
3
  
5
  
4
#include "interface.h"
6
#include "interface.h"
5
#include "mutex.h"
7
#include "mutex.h"
6
  
8
  
  
9
#define TB_IMPL
  
10
#include "termbox2.h"
  
11
  
  
12
static int block_width = 14;
  
13
static int block_height = 4;
  
14
  
  
15
void draw_block(int x, int y, const char *label, int empty) {
  
16
  
  
17
	tb_set_cell(x, y, 0x250C, TB_WHITE, TB_DEFAULT);
  
18
    tb_set_cell(x+block_width-1, y, 0x2510, TB_WHITE, TB_DEFAULT);
  
19
    tb_set_cell(x, y+block_height-1, 0x2514, TB_WHITE, TB_DEFAULT);
  
20
    tb_set_cell(x+block_width-1, y+block_height-1, 0x2518, TB_WHITE, TB_DEFAULT);
  
21
	
  
22
	tb_printf(x+(block_width/2) - (strlen(label)/2), y+(block_height/2)-1 ,TB_YELLOW, 0, label);
  
23
	tb_printf(x+(block_width/2) - (strlen("empty")/2), y+(block_height/2) ,TB_DIM, 0, "empty");
  
24
	
  
25
	tb_present();
  
26
}
  
27
  
  
28
void draw_blocks() {
  
29
	int offset_x = 0;
  
30
	int offset_y = 6;
  
31
	char col_names[6] = {'A', 'B', 'C', 'D', 'E', 'F'};
  
32
  
  
33
	for (int r = 0; r < 4; r++) {
  
34
		for (int c = 0; c < 6; c++) {
  
35
			char label[3];
  
36
            label[0] = col_names[c];
  
37
            label[1] = '1' + r;
  
38
            label[2] = '\0';
  
39
			draw_block(offset_x+(block_width*c), offset_y+(block_height*r), label, 0);
  
40
		}
  
41
	}
  
42
}
  
43
  
  
44
void draw_help_tooltip(const char* tooltip_text) {
  
45
	tb_printf(tb_width() - strlen(tooltip_text) - 1, tb_height()-1, TB_DIM, 0, tooltip_text);
  
46
	tb_present();
  
47
}
  
48
  
7
void *interface(void *arg) {
49
void *interface(void *arg) {
8
	InterfaceArgs* args = (InterfaceArgs*)arg;
50
	InterfaceArgs* args = (InterfaceArgs*)arg;
9
	(void)args;
51
  
  
52
	int ret;
  
53
	setlocale(LC_ALL, "");
  
54
  
  
55
	ret = tb_init();
  
56
	if (ret) {
  
57
		fprintf(stderr, "tb_init() failed with error code %d\n", ret);
  
58
		exit(1);
  
59
	}
  
60
  
  
61
	tb_set_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE);
  
62
	struct tb_event ev;
  
63
  
  
64
	tb_clear();
  
65
	tb_present();
  
66
  
  
67
	// Show currently selected soundfont.
  
68
	tb_printf(0, 0, TB_GREEN, 0, "Soundfont: %s", args->soundfont_file);
  
69
	tb_printf(0, 1, TB_GREEN, 0, "Preset: %s", args->soundfont_preset);
  
70
	tb_present();
  
71
  
  
72
	// Draw interface.
  
73
	draw_help_tooltip("Ctrl+q - Quit");
  
74
	draw_blocks();
  
75
	/* draw_block(10, 10, "A1"); */
10
  
76
  
11
	while(1) {
77
	while(1) {
12
		// Do the thread stuff here.
78
		ret = tb_poll_event(&ev);
13
		sleep(1);
79
  
14
		fprintf(stdout, "hi from interface thread\n");
80
		if (ret != TB_OK) {
  
81
			if (ret == TB_ERR_POLL && tb_last_errno() == EINTR) {
  
82
				/* Poll was interrupted, maybe by a SIGWINCH; try again */
  
83
				continue;
  
84
			}
  
85
			/* Some other error occurred; bail */
  
86
			break;
  
87
		}
  
88
  
  
89
		switch (ev.type) {
  
90
			case TB_EVENT_KEY: 
  
91
				if (ev.key == TB_KEY_CTRL_Q) {
  
92
					tb_shutdown();
  
93
					exit(0);
  
94
				}
  
95
  
  
96
				if (ev.key == TB_KEY_ARROW_UP) {
  
97
  
  
98
				}
  
99
  
  
100
				if (ev.key == TB_KEY_ARROW_DOWN) {
  
101
  
  
102
				}
  
103
  
  
104
				break;
  
105
		}	
15
	}
106
	}
16
}
107
}
  
108
  
diff --git a/main.c b/main.c
...
13
void help(const char *argv0) {
13
void help(const char *argv0) {
14
	printf("Usage: %s [options]\n"
14
	printf("Usage: %s [options]\n"
15
			"\nAvailable options:\n"
15
			"\nAvailable options:\n"
  
16
			"  -n,--new                     creates a new song file\n"
16
			"  -l,--list                    list available devices\n"
17
			"  -l,--list                    list available devices\n"
17
			"  -c,--client=client:port      device client and port\n"
18
			"  -c,--client=client:port      device client and port\n"
18
			"  -s,--soundfont=file.sf2      soundfont file\n"
19
			"  -s,--soundfont=file.sf2      soundfont file\n"
...
23
}
24
}
24
  
25
  
25
int main(int argc, char *argv[]) {
26
int main(int argc, char *argv[]) {
26
	const char short_options[] = "lc:s:p:hv";
27
	const char short_options[] = "n:lc:s:p:hv";
27
	const struct option long_options[] = {
28
	const struct option long_options[] = {
  
29
		{ "new", 1, NULL, 'n' },
28
		{ "list", 0, NULL, 'l' },
30
		{ "list", 0, NULL, 'l' },
29
		{ "client", 1, NULL, 'c' },
31
		{ "client", 1, NULL, 'c' },
30
		{ "soundfont", 1, NULL, 's' },
32
		{ "soundfont", 1, NULL, 's' },
...
34
		{ 0 },
36
		{ 0 },
35
	};
37
	};
36
  
38
  
  
39
	char *new_song_name = NULL;
37
	char *port_name = NULL;
40
	char *port_name = NULL;
38
	char *soundfont_file = NULL;
41
	char *soundfont_file = NULL;
39
	int soundfont_preset = 0;
42
	int soundfont_preset = 0;
...
41
	int opt;
44
	int opt;
42
	while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
45
	while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
43
		switch (opt) {
46
		switch (opt) {
  
47
			case 'n':
  
48
				new_song_name = optarg;
44
			case 'l':
49
			case 'l':
45
				fprintf(stderr, "List feature is NOT implemented yet.\n");
50
				fprintf(stderr, "List feature is NOT implemented yet.\n");
46
				return 0;
51
				return 0;
...
67
				fprintf(stdout, "Missing options. Check help.\n");
72
				fprintf(stdout, "Missing options. Check help.\n");
68
				return 0;
73
				return 0;
69
		}
74
		}
  
75
	}
  
76
  
  
77
	if (new_song_name != NULL) {
  
78
		fprintf(stdout, "Creating new song file with name %s\n", new_song_name);
  
79
		return 0;
70
	}
80
	}
71
  
81
  
72
	if (port_name == NULL || soundfont_file == NULL || soundfont_preset < 0) {
82
	if (port_name == NULL || soundfont_file == NULL || soundfont_preset < 0) {
...
75
		return 1;
85
		return 1;
76
	}
86
	}
77
  
87
  
78
	fprintf(stdout, "> Device port:   %s\n", port_name);
88
	/* fprintf(stdout, "> Device port:   %s\n", port_name); */
79
	fprintf(stdout, "> Soundfont:     %s\n", soundfont_file);
89
	/* fprintf(stdout, "> Soundfont:     %s\n", soundfont_file); */
80
	fprintf(stdout, "> SF preset:     %d\n", soundfont_preset);
90
	/* fprintf(stdout, "> SF preset:     %d\n", soundfont_preset); */
  
91
	/* fprintf(stdout, "> Song name:     %s\n", new_song_name); */
81
  
92
  
82
	// Create mutex.
93
	// Create mutex.
83
	initialize_mutex();	
94
	initialize_mutex();	
84
  
95
  
  
96
	// Create UI thread.
  
97
	pthread_t interface_thread;
  
98
	InterfaceArgs interface_args = {
  
99
		.soundfont_file = soundfont_file,
  
100
		.soundfont_preset = soundfont_preset,
  
101
	};
  
102
  
  
103
	if (pthread_create(&interface_thread, NULL, interface, (void*)&interface_args) != 0) {
  
104
		fprintf(stderr, "Error creating interface thread\n");
  
105
		return 1;
  
106
	}
  
107
  
85
	// Create synth thread.
108
	// Create synth thread.
86
	pthread_t synth_thread;
109
	pthread_t synth_thread;
87
	SynthArgs synth_args = {
110
	SynthArgs synth_args = {
...
103
		return 1;
126
		return 1;
104
	}
127
	}
105
  
128
  
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.
129
	// Start threads.
  
130
	pthread_join(interface_thread, NULL);
119
	pthread_join(midi_thread, NULL);
131
	pthread_join(midi_thread, NULL);
120
	pthread_join(synth_thread, NULL);
132
	pthread_join(synth_thread, NULL);
121
	pthread_join(interface_thread, NULL);
  
122
  
133
  
123
	// Destroy mutex.
134
	// Destroy mutex.
124
	destroy_mutex();
135
	destroy_mutex();
...
diff --git a/midi.c b/midi.c
...
6
  
6
  
7
#include "midi.h"
7
#include "midi.h"
8
#include "mutex.h"
8
#include "mutex.h"
  
9
#include "termbox2.h"
9
  
10
  
10
static snd_seq_t *seq_handle;
11
static snd_seq_t *seq_handle;
11
static snd_seq_addr_t *ports;
12
static snd_seq_addr_t *ports;
...
40
	}
41
	}
41
  
42
  
42
	// Listing assigned ports.
43
	// Listing assigned ports.
43
	fprintf(stdout, "Ports:\n");
44
	/* fprintf(stdout, "Ports:\n"); */
44
	for (int j = 0; j < MAX_MIDI_PORTS; j++) {
45
	/* for (int j = 0; j < MAX_MIDI_PORTS; j++) { */
45
		fprintf(stdout, " client: %d, port: %d\n", ports[j].client, ports[j].port);
46
	/* 	fprintf(stdout, " client: %d, port: %d\n", ports[j].client, ports[j].port); */
46
	}
47
	/* } */
47
  
48
  
48
	// Connecting ports.
49
	// Connecting ports.
49
	for (int i = 0; i < MAX_MIDI_PORTS; ++i) {
50
	for (int i = 0; i < MAX_MIDI_PORTS; ++i) {
...
87
						shared_data.note = ev->data.note.note;
88
						shared_data.note = ev->data.note.note;
88
						shared_data.state = 1;
89
						shared_data.state = 1;
89
						shared_data.velocity = ev->data.note.velocity;
90
						shared_data.velocity = ev->data.note.velocity;
90
						printf("%3d:%-3dNote on  %2d, note %d, velocity: %3d\n",
91
						tb_printf(0, 3, TB_CYAN, 0, "Note: %3d", ev->data.note.note);
91
								ev->source.client, ev->source.port,
92
						tb_printf(0, 4, TB_CYAN, 0, "Velocity: %3d", ev->data.note.velocity);
92
								ev->data.note.channel,
93
						tb_present();
93
								ev->data.note.note,
94
						/* printf("%3d:%-3dNote on  %2d, note %d, velocity: %3d\n", */
94
								ev->data.note.velocity);
95
						/* 		ev->source.client, ev->source.port, */
  
96
						/* 		ev->data.note.channel, */
  
97
						/* 		ev->data.note.note, */
  
98
						/* 		ev->data.note.velocity); */
95
						break;
99
						break;
96
  
100
  
97
					case SND_SEQ_EVENT_NOTEOFF:
101
					case SND_SEQ_EVENT_NOTEOFF:
98
						shared_data.note = ev->data.note.note;
102
						shared_data.note = ev->data.note.note;
99
						shared_data.state = 0;
103
						shared_data.state = 0;
100
						shared_data.velocity = 0;
104
						shared_data.velocity = 0;
101
						printf("%3d:%-3dNote off\t%2d, note %d\n",
105
						/* printf("%3d:%-3dNote off\t%2d, note %d\n", */
102
								ev->source.client, ev->source.port,
106
						/* 		ev->source.client, ev->source.port, */
103
								ev->data.note.channel,
107
						/* 		ev->data.note.channel, */
104
								ev->data.note.note);
108
						/* 		ev->data.note.note); */
105
						break;
109
						break;
106
  
110
  
107
  
111
  
...
diff --git a/synth.c b/synth.c
...
4
#include "synth.h"
4
#include "synth.h"
5
#include "mutex.h"
5
#include "mutex.h"
6
#include "minisdl_audio.h"
6
#include "minisdl_audio.h"
  
7
#include "termbox2.h"
7
  
8
  
8
#define TSF_IMPLEMENTATION
9
#define TSF_IMPLEMENTATION
9
#include "tsf.h"
10
#include "tsf.h"
...
73
  
74
  
74
		SDL_UnlockMutex(g_Mutex);
75
		SDL_UnlockMutex(g_Mutex);
75
  
76
  
76
		printf("Consumed: note=%d, state=%d velocity:%d\n", shared_data.note, shared_data.state, shared_data.velocity);
77
		/* printf("Consumed: note=%d, state=%d velocity:%d\n", shared_data.note, shared_data.state, shared_data.velocity); */
77
  
78
  
78
		// Reset state to indicate data has been consumed.
79
		// Reset state to indicate data has been consumed.
79
		shared_data.action = 0; 
80
		shared_data.action = 0; 
...