1 /*
2 * nas.c -- The Network Audio System backend for the spd_audio library.
3 *
4 * Copyright (C) 2004,2006 Brailcom, o.p.s.
5 *
6 * This is free software; you can redistribute it and/or modify it under the
7 * terms of the GNU Lesser General Public License as published by the Free
8 * Software Foundation; either version 2.1, or (at your option) any later
9 * version.
10 *
11 * This software is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 *
19 * $Id: nas.c,v 1.8 2006-07-11 16:12:26 hanke Exp $
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/time.h>
29 #include <glib.h>
30 #include <audio/audiolib.h>
31 #include <audio/soundlib.h>
32
33 #include <pthread.h>
34
35 #define SPD_AUDIO_PLUGIN_ENTRY spd_nas_LTX_spd_audio_plugin_get
36 #include <spd_audio_plugin.h>
37
38 typedef struct {
39 AudioID id;
40 AuServer *aud;
41 AuFlowID flow;
42 pthread_mutex_t flow_mutex;
43 pthread_t nas_event_handler;
44 pthread_cond_t pt_cond;
45 pthread_mutex_t pt_mutex;
46 } spd_nas_id_t;
47
48 static int nas_log_level;
49
50 /* Internal event handler */
_nas_handle_events(void * par)51 static void *_nas_handle_events(void *par)
52 {
53 spd_nas_id_t *nas_id = (spd_nas_id_t *) par;
54 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
55
56 while (1)
57 AuHandleEvents(nas_id->aud);
58
59 }
60
61 /* NAS Server error handler */
62 /* Unfortunatelly we can't return these errors to the caller
63 since this handler gets called in the event handler thread. */
_nas_handle_server_error(AuServer * server,AuErrorEvent * event)64 static AuBool _nas_handle_server_error(AuServer * server, AuErrorEvent * event)
65 {
66 fprintf(stderr, "ERROR: Non-fatal server error in NAS\n");
67
68 if (event->type != 0) {
69 fprintf(stderr,
70 "Event of a different type received in NAS error handler.");
71 return -1;
72 }
73
74 /* It's a pain but we can't ask for string return code
75 since it's not allowed to talk to the server inside error handlers
76 because of possible deadlocks. */
77 fprintf(stderr, "NAS: Serial number of failed request: %d\n",
78 event->serial);
79 fprintf(stderr, "NAS: Error code: %d\n", event->error_code);
80 fprintf(stderr, "NAS: Resource id: %d\n", event->resourceid);
81 fprintf(stderr, "NAS: Request code: %d\n", event->request_code);
82 fprintf(stderr, "NAS: Minor code: %d\n\n", event->minor_code);
83
84 return 0;
85 }
86
nas_open(void ** pars)87 static AudioID *nas_open(void **pars)
88 {
89 spd_nas_id_t *nas_id;
90 int ret;
91 AuBool r;
92
93 nas_id = (spd_nas_id_t *) g_malloc(sizeof(spd_nas_id_t));
94
95 nas_id->aud = AuOpenServer(pars[2], 0, NULL, 0, NULL, NULL);
96 if (!nas_id->aud) {
97 fprintf(stderr, "Can't connect to NAS audio server\n");
98 return NULL;
99 }
100
101 AuSetErrorHandler(nas_id->aud, _nas_handle_server_error);
102 /* return value incompatible with documentation here */
103 /* if (!r){
104 fprintf(stderr, "Can't set default NAS event handler\n");
105 return -1;
106 } */
107
108 nas_id->flow = 0;
109
110 pthread_cond_init(&nas_id->pt_cond, NULL);
111 pthread_mutex_init(&nas_id->pt_mutex, NULL);
112 pthread_mutex_init(&nas_id->flow_mutex, NULL);
113
114 ret =
115 pthread_create(&nas_id->nas_event_handler, NULL, _nas_handle_events,
116 (void *)nas_id);
117 if (ret != 0) {
118 fprintf(stderr,
119 "ERROR: NAS Audio module: thread creation failed\n");
120 return NULL;
121 }
122
123 return (AudioID *) nas_id;
124 }
125
nas_play(AudioID * id,AudioTrack track)126 static int nas_play(AudioID * id, AudioTrack track)
127 {
128 char *buf;
129 Sound s;
130 AuEventHandlerRec *event_handler;
131 float lenght;
132 struct timeval now;
133 struct timespec timeout;
134 spd_nas_id_t *nas_id = (spd_nas_id_t *) id;
135
136 if (nas_id == NULL)
137 return -2;
138
139 s = SoundCreate(SoundFileFormatNone,
140 AuFormatLinearSigned16LSB,
141 track.num_channels,
142 track.sample_rate, track.num_samples, NULL);
143
144 buf = (char *)track.samples;
145
146 pthread_mutex_lock(&nas_id->flow_mutex);
147
148 event_handler = AuSoundPlayFromData(nas_id->aud,
149 s,
150 buf,
151 AuNone,
152 ((nas_id->id.volume +
153 100) / 2) * 1500, NULL, NULL,
154 &nas_id->flow, NULL, NULL, NULL);
155
156 if (event_handler == NULL) {
157 pthread_mutex_unlock(&nas_id->flow_mutex);
158 fprintf(stderr,
159 "AuSoundPlayFromData failed for unknown resons.\n");
160 return -1;
161 }
162
163 if (nas_id->flow == 0) {
164 fprintf(stderr, "Couldn't start data flow");
165 }
166 pthread_mutex_unlock(&nas_id->flow_mutex);
167
168 /* Another timing magic */
169 pthread_mutex_lock(&nas_id->pt_mutex);
170 lenght = (((float)track.num_samples) / (float)track.sample_rate);
171 gettimeofday(&now, NULL);
172 timeout.tv_sec = now.tv_sec + (int)lenght;
173 timeout.tv_nsec =
174 now.tv_usec * 1000 + (lenght - (int)lenght) * 1000000000;
175 pthread_cond_timedwait(&nas_id->pt_cond, &nas_id->pt_mutex, &timeout);
176 pthread_mutex_unlock(&nas_id->pt_mutex);
177
178 pthread_mutex_lock(&nas_id->flow_mutex);
179 nas_id->flow = 0;
180 pthread_mutex_unlock(&nas_id->flow_mutex);
181
182 return 0;
183 }
184
nas_stop(AudioID * id)185 static int nas_stop(AudioID * id)
186 {
187 spd_nas_id_t *nas_id = (spd_nas_id_t *) id;
188
189 if (nas_id == NULL)
190 return -2;
191
192 pthread_mutex_lock(&nas_id->flow_mutex);
193 if (nas_id->flow != 0)
194 AuStopFlow(nas_id->aud, nas_id->flow, NULL);
195 nas_id->flow = 0;
196 pthread_mutex_unlock(&nas_id->flow_mutex);
197
198 pthread_mutex_lock(&nas_id->pt_mutex);
199 pthread_cond_signal(&nas_id->pt_cond);
200 pthread_mutex_unlock(&nas_id->pt_mutex);
201
202 return 0;
203 }
204
nas_close(AudioID * id)205 static int nas_close(AudioID * id)
206 {
207 spd_nas_id_t *nas_id = (spd_nas_id_t *) id;
208
209 if (nas_id == NULL)
210 return -2;
211
212 pthread_cancel(nas_id->nas_event_handler);
213 pthread_join(nas_id->nas_event_handler, NULL);
214
215 pthread_mutex_destroy(&nas_id->pt_mutex);
216 pthread_mutex_destroy(&nas_id->flow_mutex);
217
218 AuCloseServer(nas_id->aud);
219
220 g_free(nas_id);
221 id = NULL;
222
223 return 0;
224 }
225
nas_set_volume(AudioID * id,int volume)226 static int nas_set_volume(AudioID * id, int volume)
227 {
228 return 0;
229 }
230
nas_set_loglevel(int level)231 static void nas_set_loglevel(int level)
232 {
233 if (level) {
234 nas_log_level = level;
235 }
236 }
237
nas_get_playcmd(void)238 static char const *nas_get_playcmd(void)
239 {
240 return NULL;
241 }
242
243 /* Provide the NAS backend */
244 static spd_audio_plugin_t nas_functions = {
245 "nas",
246 nas_open,
247 nas_play,
248 nas_stop,
249 nas_close,
250 nas_set_volume,
251 nas_set_loglevel,
252 nas_get_playcmd
253 };
254
nas_plugin_get(void)255 spd_audio_plugin_t *nas_plugin_get(void)
256 {
257 return &nas_functions;
258 }
259
260 spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
261 __attribute__ ((weak, alias("nas_plugin_get")));
262