|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
pulse: audio output using PulseAudio server
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
copyright 2006-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 Nicholas J. Humfrey
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "out123_int.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include <stdlib.h>
|
|
Packit |
c32a2d |
#include <stdio.h>
|
|
Packit |
c32a2d |
#include <math.h>
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include <pulse/simple.h>
|
|
Packit |
c32a2d |
#include <pulse/error.h>
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "debug.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int open_pulse(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int err;
|
|
Packit |
c32a2d |
pa_simple* pas = NULL;
|
|
Packit |
c32a2d |
pa_sample_spec ss;
|
|
Packit |
c32a2d |
/* Check if already open ? */
|
|
Packit |
c32a2d |
if (ao->userptr) {
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("Pulse audio output is already open.");
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Open an audio I/O stream. */
|
|
Packit |
c32a2d |
/* When they are < 0, I shall set some default. */
|
|
Packit |
c32a2d |
if(ao->rate < 0 || ao->format < 0 || ao->channels < 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
ao->rate = 44100;
|
|
Packit |
c32a2d |
ao->channels = 2;
|
|
Packit |
c32a2d |
ao->format = MPG123_ENC_SIGNED_16;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Fill out pulse audio's data structure */
|
|
Packit |
c32a2d |
ss.channels = ao->channels;
|
|
Packit |
c32a2d |
ss.rate = ao->rate;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
switch(ao->format) {
|
|
Packit |
c32a2d |
case MPG123_ENC_SIGNED_16:
|
|
Packit |
c32a2d |
#ifdef WORDS_BIGENDIAN
|
|
Packit |
c32a2d |
ss.format=PA_SAMPLE_S16BE;
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
ss.format=PA_SAMPLE_S16LE;
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_ENC_SIGNED_24:
|
|
Packit |
c32a2d |
#ifdef WORDS_BIGENDIAN
|
|
Packit |
c32a2d |
ss.format=PA_SAMPLE_S24BE;
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
ss.format=PA_SAMPLE_S24LE;
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_ENC_SIGNED_32:
|
|
Packit |
c32a2d |
#ifdef WORDS_BIGENDIAN
|
|
Packit |
c32a2d |
ss.format=PA_SAMPLE_S32BE;
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
ss.format=PA_SAMPLE_S32LE;
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_ENC_FLOAT_32:
|
|
Packit |
c32a2d |
#ifdef WORDS_BIGENDIAN
|
|
Packit |
c32a2d |
ss.format=PA_SAMPLE_FLOAT32BE;
|
|
Packit |
c32a2d |
#else
|
|
Packit |
c32a2d |
ss.format=PA_SAMPLE_FLOAT32LE;
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_ENC_ALAW_8:
|
|
Packit |
c32a2d |
ss.format=PA_SAMPLE_ALAW;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_ENC_ULAW_8:
|
|
Packit |
c32a2d |
ss.format=PA_SAMPLE_ULAW;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_ENC_UNSIGNED_8:
|
|
Packit |
c32a2d |
ss.format=PA_SAMPLE_U8;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
default:
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error1("Unsupported audio format: 0x%x", ao->format);
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Perform the open */
|
|
Packit |
c32a2d |
pas = pa_simple_new(
|
|
Packit |
c32a2d |
NULL, /* Use the default server */
|
|
Packit |
c32a2d |
ao->name, /* Our application's name */
|
|
Packit |
c32a2d |
PA_STREAM_PLAYBACK,
|
|
Packit |
c32a2d |
ao->device, /* Use the default device if NULL */
|
|
Packit |
c32a2d |
"via out123", /* Description of our stream */
|
|
Packit |
c32a2d |
&ss, /* Our sample format */
|
|
Packit |
c32a2d |
NULL, /* Use default channel map */
|
|
Packit |
c32a2d |
NULL, /* Use default buffering attributes */
|
|
Packit |
c32a2d |
&err /* Error result code */
|
|
Packit |
c32a2d |
);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(pas == NULL)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error1("Failed to open pulse audio output: %s", pa_strerror(err));
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Store the pointer */
|
|
Packit |
c32a2d |
ao->userptr = (void*)pas;
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int get_formats_pulse(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* Only implemented Signed 16-bit audio for now */
|
|
Packit |
c32a2d |
return MPG123_ENC_SIGNED_16;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int write_pulse(out123_handle *ao, unsigned char *buf, int len)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
pa_simple *pas = (pa_simple*)ao->userptr;
|
|
Packit |
c32a2d |
int ret, err;
|
|
Packit |
c32a2d |
/* Doesn't return number of bytes but just success or not. */
|
|
Packit |
c32a2d |
ret = pa_simple_write( pas, buf, len, &err );
|
|
Packit |
c32a2d |
if(ret<0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error1("Failed to write audio: %s", pa_strerror(err));
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
return len; /* If successful, everything has been written. */
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int close_pulse(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
pa_simple *pas = (pa_simple*)ao->userptr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if (pas) {
|
|
Packit |
c32a2d |
int err; /* Do we really want to handle errors here? End is the end. */
|
|
Packit |
c32a2d |
pa_simple_drain(pas, &err;;
|
|
Packit |
c32a2d |
pa_simple_free(pas);
|
|
Packit |
c32a2d |
ao->userptr = NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void flush_pulse(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
pa_simple *pas = (pa_simple*)ao->userptr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if (pas) {
|
|
Packit |
c32a2d |
int err;
|
|
Packit |
c32a2d |
pa_simple_flush( pas, &err );
|
|
Packit |
c32a2d |
if(err && !AOQUIET)
|
|
Packit |
c32a2d |
error1("Failed to flush audio: %s", pa_strerror(err));
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int init_pulse(out123_handle* ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if (ao==NULL) return -1;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Set callbacks */
|
|
Packit |
c32a2d |
ao->open = open_pulse;
|
|
Packit |
c32a2d |
ao->flush = flush_pulse;
|
|
Packit |
c32a2d |
ao->write = write_pulse;
|
|
Packit |
c32a2d |
ao->get_formats = get_formats_pulse;
|
|
Packit |
c32a2d |
ao->close = close_pulse;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Success */
|
|
Packit |
c32a2d |
return 0;
|
|
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 */ "pulse",
|
|
Packit |
c32a2d |
/* description */ "Output audio using PulseAudio Server",
|
|
Packit |
c32a2d |
/* revision */ "$Rev:$",
|
|
Packit |
c32a2d |
/* handle */ NULL,
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* init_output */ init_pulse,
|
|
Packit |
c32a2d |
};
|
|
Packit |
c32a2d |
|