diff options
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | documents/sfspec24.pdf | bin | 0 -> 530464 bytes | |||
| -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
@@ -1,9 +1,9 @@ CC := cc CFLAGS := -Wall -Wextra -Wshadow -Wunused -Wswitch-enum -Wpedantic -Wundef LDFLAGS := -lm -ldl -lpthread -lasound -CFILES := main.c minisdl_audio.c +FILES := main.c midi.c minisdl_audio.c PROG := ttdaw $(PROG): main.c - $(CC) $(CFLAGS) -o $(PROG) $(CFILES) $(LDFLAGS) + $(CC) $(CFLAGS) -o $(PROG) $(FILES) $(LDFLAGS) diff --git a/documents/sfspec24.pdf b/documents/sfspec24.pdf Binary files differnew file mode 100644 index 0000000..252aff9 --- /dev/null +++ b/documents/sfspec24.pdf @@ -2,32 +2,48 @@ #include <stdlib.h> #include <stdarg.h> #include <getopt.h> +#include <pthread.h> #include "version.h" +#include "midi.h" void help(const char *argv0) { printf("Usage: %s [options]\n" "\nAvailable options:\n" - " -h,--help this help\n" - " -v,--version show version\n" - " -l,--list list available devices\n" - " -p,--port=client:port device port\n", + " -l,--list list available devices\n" + " -p,--port=client:port device port\n" + " -s,--soundfont=file.sf2 soundfont file\n" + " -h,--help this help\n" + " -v,--version show version\n", argv0); } int main(int argc, char *argv[]) { - const char short_options[] = "hvlp"; + const char short_options[] = "lp:s:hv:"; const struct option long_options[] = { - { "help", 0, NULL, 'h' }, - { "version", 0, NULL, 'v' }, { "list", 0, NULL, 'l' }, { "port", 1, NULL, 'p' }, + { "soundfont", 1, NULL, 's' }, + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'v' }, { 0 }, }; - int c; - while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { - switch (c) { + char *port_name = NULL; + char *soundfont_file = NULL; + + int opt; + while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { + switch (opt) { + case 'l': + fprintf(stderr, "List feature is NOT implemented yet.\n"); + return 0; + case 'p': + port_name = optarg; + break; + case 's': + soundfont_file = optarg; + break; case 'h': help(argv[0]); return 0; @@ -38,18 +54,33 @@ int main(int argc, char *argv[]) { fprintf(stdout, "%s\n", TTDAW_WARRANTY); fprintf(stdout, "\n%s\n", TTDAW_AUTHOR); return 0; - case 'l': - fprintf(stderr, "List feature is NOT implemented yet.\n"); - return 0; - case 'p': - fprintf(stderr, "Port feature is NOT implemented yet.\n"); - return 0; default: - fprintf(stdout, "No option provided\n"); + fprintf(stdout, "Missing options. Check help.\n"); return 0; } } + if (port_name == NULL || soundfont_file == NULL) { + fprintf(stdout, "Missing options. Check help.\n\n"); + fprintf(stdout, "Port and soundfile are required fields.\n\n"); + return 1; + } + + fprintf(stdout, "> Device port: %s\n", port_name); + fprintf(stdout, "> Soundfont: %s\n", soundfont_file); + + // Create and start MIDI thread. + pthread_t midi_thread; + MidiArgs midi_args = { port_name }; + + if (pthread_create(&midi_thread, NULL, midi, (void*)&midi_args) != 0) { + fprintf(stderr, "Error creating midi thread\n"); + return 1; + } + + pthread_join(midi_thread, NULL); + + fprintf(stdout, "Exiting...\n"); return 0; } @@ -0,0 +1,109 @@ +#include <stdio.h> +#include <stdlib.h> +#include <alloca.h> +#include <unistd.h> +#include <alsa/asoundlib.h> + +#include "midi.h" + +static snd_seq_t *seq_handle; +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_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); + } + + // 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); + } + + // 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. + 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; + + 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_event_t *ev; + + if (snd_seq_event_input(seq_handle, &ev) < 0) { + break; + } + + if (ev) { + switch (ev->type) { + case SND_SEQ_EVENT_NOTEON: + 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: + printf("%3d:%-3dNote off\t%2d, note %d\n", + ev->source.client, ev->source.port, + ev->data.note.channel, + ev->data.note.note); + break; + + + } + } + } + + fflush(stdout); + } + } +} + @@ -0,0 +1,16 @@ +#include <alsa/asoundlib.h> + +#ifndef MIDI_H_ +#define MIDI_H_ + +#define CLIENT_NAME "ttdaw" +#define MAX_MIDI_PORTS 1 + +typedef struct { + char *port_name; +} MidiArgs; + +void *midi(void *arg); + +#endif // MIDI_H_ + |
