Blame src/libout123/modules/nas.c

Packit c32a2d
/*
Packit c32a2d
	nas: audio output via NAS
Packit c32a2d
Packit c32a2d
	copyright ?-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
Packit c32a2d
	see COPYING and AUTHORS files in distribution or http://mpg123.org
Packit c32a2d
	initially written by Martin Denn
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
#include "out123_int.h"
Packit c32a2d
#include <fcntl.h>
Packit c32a2d
#include <audio/audiolib.h>
Packit c32a2d
#include <audio/soundlib.h>
Packit c32a2d
#include "debug.h"
Packit c32a2d
Packit c32a2d
typedef struct
Packit c32a2d
{
Packit c32a2d
	AuServer            *aud;
Packit c32a2d
	AuFlowID            flow;
Packit c32a2d
	AuDeviceAttributes  *da;
Packit c32a2d
	int                 numDevices;
Packit c32a2d
	char                *buf;
Packit c32a2d
	AuUint32            buf_size;
Packit c32a2d
	AuUint32            buf_cnt;
Packit c32a2d
	AuBool              data_sent;
Packit c32a2d
	AuBool              finished;
Packit c32a2d
} InfoRec, *InfoPtr;
Packit c32a2d
Packit c32a2d
/* seconds */
Packit c32a2d
#define NAS_SOUND_PORT_DURATION (ao->device_buffer > 0. ? ao->device_buffer : 5)
Packit c32a2d
#define NAS_SOUND_LOW_WATER_MARK 25 /* percent */
Packit c32a2d
#define NAS_MAX_FORMAT 10 /* currently, there are 7 supported formats */
Packit c32a2d
Packit c32a2d
Packit c32a2d
/* FIXME: stick this inside userptr inside out123_handle instead */
Packit c32a2d
static InfoRec info;
Packit c32a2d
Packit c32a2d
/* NAS specific routines */
Packit c32a2d
Packit c32a2d
static void nas_sendData(AuServer *aud, InfoPtr i, AuUint32 numBytes)
Packit c32a2d
{
Packit c32a2d
    if (numBytes < i->buf_cnt) {
Packit c32a2d
        AuWriteElement(aud, i->flow, 0, numBytes, i->buf, AuFalse, NULL);
Packit c32a2d
        memmove(i->buf, i->buf + numBytes, i->buf_cnt - numBytes);
Packit c32a2d
        i->buf_cnt = i->buf_cnt - numBytes;
Packit c32a2d
    }
Packit c32a2d
    else {
Packit c32a2d
         AuWriteElement(aud, i->flow, 0, i->buf_cnt, i->buf,
Packit c32a2d
                        (numBytes > i->buf_cnt), NULL);
Packit c32a2d
         i->buf_cnt = 0;
Packit c32a2d
    }
Packit c32a2d
    i->data_sent = AuTrue;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static AuBool nas_eventHandler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *handler)
Packit c32a2d
{
Packit c32a2d
    InfoPtr         i = (InfoPtr) handler->data;
Packit c32a2d
Packit c32a2d
    switch (ev->type)
Packit c32a2d
    {
Packit c32a2d
        case AuEventTypeMonitorNotify:
Packit c32a2d
            i->finished = AuTrue;
Packit c32a2d
            break;
Packit c32a2d
       case AuEventTypeElementNotify:
Packit c32a2d
           {
Packit c32a2d
               AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
Packit c32a2d
Packit c32a2d
               switch (event->kind)
Packit c32a2d
               {
Packit c32a2d
                   case AuElementNotifyKindLowWater:
Packit c32a2d
                       nas_sendData(aud, i, event->num_bytes);
Packit c32a2d
                       break;
Packit c32a2d
                   case AuElementNotifyKindState:
Packit c32a2d
                       switch (event->cur_state)
Packit c32a2d
                       {
Packit c32a2d
                           case AuStatePause:
Packit c32a2d
                               if (event->reason != AuReasonUser)
Packit c32a2d
                                   nas_sendData(aud, i, event->num_bytes);
Packit c32a2d
                               break;
Packit c32a2d
                            case AuStateStop:
Packit c32a2d
                                i->finished = AuTrue;
Packit c32a2d
                                break;
Packit c32a2d
                       }
Packit c32a2d
               }
Packit c32a2d
           }
Packit c32a2d
    }
Packit c32a2d
    return AuTrue;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* 0 on error */
Packit c32a2d
static int nas_createFlow(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
    AuDeviceID      device = AuNone;
Packit c32a2d
    AuElement       elements[2];
Packit c32a2d
    unsigned char   format;
Packit c32a2d
    AuUint32        buf_samples;
Packit c32a2d
    int             i;
Packit c32a2d
 
Packit c32a2d
Packit c32a2d
    switch(ao->format) {
Packit c32a2d
    case MPG123_ENC_SIGNED_16:
Packit c32a2d
    default:
Packit c32a2d
		if (((char) *(short *)"x")=='x') /* ugly, but painless */
Packit c32a2d
			format = AuFormatLinearSigned16LSB; /* little endian */
Packit c32a2d
		else
Packit c32a2d
		format = AuFormatLinearSigned16MSB; /* big endian */
Packit c32a2d
        break;
Packit c32a2d
    case MPG123_ENC_UNSIGNED_8:
Packit c32a2d
        format = AuFormatLinearUnsigned8;
Packit c32a2d
        break;
Packit c32a2d
    case MPG123_ENC_SIGNED_8:
Packit c32a2d
        format = AuFormatLinearSigned8;
Packit c32a2d
        break;
Packit c32a2d
    case MPG123_ENC_ULAW_8:
Packit c32a2d
        format = AuFormatULAW8;
Packit c32a2d
        break;
Packit c32a2d
    }
Packit c32a2d
    /* look for an output device */
Packit c32a2d
    for (i = 0; i < AuServerNumDevices(info.aud); i++)
Packit c32a2d
       if (((AuDeviceKind(AuServerDevice(info.aud, i)) ==
Packit c32a2d
              AuComponentKindPhysicalOutput) &&
Packit c32a2d
             AuDeviceNumTracks(AuServerDevice(info.aud, i))
Packit c32a2d
             ==  ao->channels )) {
Packit c32a2d
            device = AuDeviceIdentifier(AuServerDevice(info.aud, i));
Packit c32a2d
            break;
Packit c32a2d
       }
Packit c32a2d
    if (device == AuNone) {
Packit c32a2d
       if(!AOQUIET)
Packit c32a2d
         error1( "Couldn't find an output device providing %d channels."
Packit c32a2d
               , ao->channels );
Packit c32a2d
       return 0;
Packit c32a2d
    }
Packit c32a2d
Packit c32a2d
    /* set gain */
Packit c32a2d
    if(ao->gain >= 0) {
Packit c32a2d
        info.da = AuGetDeviceAttributes(info.aud, device, NULL);
Packit c32a2d
        if ((info.da)!=NULL) {
Packit c32a2d
            AuDeviceGain(info.da) = AuFixedPointFromSum(ao->gain, 0);
Packit c32a2d
            AuSetDeviceAttributes(info.aud, AuDeviceIdentifier(info.da),
Packit c32a2d
                                  AuCompDeviceGainMask, info.da, NULL);
Packit c32a2d
        }
Packit c32a2d
        else if(!AOQUIET)
Packit c32a2d
            error("audio/gain: setable Volume/PCM-Level not supported");
Packit c32a2d
    }
Packit c32a2d
    
Packit c32a2d
    if (!(info.flow = AuCreateFlow(info.aud, NULL))) {
Packit c32a2d
        if(!AOQUIET)
Packit c32a2d
          error("Couldn't create flow");
Packit c32a2d
        return 0;
Packit c32a2d
    }
Packit c32a2d
Packit c32a2d
    buf_samples = ao->rate * NAS_SOUND_PORT_DURATION;
Packit c32a2d
Packit c32a2d
    AuMakeElementImportClient(&elements[0],        /* element */
Packit c32a2d
                              (unsigned short) ao->rate,
Packit c32a2d
                                                   /* rate */
Packit c32a2d
                              format,              /* format */
Packit c32a2d
                              ao->channels,        /* channels */
Packit c32a2d
                              AuTrue,              /* ??? */
Packit c32a2d
                              buf_samples,         /* max samples */
Packit c32a2d
                              (AuUint32) (buf_samples / 100
Packit c32a2d
                                  * NAS_SOUND_LOW_WATER_MARK),
Packit c32a2d
                                                   /* low water mark */
Packit c32a2d
                              0,                   /* num actions */
Packit c32a2d
                              NULL);               /* actions */
Packit c32a2d
    AuMakeElementExportDevice(&elements[1],        /* element */
Packit c32a2d
                              0,                   /* input */
Packit c32a2d
                              device,              /* device */
Packit c32a2d
                              (unsigned short) ao->rate,
Packit c32a2d
                                                   /* rate */
Packit c32a2d
                              AuUnlimitedSamples,  /* num samples */
Packit c32a2d
                              0,                   /* num actions */
Packit c32a2d
                              NULL);               /* actions */
Packit c32a2d
    AuSetElements(info.aud,                        /* Au server */
Packit c32a2d
                  info.flow,                       /* flow ID */
Packit c32a2d
                  AuTrue,                          /* clocked */
Packit c32a2d
                  2,                               /* num elements */
Packit c32a2d
                  elements,                        /* elements */
Packit c32a2d
                  NULL);                           /* return status */
Packit c32a2d
Packit c32a2d
    AuRegisterEventHandler(info.aud,               /* Au server */
Packit c32a2d
                           AuEventHandlerIDMask,   /* value mask */
Packit c32a2d
                           0,                      /* type */
Packit c32a2d
                           info.flow,              /* id */
Packit c32a2d
                           nas_eventHandler,       /* callback */
Packit c32a2d
                           (AuPointer) &info;;     /* data */
Packit c32a2d
Packit c32a2d
    info.buf_size = buf_samples * ao->channels * AuSizeofFormat(format);
Packit c32a2d
    info.buf = (char *) malloc(info.buf_size);
Packit c32a2d
    if (info.buf == NULL) {
Packit c32a2d
        if(!AOQUIET)
Packit c32a2d
          error1("Unable to allocate input/output buffer of size %ld",
Packit c32a2d
            (long)info.buf_size);
Packit c32a2d
        return 0;
Packit c32a2d
    }
Packit c32a2d
    info.buf_cnt = 0;
Packit c32a2d
    info.data_sent = AuFalse;
Packit c32a2d
    info.finished = AuFalse;
Packit c32a2d
    
Packit c32a2d
    AuStartFlow(info.aud,                          /* Au server */
Packit c32a2d
                info.flow,                         /* id */
Packit c32a2d
                NULL);                             /* status */
Packit c32a2d
    return 1; /* success */
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
static void flush_nas(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
    AuEvent         ev;
Packit c32a2d
    
Packit c32a2d
    while ((!info.data_sent) && (!info.finished)) {
Packit c32a2d
        AuNextEvent(info.aud, AuTrue, &ev;;
Packit c32a2d
        AuDispatchEvent(info.aud, &ev;;
Packit c32a2d
    }
Packit c32a2d
    info.data_sent = AuFalse;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
/* returning -1 on error, 0 on success... */
Packit c32a2d
static int open_nas(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	if(!ao) return -1;
Packit c32a2d
Packit c32a2d
	if (!(info.aud = AuOpenServer(ao->device, 0, NULL, 0, NULL, NULL)))
Packit c32a2d
	{
Packit c32a2d
		if (ao->device==NULL)
Packit c32a2d
		{
Packit c32a2d
			if(!AOQUIET)
Packit c32a2d
				error("could not open default NAS server");
Packit c32a2d
		} else
Packit c32a2d
		{
Packit c32a2d
			if(!AOQUIET)
Packit c32a2d
				error1("could not open NAS server %s\n", ao->device);
Packit c32a2d
		}
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
	info.buf_size = 0;
Packit c32a2d
Packit c32a2d
	return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
static int get_formats_nas(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
    int i, j, k, ret;
Packit c32a2d
Packit c32a2d
    ret=0;
Packit c32a2d
    j = AuServerNumFormats(info.aud);
Packit c32a2d
    for (i=0; i
Packit c32a2d
        k=AuServerFormat(info.aud,i);
Packit c32a2d
        switch (k)
Packit c32a2d
        {
Packit c32a2d
        case AuFormatULAW8:
Packit c32a2d
            ret |= MPG123_ENC_ULAW_8;
Packit c32a2d
            break;
Packit c32a2d
        case AuFormatLinearUnsigned8:
Packit c32a2d
            ret |= MPG123_ENC_UNSIGNED_8;
Packit c32a2d
            break;
Packit c32a2d
        case AuFormatLinearSigned8:
Packit c32a2d
            ret |= MPG123_ENC_SIGNED_8;
Packit c32a2d
            break;
Packit c32a2d
        case AuFormatLinearSigned16LSB:
Packit c32a2d
            ret |= MPG123_ENC_SIGNED_16;
Packit c32a2d
            break;
Packit c32a2d
        }
Packit c32a2d
    }
Packit c32a2d
    return ret;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static int write_nas(out123_handle *ao,unsigned char *buf,int len)
Packit c32a2d
{
Packit c32a2d
    int buf_cnt = 0;
Packit c32a2d
Packit c32a2d
    if (info.buf_size == 0)
Packit c32a2d
    if(!nas_createFlow(ao)) return -1;
Packit c32a2d
    
Packit c32a2d
    while ((info.buf_cnt + (len - buf_cnt)) >  info.buf_size) {
Packit c32a2d
        memcpy(info.buf + info.buf_cnt,
Packit c32a2d
               buf + buf_cnt,
Packit c32a2d
               (info.buf_size - info.buf_cnt));
Packit c32a2d
        buf_cnt += (info.buf_size - info.buf_cnt);
Packit c32a2d
        info.buf_cnt += (info.buf_size - info.buf_cnt);
Packit c32a2d
		flush_nas(ao);
Packit c32a2d
    }
Packit c32a2d
    memcpy(info.buf + info.buf_cnt,
Packit c32a2d
           buf + buf_cnt,
Packit c32a2d
           (len - buf_cnt));
Packit c32a2d
    info.buf_cnt += (len - buf_cnt);
Packit c32a2d
    
Packit c32a2d
    return len;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static int close_nas(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
    if (info.aud == NULL) {
Packit c32a2d
        return 0;
Packit c32a2d
    }
Packit c32a2d
    
Packit c32a2d
    if (info.buf_size == 0) {
Packit c32a2d
        /* Au server opened, but not yet initialized */
Packit c32a2d
        AuCloseServer(info.aud);
Packit c32a2d
        return 0;
Packit c32a2d
    }
Packit c32a2d
        
Packit c32a2d
    while (!info.finished) {
Packit c32a2d
        flush_nas(ao);
Packit c32a2d
    }
Packit c32a2d
    AuCloseServer(info.aud);
Packit c32a2d
    free(info.buf);
Packit c32a2d
    
Packit c32a2d
    return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
Packit c32a2d
static int init_nas(out123_handle* ao)
Packit c32a2d
{
Packit c32a2d
	if (ao==NULL) return -1;
Packit c32a2d
Packit c32a2d
	/* Set callbacks */
Packit c32a2d
	ao->open = open_nas;
Packit c32a2d
	ao->flush = flush_nas;
Packit c32a2d
	ao->write = write_nas;
Packit c32a2d
	ao->get_formats = get_formats_nas;
Packit c32a2d
	ao->close = close_nas;
Packit c32a2d
	
Packit c32a2d
	/* Success */
Packit c32a2d
	return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
Packit c32a2d
/* 
Packit c32a2d
	Module information data structure
Packit c32a2d
*/
Packit c32a2d
mpg123_module_t mpg123_output_module_info = {
Packit c32a2d
	/* api_version */	MPG123_MODULE_API_VERSION,
Packit c32a2d
	/* name */			"nas",						
Packit c32a2d
	/* description */	"Output audio using NAS (Network Audio System)",
Packit c32a2d
	/* revision */		"$Rev:$",						
Packit c32a2d
	/* handle */		NULL,
Packit c32a2d
	
Packit c32a2d
	/* init_output */	init_nas,						
Packit c32a2d
};
Packit c32a2d
Packit c32a2d