diff options
| -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 | 166 | ||||
| -rw-r--r-- | mutex.h | 6 | ||||
| -rw-r--r-- | synth.h | 4 | ||||
| -rw-r--r-- | version.h | 16 |
9 files changed, 169 insertions, 105 deletions
@@ -1,7 +1,7 @@ CC := cc CFLAGS := -Wall -Wextra -Wshadow -Wunused -Wswitch-enum -Wpedantic -ggdb LDFLAGS := -lm -ldl -lpthread -lasound -FILES := main.c midi.c synth.c mutex.c minisdl_audio.c +FILES := main.c midi.c synth.c interface.c mutex.c minisdl_audio.c PROG := ttdaw $(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 @@ +-- Song file structure +-- This is just an outline of it and this will change. + +-- API specification: +-- note_on(note, sf_preset, effect) +-- note_off(note) +-- delay(millisecons) +-- play_pattern(pattern_name, repeat) + +-- Effects: +-- delay +-- reverb + +-- This is a pattern definition. +-- Columns go from A..F and rows go from 1..4. +-- This blocks will be visualized in the DAW. +pattern("A1", function(self) + for i = 1, 10 do + note_on(40 + i, 1, nil) + delay(10) + end +end) + +-- This is the actual song timeline. +timeline(function(self) + play_pattern("A1", 1) + play_pattern("F2", 3) + play_pattern("D4", 2) +end) + diff --git a/interface.c b/interface.c index 6399cb8..26aca55 100644 --- a/interface.c +++ b/interface.c @@ -1,2 +1,16 @@ +#include <stdio.h> +#include <unistd.h> + #include "interface.h" +#include "mutex.h" + +void *interface(void *arg) { + InterfaceArgs* args = (InterfaceArgs*)arg; + (void)args; + while(1) { + // Do the thread stuff here. + sleep(1); + fprintf(stdout, "hi from interface thread\n"); + } +} diff --git a/interface.h b/interface.h index e320785..da8227e 100644 --- a/interface.h +++ b/interface.h @@ -1,5 +1,12 @@ #ifndef INTERFACE_H #define INTERFACE_H +typedef struct { + char *soundfont_file; + int soundfont_preset; +} InterfaceArgs; + +void *interface(void *arg); + #endif // INTERFACE_H @@ -7,6 +7,7 @@ #include "version.h" #include "midi.h" #include "synth.h" +#include "interface.h" #include "mutex.h" void help(const char *argv0) { @@ -27,7 +28,7 @@ int main(int argc, char *argv[]) { { "list", 0, NULL, 'l' }, { "client", 1, NULL, 'c' }, { "soundfont", 1, NULL, 's' }, - { "preset", 1, NULL, 'p' }, + { "preset", 0, NULL, 'p' }, { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'v' }, { 0 }, @@ -35,7 +36,7 @@ int main(int argc, char *argv[]) { char *port_name = NULL; char *soundfont_file = NULL; - int soundfont_preset = -1; + int soundfont_preset = 0; int opt; while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { @@ -56,11 +57,11 @@ int main(int argc, char *argv[]) { help(argv[0]); return 0; case 'v': - fprintf(stdout, "ttdaw version %s\n", TTDAW_VERSION); - fprintf(stdout, "Website: %s.\n", TTDAW_WEBSITE); - fprintf(stdout, "%s\n", TTDAW_LICENSE); - fprintf(stdout, "%s\n", TTDAW_WARRANTY); - fprintf(stdout, "\n%s\n", TTDAW_AUTHOR); + fprintf(stdout, "ttdaw version %s\n", VERSION); + fprintf(stdout, "Website: %s.\n", WEBSITE); + fprintf(stdout, "%s\n", LICENSE); + fprintf(stdout, "%s\n", WARRANTY); + fprintf(stdout, "\n%s\n", AUTHOR); return 0; default: fprintf(stdout, "Missing options. Check help.\n"); @@ -102,8 +103,22 @@ int main(int argc, char *argv[]) { return 1; } + // Create UI thread. + pthread_t interface_thread; + InterfaceArgs interface_args = { + .soundfont_file = soundfont_file, + .soundfont_preset = soundfont_preset, + }; + + if (pthread_create(&interface_thread, NULL, interface, (void*)&interface_args) != 0) { + fprintf(stderr, "Error creating interface thread\n"); + return 1; + } + + // Start threads. pthread_join(midi_thread, NULL); pthread_join(synth_thread, NULL); + pthread_join(interface_thread, NULL); // Destroy mutex. destroy_mutex(); @@ -13,110 +13,108 @@ static snd_seq_addr_t *ports; void *midi(void *arg) { MidiArgs* args = (MidiArgs*)arg; - while (1) { - if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) { - fprintf(stderr, "Error opening ALSA sequencer.\n"); - exit(1); - } - - if (snd_seq_set_client_name(seq_handle, CLIENT_NAME) < 0) { - fprintf(stderr, "Could not set up client name.\n"); - exit(1); - } + if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) { + fprintf(stderr, "Error opening ALSA sequencer.\n"); + exit(1); + } - if (snd_seq_create_simple_port(seq_handle, CLIENT_NAME, - SND_SEQ_PORT_CAP_WRITE | - SND_SEQ_PORT_CAP_SUBS_WRITE, - SND_SEQ_PORT_TYPE_MIDI_GENERIC | - SND_SEQ_PORT_TYPE_APPLICATION) < 0) { - fprintf(stderr, "Error creating sequencer port.\n"); - exit(1); - } + if (snd_seq_set_client_name(seq_handle, CLIENT_NAME) < 0) { + fprintf(stderr, "Could not set up client name.\n"); + exit(1); + } - // Connecting ports. - ports = realloc(ports, MAX_MIDI_PORTS * sizeof(snd_seq_addr_t)); - if (snd_seq_parse_address(seq_handle, &ports[0], args->port_name) < 0) { - fprintf(stderr, "Invalid port %s.\n", args->port_name); - exit(1); - } + if (snd_seq_create_simple_port(seq_handle, CLIENT_NAME, + SND_SEQ_PORT_CAP_WRITE | + SND_SEQ_PORT_CAP_SUBS_WRITE, + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SND_SEQ_PORT_TYPE_APPLICATION) < 0) { + fprintf(stderr, "Error creating sequencer port.\n"); + exit(1); + } - // Listing assigned ports. - fprintf(stdout, "Ports:\n"); - for (int j = 0; j < MAX_MIDI_PORTS; j++) { - fprintf(stdout, " client: %d, port: %d\n", ports[j].client, ports[j].port); - } + // Connecting ports. + ports = realloc(ports, MAX_MIDI_PORTS * sizeof(snd_seq_addr_t)); + if (snd_seq_parse_address(seq_handle, &ports[0], args->port_name) < 0) { + fprintf(stderr, "Invalid port %s.\n", args->port_name); + exit(1); + } - // Connecting ports. - for (int i = 0; i < MAX_MIDI_PORTS; ++i) { - int err = snd_seq_connect_from(seq_handle, 0, ports[i].client, ports[i].port); - if (err < 0) { - fprintf(stderr, "Cannot connect from port %d:%d - %s", ports[i].client, ports[i].port, snd_strerror(err)); - exit(1); - } - } + // Listing assigned ports. + fprintf(stdout, "Ports:\n"); + for (int j = 0; j < MAX_MIDI_PORTS; j++) { + fprintf(stdout, " client: %d, port: %d\n", ports[j].client, ports[j].port); + } - if (snd_seq_nonblock(seq_handle, 1) < 0) { - fprintf(stderr, "Set nonblock mode failed."); + // Connecting ports. + for (int i = 0; i < MAX_MIDI_PORTS; ++i) { + int err = snd_seq_connect_from(seq_handle, 0, ports[i].client, ports[i].port); + if (err < 0) { + fprintf(stderr, "Cannot connect from port %d:%d - %s", ports[i].client, ports[i].port, snd_strerror(err)); exit(1); } + } + + if (snd_seq_nonblock(seq_handle, 1) < 0) { + fprintf(stderr, "Set nonblock mode failed."); + exit(1); + } + + // Reading MIDI device. + struct pollfd *pfds; + size_t npfds; - // Reading MIDI device. - struct pollfd *pfds; - size_t npfds; + npfds = snd_seq_poll_descriptors_count(seq_handle, POLLIN); + pfds = alloca(sizeof(*pfds) * npfds); - npfds = snd_seq_poll_descriptors_count(seq_handle, POLLIN); - pfds = alloca(sizeof(*pfds) * npfds); + for (;;) { + snd_seq_poll_descriptors(seq_handle, pfds, npfds, POLLIN); + if (poll(pfds, npfds, -1) < 0) { + break; + } for (;;) { - snd_seq_poll_descriptors(seq_handle, pfds, npfds, POLLIN); - if (poll(pfds, npfds, -1) < 0) { + snd_seq_event_t *ev; + + if (snd_seq_event_input(seq_handle, &ev) < 0) { break; } - for (;;) { - snd_seq_event_t *ev; + if (ev) { + pthread_mutex_lock(&mutex); + switch (ev->type) { + case SND_SEQ_EVENT_NOTEON: + shared_data.note = ev->data.note.note; + shared_data.state = 1; + shared_data.velocity = ev->data.note.velocity; + printf("%3d:%-3dNote on %2d, note %d, velocity: %3d\n", + ev->source.client, ev->source.port, + ev->data.note.channel, + ev->data.note.note, + ev->data.note.velocity); + break; + + case SND_SEQ_EVENT_NOTEOFF: + shared_data.note = ev->data.note.note; + shared_data.state = 0; + shared_data.velocity = 0; + printf("%3d:%-3dNote off\t%2d, note %d\n", + ev->source.client, ev->source.port, + ev->data.note.channel, + ev->data.note.note); + break; - if (snd_seq_event_input(seq_handle, &ev) < 0) { - break; - } - if (ev) { - pthread_mutex_lock(&mutex); - switch (ev->type) { - case SND_SEQ_EVENT_NOTEON: - shared_data.note = ev->data.note.note; - shared_data.state = 1; - shared_data.velocity = ev->data.note.velocity; - printf("%3d:%-3dNote on %2d, note %d, velocity: %3d\n", - ev->source.client, ev->source.port, - ev->data.note.channel, - ev->data.note.note, - ev->data.note.velocity); - break; - - case SND_SEQ_EVENT_NOTEOFF: - shared_data.note = ev->data.note.note; - shared_data.state = 0; - shared_data.velocity = 0; - printf("%3d:%-3dNote off\t%2d, note %d\n", - ev->source.client, ev->source.port, - ev->data.note.channel, - ev->data.note.note); - break; - default: - break; - } - - shared_data.action = 1; - - pthread_cond_signal(&cond_synth); - pthread_mutex_unlock(&mutex); } - } - fflush(stdout); + shared_data.action = 1; + + pthread_cond_signal(&cond_synth); + pthread_mutex_unlock(&mutex); + } } + + fflush(stdout); } } @@ -1,5 +1,5 @@ -#ifndef MUTEX_H -#define MUTEX_H +#ifndef MUTEX_H_ +#define MUTEX_H_ #include <pthread.h> @@ -19,5 +19,5 @@ extern pthread_cond_t cond_synth; void initialize_mutex(); void destroy_mutex(); -#endif // MUTEX_H +#endif // MUTEX_H_ @@ -2,7 +2,7 @@ #define SYNTH_H_ #define AUDIO_FREQ 44100 -#define AUDIO_SAMPLES 64 +#define AUDIO_SAMPLES 128 #define AUDIO_CHANNELS 2 typedef struct { @@ -12,5 +12,5 @@ typedef struct { void *synth(void *arg); -#endif // SYNTH_H_ +#endif // SYNTH_H_ @@ -1,11 +1,11 @@ -#ifndef TTDAW_VERSION_H -#define TTDAW_VERSION_H +#ifndef VERSION_H_ +#define VERSION_H_ -#define TTDAW_VERSION "0.1" -#define TTDAW_WEBSITE "https://github.com/mitjafelicijan/ttdaw" -#define TTDAW_LICENSE "This is free software: you are free to change and redistribute it." -#define TTDAW_WARRANTY "There is NO WARRANTY, to the extent permitted by law." -#define TTDAW_AUTHOR "Written by Mitja Felicijan <https://mitjafelicijan.com>." +#define VERSION "0.1" +#define WEBSITE "https://github.com/mitjafelicijan/ttdaw" +#define LICENSE "This is free software: you are free to change and redistribute it." +#define WARRANTY "There is NO WARRANTY, to the extent permitted by law." +#define AUTHOR "Written by Mitja Felicijan <https://mitjafelicijan.com>." -#endif +#endif // VERSION_H_ |
