/* seqdemo.c by Matthias Nagorni */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <alsa/asoundlib.h>

snd_seq_t *open_seq();
void midi_action(snd_seq_t *seq_handle);

snd_seq_t *open_seq() {

    snd_seq_t *seq_handle;
    int portid;

    if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, 0) < 0) {
        fprintf(stderr, "Error opening ALSA sequencer.\n");
        exit(1);
    }
    snd_seq_set_client_name(seq_handle, "ALSA Sequencer Demo");
    if ((portid = snd_seq_create_simple_port(seq_handle, "ALSA Sequencer Demo",
                                             SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
                                             SND_SEQ_PORT_TYPE_APPLICATION)) < 0) {
        fprintf(stderr, "Error creating sequencer port.\n");
        exit(1);
    }
    return(seq_handle);
}

void midi_action(snd_seq_t *seq_handle) {

    snd_seq_event_t *ev;

    do {
        snd_seq_event_input(seq_handle, &ev);
        switch (ev->type) {
            case SND_SEQ_EVENT_CONTROLLER: 
                fprintf(stderr, "Control event on Channel %2d: %5d       \r",
                        ev->data.control.channel, ev->data.control.value);
                break;
            case SND_SEQ_EVENT_PITCHBEND:
                fprintf(stderr, "Pitchbender event on Channel %2d: %5d   \r", 
                        ev->data.control.channel, ev->data.control.value);
                break;
            case SND_SEQ_EVENT_NOTEON:
                fprintf(stderr, "Note On event on Channel %2d: %5d       \r",
                        ev->data.control.channel, ev->data.note.note);
                break;        
            case SND_SEQ_EVENT_NOTEOFF: 
                fprintf(stderr, "Note Off event on Channel %2d: %5d      \r",         
                        ev->data.control.channel, ev->data.note.note);           
                break;        
        }
        snd_seq_free_event(ev);
    } while (snd_seq_event_input_pending(seq_handle, 0) > 0);
}

int main(int argc, char *argv[]) {

    snd_seq_t *seq_handle;
    int npfd;
    struct pollfd *pfd;
  
    fd_set rfds;
    
    seq_handle = open_seq();

    npfd = snd_seq_poll_descriptors_count(seq_handle, POLLIN);
    pfd = (struct pollfd *)alloca(npfd * sizeof(struct pollfd));
    snd_seq_poll_descriptors(seq_handle, pfd, npfd, POLLIN);

    while (1) {
        FD_ZERO(&rfds);
        FD_SET(pfd[0].fd, &rfds);

        if (select(npfd, &rfds, NULL, NULL, NULL)) {
            midi_action(seq_handle);
        }  
    }
}
