summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_out/Makefile.am7
-rw-r--r--src/audio_out/audio_file_out.c360
2 files changed, 366 insertions, 1 deletions
diff --git a/src/audio_out/Makefile.am b/src/audio_out/Makefile.am
index 6451fcc37..ab5f1dac9 100644
--- a/src/audio_out/Makefile.am
+++ b/src/audio_out/Makefile.am
@@ -42,7 +42,8 @@ endif
# all xine audio out plugins should be named like the
# scheme "xineplug_ao_out_"
#
-lib_LTLIBRARIES = xineplug_ao_out_none.la $(oss_module) \
+lib_LTLIBRARIES = xineplug_ao_out_none.la xineplug_ao_out_file.la \
+ $(oss_module) \
$(alsa_module) \
$(sun_module) \
$(arts_module) \
@@ -61,6 +62,10 @@ xineplug_ao_out_none_la_SOURCES = audio_none_out.c
xineplug_ao_out_none_la_LIBADD = $(XINE_LIB)
xineplug_ao_out_none_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+xineplug_ao_out_file_la_SOURCES = audio_file_out.c
+xineplug_ao_out_file_la_LIBADD = $(XINE_LIB)
+xineplug_ao_out_file_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+
xineplug_ao_out_oss_la_SOURCES = audio_oss_out.c
xineplug_ao_out_oss_la_LIBADD = $(XINE_LIB)
xineplug_ao_out_oss_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
diff --git a/src/audio_out/audio_file_out.c b/src/audio_out/audio_file_out.c
new file mode 100644
index 000000000..4fa6b5f51
--- /dev/null
+++ b/src/audio_out/audio_file_out.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2000-2003 the xine project
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine 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.
+ *
+ * xine 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: audio_file_out.c,v 1.1 2004/03/02 19:48:41 hadess Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <math.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "xine_internal.h"
+#include "xineutils.h"
+#include "audio_out.h"
+#include "bswap.h"
+
+#define AO_OUT_FILE_IFACE_VERSION 8
+
+#define GAP_TOLERANCE INT_MAX
+
+/* Taken (hStudlyCapsAndAll) from sox's wavwritehdr */
+
+struct wavhdr {
+ unsigned char bRiffMagic[4]; // 'RIFF'
+ uint32_t wRiffLength ; // length of file minus the 8 byte riff header
+ unsigned char bWaveMagic[8]; // 'WAVEfmt '
+ uint32_t wFmtSize; // length of format chunk minus 8 byte header
+ uint16_t wFormatTag; // identifies PCM, ULAW etc
+ uint16_t wChannels;
+ uint32_t dwSamplesPerSecond; // samples per second per channel
+ uint32_t dwAvgBytesPerSec; // non-trivial for compressed formats
+ uint16_t wBlockAlign; // basic block size
+ uint16_t wBitsPerSample; // non-trivial for compressed formats
+
+ // PCM formats then go straight to the data chunk:
+ unsigned char bData[4]; // 'data'
+ unsigned long dwDataLength; // length of data chunk minus 8 byte header
+};
+
+typedef struct file_driver_s {
+ ao_driver_t ao_driver;
+
+ xine_t *xine;
+
+ int capabilities;
+ int mode;
+
+ int32_t sample_rate;
+ uint32_t num_channels;
+ uint32_t bits_per_sample;
+ uint32_t bytes_per_frame;
+
+ char *fname;
+ int fd;
+ size_t bytes_written;
+} file_driver_t;
+
+typedef struct {
+ audio_driver_class_t driver_class;
+
+ config_values_t *config;
+ xine_t *xine;
+} file_class_t;
+
+/*
+ * open the audio device for writing to
+ */
+static int ao_file_open(ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int mode)
+{
+ file_driver_t *this = (file_driver_t *) this_gen;
+ struct wavhdr w;
+
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "audio_file_out: ao_open bits=%d rate=%d, mode=%d\n", bits, rate, mode);
+
+ this->mode = mode;
+ this->sample_rate = rate;
+ this->bits_per_sample = bits;
+
+ switch (mode) {
+ case AO_CAP_MODE_MONO:
+ this->num_channels = 1;
+ break;
+ case AO_CAP_MODE_STEREO:
+ this->num_channels = 2;
+ break;
+ }
+ this->bytes_per_frame = (this->bits_per_sample*this->num_channels) / 8;
+
+ this->fd = -1;
+ this->fname = getenv("XINE_WAVE_OUTPUT");
+ if (!this->fname)
+ this->fname = "xine-out.wav";
+
+ this->fd = open(this->fname, O_WRONLY|O_TRUNC|O_CREAT, 0644);
+
+ if (this->fd == -1) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG, "audio_file_out: Failed to open file '%s': %s\n",
+ this->fname, strerror(errno));
+ return 0;
+ }
+
+ w.bRiffMagic[0] = 'R';
+ w.bRiffMagic[1] = 'I';
+ w.bRiffMagic[2] = 'F';
+ w.bRiffMagic[3] = 'F';
+ w.wRiffLength = le2me_32(0x7ff00024);
+ w.bWaveMagic[0] = 'W';
+ w.bWaveMagic[1] = 'A';
+ w.bWaveMagic[2] = 'V';
+ w.bWaveMagic[3] = 'E';
+ w.bWaveMagic[4] = 'f';
+ w.bWaveMagic[5] = 'm';
+ w.bWaveMagic[6] = 't';
+ w.bWaveMagic[7] = ' ';
+ w.wFmtSize = le2me_32(0x10);
+ w.wFormatTag = le2me_16(1); // PCM;
+ w.wChannels = le2me_16(this->num_channels);
+ w.dwSamplesPerSecond = le2me_32(this->sample_rate);
+ w.dwAvgBytesPerSec = le2me_32(this->sample_rate * this->bytes_per_frame);
+ w.wBlockAlign = this->bytes_per_frame;
+ w.wBitsPerSample = this->bits_per_sample;
+ w.bData[0] = 'd';
+ w.bData[1] = 'a';
+ w.bData[2] = 't';
+ w.bData[3] = 'a';
+ w.dwDataLength = 0x7ffff000;
+
+ this->bytes_written = 0;
+ if (write(this->fd, &w, sizeof(w)) != sizeof(w)) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG, "audio_file_out: Failed to write WAVE header to file '%s': %s\n",
+ this->fname, strerror(errno));
+ close(this->fd);
+ this->fd = -1;
+ return 0;
+ }
+ return this->sample_rate;
+}
+
+
+static int ao_file_num_channels(ao_driver_t *this_gen)
+{
+ file_driver_t *this = (file_driver_t *) this_gen;
+ return this->num_channels;
+}
+
+static int ao_file_bytes_per_frame(ao_driver_t *this_gen)
+{
+ file_driver_t *this = (file_driver_t *) this_gen;
+ return this->bytes_per_frame;
+}
+
+static int ao_file_get_gap_tolerance (ao_driver_t *this_gen)
+{
+ return GAP_TOLERANCE;
+}
+
+static int ao_file_write(ao_driver_t *this_gen, int16_t *data,
+ uint32_t num_frames)
+{
+ file_driver_t *this = (file_driver_t *) this_gen;
+ size_t len = num_frames * this->bytes_per_frame;
+
+ while(len) {
+ size_t thislen = write(this->fd, data, len);
+
+ if (thislen == -1) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG, "audio_file_out: Failed to write data to file '%s': %s\n",
+ this->fname, strerror(errno));
+ return -1;
+ }
+ len -= thislen;
+ this->bytes_written += thislen;
+ }
+
+ /* Delay for an appropriate amount of time to prevent padding */
+ xine_usec_sleep ((1000 * num_frames / this->sample_rate)*1000);
+
+ return 1;
+}
+
+
+static int ao_file_delay (ao_driver_t *this_gen)
+{
+ return 0;
+}
+
+static void ao_file_close(ao_driver_t *this_gen)
+{
+ file_driver_t *this = (file_driver_t *) this_gen;
+ uint32_t len;
+
+ len = le2me_32(this->bytes_written);
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_file_out: Close file '%s'. %d KiB written\n",
+ this->fname, this->bytes_written / 1024);
+
+ if (lseek(this->fd, 40, SEEK_SET) != -1) {
+ write(this->fd, &len, 4);
+ len = le2me_32(this->bytes_written + 0x24);
+ if (lseek(this->fd, 4, SEEK_SET) != -1)
+ write(this->fd, &len, 4);
+ }
+
+ close(this->fd);
+ this->fd = -1;
+}
+
+static uint32_t ao_file_get_capabilities (ao_driver_t *this_gen) {
+ file_driver_t *this = (file_driver_t *) this_gen;
+ return this->capabilities;
+}
+
+static void ao_file_exit(ao_driver_t *this_gen)
+{
+ file_driver_t *this = (file_driver_t *) this_gen;
+
+ if (this->fd != -1)
+ ao_file_close(this_gen);
+
+ free (this);
+}
+
+static int ao_file_get_property (ao_driver_t *this_gen, int property) {
+
+ return 0;
+}
+
+static int ao_file_set_property (ao_driver_t *this_gen, int property, int value) {
+
+ return ~value;
+}
+
+static int ao_file_ctrl(ao_driver_t *this_gen, int cmd, ...) {
+ /*file_driver_t *this = (file_driver_t *) this_gen;*/
+
+ switch (cmd) {
+
+ case AO_CTRL_PLAY_PAUSE:
+ break;
+
+ case AO_CTRL_PLAY_RESUME:
+ break;
+
+ case AO_CTRL_FLUSH_BUFFERS:
+ break;
+ }
+
+ return 0;
+}
+
+static ao_driver_t *open_plugin (audio_driver_class_t *class_gen,
+ const void *data) {
+
+ file_class_t *class = (file_class_t *) class_gen;
+ /* config_values_t *config = class->config; */
+ file_driver_t *this;
+
+ lprintf ("open_plugin called\n");
+
+ this = (file_driver_t *) xine_xmalloc (sizeof (file_driver_t));
+
+ this->xine = class->xine;
+ this->capabilities = AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO;
+
+ this->sample_rate = 0;
+
+ this->ao_driver.get_capabilities = ao_file_get_capabilities;
+ this->ao_driver.get_property = ao_file_get_property;
+ this->ao_driver.set_property = ao_file_set_property;
+ this->ao_driver.open = ao_file_open;
+ this->ao_driver.num_channels = ao_file_num_channels;
+ this->ao_driver.bytes_per_frame = ao_file_bytes_per_frame;
+ this->ao_driver.delay = ao_file_delay;
+ this->ao_driver.write = ao_file_write;
+ this->ao_driver.close = ao_file_close;
+ this->ao_driver.exit = ao_file_exit;
+ this->ao_driver.get_gap_tolerance = ao_file_get_gap_tolerance;
+ this->ao_driver.control = ao_file_ctrl;
+
+ this->fd = -1;
+
+ return &this->ao_driver;
+}
+
+/*
+ * class functions
+ */
+
+static char* get_identifier (audio_driver_class_t *this_gen) {
+ return "file";
+}
+
+static char* get_description (audio_driver_class_t *this_gen) {
+ return _("xine file audio output plugin");
+}
+
+static void dispose_class (audio_driver_class_t *this_gen) {
+
+ file_class_t *this = (file_class_t *) this_gen;
+
+ free (this);
+}
+
+static void *init_class (xine_t *xine, void *data) {
+
+ file_class_t *this;
+
+ lprintf ("init class\n");
+
+ this = (file_class_t *) xine_xmalloc (sizeof (file_class_t));
+
+ this->driver_class.open_plugin = open_plugin;
+ this->driver_class.get_identifier = get_identifier;
+ this->driver_class.get_description = get_description;
+ this->driver_class.dispose = dispose_class;
+
+ this->config = xine->config;
+ this->xine = xine;
+
+ return this;
+}
+
+static ao_info_t ao_info_file = {
+ -1 /* do not auto probe this one */
+};
+
+/*
+ * exported plugin catalog entry
+ */
+
+plugin_info_t xine_plugin_info[] = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_AUDIO_OUT, AO_OUT_FILE_IFACE_VERSION, "file", XINE_VERSION_CODE, &ao_info_file, init_class },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
+