aboutsummaryrefslogtreecommitdiffhomepage
path: root/libao2
diff options
context:
space:
mode:
Diffstat (limited to 'libao2')
-rw-r--r--libao2/ao_arts.c148
-rw-r--r--libao2/ao_esd.c477
-rw-r--r--libao2/ao_portaudio.c431
-rw-r--r--libao2/ao_pulse.c2
-rw-r--r--libao2/ao_sgi.c301
-rw-r--r--libao2/audio_out.c11
6 files changed, 435 insertions, 935 deletions
diff --git a/libao2/ao_arts.c b/libao2/ao_arts.c
deleted file mode 100644
index d828e7953e..0000000000
--- a/libao2/ao_arts.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * aRts audio output driver for MPlayer
- *
- * copyright (c) 2002 Michele Balistreri <brain87@gmx.net>
- *
- * This file is part of MPlayer.
- *
- * MPlayer is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * MPlayer 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 General Public License along
- * with MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <artsc.h>
-#include <stdio.h>
-
-#include "config.h"
-#include "audio_out.h"
-#include "audio_out_internal.h"
-#include "libaf/af_format.h"
-#include "mp_msg.h"
-
-#define OBTAIN_BITRATE(a) (((a != AF_FORMAT_U8) && (a != AF_FORMAT_S8)) ? 16 : 8)
-
-/* Feel free to experiment with the following values: */
-#define ARTS_PACKETS 10 /* Number of audio packets */
-#define ARTS_PACKET_SIZE_LOG2 11 /* Log2 of audio packet size */
-
-static arts_stream_t stream;
-
-static const ao_info_t info =
-{
- "aRts audio output",
- "arts",
- "Michele Balistreri <brain87@gmx.net>",
- ""
-};
-
-LIBAO_EXTERN(arts)
-
-static int control(int cmd, void *arg)
-{
- return CONTROL_UNKNOWN;
-}
-
-static int init(int rate_hz, int channels, int format, int flags)
-{
- int err;
- int frag_spec;
-
- if( (err=arts_init()) ) {
- mp_tmsg(MSGT_AO, MSGL_ERR, "[AO ARTS] %s\n", arts_error_text(err));
- return 0;
- }
- mp_tmsg(MSGT_AO, MSGL_INFO, "[AO ARTS] Connected to sound server.\n");
-
- /*
- * arts supports 8bit unsigned and 16bit signed sample formats
- * (16bit apparently in little endian format, even in the case
- * when artsd runs on a big endian cpu).
- *
- * Unsupported formats are translated to one of these two formats
- * using mplayer's audio filters.
- */
- switch (format) {
- case AF_FORMAT_U8:
- case AF_FORMAT_S8:
- format = AF_FORMAT_U8;
- break;
- default:
- format = AF_FORMAT_S16_LE; /* artsd always expects little endian?*/
- break;
- }
-
- ao_data.format = format;
- ao_data.channels = channels;
- ao_data.samplerate = rate_hz;
- ao_data.bps = (rate_hz*channels);
-
- if(format != AF_FORMAT_U8 && format != AF_FORMAT_S8)
- ao_data.bps*=2;
-
- stream=arts_play_stream(rate_hz, OBTAIN_BITRATE(format), channels, "MPlayer");
-
- if(stream == NULL) {
- mp_tmsg(MSGT_AO, MSGL_ERR, "[AO ARTS] Unable to open a stream.\n");
- arts_free();
- return 0;
- }
-
- /* Set the stream to blocking: it will not block anyway, but it seems */
- /* to be working better */
- arts_stream_set(stream, ARTS_P_BLOCKING, 1);
- frag_spec = ARTS_PACKET_SIZE_LOG2 | ARTS_PACKETS << 16;
- arts_stream_set(stream, ARTS_P_PACKET_SETTINGS, frag_spec);
- ao_data.buffersize = arts_stream_get(stream, ARTS_P_BUFFER_SIZE);
- mp_tmsg(MSGT_AO, MSGL_INFO, "[AO ARTS] Stream opened.\n");
-
- mp_tmsg(MSGT_AO, MSGL_INFO, "[AO ARTS] buffer size: %d\n",
- ao_data.buffersize);
- mp_tmsg(MSGT_AO, MSGL_INFO, "[AO ARTS] buffer size: %d\n",
- arts_stream_get(stream, ARTS_P_PACKET_SIZE));
-
- return 1;
-}
-
-static void uninit(int immed)
-{
- arts_close_stream(stream);
- arts_free();
-}
-
-static int play(void* data,int len,int flags)
-{
- return arts_write(stream, data, len);
-}
-
-static void audio_pause(void)
-{
-}
-
-static void audio_resume(void)
-{
-}
-
-static void reset(void)
-{
-}
-
-static int get_space(void)
-{
- return arts_stream_get(stream, ARTS_P_BUFFER_SPACE);
-}
-
-static float get_delay(void)
-{
- return ((float) (ao_data.buffersize - arts_stream_get(stream,
- ARTS_P_BUFFER_SPACE))) / ((float) ao_data.bps);
-}
diff --git a/libao2/ao_esd.c b/libao2/ao_esd.c
deleted file mode 100644
index e7c6701aa0..0000000000
--- a/libao2/ao_esd.c
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * EsounD audio output driver for MPlayer
- *
- * copyright (c) 2002 Juergen Keil <jk@tools.de>
- *
- * This file is part of MPlayer.
- *
- * MPlayer is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * MPlayer 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 General Public License along
- * with MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
- /*
- * TODO / known problems:
- * - does not work well when the esd daemon has autostandby disabled
- * (workaround: run esd with option "-as 2" - fortunatelly this is
- * the default)
- * - plays noise on a linux 2.4.4 kernel with a SB16PCI card, when using
- * a local tcp connection to the esd daemon; there is no noise when using
- * a unix domain socket connection.
- * (there are EIO errors reported by the sound card driver, so this is
- * most likely a linux sound card driver problem)
- */
-
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <time.h>
-#ifdef __svr4__
-#include <stropts.h>
-#endif
-#include <esd.h>
-
-#include "config.h"
-#include "audio_out.h"
-#include "audio_out_internal.h"
-#include "libaf/af_format.h"
-#include "mp_msg.h"
-
-
-#define ESD_RESAMPLES 0
-#define ESD_DEBUG 0
-
-#if ESD_DEBUG
-#define dprintf(...) printf(__VA_ARGS__)
-#else
-#define dprintf(...) /**/
-#endif
-
-
-#define ESD_CLIENT_NAME "MPlayer"
-#define ESD_MAX_DELAY (1.0f) /* max amount of data buffered in esd (#sec) */
-
-static const ao_info_t info =
-{
- "EsounD audio output",
- "esd",
- "Juergen Keil <jk@tools.de>",
- ""
-};
-
-LIBAO_EXTERN(esd)
-
-static int esd_fd = -1;
-static int esd_play_fd = -1;
-static esd_server_info_t *esd_svinfo;
-static int esd_latency;
-static int esd_bytes_per_sample;
-static unsigned long esd_samples_written;
-static struct timeval esd_play_start;
-extern float audio_delay;
-
-/*
- * to set/get/query special features/parameters
- */
-static int control(int cmd, void *arg)
-{
- esd_player_info_t *esd_pi;
- esd_info_t *esd_i;
- time_t now;
- static time_t vol_cache_time;
- static ao_control_vol_t vol_cache;
-
- switch (cmd) {
- case AOCONTROL_GET_VOLUME:
- time(&now);
- if (now == vol_cache_time) {
- *(ao_control_vol_t *)arg = vol_cache;
- return CONTROL_OK;
- }
-
- dprintf("esd: get vol\n");
- if ((esd_i = esd_get_all_info(esd_fd)) == NULL)
- return CONTROL_ERROR;
-
- for (esd_pi = esd_i->player_list; esd_pi != NULL; esd_pi = esd_pi->next)
- if (strcmp(esd_pi->name, ESD_CLIENT_NAME) == 0)
- break;
-
- if (esd_pi != NULL) {
- ao_control_vol_t *vol = (ao_control_vol_t *)arg;
- vol->left = esd_pi->left_vol_scale * 100 / ESD_VOLUME_BASE;
- vol->right = esd_pi->right_vol_scale * 100 / ESD_VOLUME_BASE;
-
- vol_cache = *vol;
- vol_cache_time = now;
- }
- esd_free_all_info(esd_i);
-
- return CONTROL_OK;
-
- case AOCONTROL_SET_VOLUME:
- dprintf("esd: set vol\n");
- if ((esd_i = esd_get_all_info(esd_fd)) == NULL)
- return CONTROL_ERROR;
-
- for (esd_pi = esd_i->player_list; esd_pi != NULL; esd_pi = esd_pi->next)
- if (strcmp(esd_pi->name, ESD_CLIENT_NAME) == 0)
- break;
-
- if (esd_pi != NULL) {
- ao_control_vol_t *vol = (ao_control_vol_t *)arg;
- esd_set_stream_pan(esd_fd, esd_pi->source_id,
- vol->left * ESD_VOLUME_BASE / 100,
- vol->right * ESD_VOLUME_BASE / 100);
-
- vol_cache = *vol;
- time(&vol_cache_time);
- }
- esd_free_all_info(esd_i);
- return CONTROL_OK;
-
- default:
- return CONTROL_UNKNOWN;
- }
-}
-
-
-/*
- * open & setup audio device
- * return: 1=success 0=fail
- */
-static int init(int rate_hz, int channels, int format, int flags)
-{
- esd_format_t esd_fmt;
- int bytes_per_sample;
- int fl;
- char *server = ao_subdevice; /* NULL for localhost */
- float lag_seconds, lag_net = 0., lag_serv;
- struct timeval proto_start, proto_end;
-
- global_ao->no_persistent_volume = true;
-
- if (esd_fd < 0) {
- esd_fd = esd_open_sound(server);
- if (esd_fd < 0) {
- mp_tmsg(MSGT_AO, MSGL_ERR, "[AO ESD] esd_open_sound failed: %s\n",
- strerror(errno));
- return 0;
- }
-
- /* get server info, and measure network latency */
- gettimeofday(&proto_start, NULL);
- esd_svinfo = esd_get_server_info(esd_fd);
- if(server) {
- gettimeofday(&proto_end, NULL);
- lag_net = (proto_end.tv_sec - proto_start.tv_sec) +
- (proto_end.tv_usec - proto_start.tv_usec) / 1000000.0;
- lag_net /= 2.0; /* round trip -> one way */
- } else
- lag_net = 0.0; /* no network lag */
-
- /*
- if (esd_svinfo) {
- mp_msg(MSGT_AO, MSGL_INFO, "AO: [esd] server info:\n");
- esd_print_server_info(esd_svinfo);
- }
- */
- }
-
- esd_fmt = ESD_STREAM | ESD_PLAY;
-
-#if ESD_RESAMPLES
- /* let the esd daemon convert sample rate */
-#else
- /* let mplayer's audio filter convert the sample rate */
- if (esd_svinfo != NULL)
- rate_hz = esd_svinfo->rate;
-#endif
- ao_data.samplerate = rate_hz;
-
- /* EsounD can play mono or stereo */
- switch (channels) {
- case 1:
- esd_fmt |= ESD_MONO;
- ao_data.channels = bytes_per_sample = 1;
- break;
- default:
- esd_fmt |= ESD_STEREO;
- ao_data.channels = bytes_per_sample = 2;
- break;
- }
-
- /* EsounD can play 8bit unsigned and 16bit signed native */
- switch (format) {
- case AF_FORMAT_S8:
- case AF_FORMAT_U8:
- esd_fmt |= ESD_BITS8;
- ao_data.format = AF_FORMAT_U8;
- break;
- default:
- esd_fmt |= ESD_BITS16;
- ao_data.format = AF_FORMAT_S16_NE;
- bytes_per_sample *= 2;
- break;
- }
-
- /* modify audio_delay depending on esd_latency
- * latency is number of samples @ 44.1khz stereo 16 bit
- * adjust according to rate_hz & bytes_per_sample
- */
-#ifdef CONFIG_ESD_LATENCY
- esd_latency = esd_get_latency(esd_fd);
-#else
- esd_latency = ((channels == 1 ? 2 : 1) * ESD_DEFAULT_RATE *
- (ESD_BUF_SIZE + 64 * (4.0f / bytes_per_sample))
- ) / rate_hz;
- esd_latency += ESD_BUF_SIZE * 2;
-#endif
- if(esd_latency > 0) {
- lag_serv = (esd_latency * 4.0f) / (bytes_per_sample * rate_hz);
- lag_seconds = lag_net + lag_serv;
- audio_delay += lag_seconds;
- mp_tmsg(MSGT_AO, MSGL_INFO,"[AO ESD] latency: [server: %0.2fs, net: %0.2fs] (adjust %0.2fs)\n",
- lag_serv, lag_net, lag_seconds);
- }
-
- esd_play_fd = esd_play_stream_fallback(esd_fmt, rate_hz,
- server, ESD_CLIENT_NAME);
- if (esd_play_fd < 0) {
- mp_tmsg(MSGT_AO, MSGL_ERR, "[AO ESD] failed to open ESD playback stream: %s\n", strerror(errno));
- return 0;
- }
-
- /* enable non-blocking i/o on the socket connection to the esd server */
- if ((fl = fcntl(esd_play_fd, F_GETFL)) >= 0)
- fcntl(esd_play_fd, F_SETFL, O_NDELAY|fl);
-
-#if ESD_DEBUG
- {
- int sbuf, rbuf, len;
- len = sizeof(sbuf);
- getsockopt(esd_play_fd, SOL_SOCKET, SO_SNDBUF, &sbuf, &len);
- len = sizeof(rbuf);
- getsockopt(esd_play_fd, SOL_SOCKET, SO_RCVBUF, &rbuf, &len);
- dprintf("esd: send/receive socket buffer space %d/%d bytes\n",
- sbuf, rbuf);
- }
-#endif
-
- ao_data.bps = bytes_per_sample * rate_hz;
- ao_data.outburst = ao_data.bps > 100000 ? 4*ESD_BUF_SIZE : 2*ESD_BUF_SIZE;
-
- esd_play_start.tv_sec = 0;
- esd_samples_written = 0;
- esd_bytes_per_sample = bytes_per_sample;
-
- return 1;
-}
-
-
-/*
- * close audio device
- */
-static void uninit(int immed)
-{
- if (esd_play_fd >= 0) {
- esd_close(esd_play_fd);
- esd_play_fd = -1;
- }
-
- if (esd_svinfo) {
- esd_free_server_info(esd_svinfo);
- esd_svinfo = NULL;
- }
-
- if (esd_fd >= 0) {
- esd_close(esd_fd);
- esd_fd = -1;
- }
-}
-
-
-/*
- * plays 'len' bytes of 'data'
- * it should round it down to outburst*n
- * return: number of bytes played
- */
-static int play(void* data, int len, int flags)
-{
- int offs;
- int nwritten;
- int nsamples;
- int n;
-
- /* round down buffersize to a multiple of ESD_BUF_SIZE bytes */
- len = len / ESD_BUF_SIZE * ESD_BUF_SIZE;
- if (len <= 0)
- return 0;
-
-#define SINGLE_WRITE 0
-#if SINGLE_WRITE
- nwritten = write(esd_play_fd, data, len);
-#else
- for (offs = 0, nwritten=0; offs + ESD_BUF_SIZE <= len; offs += ESD_BUF_SIZE) {
- /*
- * note: we're writing to a non-blocking socket here.
- * A partial write means, that the socket buffer is full.
- */
- n = write(esd_play_fd, (char*)data + offs, ESD_BUF_SIZE);
- if ( n < 0 ) {
- if ( errno != EAGAIN )
- dprintf("esd play: write failed: %s\n", strerror(errno));
- break;
- } else if ( n != ESD_BUF_SIZE ) {
- nwritten += n;
- break;
- } else
- nwritten += n;
- }
-#endif
-
- if (nwritten > 0) {
- if (!esd_play_start.tv_sec)
- gettimeofday(&esd_play_start, NULL);
- nsamples = nwritten / esd_bytes_per_sample;
- esd_samples_written += nsamples;
-
- dprintf("esd play: %d %lu\n", nsamples, esd_samples_written);
- } else {
- dprintf("esd play: blocked / %lu\n", esd_samples_written);
- }
-
- return nwritten;
-}
-
-
-/*
- * stop playing, keep buffers (for pause)
- */
-static void audio_pause(void)
-{
- /*
- * not possible with esd. the esd daemom will continue playing
- * buffered data (not more than ESD_MAX_DELAY seconds of samples)
- */
-}
-
-
-/*
- * resume playing, after audio_pause()
- */
-static void audio_resume(void)
-{
- /*
- * not possible with esd.
- *
- * Let's hope the pause was long enough that the esd ran out of
- * buffered data; we restart our time based delay computation
- * for an audio resume.
- */
- esd_play_start.tv_sec = 0;
- esd_samples_written = 0;
-}
-
-
-/*
- * stop playing and empty buffers (for seeking/pause)
- */
-static void reset(void)
-{
-#ifdef __svr4__
- /* throw away data buffered in the esd connection */
- if (ioctl(esd_play_fd, I_FLUSH, FLUSHW))
- perror("I_FLUSH");
-#endif
-}
-
-
-/*
- * return: how many bytes can be played without blocking
- */
-static int get_space(void)
-{
- struct timeval tmout;
- fd_set wfds;
- float current_delay;
- int space;
-
- /*
- * Don't buffer too much data in the esd daemon.
- *
- * If we send too much, esd will block in write()s to the sound
- * device, and the consequence is a huge slow down for things like
- * esd_get_all_info().
- */
- if ((current_delay = get_delay()) >= ESD_MAX_DELAY) {
- dprintf("esd get_space: too much data buffered\n");
- return 0;
- }
-
- FD_ZERO(&wfds);
- FD_SET(esd_play_fd, &wfds);
- tmout.tv_sec = 0;
- tmout.tv_usec = 0;
-
- if (select(esd_play_fd + 1, NULL, &wfds, NULL, &tmout) != 1)
- return 0;
-
- if (!FD_ISSET(esd_play_fd, &wfds))
- return 0;
-
- /* try to fill 50% of the remaining "free" buffer space */
- space = (ESD_MAX_DELAY - current_delay) * ao_data.bps * 0.5f;
-
- /* round up to next multiple of ESD_BUF_SIZE */
- space = (space + ESD_BUF_SIZE-1) / ESD_BUF_SIZE * ESD_BUF_SIZE;
-
- dprintf("esd get_space: %d\n", space);
- return space;
-}
-
-
-/*
- * return: delay in seconds between first and last sample in buffer
- */
-static float get_delay(void)
-{
- struct timeval now;
- double buffered_samples_time;
- double play_time;
-
- if (!esd_play_start.tv_sec)
- return 0;
-
- buffered_samples_time = (float)esd_samples_written / ao_data.samplerate;
- gettimeofday(&now, NULL);
- play_time = now.tv_sec - esd_play_start.tv_sec;
- play_time += (now.tv_usec - esd_play_start.tv_usec) / 1000000.;
-
- /* dprintf("esd delay: %f %f\n", play_time, buffered_samples_time); */
-
- if (play_time > buffered_samples_time) {
- dprintf("esd: underflow\n");
- esd_play_start.tv_sec = 0;
- esd_samples_written = 0;
- return 0;
- }
-
- dprintf("esd: get_delay %f\n", buffered_samples_time - play_time);
- return buffered_samples_time - play_time;
-}
diff --git a/libao2/ao_portaudio.c b/libao2/ao_portaudio.c
new file mode 100644
index 0000000000..c8275f0b38
--- /dev/null
+++ b/libao2/ao_portaudio.c
@@ -0,0 +1,431 @@
+/*
+ * This file is part of mplayer2.
+ *
+ * mplayer2 is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mplayer2 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 General Public License along
+ * with mplayer2. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include <libavutil/avutil.h>
+#include <portaudio.h>
+
+#include "config.h"
+#include "subopt-helper.h"
+#include "libaf/af_format.h"
+#include "mp_msg.h"
+#include "audio_out.h"
+
+struct priv {
+ PaStream *stream;
+ int framelen;
+
+ pthread_mutex_t ring_mutex;
+
+ // protected by ring_mutex
+ unsigned char *ring;
+ int ring_size; // max size of the ring
+ int read_pos; // points to first byte that can be read
+ int read_len; // number of bytes that can be read
+ double play_time; // time when last packet returned to PA is on speaker
+ // 0 is N/A (0 is not a valid PA time value)
+ int play_silence; // play this many bytes of silence, before real data
+ bool play_remaining;// play what's left in the buffer, then stop stream
+};
+
+struct format_map {
+ int mp_format;
+ PaSampleFormat pa_format;
+};
+
+static const struct format_map format_maps[] = {
+ // first entry is the default format
+ {AF_FORMAT_S16_NE, paInt16},
+ {AF_FORMAT_S24_NE, paInt24},
+ {AF_FORMAT_S32_NE, paInt32},
+ {AF_FORMAT_S8, paInt8},
+ {AF_FORMAT_U8, paUInt8},
+ {AF_FORMAT_FLOAT_NE, paFloat32},
+ {AF_FORMAT_UNKNOWN, 0}
+};
+
+static void print_help(void)
+{
+ mp_msg(MSGT_AO, MSGL_FATAL,
+ "\n-ao portaudio commandline help:\n"
+ "Example: mplayer -ao portaudio:device=subdevice\n"
+ "\nOptions:\n"
+ " device=subdevice\n"
+ " Audio device PortAudio should use. Devices can be listed\n"
+ " with -ao portaudio:device=help\n"
+ " The subdevice can be passed as index, or as complete name.\n");
+}
+
+static bool check_pa_ret(int ret)
+{
+ if (ret < 0) {
+ mp_msg(MSGT_AO, MSGL_ERR, "[portaudio] %s\n",
+ Pa_GetErrorText(ret));
+ if (ret == paUnanticipatedHostError) {
+ const PaHostErrorInfo* hosterr = Pa_GetLastHostErrorInfo();
+ mp_msg(MSGT_AO, MSGL_ERR, "[portaudio] Host error: %s\n",
+ hosterr->errorText);
+ }
+ return false;
+ }
+ return true;
+}
+
+// Amount of bytes that contain audio of the given duration, aligned to frames.
+static int seconds_to_bytes(struct ao *ao, double duration_seconds)
+{
+ struct priv *priv = ao->priv;
+
+ int bytes = duration_seconds * ao->bps;
+ if (bytes % priv->framelen)
+ bytes += priv->framelen - (bytes % priv->framelen);
+ return bytes;
+}
+
+static int to_int(const char *s, int return_on_error)
+{
+ char *endptr;
+ int res = strtol(s, &endptr, 10);
+ return (s[0] && !endptr[0]) ? res : return_on_error;
+}
+
+static int find_device(struct ao *ao, const char *name)
+{
+ int help = strcmp(name, "help") == 0;
+ int count = Pa_GetDeviceCount();
+ check_pa_ret(count);
+ int found = paNoDevice;
+ int index = to_int(name, -1);
+ if (help)
+ mp_msg(MSGT_AO, MSGL_INFO, "PortAudio devices:\n");
+ for (int n = 0; n < count; n++) {
+ const PaDeviceInfo* info = Pa_GetDeviceInfo(n);
+ if (help) {
+ if (info->maxOutputChannels < 1)
+ continue;
+ mp_msg(MSGT_AO, MSGL_INFO, " %d '%s', %d channels, latency: %.2f "
+ "ms, sample rate: %.0f\n", n, info->name,
+ info->maxOutputChannels,
+ info->defaultHighOutputLatency * 1000,
+ info->defaultSampleRate);
+ }
+ if (strcmp(name, info->name) == 0 || n == index) {
+ found = n;
+ break;
+ }
+ }
+ if (found == paNoDevice && !help)
+ mp_msg(MSGT_AO, MSGL_FATAL, "[portaudio] Device '%s' not found!\n",
+ name);
+ return found;
+}
+
+static int ring_write(struct ao *ao, unsigned char *data, int len)
+{
+ struct priv *priv = ao->priv;
+
+ int free = priv->ring_size - priv->read_len;
+ int write_pos = (priv->read_pos + priv->read_len) % priv->ring_size;
+ int write_len = FFMIN(len, free);
+ int len1 = FFMIN(priv->ring_size - write_pos, write_len);
+ int len2 = write_len - len1;
+
+ memcpy(priv->ring + write_pos, data, len1);
+ memcpy(priv->ring, data + len1, len2);
+
+ priv->read_len += write_len;
+
+ return write_len;
+}
+
+static int ring_read(struct ao *ao, unsigned char *data, int len)
+{
+ struct priv *priv = ao->priv;
+
+ int read_len = FFMIN(len, priv->read_len);
+ int len1 = FFMIN(priv->ring_size - priv->read_pos, read_len);
+ int len2 = read_len - len1;
+
+ memcpy(data, priv->ring + priv->read_pos, len1);
+ memcpy(data + len1, priv->ring, len2);
+
+ priv->read_len -= read_len;
+ priv->read_pos = (priv->read_pos + read_len) % priv->ring_size;
+
+ return read_len;
+}
+
+static void fill_silence(unsigned char *ptr, int len)
+{
+ memset(ptr, 0, len);
+}
+
+static int stream_callback(const void *input,
+ void *output_v,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo *timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData)
+{
+ struct ao *ao = userData;
+ struct priv *priv = ao->priv;
+ int res = paContinue;
+ unsigned char *output = output_v;
+ int len_bytes = frameCount * priv->framelen;
+
+ pthread_mutex_lock(&priv->ring_mutex);
+
+ // NOTE: PA + ALSA in dmix mode seems to pretend that there is no latency
+ // (outputBufferDacTime == currentTime)
+ priv->play_time = timeInfo->outputBufferDacTime
+ + len_bytes / (float)ao->bps;
+
+ if (priv->play_silence > 0) {
+ int bytes = FFMIN(priv->play_silence, len_bytes);
+ fill_silence(output, bytes);
+ priv->play_silence -= bytes;
+ len_bytes -= bytes;
+ output += bytes;
+ }
+ int read = ring_read(ao, output, len_bytes);
+ len_bytes -= read;
+ output += read;
+
+ if (len_bytes > 0) {
+ if (priv->play_remaining) {
+ res = paComplete;
+ priv->play_remaining = false;
+ } else {
+ mp_msg(MSGT_AO, MSGL_ERR, "[portaudio] Buffer underflow!\n");
+ }
+ fill_silence(output, len_bytes);
+ }
+
+ pthread_mutex_unlock(&priv->ring_mutex);
+
+ return res;
+}
+
+static void uninit(struct ao *ao, bool cut_audio)
+{
+ struct priv *priv = ao->priv;
+
+ if (priv->stream) {
+ if (!cut_audio && Pa_IsStreamActive(priv->stream) == 1) {
+ pthread_mutex_lock(&priv->ring_mutex);
+
+ priv->play_remaining = true;
+
+ pthread_mutex_unlock(&priv->ring_mutex);
+
+ check_pa_ret(Pa_StopStream(priv->stream));
+ }
+ check_pa_ret(Pa_CloseStream(priv->stream));
+ }
+
+ pthread_mutex_destroy(&priv->ring_mutex);
+ Pa_Terminate();
+}
+
+static int init(struct ao *ao, char *params)
+{
+ struct priv *priv = talloc_zero(ao, struct priv);
+ ao->priv = priv;
+
+ if (!check_pa_ret(Pa_Initialize()))
+ return -1;
+
+ pthread_mutex_init(&priv->ring_mutex, NULL);
+
+ char *device = NULL;
+ const opt_t subopts[] = {
+ {"device", OPT_ARG_MSTRZ, &device, NULL},
+ {NULL}
+ };
+ if (subopt_parse(params, subopts) != 0) {
+ print_help();
+ goto error_exit;
+ }
+
+ int pa_device = Pa_GetDefaultOutputDevice();
+ if (device)
+ pa_device = find_device(ao, device);
+ if (pa_device == paNoDevice)
+ goto error_exit;
+
+ PaStreamParameters sp = {
+ .device = pa_device,
+ .channelCount = ao->channels,
+ .suggestedLatency
+ = Pa_GetDeviceInfo(pa_device)->defaultHighOutputLatency,
+ };
+
+ const struct format_map *fmt = format_maps;
+ while (fmt->pa_format) {
+ if (fmt->mp_format == ao->format) {
+ PaStreamParameters test = sp;
+ test.sampleFormat = fmt->pa_format;
+ if (Pa_IsFormatSupported(NULL, &test, ao->samplerate) == paNoError)
+ break;
+ }
+ fmt++;
+ }
+ if (!fmt->pa_format) {
+ mp_msg(MSGT_AO, MSGL_V,
+ "[portaudio] Unsupported format, using default.\n");
+ fmt = format_maps;
+ }
+
+ ao->format = fmt->mp_format;
+ sp.sampleFormat = fmt->pa_format;
+ priv->framelen = ao->channels * (af_fmt2bits(ao->format) / 8);
+ ao->bps = ao->samplerate * priv->framelen;
+
+ if (!check_pa_ret(Pa_IsFormatSupported(NULL, &sp, ao->samplerate)))
+ goto error_exit;
+ if (!check_pa_ret(Pa_OpenStream(&priv->stream, NULL, &sp, ao->samplerate,
+ paFramesPerBufferUnspecified, paNoFlag,
+ stream_callback, ao)))
+ goto error_exit;
+
+ priv->ring_size = seconds_to_bytes(ao, 0.5);
+ priv->ring = talloc_zero_size(priv, priv->ring_size);
+
+ free(device);
+ return 0;
+
+error_exit:
+ uninit(ao, true);
+ free(device);
+ return -1;
+}
+
+static int play(struct ao *ao, void *data, int len, int flags)
+{
+ struct priv *priv = ao->priv;
+
+ pthread_mutex_lock(&priv->ring_mutex);
+
+ int write_len = ring_write(ao, data, len);
+ if (flags & AOPLAY_FINAL_CHUNK)
+ priv->play_remaining = true;
+
+ pthread_mutex_unlock(&priv->ring_mutex);
+
+ if (Pa_IsStreamStopped(priv->stream) == 1)
+ check_pa_ret(Pa_StartStream(priv->stream));
+
+ return write_len;
+}
+
+static int get_space(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+
+ pthread_mutex_lock(&priv->ring_mutex);
+
+ int free = priv->ring_size - priv->read_len;
+
+ pthread_mutex_unlock(&priv->ring_mutex);
+
+ return free;
+}
+
+static float get_delay(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+
+ double stream_time = Pa_GetStreamTime(priv->stream);
+
+ pthread_mutex_lock(&priv->ring_mutex);
+
+ float frame_time = priv->play_time ? priv->play_time - stream_time : 0;
+ float buffer_latency = (priv->read_len + priv->play_silence)
+ / (float)ao->bps;
+
+ pthread_mutex_unlock(&priv->ring_mutex);
+
+ return buffer_latency + frame_time;
+}
+
+static void reset(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+
+ if (Pa_IsStreamStopped(priv->stream) != 1)
+ check_pa_ret(Pa_AbortStream(priv->stream));
+
+ pthread_mutex_lock(&priv->ring_mutex);
+
+ priv->read_len = 0;
+ priv->read_pos = 0;
+ priv->play_remaining = false;
+ priv->play_time = 0;
+ priv->play_silence = 0;
+
+ pthread_mutex_unlock(&priv->ring_mutex);
+}
+
+static void pause(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+
+ check_pa_ret(Pa_AbortStream(priv->stream));
+
+ double stream_time = Pa_GetStreamTime(priv->stream);
+
+ pthread_mutex_lock(&priv->ring_mutex);
+
+ // When playback resumes, replace the lost audio (due to dropping the
+ // portaudio/driver/hardware internal buffers) with silence.
+ float frame_time = priv->play_time ? priv->play_time - stream_time : 0;
+ priv->play_silence += seconds_to_bytes(ao, FFMAX(frame_time, 0));
+ priv->play_time = 0;
+
+ pthread_mutex_unlock(&priv->ring_mutex);
+}
+
+static void resume(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+
+ check_pa_ret(Pa_StartStream(priv->stream));
+}
+
+const struct ao_driver audio_out_portaudio = {
+ .is_new = true,
+ .info = &(const struct ao_info) {
+ "PortAudio",
+ "portaudio",
+ "wm4",
+ "",
+ },
+ .init = init,
+ .uninit = uninit,
+ .reset = reset,
+ .get_space = get_space,
+ .play = play,
+ .get_delay = get_delay,
+ .pause = pause,
+ .resume = resume,
+};
diff --git a/libao2/ao_pulse.c b/libao2/ao_pulse.c
index ed6e08286a..e48c402fa6 100644
--- a/libao2/ao_pulse.c
+++ b/libao2/ao_pulse.c
@@ -186,7 +186,7 @@ static int init(struct ao *ao, char *params)
ao->priv = priv;
if (params) {
- devarg = strdup(ao_subdevice);
+ devarg = strdup(params);
sink = strchr(devarg, ':');
if (sink)
*sink++ = 0;
diff --git a/libao2/ao_sgi.c b/libao2/ao_sgi.c
deleted file mode 100644
index 492c8ff3ba..0000000000
--- a/libao2/ao_sgi.c
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * SGI/IRIX audio output driver
- *
- * copyright (c) 2001 oliver.schoenbrunner@jku.at
- *
- * This file is part of MPlayer.
- *
- * MPlayer is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * MPlayer 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 General Public License along
- * with MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <dmedia/audio.h>
-
-#include "audio_out.h"
-#include "audio_out_internal.h"
-#include "mp_msg.h"
-#include "libaf/af_format.h"
-
-static const ao_info_t info =
-{
- "sgi audio output",
- "sgi",
- "Oliver Schoenbrunner",
- ""
-};
-
-LIBAO_EXTERN(sgi)
-
-
-static ALconfig ao_config;
-static ALport ao_port;
-static int sample_rate;
-static int queue_size;
-static int bytes_per_frame;
-
-/**
- * \param [in/out] format
- * \param [out] width
- *
- * \return the closest matching SGI AL sample format
- *
- * \note width is set to required per-channel sample width
- * format is updated to match the SGI AL sample format
- */
-static int fmt2sgial(int *format, int *width) {
- int smpfmt = AL_SAMPFMT_TWOSCOMP;
-
- /* SGI AL only supports float and signed integers in native
- * endianness. If this is something else, we must rely on the audio
- * filter to convert it to a compatible format. */
-
- /* 24-bit audio is supported, but only with 32-bit alignment.
- * mplayer's 24-bit format is packed, unfortunately.
- * So we must upgrade 24-bit requests to 32 bits. Then we drop the
- * lowest 8 bits during playback. */
-
- switch(*format) {
- case AF_FORMAT_U8:
- case AF_FORMAT_S8:
- *width = AL_SAMPLE_8;
- *format = AF_FORMAT_S8;
- break;
-
- case AF_FORMAT_U16_LE:
- case AF_FORMAT_U16_BE:
- case AF_FORMAT_S16_LE:
- case AF_FORMAT_S16_BE:
- *width = AL_SAMPLE_16;
- *format = AF_FORMAT_S16_NE;
- break;
-
- case AF_FORMAT_U24_LE:
- case AF_FORMAT_U24_BE:
- case AF_FORMAT_S24_LE:
- case AF_FORMAT_S24_BE:
- case AF_FORMAT_U32_LE:
- case AF_FORMAT_U32_BE:
- case AF_FORMAT_S32_LE:
- case AF_FORMAT_S32_BE:
- *width = AL_SAMPLE_24;
- *format = AF_FORMAT_S32_NE;
- break;
-
- case AF_FORMAT_FLOAT_LE:
- case AF_FORMAT_FLOAT_BE:
- *width = 4;
- *format = AF_FORMAT_FLOAT_NE;
- smpfmt = AL_SAMPFMT_FLOAT;
- break;
-
- default:
- *width = AL_SAMPLE_16;
- *format = AF_FORMAT_S16_NE;
- break;
-
- }
-
- return smpfmt;
-}
-
-// to set/get/query special features/parameters
-static int control(int cmd, void *arg){
-
- mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] control.\n");
-
- return CONTROL_UNKNOWN;
-}
-
-// open & setup audio device
-// return: 1=success 0=fail
-static int init(int rate, int channels, int format, int flags) {
-
- int smpwidth, smpfmt;
- int rv = AL_DEFAULT_OUTPUT;
-
- smpfmt = fmt2sgial(&format, &smpwidth);
-
- mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] init: Samplerate: %iHz Channels: %s Format %s\n", rate, (channels > 1) ? "Stereo" : "Mono", af_fmt2str_short(format));
-
- { /* from /usr/share/src/dmedia/audio/setrate.c */
-
- double frate, realrate;
- ALpv x[2];
-
- if(ao_subdevice) {
- rv = alGetResourceByName(AL_SYSTEM, ao_subdevice, AL_OUTPUT_DEVICE_TYPE);
- if (!rv) {
- mp_tmsg(MSGT_AO, MSGL_ERR, "[AO SGI] play: invalid device.\n");
- return 0;
- }
- }
-
- frate = rate;
-
- x[0].param = AL_RATE;
- x[0].value.ll = alDoubleToFixed(rate);
- x[1].param = AL_MASTER_CLOCK;
- x[1].value.i = AL_CRYSTAL_MCLK_TYPE;
-
- if (alSetParams(rv,x, 2)<0) {
- mp_tmsg(MSGT_AO, MSGL_WARN, "[AO SGI] init: setparams failed: %s\nCould not set desired samplerate.\n", alGetErrorString(oserror()));
- }
-
- if (x[0].sizeOut < 0) {
- mp_tmsg(MSGT_AO, MSGL_WARN, "[AO SGI] init: AL_RATE was not accepted on the given resource.\n");
- }
-
- if (alGetParams(rv,x, 1)<0) {
- mp_tmsg(MSGT_AO, MSGL_WARN, "[AO SGI] init: getparams failed: %s\n", alGetErrorString(oserror()));
- }
-
- realrate = alFixedToDouble(x[0].value.ll);
- if (frate != realrate) {
- mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] init: samplerate is now %f (desired rate is %f)\n", realrate, frate);
- }
- sample_rate = (int)realrate;
- }
-
- bytes_per_frame = channels * smpwidth;
-
- ao_data.samplerate = sample_rate;
- ao_data.channels = channels;
- ao_data.format = format;
- ao_data.bps = sample_rate * bytes_per_frame;
- ao_data.buffersize=131072;
- ao_data.outburst = ao_data.buffersize/16;
-
- ao_config = alNewConfig();
-
- if (!ao_config) {
- mp_tmsg(MSGT_AO, MSGL_ERR, "[AO SGI] init: %s\n", alGetErrorString(oserror()));
- return 0;
- }
-
- if(alSetChannels(ao_config, channels) < 0 ||
- alSetWidth(ao_config, smpwidth) < 0 ||
- alSetSampFmt(ao_config, smpfmt) < 0 ||
- alSetQueueSize(ao_config, sample_rate) < 0 ||
- alSetDevice(ao_config, rv) < 0) {
- mp_tmsg(MSGT_AO, MSGL_ERR, "[AO SGI] init: %s\n", alGetErrorString(oserror()));
- return 0;
- }
-
- ao_port = alOpenPort("mplayer", "w", ao_config);
-
- if (!ao_port) {
- mp_tmsg(MSGT_AO, MSGL_ERR, "[AO SGI] init: Unable to open audio channel: %s\n", alGetErrorString(oserror()));
- return 0;
- }
-
- // printf("ao_sgi, init: port %d config %d\n", ao_port, ao_config);
- queue_size = alGetQueueSize(ao_config);
- return 1;
-
-}
-
-// close audio device
-static void uninit(int immed) {
-
- /* TODO: samplerate should be set back to the value before mplayer was started! */
-
- mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] uninit: ...\n");
-
- if (ao_config) {
- alFreeConfig(ao_config);
- ao_config = NULL;
- }
-
- if (ao_port) {
- if (!immed)
- while(alGetFilled(ao_port) > 0) sginap(1);
- alClosePort(ao_port);
- ao_port = NULL;
- }
-
-}
-
-// stop playing and empty buffers (for seeking/pause)
-static void reset(void) {
-
- mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] reset: ...\n");
-
- alDiscardFrames(ao_port, queue_size);
-}
-
-// stop playing, keep buffers (for pause)
-static void audio_pause(void) {
-
- mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] audio_pause: ...\n");
-
-}
-
-// resume playing, after audio_pause()
-static void audio_resume(void) {
-
- mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] audio_resume: ...\n");
-
-}
-
-// return: how many bytes can be played without blocking
-static int get_space(void) {
-
- // printf("ao_sgi, get_space: (ao_outburst %d)\n", ao_data.outburst);
- // printf("ao_sgi, get_space: alGetFillable [%d] \n", alGetFillable(ao_port));
-
- return alGetFillable(ao_port) * bytes_per_frame;
-
-}
-
-
-// plays 'len' bytes of 'data'
-// it should round it down to outburst*n
-// return: number of bytes played
-static int play(void* data, int len, int flags) {
-
- /* Always process data in quadword-aligned chunks (64-bits). */
- const int plen = len / (sizeof(uint64_t) * bytes_per_frame);
- const int framecount = plen * sizeof(uint64_t);
-
- // printf("ao_sgi, play: len %d flags %d (%d %d)\n", len, flags, ao_port, ao_config);
- // printf("channels %d\n", ao_data.channels);
-
- if(ao_data.format == AF_FORMAT_S32_NE) {
- /* The zen of this is explained in fmt2sgial() */
- int32_t *smpls = data;
- const int32_t *smple = smpls + (framecount * ao_data.channels);
- while(smpls < smple)
- *smpls++ >>= 8;
- }
-
- alWriteFrames(ao_port, data, framecount);
-
- return framecount * bytes_per_frame;
-
-}
-
-// return: delay in seconds between first and last sample in buffer
-static float get_delay(void){
-
- // printf("ao_sgi, get_delay: (ao_buffersize %d)\n", ao_buffersize);
-
- // return (float)queue_size/((float)sample_rate);
- const int outstanding = alGetFilled(ao_port);
- return (float)((outstanding < 0) ? queue_size : outstanding) /
- ((float)sample_rate);
-}
diff --git a/libao2/audio_out.c b/libao2/audio_out.c
index 268c17d749..36b3e2d7c6 100644
--- a/libao2/audio_out.c
+++ b/libao2/audio_out.c
@@ -55,6 +55,7 @@ extern const struct ao_driver audio_out_v4l2;
extern const struct ao_driver audio_out_mpegpes;
extern const struct ao_driver audio_out_pcm;
extern const struct ao_driver audio_out_pss;
+extern const struct ao_driver audio_out_portaudio;
static const struct ao_driver * const audio_out_drivers[] = {
// native:
@@ -82,19 +83,13 @@ static const struct ao_driver * const audio_out_drivers[] = {
#ifdef CONFIG_OSS_AUDIO
&audio_out_oss,
#endif
-#ifdef CONFIG_SGI_AUDIO
- &audio_out_sgi,
+#ifdef CONFIG_PORTAUDIO
+ &audio_out_portaudio,
#endif
#ifdef CONFIG_SUN_AUDIO
&audio_out_sun,
#endif
// wrappers:
-#ifdef CONFIG_ARTS
- &audio_out_arts,
-#endif
-#ifdef CONFIG_ESD
- &audio_out_esd,
-#endif
#ifdef CONFIG_JACK
&audio_out_jack,
#endif