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