blob: 3d044bc5fce826c67b39714c8093d498b109f3c3 [file]
/*
* nas.c -- The Network Audio System backend for the spd_audio library.
*
* Copyright (C) 2004,2006 Brailcom, o.p.s.
*
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1, or (at your option) any later
* version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* $Id: nas.c,v 1.8 2006-07-11 16:12:26 hanke Exp $
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <glib.h>
#include <audio/audiolib.h>
#include <audio/soundlib.h>
#include <pthread.h>
#ifdef USE_DLOPEN
#define SPD_AUDIO_PLUGIN_ENTRY spd_audio_plugin_get
#else
#define SPD_AUDIO_PLUGIN_ENTRY spd_nas_LTX_spd_audio_plugin_get
#endif
#include <spd_audio_plugin.h>
#include "../common/common.h"
/* Put a message into the logfile (stderr) */
#define MSG(level, arg, ...) if (level <= nas_log_level) { MSG(level, "nas: " arg, ##__VA_ARGS__); }
#define ERR(arg, ...) MSG(0, "nas ERROR: " arg, ##__VA_ARGS__)
typedef struct {
AudioID id;
AuServer *aud;
AuFlowID flow;
pthread_mutex_t flow_mutex;
pthread_t nas_event_handler;
pthread_cond_t pt_cond;
pthread_mutex_t pt_mutex;
} spd_nas_id_t;
static int nas_log_level;
/* Internal event handler */
static void *_nas_handle_events(void *par)
{
spd_nas_id_t *nas_id = (spd_nas_id_t *) par;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
spd_pthread_setname("_nas_handle_events");
while (1)
AuHandleEvents(nas_id->aud);
}
/* NAS Server error handler */
/* Unfortunatelly we can't return these errors to the caller
since this handler gets called in the event handler thread. */
static AuBool _nas_handle_server_error(AuServer * server, AuErrorEvent * event)
{
ERR("Non-fatal server error in NAS\n");
if (event->type != 0) {
ERR("Event of a different type received in NAS error handler.");
return -1;
}
/* It's a pain but we can't ask for string return code
since it's not allowed to talk to the server inside error handlers
because of possible deadlocks. */
ERR("NAS: Serial number of failed request: %d\n",
(int) event->serial);
ERR("NAS: Error code: %d\n", event->error_code);
ERR("NAS: Resource id: %d\n", (int) event->resourceid);
ERR("NAS: Request code: %d\n", event->request_code);
ERR("NAS: Minor code: %d\n\n", event->minor_code);
return 0;
}
static AudioID *nas_open(void **pars)
{
spd_nas_id_t *nas_id;
int ret;
AuServer *aud;
aud = AuOpenServer(pars[2], 0, NULL, 0, NULL, NULL);
if (!aud) {
ERR("Can't connect to NAS audio server\n");
return NULL;
}
nas_id = (spd_nas_id_t *) g_malloc(sizeof(spd_nas_id_t));
nas_id->aud = aud;
AuSetErrorHandler(nas_id->aud, _nas_handle_server_error);
/* return value incompatible with documentation here */
/* if (!r){
ERR("Can't set default NAS event handler\n");
return -1;
} */
nas_id->flow = 0;
pthread_cond_init(&nas_id->pt_cond, NULL);
pthread_mutex_init(&nas_id->pt_mutex, NULL);
pthread_mutex_init(&nas_id->flow_mutex, NULL);
ret =
pthread_create(&nas_id->nas_event_handler, NULL, _nas_handle_events,
(void *)nas_id);
if (ret != 0) {
ERR("NAS Audio module: thread creation failed\n");
return NULL;
}
return (AudioID *) nas_id;
}
static int nas_play(AudioID * id, AudioTrack track)
{
char *buf;
Sound s;
AuEventHandlerRec *event_handler;
float length;
struct timeval now;
struct timespec timeout;
spd_nas_id_t *nas_id = (spd_nas_id_t *) id;
if (nas_id == NULL)
return -2;
s = SoundCreate(SoundFileFormatNone,
AuFormatLinearSigned16LSB,
track.num_channels,
track.sample_rate, track.num_samples, NULL);
buf = (char *)track.samples;
pthread_mutex_lock(&nas_id->flow_mutex);
event_handler = AuSoundPlayFromData(nas_id->aud,
s,
buf,
AuNone,
((nas_id->id.volume +
100) / 2) * 1500, NULL, NULL,
&nas_id->flow, NULL, NULL, NULL);
if (event_handler == NULL) {
pthread_mutex_unlock(&nas_id->flow_mutex);
ERR("AuSoundPlayFromData failed for unknown reasons.\n");
return -1;
}
if (nas_id->flow == 0) {
ERR("Couldn't start data flow");
}
pthread_mutex_unlock(&nas_id->flow_mutex);
/* Another timing magic */
pthread_mutex_lock(&nas_id->pt_mutex);
length = (((float)track.num_samples) / (float)track.sample_rate);
gettimeofday(&now, NULL);
timeout.tv_sec = now.tv_sec + (int)length;
timeout.tv_nsec =
now.tv_usec * 1000 + (length - (int)length) * 1000000000;
pthread_cond_timedwait(&nas_id->pt_cond, &nas_id->pt_mutex, &timeout);
pthread_mutex_unlock(&nas_id->pt_mutex);
pthread_mutex_lock(&nas_id->flow_mutex);
nas_id->flow = 0;
pthread_mutex_unlock(&nas_id->flow_mutex);
return 0;
}
static int nas_stop(AudioID * id)
{
spd_nas_id_t *nas_id = (spd_nas_id_t *) id;
if (nas_id == NULL)
return -2;
pthread_mutex_lock(&nas_id->flow_mutex);
if (nas_id->flow != 0)
AuStopFlow(nas_id->aud, nas_id->flow, NULL);
nas_id->flow = 0;
pthread_mutex_unlock(&nas_id->flow_mutex);
pthread_mutex_lock(&nas_id->pt_mutex);
pthread_cond_signal(&nas_id->pt_cond);
pthread_mutex_unlock(&nas_id->pt_mutex);
return 0;
}
static int nas_close(AudioID * id)
{
spd_nas_id_t *nas_id = (spd_nas_id_t *) id;
if (nas_id == NULL)
return -2;
pthread_cancel(nas_id->nas_event_handler);
pthread_join(nas_id->nas_event_handler, NULL);
pthread_mutex_destroy(&nas_id->pt_mutex);
pthread_mutex_destroy(&nas_id->flow_mutex);
AuCloseServer(nas_id->aud);
g_free(nas_id);
id = NULL;
return 0;
}
static int nas_set_volume(AudioID * id, int volume)
{
return 0;
}
static void nas_set_loglevel(int level)
{
if (level) {
nas_log_level = level;
}
}
static char const *nas_get_playcmd(void)
{
return NULL;
}
/* Provide the NAS backend */
static spd_audio_plugin_t nas_functions = {
"nas",
nas_open,
nas_play,
nas_stop,
nas_close,
nas_set_volume,
nas_set_loglevel,
nas_get_playcmd
};
spd_audio_plugin_t *nas_plugin_get(void)
{
return &nas_functions;
}
spd_audio_plugin_t *
__attribute__ ((weak))
SPD_AUDIO_PLUGIN_ENTRY(void)
{
return &nas_functions;
}