hdjd

Hercules DJ controller driver for Linux
git clone https://git.woozle.org/neale/hdjd.git

Neale Pickett  ·  2017-03-18

alsa.c

  1#include <stdio.h>
  2#include <poll.h>
  3#include <sys/select.h>
  4#include <alsa/asoundlib.h>
  5#include "alsa.h"
  6#include "usb.h"
  7#include "log.h"
  8#include "dump.h"
  9
 10#define ALSA_BUFSIZE 4096
 11#define MAX_PFDS 20
 12
 13static snd_seq_t *snd_handle = NULL;
 14static int seq_port;
 15static snd_midi_event_t *midi_event_parser = NULL;
 16
 17int npfd;
 18struct pollfd pfd[MAX_PFDS];
 19
 20void alsa_interrupting()
 21{
 22  //Currently nothing to do.
 23}
 24
 25int
 26alsa_setup(const char *name)
 27{
 28  if (snd_seq_open(&snd_handle, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
 29    perror("snd_seq_open");
 30    return -1;
 31  }
 32  snd_seq_nonblock(snd_handle, 1);
 33  snd_seq_set_client_name(snd_handle, name);
 34  seq_port =
 35    snd_seq_create_simple_port(snd_handle, name,
 36			       SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_READ |
 37			       SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC);
 38
 39  // Allocate parser object for converting to and from MIDI
 40  if (snd_midi_event_new(ALSA_BUFSIZE, &midi_event_parser) < 0) {
 41    fatal("ALSA cannot allocate MIDI event parser");
 42    return -1;
 43  } else {
 44    snd_midi_event_no_status(midi_event_parser,1);
 45    return 0;
 46  }
 47}
 48
 49void
 50alsa_close()
 51{
 52  if (midi_event_parser) {
 53    snd_midi_event_free(midi_event_parser);
 54  }
 55  if (snd_handle) {
 56    snd_seq_close(snd_handle);
 57  }
 58}
 59
 60
 61void
 62alsa_fd_setup(int *nfds, fd_set *rfds, fd_set *wfds)
 63{
 64  npfd = snd_seq_poll_descriptors_count(snd_handle, POLLIN);
 65  int i;
 66	
 67  if (npfd > MAX_PFDS) {
 68    fatal("ALSA wants too many file descriptors");
 69  }
 70	
 71  snd_seq_poll_descriptors(snd_handle, pfd, npfd, POLLIN);
 72  for (i = 0; i < npfd; i += 1) {
 73    if (pfd[i].fd > *nfds) {
 74      *nfds = pfd[i].fd;
 75    }
 76    if (pfd[i].events & POLLIN) {
 77      FD_SET(pfd[i].fd, rfds);
 78    }
 79  }
 80}
 81
 82void
 83alsa_read_ready()
 84{
 85  snd_midi_event_init(midi_event_parser);
 86	
 87  for (;;) {
 88    snd_seq_event_t *ev;
 89    unsigned char buf[ALSA_BUFSIZE];
 90    long converted;
 91    int r;
 92		
 93    r = snd_seq_event_input(snd_handle, &ev);
 94		
 95    if (r == -EAGAIN) {
 96      // input queue empty
 97      break;
 98    } else if (r == -ENOSPC) {
 99      warn("Input queue overflow");
100      continue;
101    } else if (r < 0) {
102      warn("snd_seq_event_input() = %d", r);
103      continue;
104    }
105        
106    converted = snd_midi_event_decode(midi_event_parser, buf, ALSA_BUFSIZE, ev);
107    if (converted < 0) {
108      if (ev->type == SND_SEQ_EVENT_CLIENT_START ) {
109	DUMPf("Client started \n");
110	continue;
111      } else if (ev->type == SND_SEQ_EVENT_CLIENT_EXIT) {
112	DUMPf("Client exited \n");
113	continue;
114      } else if (ev->type == SND_SEQ_EVENT_CLIENT_CHANGE) {
115	DUMPf("Client status/info changed \n");
116	continue;
117      } else if (ev->type == SND_SEQ_EVENT_PORT_START) {
118	DUMPf("Port created \n");
119	continue;
120      } else if (ev->type == SND_SEQ_EVENT_PORT_EXIT) {
121	DUMPf("Port deleted \n");
122	continue;
123      } else if (ev->type == SND_SEQ_EVENT_PORT_CHANGE) {
124	DUMPf("Port status/info changed \n");
125	continue;
126      } else if (ev->type == SND_SEQ_EVENT_PORT_SUBSCRIBED) {
127	DUMPf("Port connected \n");
128	continue;
129      } else if (ev->type == SND_SEQ_EVENT_PORT_UNSUBSCRIBED) {
130	DUMPf("Port disconnected \n");
131	continue;
132      } else {
133	warn("Can't decode MIDI event type 0x%02x", ev->type);
134      }
135      continue;
136    }
137		
138    if (converted < 3) {
139      int i;
140			
141      warn("Uh oh, weird MIDI packet with length %ld (does this make sense if prefixed with 90?)", converted);
142			
143      for (i = 0; i < converted; i += 1) {
144	fprintf(stderr, "  %02x", buf[i]);
145      }
146      fprintf(stderr, "\n");
147    }
148		
149    usb_write(buf, converted);
150  }
151}
152
153void
154alsa_check_fds(fd_set *rfds, fd_set *wfds)
155{
156  int i;
157	
158  for (i = 0; i < npfd; i += 1) {
159    int fd = pfd[i].fd;
160
161    if (FD_ISSET(fd, rfds) || FD_ISSET(fd, wfds)) {
162      alsa_read_ready();
163      return;
164    }
165  }
166}
167
168void
169alsa_write(uint8_t *data, size_t datalen)
170{
171  size_t offset = 0;
172	
173  snd_midi_event_init(midi_event_parser);
174	
175  while( datalen > offset) {
176    snd_seq_event_t ev;
177    long encoded;
178    int r;
179		
180    encoded = snd_midi_event_encode(midi_event_parser, data + offset, datalen - offset, &ev);
181    if (encoded <= 1) {
182      int i;
183	
184      warn("Unable to encode MIDI message (%ld < %ld)", encoded, (long int)datalen);
185      fprintf(stderr, "    ");
186      for (i = offset; i < (long int)datalen; i += 1) {
187	fprintf(stderr, "%02x ", data[i]);
188      }
189      fprintf(stderr, "\n");
190			
191      return;
192    }
193		
194    snd_seq_ev_set_direct(&ev);
195    snd_seq_ev_set_source(&ev, seq_port);
196    snd_seq_ev_set_subs(&ev);
197    r = snd_seq_event_output(snd_handle, &ev);
198		
199    if (r < 0) {
200      warn("Couldn't write an output event");
201    }
202    if (r > 200) {
203      warn("Output buffer size %d", r);
204    }
205		
206    offset += encoded;
207  }
208  snd_seq_drain_output(snd_handle);
209}