aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--examples/sandbox.lua30
-rw-r--r--interface.c14
-rw-r--r--interface.h7
-rw-r--r--main.c29
-rw-r--r--midi.c166
-rw-r--r--mutex.h6
-rw-r--r--synth.h4
-rw-r--r--version.h16
9 files changed, 169 insertions, 105 deletions
diff --git a/Makefile b/Makefile
index fca5cb9..c8d97de 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
1CC := cc 1CC := cc
2CFLAGS := -Wall -Wextra -Wshadow -Wunused -Wswitch-enum -Wpedantic -ggdb 2CFLAGS := -Wall -Wextra -Wshadow -Wunused -Wswitch-enum -Wpedantic -ggdb
3LDFLAGS := -lm -ldl -lpthread -lasound 3LDFLAGS := -lm -ldl -lpthread -lasound
4FILES := main.c midi.c synth.c mutex.c minisdl_audio.c 4FILES := main.c midi.c synth.c interface.c mutex.c minisdl_audio.c
5PROG := ttdaw 5PROG := ttdaw
6 6
7$(PROG): main.c 7$(PROG): main.c
diff --git a/examples/sandbox.lua b/examples/sandbox.lua
new file mode 100644
index 0000000..43c3ec7
--- /dev/null
+++ b/examples/sandbox.lua
@@ -0,0 +1,30 @@
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.
17pattern("A1", function(self)
18 for i = 1, 10 do
19 note_on(40 + i, 1, nil)
20 delay(10)
21 end
22end)
23
24-- This is the actual song timeline.
25timeline(function(self)
26 play_pattern("A1", 1)
27 play_pattern("F2", 3)
28 play_pattern("D4", 2)
29end)
30
diff --git a/interface.c b/interface.c
index 6399cb8..26aca55 100644
--- a/interface.c
+++ b/interface.c
@@ -1,2 +1,16 @@
1#include <stdio.h>
2#include <unistd.h>
3
1#include "interface.h" 4#include "interface.h"
5#include "mutex.h"
6
7void *interface(void *arg) {
8 InterfaceArgs* args = (InterfaceArgs*)arg;
9 (void)args;
2 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
index e320785..da8227e 100644
--- a/interface.h
+++ b/interface.h
@@ -1,5 +1,12 @@
1#ifndef INTERFACE_H 1#ifndef INTERFACE_H
2#define INTERFACE_H 2#define INTERFACE_H
3 3
4typedef struct {
5 char *soundfont_file;
6 int soundfont_preset;
7} InterfaceArgs;
8
9void *interface(void *arg);
10
4#endif // INTERFACE_H 11#endif // INTERFACE_H
5 12
diff --git a/main.c b/main.c
index 2bc6927..d1c8567 100644
--- a/main.c
+++ b/main.c
@@ -7,6 +7,7 @@
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
12void help(const char *argv0) { 13void help(const char *argv0) {
@@ -27,7 +28,7 @@ int main(int argc, char *argv[]) {
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,7 +36,7 @@ int main(int argc, char *argv[]) {
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,11 +57,11 @@ int main(int argc, char *argv[]) {
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,8 +103,22 @@ int main(int argc, char *argv[]) {
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
index 92df3d3..e924b70 100644
--- a/midi.c
+++ b/midi.c
@@ -13,110 +13,108 @@ static snd_seq_addr_t *ports;
13void *midi(void *arg) { 13void *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
22 if (snd_seq_set_client_name(seq_handle, CLIENT_NAME) < 0) {
23 fprintf(stderr, "Could not set up client name.\n");
24 exit(1);
25 }
26 20
27 if (snd_seq_create_simple_port(seq_handle, CLIENT_NAME, 21 if (snd_seq_set_client_name(seq_handle, CLIENT_NAME) < 0) {
28 SND_SEQ_PORT_CAP_WRITE | 22 fprintf(stderr, "Could not set up client name.\n");
29 SND_SEQ_PORT_CAP_SUBS_WRITE, 23 exit(1);
30 SND_SEQ_PORT_TYPE_MIDI_GENERIC | 24 }
31 SND_SEQ_PORT_TYPE_APPLICATION) < 0) {
32 fprintf(stderr, "Error creating sequencer port.\n");
33 exit(1);
34 }
35 25
36 // Connecting ports. 26 if (snd_seq_create_simple_port(seq_handle, CLIENT_NAME,
37 ports = realloc(ports, MAX_MIDI_PORTS * sizeof(snd_seq_addr_t)); 27 SND_SEQ_PORT_CAP_WRITE |
38 if (snd_seq_parse_address(seq_handle, &ports[0], args->port_name) < 0) { 28 SND_SEQ_PORT_CAP_SUBS_WRITE,
39 fprintf(stderr, "Invalid port %s.\n", args->port_name); 29 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
40 exit(1); 30 SND_SEQ_PORT_TYPE_APPLICATION) < 0) {
41 } 31 fprintf(stderr, "Error creating sequencer port.\n");
32 exit(1);
33 }
42 34
43 // Listing assigned ports. 35 // Connecting ports.
44 fprintf(stdout, "Ports:\n"); 36 ports = realloc(ports, MAX_MIDI_PORTS * sizeof(snd_seq_addr_t));
45 for (int j = 0; j < MAX_MIDI_PORTS; j++) { 37 if (snd_seq_parse_address(seq_handle, &ports[0], args->port_name) < 0) {
46 fprintf(stdout, " client: %d, port: %d\n", ports[j].client, ports[j].port); 38 fprintf(stderr, "Invalid port %s.\n", args->port_name);
47 } 39 exit(1);
40 }
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 }
56
57 if (snd_seq_nonblock(seq_handle, 1) < 0) {
58 fprintf(stderr, "Set nonblock mode failed.");
59 exit(1);
60 }
61
62 62
63 // Reading MIDI device.
64 struct pollfd *pfds;
65 size_t npfds;
63 66
64 // Reading MIDI device. 67 npfds = snd_seq_poll_descriptors_count(seq_handle, POLLIN);
65 struct pollfd *pfds; 68 pfds = alloca(sizeof(*pfds) * npfds);
66 size_t npfds;
67 69
68 npfds = snd_seq_poll_descriptors_count(seq_handle, POLLIN); 70 for (;;) {
69 pfds = alloca(sizeof(*pfds) * npfds); 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;
96
97 case SND_SEQ_EVENT_NOTEOFF:
98 shared_data.note = ev->data.note.note;
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;
79 106
80 if (snd_seq_event_input(seq_handle, &ev) < 0) {
81 break;
82 }
83 107
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
98 case SND_SEQ_EVENT_NOTEOFF:
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
111 shared_data.action = 1;
112
113 pthread_cond_signal(&cond_synth);
114 pthread_mutex_unlock(&mutex);
115 } 108 }
116 }
117 109
118 fflush(stdout); 110 shared_data.action = 1;
111
112 pthread_cond_signal(&cond_synth);
113 pthread_mutex_unlock(&mutex);
114 }
119 } 115 }
116
117 fflush(stdout);
120 } 118 }
121} 119}
122 120
diff --git a/mutex.h b/mutex.h
index 78b4cdf..2894b41 100644
--- a/mutex.h
+++ b/mutex.h
@@ -1,5 +1,5 @@
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,5 +19,5 @@ extern pthread_cond_t cond_synth;
19void initialize_mutex(); 19void initialize_mutex();
20void destroy_mutex(); 20void destroy_mutex();
21 21
22#endif // MUTEX_H 22#endif // MUTEX_H_
23 23
diff --git a/synth.h b/synth.h
index 7e08157..2fec78b 100644
--- a/synth.h
+++ b/synth.h
@@ -2,7 +2,7 @@
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
8typedef struct { 8typedef struct {
@@ -12,5 +12,5 @@ typedef struct {
12 12
13void *synth(void *arg); 13void *synth(void *arg);
14 14
15#endif // SYNTH_H_ 15#endif // SYNTH_H_
16 16
diff --git a/version.h b/version.h
index e5962f6..76f1a1e 100644
--- a/version.h
+++ b/version.h
@@ -1,11 +1,11 @@
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