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}